Can ffmpeg show a progress bar?

Solution 1:

I've been playing around with this for a few days. That "ffmpegprogress" thing helped, but it was very hard to get to work with my set up, and hard to read the code.

In order to show the progress of ffmpeg you need to do the following:

  1. run the ffmpeg command from php without it waiting for a response (for me, this was the hardest part)
  2. tell ffmpeg to send it's output to a file
  3. from the front end (AJAX, Flash, whatever) hit either that file directly or a php file that can pull out the progress from ffmpeg's output.

Here's how I solved each part:

1. I got the following idea from "ffmpegprogress". This is what he did: one PHP file calls another through an http socket. The 2nd one actually runs the "exec" and the first file just hangs up on it. For me his implementation was too complex. He was using "fsockopen". I like CURL. So here's what I did:

$url = "http://".$_SERVER["HTTP_HOST"]."/path/to/exec/exec.php";
curl_setopt($curlH, CURLOPT_URL, $url);
$postData = "&cmd=".urlencode($cmd);
$postData .= "&outFile=".urlencode("path/to/output.txt");
curl_setopt($curlH, CURLOPT_POST, TRUE);
curl_setopt($curlH, CURLOPT_POSTFIELDS, $postData);

curl_setopt($curlH, CURLOPT_RETURNTRANSFER, TRUE);

// # this is the key!
curl_setopt($curlH, CURLOPT_TIMEOUT, 1);
$result = curl_exec($curlH);

Setting CURLOPT_TIMEOUT to 1 means it will wait 1 second for a response. Preferably that would be lower. There is also the CURLOPT_TIMEOUT_MS which takes milliseconds, but it didn't work for me.

After 1 second, CURL hangs up, but the exec command still runs. Part 1 solved.

BTW - A few people were suggesting using the "nohup" command for this. But that didn't seem to work for me.

*ALSO! Having a php file on your server that can execute code directly on the command line is an obvious security risk. You should have a password, or encode the post data in some way.

2. The "exec.php" script above must also tell ffmpeg to output to a file. Here's code for that:

exec("ffmpeg -i path/to/input.mov path/to/output.flv 1> path/to/output.txt 2>&1");

Note the "1> path/to/output.txt 2>&1". I'm no command line expert, but from what I can tell this line says "send normal output to this file, AND send errors to the same place". Check out this url for more info: http://tldp.org/LDP/abs/html/io-redirection.html

3. From the front end call a php script giving it the location of the output.txt file. That php file will then pull out the progress from the text file. Here's how I did that:

// # get duration of source
preg_match("/Duration: (.*?), start:/", $content, $matches);

$rawDuration = $matches[1];

// # rawDuration is in 00:00:00.00 format. This converts it to seconds.
$ar = array_reverse(explode(":", $rawDuration));
$duration = floatval($ar[0]);
if (!empty($ar[1])) $duration += intval($ar[1]) * 60;
if (!empty($ar[2])) $duration += intval($ar[2]) * 60 * 60;


// # get the current time
preg_match_all("/time=(.*?) bitrate/", $content, $matches); 

$last = array_pop($matches);
// # this is needed if there is more than one match
if (is_array($last)) {
    $last = array_pop($last);
}

$curTime = floatval($last);


// # finally, progress is easy
$progress = $curTime/$duration;

Hope this helps someone.

Solution 2:

There is an article in Russian which describes how to solve your problem.

The point is to catch Duration value before encoding and to catch time=... values during encoding.

--skipped--
Duration: 00:00:24.9, start: 0.000000, bitrate: 331 kb/s
--skipped--
frame=   41 q=7.0 size=     116kB time=1.6 bitrate= 579.7kbits/s
frame=   78 q=12.0 size=     189kB time=3.1 bitrate= 497.2kbits/s
frame=  115 q=13.0 size=     254kB time=4.6 bitrate= 452.3kbits/s
--skipped--