#include <bzlib.h>
#endif
+typedef enum {
+ EBML_NONE,
+ EBML_UINT,
+ EBML_FLOAT,
+ EBML_STR,
+ EBML_UTF8,
+ EBML_BIN,
+ EBML_NEST,
+ EBML_PASS,
+ EBML_STOP,
+} EbmlType;
+
+typedef const struct EbmlSyntax {
+ uint32_t id;
+ EbmlType type;
+ int list_elem_size;
+ int data_offset;
+ union {
+ uint64_t u;
+ double f;
+ const char *s;
+ const struct EbmlSyntax *n;
+ } def;
+} EbmlSyntax;
+
+typedef struct {
+ int nb_elem;
+ void *elem;
+} EbmlList;
+
+typedef struct {
+ int size;
+ uint8_t *data;
+ int64_t pos;
+} EbmlBin;
+
+typedef struct {
+ uint64_t version;
+ uint64_t max_size;
+ uint64_t id_length;
+ char *doctype;
+ uint64_t doctype_version;
+} Ebml;
+
typedef struct Track {
MatroskaTrackType type;
* the calling app uses for this track. */
uint32_t num;
uint32_t uid;
- int stream_index;
char *name;
char language[4];
MatroskaTrackEncodingCompAlgo encoding_algo;
uint8_t *encoding_settings;
int encoding_settings_len;
+
+ AVStream *stream;
} MatroskaTrack;
typedef struct MatroskaVideoTrack {
sizeof(MatroskaAudioTrack), \
sizeof(MatroskaSubtitleTrack)))
+typedef struct {
+ uint64_t start;
+ uint64_t end;
+ uint64_t uid;
+ char *title;
+} MatroskaChapter;
+
+typedef struct {
+ uint64_t track;
+ uint64_t pos;
+} MatroskaIndexPos;
+
+typedef struct {
+ uint64_t time;
+ EbmlList pos;
+} MatroskaIndex;
+
typedef struct MatroskaLevel {
uint64_t start;
uint64_t length;
} MatroskaLevel;
-typedef struct MatroskaDemuxIndex {
- uint64_t pos; /* of the corresponding *cluster*! */
- uint16_t track; /* reference to 'num' */
- uint64_t time; /* in nanoseconds */
-} MatroskaDemuxIndex;
-
typedef struct MatroskaDemuxContext {
AVFormatContext *ctx;
/* timescale in the file */
int64_t time_scale;
+ EbmlList chapters;
+ EbmlList index;
/* num_streams is the number of streams that av_new_stream() was called
* for ( = that are available to the calling program). */
int index_parsed;
int done;
- /* The index for seeking. */
- int num_indexes;
- MatroskaDemuxIndex *index;
-
/* What to skip before effectively reading a packet. */
int skip_to_keyframe;
AVStream *skip_to_stream;
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
+static EbmlSyntax ebml_header[] = {
+ { EBML_ID_EBMLREADVERSION, EBML_UINT, 0, offsetof(Ebml,version), {.u=EBML_VERSION} },
+ { EBML_ID_EBMLMAXSIZELENGTH, EBML_UINT, 0, offsetof(Ebml,max_size), {.u=8} },
+ { EBML_ID_EBMLMAXIDLENGTH, EBML_UINT, 0, offsetof(Ebml,id_length), {.u=4} },
+ { EBML_ID_DOCTYPE, EBML_STR, 0, offsetof(Ebml,doctype), {.s="(none)"} },
+ { EBML_ID_DOCTYPEREADVERSION, EBML_UINT, 0, offsetof(Ebml,doctype_version), {.u=1} },
+ { EBML_ID_EBMLVERSION, EBML_NONE },
+ { EBML_ID_DOCTYPEVERSION, EBML_NONE },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax ebml_syntax[] = {
+ { EBML_ID_HEADER, EBML_NEST, 0, 0, {.n=ebml_header} },
+ { 0 }
+};
+
+static EbmlSyntax matroska_chapter_display[] = {
+ { MATROSKA_ID_CHAPSTRING, EBML_UTF8, 0, offsetof(MatroskaChapter,title) },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_chapter_entry[] = {
+ { MATROSKA_ID_CHAPTERTIMESTART, EBML_UINT, 0, offsetof(MatroskaChapter,start), {.u=AV_NOPTS_VALUE} },
+ { MATROSKA_ID_CHAPTERTIMEEND, EBML_UINT, 0, offsetof(MatroskaChapter,end), {.u=AV_NOPTS_VALUE} },
+ { MATROSKA_ID_CHAPTERUID, EBML_UINT, 0, offsetof(MatroskaChapter,uid) },
+ { MATROSKA_ID_CHAPTERDISPLAY, EBML_NEST, 0, 0, {.n=matroska_chapter_display} },
+ { MATROSKA_ID_CHAPTERFLAGHIDDEN, EBML_NONE },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_chapter[] = {
+ { MATROSKA_ID_CHAPTERATOM, EBML_NEST, sizeof(MatroskaChapter), offsetof(MatroskaDemuxContext,chapters), {.n=matroska_chapter_entry} },
+ { MATROSKA_ID_EDITIONUID, EBML_NONE },
+ { MATROSKA_ID_EDITIONFLAGHIDDEN, EBML_NONE },
+ { MATROSKA_ID_EDITIONFLAGDEFAULT, EBML_NONE },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_chapters[] = {
+ { MATROSKA_ID_EDITIONENTRY, EBML_NEST, 0, 0, {.n=matroska_chapter} },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_index_pos[] = {
+ { MATROSKA_ID_CUETRACK, EBML_UINT, 0, offsetof(MatroskaIndexPos,track) },
+ { MATROSKA_ID_CUECLUSTERPOSITION, EBML_UINT, 0, offsetof(MatroskaIndexPos,pos) },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_index_entry[] = {
+ { MATROSKA_ID_CUETIME, EBML_UINT, 0, offsetof(MatroskaIndex,time) },
+ { MATROSKA_ID_CUETRACKPOSITION, EBML_NEST, sizeof(MatroskaIndexPos), offsetof(MatroskaIndex,pos), {.n=matroska_index_pos} },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_index[] = {
+ { MATROSKA_ID_POINTENTRY, EBML_NEST, sizeof(MatroskaIndex), offsetof(MatroskaDemuxContext,index), {.n=matroska_index_entry} },
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
+static EbmlSyntax matroska_tags[] = {
+ { EBML_ID_VOID, EBML_NONE },
+ { 0 }
+};
+
/*
* The first few functions handle EBML file parsing. The rest
* is the document interpretation. Matroska really just is a
return res;
}
-/*
- * Read an EBML header.
- * 0 is success, < 0 is failure.
- */
-
-static int
-ebml_read_header (MatroskaDemuxContext *matroska,
- char **doctype,
- int *version)
-{
- uint32_t id;
- int level_up, res = 0;
-
- /* default init */
- if (doctype)
- *doctype = NULL;
- if (version)
- *version = 1;
- if (!(id = ebml_peek_id(matroska, &level_up)) ||
- level_up != 0 || id != EBML_ID_HEADER) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "This is not an EBML file (id=0x%x/0x%x)\n", id, EBML_ID_HEADER);
- return AVERROR_INVALIDDATA;
- }
- if ((res = ebml_read_master(matroska, &id)) < 0)
- return res;
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &level_up)))
- return AVERROR(EIO);
-
- /* end-of-header */
- if (level_up)
- break;
-
- switch (id) {
- /* is our read version uptodate? */
- case EBML_ID_EBMLREADVERSION: {
- uint64_t num;
-
- if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
- return res;
- if (num > EBML_VERSION) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "EBML version %"PRIu64" (> %d) is not supported\n",
- num, EBML_VERSION);
- return AVERROR_INVALIDDATA;
- }
- break;
- }
-
- /* we only handle 8 byte lengths at max */
- case EBML_ID_EBMLMAXSIZELENGTH: {
- uint64_t num;
-
- if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
- return res;
- if (num > sizeof(uint64_t)) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "Integers of size %"PRIu64" (> %zd) not supported\n",
- num, sizeof(uint64_t));
- return AVERROR_INVALIDDATA;
- }
- break;
- }
-
- /* we handle 4 byte IDs at max */
- case EBML_ID_EBMLMAXIDLENGTH: {
- uint64_t num;
-
- if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
- return res;
- if (num > sizeof(uint32_t)) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "IDs of size %"PRIu64" (> %zu) not supported\n",
- num, sizeof(uint32_t));
- return AVERROR_INVALIDDATA;
- }
- break;
- }
-
- case EBML_ID_DOCTYPE: {
- char *text;
-
- if ((res = ebml_read_ascii(matroska, &id, &text)) < 0)
- return res;
- if (doctype) {
- if (*doctype)
- av_free(*doctype);
- *doctype = text;
- } else
- av_free(text);
- break;
- }
-
- case EBML_ID_DOCTYPEREADVERSION: {
- uint64_t num;
-
- if ((res = ebml_read_uint(matroska, &id, &num)) < 0)
- return res;
- if (version)
- *version = num;
- break;
- }
-
- default:
- av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown data type 0x%x in EBML header", id);
- /* pass-through */
-
- case EBML_ID_VOID:
- /* we ignore these two, as they don't tell us anything we
- * care about */
- case EBML_ID_EBMLVERSION:
- case EBML_ID_DOCTYPEVERSION:
- res = ebml_read_skip (matroska);
- break;
- }
- }
-
- return 0;
-}
-
-
-static int
+static MatroskaTrack *
matroska_find_track_by_num (MatroskaDemuxContext *matroska,
int num)
{
for (i = 0; i < matroska->num_tracks; i++)
if (matroska->tracks[i]->num == num)
- return i;
+ return matroska->tracks[i];
- return -1;
+ av_log(matroska->ctx, AV_LOG_ERROR, "Invalid track number %d\n", num);
+ return NULL;
}
* From here on, it's all XML-style DTD stuff... Needs no comments.
*/
+static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
+ void *data, uint32_t expected_id, int once);
+
+static int ebml_parse_elem(MatroskaDemuxContext *matroska,
+ EbmlSyntax *syntax, void *data)
+{
+ uint32_t id = syntax->id;
+ EbmlBin *bin;
+ int res;
+
+ data = (char *)data + syntax->data_offset;
+ if (syntax->list_elem_size) {
+ EbmlList *list = data;
+ list->elem = av_realloc(list->elem, (list->nb_elem+1)*syntax->list_elem_size);
+ data = (char*)list->elem + list->nb_elem*syntax->list_elem_size;
+ memset(data, 0, syntax->list_elem_size);
+ list->nb_elem++;
+ }
+ bin = data;
+
+ switch (syntax->type) {
+ case EBML_UINT: return ebml_read_uint (matroska, &id, data);
+ case EBML_FLOAT: return ebml_read_float(matroska, &id, data);
+ case EBML_STR:
+ case EBML_UTF8: av_free(*(char **)data);
+ return ebml_read_ascii(matroska, &id, data);
+ case EBML_BIN: av_free(bin->data);
+ bin->pos = url_ftell(matroska->ctx->pb);
+ return ebml_read_binary(matroska, &id, &bin->data,
+ &bin->size);
+ case EBML_NEST: if ((res=ebml_read_master(matroska, &id)) < 0)
+ return res;
+ if (id == MATROSKA_ID_SEGMENT)
+ matroska->segment_start = url_ftell(matroska->ctx->pb);
+ return ebml_parse(matroska, syntax->def.n, data, 0, 0);
+ case EBML_PASS: return ebml_parse(matroska, syntax->def.n, data, 0, 1);
+ case EBML_STOP: *(int *)data = 1; return 1;
+ default: return ebml_read_skip(matroska);
+ }
+}
+
+static int ebml_parse_id(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
+ uint32_t id, void *data)
+{
+ int i;
+ for (i=0; syntax[i].id; i++)
+ if (id == syntax[i].id)
+ break;
+ if (!syntax[i].id)
+ av_log(matroska->ctx, AV_LOG_INFO, "Unknown entry 0x%X\n", id);
+ return ebml_parse_elem(matroska, &syntax[i], data);
+}
+
+static int ebml_parse(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
+ void *data, uint32_t expected_id, int once)
+{
+ int i, res = 0;
+ uint32_t id = 0;
+
+ for (i=0; syntax[i].id; i++)
+ switch (syntax[i].type) {
+ case EBML_UINT:
+ *(uint64_t *)((char *)data+syntax[i].data_offset) = syntax[i].def.u;
+ break;
+ case EBML_FLOAT:
+ *(double *)((char *)data+syntax[i].data_offset) = syntax[i].def.f;
+ break;
+ case EBML_STR:
+ case EBML_UTF8:
+ *(char **)((char *)data+syntax[i].data_offset) = av_strdup(syntax[i].def.s);
+ break;
+ }
+
+ if (expected_id) {
+ res = ebml_read_master(matroska, &id);
+ if (id != expected_id)
+ return AVERROR_INVALIDDATA;
+ if (id == MATROSKA_ID_SEGMENT)
+ matroska->segment_start = url_ftell(matroska->ctx->pb);
+ }
+
+ while (!res) {
+ if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
+ res = AVERROR(EIO);
+ break;
+ } else if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+
+ res = ebml_parse_id(matroska, syntax, id, data);
+ if (once)
+ break;
+
+
+ if (matroska->level_up) {
+ matroska->level_up--;
+ break;
+ }
+ }
+
+ return res;
+}
+
+static void ebml_free(EbmlSyntax *syntax, void *data)
+{
+ int i, j;
+ for (i=0; syntax[i].id; i++) {
+ void *data_off = (char *)data + syntax[i].data_offset;
+ switch (syntax[i].type) {
+ case EBML_STR:
+ case EBML_UTF8: av_freep(data_off); break;
+ case EBML_BIN: av_freep(&((EbmlBin *)data_off)->data); break;
+ case EBML_NEST:
+ if (syntax[i].list_elem_size) {
+ EbmlList *list = data_off;
+ char *ptr = list->elem;
+ for (j=0; j<list->nb_elem; j++, ptr+=syntax[i].list_elem_size)
+ ebml_free(syntax[i].def.n, ptr);
+ av_free(list->elem);
+ } else
+ ebml_free(syntax[i].def.n, data_off);
+ default: break;
+ }
+ }
+}
+
static int
matroska_parse_info (MatroskaDemuxContext *matroska)
{
static int
matroska_parse_index (MatroskaDemuxContext *matroska)
{
- int res = 0;
- uint32_t id;
- MatroskaDemuxIndex idx;
-
- av_log(matroska->ctx, AV_LOG_DEBUG, "parsing index...\n");
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR(EIO);
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- /* one single index entry ('point') */
- case MATROSKA_ID_POINTENTRY:
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
-
- /* in the end, we hope to fill one entry with a
- * timestamp, a file position and a tracknum */
- idx.pos = (uint64_t) -1;
- idx.time = (uint64_t) -1;
- idx.track = (uint16_t) -1;
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR(EIO);
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- /* one single index entry ('point') */
- case MATROSKA_ID_CUETIME: {
- uint64_t time;
- if ((res = ebml_read_uint(matroska, &id,
- &time)) < 0)
- break;
- idx.time = time * matroska->time_scale;
- break;
- }
-
- /* position in the file + track to which it
- * belongs */
- case MATROSKA_ID_CUETRACKPOSITION:
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
-
- while (res == 0) {
- if (!(id = ebml_peek_id (matroska,
- &matroska->level_up))) {
- res = AVERROR(EIO);
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- /* track number */
- case MATROSKA_ID_CUETRACK: {
- uint64_t num;
- if ((res = ebml_read_uint(matroska,
- &id, &num)) < 0)
- break;
- idx.track = num;
- break;
- }
-
- /* position in file */
- case MATROSKA_ID_CUECLUSTERPOSITION: {
- uint64_t num;
- if ((res = ebml_read_uint(matroska,
- &id, &num)) < 0)
- break;
- idx.pos = num+matroska->segment_start;
- break;
- }
-
- default:
- av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown entry 0x%x in "
- "CuesTrackPositions\n", id);
- /* fall-through */
-
- case EBML_ID_VOID:
- res = ebml_read_skip(matroska);
- break;
- }
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
-
- break;
-
- default:
- av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown entry 0x%x in cuespoint "
- "index\n", id);
- /* fall-through */
-
- case EBML_ID_VOID:
- res = ebml_read_skip(matroska);
- break;
- }
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
-
- /* so let's see if we got what we wanted */
- if (idx.pos != (uint64_t) -1 &&
- idx.time != (uint64_t) -1 &&
- idx.track != (uint16_t) -1) {
- if (matroska->num_indexes % 32 == 0) {
- /* re-allocate bigger index */
- matroska->index =
- av_realloc(matroska->index,
- (matroska->num_indexes + 32) *
- sizeof(MatroskaDemuxIndex));
- }
- matroska->index[matroska->num_indexes] = idx;
- matroska->num_indexes++;
- }
- break;
-
- default:
- av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown entry 0x%x in cues header\n", id);
- /* fall-through */
-
- case EBML_ID_VOID:
- res = ebml_read_skip(matroska);
- break;
- }
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
-
- return res;
+ return ebml_parse(matroska, matroska_index, matroska, MATROSKA_ID_CUES, 0);
}
static int
matroska_parse_metadata (MatroskaDemuxContext *matroska)
{
- int res = 0;
- uint32_t id;
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR(EIO);
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- /* Hm, this is unsupported... */
- default:
- av_log(matroska->ctx, AV_LOG_INFO,
- "Unknown entry 0x%x in metadata header\n", id);
- /* fall-through */
-
- case EBML_ID_VOID:
- res = ebml_read_skip(matroska);
- break;
- }
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
-
- return res;
+ return ebml_parse(matroska, matroska_tags, matroska, MATROSKA_ID_TAGS, 0);
}
static int
}
/* read master + parse */
- if ((res = ebml_read_master(matroska, &id)) < 0)
- goto finish;
switch (id) {
case MATROSKA_ID_CUES:
if (!(res = matroska_parse_index(matroska)) ||
matroska_parse_chapters(AVFormatContext *s)
{
MatroskaDemuxContext *matroska = s->priv_data;
- int res = 0;
- uint32_t id;
-
- av_log(s, AV_LOG_DEBUG, "parsing chapters...\n");
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR(EIO);
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- case MATROSKA_ID_EDITIONENTRY: {
- uint64_t end = AV_NOPTS_VALUE, start = AV_NOPTS_VALUE;
- int64_t uid= -1;
- char* title = NULL;
- /* if there is more than one chapter edition
- we take only the first one */
- if(s->chapters) {
- ebml_read_skip(matroska);
- break;
- }
-
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR(EIO);
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- case MATROSKA_ID_CHAPTERATOM:
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR(EIO);
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- case MATROSKA_ID_CHAPTERTIMEEND:
- res = ebml_read_uint(matroska, &id, &end);
- break;
-
- case MATROSKA_ID_CHAPTERTIMESTART:
- res = ebml_read_uint(matroska, &id, &start);
- break;
-
- case MATROSKA_ID_CHAPTERDISPLAY:
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
-
- while (res == 0) {
- if (!(id = ebml_peek_id(matroska, &matroska->level_up))) {
- res = AVERROR(EIO);
- break;
- } else if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
-
- switch (id) {
- case MATROSKA_ID_CHAPSTRING:
- res = ebml_read_utf8(matroska, &id, &title);
- break;
-
- default:
- av_log(s, AV_LOG_INFO, "Ignoring unknown Chapter display ID 0x%x\n", id);
- case EBML_ID_VOID:
- res = ebml_read_skip(matroska);
- break;
- }
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
- break;
-
- case MATROSKA_ID_CHAPTERUID:
- res = ebml_read_uint(matroska, &id, &uid);
- break;
- default:
- av_log(s, AV_LOG_INFO, "Ignoring unknown Chapter atom ID 0x%x\n", id);
- case MATROSKA_ID_CHAPTERFLAGHIDDEN:
- case EBML_ID_VOID:
- res = ebml_read_skip(matroska);
- break;
- }
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
+ EbmlList *chapters_list = &matroska->chapters;
+ MatroskaChapter *chapters;
+ int i, res;
- if (start != AV_NOPTS_VALUE && uid != -1) {
- if(!ff_new_chapter(s, uid, (AVRational){1, 1000000000}, start, end, title))
- res= AVERROR(ENOMEM);
- }
- av_free(title);
- break;
-
- default:
- av_log(s, AV_LOG_INFO, "Ignoring unknown Edition entry ID 0x%x\n", id);
- case MATROSKA_ID_EDITIONUID:
- case MATROSKA_ID_EDITIONFLAGHIDDEN:
- case MATROSKA_ID_EDITIONFLAGDEFAULT:
- case EBML_ID_VOID:
- res = ebml_read_skip(matroska);
- break;
- }
-
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
- break;
- }
+ res = ebml_parse(matroska, matroska_chapters, matroska, MATROSKA_ID_CHAPTERS, 0);
- default:
- av_log(s, AV_LOG_INFO, "Expected an Edition entry (0x%x), but found 0x%x\n", MATROSKA_ID_EDITIONENTRY, id);
- case EBML_ID_VOID:
- res = ebml_read_skip(matroska);
- break;
- }
-
- if (matroska->level_up) {
- matroska->level_up--;
- break;
- }
- }
+ chapters = chapters_list->elem;
+ for (i=0; i<chapters_list->nb_elem; i++)
+ if (chapters[i].start != AV_NOPTS_VALUE && chapters[i].uid)
+ ff_new_chapter(s, chapters[i].uid, (AVRational){1, 1000000000},
+ chapters[i].start, chapters[i].end,
+ chapters[i].title);
return res;
}
AVFormatParameters *ap)
{
MatroskaDemuxContext *matroska = s->priv_data;
- char *doctype;
- int version, last_level, res = 0;
+ EbmlList *index_list;
+ MatroskaIndex *index;
+ int i, j, last_level, res = 0;
+ Ebml ebml = { 0 };
uint32_t id;
matroska->ctx = s;
/* First read the EBML header. */
- doctype = NULL;
- if ((res = ebml_read_header(matroska, &doctype, &version)) < 0)
- return res;
- if ((doctype == NULL) || strcmp(doctype, "matroska")) {
+ if (ebml_parse(matroska, ebml_syntax, &ebml, 0, 1)
+ || ebml.version > EBML_VERSION || ebml.max_size > sizeof(uint64_t)
+ || ebml.id_length > sizeof(uint32_t) || strcmp(ebml.doctype, "matroska")
+ || ebml.doctype_version > 2) {
av_log(matroska->ctx, AV_LOG_ERROR,
- "Wrong EBML doctype ('%s' != 'matroska').\n",
- doctype ? doctype : "(none)");
- if (doctype)
- av_free(doctype);
- return AVERROR_NOFMT;
- }
- av_free(doctype);
- if (version > 2) {
- av_log(matroska->ctx, AV_LOG_ERROR,
- "Matroska demuxer version 2 too old for file version %d\n",
- version);
+ "EBML header using unsupported features\n"
+ "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n",
+ ebml.version, ebml.doctype, ebml.doctype_version);
return AVERROR_NOFMT;
}
+ ebml_free(ebml_syntax, &ebml);
/* The next thing is a segment. */
while (1) {
/* stream index */
case MATROSKA_ID_CUES: {
if (!matroska->index_parsed) {
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
res = matroska_parse_index(matroska);
} else
res = ebml_read_skip(matroska);
/* metadata */
case MATROSKA_ID_TAGS: {
if (!matroska->metadata_parsed) {
- if ((res = ebml_read_master(matroska, &id)) < 0)
- break;
res = matroska_parse_metadata(matroska);
} else
res = ebml_read_skip(matroska);
}
case MATROSKA_ID_CHAPTERS: {
- if ((res = ebml_read_master(matroska, &id)) < 0)
- return res;
res = matroska_parse_chapters(s);
break;
}
int extradata_size = 0;
int extradata_offset = 0;
track = matroska->tracks[i];
- track->stream_index = -1;
/* Apply some sanity checks. */
if (track->codec_id == NULL)
}
}
+ st = track->stream = av_new_stream(s, matroska->num_streams++);
+ if (st == NULL)
+ return AVERROR(ENOMEM);
+
/* Set the FourCC from the CodecID. */
/* This is the MS compatibility mode which stores a
* BITMAPINFOHEADER in the CodecPrivate. */
track->codec_id);
}
- track->stream_index = matroska->num_streams;
-
- matroska->num_streams++;
- st = av_new_stream(s, track->stream_index);
- if (st == NULL)
- return AVERROR(ENOMEM);
av_set_pts_info(st, 64, matroska->time_scale*track->time_scale, 1000*1000*1000); /* 64 bit pts in ns */
st->codec->codec_id = codec_id;
res = 0;
}
- if (matroska->index_parsed) {
- int i, track, stream;
- for (i=0; i<matroska->num_indexes; i++) {
- MatroskaDemuxIndex *idx = &matroska->index[i];
- track = matroska_find_track_by_num(matroska, idx->track);
- if (track < 0) continue;
- stream = matroska->tracks[track]->stream_index;
- if (stream >= 0 && stream < matroska->ctx->nb_streams)
- av_add_index_entry(matroska->ctx->streams[stream],
- idx->pos, idx->time/AV_TIME_BASE,
+ index_list = &matroska->index;
+ index = index_list->elem;
+ for (i=0; i<index_list->nb_elem; i++) {
+ EbmlList *pos_list = &index[i].pos;
+ MatroskaIndexPos *pos = pos_list->elem;
+ for (j=0; j<pos_list->nb_elem; j++) {
+ MatroskaTrack *track = matroska_find_track_by_num(matroska,
+ pos[j].track);
+ if (track && track->stream)
+ av_add_index_entry(track->stream,
+ pos[j].pos + matroska->segment_start,
+ index[i].time*matroska->time_scale/AV_TIME_BASE,
0, 0, AVINDEX_KEYFRAME);
}
}
int64_t pos, uint64_t cluster_time, uint64_t duration,
int is_keyframe)
{
+ MatroskaTrack *track;
int res = 0;
- int track;
AVStream *st;
AVPacket *pkt;
uint8_t *origdata = data;
uint32_t *lace_size = NULL;
int n, flags, laces = 0;
uint64_t num;
- int stream_index;
/* first byte(s): tracknum */
if ((n = matroska_ebmlnum_uint(data, size, &num)) < 0) {
/* fetch track from num */
track = matroska_find_track_by_num(matroska, num);
- if (size <= 3 || track < 0 || track >= matroska->num_tracks) {
+ if (size <= 3 || !track || !track->stream) {
av_log(matroska->ctx, AV_LOG_INFO,
- "Invalid stream %d or size %u\n", track, size);
- av_free(origdata);
- return res;
- }
- stream_index = matroska->tracks[track]->stream_index;
- if (stream_index < 0 || stream_index >= matroska->ctx->nb_streams) {
+ "Invalid stream %"PRIu64" or size %u\n", num, size);
av_free(origdata);
return res;
}
- st = matroska->ctx->streams[stream_index];
+ st = track->stream;
if (st->discard >= AVDISCARD_ALL) {
av_free(origdata);
return res;
}
if (duration == AV_NOPTS_VALUE)
- duration = matroska->tracks[track]->default_duration / matroska->time_scale;
+ duration = track->default_duration / matroska->time_scale;
/* block_time (relative to cluster time) */
block_time = AV_RB16(data);
if (st->codec->codec_id == CODEC_ID_RA_288 ||
st->codec->codec_id == CODEC_ID_COOK ||
st->codec->codec_id == CODEC_ID_ATRAC3) {
- MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *)matroska->tracks[track];
+ MatroskaAudioTrack *audiotrack = (MatroskaAudioTrack *)track;
int a = st->codec->block_align;
int sps = audiotrack->sub_packet_size;
int cfs = audiotrack->coded_framesize;
memcpy(pkt->data, audiotrack->buf
+ a * (h*w / a - audiotrack->pkt_cnt--), a);
pkt->pos = pos;
- pkt->stream_index = stream_index;
+ pkt->stream_index = st->index;
matroska_queue_packet(matroska, pkt);
}
} else {
int offset = 0, pkt_size = lace_size[n];
uint8_t *pkt_data = data;
- if (matroska->tracks[track]->encoding_scope & 1) {
+ if (track->encoding_scope & 1) {
offset = matroska_decode_buffer(&pkt_data, &pkt_size,
- matroska->tracks[track]);
+ track);
if (offset < 0)
continue;
}
break;
}
if (offset)
- memcpy (pkt->data, matroska->tracks[track]->encoding_settings, offset);
+ memcpy (pkt->data, track->encoding_settings, offset);
memcpy (pkt->data+offset, pkt_data, pkt_size);
if (pkt_data != data)
if (n == 0)
pkt->flags = is_keyframe;
- pkt->stream_index = stream_index;
+ pkt->stream_index = st->index;
pkt->pts = timecode;
pkt->pos = pos;
MatroskaDemuxContext *matroska = s->priv_data;
int n = 0;
- av_free(matroska->index);
-
matroska_clear_queue(matroska);
for (n = 0; n < matroska->num_tracks; n++) {
av_free(track);
}
+ ebml_free(matroska_index, matroska);
return 0;
}