Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can ffmpeg show a progress bar?

I am converting a .avi file to .flv file using ffmpeg. As it takes a long time to convert a file I would like to display a progress bar. Can someone please guide me on how to go about the same.

I know that ffmpeg somehow has to output the progress in a text file and I have to read it using ajax calls. But how do I get ffmpeg to output the progress to the text file?

like image 509
Pawan Rao Avatar asked Apr 14 '09 14:04

Pawan Rao


People also ask

What ffmpeg can do?

ffmpeg is a command-line tool that converts audio or video formats. It can also capture and encode in real-time from various hardware and software sources such as a TV capture card. ffplay is a simple media player utilizing SDL and the FFmpeg libraries.

What is Ffmpeg option?

ffmpeg is a very fast video and audio converter that can also grab from a live audio/video source. It can also convert between arbitrary sample rates and resize video on the fly with a high quality polyphase filter.


2 Answers

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.

like image 98
Mike Kellogg Avatar answered Oct 10 '22 00:10

Mike Kellogg


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-- 
like image 29
baltazar Avatar answered Oct 10 '22 00:10

baltazar