fix some lgtm warnings

This commit is contained in:
Gilles Boccon-Gibod
2018-11-24 01:28:29 -05:00
parent 8de552d1b2
commit 91d2bc6b1c
18 changed files with 105 additions and 1356 deletions

View File

@@ -1,3 +1,6 @@
path_classifiers:
docs:
- Documents
test:
- Test

View File

@@ -7,7 +7,6 @@ $Id: Build.py 172 2008-07-22 18:26:24Z julien $
"""
import os
import sys
import getopt
import subprocess
@@ -81,7 +80,7 @@ try:
buildSwitch = 'build'
if rebuildAll: buildSwitch = 'rebuild'
elif makeClean: buildSwitch = 'clean'
cmd_list = ['%s/devenv.com' % VSBINDIR, '/%s' % buildSwitch, buildName, solutionFile]
cmd = " ".join(cmd_list)
print 'Executing:'

View File

@@ -7,7 +7,6 @@ $Id: Build.py 172 2008-07-22 18:26:24Z julien $
"""
import os
import sys
import getopt
import subprocess
@@ -81,7 +80,7 @@ try:
buildSwitch = 'build'
if rebuildAll: buildSwitch = 'rebuild'
elif makeClean: buildSwitch = 'clean'
cmd_list = ['%s/devenv.com' % VSBINDIR, '/%s' % buildSwitch, buildName, solutionFile]
cmd = " ".join(cmd_list)
print 'Executing:'

View File

@@ -4,7 +4,7 @@ import sys
import os
import shutil
import re
#############################################################
# GetSdkRevision
#############################################################
@@ -51,7 +51,7 @@ BENTO4_HOME = os.path.join(script_dir,'..')
BENTO4_VERSION = GetVersion()
SDK_REVISION = GetSdkRevision()
if SDK_REVISION == None:
if SDK_REVISION is None:
sys.exit(1)
print "Exporting Revision", SDK_REVISION
@@ -59,7 +59,7 @@ print "Exporting Revision", SDK_REVISION
SDK_NAME='Bento4-SRC-'+BENTO4_VERSION+'-'+SDK_REVISION
SDK_OUTPUT_ROOT=BENTO4_HOME+'/SDK'
SDK_ROOT=SDK_OUTPUT_ROOT+'/'+SDK_NAME
print SDK_NAME
# remove any previous SDK directory
@@ -71,7 +71,7 @@ if not os.path.exists(SDK_OUTPUT_ROOT):
os.makedirs(SDK_OUTPUT_ROOT)
### export
cmd = 'git archive --format=zip HEAD -o '+SDK_ROOT+'.zip'
cmd = 'git archive --format=zip HEAD -o '+SDK_ROOT+'.zip'
print cmd
#cmd = 'svn export -r'+SDK_REVISION+' https://zebulon.bok.net/svn/Bento4/trunk '+SDK_NAME
os.system(cmd)

View File

@@ -13,8 +13,6 @@
import sys
import os
import shutil
import glob
import datetime
import fnmatch
import zipfile
import re

View File

@@ -12,8 +12,6 @@
import sys
import os
import shutil
import platform
#############################################################
# Main

View File

@@ -1,32 +0,0 @@
from ctypes import *
import sys
if sys.platform == 'darwin':
bento4dll = 'libBento4C.dylib'
else:
raise "Unsupported Platform"
# library
lb4 = CDLL(bento4dll)
# type mapping
Ap4Result = c_int
Ap4Flags = c_uint
Ap4Mask = c_uint
Ap4Cardinal = c_uint
Ap4Ordinal = c_uint
Ap4TimeStamp = c_uint
Ap4Duration = c_ulong
Ap4UI32 = c_uint
Ap4SI32 = c_int
Ap4UI16 = c_ushort
Ap4SI16 = c_short
Ap4UI08 = c_ubyte
Ap4Byte = c_byte
Ap4Size = c_ulong
Ap4UI64 = c_ulonglong
Ap4SI64 = c_longlong
Ap4LargeSize = c_ulonglong
Ap4Offset = c_longlong
Ap4Position = c_ulonglong

View File

