Can ffmpeg show a progress bar?

92,264

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--

Solution 3

It’s very simple if you use the pipeview command. To do this, transform

ffmpeg -i input.avi {arguments}

to

pv input.avi | ffmpeg -i pipe:0 -v warning {arguments}

No need to get into coding!

Solution 4

FFmpeg uses stdout for outputing media data and stderr for logging/progress information. You just have to redirect stderr to a file or to stdin of a process able to handle it.

With a unix shell this is something like:

ffmpeg {ffmpeg arguments} 2> logFile

or

ffmpeg {ffmpeg arguments} 2| processFFmpegLog

Anyway, you have to run ffmpeg as a separate thread or process.

Solution 5

You can do it with ffmpeg's -progress argument and nc

WATCHER_PORT=9998

DURATION= $(ffprobe -select_streams v:0 -show_entries "stream=duration" \
    -of compact $INPUT_FILE | sed 's!.*=\(.*\)!\1!g')

nc -l $WATCHER_PORT | while read; do
    sed -n 's/out_time=\(.*\)/\1 of $DURATION/p')
done &

ffmpeg -y -i $INPUT_FILE -progress localhost:$WATCHER_PORT $OUTPUT_ARGS
Share:
92,264
toorroot
Author by

toorroot

Updated on July 05, 2022

Comments

  • toorroot
    toorroot almost 2 years

    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?

  • Riduidel
    Riduidel over 13 years
    Were I to ask the question, I would gladly accept your answer.
  • Scarface
    Scarface about 13 years
    But why use a text file? there has got to be a better way. Wouldn't you have to make a new text file for every upload?
  • Shimmy Weitzhandler
    Shimmy Weitzhandler almost 13 years
    @mouviciel, do you know how I can achieve this in .NET? I tried what you said and it doesn't print out a file, also How can I get notified for when the file is changed?
  • thorne51
    thorne51 over 10 years
    I know this is an old thread but I'd like to add something I discovered today. It seems along with ffmpeg comes ffprobe which can be used to get info in std output ("ffmpeg -i {file}" actually exits with an error code so you'll have to pipe output using 2>1 to get the info). so, have at it: ffprobe -v quiet -print_format json -show_format -i file.mp4 -show_streams
  • KRA
    KRA about 10 years
    You above code is giving below error. Please reply if possible. Parse error: syntax error, unexpected T_VAR
  • Basti
    Basti almost 9 years
    I like this answer. It's very elegant. A relevant argument for pv is -n to only get the numeric progress in percent.
  • Basti
    Basti almost 9 years
    Beware that ffmpeg will fail to encode from stdin for video that are not streamable like a *.mov file where the meta data is at the end. The reason is that ffmpeg cannot seek to the meta data on the pipe. You'd have to qt-faststart every inputfile before you can pipe it into ffmpeg.
  • Basti
    Basti almost 9 years
    This looks like it leads to the best solution. You can pipe the progress information via ffmpeg -v warning -progress /dev/stdout -i in.mp4 out.mp4 to stdout in order to get progress updates once per second. If you only need a percentage you'd ffprobe -v quiet -print_format json -show_format in.mp4 for the duration in seconds before starting the encoding and then | awk -F"=" '/out_time_ms/{print $2}' to only get the current duration in ms where progress = total_duration * 100 * 1000 / current_duration.
  • iluvcapra
    iluvcapra almost 9 years
    I didn't know you could route progress to a file; if that's the case you could just make a fifo out of sed and be on your way.
  • Basti
    Basti almost 9 years
    It might also be a good idea to create a named pipe and call ffmpeg and sed | grep | awk | whatever separately to get ffmpeg's return code. Furthermore I send ffmpeg's stdout to a logfile so that in case of an error (code > 0) you have something to look at. My wip command currently looks something like ffmpeg -v warning -progress /dev/stdout -y -i $source $encodingOptions $target >>$logFile | grep --line-buffered out_time_ms.
  • Camilo Martin
    Camilo Martin over 8 years
    I went to the article expecting to struggle a little with the Russian but it was surprisingly easy to read.
  • Darrel Holt
    Darrel Holt about 7 years
    Thank you for specifying the output with relation to stdout and stderr
  • LiveWireBT
    LiveWireBT about 6 years
    I thought of using pv first, then after realizing that my ffmpeg doesn't write any useful vstats because of a particular video filter being used I looked closer at pv and found that it does exactly what I want. I'm just using times in the format string and additional text pv -F "some additional info here %t %e" "${video}", that's all I needed.
  • Salem
    Salem about 5 years
    I think the command on the article is like this ffmpeg -y -i input.avi -vcodec xvid -acodec mp3 -ab 96 output.avi > output.txt
  • Jorge Alonso
    Jorge Alonso over 4 years
    @sebilasse correct your code because it brings errors
  • glen
    glen over 4 years
    2020 update, the longer considered active. last commit to master in Nov 2018, 9 open issues, 3 open pull requests.
  • Arthor
    Arthor about 4 years
    Hi, I have been trying to add the above code to mine, however, no success. Andy advice please for /R %%f IN (*.mkv) DO ffmpeg -v warning -hide_banner -stats -i "%%f" -map 0 -c copy "%%~nf.mp4"
  • 0xB00B
    0xB00B over 3 years
    Also now ffmpeg has its own progress bar
  • Thomas Nyberg
    Thomas Nyberg over 3 years
    @TanishqBanyal what is the correct flag to pass for ffmpeg's progress bar? I can't find the option in the documentation/internet?
  • Benji
    Benji over 2 years
    Not a progress bar, but you can pipe -progress - -y before the output. e.g. ffmpeg -i ..... -progress - -y output.mp4