Simply add ,universal_newlines=True to your subprocess.Popen line.
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)
For now you got line in cycle like:
frame= 1900 fps=453 q=18.6 Lsize= 3473kB time=00:01:16.08 bitrate= 373.9kbits/s
Use the time= value to determine progress in percentage.
Answer from Arseniy Alexandrov on Stack OverflowSimply add ,universal_newlines=True to your subprocess.Popen line.
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)
For now you got line in cycle like:
frame= 1900 fps=453 q=18.6 Lsize= 3473kB time=00:01:16.08 bitrate= 373.9kbits/s
Use the time= value to determine progress in percentage.
I've often noticed problems reading standard output (or even standard error!) with subprocess, due to buffering issues that are hard to defeat. My favorite solution, when I do need to read such stdout/stderr from the subprocess, is to switch to using, instead of subprocess, pexpect (or, on Windows, wexpect).
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?
audio - ffmpeg with python subprocess Popen - Stack Overflow
python - subprocess call ffmpeg (command line) - Stack Overflow
Pause a FFmpeg encoding in a Python Popen subprocess on Windows - Stack Overflow
Using ffmpeg in a subprocess
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)
Instead of adding -nosdtin, avoid using shell=True when opening FFmpeg subprocess.
Python code sample:
import subprocess as sp
import shlex
import psutil
from time import sleep
# Open FFmpeg subprocess with opened stdin pipe (generate synthetic pattern for testing):
process = sp.Popen(shlex.split('ffmpeg -hide_banner -y -re -f lavfi -i testsrc=size=384x216:rate=1 -c:v libx264 -pix_fmt yuv420p test.mp4'), stdin=sp.PIPE)
ps_process = psutil.Process(pid=process.pid) # Pass the PID of FFmpeg subprocess to PsUtil.
sleep(3) # Wait 3 seconds for testing ("recored 3 seconds")
ps_process.suspend() # Suspend FFmpeg process for 3 seconds
sleep(3) # Wait 3 seconds
ps_process.resume() # Resume FFmpeg process.
sleep(3)
ps_process.suspend()
sleep(3)
ps_process.resume()
# Close FFmpeg gracefully:
process.stdin.write(b'q') # Simulate user pressing q key for gracefully closing FFmpeg.
process.communicate()
process.wait()
In Linux, when using: process = sp.Popen(shlex.split('ffmpeg ...'), stdin=sp.PIPE, shell=True), I am getting the following message usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}... and Use -h to get full help or, even better, run 'man ffmpeg'.
I'm not sure of the usefulness of the psutils library here, but I do have a script working with standard libraries that'll send SIGSTOP and SIGCONT signals to the process group spawned by the ffmpeg command:
#!/usr/bin/env python3
import subprocess
import time
import os
ffmpeg_cmd = "ffmpeg -i /tmp/in.mp4 -vcodec libx264 /tmp/out.mp4"
process = subprocess.Popen(
ffmpeg_cmd,
shell=True,
preexec_fn=os.setsid,
stdout= open(os.devnull, 'w'),
stderr=subprocess.STDOUT)
while True:
os.killpg(os.getpgid(process.pid), subprocess.signal.SIGSTOP)
print("asleep")
time.sleep(10)
os.killpg(os.getpgid(process.pid), subprocess.signal.SIGCONT)
print("awake")
time.sleep(5)
Example output is:
$ ./test.py
asleep
awake
asleep
awake
asleep
Traceback (most recent call last):
File "/home/preston/superuser/1856303/./test.py", line 19, in <module>
time.sleep(10)
KeyboardInterrupt
$
You can see the work starting and stopping in a resource monitor:
