diff --git a/Makefile b/Makefile index 3e2174d07f..aefb8553fb 100644 --- a/Makefile +++ b/Makefile @@ -894,7 +894,9 @@ TAGS: tags: rm -f $@; find . -name '*.[chS]' -o -name '*.asm' | xargs ctags -a - +generated_ebml: + TOOLS/matroska.py --generate-header >libmpdemux/ebml_types.h + TOOLS/matroska.py --generate-definitions >libmpdemux/ebml_defs.c ###### tests / tools ####### diff --git a/TOOLS/matroska.py b/TOOLS/matroska.py new file mode 100755 index 0000000000..8368f35185 --- /dev/null +++ b/TOOLS/matroska.py @@ -0,0 +1,397 @@ +#!/usr/bin/python +""" +Generate C definitions for parsing Matroska files. +Can also be used to directly parse Matroska files and display their contents. +""" + +# +# This file is part of MPlayer. +# +# MPlayer is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# MPlayer is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with MPlayer; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + + +elements_ebml = ( + 'EBML, 1a45dfa3, sub', ( + 'EBMLVersion, 4286, uint', + 'EBMLReadVersion, 42f7, uint', + 'EBMLMaxIDLength, 42f2, uint', + 'EBMLMaxSizeLength, 42f3, uint', + 'DocType, 4282, str', + 'DocTypeVersion, 4287, uint', + 'DocTypeReadVersion, 4285, uint', + ), + + 'CRC32, bf, binary', + 'Void, ec, binary', +) + +elements_matroska = ( + 'Segment, 18538067, sub', ( + + 'SeekHead*, 114d9b74, sub', ( + 'Seek*, 4dbb, sub', ( + 'SeekID, 53ab, ebml_id', + 'SeekPosition, 53ac, uint', + ), + ), + + 'Info*, 1549a966, sub', ( + 'SegmentUID, 73a4, binary', + 'PrevUID, 3cb923, binary', + 'NextUID, 3eb923, binary', + 'TimecodeScale, 2ad7b1, uint', + 'DateUTC, 4461, sint', + 'Title, 7ba9, str', + 'MuxingApp, 4d80, str', + 'WritingApp, 5741, str', + 'Duration, 4489, float', + ), + + 'Cluster*, 1f43b675, sub', ( + 'Timecode, e7, uint', + 'BlockGroup*, a0, sub', ( + 'Block, a1, binary', + 'BlockDuration, 9b, uint', + 'ReferenceBlock*, fb, sint', + ), + 'SimpleBlock*, a3, binary', + ), + + 'Tracks*, 1654ae6b, sub', ( + 'TrackEntry*, ae, sub', ( + 'TrackNumber, d7, uint', + 'TrackUID, 73c5, uint', + 'TrackType, 83, uint', + 'FlagEnabled, b9, uint', + 'FlagDefault, 88, uint', + 'FlagForced, 55aa, uint', + 'FlagLacing, 9c, uint', + 'MinCache, 6de7, uint', + 'DefaultDuration, 23e383, uint', + 'TrackTimecodeScale, 23314f, float', + 'MaxBlockAdditionID, 55ee, uint', + 'Name, 536e, str', + 'Language, 22b59c, str', + 'CodecID, 86, str', + 'CodecPrivate, 63a2, binary', + 'CodecDecodeAll, aa, uint', + 'Video, e0, sub', ( + 'FlagInterlaced, 9a, uint', + 'PixelWidth, b0, uint', + 'PixelHeight, ba, uint', + 'DisplayWidth, 54b0, uint', + 'DisplayHeight, 54ba, uint', + 'FrameRate, 2383e3, float', + ), + 'Audio, e1, sub', ( + 'SamplingFrequency, b5, float', + 'Channels, 9f, uint', + 'BitDepth, 6264, uint', + ), + 'ContentEncodings, 6d80, sub', ( + 'ContentEncoding*, 6240, sub', ( + 'ContentEncodingOrder, 5031, uint', + 'ContentEncodingScope, 5032, uint', + 'ContentEncodingType, 5033, uint', + 'ContentCompression, 5034, sub', ( + 'ContentCompAlgo, 4254, uint', + 'ContentCompSettings, 4255, binary', + ), + ), + ), + ), + ), + + 'Cues, 1c53bb6b, sub', ( + 'CuePoint*, bb, sub', ( + 'CueTime, b3, uint', + 'CueTrackPositions*, b7, sub', ( + 'CueTrack, f7, uint', + 'CueClusterPosition, f1, uint', + ), + ), + ), + + 'Attachments, 1941a469, sub', ( + 'AttachedFile*, 61a7, sub', ( + 'FileName, 466e, str', + 'FileMimeType, 4660, str', + 'FileData, 465c, binary', + 'FileUID, 46ae, uint', + ), + ), + + 'Chapters, 1043a770, sub', ( + 'EditionEntry*, 45b9, sub', ( + 'EditionUID, 45bc, uint', + 'EditionFlagHidden, 45bd, uint', + 'EditionFlagDefault, 45db, uint', + 'EditionFlagOrdered, 45dd, uint', + 'ChapterAtom*, b6, sub', ( + 'ChapterUID, 73c4, uint', + 'ChapterTimeStart, 91, uint', + 'ChapterTimeEnd, 92, uint', + 'ChapterFlagHidden, 98, uint', + 'ChapterFlagEnabled, 4598, uint', + 'ChapterSegmentUID, 6e67, binary', + 'ChapterSegmentEditionUID, 6ebc, uint', + 'ChapterDisplay*, 80, sub', ( + 'ChapString, 85, str', + 'ChapLanguage*, 437c, str', + ), + ), + ), + ), + 'Tags*, 1254c367, sub', ( + 'Tag*, 7373, sub', ( + 'Targets, 63c0, sub', ( + 'TargetTypeValue, 68ca, uint', + ), + ), + ), + ), +) + + +import sys +from math import ldexp + +def byte2num(s): + return int(s.encode('hex'), 16) + +def camelcase_to_words(name): + parts = [] + start = 0 + for i in range(1, len(name)): + if name[i].isupper() and (name[i-1].islower() or + name[i+1:i+2].islower()): + parts.append(name[start:i]) + start = i + parts.append(name[start:]) + return '_'.join(parts).lower() + +class MatroskaElement(object): + + def __init__(self, name, elid, valtype, namespace): + self.name = name + self.definename = '%s_ID_%s' % (namespace, name.upper()) + self.fieldname = camelcase_to_words(name) + self.structname = 'ebml_' + self.fieldname + self.elid = elid + self.valtype = valtype + if valtype == 'sub': + self.ebmltype = 'EBML_TYPE_SUBELEMENTS' + self.valname = 'struct %s' % self.structname + else: + self.ebmltype = 'EBML_TYPE_' + valtype.upper() + try: + self.valname = {'uint': 'uint64_t', 'str': 'struct bstr', + 'binary': 'struct bstr', 'ebml_id': 'uint32_t', + 'float': 'double', 'sint': 'int64_t', + }[valtype] + except KeyError: + raise SyntaxError('Unrecognized value type ' + valtype) + self.subelements = () + + def add_subelements(self, subelements): + self.subelements = subelements + self.subids = set(x[0].elid for x in subelements) + +elementd = {} +elementlist = [] +def parse_elems(l, namespace): + subelements = [] + for el in l: + if isinstance(el, str): + name, hexid, eltype = [x.strip() for x in el.split(',')] + multiple = name.endswith('*') + name = name.strip('*') + new = MatroskaElement(name, hexid, eltype, namespace) + elementd[hexid] = new + elementlist.append(new) + subelements.append((new, multiple)) + else: + new.add_subelements(parse_elems(el, namespace)) + return subelements + +parse_elems(elements_ebml, 'EBML') +parse_elems(elements_matroska, 'MATROSKA') + +def generate_C_header(): + print('// Generated by TOOLS/matroska.py, do not edit manually') + print + + for el in elementlist: + print('#define %-40s 0x%s' % (el.definename, el.elid)) + + print + + for el in reversed(elementlist): + if not el.subelements: + continue + print + print('struct %s {' % el.structname) + l = max(len(subel.valname) for subel, multiple in el.subelements)+1 + for subel, multiple in el.subelements: + print(' %-*s %s%s;' % (l, subel.valname, (' ', '*')[multiple], + subel.fieldname)) + print + for subel, multiple in el.subelements: + print(' int n_%s;' % (subel.fieldname)) + print('};') + + for el in elementlist: + if not el.subelements: + continue + print('extern const struct ebml_elem_desc %s_desc;' % el.structname) + + print + print('#define MAX_EBML_SUBELEMENTS %d' % max(len(el.subelements) + for el in elementlist)) + + + +def generate_C_definitions(): + print('// Generated by TOOLS/matroska.py, do not edit manually') + print + for el in reversed(elementlist): + print + if el.subelements: + print('#define N %s' % el.fieldname) + print('E_S("%s", %d)' % (el.name, len(el.subelements))) + for subel, multiple in el.subelements: + print('F(%s, %s, %d)' % (subel.definename, subel.fieldname, + multiple)) + print('}};') + print('#undef N') + else: + print('E("%s", %s, %s)' % (el.name, el.fieldname, el.ebmltype)) + +def read(s, length): + t = s.read(length) + if len(t) != length: + raise IOError + return t + +def read_id(s): + t = read(s, 1) + i = 0 + mask = 128 + if ord(t) == 0: + raise SyntaxError + while not ord(t) & mask: + i += 1 + mask >>= 1 + t += read(s, i) + return t + +def read_vint(s): + t = read(s, 1) + i = 0 + mask = 128 + if ord(t) == 0: + raise SyntaxError + while not ord(t) & mask: + i += 1 + mask >>= 1 + t = chr(ord(t) & (mask - 1)) + t += read(s, i) + return i+1, byte2num(t) + +def read_str(s, length): + return read(s, length) + +def read_uint(s, length): + t = read(s, length) + return byte2num(t) + +def read_sint(s, length): + i = read_uint(s, length) + mask = 1 << (length * 8 - 1) + if i & mask: + i -= 2 * mask + return i + +def read_float(s, length): + t = read(s, length) + i = byte2num(t) + if length == 4: + f = ldexp((i & 0x7fffff) + (1 << 23), (i >> 23 & 0xff) - 150) + if i & (1 << 31): + f = -f + return f + raise SyntaxError + +def parse_one(s, depth, parent, maxlen): + elid = read_id(s).encode('hex') + elem = elementd.get(elid) + if parent is not None and elid not in parent.subids and elid not in ('ec', 'bf'): + print('Unexpected:', elid) + if 1: + raise NotImplementedError + size, length = read_vint(s) + this_length = len(elid) / 2 + size + length + if elem is not None: + if elem.valtype != 'skip': + print depth, elid, elem.name, 'size:', length, 'value:', + if elem.valtype == 'sub': + print('subelements:') + while length > 0: + length -= parse_one(s, depth + 1, elem, length) + if length < 0: + raise SyntaxError + elif elem.valtype == 'str': + print 'string', repr(read_str(s, length)) + elif elem.valtype in ('binary', 'ebml_id'): + t = read_str(s, length) + dec = '' + if elem.valtype == 'ebml_id': + idelem = elementd.get(t.encode('hex')) + if idelem is None: + dec = '(UNKNOWN)' + else: + dec = '(%s)' % idelem.name + if len(t) < 20: + t = t.encode('hex') + else: + t = '' % len(t) + print 'binary', t, dec + elif elem.valtype == 'uint': + print 'uint', read_uint(s, length) + elif elem.valtype == 'sint': + print 'sint', read_sint(s, length) + elif elem.valtype == 'float': + print 'float', read_float(s, length) + elif elem.valtype == 'skip': + read(s, length) + else: + raise NotImplementedError + else: + print(depth, 'Unknown element:', elid, 'size:', length) + read(s, length) + return this_length + +def parse_toplevel(s): + parse_one(s, 0, None, 1 << 63) + +if sys.argv[1] == '--generate-header': + generate_C_header() +elif sys.argv[1] == '--generate-definitions': + generate_C_definitions() +else: + s = open(sys.argv[1]) + while 1: + parse_toplevel(s) diff --git a/libmpdemux/demux_mkv.c b/libmpdemux/demux_mkv.c index b4a17d036f..99e3302c1d 100644 --- a/libmpdemux/demux_mkv.c +++ b/libmpdemux/demux_mkv.c @@ -607,7 +607,7 @@ static int demux_mkv_read_trackaudio(demuxer_t *demuxer, mkv_track_t *track) len += il; while (length > 0) { switch (ebml_read_id(s, &il)) { - case MATROSKA_ID_AUDIOSAMPLINGFREQ: + case MATROSKA_ID_SAMPLINGFREQUENCY: fnum = ebml_read_float(s, &l); if (fnum == EBML_FLOAT_INVALID) return 0; @@ -616,7 +616,7 @@ static int demux_mkv_read_trackaudio(demuxer_t *demuxer, mkv_track_t *track) "[mkv] | + Sampling frequency: %f\n", track->a_sfreq); break; - case MATROSKA_ID_AUDIOBITDEPTH: + case MATROSKA_ID_BITDEPTH: num = ebml_read_uint(s, &l); if (num == EBML_UINT_INVALID) return 0; @@ -625,7 +625,7 @@ static int demux_mkv_read_trackaudio(demuxer_t *demuxer, mkv_track_t *track) track->a_bps); break; - case MATROSKA_ID_AUDIOCHANNELS: + case MATROSKA_ID_CHANNELS: num = ebml_read_uint(s, &l); if (num == EBML_UINT_INVALID) return 0; @@ -655,7 +655,7 @@ static int demux_mkv_read_trackvideo(demuxer_t *demuxer, mkv_track_t *track) len += il; while (length > 0) { switch (ebml_read_id(s, &il)) { - case MATROSKA_ID_VIDEOFRAMERATE: + case MATROSKA_ID_FRAMERATE: fnum = ebml_read_float(s, &l); if (fnum == EBML_FLOAT_INVALID) return 0; @@ -666,7 +666,7 @@ static int demux_mkv_read_trackvideo(demuxer_t *demuxer, mkv_track_t *track) track->default_duration = 1 / track->v_frate; break; - case MATROSKA_ID_VIDEODISPLAYWIDTH: + case MATROSKA_ID_DISPLAYWIDTH: num = ebml_read_uint(s, &l); if (num == EBML_UINT_INVALID) return 0; @@ -675,7 +675,7 @@ static int demux_mkv_read_trackvideo(demuxer_t *demuxer, mkv_track_t *track) track->v_dwidth); break; - case MATROSKA_ID_VIDEODISPLAYHEIGHT: + case MATROSKA_ID_DISPLAYHEIGHT: num = ebml_read_uint(s, &l); if (num == EBML_UINT_INVALID) return 0; @@ -684,7 +684,7 @@ static int demux_mkv_read_trackvideo(demuxer_t *demuxer, mkv_track_t *track) track->v_dheight); break; - case MATROSKA_ID_VIDEOPIXELWIDTH: + case MATROSKA_ID_PIXELWIDTH: num = ebml_read_uint(s, &l); if (num == EBML_UINT_INVALID) return 0; @@ -693,7 +693,7 @@ static int demux_mkv_read_trackvideo(demuxer_t *demuxer, mkv_track_t *track) track->v_width); break; - case MATROSKA_ID_VIDEOPIXELHEIGHT: + case MATROSKA_ID_PIXELHEIGHT: num = ebml_read_uint(s, &l); if (num == EBML_UINT_INVALID) return 0; @@ -755,7 +755,7 @@ static int demux_mkv_read_trackentry(demuxer_t *demuxer) track->tnum); break; - case MATROSKA_ID_TRACKNAME: + case MATROSKA_ID_NAME: track->name = ebml_read_utf8(s, &l); if (track->name == NULL) goto err_out; @@ -785,14 +785,14 @@ static int demux_mkv_read_trackentry(demuxer_t *demuxer) } break; - case MATROSKA_ID_TRACKAUDIO: + case MATROSKA_ID_AUDIO: mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Audio track\n"); l = demux_mkv_read_trackaudio(demuxer, track); if (l == 0) goto err_out; break; - case MATROSKA_ID_TRACKVIDEO: + case MATROSKA_ID_VIDEO: mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + Video track\n"); l = demux_mkv_read_trackvideo(demuxer, track); if (l == 0) @@ -838,7 +838,7 @@ static int demux_mkv_read_trackentry(demuxer_t *demuxer) track->private_size); break; - case MATROSKA_ID_TRACKLANGUAGE: + case MATROSKA_ID_LANGUAGE: free(track->language); track->language = ebml_read_utf8(s, &l); if (track->language == NULL) @@ -847,7 +847,7 @@ static int demux_mkv_read_trackentry(demuxer_t *demuxer) track->language); break; - case MATROSKA_ID_TRACKFLAGDEFAULT: + case MATROSKA_ID_FLAGDEFAULT: num = ebml_read_uint(s, &l); if (num == EBML_UINT_INVALID) goto err_out; @@ -856,7 +856,7 @@ static int demux_mkv_read_trackentry(demuxer_t *demuxer) track->default_track); break; - case MATROSKA_ID_TRACKDEFAULTDURATION: + case MATROSKA_ID_DEFAULTDURATION: num = ebml_read_uint(s, &l); if (num == EBML_UINT_INVALID) goto err_out; @@ -873,7 +873,7 @@ static int demux_mkv_read_trackentry(demuxer_t *demuxer) } break; - case MATROSKA_ID_TRACKENCODINGS: + case MATROSKA_ID_CONTENTENCODINGS: l = demux_mkv_read_trackencodings(demuxer, track); if (l == 0) goto err_out; @@ -955,7 +955,7 @@ static int demux_mkv_read_cues(demuxer_t *demuxer) time = track = pos = EBML_UINT_INVALID; switch (ebml_read_id(s, &il)) { - case MATROSKA_ID_POINTENTRY:; + case MATROSKA_ID_CUEPOINT:; uint64_t len; len = ebml_read_length(s, &i); @@ -970,7 +970,7 @@ static int demux_mkv_read_cues(demuxer_t *demuxer) time = ebml_read_uint(s, &l); break; - case MATROSKA_ID_CUETRACKPOSITION:; + case MATROSKA_ID_CUETRACKPOSITIONS:; uint64_t le = ebml_read_length(s, &i); l = le + i; @@ -1386,7 +1386,7 @@ static int demux_mkv_read_seekhead(demuxer_t *demuxer) seek_pos = EBML_UINT_INVALID; switch (ebml_read_id(s, &il)) { - case MATROSKA_ID_SEEKENTRY:; + case MATROSKA_ID_SEEK:; uint64_t len = ebml_read_length(s, &i); l = len + i; @@ -2747,7 +2747,7 @@ static int demux_mkv_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds) if (mkv_d->cluster_size > 0) { switch (ebml_read_id(s, &il)) { - case MATROSKA_ID_CLUSTERTIMECODE:; + case MATROSKA_ID_TIMECODE:; uint64_t num = ebml_read_uint(s, &l); if (num == EBML_UINT_INVALID) return 0; @@ -2858,7 +2858,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, if (type == MATROSKA_ID_CLUSTER) { while (!s->eof && stream_tell(s) < end) { if (ebml_read_id(s, NULL) - == MATROSKA_ID_CLUSTERTIMECODE) { + == MATROSKA_ID_TIMECODE) { uint64_t tc = ebml_read_uint(s, NULL); tc *= mkv_d->tc_scale; add_cluster_position(mkv_d, start, tc); diff --git a/libmpdemux/ebml.c b/libmpdemux/ebml.c index 30528e15cb..37e2fe0ffb 100644 --- a/libmpdemux/ebml.c +++ b/libmpdemux/ebml.c @@ -1,5 +1,6 @@ /* * native ebml reader for the Matroska demuxer + * new parser copyright (c) 2010 Uoti Urpala * copyright (c) 2004 Aurelien Jacobs * based on the one written by Ronald Bultje for gstreamer * @@ -23,13 +24,18 @@ #include "config.h" #include +#include +#include +#include +#include -#include "stream/stream.h" +#include +#include +#include "talloc.h" #include "ebml.h" -#include "libavutil/common.h" +#include "stream/stream.h" #include "mpbswap.h" -#include "libavutil/intfloat_readwrite.h" - +#include "mp_msg.h" #ifndef SIZE_MAX #define SIZE_MAX ((size_t)-1) @@ -288,7 +294,7 @@ char *ebml_read_header(stream_t *s, int *version) uint32_t id; char *str = NULL; - if (ebml_read_master(s, &length) != EBML_ID_HEADER) + if (ebml_read_master(s, &length) != EBML_ID_EBML) return 0; if (version) @@ -350,3 +356,385 @@ char *ebml_read_header(stream_t *s, int *version) return str; } + + + +#define EVALARGS(F, ...) F(__VA_ARGS__) +#define E(str, N, type) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, type }; +#define E_SN(str, count, N) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, EBML_TYPE_SUBELEMENTS, sizeof(struct ebml_ ## N), count, (const struct ebml_field_desc[]){ +#define E_S(str, count) EVALARGS(E_SN, str, count, N) +#define FN(id, name, multiple, N) { id, multiple, offsetof(struct ebml_ ## N, name), offsetof(struct ebml_ ## N, n_ ## name), &ebml_##name##_desc}, +#define F(id, name, multiple) EVALARGS(FN, id, name, multiple, N) +#include "ebml_defs.c" +#undef EVALARGS +#undef SN +#undef S +#undef FN +#undef F + +// Used to read/write pointers to different struct types +struct generic; +#define generic_struct struct generic + +static uint32_t ebml_parse_id(uint8_t *data, int *length) +{ + int len = 1; + uint32_t id = *data++; + for (int len_mask = 0x80; !(id & len_mask); len_mask >>= 1) { + len++; + if (len > 4) { + *length = -1; + return EBML_ID_INVALID; + } + } + *length = len; + while (--len) + id = (id << 8) | *data++; + return id; +} + +static uint64_t parse_vlen(uint8_t *data, int *length, bool is_length) +{ + uint64_t r = *data++; + int len = 1; + int len_mask; + for (len_mask = 0x80; !(r & len_mask); len_mask >>= 1) { + len++; + if (len > 8) { + *length = -1; + return -1; + } + } + r &= len_mask - 1; + + int num_allones = 0; + if (r == len_mask - 1) + num_allones++; + for (int i = 1; i < len; i++) { + if (*data == 255) + num_allones++; + r = (r << 8) | *data++; + } + if (is_length && num_allones == len) { + // According to Matroska specs this means "unknown length" + // Could be supported if there are any actual files using it + *length = -1; + return -1; + } + *length = len; + return r; +} + +static uint64_t ebml_parse_length(uint8_t *data, int *length) +{ + return parse_vlen(data, length, true); +} + +static uint64_t ebml_parse_uint(uint8_t *data, int length) +{ + assert(length >= 1 && length <= 8); + uint64_t r = 0; + while (length--) + r = (r << 8) + *data++; + return r; +} + +static int64_t ebml_parse_sint(uint8_t *data, int length) +{ + assert(length >=1 && length <= 8); + int64_t r = 0; + if (*data & 0x80) + r = -1; + while (length--) + r = (r << 8) | *data++; + return r; +} + +static double ebml_parse_float(uint8_t *data, int length) +{ + assert(length == 4 || length == 8); + uint64_t i = ebml_parse_uint(data, length); + if (length == 4) + return av_int2flt(i); + else + return av_int2dbl(i); +} + + +// target must be initialized to zero +static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target, + uint8_t *data, int size, + const struct ebml_elem_desc *type, int level) +{ + assert(type->type == EBML_TYPE_SUBELEMENTS); + assert(level < 8); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "%.*s[mkv] Parsing element %s\n", + level, " ", type->name); + + char *s = target; + int len; + uint8_t *end = data + size; + uint8_t *p = data; + int num_elems[MAX_EBML_SUBELEMENTS] = {}; + while (p < end) { + uint8_t *startp = p; + uint32_t id = ebml_parse_id(p, &len); + if (len > end - p) + goto past_end_error; + if (len < 0) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Error parsing subelement " + "id\n"); + goto other_error; + } + p += len; + uint64_t length = ebml_parse_length(p, &len); + if (len > end - p) + goto past_end_error; + if (len < 0) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Error parsing subelement " + "length\n"); + goto other_error; + } + p += len; + + int field_idx = -1; + for (int i = 0; i < type->field_count; i++) + if (type->fields[i].id == id) { + field_idx = i; + num_elems[i]++; + break; + } + + if (length > end - p) { + if (field_idx >= 0 && type->fields[field_idx].desc->type + != EBML_TYPE_SUBELEMENTS) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Subelement content goes " + "past end of containing element\n"); + goto other_error; + } + // Try to parse what is possible from inside this partial element + ctx->has_errors = true; + length = end - p; + } + p += length; + + continue; + + past_end_error: + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Subelement headers go " + "past end of containing element\n"); + other_error: + ctx->has_errors = true; + end = startp; + break; + } + + for (int i = 0; i < type->field_count; i++) + if (num_elems[i] && type->fields[i].multiple) { + char *ptr = s + type->fields[i].offset; + switch (type->fields[i].desc->type) { + case EBML_TYPE_SUBELEMENTS: + num_elems[i] = FFMIN(num_elems[i], + 1000000000 / type->fields[i].desc->size); + int size = num_elems[i] * type->fields[i].desc->size; + *(generic_struct **) ptr = talloc_zero_size(ctx->talloc_ctx, + size); + break; + case EBML_TYPE_UINT: + *(uint64_t **) ptr = talloc_zero_array(ctx->talloc_ctx, + uint64_t, num_elems[i]); + break; + case EBML_TYPE_SINT: + *(int64_t **) ptr = talloc_zero_array(ctx->talloc_ctx, + int64_t, num_elems[i]); + break; + case EBML_TYPE_FLOAT: + *(double **) ptr = talloc_zero_array(ctx->talloc_ctx, + double, num_elems[i]); + break; + case EBML_TYPE_STR: + case EBML_TYPE_BINARY: + *(struct bstr **) ptr = talloc_zero_array(ctx->talloc_ctx, + struct bstr, + num_elems[i]); + break; + case EBML_TYPE_EBML_ID: + *(int32_t **) ptr = talloc_zero_array(ctx->talloc_ctx, + uint32_t, num_elems[i]); + break; + default: + abort(); + } + } + + while (data < end) { + int len; + uint32_t id = ebml_parse_id(data, &len); + assert(len >= 0 && len <= end - data); + data += len; + uint64_t length = ebml_parse_length(data, &len); + assert(len >= 0 && len <= end - data); + data += len; + if (length > end - data) { + // Try to parse what is possible from inside this partial element + length = end - data; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Next subelement content goes " + "past end of containing element, will be truncated\n"); + } + int field_idx = -1; + for (int i = 0; i < type->field_count; i++) + if (type->fields[i].id == id) { + field_idx = i; + break; + } + if (field_idx < 0) { + if (id == 0xec) + mp_msg(MSGT_DEMUX, MSGL_DBG2, "%.*s[mkv] Ignoring Void element " + "size: %"PRIu64"\n", level+1, " ", length); + else if (id == 0xbf) + mp_msg(MSGT_DEMUX, MSGL_DBG2, "%.*s[mkv] Ignoring CRC-32 " + "element size: %"PRIu64"\n", level+1, " ", + length); + else + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Ignoring unrecognized " + "subelement. ID: %x size: %"PRIu64"\n", id, length); + data += length; + continue; + } + const struct ebml_field_desc *fd = &type->fields[field_idx]; + const struct ebml_elem_desc *ed = fd->desc; + bool multiple = fd->multiple; + int *countptr = (int *) (s + fd->count_offset); + if (*countptr >= num_elems[field_idx]) { + // Shouldn't happen with on any sane file without bugs + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Too many subelems?\n"); + ctx->has_errors = true; + data += length; + continue; + } + if (*countptr > 0 && !multiple) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "[mkv] Another subelement of type " + "%x %s (size: %"PRIu64"). Only one allowed. Ignoring.\n", + id, ed->name, length); + ctx->has_errors = true; + data += length; + continue; + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, "%.*s[mkv] Parsing %x %s size: %"PRIu64 + " value: ", level+1, " ", id, ed->name, length); + + char *fieldptr = s + fd->offset; + switch (ed->type) { + case EBML_TYPE_SUBELEMENTS: + mp_msg(MSGT_DEMUX, MSGL_DBG2, "subelements\n"); + char *subelptr; + if (multiple) { + char *array_start = (char *) *(generic_struct **) fieldptr; + subelptr = array_start + *countptr * ed->size; + } else + subelptr = fieldptr; + ebml_parse_element(ctx, subelptr, data, length, ed, level + 1); + break; + + case EBML_TYPE_UINT:; + uint64_t *uintptr; +#define GETPTR(subelptr, fieldtype) \ + if (multiple) \ + subelptr = *(fieldtype **) fieldptr + *countptr; \ + else \ + subelptr = (fieldtype *) fieldptr + GETPTR(uintptr, uint64_t); + if (length < 1 || length > 8) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "uint invalid length %"PRIu64 + "\n", length); + goto error; + } + *uintptr = ebml_parse_uint(data, length); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "uint %"PRIu64"\n", *uintptr); + break; + + case EBML_TYPE_SINT:; + int64_t *sintptr; + GETPTR(sintptr, int64_t); + if (length < 1 || length > 8) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "sint invalid length %"PRIu64 + "\n", length); + goto error; + } + *sintptr = ebml_parse_sint(data, length); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "sint %"PRId64"\n", *sintptr); + break; + + case EBML_TYPE_FLOAT:; + double *floatptr; + GETPTR(floatptr, double); + if (length != 4 && length != 8) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "float invalid length %"PRIu64 + "\n", length); + goto error; + } + *floatptr = ebml_parse_float(data, length); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "float %f\n", *floatptr); + break; + + case EBML_TYPE_STR: + case EBML_TYPE_BINARY:; + struct bstr *strptr; + GETPTR(strptr, struct bstr); + strptr->start = data; + strptr->len = length; + if (ed->type == EBML_TYPE_STR) + mp_msg(MSGT_DEMUX, MSGL_DBG2, "string \"%.*s\"\n", + strptr->len, strptr->start); + else + mp_msg(MSGT_DEMUX, MSGL_DBG2, "binary %d bytes\n", + strptr->len); + break; + + case EBML_TYPE_EBML_ID:; + uint32_t *idptr; + GETPTR(idptr, uint32_t); + *idptr = ebml_parse_id(data, &len); + if (len != length) { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "ebml_id broken value\n"); + goto error; + } + mp_msg(MSGT_DEMUX, MSGL_DBG2, "ebml_id %x\n", (unsigned)*idptr); + break; + default: + abort(); + } + *countptr += 1; + error: + data += length; + } +} + +// target must be initialized to zero +int ebml_read_element(struct stream *s, struct ebml_parse_ctx *ctx, + void *target, const struct ebml_elem_desc *desc) +{ + ctx->has_errors = false; + int msglevel = ctx->no_error_messages ? MSGL_DBG2 : MSGL_WARN; + uint64_t length = ebml_read_length(s, NULL); + if (s->eof) { + mp_msg(MSGT_DEMUX, msglevel, "[mkv] Unexpected end of file " + "- partial or corrupt file?\n"); + return -1; + } + if (length > 1000000000) { + mp_msg(MSGT_DEMUX, msglevel, "[mkv] Refusing to read element over " + "100 MB in size\n"); + return -1; + } + ctx->talloc_ctx = talloc_size(NULL, length + 8); + int read_len = stream_read(s, ctx->talloc_ctx, length); + if (read_len < length) + mp_msg(MSGT_DEMUX, msglevel, "[mkv] Unexpected end of file " + "- partial or corrupt file?\n"); + ebml_parse_element(ctx, target, ctx->talloc_ctx, read_len, desc, 0); + if (ctx->has_errors) + mp_msg(MSGT_DEMUX, msglevel, "[mkv] Error parsing element %s\n", + desc->name); + return 0; +} diff --git a/libmpdemux/ebml.h b/libmpdemux/ebml.h index c3a785099c..fc6a0ddbac 100644 --- a/libmpdemux/ebml.h +++ b/libmpdemux/ebml.h @@ -20,158 +20,56 @@ #define MPLAYER_EBML_H #include +#include +#include + #include "stream/stream.h" /* EBML version supported */ #define EBML_VERSION 1 -/* - * EBML element IDs. max. 32-bit. - */ +enum ebml_elemtype { + EBML_TYPE_SUBELEMENTS, + EBML_TYPE_UINT, + EBML_TYPE_SINT, + EBML_TYPE_FLOAT, + EBML_TYPE_STR, + EBML_TYPE_BINARY, + EBML_TYPE_EBML_ID, +}; -/* top-level master-IDs */ -#define EBML_ID_HEADER 0x1A45DFA3 +struct ebml_field_desc { + uint32_t id; + bool multiple; + int offset; + int count_offset; + const struct ebml_elem_desc *desc; +}; -/* IDs in the HEADER master */ -#define EBML_ID_EBMLVERSION 0x4286 -#define EBML_ID_EBMLREADVERSION 0x42F7 -#define EBML_ID_EBMLMAXIDLENGTH 0x42F2 -#define EBML_ID_EBMLMAXSIZELENGTH 0x42F3 -#define EBML_ID_DOCTYPE 0x4282 -#define EBML_ID_DOCTYPEVERSION 0x4287 -#define EBML_ID_DOCTYPEREADVERSION 0x4285 +struct ebml_elem_desc { + char *name; + enum ebml_elemtype type; + int size; + int field_count; + const struct ebml_field_desc *fields; +}; -/* general EBML types */ -#define EBML_ID_VOID 0xEC +struct ebml_parse_ctx { + void *talloc_ctx; + bool has_errors; + bool no_error_messages; +}; -/* ID returned in error cases */ -#define EBML_ID_INVALID 0xFFFFFFFF +struct bstr { + uint8_t *start; + int len; +}; +#include "ebml_types.h" -/* - * Matroska element IDs. max. 32-bit. - */ +#define EBML_ID_INVALID 0xffffffff -/* toplevel segment */ -#define MATROSKA_ID_SEGMENT 0x18538067 - -/* matroska top-level master IDs */ -#define MATROSKA_ID_INFO 0x1549A966 -#define MATROSKA_ID_TRACKS 0x1654AE6B -#define MATROSKA_ID_CUES 0x1C53BB6B -#define MATROSKA_ID_TAGS 0x1254C367 -#define MATROSKA_ID_SEEKHEAD 0x114D9B74 -#define MATROSKA_ID_ATTACHMENTS 0x1941A469 -#define MATROSKA_ID_CHAPTERS 0x1043A770 -#define MATROSKA_ID_CLUSTER 0x1F43B675 - -/* IDs in the info master */ -#define MATROSKA_ID_TIMECODESCALE 0x2AD7B1 -#define MATROSKA_ID_DURATION 0x4489 -#define MATROSKA_ID_WRITINGAPP 0x5741 -#define MATROSKA_ID_MUXINGAPP 0x4D80 -#define MATROSKA_ID_DATEUTC 0x4461 -#define MATROSKA_ID_SEGMENTUID 0x73A4 - -/* ID in the tracks master */ -#define MATROSKA_ID_TRACKENTRY 0xAE - -/* IDs in the trackentry master */ -#define MATROSKA_ID_TRACKNUMBER 0xD7 -#define MATROSKA_ID_TRACKUID 0x73C5 -#define MATROSKA_ID_TRACKTYPE 0x83 -#define MATROSKA_ID_TRACKAUDIO 0xE1 -#define MATROSKA_ID_TRACKVIDEO 0xE0 -#define MATROSKA_ID_CODECID 0x86 -#define MATROSKA_ID_CODECPRIVATE 0x63A2 -#define MATROSKA_ID_CODECNAME 0x258688 -#define MATROSKA_ID_CODECINFOURL 0x3B4040 -#define MATROSKA_ID_CODECDOWNLOADURL 0x26B240 -#define MATROSKA_ID_TRACKNAME 0x536E -#define MATROSKA_ID_TRACKLANGUAGE 0x22B59C -#define MATROSKA_ID_TRACKFLAGENABLED 0xB9 -#define MATROSKA_ID_TRACKFLAGDEFAULT 0x88 -#define MATROSKA_ID_TRACKFLAGLACING 0x9C -#define MATROSKA_ID_TRACKMINCACHE 0x6DE7 -#define MATROSKA_ID_TRACKMAXCACHE 0x6DF8 -#define MATROSKA_ID_TRACKDEFAULTDURATION 0x23E383 -#define MATROSKA_ID_TRACKENCODINGS 0x6D80 - -/* IDs in the trackaudio master */ -#define MATROSKA_ID_AUDIOSAMPLINGFREQ 0xB5 -#define MATROSKA_ID_AUDIOBITDEPTH 0x6264 -#define MATROSKA_ID_AUDIOCHANNELS 0x9F - -/* IDs in the trackvideo master */ -#define MATROSKA_ID_VIDEOFRAMERATE 0x2383E3 -#define MATROSKA_ID_VIDEODISPLAYWIDTH 0x54B0 -#define MATROSKA_ID_VIDEODISPLAYHEIGHT 0x54BA -#define MATROSKA_ID_VIDEOPIXELWIDTH 0xB0 -#define MATROSKA_ID_VIDEOPIXELHEIGHT 0xBA -#define MATROSKA_ID_VIDEOFLAGINTERLACED 0x9A -#define MATROSKA_ID_VIDEOSTEREOMODE 0x53B9 -#define MATROSKA_ID_VIDEODISPLAYUNIT 0x54B2 -#define MATROSKA_ID_VIDEOASPECTRATIO 0x54B3 -#define MATROSKA_ID_VIDEOCOLOURSPACE 0x2EB524 -#define MATROSKA_ID_VIDEOGAMMA 0x2FB523 - -/* IDs in the trackencodings master */ -#define MATROSKA_ID_CONTENTENCODING 0x6240 -#define MATROSKA_ID_CONTENTENCODINGORDER 0x5031 -#define MATROSKA_ID_CONTENTENCODINGSCOPE 0x5032 -#define MATROSKA_ID_CONTENTENCODINGTYPE 0x5033 -#define MATROSKA_ID_CONTENTCOMPRESSION 0x5034 -#define MATROSKA_ID_CONTENTCOMPALGO 0x4254 -#define MATROSKA_ID_CONTENTCOMPSETTINGS 0x4255 - -/* ID in the cues master */ -#define MATROSKA_ID_POINTENTRY 0xBB - -/* IDs in the pointentry master */ -#define MATROSKA_ID_CUETIME 0xB3 -#define MATROSKA_ID_CUETRACKPOSITION 0xB7 - -/* IDs in the cuetrackposition master */ -#define MATROSKA_ID_CUETRACK 0xF7 -#define MATROSKA_ID_CUECLUSTERPOSITION 0xF1 - -/* IDs in the seekhead master */ -#define MATROSKA_ID_SEEKENTRY 0x4DBB - -/* IDs in the seekpoint master */ -#define MATROSKA_ID_SEEKID 0x53AB -#define MATROSKA_ID_SEEKPOSITION 0x53AC - -/* IDs in the chapters master */ -#define MATROSKA_ID_EDITIONENTRY 0x45B9 -#define MATROSKA_ID_EDITIONFLAGDEFAULT 0x45DB -#define MATROSKA_ID_EDITIONFLAGORDERED 0x45DD -#define MATROSKA_ID_CHAPTERATOM 0xB6 -#define MATROSKA_ID_CHAPTERTIMESTART 0x91 -#define MATROSKA_ID_CHAPTERTIMEEND 0x92 -#define MATROSKA_ID_CHAPTERDISPLAY 0x80 -#define MATROSKA_ID_CHAPSTRING 0x85 -#define MATROSKA_ID_CHAPTERSEGMENTUID 0x6E67 -#define MATROSKA_ID_CHAPTERSEGMENTEDITIONUID\ - 0x6EBC - -/* IDs in the cluster master */ -#define MATROSKA_ID_CLUSTERTIMECODE 0xE7 -#define MATROSKA_ID_BLOCKGROUP 0xA0 - -/* IDs in the blockgroup master */ -#define MATROSKA_ID_BLOCKDURATION 0x9B -#define MATROSKA_ID_BLOCK 0xA1 -#define MATROSKA_ID_SIMPLEBLOCK 0xA3 -#define MATROSKA_ID_REFERENCEBLOCK 0xFB - -/* IDs in the attachments master */ -#define MATROSKA_ID_ATTACHEDFILE 0x61A7 -#define MATROSKA_ID_FILENAME 0x466E -#define MATROSKA_ID_FILEMIMETYPE 0x4660 -#define MATROSKA_ID_FILEDATA 0x465C -#define MATROSKA_ID_FILEUID 0x46AE /* matroska track types */ #define MATROSKA_TRACK_VIDEO 0x01 /* rectangle-shaped pictures aka video */ @@ -213,4 +111,7 @@ int ebml_read_skip (stream_t *s, uint64_t *length); uint32_t ebml_read_master (stream_t *s, uint64_t *length); char *ebml_read_header (stream_t *s, int *version); +int ebml_read_element(struct stream *s, struct ebml_parse_ctx *ctx, + void *target, const struct ebml_elem_desc *desc); + #endif /* MPLAYER_EBML_H */ diff --git a/libmpdemux/ebml_defs.c b/libmpdemux/ebml_defs.c new file mode 100644 index 0000000000..f0296a3d62 --- /dev/null +++ b/libmpdemux/ebml_defs.c @@ -0,0 +1,382 @@ +// Generated by TOOLS/matroska.py, do not edit manually + + +E("TargetTypeValue", target_type_value, EBML_TYPE_UINT) + +#define N targets +E_S("Targets", 1) +F(MATROSKA_ID_TARGETTYPEVALUE, target_type_value, 0) +}}; +#undef N + +#define N tag +E_S("Tag", 1) +F(MATROSKA_ID_TARGETS, targets, 0) +}}; +#undef N + +#define N tags +E_S("Tags", 1) +F(MATROSKA_ID_TAG, tag, 1) +}}; +#undef N + +E("ChapLanguage", chap_language, EBML_TYPE_STR) + +E("ChapString", chap_string, EBML_TYPE_STR) + +#define N chapter_display +E_S("ChapterDisplay", 2) +F(MATROSKA_ID_CHAPSTRING, chap_string, 0) +F(MATROSKA_ID_CHAPLANGUAGE, chap_language, 1) +}}; +#undef N + +E("ChapterSegmentEditionUID", chapter_segment_edition_uid, EBML_TYPE_UINT) + +E("ChapterSegmentUID", chapter_segment_uid, EBML_TYPE_BINARY) + +E("ChapterFlagEnabled", chapter_flag_enabled, EBML_TYPE_UINT) + +E("ChapterFlagHidden", chapter_flag_hidden, EBML_TYPE_UINT) + +E("ChapterTimeEnd", chapter_time_end, EBML_TYPE_UINT) + +E("ChapterTimeStart", chapter_time_start, EBML_TYPE_UINT) + +E("ChapterUID", chapter_uid, EBML_TYPE_UINT) + +#define N chapter_atom +E_S("ChapterAtom", 8) +F(MATROSKA_ID_CHAPTERUID, chapter_uid, 0) +F(MATROSKA_ID_CHAPTERTIMESTART, chapter_time_start, 0) +F(MATROSKA_ID_CHAPTERTIMEEND, chapter_time_end, 0) +F(MATROSKA_ID_CHAPTERFLAGHIDDEN, chapter_flag_hidden, 0) +F(MATROSKA_ID_CHAPTERFLAGENABLED, chapter_flag_enabled, 0) +F(MATROSKA_ID_CHAPTERSEGMENTUID, chapter_segment_uid, 0) +F(MATROSKA_ID_CHAPTERSEGMENTEDITIONUID, chapter_segment_edition_uid, 0) +F(MATROSKA_ID_CHAPTERDISPLAY, chapter_display, 1) +}}; +#undef N + +E("EditionFlagOrdered", edition_flag_ordered, EBML_TYPE_UINT) + +E("EditionFlagDefault", edition_flag_default, EBML_TYPE_UINT) + +E("EditionFlagHidden", edition_flag_hidden, EBML_TYPE_UINT) + +E("EditionUID", edition_uid, EBML_TYPE_UINT) + +#define N edition_entry +E_S("EditionEntry", 5) +F(MATROSKA_ID_EDITIONUID, edition_uid, 0) +F(MATROSKA_ID_EDITIONFLAGHIDDEN, edition_flag_hidden, 0) +F(MATROSKA_ID_EDITIONFLAGDEFAULT, edition_flag_default, 0) +F(MATROSKA_ID_EDITIONFLAGORDERED, edition_flag_ordered, 0) +F(MATROSKA_ID_CHAPTERATOM, chapter_atom, 1) +}}; +#undef N + +#define N chapters +E_S("Chapters", 1) +F(MATROSKA_ID_EDITIONENTRY, edition_entry, 1) +}}; +#undef N + +E("FileUID", file_uid, EBML_TYPE_UINT) + +E("FileData", file_data, EBML_TYPE_BINARY) + +E("FileMimeType", file_mime_type, EBML_TYPE_STR) + +E("FileName", file_name, EBML_TYPE_STR) + +#define N attached_file +E_S("AttachedFile", 4) +F(MATROSKA_ID_FILENAME, file_name, 0) +F(MATROSKA_ID_FILEMIMETYPE, file_mime_type, 0) +F(MATROSKA_ID_FILEDATA, file_data, 0) +F(MATROSKA_ID_FILEUID, file_uid, 0) +}}; +#undef N + +#define N attachments +E_S("Attachments", 1) +F(MATROSKA_ID_ATTACHEDFILE, attached_file, 1) +}}; +#undef N + +E("CueClusterPosition", cue_cluster_position, EBML_TYPE_UINT) + +E("CueTrack", cue_track, EBML_TYPE_UINT) + +#define N cue_track_positions +E_S("CueTrackPositions", 2) +F(MATROSKA_ID_CUETRACK, cue_track, 0) +F(MATROSKA_ID_CUECLUSTERPOSITION, cue_cluster_position, 0) +}}; +#undef N + +E("CueTime", cue_time, EBML_TYPE_UINT) + +#define N cue_point +E_S("CuePoint", 2) +F(MATROSKA_ID_CUETIME, cue_time, 0) +F(MATROSKA_ID_CUETRACKPOSITIONS, cue_track_positions, 1) +}}; +#undef N + +#define N cues +E_S("Cues", 1) +F(MATROSKA_ID_CUEPOINT, cue_point, 1) +}}; +#undef N + +E("ContentCompSettings", content_comp_settings, EBML_TYPE_BINARY) + +E("ContentCompAlgo", content_comp_algo, EBML_TYPE_UINT) + +#define N content_compression +E_S("ContentCompression", 2) +F(MATROSKA_ID_CONTENTCOMPALGO, content_comp_algo, 0) +F(MATROSKA_ID_CONTENTCOMPSETTINGS, content_comp_settings, 0) +}}; +#undef N + +E("ContentEncodingType", content_encoding_type, EBML_TYPE_UINT) + +E("ContentEncodingScope", content_encoding_scope, EBML_TYPE_UINT) + +E("ContentEncodingOrder", content_encoding_order, EBML_TYPE_UINT) + +#define N content_encoding +E_S("ContentEncoding", 4) +F(MATROSKA_ID_CONTENTENCODINGORDER, content_encoding_order, 0) +F(MATROSKA_ID_CONTENTENCODINGSCOPE, content_encoding_scope, 0) +F(MATROSKA_ID_CONTENTENCODINGTYPE, content_encoding_type, 0) +F(MATROSKA_ID_CONTENTCOMPRESSION, content_compression, 0) +}}; +#undef N + +#define N content_encodings +E_S("ContentEncodings", 1) +F(MATROSKA_ID_CONTENTENCODING, content_encoding, 1) +}}; +#undef N + +E("BitDepth", bit_depth, EBML_TYPE_UINT) + +E("Channels", channels, EBML_TYPE_UINT) + +E("SamplingFrequency", sampling_frequency, EBML_TYPE_FLOAT) + +#define N audio +E_S("Audio", 3) +F(MATROSKA_ID_SAMPLINGFREQUENCY, sampling_frequency, 0) +F(MATROSKA_ID_CHANNELS, channels, 0) +F(MATROSKA_ID_BITDEPTH, bit_depth, 0) +}}; +#undef N + +E("FrameRate", frame_rate, EBML_TYPE_FLOAT) + +E("DisplayHeight", display_height, EBML_TYPE_UINT) + +E("DisplayWidth", display_width, EBML_TYPE_UINT) + +E("PixelHeight", pixel_height, EBML_TYPE_UINT) + +E("PixelWidth", pixel_width, EBML_TYPE_UINT) + +E("FlagInterlaced", flag_interlaced, EBML_TYPE_UINT) + +#define N video +E_S("Video", 6) +F(MATROSKA_ID_FLAGINTERLACED, flag_interlaced, 0) +F(MATROSKA_ID_PIXELWIDTH, pixel_width, 0) +F(MATROSKA_ID_PIXELHEIGHT, pixel_height, 0) +F(MATROSKA_ID_DISPLAYWIDTH, display_width, 0) +F(MATROSKA_ID_DISPLAYHEIGHT, display_height, 0) +F(MATROSKA_ID_FRAMERATE, frame_rate, 0) +}}; +#undef N + +E("CodecDecodeAll", codec_decode_all, EBML_TYPE_UINT) + +E("CodecPrivate", codec_private, EBML_TYPE_BINARY) + +E("CodecID", codec_id, EBML_TYPE_STR) + +E("Language", language, EBML_TYPE_STR) + +E("Name", name, EBML_TYPE_STR) + +E("MaxBlockAdditionID", max_block_addition_id, EBML_TYPE_UINT) + +E("TrackTimecodeScale", track_timecode_scale, EBML_TYPE_FLOAT) + +E("DefaultDuration", default_duration, EBML_TYPE_UINT) + +E("MinCache", min_cache, EBML_TYPE_UINT) + +E("FlagLacing", flag_lacing, EBML_TYPE_UINT) + +E("FlagForced", flag_forced, EBML_TYPE_UINT) + +E("FlagDefault", flag_default, EBML_TYPE_UINT) + +E("FlagEnabled", flag_enabled, EBML_TYPE_UINT) + +E("TrackType", track_type, EBML_TYPE_UINT) + +E("TrackUID", track_uid, EBML_TYPE_UINT) + +E("TrackNumber", track_number, EBML_TYPE_UINT) + +#define N track_entry +E_S("TrackEntry", 19) +F(MATROSKA_ID_TRACKNUMBER, track_number, 0) +F(MATROSKA_ID_TRACKUID, track_uid, 0) +F(MATROSKA_ID_TRACKTYPE, track_type, 0) +F(MATROSKA_ID_FLAGENABLED, flag_enabled, 0) +F(MATROSKA_ID_FLAGDEFAULT, flag_default, 0) +F(MATROSKA_ID_FLAGFORCED, flag_forced, 0) +F(MATROSKA_ID_FLAGLACING, flag_lacing, 0) +F(MATROSKA_ID_MINCACHE, min_cache, 0) +F(MATROSKA_ID_DEFAULTDURATION, default_duration, 0) +F(MATROSKA_ID_TRACKTIMECODESCALE, track_timecode_scale, 0) +F(MATROSKA_ID_MAXBLOCKADDITIONID, max_block_addition_id, 0) +F(MATROSKA_ID_NAME, name, 0) +F(MATROSKA_ID_LANGUAGE, language, 0) +F(MATROSKA_ID_CODECID, codec_id, 0) +F(MATROSKA_ID_CODECPRIVATE, codec_private, 0) +F(MATROSKA_ID_CODECDECODEALL, codec_decode_all, 0) +F(MATROSKA_ID_VIDEO, video, 0) +F(MATROSKA_ID_AUDIO, audio, 0) +F(MATROSKA_ID_CONTENTENCODINGS, content_encodings, 0) +}}; +#undef N + +#define N tracks +E_S("Tracks", 1) +F(MATROSKA_ID_TRACKENTRY, track_entry, 1) +}}; +#undef N + +E("SimpleBlock", simple_block, EBML_TYPE_BINARY) + +E("ReferenceBlock", reference_block, EBML_TYPE_SINT) + +E("BlockDuration", block_duration, EBML_TYPE_UINT) + +E("Block", block, EBML_TYPE_BINARY) + +#define N block_group +E_S("BlockGroup", 3) +F(MATROSKA_ID_BLOCK, block, 0) +F(MATROSKA_ID_BLOCKDURATION, block_duration, 0) +F(MATROSKA_ID_REFERENCEBLOCK, reference_block, 1) +}}; +#undef N + +E("Timecode", timecode, EBML_TYPE_UINT) + +#define N cluster +E_S("Cluster", 3) +F(MATROSKA_ID_TIMECODE, timecode, 0) +F(MATROSKA_ID_BLOCKGROUP, block_group, 1) +F(MATROSKA_ID_SIMPLEBLOCK, simple_block, 1) +}}; +#undef N + +E("Duration", duration, EBML_TYPE_FLOAT) + +E("WritingApp", writing_app, EBML_TYPE_STR) + +E("MuxingApp", muxing_app, EBML_TYPE_STR) + +E("Title", title, EBML_TYPE_STR) + +E("DateUTC", date_utc, EBML_TYPE_SINT) + +E("TimecodeScale", timecode_scale, EBML_TYPE_UINT) + +E("NextUID", next_uid, EBML_TYPE_BINARY) + +E("PrevUID", prev_uid, EBML_TYPE_BINARY) + +E("SegmentUID", segment_uid, EBML_TYPE_BINARY) + +#define N info +E_S("Info", 9) +F(MATROSKA_ID_SEGMENTUID, segment_uid, 0) +F(MATROSKA_ID_PREVUID, prev_uid, 0) +F(MATROSKA_ID_NEXTUID, next_uid, 0) +F(MATROSKA_ID_TIMECODESCALE, timecode_scale, 0) +F(MATROSKA_ID_DATEUTC, date_utc, 0) +F(MATROSKA_ID_TITLE, title, 0) +F(MATROSKA_ID_MUXINGAPP, muxing_app, 0) +F(MATROSKA_ID_WRITINGAPP, writing_app, 0) +F(MATROSKA_ID_DURATION, duration, 0) +}}; +#undef N + +E("SeekPosition", seek_position, EBML_TYPE_UINT) + +E("SeekID", seek_id, EBML_TYPE_EBML_ID) + +#define N seek +E_S("Seek", 2) +F(MATROSKA_ID_SEEKID, seek_id, 0) +F(MATROSKA_ID_SEEKPOSITION, seek_position, 0) +}}; +#undef N + +#define N seek_head +E_S("SeekHead", 1) +F(MATROSKA_ID_SEEK, seek, 1) +}}; +#undef N + +#define N segment +E_S("Segment", 8) +F(MATROSKA_ID_SEEKHEAD, seek_head, 1) +F(MATROSKA_ID_INFO, info, 1) +F(MATROSKA_ID_CLUSTER, cluster, 1) +F(MATROSKA_ID_TRACKS, tracks, 1) +F(MATROSKA_ID_CUES, cues, 0) +F(MATROSKA_ID_ATTACHMENTS, attachments, 0) +F(MATROSKA_ID_CHAPTERS, chapters, 0) +F(MATROSKA_ID_TAGS, tags, 1) +}}; +#undef N + +E("Void", void, EBML_TYPE_BINARY) + +E("CRC32", crc32, EBML_TYPE_BINARY) + +E("DocTypeReadVersion", doc_type_read_version, EBML_TYPE_UINT) + +E("DocTypeVersion", doc_type_version, EBML_TYPE_UINT) + +E("DocType", doc_type, EBML_TYPE_STR) + +E("EBMLMaxSizeLength", ebml_max_size_length, EBML_TYPE_UINT) + +E("EBMLMaxIDLength", ebml_max_id_length, EBML_TYPE_UINT) + +E("EBMLReadVersion", ebml_read_version, EBML_TYPE_UINT) + +E("EBMLVersion", ebml_version, EBML_TYPE_UINT) + +#define N ebml +E_S("EBML", 7) +F(EBML_ID_EBMLVERSION, ebml_version, 0) +F(EBML_ID_EBMLREADVERSION, ebml_read_version, 0) +F(EBML_ID_EBMLMAXIDLENGTH, ebml_max_id_length, 0) +F(EBML_ID_EBMLMAXSIZELENGTH, ebml_max_size_length, 0) +F(EBML_ID_DOCTYPE, doc_type, 0) +F(EBML_ID_DOCTYPEVERSION, doc_type_version, 0) +F(EBML_ID_DOCTYPEREADVERSION, doc_type_read_version, 0) +}}; +#undef N diff --git a/libmpdemux/ebml_types.h b/libmpdemux/ebml_types.h new file mode 100644 index 0000000000..45b274731b --- /dev/null +++ b/libmpdemux/ebml_types.h @@ -0,0 +1,433 @@ +// Generated by TOOLS/matroska.py, do not edit manually + +#define EBML_ID_EBML 0x1a45dfa3 +#define EBML_ID_EBMLVERSION 0x4286 +#define EBML_ID_EBMLREADVERSION 0x42f7 +#define EBML_ID_EBMLMAXIDLENGTH 0x42f2 +#define EBML_ID_EBMLMAXSIZELENGTH 0x42f3 +#define EBML_ID_DOCTYPE 0x4282 +#define EBML_ID_DOCTYPEVERSION 0x4287 +#define EBML_ID_DOCTYPEREADVERSION 0x4285 +#define EBML_ID_CRC32 0xbf +#define EBML_ID_VOID 0xec +#define MATROSKA_ID_SEGMENT 0x18538067 +#define MATROSKA_ID_SEEKHEAD 0x114d9b74 +#define MATROSKA_ID_SEEK 0x4dbb +#define MATROSKA_ID_SEEKID 0x53ab +#define MATROSKA_ID_SEEKPOSITION 0x53ac +#define MATROSKA_ID_INFO 0x1549a966 +#define MATROSKA_ID_SEGMENTUID 0x73a4 +#define MATROSKA_ID_PREVUID 0x3cb923 +#define MATROSKA_ID_NEXTUID 0x3eb923 +#define MATROSKA_ID_TIMECODESCALE 0x2ad7b1 +#define MATROSKA_ID_DATEUTC 0x4461 +#define MATROSKA_ID_TITLE 0x7ba9 +#define MATROSKA_ID_MUXINGAPP 0x4d80 +#define MATROSKA_ID_WRITINGAPP 0x5741 +#define MATROSKA_ID_DURATION 0x4489 +#define MATROSKA_ID_CLUSTER 0x1f43b675 +#define MATROSKA_ID_TIMECODE 0xe7 +#define MATROSKA_ID_BLOCKGROUP 0xa0 +#define MATROSKA_ID_BLOCK 0xa1 +#define MATROSKA_ID_BLOCKDURATION 0x9b +#define MATROSKA_ID_REFERENCEBLOCK 0xfb +#define MATROSKA_ID_SIMPLEBLOCK 0xa3 +#define MATROSKA_ID_TRACKS 0x1654ae6b +#define MATROSKA_ID_TRACKENTRY 0xae +#define MATROSKA_ID_TRACKNUMBER 0xd7 +#define MATROSKA_ID_TRACKUID 0x73c5 +#define MATROSKA_ID_TRACKTYPE 0x83 +#define MATROSKA_ID_FLAGENABLED 0xb9 +#define MATROSKA_ID_FLAGDEFAULT 0x88 +#define MATROSKA_ID_FLAGFORCED 0x55aa +#define MATROSKA_ID_FLAGLACING 0x9c +#define MATROSKA_ID_MINCACHE 0x6de7 +#define MATROSKA_ID_DEFAULTDURATION 0x23e383 +#define MATROSKA_ID_TRACKTIMECODESCALE 0x23314f +#define MATROSKA_ID_MAXBLOCKADDITIONID 0x55ee +#define MATROSKA_ID_NAME 0x536e +#define MATROSKA_ID_LANGUAGE 0x22b59c +#define MATROSKA_ID_CODECID 0x86 +#define MATROSKA_ID_CODECPRIVATE 0x63a2 +#define MATROSKA_ID_CODECDECODEALL 0xaa +#define MATROSKA_ID_VIDEO 0xe0 +#define MATROSKA_ID_FLAGINTERLACED 0x9a +#define MATROSKA_ID_PIXELWIDTH 0xb0 +#define MATROSKA_ID_PIXELHEIGHT 0xba +#define MATROSKA_ID_DISPLAYWIDTH 0x54b0 +#define MATROSKA_ID_DISPLAYHEIGHT 0x54ba +#define MATROSKA_ID_FRAMERATE 0x2383e3 +#define MATROSKA_ID_AUDIO 0xe1 +#define MATROSKA_ID_SAMPLINGFREQUENCY 0xb5 +#define MATROSKA_ID_CHANNELS 0x9f +#define MATROSKA_ID_BITDEPTH 0x6264 +#define MATROSKA_ID_CONTENTENCODINGS 0x6d80 +#define MATROSKA_ID_CONTENTENCODING 0x6240 +#define MATROSKA_ID_CONTENTENCODINGORDER 0x5031 +#define MATROSKA_ID_CONTENTENCODINGSCOPE 0x5032 +#define MATROSKA_ID_CONTENTENCODINGTYPE 0x5033 +#define MATROSKA_ID_CONTENTCOMPRESSION 0x5034 +#define MATROSKA_ID_CONTENTCOMPALGO 0x4254 +#define MATROSKA_ID_CONTENTCOMPSETTINGS 0x4255 +#define MATROSKA_ID_CUES 0x1c53bb6b +#define MATROSKA_ID_CUEPOINT 0xbb +#define MATROSKA_ID_CUETIME 0xb3 +#define MATROSKA_ID_CUETRACKPOSITIONS 0xb7 +#define MATROSKA_ID_CUETRACK 0xf7 +#define MATROSKA_ID_CUECLUSTERPOSITION 0xf1 +#define MATROSKA_ID_ATTACHMENTS 0x1941a469 +#define MATROSKA_ID_ATTACHEDFILE 0x61a7 +#define MATROSKA_ID_FILENAME 0x466e +#define MATROSKA_ID_FILEMIMETYPE 0x4660 +#define MATROSKA_ID_FILEDATA 0x465c +#define MATROSKA_ID_FILEUID 0x46ae +#define MATROSKA_ID_CHAPTERS 0x1043a770 +#define MATROSKA_ID_EDITIONENTRY 0x45b9 +#define MATROSKA_ID_EDITIONUID 0x45bc +#define MATROSKA_ID_EDITIONFLAGHIDDEN 0x45bd +#define MATROSKA_ID_EDITIONFLAGDEFAULT 0x45db +#define MATROSKA_ID_EDITIONFLAGORDERED 0x45dd +#define MATROSKA_ID_CHAPTERATOM 0xb6 +#define MATROSKA_ID_CHAPTERUID 0x73c4 +#define MATROSKA_ID_CHAPTERTIMESTART 0x91 +#define MATROSKA_ID_CHAPTERTIMEEND 0x92 +#define MATROSKA_ID_CHAPTERFLAGHIDDEN 0x98 +#define MATROSKA_ID_CHAPTERFLAGENABLED 0x4598 +#define MATROSKA_ID_CHAPTERSEGMENTUID 0x6e67 +#define MATROSKA_ID_CHAPTERSEGMENTEDITIONUID 0x6ebc +#define MATROSKA_ID_CHAPTERDISPLAY 0x80 +#define MATROSKA_ID_CHAPSTRING 0x85 +#define MATROSKA_ID_CHAPLANGUAGE 0x437c +#define MATROSKA_ID_TAGS 0x1254c367 +#define MATROSKA_ID_TAG 0x7373 +#define MATROSKA_ID_TARGETS 0x63c0 +#define MATROSKA_ID_TARGETTYPEVALUE 0x68ca + + +struct ebml_targets { + uint64_t target_type_value; + + int n_target_type_value; +}; + +struct ebml_tag { + struct ebml_targets targets; + + int n_targets; +}; + +struct ebml_tags { + struct ebml_tag *tag; + + int n_tag; +}; + +struct ebml_chapter_display { + struct bstr chap_string; + struct bstr *chap_language; + + int n_chap_string; + int n_chap_language; +}; + +struct ebml_chapter_atom { + uint64_t chapter_uid; + uint64_t chapter_time_start; + uint64_t chapter_time_end; + uint64_t chapter_flag_hidden; + uint64_t chapter_flag_enabled; + struct bstr chapter_segment_uid; + uint64_t chapter_segment_edition_uid; + struct ebml_chapter_display *chapter_display; + + int n_chapter_uid; + int n_chapter_time_start; + int n_chapter_time_end; + int n_chapter_flag_hidden; + int n_chapter_flag_enabled; + int n_chapter_segment_uid; + int n_chapter_segment_edition_uid; + int n_chapter_display; +}; + +struct ebml_edition_entry { + uint64_t edition_uid; + uint64_t edition_flag_hidden; + uint64_t edition_flag_default; + uint64_t edition_flag_ordered; + struct ebml_chapter_atom *chapter_atom; + + int n_edition_uid; + int n_edition_flag_hidden; + int n_edition_flag_default; + int n_edition_flag_ordered; + int n_chapter_atom; +}; + +struct ebml_chapters { + struct ebml_edition_entry *edition_entry; + + int n_edition_entry; +}; + +struct ebml_attached_file { + struct bstr file_name; + struct bstr file_mime_type; + struct bstr file_data; + uint64_t file_uid; + + int n_file_name; + int n_file_mime_type; + int n_file_data; + int n_file_uid; +}; + +struct ebml_attachments { + struct ebml_attached_file *attached_file; + + int n_attached_file; +}; + +struct ebml_cue_track_positions { + uint64_t cue_track; + uint64_t cue_cluster_position; + + int n_cue_track; + int n_cue_cluster_position; +}; + +struct ebml_cue_point { + uint64_t cue_time; + struct ebml_cue_track_positions *cue_track_positions; + + int n_cue_time; + int n_cue_track_positions; +}; + +struct ebml_cues { + struct ebml_cue_point *cue_point; + + int n_cue_point; +}; + +struct ebml_content_compression { + uint64_t content_comp_algo; + struct bstr content_comp_settings; + + int n_content_comp_algo; + int n_content_comp_settings; +}; + +struct ebml_content_encoding { + uint64_t content_encoding_order; + uint64_t content_encoding_scope; + uint64_t content_encoding_type; + struct ebml_content_compression content_compression; + + int n_content_encoding_order; + int n_content_encoding_scope; + int n_content_encoding_type; + int n_content_compression; +}; + +struct ebml_content_encodings { + struct ebml_content_encoding *content_encoding; + + int n_content_encoding; +}; + +struct ebml_audio { + double sampling_frequency; + uint64_t channels; + uint64_t bit_depth; + + int n_sampling_frequency; + int n_channels; + int n_bit_depth; +}; + +struct ebml_video { + uint64_t flag_interlaced; + uint64_t pixel_width; + uint64_t pixel_height; + uint64_t display_width; + uint64_t display_height; + double frame_rate; + + int n_flag_interlaced; + int n_pixel_width; + int n_pixel_height; + int n_display_width; + int n_display_height; + int n_frame_rate; +}; + +struct ebml_track_entry { + uint64_t track_number; + uint64_t track_uid; + uint64_t track_type; + uint64_t flag_enabled; + uint64_t flag_default; + uint64_t flag_forced; + uint64_t flag_lacing; + uint64_t min_cache; + uint64_t default_duration; + double track_timecode_scale; + uint64_t max_block_addition_id; + struct bstr name; + struct bstr language; + struct bstr codec_id; + struct bstr codec_private; + uint64_t codec_decode_all; + struct ebml_video video; + struct ebml_audio audio; + struct ebml_content_encodings content_encodings; + + int n_track_number; + int n_track_uid; + int n_track_type; + int n_flag_enabled; + int n_flag_default; + int n_flag_forced; + int n_flag_lacing; + int n_min_cache; + int n_default_duration; + int n_track_timecode_scale; + int n_max_block_addition_id; + int n_name; + int n_language; + int n_codec_id; + int n_codec_private; + int n_codec_decode_all; + int n_video; + int n_audio; + int n_content_encodings; +}; + +struct ebml_tracks { + struct ebml_track_entry *track_entry; + + int n_track_entry; +}; + +struct ebml_block_group { + struct bstr block; + uint64_t block_duration; + int64_t *reference_block; + + int n_block; + int n_block_duration; + int n_reference_block; +}; + +struct ebml_cluster { + uint64_t timecode; + struct ebml_block_group *block_group; + struct bstr *simple_block; + + int n_timecode; + int n_block_group; + int n_simple_block; +}; + +struct ebml_info { + struct bstr segment_uid; + struct bstr prev_uid; + struct bstr next_uid; + uint64_t timecode_scale; + int64_t date_utc; + struct bstr title; + struct bstr muxing_app; + struct bstr writing_app; + double duration; + + int n_segment_uid; + int n_prev_uid; + int n_next_uid; + int n_timecode_scale; + int n_date_utc; + int n_title; + int n_muxing_app; + int n_writing_app; + int n_duration; +}; + +struct ebml_seek { + uint32_t seek_id; + uint64_t seek_position; + + int n_seek_id; + int n_seek_position; +}; + +struct ebml_seek_head { + struct ebml_seek *seek; + + int n_seek; +}; + +struct ebml_segment { + struct ebml_seek_head *seek_head; + struct ebml_info *info; + struct ebml_cluster *cluster; + struct ebml_tracks *tracks; + struct ebml_cues cues; + struct ebml_attachments attachments; + struct ebml_chapters chapters; + struct ebml_tags *tags; + + int n_seek_head; + int n_info; + int n_cluster; + int n_tracks; + int n_cues; + int n_attachments; + int n_chapters; + int n_tags; +}; + +struct ebml_ebml { + uint64_t ebml_version; + uint64_t ebml_read_version; + uint64_t ebml_max_id_length; + uint64_t ebml_max_size_length; + struct bstr doc_type; + uint64_t doc_type_version; + uint64_t doc_type_read_version; + + int n_ebml_version; + int n_ebml_read_version; + int n_ebml_max_id_length; + int n_ebml_max_size_length; + int n_doc_type; + int n_doc_type_version; + int n_doc_type_read_version; +}; +extern const struct ebml_elem_desc ebml_ebml_desc; +extern const struct ebml_elem_desc ebml_segment_desc; +extern const struct ebml_elem_desc ebml_seek_head_desc; +extern const struct ebml_elem_desc ebml_seek_desc; +extern const struct ebml_elem_desc ebml_info_desc; +extern const struct ebml_elem_desc ebml_cluster_desc; +extern const struct ebml_elem_desc ebml_block_group_desc; +extern const struct ebml_elem_desc ebml_tracks_desc; +extern const struct ebml_elem_desc ebml_track_entry_desc; +extern const struct ebml_elem_desc ebml_video_desc; +extern const struct ebml_elem_desc ebml_audio_desc; +extern const struct ebml_elem_desc ebml_content_encodings_desc; +extern const struct ebml_elem_desc ebml_content_encoding_desc; +extern const struct ebml_elem_desc ebml_content_compression_desc; +extern const struct ebml_elem_desc ebml_cues_desc; +extern const struct ebml_elem_desc ebml_cue_point_desc; +extern const struct ebml_elem_desc ebml_cue_track_positions_desc; +extern const struct ebml_elem_desc ebml_attachments_desc; +extern const struct ebml_elem_desc ebml_attached_file_desc; +extern const struct ebml_elem_desc ebml_chapters_desc; +extern const struct ebml_elem_desc ebml_edition_entry_desc; +extern const struct ebml_elem_desc ebml_chapter_atom_desc; +extern const struct ebml_elem_desc ebml_chapter_display_desc; +extern const struct ebml_elem_desc ebml_tags_desc; +extern const struct ebml_elem_desc ebml_tag_desc; +extern const struct ebml_elem_desc ebml_targets_desc; + +#define MAX_EBML_SUBELEMENTS 19