parent
4489056d30
commit
57cc3cd69a
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