Opened 4 years ago
Last modified 3 years ago
#10163 new defect
FFprobe / libavformat gives wrong duration for MP3 file
| Reported by: | Jonathan | Owned by: | |
|---|---|---|---|
| Priority: | normal | Component: | avformat |
| Version: | unspecified | Keywords: | MP3 |
| Cc: | Jonathan | Blocked By: | |
| Blocking: | Reproduced by developer: | no | |
| Analyzed by developer: | no |
Description
Summary of the bug:
libavformat seems to give a duration that's higher than the duration of the decoded samples. I've seen this with both files "from the wild" as well as files generated with FFmpeg lavfi.
All the files have contained "xing" tags that state the number of frames upfront. If I understand correctly, this frame count is then used to estimate the duration in mp3dec.c as st->duration = av_rescale_q(mp3->frames, (AVRational){spf, c.sample_rate}, st->time_base);. I.e. the resulting duration value is based on the assumption that the media is made up of frames * spf samples. However, this doesn't seem to account for the fact that some of those samples are meant to be skipped or discarded at decoding. If one subtracts skipped and discarded samples, the result seems to be a more accurate duration value.
How to reproduce:
# Generate 1s long test file:
ffmpeg -f lavfi -i sine -t 1 test1.mp3
# Probe it to get its duration
ffprobe -loglevel trace test1.mp3
ffprobe version 5.1.2 Copyright (c) 2007-2022 the FFmpeg developers
built with Apple clang version 14.0.0 (clang-1400.0.29.202)
configuration: --prefix=/usr/local/Cellar/ffmpeg/5.1.2_1 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox
libavutil 57. 28.100 / 57. 28.100
libavcodec 59. 37.100 / 59. 37.100
libavformat 59. 27.100 / 59. 27.100
libavdevice 59. 7.100 / 59. 7.100
libavfilter 8. 44.100 / 8. 44.100
libswscale 6. 7.100 / 6. 7.100
libswresample 4. 7.100 / 4. 7.100
libpostproc 56. 6.100 / 56. 6.100
Input #0, mp3, from 'test1.mp3':
Metadata:
encoder : Lavf59.27.100
Duration: 00:00:01.04, start: 0.025057, bitrate: 65 kb/s
Stream #0:0: Audio: mp3, 44100 Hz, mono, fltp, 64 kb/s
Here, FFprobe gives a duration of 1.04s, whereas I would have expected it to give 1s.
We can derive the resulting value 1.04 from the calculation frames*spf/sample_rate = 40*1152/44100 = 1.0449.
Running FFprobe with -loglevel trace reveals:
[mp3 @ 0x7fbb70f05040] demuxer injecting skip 1105 / discard 0 [mp3float @ 0x7fbb72004680] skip 1105 / discard 0 samples due to side data [mp3float @ 0x7fbb72004680] skip 1105/1152 samples [mp3 @ 0x7fbb70f05040] demuxer injecting skip 0 / discard 875
We can modify the calculation to account for skip=1105 and discard=875: (frames*spf-skip-discard)/sample_rate = (40*1152-1105-875)/44100 = 1. This modified calculation gives the value I would have expected.
Proposed patch:
Below is a patch that I think may achieve the above (assuming I understood the fields of the mp3 and sti structs correctly). I should note that this change seems to break "seek-extra-mp3" in the fate tests.
diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c
index 90d75626b4..4b9527a691 100644
--- a/libavformat/mp3dec.c
+++ b/libavformat/mp3dec.c
@@ -322,6 +322,7 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
int vbrtag_size = 0;
MP3DecContext *mp3 = s->priv_data;
int ret;
+ FFStream *sti;
ffio_init_checksum(s->pb, ff_crcA001_update, 0);
@@ -349,9 +350,13 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
/* Skip the vbr tag frame */
avio_seek(s->pb, base + vbrtag_size, SEEK_SET);
- if (mp3->frames)
- st->duration = av_rescale_q(mp3->frames, (AVRational){spf, c.sample_rate},
+ if (mp3->frames) {
+ sti = ffstream(st);
+ st->duration = av_rescale_q(mp3->frames * spf - sti->start_skip_samples - mp3->end_pad,
+ (AVRational){1, c.sample_rate},
st->time_base);
+ }
+
if (mp3->header_filesize && mp3->frames && !mp3->is_cbr)
st->codecpar->bit_rate = av_rescale(mp3->header_filesize, 8 * c.sample_rate, mp3->frames * (int64_t)spf);



https://patchwork.ffmpeg.org/project/ffmpeg/patch/20230823183335.988006-1-ulrik.mikaelsson@gmail.com/
Will fix this
The issue #9755 still remains I imagine