X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Favidec.c;h=659bfa7b6d60e557d5457d069009db65532e04b4;hb=5f2c8315b3c1b28da0386fddb118ad6a0ed77a0c;hp=26deb31ec2a5eb9b790f6d42ed738297ab93ad4d;hpb=c8f0b20b4a6bb6691928789d83e4b02896969848;p=ffmpeg diff --git a/libavformat/avidec.c b/libavformat/avidec.c index 26deb31ec2a..659bfa7b6d6 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -19,15 +19,19 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "libavutil/avstring.h" #include "libavutil/bswap.h" #include "libavutil/dict.h" +#include "libavutil/internal.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "avformat.h" #include "avi.h" #include "dv.h" #include "internal.h" +#include "isom.h" #include "riff.h" #undef NDEBUG @@ -39,6 +43,7 @@ typedef struct AVIStream { int remaining; int packet_size; + uint32_t handler; uint32_t scale; uint32_t rate; int sample_size; /* size of one sample (or packet) @@ -57,7 +62,7 @@ typedef struct AVIStream { uint8_t *sub_buffer; } AVIStream; -typedef struct { +typedef struct AVIContext { int64_t riff_end; int64_t movi_end; int64_t fsize; @@ -90,7 +95,7 @@ static int avi_load_index(AVFormatContext *s); static int guess_ni_flag(AVFormatContext *s); #define print_tag(str, tag, size) \ - av_dlog(NULL, "%s: tag=%c%c%c%c size=0x%x\n", \ + av_log(NULL, AV_LOG_TRACE, "%s: tag=%c%c%c%c size=0x%x\n", \ str, tag & 0xff, \ (tag >> 8) & 0xff, \ (tag >> 16) & 0xff, \ @@ -148,9 +153,9 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num) AVIStream *ast; int i; int64_t last_pos = -1; - int64_t filesize = avio_size(s->pb); + int64_t filesize = avi->fsize; - av_dlog(s, + av_log(s, AV_LOG_TRACE, "longs_pre_entry:%d index_type:%d entries_in_use:%d " "chunk_id:%X base:%16"PRIX64"\n", longs_pre_entry, @@ -191,7 +196,7 @@ static int read_braindead_odml_indx(AVFormatContext *s, int frame_num) int key = len >= 0; len &= 0x7FFFFFFF; - av_dlog(s, "pos:%"PRId64", len:%X\n", pos, len); + av_log(s, AV_LOG_TRACE, "pos:%"PRId64", len:%X\n", pos, len); if (pb->eof_reached) return AVERROR_INVALIDDATA; @@ -371,7 +376,7 @@ static int avi_read_header(AVFormatContext *s) return ret; avi->fsize = avio_size(pb); - if (avi->fsize <= 0) + if (avi->fsize <= 0 || avi->fsize < avi->riff_end) avi->fsize = avi->riff_end == 8 ? INT64_MAX : avi->riff_end; /* first list tag */ @@ -399,8 +404,8 @@ static int avi_read_header(AVFormatContext *s) if (size) avi->movi_end = avi->movi_list + size + (size & 1); else - avi->movi_end = avio_size(pb); - av_dlog(NULL, "movi end=%"PRIx64"\n", avi->movi_end); + avi->movi_end = avi->fsize; + av_log(NULL, AV_LOG_TRACE, "movi end=%"PRIx64"\n", avi->movi_end); goto end_of_header; } else if (tag1 == MKTAG('I', 'N', 'F', 'O')) ff_read_riff_info(s, size - 4); @@ -483,13 +488,15 @@ static int avi_read_header(AVFormatContext *s) ast = s->streams[0]->priv_data; av_freep(&s->streams[0]->codec->extradata); av_freep(&s->streams[0]->codec); + av_freep(&s->streams[0]->info); av_freep(&s->streams[0]); s->nb_streams = 0; if (CONFIG_DV_DEMUXER) { avi->dv_demux = avpriv_dv_init_demux(s); if (!avi->dv_demux) goto fail; - } + } else + goto fail; s->streams[0]->priv_data = ast; avio_skip(pb, 3 * 4); ast->scale = avio_rl32(pb); @@ -510,7 +517,7 @@ static int avi_read_header(AVFormatContext *s) } assert(stream_index < s->nb_streams); - st->codec->stream_codec_tag = handler; + ast->handler = handler; avio_rl32(pb); /* flags */ avio_rl16(pb); /* priority */ @@ -520,7 +527,7 @@ static int avi_read_header(AVFormatContext *s) ast->rate = avio_rl32(pb); if (!(ast->scale && ast->rate)) { av_log(s, AV_LOG_WARNING, - "scale/rate is %u/%u which is invalid. " + "scale/rate is %"PRIu32"/%"PRIu32" which is invalid. " "(This file has been generated by broken software.)\n", ast->scale, ast->rate); @@ -542,7 +549,7 @@ static int avi_read_header(AVFormatContext *s) avio_rl32(pb); /* quality */ ast->sample_size = avio_rl32(pb); /* sample ssize */ ast->cum_len *= FFMAX(1, ast->sample_size); - av_dlog(s, "%"PRIu32" %"PRIu32" %d\n", + av_log(s, AV_LOG_TRACE, "%"PRIu32" %"PRIu32" %d\n", ast->rate, ast->scale, ast->sample_size); switch (tag1) { @@ -564,6 +571,23 @@ static int avi_read_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "unknown stream type %X\n", tag1); goto fail; } + + if (ast->sample_size < 0) { + if (s->error_recognition & AV_EF_EXPLODE) { + av_log(s, AV_LOG_ERROR, + "Invalid sample_size %d at stream %d\n", + ast->sample_size, + stream_index); + goto fail; + } + av_log(s, AV_LOG_WARNING, + "Invalid sample_size %d at stream %d " + "setting it to 0\n", + ast->sample_size, + stream_index); + ast->sample_size = 0; + } + if (ast->sample_size == 0) st->duration = st->nb_frames; ast->frame_offset = ast->cum_len; @@ -601,7 +625,7 @@ static int avi_read_header(AVFormatContext *s) if (size > 10 * 4 && size < (1 << 30)) { st->codec->extradata_size = size - 10 * 4; st->codec->extradata = av_malloc(st->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); + AV_INPUT_BUFFER_PADDING_SIZE); if (!st->codec->extradata) { st->codec->extradata_size = 0; return AVERROR(ENOMEM); @@ -642,9 +666,25 @@ static int avi_read_header(AVFormatContext *s) st->codec->codec_tag = tag1; st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1); + /* If codec is not found yet, try with the mov tags. */ + if (!st->codec->codec_id) { + char tag_buf[32]; + av_get_codec_tag_string(tag_buf, sizeof(tag_buf), tag1); + st->codec->codec_id = + ff_codec_get_id(ff_codec_movvideo_tags, tag1); + if (st->codec->codec_id) + av_log(s, AV_LOG_WARNING, + "mov tag found in avi (fourcc %s)\n", + tag_buf); + } /* This is needed to get the pict type which is necessary * for generating correct pts. */ st->need_parsing = AVSTREAM_PARSE_HEADERS; + + if (st->codec->codec_id == AV_CODEC_ID_MPEG4 && + ast->handler == MKTAG('X', 'V', 'I', 'D')) + st->codec->codec_tag = MKTAG('X', 'V', 'I', 'D'); + // Support "Resolution 1:1" for Avid AVI Codec if (tag1 == MKTAG('A', 'V', 'R', 'n') && st->codec->extradata_size >= 31 && @@ -654,10 +694,12 @@ static int avi_read_header(AVFormatContext *s) if (st->codec->codec_tag == 0 && st->codec->height > 0 && st->codec->extradata_size < 1U << 30) { st->codec->extradata_size += 9; - st->codec->extradata = av_realloc(st->codec->extradata, - st->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); - if (st->codec->extradata) + if ((ret = av_reallocp(&st->codec->extradata, + st->codec->extradata_size + + AV_INPUT_BUFFER_PADDING_SIZE)) < 0) { + st->codec->extradata_size = 0; + return ret; + } else memcpy(st->codec->extradata + st->codec->extradata_size - 9, "BottomUp", 9); } @@ -666,7 +708,7 @@ static int avi_read_header(AVFormatContext *s) // avio_skip(pb, size - 5 * 4); break; case AVMEDIA_TYPE_AUDIO: - ret = ff_get_wav_header(pb, st->codec, size); + ret = ff_get_wav_header(s, pb, st->codec, size); if (ret < 0) return ret; ast->dshow_block_align = st->codec->block_align; @@ -694,7 +736,7 @@ static int avi_read_header(AVFormatContext *s) st->need_parsing = AVSTREAM_PARSE_NONE; /* AVI files with Xan DPCM audio (wrongly) declare PCM * audio in the header but have Axan as stream_code_tag. */ - if (st->codec->stream_codec_tag == AV_RL32("Axan")) { + if (ast->handler == AV_RL32("Axan")) { st->codec->codec_id = AV_CODEC_ID_XAN_DPCM; st->codec->codec_tag = 0; } @@ -744,7 +786,7 @@ static int avi_read_header(AVFormatContext *s) if (active_aspect.num && active_aspect.den && active.num && active.den) { st->sample_aspect_ratio = av_div_q(active_aspect, active); - av_dlog(s, "vprp %d/%d %d/%d\n", + av_log(s, AV_LOG_TRACE, "vprp %d/%d %d/%d\n", active_aspect.num, active_aspect.den, active.num, active.den); } @@ -767,7 +809,7 @@ static int avi_read_header(AVFormatContext *s) if (s->error_recognition & AV_EF_EXPLODE) goto fail; avi->movi_list = avio_tell(pb) - 4; - avi->movi_end = avio_size(pb); + avi->movi_end = avi->fsize; goto end_of_header; } /* skip tag */ @@ -788,7 +830,11 @@ fail: if (!avi->index_loaded && pb->seekable) avi_load_index(s); avi->index_loaded = 1; - avi->non_interleaved |= guess_ni_flag(s); + + if ((ret = guess_ni_flag(s)) < 0) + return ret; + + avi->non_interleaved |= ret; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; if (st->nb_index_entries) @@ -813,7 +859,8 @@ fail: static int read_gab2_sub(AVStream *st, AVPacket *pkt) { - if (!strcmp(pkt->data, "GAB2") && AV_RL16(pkt->data + 5) == 2) { + if (pkt->size >= 7 && + !strcmp(pkt->data, "GAB2") && AV_RL16(pkt->data + 5) == 2) { uint8_t desc[256]; int score = AVPROBE_SCORE_EXTENSION, ret; AVIStream *ast = st->priv_data; @@ -927,7 +974,7 @@ start_sync: size = d[4] + (d[5] << 8) + (d[6] << 16) + (d[7] << 24); n = get_stream_idx(d + 2); - av_dlog(s, "%X %X %X %X %X %X %X %X %"PRId64" %u %d\n", + av_log(s, AV_LOG_TRACE, "%X %X %X %X %X %X %X %X %"PRId64" %u %d\n", d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], i, size, n); if (i + (uint64_t)size > avi->fsize || d[0] > 127) continue; @@ -947,7 +994,7 @@ start_sync: goto start_sync; } - n = get_stream_idx(d); + n = avi->dv_demux ? 0 : get_stream_idx(d); if (!((i - avi->last_pkt_pos) & 1) && get_stream_idx(d + 1) < s->nb_streams) @@ -1048,14 +1095,13 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt) AVIContext *avi = s->priv_data; AVIOContext *pb = s->pb; int err; -#if FF_API_DESTRUCT_PACKET - void *dstr; -#endif if (CONFIG_DV_DEMUXER && avi->dv_demux) { int size = avpriv_dv_get_packet(avi->dv_demux, pkt); if (size >= 0) return size; + else + goto resync; } if (avi->non_interleaved) { @@ -1082,7 +1128,7 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt) (AVRational) { FFMAX(1, ast->sample_size), AV_TIME_BASE }); - av_dlog(s, "%"PRId64" %d/%d %"PRId64"\n", ts, + av_log(s, AV_LOG_TRACE, "%"PRId64" %d/%d %"PRId64"\n", ts, st->time_base.num, st->time_base.den, ast->frame_offset); if (ts < best_ts) { best_ts = ts; @@ -1164,18 +1210,12 @@ resync: if (CONFIG_DV_DEMUXER && avi->dv_demux) { AVBufferRef *avbuf = pkt->buf; -#if FF_API_DESTRUCT_PACKET - dstr = pkt->destruct; -#endif size = avpriv_dv_produce_packet(avi->dv_demux, pkt, pkt->data, pkt->size); -#if FF_API_DESTRUCT_PACKET - pkt->destruct = dstr; -#endif pkt->buf = avbuf; pkt->flags |= AV_PKT_FLAG_KEY; if (size < 0) - av_free_packet(pkt); + av_packet_unref(pkt); } else if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE && !st->codec->codec_tag && read_gab2_sub(st, pkt)) { ast->frame_offset++; @@ -1188,7 +1228,7 @@ resync: // pkt->dts += ast->start; if (ast->sample_size) pkt->dts /= ast->sample_size; - av_dlog(s, + av_log(s, AV_LOG_TRACE, "dts:%"PRId64" offset:%"PRId64" %d/%d smpl_siz:%d " "base:%d st:%d size:%d\n", pkt->dts, @@ -1261,7 +1301,7 @@ static int avi_read_idx1(AVFormatContext *s, int size) flags = avio_rl32(pb); pos = avio_rl32(pb); len = avio_rl32(pb); - av_dlog(s, "%d: tag=0x%x flags=0x%x pos=0x%x len=%d/", + av_log(s, AV_LOG_TRACE, "%d: tag=0x%x flags=0x%x pos=0x%x len=%d/", i, tag, flags, pos, len); index = ((tag & 0xff) - '0') * 10; @@ -1277,7 +1317,7 @@ static int avi_read_idx1(AVFormatContext *s, int size) } pos += data_offset; - av_dlog(s, "%d cum_len=%"PRId64"\n", len, ast->cum_len); + av_log(s, AV_LOG_TRACE, "%d cum_len=%"PRId64"\n", len, ast->cum_len); if (pb->eof_reached) return AVERROR_INVALIDDATA; @@ -1293,6 +1333,64 @@ static int avi_read_idx1(AVFormatContext *s, int size) return 0; } +/* Scan the index and consider any file with streams more than + * 2 seconds or 64MB apart non-interleaved. */ +static int check_stream_max_drift(AVFormatContext *s) +{ + int64_t min_pos, pos; + int i; + int *idx = av_mallocz_array(s->nb_streams, sizeof(*idx)); + if (!idx) + return AVERROR(ENOMEM); + + for (min_pos = pos = 0; min_pos != INT64_MAX; pos = min_pos + 1LU) { + int64_t max_dts = INT64_MIN / 2; + int64_t min_dts = INT64_MAX / 2; + int64_t max_buffer = 0; + + min_pos = INT64_MAX; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AVIStream *ast = st->priv_data; + int n = st->nb_index_entries; + while (idx[i] < n && st->index_entries[idx[i]].pos < pos) + idx[i]++; + if (idx[i] < n) { + int64_t dts; + dts = av_rescale_q(st->index_entries[idx[i]].timestamp / + FFMAX(ast->sample_size, 1), + st->time_base, AV_TIME_BASE_Q); + min_dts = FFMIN(min_dts, dts); + min_pos = FFMIN(min_pos, st->index_entries[idx[i]].pos); + } + } + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AVIStream *ast = st->priv_data; + + if (idx[i] && min_dts != INT64_MAX / 2) { + int64_t dts; + dts = av_rescale_q(st->index_entries[idx[i] - 1].timestamp / + FFMAX(ast->sample_size, 1), + st->time_base, AV_TIME_BASE_Q); + max_dts = FFMAX(max_dts, dts); + max_buffer = FFMAX(max_buffer, + av_rescale(dts - min_dts, + st->codec->bit_rate, + AV_TIME_BASE)); + } + } + if (max_dts - min_dts > 2 * AV_TIME_BASE || + max_buffer > 1024 * 1024 * 8 * 8) { + av_free(idx); + return 1; + } + } + av_free(idx); + return 0; +} + static int guess_ni_flag(AVFormatContext *s) { int i; @@ -1322,7 +1420,11 @@ static int guess_ni_flag(AVFormatContext *s) first_end = st->index_entries[n - 1].pos; } avio_seek(s->pb, oldpos, SEEK_SET); - return last_start > first_end; + + if (last_start > first_end) + return 1; + + return check_stream_max_drift(s); } static int avi_load_index(AVFormatContext *s) @@ -1335,13 +1437,13 @@ static int avi_load_index(AVFormatContext *s) if (avio_seek(pb, avi->movi_end, SEEK_SET) < 0) goto the_end; // maybe truncated file - av_dlog(s, "movi_end=0x%"PRIx64"\n", avi->movi_end); + av_log(s, AV_LOG_TRACE, "movi_end=0x%"PRIx64"\n", avi->movi_end); for (;;) { if (pb->eof_reached) break; tag = avio_rl32(pb); size = avio_rl32(pb); - av_dlog(s, "tag=%c%c%c%c size=0x%x\n", + av_log(s, AV_LOG_TRACE, "tag=%c%c%c%c size=0x%x\n", tag & 0xff, (tag >> 8) & 0xff, (tag >> 16) & 0xff, @@ -1368,7 +1470,7 @@ static void seek_subtitle(AVStream *st, AVStream *st2, int64_t timestamp) { AVIStream *ast2 = st2->priv_data; int64_t ts2 = av_rescale_q(timestamp, st->time_base, st2->time_base); - av_free_packet(&ast2->sub_pkt); + av_packet_unref(&ast2->sub_pkt); if (avformat_seek_file(ast2->sub_ctx, 0, INT64_MIN, ts2, ts2, 0) >= 0 || avformat_seek_file(ast2->sub_ctx, 0, ts2, ts2, INT64_MAX, 0) >= 0) ff_read_packet(ast2->sub_ctx, &ast2->sub_pkt); @@ -1383,12 +1485,17 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, int64_t pos; AVIStream *ast; + /* Does not matter which stream is requested dv in avi has the + * stream information in the first video stream. + */ + if (avi->dv_demux) + stream_index = 0; + if (!avi->index_loaded) { /* we only load the index on demand */ avi_load_index(s); avi->index_loaded = 1; } - assert(stream_index >= 0); st = s->streams[stream_index]; ast = st->priv_data; @@ -1402,14 +1509,13 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, pos = st->index_entries[index].pos; timestamp = st->index_entries[index].timestamp / FFMAX(ast->sample_size, 1); - av_dlog(s, "XX %"PRId64" %d %"PRId64"\n", + av_log(s, AV_LOG_TRACE, "XX %"PRId64" %d %"PRId64"\n", timestamp, index, st->index_entries[index].timestamp); if (CONFIG_DV_DEMUXER && avi->dv_demux) { /* One and only one real stream for DV in AVI, and it has video */ /* offsets. Calling with other stream indexes should have failed */ /* the av_index_search_timestamp call above. */ - assert(stream_index == 0); /* Feed the DV video stream version of the timestamp to the */ /* DV demux so it can synthesize correct timestamps. */ @@ -1455,7 +1561,7 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, index++; } - av_dlog(s, "%"PRId64" %d %"PRId64"\n", + av_log(s, AV_LOG_TRACE, "%"PRId64" %d %"PRId64"\n", timestamp, index, st2->index_entries[index].timestamp); /* extract the current frame number */ ast2->frame_offset = st2->index_entries[index].timestamp; @@ -1481,7 +1587,7 @@ static int avi_read_close(AVFormatContext *s) avformat_close_input(&ast->sub_ctx); } av_free(ast->sub_buffer); - av_free_packet(&ast->sub_pkt); + av_packet_unref(&ast->sub_pkt); } } @@ -1507,6 +1613,7 @@ AVInputFormat ff_avi_demuxer = { .name = "avi", .long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"), .priv_data_size = sizeof(AVIContext), + .extensions = "avi", .read_probe = avi_probe, .read_header = avi_read_header, .read_packet = avi_read_packet,