158

How can I get video duration in seconds?

What I've tried:

ffmpeg -i file.flv 2>&1 | grep "Duration"
  Duration: 00:39:43.08, start: 0.040000, bitrate: 386 kb/s


mediainfo file.flv | grep Duration
Duration : 39mn 43s

This what close, but it's not so accurate, 2383 is 39.71 minutes

ffmpeg -i file.flv 2>&1 | grep "Duration"| cut -d ' ' -f 4 | sed s/,// | sed 's@\..*@@g' | awk '{ split($1, A, ":"); split(A[3], B, "."); print 3600*A[1] + 60*A[2] + B[1] }'
2383
phuclv
  • 30,396
  • 15
  • 136
  • 260
user2783132
  • 1,967

13 Answers13

268

Just use ffprobe directly. No need for sed, grep, etc. There are several "durations" you can acquire (depending on your input).

Format (container) duration

ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4

Result:

30.024000

Adding the -sexagesimal option will use the HOURS:MM:SS.MICROSECONDS time unit format:

0:00:30.024000

Video stream duration

If you want the duration of a particular video or audio stream:

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

Result:

30.000000

The above commands are from FFmpeg Wiki: FFprobe Tips.

With ffmpeg

You can use ffmpeg to get duration by decoding the input:

ffmpeg -i input.mp4 -f null -
…
frame= 1587 fps=0.0 q=0.0 Lsize=N/A time=00:01:03.48 bitrate=N/A

In this example time=00:01:03.48 is the duration.

This may take a long time depending on your input file.

llogan
  • 63,280
81

To get minutes, you have to divide 2383 seconds by 60.

39.7167

and then multiply the fractional part .7167 by 60 to get the remaining seconds.

43.002

So it's 39 minutes, 43 seconds. The application appears to be giving you an accurate value.

42

If you have ffmpeg, you should also have ffprobe:

ffprobe -i input.file -show_format | grep duration
ffprobe -i input.file -show_format -v quiet | sed -n 's/duration=//p'

This will also give fractions of seconds, if that's a problem you can further process that away with sed.

evilsoup
  • 14,056
10
mediainfo --Output='General;%Duration%' file.flv

This outputs the duration in milliseconds as a single integer value. No need for grep/cut/sed/...

3

Solution with mplayer that gives seconds directly:

mplayer -identify -frames 0 -vo null -nosound file.flv 2>&1 | awk -F= '/LENGTH/{print $2}'
Giacomo1968
  • 58,727
micke
  • 3,485
  • 19
  • 29
2

Duration information can appear on several place, for example, mp4 files may have duration under stream sections, but mkv files may not. So the purpose is to print duration information from as many places as possible:

ffprobe.exe  -show_entries format=duration:stream_tags=duration:stream=duration -v quiet -print_format json input_file.mp4

-show_entries is a flag with very weird syntax, the meaning of the above is that to print:

  • duration under format section
  • duration under tags under stream sections
  • duration under stream sections.

-print_format json is optional. JSON is more readable then the default format.

You can also use -sections to see what are the sections in the file and add more to the above to make it more complete.

jw_
  • 902
2

For my ffmpeg-0.6.5-1.el6.rf.x86_64, to get just the second, ffprobe command format is:

ffprobe <file> -show_format 2>&1 | sed -n 's/duration=//p' 
Sunry
  • 147
1

I came across the issue of getting some strange and incorrect metadata from some video files I was working with and I couldn't succeed on finding a pattern or any type of handling using code and tools like ffmpeg, mp4box, ffprobe, mediainfo, mplayer, to get the real duration of the video.

Identifying the real duration of the video was a requirement for a project I was working and the only way I found to get it always right was to reencode the video file using ffmpeg and forcing encoded files to ignore original file's metadata, like:

ffmpeg -i INPUT_FILENAME -acodec copy -vcodec copy -map_metadata -1 OUTPUT_FILENAME"

