mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-02-19 01:53:57 +08:00
framereader only has to support h265 and raw (#1588)
old-commit-hash: 639cf2288f
This commit is contained in:
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")
|
||||
Reference in New Issue
Block a user