framereader only has to support h265 and raw (#1588)
	
		
	
				
					
				
			
							parent
							
								
									44851ae934
								
							
						
					
					
						commit
						639cf2288f
					
				
				 6 changed files with 152 additions and 1729 deletions
			
			
		
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						| @ -1,24 +0,0 @@ | ||||
| Simple easy-to-use hacky matroska parser | ||||
| 
 | ||||
| Define your handler class: | ||||
| 
 | ||||
|     class MyMatroskaHandler(mkvparse.MatroskaHandler): | ||||
|         def tracks_available(self): | ||||
|             ... | ||||
| 
 | ||||
|         def segment_info_available(self): | ||||
|             ... | ||||
| 
 | ||||
|         def frame(self, track_id, timestamp, data, more_laced_blocks, duration, keyframe_flag, invisible_flag, discardable_flag): | ||||
|             ... | ||||
| 
 | ||||
| and `mkvparse.mkvparse(file, MyMatroskaHandler())` | ||||
| 
 | ||||
| 
 | ||||
| Supports lacing and setting global timecode scale, subtitles (BlockGroup). Does not support cues, tags, chapters, seeking and so on. Supports resyncing when something bad is encountered in matroska stream. | ||||
| 
 | ||||
| Also contains example of generation of Matroska files from python | ||||
| 
 | ||||
| Subtitles should remain as text, binary data gets encoded to hex. | ||||
| 
 | ||||
| Licence=MIT | ||||
| @ -1,187 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| import sys | ||||
| import random | ||||
| 
 | ||||
| # Simple hacky Matroska generator | ||||
| # Reads mp3 file "q.mp3" and jpeg images from img/0.jpg, img/1.jpg and so on and | ||||
| # writes Matroska file with mjpeg and mp3 to stdout | ||||
| 
 | ||||
| # License=MIT | ||||
| 
 | ||||
| # unsigned | ||||
| def big_endian_number(number): | ||||
|     if(number<0x100): | ||||
|         return chr(number) | ||||
|     return big_endian_number(number>>8) + chr(number&0xFF) | ||||
| 
 | ||||
| ben=big_endian_number | ||||
| 
 | ||||
| def ebml_encode_number(number): | ||||
|     def trailing_bits(rest_of_number, number_of_bits): | ||||
|         # like big_endian_number, but can do padding zeroes | ||||
|         if number_of_bits==8: | ||||
|             return chr(rest_of_number&0xFF); | ||||
|         else: | ||||
|             return trailing_bits(rest_of_number>>8, number_of_bits-8) + chr(rest_of_number&0xFF) | ||||
| 
 | ||||
|     if number == -1: | ||||
|         return chr(0xFF) | ||||
|     if number < 2**7 - 1: | ||||
|         return chr(number|0x80) | ||||
|     if number < 2**14 - 1: | ||||
|         return chr(0x40 | (number>>8)) + trailing_bits(number, 8) | ||||
|     if number < 2**21 - 1: | ||||
|         return chr(0x20 | (number>>16)) + trailing_bits(number, 16) | ||||
|     if number < 2**28 - 1: | ||||
|         return chr(0x10 | (number>>24)) + trailing_bits(number, 24) | ||||
|     if number < 2**35 - 1: | ||||
|         return chr(0x08 | (number>>32)) + trailing_bits(number, 32) | ||||
|     if number < 2**42 - 1: | ||||
|         return chr(0x04 | (number>>40)) + trailing_bits(number, 40) | ||||
|     if number < 2**49 - 1: | ||||
|         return chr(0x02 | (number>>48)) + trailing_bits(number, 48) | ||||
|     if number < 2**56 - 1: | ||||
|         return chr(0x01) + trailing_bits(number, 56) | ||||
|     raise Exception("NUMBER TOO BIG") | ||||
| 
 | ||||
| def ebml_element(element_id, data, length=None): | ||||
|     if length==None: | ||||
|         length = len(data) | ||||
|     return big_endian_number(element_id) + ebml_encode_number(length) + data | ||||
| 
 | ||||
| 
 | ||||
| def write_ebml_header(f, content_type, version, read_version): | ||||
|     f.write( | ||||
|         ebml_element(0x1A45DFA3, "" # EBML | ||||
|             + ebml_element(0x4286, ben(1))   # EBMLVersion | ||||
|             + ebml_element(0x42F7, ben(1))   # EBMLReadVersion | ||||
|             + ebml_element(0x42F2, ben(4))   # EBMLMaxIDLength | ||||
|             + ebml_element(0x42F3, ben(8))   # EBMLMaxSizeLength | ||||
|             + ebml_element(0x4282, content_type) # DocType | ||||
|             + ebml_element(0x4287, ben(version))   # DocTypeVersion | ||||
|             + ebml_element(0x4285, ben(read_version))   # DocTypeReadVersion | ||||
|             )) | ||||
| 
 | ||||
| def write_infinite_segment_header(f): | ||||
|     # write segment element header | ||||
|     f.write(ebml_element(0x18538067,"",-1)) # Segment (unknown length) | ||||
| 
 | ||||
| def random_uid(): | ||||
|     def rint(): | ||||
|         return int(random.random()*(0x100**4)) | ||||
|     return ben(rint()) + ben(rint()) + ben(rint()) + ben(rint()) | ||||
| 
 | ||||
| 
 | ||||
| def example(): | ||||
|     write_ebml_header(sys.stdout, "matroska", 2, 2) | ||||
|     write_infinite_segment_header(sys.stdout) | ||||
| 
 | ||||
| 
 | ||||
|     # write segment info (optional) | ||||
|     sys.stdout.write(ebml_element(0x1549A966, "" # SegmentInfo | ||||
|         + ebml_element(0x73A4, random_uid()) # SegmentUID | ||||
|         + ebml_element(0x7BA9, "mkvgen.py test") # Title | ||||
|         + ebml_element(0x4D80, "mkvgen.py") # MuxingApp | ||||
|         + ebml_element(0x5741, "mkvgen.py") # WritingApp | ||||
|         )) | ||||
| 
 | ||||
|     # write trans data (codecs etc.) | ||||
|     sys.stdout.write(ebml_element(0x1654AE6B, "" # Tracks | ||||
|         + ebml_element(0xAE, "" # TrackEntry | ||||
|             + ebml_element(0xD7, ben(1)) # TrackNumber | ||||
|             + ebml_element(0x73C5, ben(0x77)) # TrackUID | ||||
|             + ebml_element(0x83, ben(0x01)) # TrackType | ||||
|                 #0x01 track is a video track | ||||
|                 #0x02 track is an audio track | ||||
|                 #0x03 track is a complex track, i.e. a combined video and audio track | ||||
|                 #0x10 track is a logo track | ||||
|                 #0x11 track is a subtitle track | ||||
|                 #0x12 track is a button track | ||||
|                 #0x20 track is a control track | ||||
|             + ebml_element(0x536E, "mjpeg data") # Name | ||||
|             + ebml_element(0x86, "V_MJPEG") # CodecID | ||||
|             #+ ebml_element(0x23E383, ben(100000000)) # DefaultDuration (opt.), nanoseconds | ||||
|             #+ ebml_element(0x6DE7, ben(100)) # MinCache | ||||
|             + ebml_element(0xE0, "" # Video | ||||
|                 + ebml_element(0xB0, ben(640)) # PixelWidth | ||||
|                 + ebml_element(0xBA, ben(480)) # PixelHeight | ||||
|                 ) | ||||
|             ) | ||||
|         + ebml_element(0xAE, "" # TrackEntry | ||||
|             + ebml_element(0xD7, ben(2)) # TrackNumber | ||||
|             + ebml_element(0x73C5, ben(0x78)) # TrackUID | ||||
|             + ebml_element(0x83, ben(0x02)) # TrackType | ||||
|                 #0x01 track is a video track | ||||
|                 #0x02 track is an audio track | ||||
|                 #0x03 track is a complex track, i.e. a combined video and audio track | ||||
|                 #0x10 track is a logo track | ||||
|                 #0x11 track is a subtitle track | ||||
|                 #0x12 track is a button track | ||||
|                 #0x20 track is a control track | ||||
|             + ebml_element(0x536E, "content of mp3 file") # Name | ||||
|             #+ ebml_element(0x6DE7, ben(100)) # MinCache | ||||
|             + ebml_element(0x86, "A_MPEG/L3") # CodecID | ||||
|             #+ ebml_element(0xE1, "") # Audio | ||||
|          ) | ||||
|         )) | ||||
| 
 | ||||
| 
 | ||||
|     mp3file = open("q.mp3", "rb") | ||||
|     mp3file.read(500000); | ||||
| 
 | ||||
|     def mp3framesgenerator(f): | ||||
|         debt="" | ||||
|         while True: | ||||
|             for i in range(0,len(debt)+1): | ||||
|                 if i >= len(debt)-1: | ||||
|                     debt = debt + f.read(8192) | ||||
|                     break | ||||
|                 #sys.stderr.write("i="+str(i)+" len="+str(len(debt))+"\n") | ||||
|                 if ord(debt[i])==0xFF and (ord(debt[i+1]) & 0xF0)==0XF0 and i>700: | ||||
|                     if i>0: | ||||
|                         yield debt[0:i] | ||||
|                         #   sys.stderr.write("len="+str(i)+"\n") | ||||
|                         debt = debt[i:] | ||||
|                         break | ||||
| 
 | ||||
| 
 | ||||
|     mp3 = mp3framesgenerator(mp3file) | ||||
|     next(mp3) | ||||
| 
 | ||||
| 
 | ||||
|     for i in range(0,530): | ||||
|         framefile = open("img/"+str(i)+".jpg", "rb") | ||||
|         framedata = framefile.read() | ||||
|         framefile.close() | ||||
| 
 | ||||
|         # write cluster (actual video data) | ||||
| 
 | ||||
|         if random.random()<1: | ||||
|             sys.stdout.write(ebml_element(0x1F43B675, "" # Cluster | ||||
|                 + ebml_element(0xE7, ben(int(i*26*4))) # TimeCode, uint, milliseconds | ||||
|                 # + ebml_element(0xA7, ben(0)) # Position, uint | ||||
|                 + ebml_element(0xA3, "" # SimpleBlock | ||||
|                     + ebml_encode_number(1) # track number | ||||
|                     + chr(0x00) + chr(0x00) # timecode, relative to Cluster timecode, sint16, in milliseconds | ||||
|                     + chr(0x00) # flags | ||||
|                     + framedata | ||||
|                     ))) | ||||
| 
 | ||||
|         for u in range(0,4): | ||||
|             mp3f=next(mp3) | ||||
|             if random.random()<1: | ||||
|                 sys.stdout.write(ebml_element(0x1F43B675, "" # Cluster | ||||
|                     + ebml_element(0xE7, ben(i*26*4+u*26)) # TimeCode, uint, milliseconds | ||||
|                     + ebml_element(0xA3, "" # SimpleBlock | ||||
|                         + ebml_encode_number(2) # track number | ||||
|                         + chr(0x00) + chr(0x00) # timecode, relative to Cluster timecode, sint16, in milliseconds | ||||
|                         + chr(0x00) # flags | ||||
|                         + mp3f | ||||
|                         ))) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     example() | ||||
| @ -1,64 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| # flake8: noqa | ||||
| 
 | ||||
| import re | ||||
| import binascii | ||||
| 
 | ||||
| from tools.lib.mkvparse import mkvparse | ||||
| from tools.lib.mkvparse import mkvgen | ||||
| from tools.lib.mkvparse.mkvgen import ben, ebml_element, ebml_encode_number | ||||
| 
 | ||||
| class MatroskaIndex(mkvparse.MatroskaHandler): | ||||
|   # def __init__(self, banlist, nocluster_mode): | ||||
|   #   pass | ||||
|   def __init__(self): | ||||
|     self.frameindex = [] | ||||
| 
 | ||||
|   def tracks_available(self): | ||||
|     _, self.config_record = self.tracks[1]['CodecPrivate']  # pylint: disable=no-member | ||||
| 
 | ||||
|   def frame(self, track_id, timestamp, pos, length, more_laced_frames, duration, | ||||
|         keyframe, invisible, discardable): | ||||
|     self.frameindex.append((pos, length, keyframe)) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def mkvindex(f): | ||||
|   handler = MatroskaIndex() | ||||
|   mkvparse.mkvparse(f, handler) | ||||
|   return handler.config_record, handler.frameindex | ||||
| 
 | ||||
| 
 | ||||
| def simple_gen(of, config_record, w, h, framedata): | ||||
|   mkvgen.write_ebml_header(of, "matroska", 2, 2) | ||||
|   mkvgen.write_infinite_segment_header(of) | ||||
| 
 | ||||
|   of.write(ebml_element(0x1654AE6B, "" # Tracks | ||||
|     + ebml_element(0xAE, "" # TrackEntry | ||||
|       + ebml_element(0xD7, ben(1)) # TrackNumber | ||||
|       + ebml_element(0x73C5, ben(1)) # TrackUID | ||||
|       + ebml_element(0x83, ben(1)) # TrackType = video track | ||||
|       + ebml_element(0x86, "V_MS/VFW/FOURCC") # CodecID | ||||
|       + ebml_element(0xE0, "" # Video | ||||
|         + ebml_element(0xB0, ben(w)) # PixelWidth | ||||
|         + ebml_element(0xBA, ben(h)) # PixelHeight | ||||
|         ) | ||||
|       + ebml_element(0x63A2, config_record) # CodecPrivate (ffv1 configuration record) | ||||
|       ) | ||||
|     )) | ||||
| 
 | ||||
|   blocks = [] | ||||
|   for fd in framedata: | ||||
|     blocks.append( | ||||
|       ebml_element(0xA3, "" # SimpleBlock | ||||
|         + ebml_encode_number(1) # track number | ||||
|         + chr(0x00) + chr(0x00) # timecode, relative to Cluster timecode, sint16, in milliseconds | ||||
|         + chr(0x80) # flags (keyframe) | ||||
|         + fd | ||||
|         ) | ||||
|       ) | ||||
| 
 | ||||
|   of.write(ebml_element(0x1F43B675, "" # Cluster | ||||
|     + ebml_element(0xE7, ben(0)) # TimeCode, uint, milliseconds | ||||
|     # + ebml_element(0xA7, ben(0)) # Position, uint | ||||
|     + ''.join(blocks))) | ||||
| @ -1,763 +0,0 @@ | ||||
| # Licence==MIT; Vitaly "_Vi" Shukela 2012 | ||||
| 
 | ||||
| # Simple easy-to-use hacky matroska parser | ||||
| 
 | ||||
| # Supports SimpleBlock and BlockGroup, lacing, TimecodeScale. | ||||
| # Does not support seeking, cues, chapters and other features. | ||||
| # No proper EOF handling unfortunately | ||||
| 
 | ||||
| # See "mkvuser.py" for the example | ||||
| # pylint: skip-file | ||||
| # flake8: noqa | ||||
| 
 | ||||
| import traceback | ||||
| from struct import unpack | ||||
| 
 | ||||
| import sys | ||||
| import datetime | ||||
| 
 | ||||
| if sys.version < '3': | ||||
|     range=xrange # pylint disable=undefined-variable | ||||
| else: | ||||
|     #identity=lambda x:x | ||||
|     def ord(something): | ||||
|         if type(something)==bytes: | ||||
|             if something == b"": | ||||
|                 raise StopIteration | ||||
|             return something[0] | ||||
|         else: | ||||
|             return something | ||||
| 
 | ||||
| def get_major_bit_number(n): | ||||
|     ''' | ||||
|         Takes uint8, returns number of the most significant bit plus the number with that bit cleared. | ||||
|         Examples: | ||||
|         0b10010101 -> (0, 0b00010101) | ||||
|         0b00010101 -> (3, 0b00000101) | ||||
|         0b01111111 -> (1, 0b00111111) | ||||
|     ''' | ||||
|     if not n: | ||||
|         raise Exception("Bad number") | ||||
|     i=0x80; | ||||
|     r=0 | ||||
|     while not n&i: | ||||
|         r+=1 | ||||
|         i>>=1 | ||||
|     return (r,n&~i); | ||||
| 
 | ||||
| def read_matroska_number(f, unmodified=False, signed=False): | ||||
|     ''' | ||||
|         Read ebml number. Unmodified means don't clear the length bit (as in Element IDs) | ||||
|         Returns the number and it's length as a tuple | ||||
| 
 | ||||
|         See examples in "parse_matroska_number" function | ||||
|     ''' | ||||
|     if unmodified and signed: | ||||
|         raise Exception("Contradictary arguments") | ||||
|     first_byte=f.read(1) | ||||
|     if(first_byte==""): | ||||
|         raise StopIteration | ||||
|     r = ord(first_byte) | ||||
|     (n,r2) = get_major_bit_number(r) | ||||
|     if not unmodified: | ||||
|         r=r2 | ||||
|     # from now "signed" means "negative" | ||||
|     i=n | ||||
|     while i: | ||||
|         r = r * 0x100 + ord(f.read(1)) | ||||
|         i-=1 | ||||
|     if signed: | ||||
|         r-=(2**(7*n+7)-1) | ||||
|     else: | ||||
|         if r==2**(7*n+7)-1: | ||||
|             return (-1, n+1) | ||||
|     return (r,n+1) | ||||
| 
 | ||||
| def parse_matroska_number(data, pos, unmodified=False, signed=False): | ||||
|     ''' | ||||
|         Parse ebml number from buffer[pos:]. Just like read_matroska_number. | ||||
|         Unmodified means don't clear the length bit (as in Element IDs) | ||||
|         Returns the number plus the new position in input buffer | ||||
| 
 | ||||
|         Examples: | ||||
|         "\x81" -> (1, pos+1) | ||||
|         "\x40\x01" -> (1, pos+2) | ||||
|         "\x20\x00\x01" -> (1, pos+3) | ||||
|         "\x3F\xFF\xFF" -> (0x1FFFFF, pos+3) | ||||
|         "\x20\x00\x01" unmodified -> (0x200001, pos+3) | ||||
|         "\xBF" signed -> (0, pos+1) | ||||
|         "\xBE" signed -> (-1, pos+1) | ||||
|         "\xC0" signed -> (1, pos+1) | ||||
|         "\x5F\xEF" signed -> (-16, pos+2) | ||||
|     ''' | ||||
|     if unmodified and signed: | ||||
|         raise Exception("Contradictary arguments") | ||||
|     r = ord(data[pos]) | ||||
|     pos+=1 | ||||
|     (n,r2) = get_major_bit_number(r) | ||||
|     if not unmodified: | ||||
|         r=r2 | ||||
|     # from now "signed" means "negative" | ||||
|     i=n | ||||
|     while i: | ||||
|         r = r * 0x100 + ord(data[pos]) | ||||
|         pos+=1 | ||||
|         i-=1 | ||||
|     if signed: | ||||
|         r-=(2**(7*n+6)-1) | ||||
|     else: | ||||
|         if r==2**(7*n+7)-1: | ||||
|             return (-1, pos) | ||||
|     return (r,pos) | ||||
| 
 | ||||
| def parse_xiph_number(data, pos): | ||||
|     ''' | ||||
|         Parse the Xiph lacing number from data[pos:] | ||||
|         Returns the number plus the new position | ||||
| 
 | ||||
|         Examples: | ||||
|         "\x01" -> (1,    pos+1) | ||||
|         "\x55" -> (0x55, pos+1) | ||||
|         "\xFF\x04" -> (0x103,  pos+2) | ||||
|         "\xFF\xFF\x04" -> (0x202,  pos+3) | ||||
|         "\xFF\xFF\x00" -> (0x1FE,  pos+3) | ||||
|     ''' | ||||
|     v = ord(data[pos]) | ||||
|     pos+=1 | ||||
| 
 | ||||
|     r=0 | ||||
|     while v==255: | ||||
|         r+=v | ||||
|         v = ord(data[pos]) | ||||
|         pos+=1 | ||||
| 
 | ||||
|     r+=v | ||||
|     return (r, pos) | ||||
| 
 | ||||
| 
 | ||||
| def parse_fixedlength_number(data, pos, length, signed=False): | ||||
|     ''' | ||||
|         Read the big-endian number from data[pos:pos+length] | ||||
|         Returns the number plus the new position | ||||
| 
 | ||||
|         Examples: | ||||
|         "\x01" -> (0x1,    pos+1) | ||||
|         "\x55" -> (0x55, pos+1) | ||||
|         "\x55" signed -> (0x55, pos+1) | ||||
|         "\xFF\x04" -> (0xFF04,  pos+2) | ||||
|         "\xFF\x04" signed -> (-0x00FC,  pos+2) | ||||
|     ''' | ||||
|     r=0 | ||||
|     for i in range(length): | ||||
|         r=r*0x100+ord(data[pos+i]) | ||||
|     if signed: | ||||
|         if ord(data[pos]) & 0x80: | ||||
|             r-=2**(8*length) | ||||
|     return (r, pos+length) | ||||
| 
 | ||||
| def read_fixedlength_number(f, length, signed=False): | ||||
|     """ Read length bytes and parse (parse_fixedlength_number) it. | ||||
|     Returns only the number""" | ||||
|     buf = f.read(length) | ||||
|     (r, pos) = parse_fixedlength_number(buf, 0, length, signed) | ||||
|     return r | ||||
| 
 | ||||
| def read_ebml_element_header(f): | ||||
|     ''' | ||||
|         Read Element ID and size | ||||
|         Returns id, element size and this header size | ||||
|     ''' | ||||
|     (id_, n) = read_matroska_number(f, unmodified=True) | ||||
|     (size, n2) = read_matroska_number(f) | ||||
|     return (id_, size, n+n2) | ||||
| 
 | ||||
| class EbmlElementType: | ||||
|     VOID=0 | ||||
|     MASTER=1 # read all subelements and return tree. Don't use this too large things like Segment | ||||
|     UNSIGNED=2 | ||||
|     SIGNED=3 | ||||
|     TEXTA=4 | ||||
|     TEXTU=5 | ||||
|     BINARY=6 | ||||
|     FLOAT=7 | ||||
|     DATE=8 | ||||
| 
 | ||||
|     JUST_GO_ON=10 # For "Segment". | ||||
|     # Actually MASTER, but don't build the tree for all subelements, | ||||
|     # interpreting all child elements as if they were top-level elements | ||||
| 
 | ||||
| 
 | ||||
| EET=EbmlElementType | ||||
| 
 | ||||
| # lynx -width=10000 -dump http://matroska.org/technical/specs/index.html | ||||
| #   | sed 's/not 0/not0/g; s/> 0/>0/g; s/Sampling Frequency/SamplingFrequency/g' | ||||
| #   | awk '{print $1 " " $3 " " $8}' | ||||
| #   | grep '\[..\]' | ||||
| #   | perl -ne '/(\S+) (\S+) (.)/; | ||||
| #       $name=$1; $id=$2; $type=$3; | ||||
| #       $id=~s/\[|\]//g; | ||||
| #       %types = (m=>"EET.MASTER", | ||||
| #                 u=>"EET.UNSIGNED", | ||||
| #                 i=>"EET.SIGNED", | ||||
| #                 8=>"EET.TEXTU", | ||||
| #                 s=>"EET.TEXTA", | ||||
| #                 b=>"EET.BINARY", | ||||
| #                 f=>"EET.FLOAT", | ||||
| #                 d=>"EET.DATE"); | ||||
| #       $t=$types{$type}; | ||||
| #       next unless $t; | ||||
| #       $t="EET.JUST_GO_ON" if $name eq "Segment" or $name eq "Cluster"; | ||||
| #       print "\t0x$id: ($t, \"$name\"),\n";' | ||||
| 
 | ||||
| element_types_names = { | ||||
| 	0x1A45DFA3: (EET.MASTER, "EBML"), | ||||
| 	0x4286: (EET.UNSIGNED, "EBMLVersion"), | ||||
| 	0x42F7: (EET.UNSIGNED, "EBMLReadVersion"), | ||||
| 	0x42F2: (EET.UNSIGNED, "EBMLMaxIDLength"), | ||||
| 	0x42F3: (EET.UNSIGNED, "EBMLMaxSizeLength"), | ||||
| 	0x4282: (EET.TEXTA, "DocType"), | ||||
| 	0x4287: (EET.UNSIGNED, "DocTypeVersion"), | ||||
| 	0x4285: (EET.UNSIGNED, "DocTypeReadVersion"), | ||||
| 	0xEC: (EET.BINARY, "Void"), | ||||
| 	0xBF: (EET.BINARY, "CRC-32"), | ||||
| 	0x1B538667: (EET.MASTER, "SignatureSlot"), | ||||
| 	0x7E8A: (EET.UNSIGNED, "SignatureAlgo"), | ||||
| 	0x7E9A: (EET.UNSIGNED, "SignatureHash"), | ||||
| 	0x7EA5: (EET.BINARY, "SignaturePublicKey"), | ||||
| 	0x7EB5: (EET.BINARY, "Signature"), | ||||
| 	0x7E5B: (EET.MASTER, "SignatureElements"), | ||||
| 	0x7E7B: (EET.MASTER, "SignatureElementList"), | ||||
| 	0x6532: (EET.BINARY, "SignedElement"), | ||||
| 	0x18538067: (EET.JUST_GO_ON, "Segment"), | ||||
| 	0x114D9B74: (EET.MASTER, "SeekHead"), | ||||
| 	0x4DBB: (EET.MASTER, "Seek"), | ||||
| 	0x53AB: (EET.BINARY, "SeekID"), | ||||
| 	0x53AC: (EET.UNSIGNED, "SeekPosition"), | ||||
| 	0x1549A966: (EET.MASTER, "Info"), | ||||
| 	0x73A4: (EET.BINARY, "SegmentUID"), | ||||
| 	0x7384: (EET.TEXTU, "SegmentFilename"), | ||||
| 	0x3CB923: (EET.BINARY, "PrevUID"), | ||||
| 	0x3C83AB: (EET.TEXTU, "PrevFilename"), | ||||
| 	0x3EB923: (EET.BINARY, "NextUID"), | ||||
| 	0x3E83BB: (EET.TEXTU, "NextFilename"), | ||||
| 	0x4444: (EET.BINARY, "SegmentFamily"), | ||||
| 	0x6924: (EET.MASTER, "ChapterTranslate"), | ||||
| 	0x69FC: (EET.UNSIGNED, "ChapterTranslateEditionUID"), | ||||
| 	0x69BF: (EET.UNSIGNED, "ChapterTranslateCodec"), | ||||
| 	0x69A5: (EET.BINARY, "ChapterTranslateID"), | ||||
| 	0x2AD7B1: (EET.UNSIGNED, "TimecodeScale"), | ||||
| 	0x4489: (EET.FLOAT, "Duration"), | ||||
| 	0x4461: (EET.DATE, "DateUTC"), | ||||
| 	0x7BA9: (EET.TEXTU, "Title"), | ||||
| 	0x4D80: (EET.TEXTU, "MuxingApp"), | ||||
| 	0x5741: (EET.TEXTU, "WritingApp"), | ||||
| 	0x1F43B675: (EET.JUST_GO_ON, "Cluster"), | ||||
| 	0xE7: (EET.UNSIGNED, "Timecode"), | ||||
| 	0x5854: (EET.MASTER, "SilentTracks"), | ||||
| 	0x58D7: (EET.UNSIGNED, "SilentTrackNumber"), | ||||
| 	0xA7: (EET.UNSIGNED, "Position"), | ||||
| 	0xAB: (EET.UNSIGNED, "PrevSize"), | ||||
| 	0xA3: (EET.BINARY, "SimpleBlock"), | ||||
| 	0xA0: (EET.MASTER, "BlockGroup"), | ||||
| 	0xA1: (EET.BINARY, "Block"), | ||||
| 	0xA2: (EET.BINARY, "BlockVirtual"), | ||||
| 	0x75A1: (EET.MASTER, "BlockAdditions"), | ||||
| 	0xA6: (EET.MASTER, "BlockMore"), | ||||
| 	0xEE: (EET.UNSIGNED, "BlockAddID"), | ||||
| 	0xA5: (EET.BINARY, "BlockAdditional"), | ||||
| 	0x9B: (EET.UNSIGNED, "BlockDuration"), | ||||
| 	0xFA: (EET.UNSIGNED, "ReferencePriority"), | ||||
| 	0xFB: (EET.SIGNED, "ReferenceBlock"), | ||||
| 	0xFD: (EET.SIGNED, "ReferenceVirtual"), | ||||
| 	0xA4: (EET.BINARY, "CodecState"), | ||||
| 	0x8E: (EET.MASTER, "Slices"), | ||||
| 	0xE8: (EET.MASTER, "TimeSlice"), | ||||
| 	0xCC: (EET.UNSIGNED, "LaceNumber"), | ||||
| 	0xCD: (EET.UNSIGNED, "FrameNumber"), | ||||
| 	0xCB: (EET.UNSIGNED, "BlockAdditionID"), | ||||
| 	0xCE: (EET.UNSIGNED, "Delay"), | ||||
| 	0xCF: (EET.UNSIGNED, "SliceDuration"), | ||||
| 	0xC8: (EET.MASTER, "ReferenceFrame"), | ||||
| 	0xC9: (EET.UNSIGNED, "ReferenceOffset"), | ||||
| 	0xCA: (EET.UNSIGNED, "ReferenceTimeCode"), | ||||
| 	0xAF: (EET.BINARY, "EncryptedBlock"), | ||||
| 	0x1654AE6B: (EET.MASTER, "Tracks"), | ||||
| 	0xAE: (EET.MASTER, "TrackEntry"), | ||||
| 	0xD7: (EET.UNSIGNED, "TrackNumber"), | ||||
| 	0x73C5: (EET.UNSIGNED, "TrackUID"), | ||||
| 	0x83: (EET.UNSIGNED, "TrackType"), | ||||
| 	0xB9: (EET.UNSIGNED, "FlagEnabled"), | ||||
| 	0x88: (EET.UNSIGNED, "FlagDefault"), | ||||
| 	0x55AA: (EET.UNSIGNED, "FlagForced"), | ||||
| 	0x9C: (EET.UNSIGNED, "FlagLacing"), | ||||
| 	0x6DE7: (EET.UNSIGNED, "MinCache"), | ||||
| 	0x6DF8: (EET.UNSIGNED, "MaxCache"), | ||||
| 	0x23E383: (EET.UNSIGNED, "DefaultDuration"), | ||||
| 	0x23314F: (EET.FLOAT, "TrackTimecodeScale"), | ||||
| 	0x537F: (EET.SIGNED, "TrackOffset"), | ||||
| 	0x55EE: (EET.UNSIGNED, "MaxBlockAdditionID"), | ||||
| 	0x536E: (EET.TEXTU, "Name"), | ||||
| 	0x22B59C: (EET.TEXTA, "Language"), | ||||
| 	0x86: (EET.TEXTA, "CodecID"), | ||||
| 	0x63A2: (EET.BINARY, "CodecPrivate"), | ||||
| 	0x258688: (EET.TEXTU, "CodecName"), | ||||
| 	0x7446: (EET.UNSIGNED, "AttachmentLink"), | ||||
| 	0x3A9697: (EET.TEXTU, "CodecSettings"), | ||||
| 	0x3B4040: (EET.TEXTA, "CodecInfoURL"), | ||||
| 	0x26B240: (EET.TEXTA, "CodecDownloadURL"), | ||||
| 	0xAA: (EET.UNSIGNED, "CodecDecodeAll"), | ||||
| 	0x6FAB: (EET.UNSIGNED, "TrackOverlay"), | ||||
| 	0x6624: (EET.MASTER, "TrackTranslate"), | ||||
| 	0x66FC: (EET.UNSIGNED, "TrackTranslateEditionUID"), | ||||
| 	0x66BF: (EET.UNSIGNED, "TrackTranslateCodec"), | ||||
| 	0x66A5: (EET.BINARY, "TrackTranslateTrackID"), | ||||
| 	0xE0: (EET.MASTER, "Video"), | ||||
| 	0x9A: (EET.UNSIGNED, "FlagInterlaced"), | ||||
| 	0x53B8: (EET.UNSIGNED, "StereoMode"), | ||||
| 	0x53B9: (EET.UNSIGNED, "OldStereoMode"), | ||||
| 	0xB0: (EET.UNSIGNED, "PixelWidth"), | ||||
| 	0xBA: (EET.UNSIGNED, "PixelHeight"), | ||||
| 	0x54AA: (EET.UNSIGNED, "PixelCropBottom"), | ||||
| 	0x54BB: (EET.UNSIGNED, "PixelCropTop"), | ||||
| 	0x54CC: (EET.UNSIGNED, "PixelCropLeft"), | ||||
| 	0x54DD: (EET.UNSIGNED, "PixelCropRight"), | ||||
| 	0x54B0: (EET.UNSIGNED, "DisplayWidth"), | ||||
| 	0x54BA: (EET.UNSIGNED, "DisplayHeight"), | ||||
| 	0x54B2: (EET.UNSIGNED, "DisplayUnit"), | ||||
| 	0x54B3: (EET.UNSIGNED, "AspectRatioType"), | ||||
| 	0x2EB524: (EET.BINARY, "ColourSpace"), | ||||
| 	0x2FB523: (EET.FLOAT, "GammaValue"), | ||||
| 	0x2383E3: (EET.FLOAT, "FrameRate"), | ||||
| 	0xE1: (EET.MASTER, "Audio"), | ||||
| 	0xB5: (EET.FLOAT, "SamplingFrequency"), | ||||
| 	0x78B5: (EET.FLOAT, "OutputSamplingFrequency"), | ||||
| 	0x9F: (EET.UNSIGNED, "Channels"), | ||||
| 	0x7D7B: (EET.BINARY, "ChannelPositions"), | ||||
| 	0x6264: (EET.UNSIGNED, "BitDepth"), | ||||
| 	0xE2: (EET.MASTER, "TrackOperation"), | ||||
| 	0xE3: (EET.MASTER, "TrackCombinePlanes"), | ||||
| 	0xE4: (EET.MASTER, "TrackPlane"), | ||||
| 	0xE5: (EET.UNSIGNED, "TrackPlaneUID"), | ||||
| 	0xE6: (EET.UNSIGNED, "TrackPlaneType"), | ||||
| 	0xE9: (EET.MASTER, "TrackJoinBlocks"), | ||||
| 	0xED: (EET.UNSIGNED, "TrackJoinUID"), | ||||
| 	0xC0: (EET.UNSIGNED, "TrickTrackUID"), | ||||
| 	0xC1: (EET.BINARY, "TrickTrackSegmentUID"), | ||||
| 	0xC6: (EET.UNSIGNED, "TrickTrackFlag"), | ||||
| 	0xC7: (EET.UNSIGNED, "TrickMasterTrackUID"), | ||||
| 	0xC4: (EET.BINARY, "TrickMasterTrackSegmentUID"), | ||||
| 	0x6D80: (EET.MASTER, "ContentEncodings"), | ||||
| 	0x6240: (EET.MASTER, "ContentEncoding"), | ||||
| 	0x5031: (EET.UNSIGNED, "ContentEncodingOrder"), | ||||
| 	0x5032: (EET.UNSIGNED, "ContentEncodingScope"), | ||||
| 	0x5033: (EET.UNSIGNED, "ContentEncodingType"), | ||||
| 	0x5034: (EET.MASTER, "ContentCompression"), | ||||
| 	0x4254: (EET.UNSIGNED, "ContentCompAlgo"), | ||||
| 	0x4255: (EET.BINARY, "ContentCompSettings"), | ||||
| 	0x5035: (EET.MASTER, "ContentEncryption"), | ||||
| 	0x47E1: (EET.UNSIGNED, "ContentEncAlgo"), | ||||
| 	0x47E2: (EET.BINARY, "ContentEncKeyID"), | ||||
| 	0x47E3: (EET.BINARY, "ContentSignature"), | ||||
| 	0x47E4: (EET.BINARY, "ContentSigKeyID"), | ||||
| 	0x47E5: (EET.UNSIGNED, "ContentSigAlgo"), | ||||
| 	0x47E6: (EET.UNSIGNED, "ContentSigHashAlgo"), | ||||
| 	0x1C53BB6B: (EET.MASTER, "Cues"), | ||||
| 	0xBB: (EET.MASTER, "CuePoint"), | ||||
| 	0xB3: (EET.UNSIGNED, "CueTime"), | ||||
| 	0xB7: (EET.MASTER, "CueTrackPositions"), | ||||
| 	0xF7: (EET.UNSIGNED, "CueTrack"), | ||||
| 	0xF1: (EET.UNSIGNED, "CueClusterPosition"), | ||||
| 	0x5378: (EET.UNSIGNED, "CueBlockNumber"), | ||||
| 	0xEA: (EET.UNSIGNED, "CueCodecState"), | ||||
| 	0xDB: (EET.MASTER, "CueReference"), | ||||
| 	0x96: (EET.UNSIGNED, "CueRefTime"), | ||||
| 	0x97: (EET.UNSIGNED, "CueRefCluster"), | ||||
| 	0x535F: (EET.UNSIGNED, "CueRefNumber"), | ||||
| 	0xEB: (EET.UNSIGNED, "CueRefCodecState"), | ||||
| 	0x1941A469: (EET.MASTER, "Attachments"), | ||||
| 	0x61A7: (EET.MASTER, "AttachedFile"), | ||||
| 	0x467E: (EET.TEXTU, "FileDescription"), | ||||
| 	0x466E: (EET.TEXTU, "FileName"), | ||||
| 	0x4660: (EET.TEXTA, "FileMimeType"), | ||||
| 	0x465C: (EET.BINARY, "FileData"), | ||||
| 	0x46AE: (EET.UNSIGNED, "FileUID"), | ||||
| 	0x4675: (EET.BINARY, "FileReferral"), | ||||
| 	0x4661: (EET.UNSIGNED, "FileUsedStartTime"), | ||||
| 	0x4662: (EET.UNSIGNED, "FileUsedEndTime"), | ||||
| 	0x1043A770: (EET.MASTER, "Chapters"), | ||||
| 	0x45B9: (EET.MASTER, "EditionEntry"), | ||||
| 	0x45BC: (EET.UNSIGNED, "EditionUID"), | ||||
| 	0x45BD: (EET.UNSIGNED, "EditionFlagHidden"), | ||||
| 	0x45DB: (EET.UNSIGNED, "EditionFlagDefault"), | ||||
| 	0x45DD: (EET.UNSIGNED, "EditionFlagOrdered"), | ||||
| 	0xB6: (EET.MASTER, "ChapterAtom"), | ||||
| 	0x73C4: (EET.UNSIGNED, "ChapterUID"), | ||||
| 	0x91: (EET.UNSIGNED, "ChapterTimeStart"), | ||||
| 	0x92: (EET.UNSIGNED, "ChapterTimeEnd"), | ||||
| 	0x98: (EET.UNSIGNED, "ChapterFlagHidden"), | ||||
| 	0x4598: (EET.UNSIGNED, "ChapterFlagEnabled"), | ||||
| 	0x6E67: (EET.BINARY, "ChapterSegmentUID"), | ||||
| 	0x6EBC: (EET.UNSIGNED, "ChapterSegmentEditionUID"), | ||||
| 	0x63C3: (EET.UNSIGNED, "ChapterPhysicalEquiv"), | ||||
| 	0x8F: (EET.MASTER, "ChapterTrack"), | ||||
| 	0x89: (EET.UNSIGNED, "ChapterTrackNumber"), | ||||
| 	0x80: (EET.MASTER, "ChapterDisplay"), | ||||
| 	0x85: (EET.TEXTU, "ChapString"), | ||||
| 	0x437C: (EET.TEXTA, "ChapLanguage"), | ||||
| 	0x437E: (EET.TEXTA, "ChapCountry"), | ||||
| 	0x6944: (EET.MASTER, "ChapProcess"), | ||||
| 	0x6955: (EET.UNSIGNED, "ChapProcessCodecID"), | ||||
| 	0x450D: (EET.BINARY, "ChapProcessPrivate"), | ||||
| 	0x6911: (EET.MASTER, "ChapProcessCommand"), | ||||
| 	0x6922: (EET.UNSIGNED, "ChapProcessTime"), | ||||
| 	0x6933: (EET.BINARY, "ChapProcessData"), | ||||
| 	0x1254C367: (EET.MASTER, "Tags"), | ||||
| 	0x7373: (EET.MASTER, "Tag"), | ||||
| 	0x63C0: (EET.MASTER, "Targets"), | ||||
| 	0x68CA: (EET.UNSIGNED, "TargetTypeValue"), | ||||
| 	0x63CA: (EET.TEXTA, "TargetType"), | ||||
| 	0x63C5: (EET.UNSIGNED, "TagTrackUID"), | ||||
| 	0x63C9: (EET.UNSIGNED, "TagEditionUID"), | ||||
| 	0x63C4: (EET.UNSIGNED, "TagChapterUID"), | ||||
| 	0x63C6: (EET.UNSIGNED, "TagAttachmentUID"), | ||||
| 	0x67C8: (EET.MASTER, "SimpleTag"), | ||||
| 	0x45A3: (EET.TEXTU, "TagName"), | ||||
| 	0x447A: (EET.TEXTA, "TagLanguage"), | ||||
| 	0x4484: (EET.UNSIGNED, "TagDefault"), | ||||
| 	0x4487: (EET.TEXTU, "TagString"), | ||||
| 	0x4485: (EET.BINARY, "TagBinary"), | ||||
| 	0x56AA: (EET.UNSIGNED, "CodecDelay"), | ||||
| 	0x56BB: (EET.UNSIGNED, "SeekPreRoll"), | ||||
| 	0xF0: (EET.UNSIGNED, "CueRelativePosition"), | ||||
| 	0x53C0: (EET.UNSIGNED, "AlphaMode"), | ||||
| 	0x55B2: (EET.UNSIGNED, "BitsPerChannel"), | ||||
| 	0x55B5: (EET.UNSIGNED, "CbSubsamplingHorz"), | ||||
| 	0x55B6: (EET.UNSIGNED, "CbSubsamplingVert"), | ||||
| 	0x5654: (EET.TEXTU, "ChapterStringUID"), | ||||
| 	0x55B7: (EET.UNSIGNED, "ChromaSitingHorz"), | ||||
| 	0x55B8: (EET.UNSIGNED, "ChromaSitingVert"), | ||||
| 	0x55B3: (EET.UNSIGNED, "ChromaSubsamplingHorz"), | ||||
| 	0x55B4: (EET.UNSIGNED, "ChromaSubsamplingVert"), | ||||
| 	0x55B0: (EET.MASTER, "Colour"), | ||||
| 	0x234E7A: (EET.UNSIGNED, "DefaultDecodedFieldDuration"), | ||||
| 	0x75A2: (EET.SIGNED, "DiscardPadding"), | ||||
| 	0x9D: (EET.UNSIGNED, "FieldOrder"), | ||||
| 	0x55D9: (EET.FLOAT, "LuminanceMax"), | ||||
| 	0x55DA: (EET.FLOAT, "LuminanceMin"), | ||||
| 	0x55D0: (EET.MASTER, "MasteringMetadata"), | ||||
| 	0x55B1: (EET.UNSIGNED, "MatrixCoefficients"), | ||||
| 	0x55BC: (EET.UNSIGNED, "MaxCLL"), | ||||
| 	0x55BD: (EET.UNSIGNED, "MaxFALL"), | ||||
| 	0x55BB: (EET.UNSIGNED, "Primaries"), | ||||
| 	0x55D5: (EET.FLOAT, "PrimaryBChromaticityX"), | ||||
| 	0x55D6: (EET.FLOAT, "PrimaryBChromaticityY"), | ||||
| 	0x55D3: (EET.FLOAT, "PrimaryGChromaticityX"), | ||||
| 	0x55D4: (EET.FLOAT, "PrimaryGChromaticityY"), | ||||
| 	0x55D1: (EET.FLOAT, "PrimaryRChromaticityX"), | ||||
| 	0x55D2: (EET.FLOAT, "PrimaryRChromaticityY"), | ||||
| 	0x55B9: (EET.UNSIGNED, "Range"), | ||||
| 	0x55BA: (EET.UNSIGNED, "TransferCharacteristics"), | ||||
| 	0x55D7: (EET.FLOAT, "WhitePointChromaticityX"), | ||||
| 	0x55D8: (EET.FLOAT, "WhitePointChromaticityY"), | ||||
| } | ||||
| 
 | ||||
| def read_simple_element(f, type_, size): | ||||
|     date = None | ||||
|     if size==0: | ||||
|         return "" | ||||
| 
 | ||||
|     if type_==EET.UNSIGNED: | ||||
|         data=read_fixedlength_number(f, size, False) | ||||
|     elif type_==EET.SIGNED: | ||||
|         data=read_fixedlength_number(f, size, True) | ||||
|     elif type_==EET.TEXTA: | ||||
|         data=f.read(size) | ||||
|         data = data.replace(b"\x00", b"")  # filter out \0, for gstreamer | ||||
|         data = data.decode("ascii") | ||||
|     elif type_==EET.TEXTU: | ||||
|         data=f.read(size) | ||||
|         data = data.replace(b"\x00", b"")  # filter out \0, for gstreamer | ||||
|         data = data.decode("UTF-8") | ||||
|     elif type_==EET.MASTER: | ||||
|         data=read_ebml_element_tree(f, size) | ||||
|     elif type_==EET.DATE: | ||||
|         data=read_fixedlength_number(f, size, True) | ||||
|         data*= 1e-9 | ||||
|         data+= (datetime.datetime(2001, 1, 1) - datetime.datetime(1970, 1, 1)).total_seconds() | ||||
|         # now should be UNIX date | ||||
|     elif type_==EET.FLOAT: | ||||
|         if size==4: | ||||
|             data = f.read(4) | ||||
|             data = unpack(">f", data)[0] | ||||
|         elif size==8: | ||||
|             data = f.read(8) | ||||
|             data = unpack(">d", data)[0] | ||||
|         else: | ||||
|             data=read_fixedlength_number(f, size, False) | ||||
|             sys.stderr.write("mkvparse: Floating point of size %d is not supported\n" % size) | ||||
|             data = None | ||||
|     else: | ||||
|         data=f.read(size) | ||||
|     return data | ||||
| 
 | ||||
| def read_ebml_element_tree(f, total_size): | ||||
|     ''' | ||||
|         Build tree of elements, reading f until total_size reached | ||||
|         Don't use for the whole segment, it's not Haskell | ||||
| 
 | ||||
|         Returns list of pairs (element_name, element_value). | ||||
|         element_value can also be list of pairs | ||||
|     ''' | ||||
|     childs=[] | ||||
|     while(total_size>0): | ||||
|         (id_, size, hsize) = read_ebml_element_header(f) | ||||
|         if size == -1: | ||||
|             sys.stderr.write("mkvparse: Element %x without size? Damaged data? Skipping %d bytes\n" % (id_, size, total_size)) # pylint disable=too-many-format-args | ||||
|             f.read(total_size); | ||||
|             break; | ||||
|         if size>total_size: | ||||
|             sys.stderr.write("mkvparse: Element %x with size %d? Damaged data? Skipping %d bytes\n" % (id_, size, total_size)) | ||||
|             f.read(total_size); | ||||
|             break | ||||
|         type_ = EET.BINARY | ||||
|         name = "unknown_%x"%id_ | ||||
|         if id_ in element_types_names: | ||||
|             (type_, name) = element_types_names[id_] | ||||
|         data = read_simple_element(f, type_, size) | ||||
|         total_size-=(size+hsize) | ||||
|         childs.append((name, (type_, data))) | ||||
|     return childs | ||||
| 
 | ||||
| 
 | ||||
| class MatroskaHandler: | ||||
|     """ User for mkvparse should override these methods """ | ||||
|     def tracks_available(self): | ||||
|         pass | ||||
|     def segment_info_available(self): | ||||
|         pass | ||||
|     def frame(self, track_id, timestamp, data, more_laced_frames, duration, keyframe, invisible, discardable): | ||||
|         pass | ||||
|     def ebml_top_element(self, id_, name_, type_, data_): | ||||
|         pass | ||||
|     def before_handling_an_element(self): | ||||
|         pass | ||||
|     def begin_handling_ebml_element(self, id_, name, type_, headersize, datasize): | ||||
|         return type_ | ||||
|     def element_data_available(self, id_, name, type_, headersize, data): | ||||
|         pass | ||||
| 
 | ||||
| def handle_block(buffer, buffer_pos, handler, cluster_timecode, timecode_scale=1000000, duration=None, header_removal_headers_for_tracks={}): | ||||
|     ''' | ||||
|         Decode a block, handling all lacings, send it to handler with appropriate timestamp, track number | ||||
|     ''' | ||||
|     pos=0 | ||||
|     (tracknum, pos) = parse_matroska_number(buffer, pos, signed=False) | ||||
|     (tcode, pos) = parse_fixedlength_number(buffer, pos, 2, signed=True) | ||||
|     flags = ord(buffer[pos]); pos+=1 | ||||
|     f_keyframe = (flags&0x80 == 0x80) | ||||
|     f_invisible = (flags&0x08 == 0x08) | ||||
|     f_discardable = (flags&0x01 == 0x01) | ||||
|     laceflags=flags&0x06 | ||||
| 
 | ||||
|     block_timecode = (cluster_timecode + tcode)*(timecode_scale*0.000000001) | ||||
| 
 | ||||
|     header_removal_prefix = b"" | ||||
|     if tracknum in header_removal_headers_for_tracks: | ||||
|         # header_removal_prefix = header_removal_headers_for_tracks[tracknum] | ||||
|         raise NotImplementedError | ||||
| 
 | ||||
|     if laceflags == 0x00: # no lacing | ||||
|         # buf = buffer[pos:] | ||||
|         handler.frame(tracknum, block_timecode, buffer_pos+pos, len(buffer)-pos, | ||||
|                       0, duration, f_keyframe, f_invisible, f_discardable) | ||||
|         return | ||||
| 
 | ||||
|     numframes = ord(buffer[pos]); pos+=1 | ||||
|     numframes+=1 | ||||
| 
 | ||||
|     lengths=[] | ||||
| 
 | ||||
|     if laceflags == 0x02: # Xiph lacing | ||||
|         accumlength=0 | ||||
|         for i in range(numframes-1): | ||||
|             (l, pos) = parse_xiph_number(buffer, pos) | ||||
|             lengths.append(l) | ||||
|             accumlength+=l | ||||
|         lengths.append(len(buffer)-pos-accumlength) | ||||
|     elif laceflags == 0x06: # EBML lacing | ||||
|         accumlength=0 | ||||
|         if numframes: | ||||
|             (flength, pos) = parse_matroska_number(buffer, pos, signed=False) | ||||
|             lengths.append(flength) | ||||
|             accumlength+=flength | ||||
|         for i in range(numframes-2): | ||||
|             (l, pos) = parse_matroska_number(buffer, pos, signed=True) | ||||
|             flength+=l | ||||
|             lengths.append(flength) | ||||
|             accumlength+=flength | ||||
|         lengths.append(len(buffer)-pos-accumlength) | ||||
|     elif laceflags==0x04: # Fixed size lacing | ||||
|         fl=int((len(buffer)-pos)/numframes) | ||||
|         for i in range(numframes): | ||||
|             lengths.append(fl) | ||||
| 
 | ||||
|     more_laced_frames=numframes-1 | ||||
|     for i in lengths: | ||||
|         # buf = buffer[pos:pos+i] | ||||
|         handler.frame(tracknum, block_timecode, buffer_pos+pos, i, more_laced_frames, duration, | ||||
|                       f_keyframe, f_invisible, f_discardable) | ||||
|         pos+=i | ||||
|         more_laced_frames-=1 | ||||
| 
 | ||||
| 
 | ||||
| def resync(f): | ||||
|     sys.stderr.write("mvkparse: Resyncing\n") | ||||
|     while True: | ||||
|         b = f.read(1); | ||||
|         if b == b"": return (None, None); | ||||
|         if b == b"\x1F": | ||||
|             b2 = f.read(3); | ||||
|             if b2 == b"\x43\xB6\x75": | ||||
|                 (seglen, x) = read_matroska_number(f) | ||||
|                 return (0x1F43B675, seglen, x+4) # cluster | ||||
|         if b == b"\x18": | ||||
|             b2 = f.read(3) | ||||
|             if b2 == b"\x53\x80\x67": | ||||
|                 (seglen, x) = read_matroska_number(f) | ||||
|                 return (0x18538067, seglen, x+4) # segment | ||||
|         if b == b"\x16": | ||||
|             b2 = f.read(3) | ||||
|             if b2 == b"\x54\xAE\x6B": | ||||
|                 (seglen ,x )= read_matroska_number(f) | ||||
|                 return (0x1654AE6B, seglen, x+4) # tracks | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def mkvparse(f, handler): | ||||
|     ''' | ||||
|         Read mkv file f and call handler methods when track or segment information is ready or when frame is read. | ||||
|         Handles lacing, timecodes (except of per-track scaling) | ||||
|     ''' | ||||
|     timecode_scale = 1000000 | ||||
|     current_cluster_timecode = 0 | ||||
|     resync_element_id = None | ||||
|     resync_element_size = None | ||||
|     resync_element_headersize = None | ||||
|     header_removal_headers_for_tracks = {} | ||||
|     while f: | ||||
|         (id_, size, hsize) = (None, None, None) | ||||
|         tree = None | ||||
|         data = None | ||||
|         (type_, name) = (None, None) | ||||
|         try: | ||||
|             if not resync_element_id: | ||||
|                 try: | ||||
|                     handler.before_handling_an_element() | ||||
|                     (id_, size, hsize) = read_ebml_element_header(f) | ||||
|                 except StopIteration: | ||||
|                     break; | ||||
|                 if not (id_ in element_types_names): | ||||
|                     sys.stderr.write("mkvparse: Unknown element with id %x and size %d\n"%(id_, size)) | ||||
|                     (resync_element_id, resync_element_size, resync_element_headersize) = resync(f) | ||||
|                     if resync_element_id: | ||||
|                         continue; | ||||
|                     else: | ||||
|                         break; | ||||
|             else: | ||||
|                 id_ = resync_element_id | ||||
|                 size=resync_element_size | ||||
|                 hsize=resync_element_headersize | ||||
|                 resync_element_id = None | ||||
|                 resync_element_size = None | ||||
|                 resync_element_headersize = None | ||||
| 
 | ||||
|             (type_, name) = element_types_names[id_] | ||||
|             (type_, name) = element_types_names[id_] | ||||
|             type_ =  handler.begin_handling_ebml_element(id_, name, type_, hsize, size) | ||||
| 
 | ||||
|             if type_ == EET.MASTER: | ||||
|                 tree = read_ebml_element_tree(f, size) | ||||
|                 data = tree | ||||
| 
 | ||||
|         except Exception: | ||||
|             traceback.print_exc() | ||||
|             handler.before_handling_an_element() | ||||
|             (resync_element_id, resync_element_size, resync_element_headersize) = resync(f) | ||||
|             if resync_element_id: | ||||
|                 continue; | ||||
|             else: | ||||
|                 break; | ||||
| 
 | ||||
|         if name=="EBML" and type(data) == list: | ||||
|             d = dict(tree) | ||||
|             if 'EBMLReadVersion' in d: | ||||
|                 if d['EBMLReadVersion'][1]>1: sys.stderr.write("mkvparse: Warning: EBMLReadVersion too big\n") | ||||
|             if 'DocTypeReadVersion' in d: | ||||
|                 if d['DocTypeReadVersion'][1]>2: sys.stderr.write("mkvparse: Warning: DocTypeReadVersion too big\n") | ||||
|             dt = d['DocType'][1] | ||||
|             if dt != "matroska" and dt != "webm": | ||||
|                 sys.stderr.write("mkvparse: Warning: EBML DocType is not \"matroska\" or \"webm\"") | ||||
|         elif name=="Info" and type(data) == list: | ||||
|             handler.segment_info = tree | ||||
|             handler.segment_info_available() | ||||
| 
 | ||||
|             d = dict(tree) | ||||
|             if "TimecodeScale" in d: | ||||
|                 timecode_scale = d["TimecodeScale"][1] | ||||
|         elif name=="Tracks" and type(data) == list: | ||||
|             handler.tracks={} | ||||
|             for (ten, (_t, track)) in tree: | ||||
|                 if ten != "TrackEntry": continue | ||||
|                 d = dict(track) | ||||
|                 n = d['TrackNumber'][1] | ||||
|                 handler.tracks[n]=d | ||||
|                 tt = d['TrackType'][1] | ||||
|                 if   tt==0x01: d['type']='video' | ||||
|                 elif tt==0x02: d['type']='audio' | ||||
|                 elif tt==0x03: d['type']='complex' | ||||
|                 elif tt==0x10: d['type']='logo' | ||||
|                 elif tt==0x11: d['type']='subtitle' | ||||
|                 elif tt==0x12: d['type']='button' | ||||
|                 elif tt==0x20: d['type']='control' | ||||
|                 if 'TrackTimecodeScale' in d: | ||||
|                     sys.stderr.write("mkvparse: Warning: TrackTimecodeScale is not supported\n") | ||||
|                 if 'ContentEncodings' in d: | ||||
|                     try: | ||||
|                         compr = dict(d["ContentEncodings"][1][0][1][1][0][1][1]) | ||||
|                         if compr["ContentCompAlgo"][1] == 3: | ||||
|                             header_removal_headers_for_tracks[n] = compr["ContentCompSettings"][1] | ||||
|                         else: | ||||
|                             sys.stderr.write("mkvparse: Warning: compression other than " \ | ||||
|                                 "header removal is not supported\n") | ||||
|                     except: | ||||
|                         sys.stderr.write("mkvparse: Warning: unsuccessfully tried " \ | ||||
|                                 "to handle header removal compression\n") | ||||
|             handler.tracks_available() | ||||
|         # cluster contents: | ||||
|         elif name=="Timecode" and type_ == EET.UNSIGNED: | ||||
|             data=read_fixedlength_number(f, size, False) | ||||
|             current_cluster_timecode = data; | ||||
|         elif name=="SimpleBlock" and type_ == EET.BINARY: | ||||
|             pos = f.tell() | ||||
|             data=f.read(size) | ||||
|             handle_block(data, pos, handler, current_cluster_timecode, timecode_scale, None,  header_removal_headers_for_tracks) | ||||
|         elif name=="BlockGroup" and type_ == EET.MASTER: | ||||
|             d2 = dict(tree) | ||||
|             duration=None | ||||
|             raise NotImplementedError | ||||
|             # if 'BlockDuration' in d2: | ||||
|             #     duration = d2['BlockDuration'][1] | ||||
|             #     duration = duration*0.000000001*timecode_scale | ||||
|             # if 'Block' in d2: | ||||
|             #     handle_block(d2['Block'][1], None, handler, current_cluster_timecode, timecode_scale, duration, header_removal_headers_for_tracks) | ||||
|         else: | ||||
|             if type_!=EET.JUST_GO_ON and type_!=EET.MASTER: | ||||
|                 data = read_simple_element(f, type_, size) | ||||
| 
 | ||||
|         handler.ebml_top_element(id_, name, type_, data); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     print("Run mkvuser.py for the example") | ||||
					Loading…
					
					
				
		Reference in new issue