@@ -1,612 +0,0 @@
from bento4 import *
from bento4.errors import check_result
from ctypes import c_int, c_char_p, string_at
from struct import pack, unpack
def atom_type(name):
return unpack('>I', pack('>4s', name))[0]
def atom_name(type):
return unpack('>4s', pack('>I', type))[0]
class File(object):
FILE_BRAND_QT__ = atom_type('qt ')
FILE_BRAND_ISOM = atom_type('isom')
FILE_BRAND_MP41 = atom_type('mp41')
FILE_BRAND_MP42 = atom_type('mp42')
FILE_BRAND_3GP1 = atom_type('3gp1')
FILE_BRAND_3GP2 = atom_type('3gp2')
FILE_BRAND_3GP3 = atom_type('3gp3')
FILE_BRAND_3GP4 = atom_type('3gp4')
FILE_BRAND_3GP5 = atom_type('3gp5')
FILE_BRAND_3G2A = atom_type('3g2a')
FILE_BRAND_MMP4 = atom_type('mmp4')
FILE_BRAND_M4A_ = atom_type('M4A ')
FILE_BRAND_M4P_ = atom_type('M4P ')
FILE_BRAND_MJP2 = atom_type('mjp2')
def __init__(self, name='', movie=None):
self.moov = movie # can't have self.movie because movie is a property
if movie is None:
if len(name) == 0:
raise ValueError("name param cannot be empty")
result = Ap4Result()
self.bt4stream = lb4.AP4_FileByteStream_Create(c_char_p(name),
c_int(0), # read
byref(result))
check_result(result.value)
self.bt4file = lb4.AP4_File_FromStream(self.bt4stream, 0)
else:
self.bt4file = lb4.AP4_File_Create(movie.bt4movie)
movie.bt4owner = False
movie.file = self
def __del__(self):
lb4.AP4_File_Destroy(self.bt4file)
try:
lb4.AP4_Stream_Release(self.bt4stream)
except AttributeError:
pass # depending on how the object was created,
# self.bt4stream may or may not exist
def inspect(self, inspector):
f = lb4.AP4_File_Inspect
f.restype = check_result
f(self.bt4file, inspector.bt4inspector)
@property
def moov_is_before_mdat(self):
c = lb4.AP4_File_IsMoovBeforeMdat(self.bt4file)
return True if c !=0 else False
@property
def movie(self):
if self.moov:
return self.moov
bt4movie = lb4.AP4_File_GetMovie(self.bt4file)
if bt4movie is None:
return None
else:
self.moov = Movie(bt4movie=bt4movie)
self.moov.file = file # add a reference here for ref counting
return self.moov
def get_type(self):
"""returns a tuple (major_brand, minor_version, [compatible_brands])"""
major_brand = Ap4UI32()
minor_version = Ap4UI32()
compat = Ap4UI32()
compat_count = Ap4UI32()
# get the file type
f = lb4.AP4_File_GetFileType
f.restype = check_result
f(self.bt4file, byref(major_brand),
byref(minor_version), byref(compat_count))
# get the compatible brands
f = lb4.AP4_File_GetCompatibleBrand
f.restype = check_result
compat_brands = []
for i in xrange(compat_count.value):
f(self.bt4file, i, byref(compat))
compat_brands += [compat.value]
return (major_brand.value, minor_version.value, compat_brands)
def set_type(self, value):
"""value: a tuple (major_brand, minor_version, [compatible_brands])"""
major_brand, minor_version, compat_brands = value
compat_count = len(compat_brands)
compat_brand_array = Ap4UI32*compat_count
f = lb4.AP4_File_SetFileType
f.restype = check_result
f(self.bt4file, major_brand, minor_version,
compat_brand_array(*compat_brands), compat_count)
type = property(get_type, set_type)
class Movie(object):
def __init__(self, timescale=0, bt4movie=None):
self.bt4owner = False
if bt4movie is None:
self.bt4movie = lb4.AP4_Movie_Create(Ap4UI32(timescale))
else:
self.bt4movie = bt4movie
def __del__(self):
if self.bt4owner:
lb4.AP4_Movie_Destroy(self.bt4movie)
@property
def tracks(self):
result = {}
count = lb4.AP4_Movie_GetTrackCount(self.bt4movie)
for i in xrange(count):
bt4track = lb4.AP4_Movie_GetTrackByIndex(self.bt4movie, Ap4Ordinal(i))
track = Track(bt4track=bt4track)
track.movie = self # add a reference here for ref counting
result[track.id] = track
return result
@property
def duration(self):
return (lb4.AP4_Movie_GetDuration(self.bt4movie),
lb4.AP4_Movie_GetTimeScale(self.bt4movie))
def add_track(self, track):
result = lb4.AP4_Movie_AddTrack(self.bt4movie. track.bt4track)
if result.value != 0:
raise RuntimeError("Track insertion failed with error %d" %
result.value)
track.bt4owner = False # change ownership
track.movie = self
class Track(object):
TYPE_UNKNOWN = 0
TYPE_AUDIO = 1
TYPE_VIDEO = 2
TYPE_SYSTEM = 3
TYPE_HINT = 4
TYPE_TEXT = 5
TYPE_JPEG = 6
TYPE_RTP = 7
HANDLER_TYPE_SOUN = atom_type('soun')
HANDLER_TYPE_VIDE = atom_type('vide')
HANDLER_TYPE_HINT = atom_type('hint')
HANDLER_TYPE_MDIR = atom_type('mdir')
HANDLER_TYPE_TEXT = atom_type('text')
HANDLER_TYPE_TX3G = atom_type('tx3g')
HANDLER_TYPE_JPEG = atom_type('jpeg')
HANDLER_TYPE_ODSM = atom_type('odsm')
HANDLER_TYPE_SDSM = atom_type('sdsm')
def __init__(self, type=TYPE_UNKNOWN, sample_table=None, id=0,
track_duration=(), media_duration=(), language='',
dimensions=(), bt4track=None):
"""durations are tuple: (duration, timescale)
track_duration is in the timescale of the movie"""
self.bt4owner = False
self.movie_timescale = -1 # invalid on purpose
if bt4track is None:
self.bt4owner = True
self.bt4track = lb4.AP4_Track_Create(c_int(type),
sample_table.bt4table,
Ap4UI32(id),
Ap4UI32(track_duration[1]),
Ap4UI32(track_duration[0]),
Ap4UI32(media_duration[1]),
Ap4UI32(media_duration[0]),
c_char_p(language),
Ap4UI32(width),
Ap4UI32(height))
self.movie_timescale = track_duration[1]
sample_table.bt4owner = False # change the ownership
else:
self.bt4track = bt4track
def __del__(self):
if self.bt4owner:
lb4.AP4_Track_Destroy(self.bt4track)
@property
def type(self):
return lb4.AP4_Track_GetType(self.bt4track)
@property
def handler_type(self):
return lb4.AP4_Track_GetHandlerType(self.bt4track)
@property
def id(self):
return lb4.AP4_Track_GetId(self.bt4track)
@property
def media_duration(self):
return (lb4.AP4_Track_GetMediaDuration(self.bt4track),
lb4.AP4_Track_GetMediaTimeScale(self.bt4track))
@property
def duration(self):
if self.movie_timescale == -1:
# get it from the movie
self.movie_timescale = self.movie.duration[1]
return (lb4.AP4_Track_GetDuration(self.bt4track), self.movie_timescale)
@property
def language(self):
f = lb4.AP4_Track_GetLanguage
f.restype = c_char_p
return f(self.bt4track)
@property
def sample_count(self):
return lb4.AP4_Track_GetSampleCount(self.bt4track)
def sample(self, index):
if index<0 or index>=self.sample_count:
raise IndexError()
bt4sample = lb4.AP4_Sample_CreateEmpty()
f = lb4.AP4_Track_GetSample
f.restype = check_result
try:
f(self.bt4track, index, bt4sample)
except Exception, e:
lb4.AP4_Sample_Destroy(bt4sample) # prevents a leak
raise e
return Sample(bt4sample=bt4sample)
def sample_iterator(self, start=0, end=-1):
current = start
end = end if end != -1 else self.sample_count
while current<end:
yield self.sample(current)
current += 1
def set_movie_timescale(self, timescale):
f = lb4.AP4_Track_SetMovieTimeScale
f.restype = check_result
f(self.bt4track, Ap4UI32(timescale))
def sample_index_for_timestamp_ms(self, ts):
result = Ap4Ordinal()
f = lb4.AP4_Track_GetSampleIndexForTimeStampMs
f.restype = check_result
f(self.bt4track, Ap4TimeStamp(ts), byref(result))
return result.value
def sample_description(self, index):
bt4desc = lb4.AP4_Track_GetSampleDescription(self.bt4track,
Ap4Ordinal(index))
if bt4desc == 0:
raise IndexError()
sampledesc_type = lb4.AP4_SampleDescription_GetType(bt4desc)
track_type = self.type
if sampledesc_type == SampleDescription.TYPE_AVC:
result = AvcSampleDescription(bt4desc=bt4desc)
elif sampledesc_type == SampleDescription.TYPE_MPEG:
if track_type == Track.TYPE_AUDIO:
result = MpegAudioSampleDescription(bt4desc=bt4desc)
elif track_type == Track.TYPE_VIDEO:
result = MpegVideoSampleDescription(bt4desc=bt4desc)
elif track_type == Track.TYPE_MPEG:
result = MpegSystemSampleDescription(bt4desc=bt4desc)
else:
result = MpegSampleDescription(bt4desc=bt4desc)
else:
if track_type == Track.TYPE_AUDIO:
result = GenericAudioSampleDescription(bt4desc=bt4desc)
elif track_type == Track.TYPE_VIDEO:
result = GenericVideoSampleDescription(bt4desc=bt4desc)
else:
result = SampleDescription(bt4desc)
result.track = self # add a reference
return result
class Sample(object):
def __init__(self, data_stream=None, offset=0, size=0, desc_index=0,
dts=0, cts_offset=0, is_sync=False, bt4sample=None):
if bt4sample is None:
raise NotImplementedError()
else:
self.bt4sample = bt4sample
def __del__(self):
# always the owner of the sample
lb4.AP4_Sample_Destroy(self.bt4sample)
@property
def data(self):
bt4buffer = lb4.AP4_DataBuffer_Create(self.size)
f = lb4.AP4_Sample_ReadData
f.restype = check_result
try:
f(self.bt4sample, bt4buffer)
return string_at(lb4.AP4_DataBuffer_GetData(bt4buffer),
lb4.AP4_DataBuffer_GetDataSize(bt4buffer))
except Exception, e:
raise e
finally:
lb4.AP4_DataBuffer_Destroy(bt4buffer) # prevents a leak
@property
def offset(self):
return lb4.AP4_Sample_GetOffset(self,bt4sample)
@property
def size(self):
return lb4.AP4_Sample_GetSize(self.bt4sample)
@property
def description_index(self):
return lb4.AP4_Sample_GetDescriptionIndex(self.bt4sample)
@property
def dts(self):
return lb4.AP4_Sample_GetDts(self.bt4sample)
@property
def cts(self):
return lb4.AP4_Sample_GetCts(self.bt4sample)
@property
def is_sync(self):
v = lb4.AP4_Sample_IsSync(self.bt4sample)
return False if v==0 else True
class SampleDescription(object):
TYPE_UNKNOWN = 0
TYPE_MPEG = 1
TYPE_PROTECTED = 2
TYPE_AVC = 3
def __init__(self, bt4desc=None, bt4owner=False, **kwargs):
self.bt4desc = bt4desc
self.bt4owner = bt4owner
super(SampleDescription, self).__init__(**kwargs)
def __del__(self):
if self.bt4owner:
lb4.AP4_SampleDescription_Destroy(self.bt4desc)
@property
def type(self):
return lb4.AP4_SampleDescription_GetType(self.bt4desc)
@property
def format(self):
return lb4.AP4_SampleDescription_GetFormat(self.bt4desc)
class AudioSampleDescription(object):
"""mixin class"""
def __init__(self, bt4audiodesc, **kwargs):
self.bt4audiodesc = bt4audiodesc
super(AudioSampleDescription, self).__init__(**kwargs)
@property
def sample_rate(self):
return lb4.AP4_AudioSampleDescription_GetSampleRate(self.bt4audiodesc)
@property
def sample_size(self):
return lb4.AP4_AudioSampleDescription_GetSampleSize(self.bt4audiodesc)
@property
def channel_count(self):
return lb4.AP4_AudioSampleDescription_GetChannelCount(self.bt4audiodesc)
class VideoSampleDescription(object):
"""mixin class"""
def __init__(self, bt4videodesc, **kwargs):
self.bt4videodesc = bt4videodesc
super(VideoSampleDescription, self).__init__(**kwargs)
@property
def width(self):
return lb4.AP4_VideoSampleDescription_GetWidth(self.bt4videodesc)
@property
def height(self):
return lb4.AP4_VideoSampleDescription_GetHeight(self.bt4videodesc)
@property
def depth(self):
return lb4.AP4_VideoSampleDescription_GetDepth(self.bt4videodesc)
@property
def compressor_name(self):
f = lb4.AP4_VideoSampleDescription_GetCompressorName
f.restype = c_char_p
return f(self.bt4videodesc)
class MpegSampleDescription(SampleDescription):
STREAM_TYPE_FORBIDDEN = 0x00
STREAM_TYPE_OD = 0x01
STREAM_TYPE_CR = 0x02
STREAM_TYPE_BIFS = 0x03
STREAM_TYPE_VISUAL = 0x04
STREAM_TYPE_AUDIO = 0x05
STREAM_TYPE_MPEG7 = 0x06
STREAM_TYPE_IPMP = 0x07
STREAM_TYPE_OCI = 0x08
STREAM_TYPE_MPEGJ = 0x09
STREAM_TYPE_TEXT = 0x0D
OTI_MPEG4_SYSTEM = 0x01
OTI_MPEG4_SYSTEM_COR = 0x02
OTI_MPEG4_TEXT = 0x08
OTI_MPEG4_VISUAL = 0x20
OTI_MPEG4_AUDIO = 0x40
OTI_MPEG2_VISUAL_SIMPLE = 0x60
OTI_MPEG2_VISUAL_MAIN = 0x61
OTI_MPEG2_VISUAL_SNR = 0x62
OTI_MPEG2_VISUAL_SPATIAL = 0x63
OTI_MPEG2_VISUAL_HIGH = 0x64
OTI_MPEG2_VISUAL_422 = 0x65
OTI_MPEG2_AAC_AUDIO_MAIN = 0x66
OTI_MPEG2_AAC_AUDIO_LC = 0x67
OTI_MPEG2_AAC_AUDIO_SSRP = 0x68
OTI_MPEG2_PART3_AUDIO = 0x69
OTI_MPEG1_VISUAL = 0x6A
OTI_MPEG1_AUDIO = 0x6B
OTI_JPEG = 0x6C
def __init__(self, bt4mpegdesc, **kwargs):
self.bt4mpegdesc = bt4mpegdesc
super(MpegSampleDescription, self).__init__(**kwargs)
@property
def stream_type(self):
return lb4.AP4_MpegSampleDescription_GetStreamType(self.bt4mpegdesc)
@property
def object_type_id(self):
return lb4.AP4_MpegSampleDescription_GetObjectTypeId(self.bt4mpegdesc)
@property
def buffer_size(self):
return lb4.AP4_MpegSampleDescription_GetBufferSize(self.bt4mpegdesc)
@property
def max_bitrate(self):
return lb4.AP4_MpegSampleDescription_GetMaxBitrate(self.bt4mpegdesc)
@property
def avg_bitrate(self):
return lb4.AP4_MpegSampleDescription_GetAvgBitrate(self.bt4mpegdesc)
@property
def decoder_info(self):
bt4buf = lb4.AP4_MpegSampleDescription_GetDecoderInfo(self.bt4mpegdesc)
return string_at(lb4.AP4_DataBuffer_GetData(bt4buf),
lb4.AP4_DataBuffer_GetDataSize(bt4buf))
class GenericAudioSampleDescription(SampleDescription,
AudioSampleDescription):
def __init__(self, bt4desc):
bt4audiodesc = lb4.AP4_SampleDescription_AsAudio(bt4desc)
super(GenericAudioSampleDescription, self).__init__(bt4desc=bt4desc,
bt4audiodesc=bt4audiodesc)
class GenericVideoSampleDescription(SampleDescription,
VideoSampleDescription):
def __init__(self, bt4desc):
bt4videodesc = lb4.AP4_SampleDescription_AsVideo(bt4desc)
super(GenericVideoSampleDescription, self).__init__(bt4desc=bt4desc,
bt4videodesc=bt4videodesc)
def avc_profile_name(profile):
f = lb4.AP4_AvcSampleDescription_GetProfileName
f.restype = c_char_p
return f(profile)
class AvcSampleDescription(SampleDescription, VideoSampleDescription):
def __init__(self, bt4desc):
bt4videodesc = lb4.AP4_SampleDescription_AsVideo(bt4desc)
super(AvcSampleDescription, self).__init__(bt4desc=bt4desc,
bt4videodesc=bt4videodesc)
self.bt4avcdesc = lb4.AP4_SampleDescription_AsAvc(bt4desc)
@property
def config_version(self):
return lb4.AP4_AvcSampleDescription_GetConfigurationVersion(self.bt4avcdesc)
@property
def profile(self):
return lb4.AP4_AvcSampleDescription_GetProfile(self.bt4avcdesc)
@property
def level(self):
return lb4.AP4_AvcSampleDescription_GetLevel(self.bt4avcdesc)
@property
def profile_compatibility(self):
return lb4.AP4_AvcSampleDescription_GetProfileCompatibility(self.bt4avcdesc)
@property
def nalu_length_size(self):
return lb4.AP4_AvcSampleDescription_GetNaluLengthSize(self.bt4avcdesc)
@property
def sequence_params(self):
result = []
count = lb4.AP4_AvcSampleDescription_GetSequenceParameterCount(self.bt4avcdesc)
for i in xrange(count):
bt4buf = lb4.AP4_AvcSampleDescription_GetSequenceParameter(self.bt4avcdesc, i)
result += [string_at(lb4.AP4_DataBuffer_GetData(bt4buf),
lb4.AP4_DataBuffer_GetDataSize(bt4buf))]
return result
@property
def picture_params(self):
result = []
count = lb4.AP4_AvcSampleDescription_GetPictureParametersCount(self.bt4avcdesc)
for i in xrange(count):
bt4buf = lb4.AP4_AvcSampleDescription_GetPictureParameter(self.bt4avcdesc, i)
result += [string_at(AP4_DataBuffer_GetData(bt4buf),
AP4_DataBuffer_GetDataSize(bt4buf))]
return result
class MpegSystemSampleDescription(MpegSampleDescription):
def __init__(self, bt4desc):
bt4mpegdesc = lb4.AP4_SampleDescription_AsMpeg(bt4desc)
super(MpegSystemSampleDescription, self).__init__(bt4desc=bt4desc,
bt4mpegdesc=bt4mpegdesc)
def mpeg_audio_object_type_string(type):
f = lb4.AP4_MpegAudioSampleDescription_GetMpegAudioObjectTypeString
f.restype = c_char_p
return f(type)
class MpegAudioSampleDescription(MpegSampleDescription,
AudioSampleDescription) :
MPEG4_AUDIO_OBJECT_TYPE_AAC_MAIN = 1 # AAC Main Profile
MPEG4_AUDIO_OBJECT_TYPE_AAC_LC = 2 # AAC Low Complexity
MPEG4_AUDIO_OBJECT_TYPE_AAC_SSR = 3 # AAC Scalable Sample Rate
MPEG4_AUDIO_OBJECT_TYPE_AAC_LTP = 4 # AAC Long Term Predictor
MPEG4_AUDIO_OBJECT_TYPE_SBR = 5 # Spectral Band Replication
MPEG4_AUDIO_OBJECT_TYPE_AAC_SCALABLE = 6 # AAC Scalable
MPEG4_AUDIO_OBJECT_TYPE_TWINVQ = 7 # Twin VQ
MPEG4_AUDIO_OBJECT_TYPE_ER_AAC_LC = 17 # Error Resilient AAC Low Complexity
MPEG4_AUDIO_OBJECT_TYPE_ER_AAC_LTP = 19 # Error Resilient AAC Long Term Prediction
MPEG4_AUDIO_OBJECT_TYPE_ER_AAC_SCALABLE = 20 # Error Resilient AAC Scalable
MPEG4_AUDIO_OBJECT_TYPE_ER_TWINVQ = 21 # Error Resilient Twin VQ
MPEG4_AUDIO_OBJECT_TYPE_ER_BSAC = 22 # Error Resilient Bit Sliced Arithmetic Coding
MPEG4_AUDIO_OBJECT_TYPE_ER_AAC_LD = 23 # Error Resilient AAC Low Delay
MPEG4_AUDIO_OBJECT_TYPE_LAYER_1 = 32 # MPEG Layer 1
MPEG4_AUDIO_OBJECT_TYPE_LAYER_2 = 33 # MPEG Layer 2
MPEG4_AUDIO_OBJECT_TYPE_LAYER_3 = 34 # MPEG Layer 3
def __init__(self, bt4desc):
bt4audiodesc = lb4.AP4_SampleDescription_AsAudio(bt4desc)
bt4mpegdesc = lb4.AP4_SampleDescription_AsMpeg(bt4desc)
super(MpegAudioSampleDescription, self).__init__(bt4desc=bt4desc,
bt4audiodesc=bt4audiodesc,
bt4mpegdesc=bt4mpegdesc)
self.bt4mpegaudiodesc = lb4.AP4_SampleDescription_AsMpegAudio(bt4desc)
@property
def mpeg4_audio_object_type(self):
return lb4.AP4_MpegAudioSampleDescription_GetMpeg4AudioObjecType(self.bt4mpegaudiodesc)
class MpegVideoSampleDescription(MpegSampleDescription,
VideoSampleDescription):
def __init__(self, bt4desc):
bt4videodesc = lb4.AP4_SampleDescription_AsVideo(bt4desc)
bt4mpegdesc = lb4.AP4_SampleDescription_AsMpeg(bt4desc)
super(MpegVideoSampleDescription, self).__init__(bt4desc=bt4desc,
bt4mpegdesc=bt4mpegdesc,
bt4videodesc=bt4videodesc)

