X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmatroskadec.c;h=c16e18cb224b5a3f5985a3a37b8c363036b7ef1e;hb=979b5b89594c7628bd846c63198cb64ef9d81d16;hp=162697ff1226f92c9fbe86b0d1319c55ccf25ae1;hpb=d5274f86a838ae5f9d56f41c6ee520cce466bd83;p=ffmpeg diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index 162697ff122..c16e18cb224 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -249,6 +249,7 @@ typedef struct MatroskaTrack { AVStream *stream; int64_t end_timecode; int ms_compat; + int needs_decoding; uint64_t max_block_additional_id; uint32_t palette[AVPALETTE_COUNT]; @@ -554,7 +555,7 @@ static EbmlSyntax matroska_track[] = { { MATROSKA_ID_CODECID, EBML_STR, 0, offsetof(MatroskaTrack, codec_id) }, { MATROSKA_ID_CODECPRIVATE, EBML_BIN, 0, offsetof(MatroskaTrack, codec_priv) }, { MATROSKA_ID_CODECDELAY, EBML_UINT, 0, offsetof(MatroskaTrack, codec_delay) }, - { MATROSKA_ID_TRACKLANGUAGE, EBML_UTF8, 0, offsetof(MatroskaTrack, language), { .s = "eng" } }, + { MATROSKA_ID_TRACKLANGUAGE, EBML_STR, 0, offsetof(MatroskaTrack, language), { .s = "eng" } }, { MATROSKA_ID_TRACKDEFAULTDURATION, EBML_UINT, 0, offsetof(MatroskaTrack, default_duration) }, { MATROSKA_ID_TRACKTIMECODESCALE, EBML_FLOAT, 0, offsetof(MatroskaTrack, time_scale), { .f = 1.0 } }, { MATROSKA_ID_TRACKFLAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTrack, flag_default), { .u = 1 } }, @@ -747,6 +748,25 @@ static EbmlSyntax matroska_cluster_enter[] = { }; #undef CHILD_OF +static const CodecMime mkv_image_mime_tags[] = { + {"image/gif" , AV_CODEC_ID_GIF}, + {"image/jpeg" , AV_CODEC_ID_MJPEG}, + {"image/png" , AV_CODEC_ID_PNG}, + {"image/tiff" , AV_CODEC_ID_TIFF}, + + {"" , AV_CODEC_ID_NONE} +}; + +static const CodecMime mkv_mime_tags[] = { + {"text/plain" , AV_CODEC_ID_TEXT}, + {"application/x-truetype-font", AV_CODEC_ID_TTF}, + {"application/x-font" , AV_CODEC_ID_TTF}, + {"application/vnd.ms-opentype", AV_CODEC_ID_OTF}, + {"binary" , AV_CODEC_ID_BIN_DATA}, + + {"" , AV_CODEC_ID_NONE} +}; + static const char *const matroska_doctypes[] = { "matroska", "webm" }; static int matroska_read_close(AVFormatContext *s); @@ -1545,7 +1565,7 @@ static int matroska_probe(const AVProbeData *p) } static MatroskaTrack *matroska_find_track_by_num(MatroskaDemuxContext *matroska, - int num) + uint64_t num) { MatroskaTrack *tracks = matroska->tracks.elem; int i; @@ -1554,7 +1574,7 @@ static MatroskaTrack *matroska_find_track_by_num(MatroskaDemuxContext *matroska, if (tracks[i].num == num) return &tracks[i]; - av_log(matroska->ctx, AV_LOG_ERROR, "Invalid track number %d\n", num); + av_log(matroska->ctx, AV_LOG_ERROR, "Invalid track number %"PRIu64"\n", num); return NULL; } @@ -1599,6 +1619,7 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size, #if CONFIG_LZO case MATROSKA_TRACK_ENCODING_COMP_LZO: do { + int insize = isize; olen = pkt_size *= 3; newpktdata = av_realloc(pkt_data, pkt_size + AV_LZO_OUTPUT_PADDING + AV_INPUT_BUFFER_PADDING_SIZE); @@ -1607,7 +1628,7 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size, goto failed; } pkt_data = newpktdata; - result = av_lzo1x_decode(pkt_data, &olen, data, &isize); + result = av_lzo1x_decode(pkt_data, &olen, data, &insize); } while (result == AV_LZO_OUTPUT_FULL && pkt_size < 10000000); if (result) { result = AVERROR_INVALIDDATA; @@ -2385,6 +2406,11 @@ static int matroska_parse_tracks(AVFormatContext *s) } } } + track->needs_decoding = encodings && !encodings[0].type && + encodings[0].scope & 1 && + (encodings[0].compression.algo != + MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP || + encodings[0].compression.settings.size); for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { if (!strncmp(ff_mkv_codec_tags[j].str, track->codec_id, @@ -2402,8 +2428,8 @@ static int matroska_parse_tracks(AVFormatContext *s) if (key_id_base64) { /* export encryption key id as base64 metadata tag */ - av_dict_set(&st->metadata, "enc_key_id", key_id_base64, 0); - av_freep(&key_id_base64); + av_dict_set(&st->metadata, "enc_key_id", key_id_base64, + AV_DICT_DONT_STRDUP_VAL); } if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") && @@ -2586,32 +2612,46 @@ static int matroska_parse_tracks(AVFormatContext *s) track->audio.sub_packet_h = avio_rb16(&b); track->audio.frame_size = avio_rb16(&b); track->audio.sub_packet_size = avio_rb16(&b); - if (flavor < 0 || - track->audio.coded_framesize <= 0 || + if (track->audio.coded_framesize <= 0 || track->audio.sub_packet_h <= 0 || - track->audio.frame_size <= 0 || - track->audio.sub_packet_size <= 0 && codec_id != AV_CODEC_ID_SIPR) + track->audio.frame_size <= 0) return AVERROR_INVALIDDATA; - track->audio.buf = av_malloc_array(track->audio.sub_packet_h, - track->audio.frame_size); - if (!track->audio.buf) - return AVERROR(ENOMEM); + if (codec_id == AV_CODEC_ID_RA_288) { + if (track->audio.sub_packet_h & 1 || 2 * track->audio.frame_size + != (int64_t)track->audio.sub_packet_h * track->audio.coded_framesize) + return AVERROR_INVALIDDATA; st->codecpar->block_align = track->audio.coded_framesize; track->codec_priv.size = 0; } else { - if (codec_id == AV_CODEC_ID_SIPR && flavor < 4) { + if (codec_id == AV_CODEC_ID_SIPR) { static const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 }; + if (flavor > 3) + return AVERROR_INVALIDDATA; track->audio.sub_packet_size = ff_sipr_subpk_size[flavor]; st->codecpar->bit_rate = sipr_bit_rate[flavor]; - } + } else if (track->audio.sub_packet_size <= 0 || + track->audio.frame_size % track->audio.sub_packet_size) + return AVERROR_INVALIDDATA; st->codecpar->block_align = track->audio.sub_packet_size; extradata_offset = 78; } + track->audio.buf = av_malloc_array(track->audio.sub_packet_h, + track->audio.frame_size); + if (!track->audio.buf) + return AVERROR(ENOMEM); } else if (codec_id == AV_CODEC_ID_FLAC && track->codec_priv.size) { ret = matroska_parse_flac(s, track, &extradata_offset); if (ret < 0) return ret; + } else if (codec_id == AV_CODEC_ID_WAVPACK && track->codec_priv.size < 2) { + av_log(matroska->ctx, AV_LOG_INFO, "Assuming WavPack version 4.10 " + "in absence of valid CodecPrivate.\n"); + extradata_size = 2; + extradata = av_mallocz(2 + AV_INPUT_BUFFER_PADDING_SIZE); + if (!extradata) + return AVERROR(ENOMEM); + AV_WL16(extradata, 0x410); } else if (codec_id == AV_CODEC_ID_PRORES && track->codec_priv.size == 4) { fourcc = AV_RL32(track->codec_priv.data); } else if (codec_id == AV_CODEC_ID_VP9 && track->codec_priv.size) { @@ -2873,10 +2913,10 @@ static int matroska_read_header(AVFormatContext *s) av_dict_set(&st->metadata, "mimetype", attachments[j].mime, 0); st->codecpar->codec_id = AV_CODEC_ID_NONE; - for (i = 0; ff_mkv_image_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { - if (!strncmp(ff_mkv_image_mime_tags[i].str, attachments[j].mime, - strlen(ff_mkv_image_mime_tags[i].str))) { - st->codecpar->codec_id = ff_mkv_image_mime_tags[i].id; + for (i = 0; mkv_image_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { + if (!strncmp(mkv_image_mime_tags[i].str, attachments[j].mime, + strlen(mkv_image_mime_tags[i].str))) { + st->codecpar->codec_id = mkv_image_mime_tags[i].id; break; } } @@ -2904,10 +2944,10 @@ static int matroska_read_header(AVFormatContext *s) memcpy(st->codecpar->extradata, attachments[j].bin.data, attachments[j].bin.size); - for (i = 0; ff_mkv_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { - if (!strncmp(ff_mkv_mime_tags[i].str, attachments[j].mime, - strlen(ff_mkv_mime_tags[i].str))) { - st->codecpar->codec_id = ff_mkv_mime_tags[i].id; + for (i = 0; mkv_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { + if (!strncmp(mkv_mime_tags[i].str, attachments[j].mime, + strlen(mkv_mime_tags[i].str))) { + st->codecpar->codec_id = mkv_mime_tags[i].id; break; } } @@ -2924,10 +2964,6 @@ static int matroska_read_header(AVFormatContext *s) (AVRational) { 1, 1000000000 }, chapters[i].start, chapters[i].end, chapters[i].title); - if (chapters[i].chapter) { - av_dict_set(&chapters[i].chapter->metadata, - "title", chapters[i].title, 0); - } max_start = chapters[i].start; } @@ -2990,7 +3026,9 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, return 0; } - av_assert0(size > 0); + if (size <= 0) + return AVERROR_INVALIDDATA; + *laces = *data + 1; data += 1; size -= 1; @@ -3016,7 +3054,7 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, break; } } - if (size <= total) { + if (size < total) { return AVERROR_INVALIDDATA; } @@ -3063,7 +3101,7 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, } data += offset; size -= offset; - if (size <= total) { + if (size < total) { return AVERROR_INVALIDDATA; } lace_size[*laces - 1] = size - total; @@ -3109,7 +3147,7 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, } memcpy(track->audio.buf + y * w, data, w); } else { - if (size < sps * w / sps || h<=0 || w%sps) { + if (size < w) { av_log(matroska->ctx, AV_LOG_ERROR, "Corrupt generic RM-style audio packet size\n"); return AVERROR_INVALIDDATA; @@ -3154,19 +3192,21 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, } /* reconstruct full wavpack blocks from mangled matroska ones */ -static int matroska_parse_wavpack(MatroskaTrack *track, uint8_t *src, - uint8_t **pdst, int *size) +static int matroska_parse_wavpack(MatroskaTrack *track, + uint8_t **data, int *size) { uint8_t *dst = NULL; + uint8_t *src = *data; int dstlen = 0; int srclen = *size; uint32_t samples; uint16_t ver; int ret, offset = 0; - if (srclen < 12 || track->stream->codecpar->extradata_size < 2) + if (srclen < 12) return AVERROR_INVALIDDATA; + av_assert1(track->stream->codecpar->extradata_size >= 2); ver = AV_RL16(track->stream->codecpar->extradata); samples = AV_RL32(src); @@ -3226,7 +3266,7 @@ static int matroska_parse_wavpack(MatroskaTrack *track, uint8_t *src, memset(dst + dstlen, 0, AV_INPUT_BUFFER_PADDING_SIZE); - *pdst = dst; + *data = dst; *size = dstlen; return 0; @@ -3236,14 +3276,11 @@ fail: return ret; } -static int matroska_parse_prores(MatroskaTrack *track, uint8_t *src, - uint8_t **pdst, int *size) +static int matroska_parse_prores(MatroskaTrack *track, + uint8_t **data, int *size) { - uint8_t *dst = src; - int dstlen = *size; - - if (AV_RB32(&src[4]) != MKBETAG('i', 'c', 'p', 'f')) { - dstlen += 8; + uint8_t *dst; + int dstlen = *size + 8; dst = av_malloc(dstlen + AV_INPUT_BUFFER_PADDING_SIZE); if (!dst) @@ -3251,11 +3288,10 @@ static int matroska_parse_prores(MatroskaTrack *track, uint8_t *src, AV_WB32(dst, dstlen); AV_WB32(dst + 4, MKBETAG('i', 'c', 'p', 'f')); - memcpy(dst + 8, src, dstlen - 8); + memcpy(dst + 8, *data, dstlen - 8); memset(dst + dstlen, 0, AV_INPUT_BUFFER_PADDING_SIZE); - } - *pdst = dst; + *data = dst; *size = dstlen; return 0; @@ -3378,51 +3414,46 @@ static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, static int matroska_parse_frame(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, - AVBufferRef *buf, uint8_t *data, int pkt_size, + AVBufferRef *buf, uint8_t **data, int pkt_size, uint64_t timecode, uint64_t lace_duration, int64_t pos, int is_keyframe, uint8_t *additional, uint64_t additional_id, int additional_size, int64_t discard_padding) { - MatroskaTrackEncoding *encodings = track->encodings.elem; - uint8_t *pkt_data = data; - int res; + uint8_t *pkt_data = *data; + int res = 0; AVPacket pktl, *pkt = &pktl; - if (encodings && !encodings->type && encodings->scope & 1) { - res = matroska_decode_buffer(&pkt_data, &pkt_size, track); - if (res < 0) - return res; - } - if (st->codecpar->codec_id == AV_CODEC_ID_WAVPACK) { - uint8_t *wv_data; - res = matroska_parse_wavpack(track, pkt_data, &wv_data, &pkt_size); + res = matroska_parse_wavpack(track, &pkt_data, &pkt_size); if (res < 0) { av_log(matroska->ctx, AV_LOG_ERROR, "Error parsing a wavpack block.\n"); goto fail; } - if (pkt_data != data) - av_freep(&pkt_data); - pkt_data = wv_data; + if (!buf) + av_freep(data); + buf = NULL; } - if (st->codecpar->codec_id == AV_CODEC_ID_PRORES) { - uint8_t *pr_data; - res = matroska_parse_prores(track, pkt_data, &pr_data, &pkt_size); + if (st->codecpar->codec_id == AV_CODEC_ID_PRORES && + AV_RB32(pkt_data + 4) != MKBETAG('i', 'c', 'p', 'f')) { + res = matroska_parse_prores(track, &pkt_data, &pkt_size); if (res < 0) { av_log(matroska->ctx, AV_LOG_ERROR, "Error parsing a prores block.\n"); goto fail; } - if (pkt_data != data) - av_freep(&pkt_data); - pkt_data = pr_data; + if (!buf) + av_freep(data); + buf = NULL; } + if (!pkt_size && !additional_size) + goto no_output; + av_init_packet(pkt); - if (pkt_data != data) + if (!buf) pkt->buf = av_buffer_create(pkt_data, pkt_size + AV_INPUT_BUFFER_PADDING_SIZE, NULL, NULL, 0); else @@ -3491,9 +3522,10 @@ FF_ENABLE_DEPRECATION_WARNINGS return 0; +no_output: fail: - if (pkt_data != data) - av_freep(&pkt_data); + if (!buf) + av_freep(data); return res; } @@ -3522,13 +3554,16 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf size -= n; track = matroska_find_track_by_num(matroska, num); - if (!track || !track->stream) { - av_log(matroska->ctx, AV_LOG_INFO, - "Invalid stream %"PRIu64"\n", num); + if (!track || size < 3) return AVERROR_INVALIDDATA; - } else if (size <= 3) + + if (!(st = track->stream)) { + av_log(matroska->ctx, AV_LOG_VERBOSE, + "No stream associated to TrackNumber %"PRIu64". " + "Ignoring Block with this TrackNumber.\n", num); return 0; - st = track->stream; + } + if (st->discard >= AVDISCARD_ALL) return res; av_assert1(block_duration != AV_NOPTS_VALUE); @@ -3592,29 +3627,41 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf for (n = 0; n < laces; n++) { int64_t lace_duration = block_duration*(n+1) / laces - block_duration*n / laces; + uint8_t *out_data = data; + int out_size = lace_size[n]; - if ((st->codecpar->codec_id == AV_CODEC_ID_RA_288 || - st->codecpar->codec_id == AV_CODEC_ID_COOK || - st->codecpar->codec_id == AV_CODEC_ID_SIPR || - st->codecpar->codec_id == AV_CODEC_ID_ATRAC3) && - st->codecpar->block_align && track->audio.sub_packet_size) { - res = matroska_parse_rm_audio(matroska, track, st, data, - lace_size[n], + if (track->needs_decoding) { + res = matroska_decode_buffer(&out_data, &out_size, track); + if (res < 0) + return res; + /* Given that we are here means that out_data is no longer + * owned by buf, so set it to NULL. This depends upon + * zero-length header removal compression being ignored. */ + av_assert1(out_data != data); + buf = NULL; + } + + if (track->audio.buf) { + res = matroska_parse_rm_audio(matroska, track, st, + out_data, out_size, timecode, pos); + if (!buf) + av_free(out_data); if (res) return res; - } else if (st->codecpar->codec_id == AV_CODEC_ID_WEBVTT) { res = matroska_parse_webvtt(matroska, track, st, - data, lace_size[n], + out_data, out_size, timecode, lace_duration, pos); + if (!buf) + av_free(out_data); if (res) return res; } else { - res = matroska_parse_frame(matroska, track, st, buf, data, lace_size[n], - timecode, lace_duration, pos, - !n ? is_keyframe : 0, + res = matroska_parse_frame(matroska, track, st, buf, &out_data, + out_size, timecode, lace_duration, + pos, !n ? is_keyframe : 0, additional, additional_id, additional_size, discard_padding); if (res) @@ -4113,8 +4160,8 @@ static int webm_dash_manifest_cues(AVFormatContext *s, int64_t init_range) } end += ret; } - av_dict_set(&s->streams[0]->metadata, CUE_TIMESTAMPS, buf, 0); - av_free(buf); + av_dict_set(&s->streams[0]->metadata, CUE_TIMESTAMPS, + buf, AV_DICT_DONT_STRDUP_VAL); return 0; } @@ -4139,8 +4186,8 @@ static int webm_dash_manifest_read_header(AVFormatContext *s) if (!matroska->is_live) { buf = av_asprintf("%g", matroska->duration); if (!buf) return AVERROR(ENOMEM); - av_dict_set(&s->streams[0]->metadata, DURATION, buf, 0); - av_free(buf); + av_dict_set(&s->streams[0]->metadata, DURATION, + buf, AV_DICT_DONT_STRDUP_VAL); // initialization range // 5 is the offset of Cluster ID.