Search code examples
videoffmpegdurationffprobe

How to get accurate output video duration from ffmpeg?


Is there a way to get ffmpeg.exe to give the duration of the OUTPUT video?

I know how to get a video duration from an input video from ffmpeg. Just look for "Duration: " and parse the HH:MM:SS:XX.

Sample output:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'video.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf60.3.100
  Duration: 00:05:53.24, start: 0.000000, bitrate: 1102 kb/s
  Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 854x480 [SAR 1:1 DAR 427:240], 937 kb/s, 23.98 fps, 23.98 tbr, 24k tbn (default)
    Metadata:
      handler_name    : VideoHandler
      vendor_id       : [0][0][0][0]
  Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 159 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
      vendor_id       : [0][0][0][0]

Unfortunately, this is only accurate to hundredths of a second, and I need something more precise than that. And it's the duration of the input video, and I need the duration of the output video.

I know how to get a video duration, in seconds, accurate to six decimal places, from a video by using ffprobe.exe.

ffprobe -v error -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=duration video.mp4

Sample output:
353.227875

I also have read that calls to the ffmpeg libraries return a floating point value that is the duration of the output video in seconds. So ffmpeg.exe knows the output video duration, and it knows it quite accurately.

So, is there a way to get an accurate duration of the output video from ffmpeg.exe? Or am I stuck making one call to ffmpeg.exe to do the video processing, then a separate call to ffprobe.exe to get the video duration?


Solution

  • Gyan, in his comment to my question, suggested this solution:

    Add -progress <FILE> to the command. See the last out_time_us value after encoding. Note that this is the last presentation timestamp so you'll still need to add the last frame duration - use inverse of framerate.

    I gave the -progress parameter a shot on a short clip that has an fps of 25.

    I added -progress progressfile.txt to my ffmpeg command. In progressfile.txt, the final out_time_ms line was out_time_ms=1621333, which is 1.621333 seconds, or at 25fps, that's 40.533325 frames, which is weird (how can video have just over half a frame?). Not good. Even if I add the time for the final frame (1/25 = 0.04 seconds), it's still a weird frame count of 41.533325 frames. It's also inaccurate as the video actually has 43 frames in it.

    The progress file did, however, have the line frame=43 which, at 25fps, is 1.72 seconds. And "1.720000" is exactly what ffprobe output as the video duration.

    So, there are two possible solutions I can see to getting an accurate video duration in seconds. I can either:

    1. Create the ffmpeg progress file using the -progress parameter, then read that progress file, parse for the final frame value, and multiply that value by the fps to get the video duration in seconds, or ...

    2. Run ffprobe with the parameters above on the newly created video file, then parse the output to get the video duration in seconds.

    I will be hitting the disk a couple of times either way. I will create the ffmpeg progress file (hit 1), open, read, and parse the progress file (hit 2), then if I want to be clean and tidy, delete the progress file (optional hit 3) -- or run ffprobe (hit 1), which will open and read the video file (hit 2) and give me the duration. Of note, the ffprobe and video file read disk hits might be more substantial than the hits for creating, reading, and deleting a small text file.

    I'll have to try some tests to determine which option works best for my use case.

    Thanks Gyan!