View File

@@ -1,61 +0,0 @@
SUCCESS = 0
FAILURE = -1
ERROR_OUT_OF_MEMORY = -2
ERROR_INVALID_PARAMETERS = -3
ERROR_NO_SUCH_FILE = -4
ERROR_PERMISSION_DENIED = -5
ERROR_CANNOT_OPEN_FILE = -6
ERROR_EOS = -7
ERROR_WRITE_FAILED = -8
ERROR_READ_FAILED = -9
ERROR_INVALID_FORMAT = -10
ERROR_NO_SUCH_ITEM = -11
ERROR_OUT_OF_RANGE = -12
ERROR_INTERNAL = -13
ERROR_INVALID_STATE = -14
ERROR_LIST_EMPTY = -15
ERROR_LIST_OPERATION_ABORTED = -16
ERROR_INVALID_RTP_CONSTRUCTOR_TYPE = -17
ERROR_NOT_SUPPORTED = -18
ERROR_INVALID_TRACK_TYPE = -19
ERROR_INVALID_RTP_PACKET_EXTRA_DATA = -20
ERROR_BUFFER_TOO_SMALL = -21
ERROR_NOT_ENOUGH_DATA = -22
RESULT_EXCEPTION_MAP = {
FAILURE: (Exception, ''),
ERROR_OUT_OF_MEMORY: (MemoryError, ''),
ERROR_INVALID_PARAMETERS: (ValueError, 'Invalid parameter '),
ERROR_NO_SUCH_FILE: (IOError, 'No such file '),
ERROR_PERMISSION_DENIED: (IOError, 'Permission denied '),
ERROR_CANNOT_OPEN_FILE: (IOError, 'Cannot open file '),
ERROR_EOS: (EOFError, ''),
ERROR_WRITE_FAILED: (IOError, 'Write failed '),
ERROR_READ_FAILED: (IOError, 'Read failed '),
ERROR_INVALID_FORMAT: (ValueError, 'Invalid format '),
ERROR_NO_SUCH_ITEM: (LookupError, ''),
ERROR_OUT_OF_RANGE: (IndexError, ''),
ERROR_INTERNAL: (RuntimeError, 'Bento4 internal error '),
ERROR_INVALID_STATE: (RuntimeError, 'Bento4 invalid state'),
ERROR_LIST_EMPTY: (IndexError, 'List empty '),
ERROR_LIST_OPERATION_ABORTED: (RuntimeError, 'List operation aborted '),
ERROR_INVALID_RTP_CONSTRUCTOR_TYPE: (ValueError, 'Invalid RTP constructor type '),
ERROR_NOT_SUPPORTED: (NotImplementedError, ''),
ERROR_INVALID_TRACK_TYPE: (ValueError, 'Invalid track type '),
ERROR_INVALID_RTP_PACKET_EXTRA_DATA: (ValueError, 'Invalid Rtp packet extra data '),
ERROR_BUFFER_TOO_SMALL: (MemoryError, 'Buffer too small '),
ERROR_NOT_ENOUGH_DATA: (IOError, 'Not enough data ')
}
def check_result(result, msg=''):
# shortcut
if result == SUCCESS:
return
try:
exception, msg_prefix = RESULT_EXCEPTION_MAP[result]
except KeyError:
raise RuntimeError("Bento4 unknown error: code %d" % result)
raise exception(msg_prefix+msg)

