3 Generate C definitions for parsing Matroska files.
4 Can also be used to directly parse Matroska files and display their contents.
8 # This file is part of MPlayer.
10 # MPlayer is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # MPlayer is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License along
21 # with MPlayer; if not, write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 'EBML, 1a45dfa3, sub', (
28 'EBMLVersion, 4286, uint',
29 'EBMLReadVersion, 42f7, uint',
30 'EBMLMaxIDLength, 42f2, uint',
31 'EBMLMaxSizeLength, 42f3, uint',
33 'DocTypeVersion, 4287, uint',
34 'DocTypeReadVersion, 4285, uint',
42 'Segment, 18538067, sub', (
44 'SeekHead*, 114d9b74, sub', (
46 'SeekID, 53ab, ebml_id',
47 'SeekPosition, 53ac, uint',
51 'Info*, 1549a966, sub', (
52 'SegmentUID, 73a4, binary',
53 'PrevUID, 3cb923, binary',
54 'NextUID, 3eb923, binary',
55 'TimecodeScale, 2ad7b1, uint',
56 'DateUTC, 4461, sint',
58 'MuxingApp, 4d80, str',
59 'WritingApp, 5741, str',
60 'Duration, 4489, float',
63 'Cluster*, 1f43b675, sub', (
65 'BlockGroup*, a0, sub', (
67 'BlockDuration, 9b, uint',
68 'ReferenceBlock*, fb, sint',
70 'SimpleBlock*, a3, binary',
73 'Tracks*, 1654ae6b, sub', (
74 'TrackEntry*, ae, sub', (
75 'TrackNumber, d7, uint',
76 'TrackUID, 73c5, uint',
77 'TrackType, 83, uint',
78 'FlagEnabled, b9, uint',
79 'FlagDefault, 88, uint',
80 'FlagForced, 55aa, uint',
81 'FlagLacing, 9c, uint',
82 'MinCache, 6de7, uint',
83 'DefaultDuration, 23e383, uint',
84 'TrackTimecodeScale, 23314f, float',
85 'MaxBlockAdditionID, 55ee, uint',
87 'Language, 22b59c, str',
89 'CodecPrivate, 63a2, binary',
90 'CodecDecodeAll, aa, uint',
92 'FlagInterlaced, 9a, uint',
93 'PixelWidth, b0, uint',
94 'PixelHeight, ba, uint',
95 'DisplayWidth, 54b0, uint',
96 'DisplayHeight, 54ba, uint',
97 'FrameRate, 2383e3, float',
100 'SamplingFrequency, b5, float',
101 'Channels, 9f, uint',
102 'BitDepth, 6264, uint',
104 'ContentEncodings, 6d80, sub', (
105 'ContentEncoding*, 6240, sub', (
106 'ContentEncodingOrder, 5031, uint',
107 'ContentEncodingScope, 5032, uint',
108 'ContentEncodingType, 5033, uint',
109 'ContentCompression, 5034, sub', (
110 'ContentCompAlgo, 4254, uint',
111 'ContentCompSettings, 4255, binary',
118 'Cues, 1c53bb6b, sub', (
119 'CuePoint*, bb, sub', (
121 'CueTrackPositions*, b7, sub', (
122 'CueTrack, f7, uint',
123 'CueClusterPosition, f1, uint',
128 'Attachments, 1941a469, sub', (
129 'AttachedFile*, 61a7, sub', (
130 'FileName, 466e, str',
131 'FileMimeType, 4660, str',
132 'FileData, 465c, binary',
133 'FileUID, 46ae, uint',
137 'Chapters, 1043a770, sub', (
138 'EditionEntry*, 45b9, sub', (
139 'EditionUID, 45bc, uint',
140 'EditionFlagHidden, 45bd, uint',
141 'EditionFlagDefault, 45db, uint',
142 'EditionFlagOrdered, 45dd, uint',
143 'ChapterAtom*, b6, sub', (
144 'ChapterUID, 73c4, uint',
145 'ChapterTimeStart, 91, uint',
146 'ChapterTimeEnd, 92, uint',
147 'ChapterFlagHidden, 98, uint',
148 'ChapterFlagEnabled, 4598, uint',
149 'ChapterSegmentUID, 6e67, binary',
150 'ChapterSegmentEditionUID, 6ebc, uint',
151 'ChapterDisplay*, 80, sub', (
152 'ChapString, 85, str',
153 'ChapLanguage*, 437c, str',
158 'Tags*, 1254c367, sub', (
160 'Targets, 63c0, sub', (
161 'TargetTypeValue, 68ca, uint',
170 from math
import ldexp
173 return int(s
.encode('hex'), 16)
175 def camelcase_to_words(name
):
178 for i
in range(1, len(name
)):
179 if name
[i
].isupper() and (name
[i
-1].islower() or
180 name
[i
+1:i
+2].islower()):
181 parts
.append(name
[start
:i
])
183 parts
.append(name
[start
:])
184 return '_'.join(parts
).lower()
186 class MatroskaElement(object):
188 def __init__(self
, name
, elid
, valtype
, namespace
):
190 self
.definename
= '%s_ID_%s' % (namespace
, name
.upper())
191 self
.fieldname
= camelcase_to_words(name
)
192 self
.structname
= 'ebml_' + self
.fieldname
194 self
.valtype
= valtype
196 self
.ebmltype
= 'EBML_TYPE_SUBELEMENTS'
197 self
.valname
= 'struct %s' % self
.structname
199 self
.ebmltype
= 'EBML_TYPE_' + valtype
.upper()
201 self
.valname
= {'uint': 'uint64_t', 'str': 'struct bstr',
202 'binary': 'struct bstr', 'ebml_id': 'uint32_t',
203 'float': 'double', 'sint': 'int64_t',
206 raise SyntaxError('Unrecognized value type ' + valtype
)
207 self
.subelements
= ()
209 def add_subelements(self
, subelements
):
210 self
.subelements
= subelements
211 self
.subids
= set(x
[0].elid
for x
in subelements
)
215 def parse_elems(l
, namespace
):
218 if isinstance(el
, str):
219 name
, hexid
, eltype
= [x
.strip() for x
in el
.split(',')]
220 multiple
= name
.endswith('*')
221 name
= name
.strip('*')
222 new
= MatroskaElement(name
, hexid
, eltype
, namespace
)
223 elementd
[hexid
] = new
224 elementlist
.append(new
)
225 subelements
.append((new
, multiple
))
227 new
.add_subelements(parse_elems(el
, namespace
))
230 parse_elems(elements_ebml
, 'EBML')
231 parse_elems(elements_matroska
, 'MATROSKA')
233 def generate_C_header():
234 print('// Generated by TOOLS/matroska.py, do not edit manually')
237 for el
in elementlist
:
238 print('#define %-40s 0x%s' % (el
.definename
, el
.elid
))
242 for el
in reversed(elementlist
):
243 if not el
.subelements
:
246 print('struct %s {' % el
.structname
)
247 l
= max(len(subel
.valname
) for subel
, multiple
in el
.subelements
)+1
248 for subel
, multiple
in el
.subelements
:
249 print(' %-*s %s%s;' % (l
, subel
.valname
, (' ', '*')[multiple
],
252 for subel
, multiple
in el
.subelements
:
253 print(' int n_%s;' % (subel
.fieldname
))
256 for el
in elementlist
:
257 if not el
.subelements
:
259 print('extern const struct ebml_elem_desc %s_desc;' % el
.structname
)
262 print('#define MAX_EBML_SUBELEMENTS %d' % max(len(el
.subelements
)
263 for el
in elementlist
))
267 def generate_C_definitions():
268 print('// Generated by TOOLS/matroska.py, do not edit manually')
270 for el
in reversed(elementlist
):
273 print('#define N %s' % el
.fieldname
)
274 print('E_S("%s", %d)' % (el
.name
, len(el
.subelements
)))
275 for subel
, multiple
in el
.subelements
:
276 print('F(%s, %s, %d)' % (subel
.definename
, subel
.fieldname
,
281 print('E("%s", %s, %s)' % (el
.name
, el
.fieldname
, el
.ebmltype
))
295 while not ord(t
) & mask
:
307 while not ord(t
) & mask
:
310 t
= chr(ord(t
) & (mask
- 1))
312 return i
+1, byte2num(t
)
314 def read_str(s
, length
):
315 return read(s
, length
)
317 def read_uint(s
, length
):
321 def read_sint(s
, length
):
322 i
= read_uint(s
, length
)
323 mask
= 1 << (length
* 8 - 1)
328 def read_float(s
, length
):
332 f
= ldexp((i
& 0x7fffff) + (1 << 23), (i
>> 23 & 0xff) - 150)
338 def parse_one(s
, depth
, parent
, maxlen
):
339 elid
= read_id(s
).encode('hex')
340 elem
= elementd
.get(elid
)
341 if parent
is not None and elid
not in parent
.subids
and elid
not in ('ec', 'bf'):
342 print('Unexpected:', elid
)
344 raise NotImplementedError
345 size
, length
= read_vint(s
)
346 this_length
= len(elid
) / 2 + size
+ length
348 if elem
.valtype
!= 'skip':
349 print depth
, elid
, elem
.name
, 'size:', length
, 'value:',
350 if elem
.valtype
== 'sub':
351 print('subelements:')
353 length
-= parse_one(s
, depth
+ 1, elem
, length
)
356 elif elem
.valtype
== 'str':
357 print 'string', repr(read_str(s
, length
))
358 elif elem
.valtype
in ('binary', 'ebml_id'):
359 t
= read_str(s
, length
)
361 if elem
.valtype
== 'ebml_id':
362 idelem
= elementd
.get(t
.encode('hex'))
366 dec
= '(%s)' % idelem
.name
370 t
= '<skipped %d bytes>' % len(t
)
371 print 'binary', t
, dec
372 elif elem
.valtype
== 'uint':
373 print 'uint', read_uint(s
, length
)
374 elif elem
.valtype
== 'sint':
375 print 'sint', read_sint(s
, length
)
376 elif elem
.valtype
== 'float':
377 print 'float', read_float(s
, length
)
378 elif elem
.valtype
== 'skip':
381 raise NotImplementedError
383 print(depth
, 'Unknown element:', elid
, 'size:', length
)
387 def parse_toplevel(s
):
388 parse_one(s
, 0, None, 1 << 63)
390 if sys
.argv
[1] == '--generate-header':
392 elif sys
.argv
[1] == '--generate-definitions':
393 generate_C_definitions()
395 s
= open(sys
.argv
[1])