Getting realtime output from ffmpeg to be used in progress bar (PyQt4, stdout)
In this specific case for capturing ffmpeg's status output (which goes to STDERR), this SO question solved it for me: FFMPEG and Pythons subprocess
The trick is to add universal_newlines=True
to the subprocess.Popen()
call, because ffmpeg's output is in fact unbuffered but comes with newline-characters.
cmd = "ffmpeg -i in.mp4 -y out.avi"process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,universal_newlines=True)for line in process.stdout: print(line)
Also note that in this code sample the STDERR status output is directly redirected to subprocess.STDOUT
The only way I've found to get dynamic feedback/output from a child process is to use something like pexpect:
#! /usr/bin/pythonimport pexpectcmd = "foo.sh"thread = pexpect.spawn(cmd)print "started %s" % cmdcpl = thread.compile_pattern_list([pexpect.EOF, 'waited (\d+)'])while True: i = thread.expect_list(cpl, timeout=None) if i == 0: # EOF print "the sub process exited" break elif i == 1: waited_time = thread.match.group(1) print "the sub process waited %d seconds" % int(waited_time)thread.close()
the called sub process foo.sh just waits a random amount of time between 10 and 20 seconds, here's the code for it:
#! /bin/shn=5while [ $n -gt 0 ]; do ns=`date +%N` p=`expr $ns % 10 + 10` sleep $p echo waited $p n=`expr $n - 1`done
You'll want to use some regular expression that matches the output you're getting from ffmpeg and does some kind of calculation on it to show the progress bar, but this will at least get you the unbuffered output from ffmpeg.
- Calling from the shell is generally not required.
- I know from experince that part of the ffmpeg output comes on
stderr
, notstdout
.
If all you want to do is print the output line, like in your example above, then simply this will do:
import subprocesscmd = 'ffmpeg -i file.mp4 file.avi'args = cmd.split()p = subprocess.Popen(args)
Note that the line of ffmpeg chat is terminated with \r
, so it will overwrite in the same line! I think this means you can't iterate over the lines in p.stderr
, as you do with your rsync example. To build your own progress bar, then, you may need to handle the reading yourself, this should get you started:
p = subprocess.Popen(args, stderr=subprocess.PIPE)while True: chatter = p.stderr.read(1024) print("OUTPUT>>> " + chatter.rstrip())