View File

@@ -1,170 +0,0 @@
from bento4 import *
from ctypes import CFUNCTYPE, c_char_p, byref, c_int, c_float, string_at
from xml.etree.ElementTree import Element, SubElement
from base64 import b16encode
class AtomInspector(object):
def __init__(self, bt4inspector):
self.bt4inspector = bt4inspector
super(AtomInspector, self).__init__()
def __del__(self):
lb4.AP4_AtomInspector_Destroy(self.bt4inspector)
class PrintInspector(AtomInspector):
def __init__(self, stream):
self.stream = stream
bt4inspector = lb4.AP4_PrintInspector_Create(stream.bt4stream)
super(PrintInspector, self).__init__(bt4inspector)
class InspectorDelegate(Structure):
pass
start_element_proto = CFUNCTYPE(None,
POINTER(InspectorDelegate),
c_char_p, c_char_p)
end_element_proto = CFUNCTYPE(None,
POINTER(InspectorDelegate))
add_int_field_proto = CFUNCTYPE(None,
POINTER(InspectorDelegate),
c_char_p, Ap4UI64, c_int)
add_float_field_proto = CFUNCTYPE(None,
POINTER(InspectorDelegate),
c_char_p, c_float, c_int)
add_string_field_proto = CFUNCTYPE(None,
POINTER(InspectorDelegate),
c_char_p, c_char_p, c_int)
add_bytes_field_proto = CFUNCTYPE(None,
POINTER(InspectorDelegate),
c_char_p, c_void_p, Ap4Size, c_int)
InspectorDelegate._fields_ = [("start_element", start_element_proto),
("end_element", end_element_proto),
("add_int_field", add_int_field_proto),
("add_float_field", add_float_field_proto),
("add_string_field", add_string_field_proto),
("add_bytes_field", add_bytes_field_proto),
("destroy", c_void_p), # must be set to None
("oid", c_int)] # object id
# global dict of objects constructed with a delegate
pyinspector_objects = {}
#
# redirection functions
#
def delegate_start_element(pdelegate, name, extra):
pyinspector = pyinspector_objects[pdelegate[0].oid]
pyinspector.c_start_element(name, extra)
def delegate_end_element(pdelegate):
pyinspector = pyinspector_objects[pdelegate[0].oid]
pyinspector.c_end_element()
def delegate_add_int_field(pdelegate, name, value, hint):
pyinspector = pyinspector_objects[pdelegate[0].oid]
pyinspector.c_add_int_field(name, value, hint)
def delegate_add_float_field(pdelegate, name, value, hint):
pyinspector = pyinspector_objects[pdelegate[0].oid]
pyinspector.c_add_float_field(name, value, hint)
def delegate_add_string_field(pdelegate, name, value, hint):
pyinspector = pyinspector_objects[pdelegate[0].oid]
pyinspector.c_add_string_field(name, value, hint)
def delegate_add_bytes_field(pdelegate, name, bytes, byte_count, hint):
pyinspector = pyinspector_objects[pdelegate[0].oid]
pyinspector.c_add_bytes_field(name, bytes, byte_count, hint)
class PyInspector(AtomInspector):
def __init__(self, pyinspector):
self.delegate = InspectorDelegate(
start_element=start_element_proto(delegate_start_element),
end_element=end_element_proto(delegate_end_element),
add_int_field=add_int_field_proto(delegate_add_int_field),
add_float_field=add_float_field_proto(delegate_add_float_field),
add_string_field=add_string_field_proto(delegate_add_string_field),
add_bytes_field=add_bytes_field_proto(delegate_add_bytes_field),
destroy=None,
oid=id(pyinspector))
pyinspector_objects[id(pyinspector)] = pyinspector
bt4inspector = lb4.AP4_AtomInspector_FromDelegate(byref(self.delegate))
super(PyInspector, self).__init__(bt4inspector)
def c_start_element(self, name, extra):
pass
def c_end_element(self):
pass
def c_add_int_field(self, name, value, hint):
pass
def c_add_float_field(self, name, value, hint):
pass
def c_add_string_field(self, name, value, hint):
pass
def c_add_bytes_field(self, name, bytes, byte_count, hint):
pass
class XmlInspector(PyInspector):
def __init__(self):
self.root = Element("Mp4File")
self.current = (None, self.root) # parent, element
super(XmlInspector, self).__init__(self)
def c_start_element(self, name, extra):
parent, element = self.current
new_element = SubElement(element, "Atom", name=name[1:-1])
if extra:
a = extra.split('=')
if len(a) == 2:
new_element.attrib[a[0]] = a[1]
self.current = ((parent, element), new_element)
def c_end_element(self):
(grandparent, parent), element = self.current
self.current = (grandparent, parent)
def c_add_int_field(self, name, value, hint):
int_element = SubElement(self.current[1], "Field",
name=name, type='int')
int_element.text = str(value)
def c_add_float_field(self, name, value, hint):
float_element = SubElement(self.current[1], "Field",
name=name, type='float')
float_element.text = str(value)
def c_add_string_field(self, name, value, hint):
str_element = SubElement(self.current[1], "Field",
name=name, type='string')
str_element.text = value
def c_add_bytes_field(self, name, bytes, byte_count, hint):
bytes_element = SubElement(self.current[1], "Field",
name=name, type='bytes')
bytes_element.text = b16encode(string_at(bytes, byte_count))

