demux_lavf: get total duration from track durations

Before this change, mpv used to get the total duration from
`avformat_find_stream_info` and used the per-track duration as a
fallback. This change reverses this order of preference.

The timestamps returned by `avformat_find_stream_info` are truncated or
rounded or floored (depending on the decoder) at the 6th decimal place.
For e.g. `avformat_find_stream_info` may return us a duration like
44.138667, whereas the duration we get from the per-track struct has a
higher degree of precision like 44.13866666666... and so on.

This caused various problems such as the playback_pts being a bigger
value than the duration, which would cause time-remaining to be a
negative value in some cases. Or cause you to reach a negative starting
timestamp when looping on an audio file with `gapless-audio`.

Moreover, we already skipped calling `avformat_find_stream_info` for
mp4, so we had already been utilizing this per-track fallback method for
finding the duration for mp4 files. It should be noted that while this
change is only required for audio-only formats, there is no harm in
doing this for videos as well.
This commit is contained in:
llyyr 2023-08-31 15:20:32 +05:30 committed by Dudemanguy
parent 1bf6abc62d
commit e6afc53e7c
1 changed files with 18 additions and 18 deletions

View File

@ -1150,25 +1150,25 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
demuxer->is_network |= priv->format_hack.is_network;
demuxer->seekable &= !priv->format_hack.no_seek;
if (priv->avfc->duration > 0) {
demuxer->duration = (double)priv->avfc->duration / AV_TIME_BASE;
} else {
double total_duration = 0;
double av_duration = 0;
for (int n = 0; n < priv->avfc->nb_streams; n++) {
AVStream *st = priv->avfc->streams[n];
if (st->duration <= 0)
continue;
double f_duration = st->duration * av_q2d(st->time_base);
total_duration = MPMAX(total_duration, f_duration);
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
av_duration = MPMAX(av_duration, f_duration);
}
double duration = av_duration > 0 ? av_duration : total_duration;
if (duration > 0)
demuxer->duration = duration;
// We initially prefer track durations over container durations because they
// have a higher degree of precision over the container duration which are
// only accurate to the 6th decimal place. This is probably a lavf bug.
double total_duration = 0;
double av_duration = 0;
for (int n = 0; n < priv->avfc->nb_streams; n++) {
AVStream *st = priv->avfc->streams[n];
if (st->duration <= 0)
continue;
double f_duration = st->duration * av_q2d(st->time_base);
total_duration = MPMAX(total_duration, f_duration);
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
av_duration = MPMAX(av_duration, f_duration);
}
double duration = av_duration > 0 ? av_duration : total_duration;
if (duration == 0 && priv->avfc->duration > 0)
duration = (double)priv->avfc->duration / AV_TIME_BASE;
demuxer->duration = duration;
if (demuxer->duration < 0 && priv->format_hack.no_seek_on_no_duration)
demuxer->seekable = false;