6

How does one go about getting specific pieces of information, like the 'duration' from the output of ffmpeg -i /var/thismovie.avi ?

I need frame height, frame width, duration of movie and frame rate. All of these are in the out put of the above command but how do i get the bits i need individually and put them in a variable in PHP?

I've given up on trying to install ffmpeg-php which is what I used before this to do the same job.

Thanks

ffmpeg -i /var/thismovie.avi produce an output like this

ffmpeg version N-43171-ga763caf Copyright (c) 2000-2012 the FFmpeg developers built on Aug 3 2012 07:56:19 with gcc 4.1.2 (GCC) 20080704 (Red Hat 4.1.2-52) configuration: --enable-version3 --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libvpx --enable-libfaac --enable-libmp3lame --enable-libtheora --enable-libvorbis --enable-pic --enable-libx264 --enable-libxvid --disable-ffplay --enable-shared --enable-gpl --enable-postproc --enable-nonfree --enable-avfilter --enable-pthreads --extra-cflags=-fPIC libavutil 51. 66.100 / 51. 66.100 libavcodec 54. 48.100 / 54. 48.100 libavformat 54. 22.100 / 54. 22.100 libavdevice 54. 2.100 / 54. 2.100 libavfilter 3. 5.102 / 3. 5.102 libswscale 2. 1.100 / 2. 1.100 libswresample 0. 15.100 / 0. 15.100 libpostproc 52. 0.100 / 52. 0.100 [avi @ 0x18e8e240] non-interleaved AVI [avi @ 0x18e8e240] max_analyze_duration 5000000 reached at 5000000 Input #0, avi, from '/var/www/vhosts/mysite.com/httpdocs/movie.avi': Duration: 00:00:10.76, start: 0.000000, bitrate: 5180 kb/s Stream #0:0: Video: h264 (High) (H264 / 0x34363248), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 25 fps, 25 tbr, 25 tbn, 50 tbc Stream #0:1: Audio: ac3 ([0] [0][0] / 0x2000), 48000 Hz, stereo, s16, 128 kb/s At least one output file must be specified` 
Hawili
  • 1,601
  • 11
  • 15
Jizbo Jonez
  • 1,160
  • 8
  • 22
  • 39
  • 1
    ffmpeg-php sounds like exactly the right tool for the job. What you're trying to do sounds like a terrible workaround for not being able to install an extension. – Matt Ball Aug 04 '12 at 01:33
  • 1
    if you are able to execute command from php using [`exec`](http://www.php.net/manual/en/function.exec.php) you can capture the output of the command as php variable then parse it the way you like – Hawili Aug 04 '12 at 01:35
  • @Matt Ball on the contrary, it saves me having to install ffmpeg-php just to get 4 values from a movie. And another reason is that ffmpeg-php hasn't been developed since 2007 from what I gather. I already have (and need) ffmpeg installed so why not just make use of that to get the info I'm after? – Jizbo Jonez Aug 04 '12 at 02:08
  • @Hawili yes that's where I was heading, but how to parse the info exactly? – Jizbo Jonez Aug 04 '12 at 02:08
  • Add a sample output, I'll try to show you how – Hawili Aug 04 '12 at 02:12
  • Ok thanks Hawili, here's a sample - Duration: 00:00:10.76, start: 0.000000, bitrate: 5180 kb/s Stream #0:0: Video: h264 (High) (H264 / 0x34363248), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 25 fps, 25 tbr, 25 tbn, 50 tbc Stream #0:1: Audio: ac3 ([0] [0][0] / 0x2000), 48000 Hz, stereo, s16, 128 kb/s – Jizbo Jonez Aug 04 '12 at 02:16
  • @jizbojonez, please check the answer below – Hawili Aug 04 '12 at 03:18

4 Answers4

15

Based on ffprobe solution suggested by @blahdiblah and inspired by another question answer: https://github.com/paulofreitas/php-ffprobe

Note: requires ffmpeg 0.9+, supports ALL ffmpeg supported file types

Using the class

// Example 1
$info = new ffprobe($filename);
var_dump($info->format->format_long_name); // e.g. string(10) "AVI format"
var_dump($info->streams[0]->duration);     // e.g. string(11) "5674.674675"

// Example 2 (prettified)
$info = new ffprobe($filename, true);
var_dump($info->format->format_long_name); // e.g. string(10) "AVI format"
var_dump($info->streams[0]->duration);     // e.g. string(14) "1:34:34.674675"

Extending the class

class ffprobe_ext extends ffprobe
{
    public function __construct($filename)
    {
        parent::__construct($filename);
    }

    public function getVideoStream()
    {
        foreach ($this->streams as $stream) {
            if ($stream->codec_type == 'video') {
                return $stream;
            }
        }
    }

    public function getVideoInfo()
    {
        $stream = $this->getVideoStream();
        $info   = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS);
        $info->duration     = (float) $stream->duration;
        $info->frame_height = (int) $stream->height;
        $info->frame_width  = (int) $stream->width;
        eval("\$frame_rate = {$stream->r_frame_rate};");
        $info->frame_rate   = (float) $frame_rate;

        return $info;
    }
}

