When you use subprocess, your command must either be a string that looks exactly like what you would type on the command line (and you set shell=True), or a list where each command is an item in the list (and you take the default shell=False). In either case, you have to deal with the variable part of the string. For instance, the operating system has no idea what "%03d" is, you have to fill it in.
I can't tell from your question exactly what the parameters are, but lets assume you want to convert frame 3, it would look something like this in a string:
my_frame = 3
subprocess.call(
'ffmpeg -r 10 -i frame%03d.png -r ntsc movie%03d.mpg' % (my_frame, my_frame),
shell=True)
Its kinda subtle in this example, but that's risky. Suppose these things were in a directory whose name name had spaces (e.g., ./My Movies/Scary Movie). The shell would be confused by those spaces.
So, you can put it into a list and avoid the problem
my_frame = 3
subprocess.call([
'ffmpeg',
'-r', '10',
'-i', 'frame%03d.png' % my_frame,
'-r', 'ntsc',
'movie%03d.mpg' % my_frame,
])
More typing, but safer.
Answer from tdelaney on Stack Overflowpython - subprocess call ffmpeg (command line) - Stack Overflow
Help me understand subprocess (vs OS, application is FFmpeg/FFprobe)
Multiple Ffmpeg stream into a single subprocess pipeline
python - Getting realtime output from ffmpeg to be used in progress bar (PyQt4, stdout) - Stack Overflow
Videos
When you use subprocess, your command must either be a string that looks exactly like what you would type on the command line (and you set shell=True), or a list where each command is an item in the list (and you take the default shell=False). In either case, you have to deal with the variable part of the string. For instance, the operating system has no idea what "%03d" is, you have to fill it in.
I can't tell from your question exactly what the parameters are, but lets assume you want to convert frame 3, it would look something like this in a string:
my_frame = 3
subprocess.call(
'ffmpeg -r 10 -i frame%03d.png -r ntsc movie%03d.mpg' % (my_frame, my_frame),
shell=True)
Its kinda subtle in this example, but that's risky. Suppose these things were in a directory whose name name had spaces (e.g., ./My Movies/Scary Movie). The shell would be confused by those spaces.
So, you can put it into a list and avoid the problem
my_frame = 3
subprocess.call([
'ffmpeg',
'-r', '10',
'-i', 'frame%03d.png' % my_frame,
'-r', 'ntsc',
'movie%03d.mpg' % my_frame,
])
More typing, but safer.
I found this alternative, simple, answer to also work.
subprocess.call('ffmpeg -r 10 -i frame%03d.png -r ntsc '+str(out_movie), shell=True)
I forgot to save some of my Jupyter notebook last night, so code that is psudo or actual runs will be marked
Some notes/context:
-
Development environment is Windows/Jupyter running whatever latest/stable Python (3.7?, no idea).
-
FFmpeg/FFprobe is open source video processing and querying software that runs on the host machine
-
This task is to ping FFMpeg on the host machine, and store the output as a variable for Python
I have this running using OS no problem (no pseudo, actually runs):
# importing os module import os # Command to execute cmd = 'ffmpeg -r 24 -i test1.mkv -r 24 -i test2.mkv -lavfi libvmaf="n_threads=20:n_subsample=10" -f null -' #Using os.system() method os.system(cmd)
Psudo output:
[Parsed_libvmaf_0 @ 00000148cfab6a80] VMAF score: 96.393400
Great!
But I need to store this as a variable in Python, which I believe is not possible with the OS.System method
But I just can't seem to figure out how to get subprocess to either work, or return the output (actual code):
# importing sibprocess module
import subprocess
# Command to execute
cmd = 'ffmpeg -r 24 -i test.mkv -r 24 -i testsrtlaopus111.mkv -lavfi libvmaf="n_threads=20:n_subsample=10" -f null -'
# Using os.system() method
returned_value = subprocess.check_output(cmd, shell=True)
# Runs the cmd, returns output
print('returned value:', returned_value)Which runs, I can see the ffmpeg command running in terminal, but once the command in terminal finishes processing, the python returns:
returned value: b''
Instead of the desired output:
[Parsed_libvmaf_0 @ 00000148cfab6a80] VMAF score: 96.393400
What am I doing wrong here?
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/python
import pexpect
cmd = "foo.sh"
thread = pexpect.spawn(cmd)
print "started %s" % cmd
cpl = 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/sh
n=5
while [ $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.