How can stop the shell command using python? - python

ffmpeg -f gdigrab -framerate 30 -i desktop -c:v h264_nvenc -qp 0 output.mkv
Hi there.
That's my shell command that capture data from screen. My command is waiting for press q, and when i press q command stops.
For ex i want to run my command through subprocess.Popen and stop it in 5 seconds.
How can i do that?

Related

subprocess not creating output file of ffmpeg command

I am trying to run an ffmpeg command that records my screen and creates an .mp4 file of the recording in python. The command works when I run it in my shell, but is not working when I am running it in a Python script using subprocess.
The issue is that when running it with subprocess, the output.mp4 file is not created.
Here is the command:
timeout 10 ffmpeg -video_size 1920x1080 -framerate 60 -f x11grab -i :0.0+0,0 -f alsa -ac 2 -i pulse -acodec aac -strict experimental output.mp4
Here is the python code:
os.chdir('/home/user/Desktop/myProject/')
subprocess.run('timeout 5 ffmpeg -video_size 1920x1080 -framerate 60 -f x11grab -i :0.0+0,0 -f alsa -ac 2 -i pulse -acodec aac -strict experimental out.mp4')
Is there an additional configuration to add so that subprocess can write output files?
subprocess.run returns an CompletedProcess object. You should assign that to a variable, and then print out all output and errors of the command (Because i think, ffmpeg gives an error and doesn't try to write the file at all, but you do not see that).
Additionally, you have to either set the keyword argument shell to True, or use shlex.split, else the command will not be formatted right. shlex.split is the preferred way, as you can read here:
Providing a sequence of arguments is generally preferred, as it allows
the module to take care of any required escaping and quoting of
arguments (e.g. to permit spaces in file names).
And you do not want to manually convert the string into a list of arguments !
And there is no need to stop ffmpeg from the outside (another reason why your file might not get written). Use the builtin command line option -t for that.
import shlex
import subprocess
import os
os.chdir('/home/user/Desktop/myProject/')
p = subprocess.run(shlex.split("ffmpeg -video_size 1920x1080 -framerate 60 -f x11grab -i :0.0+0,0 -f alsa -ac 2 -i pulse -acodec aac -strict experimental -t 00:00:05 out.mp4"), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print(p.stdout)
Instead of using timeout you may use the -t option as posted here.
Add -t 00:00:05 argument, and remove the timeout:
subprocess.run('ffmpeg -video_size 1920x1080 -framerate 60 -f x11grab -i :0.0+0,0 -f alsa -ac 2 -i pulse -acodec aac -strict experimental -t 00:00:05 out.mp4')
I think it's more elegant to use command argument than using timeout for terminating the process.
On Windows, for hysterical reasons, you can pass in a single string without shell=True and it will work. For portable code, you need to either specify shell=True, or refactor the code to avoid it (which is generally recommended wherever feasible).
Note also that subprocess.run() has keyword arguments both for setting a timeout and for specifying the working directory for the subprocess.
subprocess.run(
['ffmpeg', '-video_size', '1920x1080', '-framerate', '60',
'-f', 'x11grab', '-i', ':0.0+0,0', '-f', 'alsa',
'-ac', '2', '-i', 'pulse', '-acodec', 'aac',
'-strict', 'experimental', 'out.mp4'],
cwd='/home/user/Desktop/myProject/', # current working directory
timeout=5, # timeout
check=True # check for errors
)
With check=True you will get an exception if the command fails, the timeout will raise an exception if the command times out, regardless of whether you have check=True.
Without more information about what failed, it's hard to specify how exactly to fix your problem; but with this, hopefully you should at least get enough information in error messages to guide you.

ffmpeg video recording gets corrupted

I run the following command to record video thru ffmpeg
ffmpeg -y -rtbufsize 100M -f gdigrab -framerate 10 -i desktop -c:v libx264 -r 10 -tune zerolatency -pix_fmt yuv420p record.mp4
This works fine when I run it thru PowerShell(I stop the recording manually by pressing ctrl + c).
I am trying to do the same thing thru Python and I have created two functions to start and stop the operation.
def recThread():
cmd = 'ffmpeg -y -rtbufsize 100M -f gdigrab -framerate 10 -i desktop -c:v libx264 -r 10 -tune zerolatency -pix_fmt yuv420p ' + videoFile
global proc
proc = subprocess.Popen(cmd)
proc.wait()
def stop():
proc.terminate()
However when I run this, the video is corrupted.
I have tried using os.system command instead of subprocess and got the same result. Any help would be appreciated.
I tried changing the video format to avi and it worked like a charm. After that investigated why the same thing was not working with mp4, and found that if h264 encoder is used, ffmpeg performs an operation at the time of exit to support h264 conversion. proc.terminate() does not let ffmpeg exit gracefully.

Trying to send commands via Subprocess to operate with ffmpeg

I am trying to build a script that converts video files via ffmpeg inside Python 3.
Via Windows PowerShell I successfully obtained the desired result via the following command:
ffmpeg -i test.webm -c:v libx264 converted.mp4
However, if I try to repeat the same operation inside python via the following code:
import subprocess
from os import getcwd
print(getcwd()) # current directory
subprocess.call(["ffmpeg", " -f test.webm -c libx264 converted.mp4"])
I get the following error:
Output #0, mp4, to ' -f test.webm -c libx264 converted.mp4':
Output file #0 does not contain any stream
I am in the correct folder where the files are. Do you have better methods to execute commands in shell via Python? That should preferably work on different platforms.
try this:
import shlex
cmd = shlex.split("ffmpeg -f test.webm -c libx264 converted.mp4")
subprocess.call(cmd)
you need pass each argument as a single element in a list, that's how argv works, or let shell do the split:
subprocess.call("ffmpeg -f test.webm -c libx264 converted.mp4", shell=True)

Run two ffmpeg commands one after another

I need to run two ffmpeg commands, one after the other i.e., wait until the first command has finished, and then run the second command. The first command is
ffmpeg -threads 8 -i D:\imagesequence\dpx\brn_055.%04d.dpx D:\imagesequence\dpx\test2.mov
and the second is
ffmpeg -i D:/imagesequence/background.jpg -vf "movie='D\:/imagesequence/dpx/thumbnail.jpg' [watermark]; [in][watermark] overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/3 [out]" D:/imagesequence/dpx/final_with_text_mod_04.jpg
What is the best way to accomplish this in Python?
You don't have to do anything more than calling 2 times a ffmpeg command with subprocess python module, this is already the default behaviour
import subprocess
execstr1 = 'ffmpeg -x -y -z ...'
execstr2 = 'ffmpeg -a -b -c ...'
out1 = subprocess.check_output(execstr1, shell=True)
out2 = subprocess.check_output(execstr2, shell=True)

bash or python command to cycle through terminals and execute commands on them?

Stating the problem in a simplified form:
I'm ssh'ing to two servers using two bash terminals and running programs on the servers whose outputs I need to continuously view. Server1's output appears on terminal1 and Server2's output on terminal2.
Is there a way to run a script which is aware of how many terminals are open, and be able to cycle through them and execute bash commands on them?
Pseudocode:
open terminal1
run program1
open terminal2
run program2
switch to terminal1
run program3 on terminal1
Looked at the man page for xterm, but there was no option to switch between terminals.
The closest I could get was this and this. But both didn't help.
In [5]: import subprocess
In [6]: import shlex
In [7]: subprocess.Popen(shlex.split('gnome-terminal -x bash -c "ls; read -n1"'))
Out[7]: <subprocess.Popen object at 0x9480a2c>
screen
An alternative to screen would be tmux. Once you split your screens as you need them you can send commands to either one from a separate terminal something like:
tmux send-keys -t sessionname:0.0 "ls -al" "Enter"
tmux send-keys -t sessionname:0.1 "ls -al" "Enter"
The -t option references "sessionname":"window number"."pane number". I believe you can do a similar thing with screen but I've never used it.
Another option you might consider, if having two separate screens is not highly pertinent, is the python utility fabric. You can script commands to multiple servers and fetch results.
Creating a bash script that runs screen was the solution for me in a similar case.
You can use screen to create a screen session, and inside, create multiple numbered windows, and execute commands on them.
I am running a scrip on a cluster with 8 computers, so I ssh in each of them and run the command htop to check if no one is using.
The flag -S names a session on screen, -p enumerates the session window, and -X stuff runs a command. Note that in order to run a command " is needed on a new line to simulate carriage return(Enter)
Here is the script
#!/bin/bash
screen -d -m -S dracos
# window 0 is created by default, command ssh is executed " needed in new line to simulate pressing Enter
screen -S dracos -p 0 -X stuff "ssh draco1
"
screen -S dracos -p 0 -X stuff "htop
"
for n in {2..8}; do
# create now window using `screen` command
screen -S dracos -X screen $n
#ssh to each draco
screen -S dracos -p $n -X stuff "ssh draco$n
"
#run htop in each draco
screen -S dracos -p $n -X stuff "htop
"
screen -S dracos -p $n -X stuff "<your_new_command_here>
"
done
If you want to run commands in other order you can put the line bellow after the for
screen -S dracos -p $n -X stuff "<your_new_command_here>

Categories