View File

@@ -1,354 +0,0 @@
from ctypes import c_int, c_double, c_char_p, byref, \
CFUNCTYPE, POINTER, string_at
from bento4 import *
from bento4.errors import check_result, SUCCESS, ERROR_READ_FAILED, FAILURE, \
ERROR_WRITE_FAILED, ERROR_EOS, ERROR_OUT_OF_RANGE, \
ERROR_NOT_SUPPORTED
class ByteStream(object):
"""abstract ByteStream class"""
def __init__(self, bt4stream):
self.bt4stream = bt4stream
super(ByteStream, self).__init__()
def __del__(self):
lb4.AP4_ByteStream_Release(self.bt4stream)
@property
def size(self):
v = Ap4LargeSize()
f = lb4.AP4_ByteStream_GetSize
f.restype = check_result
f(self.bt4stream, byref(v))
return v.value
def read_partial(self, bytes_to_read):
bytes_read = Ap4Size()
p = create_string_buffer(bytes_to_read)
f = lb4.AP4_ByteStream_ReadPartial
f.restype = check_result
f(self.bt4stream, p, Ap4Size(bytes_to_read), byref(bytes_read))
return (p.raw, bytes_read.value)
def read(self, bytes_to_read):
p = create_string_buffer(bytes_to_read)
f = lb4.AP4_ByteStream_Read
f.restype = check_result
f(self.bt4stream, p, Ap4Size(bytes_to_read))
return p.raw
def read_double(self):
v = c_double()
f = lb4.AP4_ByteStream_ReadDouble
f.restype = check_result
f(self.bt4stream, byref(v))
return v.value
def read_ui64(self):
v = Ap4UI64()
f = lb4.AP4_ByteStream_ReadUI64
f.restype = check_result
f(self.bt4stream, byref(v))
return v.value
def read_ui32(self):
v = Ap4UI32()
f = lb4.AP4_ByteStream_ReadUI32
f.restype = check_result
f(self.bt4stream, byref(v))
return v.value
def read_ui24(self):
v = Ap4UI32()
f = lb4.AP4_ByteStream_ReadUI24
f.restype = check_result
f(self.bt4stream, byref(v))
return v.value
def read_ui16(self):
v = Ap4UI16()
f = lb4.AP4_ByteStream_ReadUI16
f.restype = check_result
f(self.bt4stream, byref(v))
return v.value
def read_ui08(self):
v = Ap4UI08()
f = lb4.AP4_ByteStream_ReadUI08
f.restype = check_result
f(self.straem, byref(v))
return v.value
def read_string(self, length):
p = create_string_buffer(length+1)
f = lb4.AP4_ByteStream_ReadString
f.restype = check_result
f(self.bt4stream, p, Ap4Size(length+1))
return p.value
def write_partial(self, buffer):
bytes_written = Ap4Size()
f = lb4.AP4_ByteStream_WritePartial
f.restype = check_result
f(self.bt4stream, c_char_p(buffer),
Ap4Size(len(buffer)),byref(bytes_written))
return bytes_written
def write(self, buffer):
f = lb4.AP4_ByteStream_Write
f.restype = check_result
f(self.bt4stream, c_char_p(buffer), Ap4Size(len(buffer)))
def write_double(self, value):
f = lb4.AP4_ByteStream_WriteDouble
f.restype = check_result
f(self.bt4stream, c_double(value))
def write_ui64(self, value):
f = lb4.AP4_ByteStream_WriteUI64
f.restype = check_result
f(self.bt4stream, Ap4UI64(value))
def write_ui32(self, value):
f = lb4.AP4_ByteStream_WriteUI32
f.restype = check_result
f(self.bt4stream, Ap4UI32(value))
def write_ui24(self, value):
f = lb4.AP4_ByteStream_WriteUI24
f.restype = check_result
f(self.bt4stream, Ap4UI32(value))
def write_ui16(self, value):
f = lb4.AP4_ByteStream_WriteUI16
f.restype = check_result
f(self.bt4stream, Ap4UI16(value))
def write_ui08(self, value):
f = lb4.AP4_ByteStream_WriteUI08
f.restype = check_result
f(self.bt4stream, Ap4UI08(value))
def write_string(self, value):
f = lb4.AP4_ByteStream_Write
f.restype = check_result
f(self.bt4stream, c_char_p(buffer))
def copy_to(self, receiver, size):
f = lb4.AP4_ByteStream_CopyTo
f.restype = check_result
f(self.bt4stream, receiver.stream, AP4_Size(size))
def seek(self, position):
f = lb4.AP4_ByteStream_Seek
f.restype = check_result
f(self.bt4stream, Ap4Position(position))
def flush(self):
f = lb4.AP4_ByteStream_Flush
f.restype = check_result
f(self.bt4stream)
def tell(self):
pos = Ap4Position()
f = lb4.AP4_ByteStream_Tell
f.restype = check_result
f(self.bt4stream, byref(pos))
return pos.value
class MemoryByteStream(ByteStream):
@staticmethod
def from_buffer(buffer):
"""Factory method"""
buffer_size = Ap4Size(len(buffer))
bt4stream = lb4.AP4_MemoryByteStream_FromBuffer(c_char_p(buffer),
buffer_size)
return MemoryByteStream(bt4stream=bt4stream)
def __init__(self, size=0, bt4stream=None):
if bt4stream is None:
bt4stream = lb4.AP4_MemoryByteStream_Create(Ap4Size(size))
super(MemoryByteStream, self).__init__(bt4stream)
class FileByteStream(ByteStream):
MODE_READ = 0
MODE_WRITE = 1
MODE_READ_WRITE = 2
def __init__(self, name, mode):
result = Ap4Result(0)
f = lb4.AP4_FileByteStream_Create
bt4stream = f(c_char_p(name), c_int(mode), byref(result))
check_result(result)
super(FileByteStream, self).__init__(bt4stream)
class ByteStreamDelegate(Structure):
pass
read_partial_proto = CFUNCTYPE(Ap4Result,
POINTER(ByteStreamDelegate),
POINTER(Ap4Byte),
Ap4Size,
POINTER(Ap4Size))
write_partial_proto = CFUNCTYPE(Ap4Result,
POINTER(ByteStreamDelegate),
c_void_p,
Ap4Size,
POINTER(Ap4Size))
seek_proto = CFUNCTYPE(Ap4Result,
POINTER(ByteStreamDelegate),
Ap4Position)
tell_proto = CFUNCTYPE(Ap4Result,
POINTER(ByteStreamDelegate),
POINTER(Ap4Position))
get_size_proto = CFUNCTYPE(Ap4Result,
POINTER(ByteStreamDelegate),
POINTER(Ap4LargeSize))
flush_proto = CFUNCTYPE(Ap4Result,
POINTER(ByteStreamDelegate))
ByteStreamDelegate._fields_ = [("read_partial", read_partial_proto),
("write_partial", write_partial_proto),
("seek", seek_proto),
("tell", tell_proto),
("get_size", get_size_proto),
("flush", flush_proto),
("destroy", c_void_p), # must be set to None
("oid", c_int)] # object id
# dict where we are going to store the pybytestream instances by id
PYSTREAM_OBJECTS = {}
def delegate_read_partial(pdelegate, buffer, bytes_to_read, bytes_read):
pystream = PYSTREAM_OBJECTS[pdelegate[0].oid]
return pystream.c_read_partial(buffer,
bytes_to_read,
bytes_read)
def delegate_read_partial(pdelegate, buffer, bytes_to_write, bytes_written):
pystream = PYSTREAM_OBJECTS[pdelegate[0].oid]
return pystream.c_write_partial(buffer,
bytes_to_write,
bytes_written)
def delegate_seek(pdelegate, position):
pystream = PYSTREAM_OBJECTS[pdelegate[0].oid]
return pystream.c_seek(position)
def delegate_tell(pdelegate, position):
pystream = PYSTREAM_OBJECTS[pdelegate[0].oid]
return pystream.c_tell(position)
def delegate_get_size(pdelegate, size):
pystream = PYSTREAM_OBJECTS[pdelegate[0].oid]
return pystream.c_get_size(size)
def delegate_flush(pdelegate, size):
pystream = PYSTREAM_OBJECTS[pdelegate[0].oid]
return pystream.c_flush()
class PyByteStream(ByteStream):
def __init__(self, pystream):
self.delegate = ByteStreamDelegate(
read_partial=read_partial_proto(delegate_read_partial),
write_partial=write_partial_proto(delegate_write_partial),
seek=seek_proto(delegate_seek),
tell=tell_proto(delegate_tell),
get_size=get_size_proto(delegate_get_size),
flush=flush_proto(delegate_flush),
destroy=None,
oid=id(stream))
PYSTREAM_OBJECTS[id(pystream)] = pystream # store the stream
bt4stream = lb4.AP4_ByteStream_FromDelegate(byref(self.delegate))
super(PyByteStream, self).__init__(bt4stream)
def c_read_partial(self, buffer, bytes_to_read, bytes_read):
return ERROR_NOT_SUPPORTED
def c_write_partial(self, buffer, bytes_to_write, bytes_written):
return ERROR_NOT_SUPPORTED
def c_seek(self, position):
return ERROR_NOT_SUPPORTED
def c_tell(self, position):
return ERROR_NOT_SUPPORTED
def c_get_size(self, size):
return ERROR_NOT_SUPPORTED
def c_flush(self):
return ERROR_NOT_SUPPORTED
class PyFileByteStream(PyByteStream):
def __init__(self, file, size=0):
self.file = file
self.size = size
super(PyFileByteStream, self).__init__(self)
def c_read_partial(self, buffer, bytes_to_read, bytes_read):
try:
s = self.file.read(bytes_to_read)
bytes_read[0] = len(s)
buffer[:bytes_to_read] = s[:bytes_to_read] # copy the buffer
except EOFError:
return ERROR_EOS
except IOError:
return READ_FAILED
return SUCCESS
def c_write_partial(self, buffer, bytes_to_write, bytes_written):
try:
self.file.write(string_at(buffer, bytes_to_write))
bytes_written[0] = bytes_to_write
except IOError:
return WRITE_FAILED
return SUCCESS
def c_seek(self, position):
try:
self.file.seek(position)
except Exception:
return ERROR_OUT_OF_RANGE
return SUCCESS
def c_tell(self, position):
try:
position[0] = self.file.tell()
except Exception:
return FAILURE
return SUCCESS
def c_get_size(self, size):
if self.size:
size[0] = self.size
return SUCCESS
else:
return ERROR_NOT_SUPPORTED
def c_flush(self):
try:
self.file.flush()
except Exception:
return FAILURE
return SUCCESS

