X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmatroskadec.c;h=b6b30e829c8df0525316be3687dc35770eb2faed;hb=6c71d2c1357018f7f68a8bde773306298ce72057;hp=c938a1ddd63fa668ce49da5f005888e03a609dca;hpb=6cb6e159f886f3b2fe5986e048cd13f153bf9c3d;p=ffmpeg diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index c938a1ddd63..b6b30e829c8 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -30,9 +30,11 @@ #include #include "avformat.h" -/* For codec_get_id(). */ +#include "internal.h" +/* For ff_codec_get_id(). */ #include "riff.h" #include "isom.h" +#include "rm.h" #include "matroska.h" #include "libavcodec/mpeg4audio.h" #include "libavutil/intfloat_readwrite.h" @@ -128,7 +130,9 @@ typedef struct { typedef struct { uint64_t num; + uint64_t uid; uint64_t type; + char *name; char *codec_id; EbmlBin codec_priv; char *language; @@ -141,12 +145,16 @@ typedef struct { AVStream *stream; int64_t end_timecode; + int ms_compat; } MatroskaTrack; typedef struct { + uint64_t uid; char *filename; char *mime; EbmlBin bin; + + AVStream *stream; } MatroskaAttachement; typedef struct { @@ -171,9 +179,24 @@ typedef struct { typedef struct { char *name; char *string; + char *lang; + uint64_t def; EbmlList sub; } MatroskaTag; +typedef struct { + char *type; + uint64_t typevalue; + uint64_t trackuid; + uint64_t chapteruid; + uint64_t attachuid; +} MatroskaTagTarget; + +typedef struct { + MatroskaTagTarget target; + EbmlList tag; +} MatroskaTags; + typedef struct { uint64_t id; uint64_t pos; @@ -221,6 +244,7 @@ typedef struct { typedef struct { uint64_t duration; int64_t reference; + uint64_t non_simple; EbmlBin bin; } MatroskaBlock; @@ -303,6 +327,8 @@ static EbmlSyntax matroska_track_encodings[] = { static EbmlSyntax matroska_track[] = { { MATROSKA_ID_TRACKNUMBER, EBML_UINT, 0, offsetof(MatroskaTrack,num) }, + { MATROSKA_ID_TRACKNAME, EBML_UTF8, 0, offsetof(MatroskaTrack,name) }, + { MATROSKA_ID_TRACKUID, EBML_UINT, 0, offsetof(MatroskaTrack,uid) }, { MATROSKA_ID_TRACKTYPE, EBML_UINT, 0, offsetof(MatroskaTrack,type) }, { MATROSKA_ID_CODECID, EBML_STR, 0, offsetof(MatroskaTrack,codec_id) }, { MATROSKA_ID_CODECPRIVATE, EBML_BIN, 0, offsetof(MatroskaTrack,codec_priv) }, @@ -313,8 +339,6 @@ static EbmlSyntax matroska_track[] = { { MATROSKA_ID_TRACKVIDEO, EBML_NEST, 0, offsetof(MatroskaTrack,video), {.n=matroska_track_video} }, { MATROSKA_ID_TRACKAUDIO, EBML_NEST, 0, offsetof(MatroskaTrack,audio), {.n=matroska_track_audio} }, { MATROSKA_ID_TRACKCONTENTENCODINGS,EBML_NEST, 0, 0, {.n=matroska_track_encodings} }, - { MATROSKA_ID_TRACKUID, EBML_NONE }, - { MATROSKA_ID_TRACKNAME, EBML_NONE }, { MATROSKA_ID_TRACKFLAGENABLED, EBML_NONE }, { MATROSKA_ID_TRACKFLAGFORCED, EBML_NONE }, { MATROSKA_ID_TRACKFLAGLACING, EBML_NONE }, @@ -334,11 +358,11 @@ static EbmlSyntax matroska_tracks[] = { }; static EbmlSyntax matroska_attachment[] = { + { MATROSKA_ID_FILEUID, EBML_UINT, 0, offsetof(MatroskaAttachement,uid) }, { MATROSKA_ID_FILENAME, EBML_UTF8, 0, offsetof(MatroskaAttachement,filename) }, { MATROSKA_ID_FILEMIMETYPE, EBML_STR, 0, offsetof(MatroskaAttachement,mime) }, { MATROSKA_ID_FILEDATA, EBML_BIN, 0, offsetof(MatroskaAttachement,bin) }, { MATROSKA_ID_FILEDESC, EBML_NONE }, - { MATROSKA_ID_FILEUID, EBML_NONE }, { 0 } }; @@ -400,20 +424,29 @@ static EbmlSyntax matroska_index[] = { static EbmlSyntax matroska_simpletag[] = { { MATROSKA_ID_TAGNAME, EBML_UTF8, 0, offsetof(MatroskaTag,name) }, { MATROSKA_ID_TAGSTRING, EBML_UTF8, 0, offsetof(MatroskaTag,string) }, + { MATROSKA_ID_TAGLANG, EBML_STR, 0, offsetof(MatroskaTag,lang), {.s="und"} }, + { MATROSKA_ID_TAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTag,def) }, { MATROSKA_ID_SIMPLETAG, EBML_NEST, sizeof(MatroskaTag), offsetof(MatroskaTag,sub), {.n=matroska_simpletag} }, - { MATROSKA_ID_TAGLANG, EBML_NONE }, - { MATROSKA_ID_TAGDEFAULT, EBML_NONE }, + { 0 } +}; + +static EbmlSyntax matroska_tagtargets[] = { + { MATROSKA_ID_TAGTARGETS_TYPE, EBML_STR, 0, offsetof(MatroskaTagTarget,type) }, + { MATROSKA_ID_TAGTARGETS_TYPEVALUE, EBML_UINT, 0, offsetof(MatroskaTagTarget,typevalue), {.u=50} }, + { MATROSKA_ID_TAGTARGETS_TRACKUID, EBML_UINT, 0, offsetof(MatroskaTagTarget,trackuid) }, + { MATROSKA_ID_TAGTARGETS_CHAPTERUID,EBML_UINT, 0, offsetof(MatroskaTagTarget,chapteruid) }, + { MATROSKA_ID_TAGTARGETS_ATTACHUID, EBML_UINT, 0, offsetof(MatroskaTagTarget,attachuid) }, { 0 } }; static EbmlSyntax matroska_tag[] = { - { MATROSKA_ID_SIMPLETAG, EBML_NEST, sizeof(MatroskaTag), 0, {.n=matroska_simpletag} }, - { MATROSKA_ID_TAGTARGETS, EBML_NONE }, + { MATROSKA_ID_SIMPLETAG, EBML_NEST, sizeof(MatroskaTag), offsetof(MatroskaTags,tag), {.n=matroska_simpletag} }, + { MATROSKA_ID_TAGTARGETS, EBML_NEST, 0, offsetof(MatroskaTags,target), {.n=matroska_tagtargets} }, { 0 } }; static EbmlSyntax matroska_tags[] = { - { MATROSKA_ID_TAG, EBML_NEST, 0, offsetof(MatroskaDemuxContext,tags), {.n=matroska_tag} }, + { MATROSKA_ID_TAG, EBML_NEST, sizeof(MatroskaTags), offsetof(MatroskaDemuxContext,tags), {.n=matroska_tag} }, { 0 } }; @@ -450,6 +483,7 @@ static EbmlSyntax matroska_blockgroup[] = { { MATROSKA_ID_SIMPLEBLOCK, EBML_BIN, 0, offsetof(MatroskaBlock,bin) }, { MATROSKA_ID_BLOCKDURATION, EBML_UINT, 0, offsetof(MatroskaBlock,duration), {.u=AV_NOPTS_VALUE} }, { MATROSKA_ID_BLOCKREFERENCE, EBML_UINT, 0, offsetof(MatroskaBlock,reference) }, + { 1, EBML_UINT, 0, offsetof(MatroskaBlock,non_simple), {.u=1} }, { 0 } }; @@ -471,25 +505,6 @@ static EbmlSyntax matroska_clusters[] = { { 0 } }; -#define SIZE_OFF(x) sizeof(((AVFormatContext*)0)->x),offsetof(AVFormatContext,x) -const struct { - const char name[16]; - int size; - int offset; -} metadata[] = { - { "TITLE", SIZE_OFF(title) }, - { "ARTIST", SIZE_OFF(author) }, - { "WRITTEN_BY", SIZE_OFF(author) }, - { "LEAD_PERFORMER", SIZE_OFF(author) }, - { "COPYRIGHT", SIZE_OFF(copyright) }, - { "COMMENT", SIZE_OFF(comment) }, - { "ALBUM", SIZE_OFF(album) }, - { "DATE_WRITTEN", SIZE_OFF(year) }, - { "DATE_RELEASED", SIZE_OFF(year) }, - { "PART_NUMBER", SIZE_OFF(track) }, - { "GENRE", SIZE_OFF(genre) }, -}; - /* * Return: Whether we reached the end of a level in the hierarchy or not. */ @@ -974,24 +989,61 @@ static void matroska_merge_packets(AVPacket *out, AVPacket *in) av_free(in); } -static void matroska_convert_tags(AVFormatContext *s, EbmlList *list) +static void matroska_convert_tag(AVFormatContext *s, EbmlList *list, + AVMetadata **metadata, char *prefix) { MatroskaTag *tags = list->elem; - int i, j; + char key[1024]; + int i; for (i=0; i < list->nb_elem; i++) { - for (j=0; j < FF_ARRAY_ELEMS(metadata); j++){ - if (!strcmp(tags[i].name, metadata[j].name)) { - int *ptr = (int *)((char *)s + metadata[j].offset); - if (*ptr) continue; - if (metadata[j].size > sizeof(int)) - av_strlcpy((char *)ptr, tags[i].string, metadata[j].size); - else - *ptr = atoi(tags[i].string); - } - } + const char *lang = strcmp(tags[i].lang, "und") ? tags[i].lang : NULL; + if (prefix) snprintf(key, sizeof(key), "%s/%s", prefix, tags[i].name); + else av_strlcpy(key, tags[i].name, sizeof(key)); + if (tags[i].def || !lang) { + av_metadata_set(metadata, key, tags[i].string); if (tags[i].sub.nb_elem) - matroska_convert_tags(s, &tags[i].sub); + matroska_convert_tag(s, &tags[i].sub, metadata, key); + } + if (lang) { + av_strlcat(key, "-", sizeof(key)); + av_strlcat(key, lang, sizeof(key)); + av_metadata_set(metadata, key, tags[i].string); + if (tags[i].sub.nb_elem) + matroska_convert_tag(s, &tags[i].sub, metadata, key); + } + } +} + +static void matroska_convert_tags(AVFormatContext *s) +{ + MatroskaDemuxContext *matroska = s->priv_data; + MatroskaTags *tags = matroska->tags.elem; + int i, j; + + for (i=0; i < matroska->tags.nb_elem; i++) { + if (tags[i].target.attachuid) { + MatroskaAttachement *attachment = matroska->attachments.elem; + for (j=0; jattachments.nb_elem; j++) + if (attachment[j].uid == tags[i].target.attachuid) + matroska_convert_tag(s, &tags[i].tag, + &attachment[j].stream->metadata, NULL); + } else if (tags[i].target.chapteruid) { + MatroskaChapter *chapter = matroska->chapters.elem; + for (j=0; jchapters.nb_elem; j++) + if (chapter[j].uid == tags[i].target.chapteruid) + matroska_convert_tag(s, &tags[i].tag, + &chapter[j].chapter->metadata, NULL); + } else if (tags[i].target.trackuid) { + MatroskaTrack *track = matroska->tracks.elem; + for (j=0; jtracks.nb_elem; j++) + if (track[j].uid == tags[i].target.trackuid) + matroska_convert_tag(s, &tags[i].tag, + &track[j].stream->metadata, NULL); + } else { + matroska_convert_tag(s, &tags[i].tag, &s->metadata, + tags[i].target.type); + } } } @@ -1093,7 +1145,7 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) "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; + return AVERROR_PATCHWELCOME; } ebml_free(ebml_syntax, &ebml); @@ -1106,7 +1158,6 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) matroska->ctx->duration = matroska->duration * matroska->time_scale * 1000 / AV_TIME_BASE; av_metadata_set(&s->metadata, "title", matroska->title); - matroska_convert_tags(s, &matroska->tags); tracks = matroska->tracks.elem; for (i=0; i < matroska->tracks.nb_elem; i++) { @@ -1196,22 +1247,23 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") && track->codec_priv.size >= 40 && track->codec_priv.data != NULL) { + track->ms_compat = 1; track->video.fourcc = AV_RL32(track->codec_priv.data + 16); - codec_id = codec_get_id(codec_bmp_tags, track->video.fourcc); + codec_id = ff_codec_get_id(ff_codec_bmp_tags, track->video.fourcc); + extradata_offset = 40; } else if (!strcmp(track->codec_id, "A_MS/ACM") - && track->codec_priv.size >= 18 + && track->codec_priv.size >= 14 && track->codec_priv.data != NULL) { init_put_byte(&b, track->codec_priv.data, track->codec_priv.size, URL_RDONLY, NULL, NULL, NULL, NULL); - get_wav_header(&b, st->codec, track->codec_priv.size); + ff_get_wav_header(&b, st->codec, track->codec_priv.size); codec_id = st->codec->codec_id; - extradata_offset = 18; - track->codec_priv.size -= extradata_offset; + extradata_offset = FFMIN(track->codec_priv.size, 18); } else if (!strcmp(track->codec_id, "V_QUICKTIME") && (track->codec_priv.size >= 86) && (track->codec_priv.data != NULL)) { track->video.fourcc = AV_RL32(track->codec_priv.data); - codec_id=codec_get_id(codec_movvideo_tags, track->video.fourcc); + codec_id=ff_codec_get_id(codec_movvideo_tags, track->video.fourcc); } else if (codec_id == CODEC_ID_PCM_S16BE) { switch (track->audio.bitdepth) { case 8: codec_id = CODEC_ID_PCM_U8; break; @@ -1258,15 +1310,16 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) } else if (codec_id == CODEC_ID_RV10 || codec_id == CODEC_ID_RV20 || codec_id == CODEC_ID_RV30 || codec_id == CODEC_ID_RV40) { extradata_offset = 26; - track->codec_priv.size -= extradata_offset; } else if (codec_id == CODEC_ID_RA_144) { track->audio.out_samplerate = 8000; track->audio.channels = 1; } else if (codec_id == CODEC_ID_RA_288 || codec_id == CODEC_ID_COOK || - codec_id == CODEC_ID_ATRAC3) { + codec_id == CODEC_ID_ATRAC3 || codec_id == CODEC_ID_SIPR) { + int flavor; init_put_byte(&b, track->codec_priv.data,track->codec_priv.size, 0, NULL, NULL, NULL, NULL); - url_fskip(&b, 24); + url_fskip(&b, 22); + flavor = get_be16(&b); track->audio.coded_framesize = get_be32(&b); url_fskip(&b, 12); track->audio.sub_packet_h = get_be16(&b); @@ -1277,11 +1330,16 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) st->codec->block_align = track->audio.coded_framesize; track->codec_priv.size = 0; } else { + if (codec_id == CODEC_ID_SIPR && flavor < 4) { + const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 }; + track->audio.sub_packet_size = ff_sipr_subpk_size[flavor]; + st->codec->bit_rate = sipr_bit_rate[flavor]; + } st->codec->block_align = track->audio.sub_packet_size; extradata_offset = 78; - track->codec_priv.size -= extradata_offset; } } + track->codec_priv.size -= extradata_offset; if (codec_id == CODEC_ID_NONE) av_log(matroska->ctx, AV_LOG_INFO, @@ -1295,6 +1353,7 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) st->start_time = 0; if (strcmp(track->language, "und")) av_metadata_set(&st->metadata, "language", track->language); + av_metadata_set(&st->metadata, "title", track->name); if (track->flag_default) st->disposition |= AV_DISPOSITION_DEFAULT; @@ -1303,22 +1362,24 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) av_reduce(&st->codec->time_base.num, &st->codec->time_base.den, track->default_duration, 1000000000, 30000); - if(extradata){ - st->codec->extradata = extradata; - st->codec->extradata_size = extradata_size; - } else if(track->codec_priv.data && track->codec_priv.size > 0){ - st->codec->extradata = av_mallocz(track->codec_priv.size + - FF_INPUT_BUFFER_PADDING_SIZE); - if(st->codec->extradata == NULL) - return AVERROR(ENOMEM); - st->codec->extradata_size = track->codec_priv.size; - memcpy(st->codec->extradata, - track->codec_priv.data + extradata_offset, - track->codec_priv.size); + if (!st->codec->extradata) { + if(extradata){ + st->codec->extradata = extradata; + st->codec->extradata_size = extradata_size; + } else if(track->codec_priv.data && track->codec_priv.size > 0){ + st->codec->extradata = av_mallocz(track->codec_priv.size + + FF_INPUT_BUFFER_PADDING_SIZE); + if(st->codec->extradata == NULL) + return AVERROR(ENOMEM); + st->codec->extradata_size = track->codec_priv.size; + memcpy(st->codec->extradata, + track->codec_priv.data + extradata_offset, + track->codec_priv.size); + } } if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { - st->codec->codec_type = CODEC_TYPE_VIDEO; + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; st->codec->codec_tag = track->video.fourcc; st->codec->width = track->video.pixel_width; st->codec->height = track->video.pixel_height; @@ -1327,13 +1388,14 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) st->codec->height * track->video.display_width, st->codec-> width * track->video.display_height, 255); + if (st->codec->codec_id != CODEC_ID_H264) st->need_parsing = AVSTREAM_PARSE_HEADERS; } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { - st->codec->codec_type = CODEC_TYPE_AUDIO; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->sample_rate = track->audio.out_samplerate; st->codec->channels = track->audio.channels; } else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) { - st->codec->codec_type = CODEC_TYPE_SUBTITLE; + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; } } @@ -1348,7 +1410,7 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) break; av_metadata_set(&st->metadata, "filename",attachements[j].filename); st->codec->codec_id = CODEC_ID_NONE; - st->codec->codec_type = CODEC_TYPE_ATTACHMENT; + st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; st->codec->extradata = av_malloc(attachements[j].bin.size); if(st->codec->extradata == NULL) break; @@ -1362,6 +1424,7 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) break; } } + attachements[j].stream = st; } } @@ -1399,6 +1462,8 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap) } } + matroska_convert_tags(s); + return 0; } @@ -1483,7 +1548,7 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, flags = *data++; size -= 3; if (is_keyframe == -1) - is_keyframe = flags & 0x80 ? PKT_FLAG_KEY : 0; + is_keyframe = flags & 0x80 ? AV_PKT_FLAG_KEY : 0; if (cluster_time != (uint64_t)-1 && (block_time >= 0 || cluster_time >= -block_time)) { @@ -1580,9 +1645,11 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, if (res == 0) { for (n = 0; n < laces; n++) { - if (st->codec->codec_id == CODEC_ID_RA_288 || - st->codec->codec_id == CODEC_ID_COOK || - st->codec->codec_id == CODEC_ID_ATRAC3) { + if ((st->codec->codec_id == CODEC_ID_RA_288 || + st->codec->codec_id == CODEC_ID_COOK || + st->codec->codec_id == CODEC_ID_SIPR || + st->codec->codec_id == CODEC_ID_ATRAC3) && + st->codec->block_align && track->audio.sub_packet_size) { int a = st->codec->block_align; int sps = track->audio.sub_packet_size; int cfs = track->audio.coded_framesize; @@ -1596,11 +1663,15 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, for (x=0; xaudio.buf+x*2*w+y*cfs, data+x*cfs, cfs); + else if (st->codec->codec_id == CODEC_ID_SIPR) + memcpy(track->audio.buf + y*w, data, w); else for (x=0; xaudio.buf+sps*(h*x+((h+1)/2)*(y&1)+(y>>1)), data+x*sps, sps); if (++track->audio.sub_packet_cnt >= h) { + if (st->codec->codec_id == CODEC_ID_SIPR) + ff_rm_reorder_sipr_data(track->audio.buf, h, w); track->audio.sub_packet_cnt = 0; track->audio.pkt_cnt = h*w / a; } @@ -1619,6 +1690,11 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, int offset = 0, pkt_size = lace_size[n]; uint8_t *pkt_data = data; + if (lace_size[n] > size) { + av_log(matroska->ctx, AV_LOG_ERROR, "Invalid packet size\n"); + break; + } + if (encodings && encodings->scope & 1) { offset = matroska_decode_buffer(&pkt_data,&pkt_size, track); if (offset < 0) @@ -1630,7 +1706,6 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, if (av_new_packet(pkt, pkt_size+offset) < 0) { av_free(pkt); res = AVERROR(ENOMEM); - n = laces-1; break; } if (offset) @@ -1644,7 +1719,10 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, pkt->flags = is_keyframe; pkt->stream_index = st->index; - pkt->pts = timecode; + if (track->ms_compat) + pkt->dts = timecode; + else + pkt->pts = timecode; pkt->pos = pos; if (st->codec->codec_id == CODEC_ID_TEXT) pkt->convergence_duration = duration; @@ -1668,6 +1746,7 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, if (timecode != AV_NOPTS_VALUE) timecode = duration ? timecode + duration : AV_NOPTS_VALUE; data += lace_size[n]; + size -= lace_size[n]; } } @@ -1695,12 +1774,14 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) blocks_list = &cluster.blocks; blocks = blocks_list->elem; for (i=0; inb_elem; i++) - if (blocks[i].bin.size > 0) + if (blocks[i].bin.size > 0) { + int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1; res=matroska_parse_block(matroska, blocks[i].bin.data, blocks[i].bin.size, blocks[i].bin.pos, cluster.timecode, - blocks[i].duration, !blocks[i].reference, + blocks[i].duration, is_keyframe, pos); + } ebml_free(matroska_cluster, &cluster); if (res < 0) matroska->done = 1; return res; @@ -1712,7 +1793,7 @@ static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt) while (matroska_deliver_packet(matroska, pkt)) { if (matroska->done) - return AVERROR(EIO); + return AVERROR_EOF; matroska_parse_cluster(matroska); } @@ -1790,4 +1871,5 @@ AVInputFormat matroska_demuxer = { matroska_read_packet, matroska_read_close, matroska_read_seek, + .metadata_conv = ff_mkv_metadata_conv, };