%PDF- %PDF-
Direktori : /home/waritko/build/Bento4/Source/Python/bento4/ |
Current File : //home/waritko/build/Bento4/Source/Python/bento4/core.py |
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)