View File

@@ -191,7 +191,7 @@ class rijndael:
# decryption round keys
Kd = [[0] * BC for i in xrange(ROUNDS + 1)]
ROUND_KEY_COUNT = (ROUNDS + 1) * BC
KC = len(key) / 4
KC = len(key) // 4
# copy user material bytes into temporary ints
tk = []
@@ -339,14 +339,14 @@ def cbc_encrypt(plaintext, key, IV):
# padding
padding_size = 16-(len(plaintext) % 16)
plaintext += chr(padding_size)*padding_size
# init
chainBytes = IV
cipher = rijndael(key)
ciphertext = ''
# CBC Mode: For each block...
for x in range(len(plaintext)/16):
for x in range(len(plaintext)//16):
# XOR with the chaining block
block = ''.join([chr(ord(plaintext[x*16+y]) ^ ord(chainBytes[y])) for y in range(16)])
@@ -361,12 +361,12 @@ def cbc_decrypt(ciphertext, key, IV):
chainBytes = IV
cipher = rijndael(key)
plaintext = ''
# sanity check
if len(ciphertext)%16: raise ValueError('ciphertext not an integral number of blocks')
# CBC Mode: For each block...
for x in range(len(ciphertext)/16):
for x in range(len(ciphertext)//16):
# Decrypt it
block = ciphertext[x*16 : (x*16)+16]
@@ -381,5 +381,5 @@ def cbc_decrypt(ciphertext, key, IV):
# padding
padding_size = ord(plaintext[-1:])
if padding_size > 16: raise ValueError('invalid padding')
return plaintext[:-padding_size]
return plaintext[:-padding_size]

View File

@@ -17,11 +17,10 @@ __copyright__ = 'Copyright 2011-2012 Axiomatic Systems, LLC.'
import sys
import os
import os.path
from optparse import OptionParser, make_option, OptionError
from optparse import OptionParser
import urllib2
import urlparse
import shutil
import itertools
import json
import sys
from xml.etree import ElementTree
@@ -45,11 +44,11 @@ def Bento4Command(name, *args, **kwargs):
cmd += args
#print cmd
try:
return check_output(cmd)
return check_output(cmd)
except CalledProcessError, e:
#print e
raise Exception("binary tool failed with error %d" % e.returncode)
def Mp4Info(filename, **args):
return Bento4Command('mp4info', filename, **args)
@@ -60,7 +59,7 @@ def GetTrackIds(mp4):
for track in info['tracks']:
track_ids.append(track['id'])
return track_ids
def ProcessUrlTemplate(template, representation_id, bandwidth, time, number):
@@ -80,7 +79,7 @@ def ProcessUrlTemplate(template, representation_id, bandwidth, time, number):
if time is not None: result = result.replace('$Time$', time)
result = result.replace('$$', '$')
return result
class DashSegmentBaseInfo:
def __init__(self, xml):
self.initialization = None
@@ -89,21 +88,21 @@ class DashSegmentBaseInfo:
e = xml.find(DASH_NS+type)
if e is not None:
self.type = type
# parse common elements
# type specifics
if type == 'SegmentBase' or type == 'SegmentList':
init = e.find(DASH_NS+'Initialization')
if init is not None:
self.initialization = init.get('sourceURL')
if type == 'SegmentTemplate':
self.initialization = e.get('initialization')
self.media = e.get('media')
self.timescale = e.get('timescale')
self.startNumber = e.get('startNumber')
# segment timeline
st = e.find(DASH_NS+'SegmentTimeline')
if st is not None:
@@ -123,9 +122,9 @@ class DashSegmentBaseInfo:
else:
item['r'] = 0
self.segment_timeline.append(item)
break
class DashRepresentation:
def __init__(self, xml, parent):
self.xml = xml
@@ -134,23 +133,23 @@ class DashRepresentation:
self.segment_urls = []
self.segment_base = DashSegmentBaseInfo(xml)
self.duration = 0
# parse standard attributes
self.bandwidth = xml.get('bandwidth')
self.id = xml.get('id')
# compute the segment base type
node = self
self.segment_base_type = None
while node is not None:
if node.segment_base.type in ['SegmentTemplate', 'SegmentList']:
self.segment_base_type = node.segment_base.type
break
node = node.parent
break
node = node.parent
# compute the init segment URL
self.ComputeInitSegmentUrl()
def SegmentBaseLookup(self, field):
node = self
while node is not None:
@@ -166,29 +165,29 @@ class DashRepresentation:
return node.__dict__[field]
node = node.parent
return None
def ComputeInitSegmentUrl(self):
node = self
while node is not None:
if node.segment_base.initialization is not None:
self.initialization = node.segment_base.initialization
break
break
node = node.parent
self.init_segment_url = ProcessUrlTemplate(self.initialization, representation_id=self.id, bandwidth=self.bandwidth, time=None, number=None)
def GenerateSegmentUrls(self):
if self.segment_base_type == 'SegmentTemplate':
return self.GenerateSegmentUrlsFromTemplate()
else:
return self.GenerateSegmentUrlsFromList()
def GenerateSegmentUrlsFromTemplate(self):
media = self.SegmentBaseLookup('media')
if media is None:
print 'WARNING: no media attribute found for representation'
return
timeline = self.SegmentBaseLookup('segment_timeline')
if timeline is None:
start = self.SegmentBaseLookup('startNumber')
@@ -200,7 +199,7 @@ class DashRepresentation:
url = ProcessUrlTemplate(media, representation_id=self.id, bandwidth=self.bandwidth, time="0", number=str(current_number))
current_number += 1
yield url
else:
current_number = 1
current_time = 0
@@ -212,14 +211,14 @@ class DashRepresentation:
current_number += 1
current_time += s['d']
yield url
def GenerateSegmentUrlsFromList(self):
segs = self.xml.find(DASH_NS+'SegmentList').findall(DASH_NS+'SegmentURL')
for seg in segs:
media = seg.get('media')
if media is not None:
yield media
def __str__(self):
result = "Representation: "
return result
@@ -232,7 +231,7 @@ class DashAdaptationSet:
self.representations = []
for r in self.xml.findall(DASH_NS+'Representation'):
self.representations.append(DashRepresentation(r, self))
def __str__(self):
result = 'Adaptation Set:\n' + '\n'.join([str (r) for r in self.representations])
return result
@@ -245,11 +244,11 @@ class DashPeriod:
self.adaptation_sets = []
for s in self.xml.findall(DASH_NS+'AdaptationSet'):
self.adaptation_sets.append(DashAdaptationSet(s, self))
def __str__(self):
result = 'Period:\n' + '\n'.join([str(s) for s in self.adaptation_sets])
return result
class DashMPD:
def __init__(self, url, xml):
self.url = url
@@ -260,17 +259,17 @@ class DashMPD:
self.type = xml.get('type')
for p in self.xml.findall(DASH_NS+'Period'):
self.periods.append(DashPeriod(p, self))
# compute base URL (note: we'll just use the MPD URL for now)
self.base_urls = [url]
self.base_urls = [url]
base_url = self.xml.find(DASH_NS+'BaseURL')
if base_url is not None:
self.base_urls = [base_url.text]
def __str__(self):
result = "MPD:\n" + '\n'.join([str(p) for p in self.periods])
return result
def ParseMpd(url, xml):
mpd_tree = ElementTree.XML(xml)
if mpd_tree.tag.startswith(DASH_NS_COMPAT):
@@ -280,14 +279,14 @@ def ParseMpd(url, xml):
DASH_NS_URN = DASH_NS_URN_COMPAT
if Options.verbose:
print '@@@ Using backward compatible namespace'
mpd = DashMPD(url, mpd_tree)
if not (mpd.type is None or mpd.type == 'static'):
raise Exception('Only static MPDs are supported')
return mpd
def MakeNewDir(dir, is_warning=False):
if os.path.exists(dir):
if is_warning:
@@ -309,25 +308,25 @@ def OpenURL(url):
def ComputeUrl(base_url, url):
if url.startswith('http://') or url.startswith('https://'):
raise Exception('Absolute URLs are not supported')
if base_url.startswith('file://'):
return os.path.join(os.path.dirname(base_url), url)
else:
return urlparse.urljoin(base_url, url)
class Cloner:
def __init__(self, root_dir):
self.root_dir = root_dir
self.track_ids = []
self.init_filename = None
def CloneSegment(self, url, path_out, is_init):
while path_out.startswith('/'):
path_out = path_out[1:]
target_dir = os.path.join(self.root_dir, path_out)
if Options.verbose:
print 'Cloning', url, 'to', path_out
#os.makedirs(target_dir)
try:
os.makedirs(os.path.dirname(target_dir))
@@ -335,8 +334,8 @@ class Cloner:
if os.path.exists(target_dir):
pass
except:
raise
raise
data = OpenURL(url)
outfile_name = os.path.join(self.root_dir, path_out)
use_temp_file = False
@@ -348,7 +347,7 @@ class Cloner:
try:
shutil.copyfileobj(data, outfile)
outfile.close()
if Options.encrypt:
if is_init:
self.track_ids = GetTrackIds(outfile_name)
@@ -366,18 +365,18 @@ class Cloner:
if not is_init:
args += ["--fragments-info", self.init_filename]
if Options.verbose:
print 'mp4encrypt '+(' '.join(args))
Bento4Command("mp4encrypt", *args)
finally:
if use_temp_file and not is_init:
os.unlink(outfile_name)
def Cleanup(self):
if (self.init_filename):
os.unlink(self.init_filename)
def main():
# determine the platform binary name
platform = sys.platform
@@ -391,19 +390,19 @@ def main():
parser.add_option('', '--quiet', dest="verbose",
action='store_false', default=True,
help="Be quiet")
parser.add_option('', "--encrypt", metavar='<KID:KEY>',
parser.add_option('', "--encrypt", metavar='<KID:KEY>',
dest='encrypt', default=None,
help="Encrypt the media, with KID and KEY specified in Hex (32 characters each)")
help="Encrypt the media, with KID and KEY specified in Hex (32 characters each)")
parser.add_option('', "--exec-dir", metavar="<exec_dir>",
dest="exec_dir", default=os.path.join(SCRIPT_PATH, 'bin', platform),
help="Directory where the Bento4 executables are located")
help="Directory where the Bento4 executables are located")
global Options
(Options, args) = parser.parse_args()
if len(args) != 2:
parser.print_help()
sys.exit(1)
# process arguments
mpd_url = args[0]
output_dir = args[1]
@@ -411,11 +410,11 @@ def main():
if len(Options.encrypt) != 65:
raise Exception('Invalid argument for --encrypt option')
Options.kid = Options.encrypt[:32].decode('hex')
Options.key = Options.encrypt[33:].decode('hex')
Options.key = Options.encrypt[33:].decode('hex')
# create the output dir
MakeNewDir(output_dir, True)
# load and parse the MPD
if Options.verbose: print "Loading MPD from", mpd_url
try:
@@ -423,11 +422,11 @@ def main():
except Exception as e:
print "ERROR: failed to load MPD:", e
sys.exit(1)
if Options.verbose: print "Parsing MPD"
mpd_xml = mpd_xml.replace('nitialisation', 'nitialization')
mpd = ParseMpd(mpd_url, mpd_xml)
ElementTree.register_namespace('', DASH_NS_URN)
ElementTree.register_namespace('mas', MARLIN_MAS_NS_URN)
@@ -439,14 +438,14 @@ def main():
base_url = representation.AttributeLookup('base_urls')[0]
if Options.verbose:
print 'Base URL = '+base_url
# process the init segment
if Options.verbose:
print '### Processing Initialization Segment'
url = ComputeUrl(base_url, representation.init_segment_url)
cloner.CloneSegment(url, representation.init_segment_url, True)
# process all segment URLs
# process all segment URLs
if Options.verbose:
print '### Processing Media Segments for AdaptationSet', representation.id
for seg_url in representation.GenerateSegmentUrls():
@@ -456,10 +455,10 @@ def main():
except (urllib2.HTTPError, urllib2.URLError, IOError):
# move to the next representation
break
# cleanup the init segment
# cleanup the init segment
cloner.Cleanup()
# modify the MPD if needed
if Options.encrypt:
for p in mpd.xml.findall(DASH_NS+'Period'):
@@ -470,13 +469,12 @@ def main():
cid = ElementTree.SubElement(cids, MARLIN_MAS_NS+'MarlinContentId')
cid.text = 'urn:marlin:kid:'+Options.kid.encode('hex')
s.insert(0, cp)
# write the MPD
# write the MPD
xml_tree = ElementTree.ElementTree(mpd.xml)
xml_tree.write(os.path.join(output_dir, os.path.basename(urlparse.urlparse(mpd_url).path)), encoding="UTF-8", xml_declaration=True)
###########################
###########################
SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__))
if __name__ == '__main__':
main()