(This might run faster than you expect. I got surprised, for the type of content and on the environment I was using, average time was 2 seconds)

... and then get duration using a tool at your choice. I like mediainfo, btw: - "mediainfo FILE --Inform="Video;%Duration%" gives you duration in miliseconds.

Giacomo1968
  • 58,727
satirio
  • 11
1

Python

You can extract streams using following code

import json

infile = "<path to your file>" command = "ffprobe -v quiet -print_format json -show_format -show_streams {}".format(infile) res = json.loads(subprocess.check_output(command, shell=True)) streams = res.get('streams')

Each stream would have a field duration.

Special case of Webm

If your input file is webm, Your method of extracting stream duration is going to be different, since the standard store it in a different location. In each stream you would see a field 'tags'

"tags": {
                "language": "eng",
                "HANDLER_NAME": "ISO Media file produced by Google Inc. Created on: 12/09/2019.",
                "VENDOR_ID": "[0][0][0][0]",
                "ENCODER": "Lavc59.4.101 libvorbis",
                "DURATION": "00:03:14.703000000"
            }

You would have to convert this human readable time format into seconds. Which you can easily do with following utility function.

def convert_secs(text):
    if isinstance(text, float):
        num = str(text)
        nums = num.split('.')
    else:
        nums = text.split(':')
    if len(nums) == 2:
        st_sn = int(nums[0]) * 60 + float(nums[1])
        return st_sn
    elif len(nums) == 3:
        st_sn = int(nums[0]) * 3600 + int(nums[1]) * 60 + float(nums[2])
        return st_sn
    else:
        raise ValueError("Not correct time")
rusty
  • 111
