mirror of
https://github.com/axiomatic-systems/Bento4.git
synced 2026-01-12 00:18:59 +08:00
fix some lgtm warnings
This commit is contained in:
@@ -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:'
|
||||
|
||||
@@ -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:'
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import glob
|
||||
import datetime
|
||||
import fnmatch
|
||||
import zipfile
|
||||
import re
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import platform
|
||||
|
||||
#############################################################
|
||||
# Main
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -210,8 +210,7 @@ def main():
|
||||
|
||||
###########################
|
||||
if __name__ == '__main__':
|
||||
global Options
|
||||
Options = None
|
||||
Options = None # global
|
||||
try:
|
||||
main()
|
||||
except Exception, err:
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user