diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index f2d4e3c3c8..6a1aeb2794 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -44,6 +44,42 @@ #include #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 Track { MatroskaTrackType type; @@ -906,6 +942,133 @@ matroska_probe (AVProbeData *p) * 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; jnb_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) {