1
# Returns duration (in seconds) of a video $1 (uses ffmpeg).
get_video_duration() {
  OUTPUT=$(ffmpeg -i "$1" -vframes 1 -f rawvideo -y /dev/null 2>&1) ||
    { debug -e "get_video_duration: error running ffmpeg:\n$OUTPUT"; return 1; }
  DURATION=$(echo "$OUTPUT" | grep -m1 "^[[:space:]]*Duration:" |
    cut -d":" -f2- | cut -d"," -f1 | sed "s/[:\.]/ /g") || 
    { debug -e "get_video_duration: error parsing duration:\n$OUTPUT"; return 1; }
  read HOURS MINUTES SECONDS DECISECONDS <<< "$DURATION"
  echo $((10#$HOURS * 3600 + 10#$MINUTES * 60 + 10#$SECONDS))      
}

Usage:DURATION=$(get_video_duration "$VIDEO")

0

If you only need to query metadata:

ffprobe -hide_banner -v quiet -show_streams -print_format flat video.mp4

[...]

streams.stream.0.duration="5221.146009"

[...]

So you can parse it:

while read -r; do
  if [[ "$REPLY" =~ ^streams\.stream\.([0-9])+\.duration=\"([^"]+)\"$ ]]; then
    echo -E Duration of stream "${BASH_REMATCH[1]}": "${BASH_REMATCH[2]}"
  fi
done < <(ffprobe -hide_banner -v quiet -show_streams -print_format flat video.mp4)

But if you want to get the effective container's duration, you need to decode it:

AV_LOG_FORCE_NOCOLOR=y ffmpeg -nostdin -hide_banner -nostats -loglevel info -i video.mp4 -f null -c copy - 2>&1 | tail -n 2

It will take some CPU time to decode it, until:

[...]

frame=130527 fps=53271 q=-1.0 Lsize=N/A time=01:27:01.12 bitrate=N/A speed=2.13e+03x
video:152014kB audio:40790kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown

Then, you can parse it:

if [[ "$(AV_LOG_FORCE_NOCOLOR=y ffmpeg -nostdin -hide_banner -nostats -loglevel info -i video.mp4 -f null -c copy - 2>&1 | tail -n 2 | head -n 1)" =~ \ time=([0-9]+):([0-9]{2}):([0-9]{2})\.([0-9]+) ]]; then
  declare duration=0 us="${BASH_REMATCH[4]}" t
  for t in "${BASH_REMATCH[@]:1:3}"; do
    ((duration *= 60))
    ((duration += ${t#0} ))
  done
  while [ ${#us} -lt 6 ]; do us+=0; done
  ((us >= 500000)) && ((duration++))
  ((duration)) || ((duration++))
fi
echo -E Duration: "$duration"
0

I was a bit surprised to find that the simplest way of doing this isn't here, and that the closest thing doesn't give integers, but adds incorrect decimal points that need to be rounded off.

It's quite easy: just use your languages modulus operator.

In php:

$vidLength = 2383; //seconds
$secondsPerMinute = 60;

$seconds = $vidLength % $secondsPerMinute; //$seconds = 43

$minutes = ($vidLength - $seconds) / $secondsPerMinute; //$minutes = 39

No rounding errors or weird fractional parts. Just simple, plain math using one of the 6 basic arithmetic operators just like + or - that can be found in any programming language.

Also, the actual perceived error is that you've done whole minutes and then have divided the seconds by 60. 43/60 = 0,7167, effectively turning it from actual seconds to hundredths of a minute.

0

In Windows the file metadata can be accessed directly using Shell COM object's Folder.GetDetailsOf() method without installing any tools. That's how Windows Explorer shows the file details as you've always seen

That means you can use any languages that support COM objects like PowerShell, VBS, Jscript... to get the video duration. Here's an example in PowerShell:

You can use the following PowerShell script

$path = $Args[0]
$columnNumber = 27 # Magic number, may be different, see below
$shell = New-Object -COMObject Shell.Application
$folder = Split-Path $path
$file = Split-Path $path -Leaf
$shellfolder = $shell.Namespace($folder)
$shellfile = $shellfolder.ParseName($file)

$duration = $shellfolder.GetDetailsOf($shellfile, 27) # Duration in hh:mm:ss format [timespan]::Parse($duration).TotalSeconds

Save it as a *.ps1 file, e.g. duration.ps1, then call it like this

duration.ps1 path\to\video\file

This works for any types that has duration that Windows can read. You can find more details in

Of course because it uses Shell.Application, you can use any WScript languages to achieve the same purpose. For example here's a hybrid batch/Jscript version, just save it as a normal *.bat file and pass the video file path to it:

@if (@CodeSection == @Batch) @then

@echo off cscript //e:jscript //nologo "%~f0" %1 exit /b

@end

// JScript Section

var filepath = WScript.Arguments.Item(0); var slash = filepath.lastIndexOf('\'); var folder = filepath.substring(0, slash); var file = filepath.substring(slash + 1);

var shell = new ActiveXObject("Shell.Application"); var shellFolder = shell.NameSpace(folder); shellfile = shellFolder.ParseName(file); var duration = shellFolder.GetDetailsOf(shellfile, 27); WScript.Echo(duration);

You'll also need to parse the time like how I used [timespan] above, because the output is in hh:mm:ss form instead of only seconds, but that's trivial.


Note that the column number 27 above might be changed in future versions of Windows. If it's changed you can find the new magic number by running this in PowerShell:

$objFolder = (New-Object -ComObject Shell.Application).Namespace('c:\')
$(for ($columnNumber = 0; $columnNumber -lt 500; ++$columnNumber) 
{ 
    $columnName = $objFolder.GetDetailsOf($objFolder.Items, $columnNumber) 
    if ($columnName)
    {
        [PsCustomObject]@{
             MagicNum = ([string]$columnNumber).PadLeft(3);
             ColumnName = $columnName
        }
    }
}) | where { $_.ColumnName -like "*duration*" -or $_.ColumnName -like "*len*" }

Sample output on my PC:

MagicNum ColumnName
-------- ----------
 27      Length
 43      Duration
165      Filename
262      Focal length
263      35mm focal length
265      Lens maker
266      Lens model
phuclv
  • 30,396
  • 15
  • 136
  • 260