FFmpeg get duration of video file without meta data

5,773

Solution 1

For a raw bitstream, ffprobe can be used.

ffprobe -show_entries stream=r_frame_rate,nb_read_frames -select_streams v -count_frames -of compact=p=0:nk=1 -v 0 in.m2v

This produces

30/1|120

where first entry is the frame rate as a rational number, and second is the number of frames counted. Duration is 120 / (30/1) = 4.00s

Solution 2

If you don't have metadata, then you need to build those metadata by reading and parsing the whole file (this is what ffprobe does), and get a correct answer based on the file's actual content.

There is no more accurate answer. There is, however, a faster answer. As noticed, "if you open such videos in VLC, it shows you the duration of the video immediately". In several places, VLC (and other tools too) appear to make an estimate, instead of reading in and parsing the whole file (which you might be unable or unwilling to do):

/* try to calculate movie time */
if( p_sys->p_fp->i_data_packets_count > 0 )
{
    uint64_t i_count;
    uint64_t i_size = stream_Size( p_demux->s );

    if( p_sys->i_data_end > 0 && i_size > p_sys->i_data_end )
    {
        i_size = p_sys->i_data_end;
    }

    /* real number of packets */
    i_count = ( i_size - p_sys->i_data_begin ) /
              p_sys->p_fp->i_min_data_packet_size;

    /* calculate the time duration in micro-s */
    p_sys->i_length = VLC_TICK_FROM_MSFTIME(p_sys->p_fp->i_play_duration) *
               (vlc_tick_t)i_count /
               (vlc_tick_t)p_sys->p_fp->i_data_packets_count;
    if( p_sys->i_length <= p_sys->p_fp->i_preroll )
        p_sys->i_length = 0;
    else
    {
        p_sys->i_length  -= p_sys->p_fp->i_preroll;
        p_sys->i_bitrate = 8 * i_size * CLOCK_FREQ / p_sys->i_length;
    }
}

In this case,

Stream #0:0: Video: mpeg2video (Main), yuv420p(tv), 720x576 [SAR 64:45 DAR 16:9], max. 7000 kb/s, 25 fps, 25 tbr, 1200k tbn, 50 tbc

knowing that the stream speed is 7000 kb/s and the file size, dividing the file size by 7000 kbit and multiplying by 8 immediately gives the likely duration in seconds.

For example with a file I have here (actual file duration 02:32:19, file size 733,802,496)

Stream #0:0: Video: ... 562 kb/s, 25 fps ...
Stream #0:1: Audio: ... 54 kb/s

562+54 is 616 kbits, which is 77 kbytes. 733,802,496 divided by (77*1024) is 9306, and 9306 seconds are 2 hours, 35 minutes, 6 seconds, which is not exactly correct, but pretty close.

Depending on the actual codec used, the audio/video interleaving method, any padding for the same, and whether it's CBR or VBR, the actual accuracy might vary.

Lacking reliable metadata, if you value speed over accuracy, then I fear that estimate is the way to go.

In some scenarios you might be able to do both (provide an immediate estimate based on file header and size, then start reading whenever the UI/program has nothing better to do and refine the answer. You might then even save the newly calculated metadata somewhere, to retrieve them if needed later on - in a database, a thumbnail file, an alternate data stream, or even offer to update/"repair" the video file if feasible).

Command-line

From the command line, using ffmpeg:

 #!/bin/bash

 if [ ! -r "$1" ]; then
     echo "File '$1' not found"
     exit 1
 fi
 FILESIZE=$( stat -c "%s" "$1" )
 STREAMS=$( 
      ffmpeg -i "$1" 2>&1 \
      | grep 'Stream #' \
      | tr "," "\n" \
      | grep "kb/s" \
      | tr " " "\n" \
      | grep "^[1-9][0-9]*$" )
 RATE=0
 for r in $STREAMS; do
     RATE=$[ $RATE + $r ]
 done
 # I don't think that bash has decimal support, so we use bc
 SECONDS=$( echo "$FILESIZE / $RATE" | bc )
 # To get seconds in HH:MM:SS format we use 'date'
 DURATION=$( TZ=UTC date +"%H:%M:%S" -d @$SECONDS )

