From fe1c1198e670242f3cf9e3e1eef27cff77f3ee23 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Thu, 28 Jun 2012 15:49:51 +0200 Subject: [PATCH] lavf: use dts difference instead of AVPacket.duration in find_stream_info() AVPacket.duration is mostly made up and thus completely useless, this is especially true for video streams. Therefore use dts difference for framerate estimation and the max_analyze_duration check. The asyncts test now needs -analyzeduration, because the default is 5 seconds and the audio stream in the sample appears at ~10 seconds. --- libavformat/avformat.h | 10 +++++++++- libavformat/utils.c | 38 ++++++++++++++++++++++++++++++++------ tests/fate/filter.mak | 2 +- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 7c97ada2c8..0e50487414 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -718,9 +718,17 @@ typedef struct AVStream { int64_t duration_gcd; int duration_count; double duration_error[MAX_STD_TIMEBASES]; - int64_t codec_info_duration; int nb_decoded_frames; int found_decoder; + + /** + * Those are used for average framerate estimation. + */ + int64_t fps_first_dts; + int fps_first_dts_idx; + int64_t fps_last_dts; + int fps_last_dts_idx; + } *info; int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */ diff --git a/libavformat/utils.c b/libavformat/utils.c index 149913cb04..bd94f7d7a7 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -2288,6 +2288,8 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) for (i=0; inb_streams; i++) { ic->streams[i]->info->last_dts = AV_NOPTS_VALUE; + ic->streams[i]->info->fps_first_dts = AV_NOPTS_VALUE; + ic->streams[i]->info->fps_last_dts = AV_NOPTS_VALUE; } count = 0; @@ -2395,12 +2397,31 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) read_size += pkt->size; st = ic->streams[pkt->stream_index]; - if (st->codec_info_nb_frames>1) { - if (av_rescale_q(st->info->codec_info_duration, st->time_base, AV_TIME_BASE_Q) >= ic->max_analyze_duration) { + if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) { + /* check for non-increasing dts */ + if (st->info->fps_last_dts != AV_NOPTS_VALUE && + st->info->fps_last_dts >= pkt->dts) { + av_log(ic, AV_LOG_WARNING, "Non-increasing DTS in stream %d: " + "packet %d with DTS %"PRId64", packet %d with DTS " + "%"PRId64"\n", st->index, st->info->fps_last_dts_idx, + st->info->fps_last_dts, st->codec_info_nb_frames, pkt->dts); + st->info->fps_first_dts = st->info->fps_last_dts = AV_NOPTS_VALUE; + } + + /* update stored dts values */ + if (st->info->fps_first_dts == AV_NOPTS_VALUE) { + st->info->fps_first_dts = pkt->dts; + st->info->fps_first_dts_idx = st->codec_info_nb_frames; + } + st->info->fps_last_dts = pkt->dts; + st->info->fps_last_dts_idx = st->codec_info_nb_frames; + + /* check max_analyze_duration */ + if (av_rescale_q(pkt->dts - st->info->fps_first_dts, st->time_base, + AV_TIME_BASE_Q) >= ic->max_analyze_duration) { av_log(ic, AV_LOG_WARNING, "max_analyze_duration reached\n"); break; } - st->info->codec_info_duration += pkt->duration; } { int64_t last = st->info->last_dts; @@ -2460,10 +2481,15 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) for(i=0;inb_streams;i++) { st = ic->streams[i]; if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - if (st->codec_info_nb_frames>2 && !st->avg_frame_rate.num && st->info->codec_info_duration) + /* estimate average framerate if not set by demuxer */ + if (!st->avg_frame_rate.num && st->info->fps_last_dts != st->info->fps_first_dts) { + int64_t delta_dts = st->info->fps_last_dts - st->info->fps_first_dts; + int delta_packets = st->info->fps_last_dts_idx - st->info->fps_first_dts_idx; + av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, - (st->codec_info_nb_frames-2)*(int64_t)st->time_base.den, - st->info->codec_info_duration*(int64_t)st->time_base.num, 60000); + delta_packets*(int64_t)st->time_base.den, + delta_dts*(int64_t)st->time_base.num, 60000); + } // the check for tb_unreliable() is not completely correct, since this is not about handling // a unreliable/inexact time base, but a time base that is finer than necessary, as e.g. // ipmovie.c produces. diff --git a/tests/fate/filter.mak b/tests/fate/filter.mak index f847a6609c..7c1170f43f 100644 --- a/tests/fate/filter.mak +++ b/tests/fate/filter.mak @@ -22,7 +22,7 @@ FATE_SAMPLES_AVCONV += $(FATE_AMIX) FATE_ASYNCTS += fate-filter-asyncts fate-filter-asyncts: SRC = $(SAMPLES)/nellymoser/nellymoser-discont.flv -fate-filter-asyncts: CMD = pcm -i $(SRC) -af asyncts +fate-filter-asyncts: CMD = pcm -analyzeduration 10000000 -i $(SRC) -af asyncts fate-filter-asyncts: CMP = oneoff fate-filter-asyncts: REF = $(SAMPLES)/nellymoser/nellymoser-discont.pcm