$ffprobe = new ffprobe_ext($filename);
$info = $ffprobe->getVideoInfo();
var_dump($info->duration); // e.g. float(5674.674675)

Welcome to further improvements! :-)

Community
  • 1
  • 1
Paulo Freitas
  • 11,380
  • 13
  • 68
  • 93
  • Thanks for the detailed answer Paulo. This looks like exactly what I need but the class is failing at this line for some reason. I've tried with both .avi and .mpeg which are standard filetypes surely. I am also using the latest version of ffmpeg through git : if (!isset($json->format)){throw new Exception('Unsupported file type');} – Jizbo Jonez Aug 11 '12 at 08:01
  • That's unexpected... Try to `var_dump($json);` just before that `if (...)` statement, to see what you're getting from `shell_exec()`. If your `$json` dumps `{}`, it's probably that something isn't working as expected. In that case, please, tell me what your terminal dumps for `ffprobe -loglevel quiet -show_format -show_streams -print_format json video_file.avi` command (edit your question with this update, comments have some limitations). Thanks! :) – Paulo Freitas Aug 11 '12 at 20:13
  • I forgot to add the path to ffprobe in the json_decode command. It works now, and it's brilliant. Thanks a lot. One last question, how can I get just the value of say var_dump($info->duration); so for example with 4.4 second video, return just 4.4 instead of returning float(4.4) – Jizbo Jonez Aug 12 '12 at 14:09
  • 1
    Just use `$info->duration` on your code (without `var_dump()` call, I've used it in the examples for demonstration purposes), e.g. if you want to show that value, just print it with `print $info->duration;` or even `printf('%0.2f', $info->duration);` if you need it formatted. :) – Paulo Freitas Aug 12 '12 at 15:08
  • yeah I just worked it out and was about to delete that last comment. Thanks again, much appreciated. – Jizbo Jonez Aug 12 '12 at 15:13
10

Use ffprobe instead.

ffprobe is the tool packaged with FFmpeg for exactly the sort've purpose you're after: extracting video info. It outputs a variety of easily parsed formats and will be far easier than parsing the incidental information that FFmpeg outputs.

For example:

$ ffprobe -show_format -loglevel quiet mptestsrc.mp4 
[FORMAT]
filename=mptestsrc.mp4
nb_streams=1
format_name=mov,mp4,m4a,3gp,3g2,mj2
format_long_name=QuickTime/MPEG-4/Motion JPEG 2000 format
start_time=0.000000
duration=12.040000
size=237687
bit_rate=157931
TAG:major_brand=isom
TAG:minor_version=512
TAG:compatible_brands=isomiso2avc1mp41
TAG:encoder=Lavf54.20.100
[/FORMAT]
blahdiblah
  • 30,909
  • 18
  • 92
  • 149
  • 1
    ok thats awesome... but, how do I get the SPECIFIC information I need. How do I go about extracting just the duration, frame height, frame width, frame rate in php? Right now it's almost the same output as what ffmpeg -i gives except it's on separate lines. – Jizbo Jonez Aug 04 '12 at 02:35
8

You can use ffprobe which has an option to output JSON format.

Take a look at this example:

ffprobe -v quiet -print_format json -show_format Ramp\ -\ Apathy.mp3

Which produces the follwing output:

{
    "format": {
        "filename": "Ramp - Apathy.mp3",
        "nb_streams": 2,
        "format_name": "mp3",
        "format_long_name": "MP2/3 (MPEG audio layer 2/3)",
        "start_time": "0.000000",
        "duration": "203.638856",
        "size": "4072777",
        "bit_rate": "159999",
        "tags": {
            "title": "Apathy",
            "artist": "Ramp",
            "album": "Evolution Devolution Revolution",
            "date": "1999",
            "genre": "Metal"
        }
    }
}

I'm using ffprobe version 1.0.7.

Paulo Fidalgo
  • 19,844
  • 7
  • 85
  • 108
  • 1
    Thanks for the info Paulo, but that isn't much different to the accepted answer above which also uses ffprobe, your method just has slightly cleaner looking output. – Jizbo Jonez May 30 '13 at 07:24
3
$output = `ffmpeg -i /var/thismovie.avi`; //note that back quote is like exec
preg_match('/Duration: (.*?),.*?Video:.*?0x.*?([0-9]+)x([[0-9]+).*?([0-9]+) fps/i'
    ,$output , $result);

output of $result array should be like this:

Array
(
    [0] => Duration: 00:00:10.76, start: 0.000000, bitrate: 5180 kb/s Stream #0:0: Video: h264 (High) (H264 / 0x34363248), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 25 fps
    [1] => 00:00:10.76
    [2] => 1280
    [3] => 720
    [4] => 25
)

UPDATE

The actual output is multi line, so please update the pattern into

/Duration: (.*?),.*?([0-9]+)x([0-9]+) \[.*?([0-9]+) fps/is

Hawili
  • 1,601
  • 11
  • 15
  • excellent thanks alot. A total noob question here but how do I get just one of the values? I tried the following but just get an error and I can't see what is wrong - $output = shell_exec($ffmpegPath . ' -i ' . $srcFile . ' 2>&1'); //note that back quote is like exec preg_match('/Duration: (.*?),.*?Video:.*?0x.*?([0-9]+)x([[0-9]+).*?([0-9]+) fps/i',$output , $result); print_r($result[0]); – Jizbo Jonez Aug 04 '12 at 03:49
  • what is the content of $output? just do `echo $output` to verify that the above command is giving results – Hawili Aug 04 '12 at 03:53
  • if you are sure there is output, just do `var_dump($result)`, if the array is like the one I posted you can access variables using `$result[1]` for duration, `$result[2]` for width and so on – Hawili Aug 04 '12 at 03:57
  • yeah it's not happening, var_dump($result) gives an empty array, var_dump($output) gives me the entire ffmpeg output which is too much info - $output = shell_exec($ffmpegPath . ' -i /var/www/vhosts/somesite.com/httpdocs/movie.avi 2>&1'); So $output is getting something but $result gets nothing. What do you even mean by '//note that back quote is like exec'... whats a back quote? – Jizbo Jonez Aug 04 '12 at 04:21
  • first of all, edit the main question, paste $output value there as code, seems there is something need to be changed in the regular expression. now regarding back quote ` you can use this instead of shell_exec, just check my original code, see I'm not using any function! – Hawili Aug 04 '12 at 04:26
  • does it make any difference whether i use the backquote method or just use shell_exec? – Jizbo Jonez Aug 04 '12 at 04:32
  • @jizbojonez no, its exactly the same, just a shortcut to write less code :) – Hawili Aug 04 '12 at 04:35
  • ok well it looks ugly in my editor, like it's an error. So I stuck with the shell_exec for now until I get this sorted. I edited the post with the output of $output – Jizbo Jonez Aug 04 '12 at 04:36
  • "just tried using the output you placed, it worked without modifying anything!" yes doing it that way does indeed work, but why wont it work when doing a shell_exec? Like this - $output = shell_exec($ffmpegPath . ' -i /var/www/vhosts/mysite.com/httpdocs/movie.avi 2>&1'); If I echo that $output it gives me the string I gave you, the one you say has no problem, but if that $output is passed to the preg_match it doesn't happen. This is just weird. – Jizbo Jonez Aug 04 '12 at 04:53
  • ok so this is the behavior. If $output is given the shell_exec command, and then I do a echo($output) I get a long string with the required info. If I take that info and assign it to $output as a string, and then try to echo($result) I now get an array with values as you already know. The problem is if make $output use the shell_exec again and then echo($result), I get nothing. – Jizbo Jonez Aug 04 '12 at 05:11
  • @JizboJonez change the pattern to `/Duration: (.*?),.*?([0-9]+)x([0-9]+) \[.*?([0-9]+) fps/is` – Hawili Aug 04 '12 at 05:24
  • guess what.... IT WORKS!!! yey Hawili you were right about the multi line problem. Thanks heaps for your help, you are the man. – Jizbo Jonez Aug 04 '12 at 05:37
  • Btw I only just noticed the message in my inbox where you asked me to discuss this problem in chat, sorry about that. Anyway all sorted now, ur a legend. – Jizbo Jonez Aug 04 '12 at 05:51
  • That won't work with any AVI file... I've tested a video encoded with mpeg4 and `ffmpeg` output doesn't have `fps` info. I'll provide a solution based on `ffprobe` suggested by @blahdiblah. – Paulo Freitas Aug 04 '12 at 08:17
  • ok thanks blahdiblah. For a quick fix I just changed the preg_replace 'fps' part to 'tbr' which is also some form of frame rate. It seems to work so far but maybe it won't with a certain video encode type. Anyway, looking forward to your solution. – Jizbo Jonez Aug 04 '12 at 08:48
  • I forgot to tell you that I've already posted my `ffprobe` solution below. You can even read TAGs with it if you ever need! Tested with AVI/WMV/MP4 videos. I'll improve checks and update my answer as soon as I've time do to it. – Paulo Freitas Aug 05 '12 at 00:50