View File

@@ -210,8 +210,7 @@ def main():
###########################
if __name__ == '__main__':
global Options
Options = None
Options = None # global
try:
main()
except Exception, err:

View File

@@ -18,7 +18,6 @@ import shutil
import xml.etree.ElementTree as xml
from xml.dom.minidom import parseString
import tempfile
import fractions
import re
import platform
import sys
@@ -160,14 +159,11 @@ def AddSegmentTemplate(options, container, init_segment_url, media_url_template_
if options.use_segment_timeline or track.type == 'subtitles':
url_template = SEGMENT_URL_TEMPLATE
use_template_numbers = True
if options.smooth:
url_base = path.basename(options.smooth_server_manifest_filename)
url_template = url_base + DASH_MEDIA_SEGMENT_URL_PATTERN_SMOOTH % stream_name
use_template_numbers = False
elif options.hippo:
url_template = DASH_MEDIA_SEGMENT_URL_PATTERN_HIPPO % stream_name
use_template_numbers = False
args = [container, 'SegmentTemplate']
kwargs = {'timescale': str(track.timescale),
@@ -478,10 +474,10 @@ def OutputDash(options, set_attributes, audio_sets, video_sets, subtitles_sets,
else:
audio_channel_config_value = str(audio_track.channels)
scheme_id_uri = MPEG_DASH_AUDIO_CHANNEL_CONFIGURATION_SCHEME_ID_URI
audio_channel_config = xml.SubElement(representation,
'AudioChannelConfiguration',
schemeIdUri=scheme_id_uri,
value=audio_channel_config_value)
xml.SubElement(representation,
'AudioChannelConfiguration',
schemeIdUri=scheme_id_uri,
value=audio_channel_config_value)
if options.on_demand:
base_url = xml.SubElement(representation, 'BaseURL')
@@ -584,7 +580,7 @@ def ComputeHlsWidevineKeyLine(options, track):
fields[name] = value
except:
raise Exception('invalid syntax for --widevine-header option')
if 'content_id' not in fields:
fields['content_id'] = '*'
if 'kid' not in fields:
@@ -627,7 +623,7 @@ def OutputHlsCommon(options, track, media_subdir, playlist_name, media_file_name
if len(key_lines) == 0:
key_lines.append('URI="'+options.hls_key_url+'",IV=0x'+track.key_info['iv'])
for key_line in key_lines:
playlist_file.write('#EXT-X-KEY:METHOD=SAMPLE-AES,'+key_line+'\r\n')
@@ -692,12 +688,12 @@ def OutputHlsIframeIndex(options, track, media_subdir, iframes_playlist_name, me
index_playlist_file.write('#EXTINF:%f,\r\n' % (track.segment_durations[i]))
index_playlist_file.write('#EXT-X-BYTERANGE:%d@0\r\n' % (iframe_range_size))
index_playlist_file.write(fragment_basename+'\r\n')
index_playlist_file.write('#EXT-X-ENDLIST\r\n')
#############################################
def OutputHls(options, set_attributes, audio_sets, video_sets, subtitles_sets, subtitles_files):
all_audio_tracks = sum(audio_sets.values(), [])
# all_audio_tracks = sum(audio_sets.values(), [])
all_video_tracks = sum(video_sets.values(), [])
all_subtitles_tracks = sum(subtitles_sets.values(), [])
@@ -817,21 +813,21 @@ def OutputHls(options, set_attributes, audio_sets, video_sets, subtitles_sets, s
continue
language = subtitles_track.language.decode('utf-8')
language_name = LanguageNames.get(language, language).decode('utf-8')
if options.on_demand or not options.split:
media_subdir = ''
media_file_name = subtitles_track.parent.media_name
# media_file_name = subtitles_track.parent.media_name
media_playlist_name = subtitles_track.representation_id+".m3u8"
media_playlist_path = media_playlist_name
else:
media_subdir = subtitles_track.representation_id
media_file_name = ''
# media_file_name = ''
media_playlist_name = options.hls_media_playlist_name
media_playlist_path = media_subdir+'/'+media_playlist_name
master_playlist_file.write('#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="imsc1",NAME="{0:s}",DEFAULT=NO,AUTOSELECT=YES,LANGUAGE="{1:s}",URI="{2:s}"\r\n'
.format(language_name, language, media_playlist_path))
# WebVTT subtitles
if len(subtitles_files):
master_playlist_file.write('\r\n# Subtitles (WebVTT)\r\n')
@@ -1478,7 +1474,7 @@ def main():
if options.fairplay_key_uri:
if not options.hls:
sys.stderr.write('WARNING: --fairplay-key-uri is only valid with --hls, ignoring\n')
if options.hls:
if options.encryption_key and options.encryption_cenc_scheme != 'cbcs':
raise Exception('--hls requires --encryption-cenc-scheme=cbcs')

View File

@@ -15,11 +15,6 @@ __copyright__ = 'Copyright 2011-2015 Axiomatic Systems, LLC.'
from optparse import OptionParser
import shutil
import xml.etree.ElementTree as xml
from xml.dom.minidom import parseString
import tempfile
import fractions
import re
import platform
import sys
from mp4utils import *
@@ -254,7 +249,6 @@ def OutputHls(options, media_sources):
AnalyzeSources(options, media_sources)
# select audio tracks
audio_media = []
audio_tracks = SelectAudioTracks(options, [media_source for media_source in mp4_sources if not media_source.spec.get('+audio_fallback')])
# check if this is an audio-only presentation

View File

@@ -235,9 +235,9 @@ def PrintErrorAndExit(message):
sys.exit(1)
def XmlDuration(d):
h = int(d)/3600
h = int(d) // 3600
d -= h*3600
m = int(d)/60
m = int(d) // 60
s = d-m*60
xsd = 'PT'
if h:
@@ -330,7 +330,7 @@ def WalkAtoms(filename, until=None):
atoms.append(Mp4Atom(type, size, cursor))
cursor += size
file.seek(cursor)
except:
except Exception:
break
return atoms
@@ -471,7 +471,6 @@ class Mp4Track:
moov = FilterChildren(self.parent.tree, 'moov')[0]
traks = FilterChildren(moov, 'trak')
for trak in traks:
tkhd = FindChild(trak, ['tkhd'])
tenc = FindChild(trak, ('mdia', 'minf', 'stbl', 'stsd', 'encv', 'sinf', 'schi', 'tenc'))
if tenc is None:
tenc = FindChild(trak, ('mdia', 'minf', 'stbl', 'stsd', 'enca', 'sinf', 'schi', 'tenc'))
@@ -1060,8 +1059,6 @@ def ComputePlayReadyHeader(header_spec, kid_hex, key_hex):
header_xml += '</DATA></WRMHEADER>'
return WrapPlayreadyHeaderXml(header_xml)
return ""
def ComputePrimetimeMetaData(metadata_spec, kid_hex):
# construct the base64 header
if metadata_spec is None:
@@ -1142,5 +1139,3 @@ def ComputeWidevineHeader(header_spec, kid_hex):
if 'policy' in fields:
protobuf_fields.append((6, fields['policy']))
return WidevineMakeHeader(protobuf_fields)
return ""

View File

@@ -1,5 +1,4 @@
import aes
import urllib2
import os
import hashlib
import json
@@ -23,8 +22,8 @@ def WrapKey(key, kek):
# Inputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}, and
# Key, K (the KEK).
# Outputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}.
n = len(key)/8;
if n < 1:
n = len(key) // 8;
if n < 1:
raise Exception('key too short')
# 1) Initialize variables.
@@ -72,7 +71,7 @@ def UnwrapKey(key, kek):
# Inputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn}, and
# Key, K (the KEK).
# Outputs: Plaintext, n 64-bit values {P0, P1, K, Pn}.
n = len(key)/8 - 1;
n = len(key) // 8 - 1;
if n < 1:
raise Exception('wrapped key too short');
@@ -174,7 +173,7 @@ def ResolveKey(options, spec):
if options.debug:
print 'Request:', base_url, json.dumps(key_object)
response = requests.post(base_url, headers={'content-type': 'application/json'}, data=json.dumps(key_object))
else:
raise Exception('Unsupported SKM query mode')