6

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
4
  • Can you show the entire console output, please?
    – slhck
    Commented Feb 15, 2017 at 9:55
  • @slhck ok I put the whole console output in the question edit
    – utdev
    Commented Feb 15, 2017 at 10:07
  • That one does have the duration. But, in general, if it's a raw bitstream, ffprobe with count_frames will work.
    – Gyan
    Commented Feb 15, 2017 at 10:54
  • 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
    Commented Feb 15, 2017 at 11:02

2 Answers 2

5

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

13
  • does the "in" belongs to the command or is that just a random filename`?
    – utdev
    Commented Feb 16, 2017 at 14:30
  • 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?
    – utdev
    Commented Feb 16, 2017 at 14:40
  • Works here .Can you upload the errant file?
    – Gyan
    Commented Feb 17, 2017 at 13:31
  • where do you want me to upload it?
    – utdev
    Commented Feb 17, 2017 at 13:47
  • datafilehost, dropbox. drive...
    – Gyan
    Commented Feb 17, 2017 at 14:00
1
+100

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.

2
  • This is brilliant. Can you post some commands to do this? Does VLC have some CLI commands for this?
    – shivams
    Commented Jun 3, 2021 at 7:59
  • 1
    Yes, no problem. But give me some hours :-) . I can do that for Linux shell, I'm not so very proficient with Windows PowerShell
    – LSerni
    Commented Jun 3, 2021 at 8:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .