[FFmpeg-trac] #10163(avformat:new): FFprobe / libavformat gives wrong duration for MP3 file

FFmpeg trac at avcodec.org
Fri Feb 3 15:06:23 EET 2023


#10163: FFprobe / libavformat gives wrong duration for MP3 file
----------------------------------+---------------------------------------
             Reporter:  Jonathan  |                     Type:  defect
               Status:  new       |                 Priority:  normal
            Component:  avformat  |                  Version:  unspecified
             Keywords:  MP3       |               Blocked By:
             Blocking:            |  Reproduced by developer:  0
Analyzed by developer:  0         |
----------------------------------+---------------------------------------
 '''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);

 }}}
-- 
Ticket URL: <https://trac.ffmpeg.org/ticket/10163>
FFmpeg <https://ffmpeg.org>
FFmpeg issue tracker


More information about the FFmpeg-trac mailing list