Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Querying an audio/video file for information

I want a PHP function which receives the path to a file and returns an array of information about it. The PHP file can call FFmpeg.

Returned data should be something like

Array(
    [mime]          => video/ogg
    [container]     => Ogg
    [video]         => Theora
    [audio]         => Vorbis
    [duration]      => 20.3 // in seconds
)

Array(
    [mime]          => audio/ogg
    [container]     => Ogg
    [video]         =>
    [audio]         => FLAC
    [duration]      => 3
)

Array(
    [mime]          => image/gif
    [container]     => GIF
    [video]         => Animated GIF
    [audio]         =>
    [duration]      => 2
)

Array(
    [mime]          => video/webm
    [container]     => WebM
    [video]         => VP8
    [audio]         => Vorbis
    [duration]      => 900.7
)

false // not a media file

I've never worked with FFmpeg or with PHP's shell_exec() function, but it seems that FFmpeg will give information about videos (or audio files) in a fairly hard-to-parse format. I assume that something like this is possible, though.

like image 819
TRiG Avatar asked Oct 14 '10 19:10

TRiG


1 Answers

FFmpeg can do just about everything you want, but you're correct that it's in a nearly impossible to parse format. It's actually quicker if you're on a UNIX-based system to use the Operating System and the features of FFmpeg to do the parsing rather than have PHP try to understand the output. I developed the following shell command several months ago for a similar purpose.

fulltime=`ffmpeg -i MOVIE.AVI 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//`;hour=`echo $fulltime | cut -d ':' -f 1`;minute=`echo $fulltime | cut -d ':' -f 2`;second=`echo $fulltime | cut -d ':' -f 3 | cut -d '.' -f 1`;echo `expr 3600 \* $hour + 60 \* $minute + $second`

The pipe (|) tells UNIX to pass the output of the previous command as the input to the next command. The semi-colon (;) tells UNIX to begin a new command. The UNIX shell also allows for on-the-fly variable creation. To create a variable you just say var=value. To recall this variable you must use a dollar-sign, such as $var.

My command essentially takes the output of ffmpeg -i MOVIE.AVI 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,// and stores it as fulltime. This command is further broken down:

ffmpeg -i MOVIE.AVI 2>&1 means to display info (-i) for MOVIE.AVI. The 2>&1 I believe redirected the output to a pipe rather than to the screen (made this forever ago, so I'm not clear on that)

grep 'Duration' will take the input (which was the output of ffmpeg -i MOVIE.AVI 2>&1), find the word 'Duration', and output only the line that contained that word.

cut -d ' ' -f 4 will take the input (the output of the previous statement), split it up at every space (' '), and output only the fourth of these. This is because the output of FFmpeg looks something like:

MOVIE.AVI
video encoder, yada yada
audio encoder, yada yada
bitrates and stuff
Duration: hh:mm:ss fulltime: 00:30:00.1

In other words, the fourth "section" of the line containing "Duration" is the actual duration... In hours, minutes, and seconds (with 1 decimal place).

sed s/,// means to replace ',' with nothing. There was probably a good reason for this.

After this I created the three variables hour, minute, and second:

hour=`echo $fulltime | cut -d ':' -f 1`;
minute=`echo $fulltime | cut -d ':' -f 2`;
second=`echo $fulltime | cut -d ':' -f 3 | cut -d '.' -f 1`;

Notice that I'm setting hour to the output of echo $fulltime | cut -d ':' -f 1. Grabbing the first portion if I were to split the time at every colon (:)

I do similar with minutes, then I do the same with seconds only I chop off any decimal at the end. This decimal has to be removed if you're working in shell since the expr command (which I used to calculate seconds from hours, minutes, and seconds) only performs integer math. If you wish to keep the decimal for duration, you will at this point have to return the hours, minutes, and seconds to PHP and do the parsing there. This would be most easily done by replacing the above with:

echo $fulltime

This will return a string such as "00:30:00.1" to PHP after you have called shell_exec(), which you can then easily parse using explode(':',$string).

To continue with getting the seconds directly from shell:

After getting the hours, minutes, and seconds came the magical line:

echo `expr 3600 \* $hour + 60 \* $minute + $second`

The expr command says to perform a mathematical expression. Unfortunately it's rather stupid. First, special characters need to be escaped (* is a wildcard in UNIX, so you have to use \* to mean multiply). Next, it only performs integer math. It was fairly easy, as you can see, to multiply hours by 3600, minutes by 60, and add them all together.

The output of this entire, massive command, will just be a single number. The number of seconds that the video lasts. It should work for all video formats.

Mind you this is just duration. As for video encoder and audio encoder you would want to use similar methods, minus the math at the end. A generic solution would be:

ffmpeg -i MOVIE.AVI 2>&1 | grep 'Something unique to the line you want' | cut -d ' ' -f <the "section" you want>

The output of this can either continue to be parsed in shell (as I did above with the time) or it can be passed to PHP.

like image 162
stevendesu Avatar answered Oct 05 '22 10:10

stevendesu