I'm trying to record webcam video from python using ffmpeg. The following ffmpeg works properly when run from the cmd
ffmpeg -f dshow -rtbufsize 2000M -i video="HP HD Camera":audio="Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)" -y -vcodec libx264 -crf 24 output.mp4
I have checked the audio and video inputs with
ffmpeg -list_devices true -f dshow -i dummy
Now when I try to call the command using subprocess problem happens. I can just pass the whole command like this
import subprocess, time
recorder = subprocess.Popen('ffmpeg -f dshow -rtbufsize 2000M -i video="HP HD Camera":audio="Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)" -y -vcodec libx264 -crf 24 output.mp4', shell=True)
time.sleep(10)
recorder.terminate()
recorder.kill()
Now, as this is run with shell=True
this won't terminate the recording and I have checked different solution, but they won't work either. So, I opted for shell=False
. I tried with
recorder = subprocess.Popen(['ffmpeg', '-f', 'dshow', '-rtbufsize', '2000M', '-t', '60', '-i', 'video="HP HD Camera":audio="Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)"', '-y', '-vcodec', 'libx264', '-crf', '24', 'output.mp4'],shell=False)
This throws the error
[dshow @ 0000024f2becd540] Could not find video device with name ["HP HD Camera"] among source devices of type video.
video="HP HD Camera":audio="Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)": I/O error
Then, I tried
recorder = subprocess.Popen(['ffmpeg', '-f', 'dshow', '-rtbufsize', '2000M', '-t', '60', '-i', 'video=HP HD Camera:audio=Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)', '-y', '-vcodec', 'libx264', '-crf', '24', 'output.mp4'],shell=False)
This creates the output, but the video is not playable.
How to fix this?
We may add double quotes for letting Python subprocess
know the string is one argument, and we have to close FFmpeg gracefully.
Adding double quotes:
Replace the following part video="HP HD Camera":audio="Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)"
with:
"video=""HP HD Camera"":audio=""Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)"""
Doubling the quotes ""
is required because "
is between quotes " ... "
.
We may also use it without the ""
: "video=HP HD Camera:audio=Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)"
.
For getting a playable file, we have to close FFmpeg gracefully.
For simply recording 10 seconds, we may add -t 10
argument.
For closing FFmpeg gracefully programmatically, we may write 'q'.encode("GBK")
to stdin
pipe of FFmpeg sub-process.
Writing q
simulates that q
key is pressed by the user (stop recording).
Add stdin=subprocess.PIPE
argument to subprocess.Popen
.
After time.sleep(10)
add:
recorder.stdin.write('q'.encode("GBK")) # Simulate user pressing q key
recorder.communicate()
Add recorder.wait()
(instead of recorder.terminate()
and recorder.kill()
).
As good practice we may also add shlex.split(...)
for converting the command line to a list of arguments.
In Windows it supposed to work without it, but in Linux it's not.
Code sample:
import subprocess, time, shlex
recorder = subprocess.Popen(shlex.split('ffmpeg -f dshow -rtbufsize 2000M -t 60 -i """video=HP HD Camera:audio=Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)"":""audio=Headset (realme Buds Wireless 2 Neo Hands-Free AG Audio)"""'
' -y -vcodec libx264 -crf 24 output.mp4'), stdin=subprocess.PIPE, shell=False)
time.sleep(10)
recorder.stdin.write('q'.encode("GBK")) # Simulate user pressing q key
recorder.communicate()
recorder.wait()