This should be accurate even if you have multiple audio streams in a video file. I am not sure what happens if there is some data stream such as subtitles. The size of such a stream shouldn't be much, so it shouldn't interfere, but then again, it might.

Share:
5,773

Related videos on Youtube

utdev
Author by

utdev

Name: Umut Tufanoglu Currently working at: adesso.

Updated on September 18, 2022

Comments

  • utdev
    utdev over 1 year

    I got a video file which has no meta data.

    If I do this for example:

    ffmpeg -i test.m2v
    

    I get these values:

    Duration: N/A, bitrate: N/A
    

    Is there still a way to get the duration of the video / .m2v file ?

    Edit:

    The full console output:

    ffmpeg version 2.8.4 Copyright (c) 2000-2015 the FFmpeg developers
      built with gcc 5.2.0 (GCC)
      configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-avisynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libdcadec --enable-libfr
    eetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libsoxr --enable-libspeex --enab
    le-libtheora --enable-libtwolame --enable-libvidstab --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs --enable-libxvid --enable-lzma --enable-decklink
    --enable-zlib
      libavutil      54. 31.100 / 54. 31.100
      libavcodec     56. 60.100 / 56. 60.100
      libavformat    56. 40.101 / 56. 40.101
      libavdevice    56.  4.100 / 56.  4.100
      libavfilter     5. 40.101 /  5. 40.101
      libswscale      3.  1.101 /  3.  1.101
      libswresample   1.  2.101 /  1.  2.101
      libpostproc    53.  3.100 / 53.  3.100
    Input #0, mpegvideo, from '.\Test.m2v':
      Duration: N/A, bitrate: N/A
        Stream #0:0: Video: mpeg2video (Main), yuv420p(tv), 720x576 [SAR 64:45 DAR 16:9], max. 7000 kb/s, 25 fps, 25 tbr, 1200k tbn, 50 tbc
    
    • Admin
      Admin over 7 years
      Can you show the entire console output, please?
    • Admin
      Admin over 7 years
      @slhck ok I put the whole console output in the question edit
    • Admin
      Admin over 7 years
      That one does have the duration. But, in general, if it's a raw bitstream, ffprobe with count_frames will work.
    • Admin
      Admin over 7 years
      Whops I posted a wrong output, I tested it with another file and that worked, I'll post the output from the original file. Thus how would the command look with ffprobe?
  • utdev
    utdev about 7 years
    does the "in" belongs to the command or is that just a random filename`?
  • utdev
    utdev about 7 years
    Ok I tried this on a 25sec video and I got 27,48 after the calculation. It is not accurate, is it really accurate if you use it?
  • Gyan
    Gyan about 7 years
    Works here .Can you upload the errant file?
  • utdev
    utdev about 7 years
    where do you want me to upload it?
  • Gyan
    Gyan about 7 years
    datafilehost, dropbox. drive...
  • utdev
    utdev about 7 years
  • Gyan
    Gyan about 7 years
    Is accurate. There are 687 frames, all of which I extracted as a image sequence. That's 27.48s. Why do you think it's exactly 25 seconds?
  • Gyan
    Gyan about 7 years
    Use ffmpeg -i in.m2v -vsync 0 img-%d.png to check
  • utdev
    utdev about 7 years
    Did you also try this with a length of ~10minutes, there is a higher gap when I tried it with a video which has a length of ~10 minutes.
  • Gyan
    Gyan about 7 years
    No. But there's no gap, as duration is accurate in terms of # of frames stored and fps. How did you get 25s as duration of your clip?
  • utdev
    utdev about 7 years
    if I put the video in a video player, in my case power dvd it shows 25s
  • Gyan
    Gyan about 7 years
    Try another player, or time it with an app/stopwatch..etc
  • utdev
    utdev about 7 years
    The time of the player is correct, any other idea
  • shivams
    shivams almost 3 years
    This is brilliant. Can you post some commands to do this? Does VLC have some CLI commands for this?
  • LSerni
    LSerni almost 3 years
    Yes, no problem. But give me some hours :-) . I can do that for Linux shell, I'm not so very proficient with Windows PowerShell