Virtual webcams on Linux
Table of Contents
Below are a couple of ways of extending a Linux computer’s webcam provision - even if you don’t have a webcam! The first adds your mobile phone’s camera as a webcam. The second uses a static JPEG or PNG image as a webcam.
Using a phone camera as a virtual webcam⌗
Having heard about DroidCam as a way to use mobile phones as cameras for computers, I gave it a try. Thanks to Andy Simpkins’ instructions, this proved to be quite simple.
I installed DroidCam on my phone, launched it, and noted that it displayed the URL to connect to:
http://172.20.99.99:4747/video
If you try this the URL displayed will be something like the one it gave me, but almost certainly not exactly the same numbers (unless your wifi is configured exactly like mine).
My particular laptop has a built in webcam that for whatever reason presents as four (count ’em, four) devices:
❯ lsusb -tv | grep -B1 Acer
|__ Port 8: Dev 8, If 1, Class=Video, Driver=uvcvideo, 480M
ID 5986:2115 Acer, Inc
|__ Port 8: Dev 8, If 0, Class=Video, Driver=uvcvideo, 480M
ID 5986:2115 Acer, Inc
--
|__ Port 12: Dev 14, If 1, Class=Video, Driver=uvcvideo, 480M
ID 5986:2123 Acer, Inc
|__ Port 12: Dev 14, If 0, Class=Video, Driver=uvcvideo, 480M
ID 5986:2123 Acer, Inc
Two devices I can understand since it has an integrated IR camera. But four? For each camera the only significant difference between each reported device is that one has ‘ID_V4L_CAPABILITIES=:capture:’ whereas the other has ‘ID_V4L_CAPABILITIES=:’. I digress.
So anyway on my laptop there are four device nodes:
❯ ls /dev/video*
/dev/video0
/dev/video1
/dev/video2
/dev/video3
and adding a virtual camera will create a fifth device node. Devices are counted from zero, so the new device will be /dev/video4
. If your computer has no cameras attached, the new device would be /dev/video0
.
Now that’s all dealt with, here are the commands I needed to get going:
-
Install the necessary software:
sudo apt-get install ffmpeg v4l2loopback-utils
-
Load the kernel module:
sudo modprobe v4l2loopback
-
Run the middleware:
ffmpeg -re -i http://172.20.99.99:4747/video -vcodec rawvideo -pix_fmt yuv420p -f v4l2 /dev/video4
(I’ll refer you back to Andy’s explanation of the various parts of this command.)
And at this point I could use the virtual camera in Google Meet within Firefox. But not in Chrome. No, dunno. (I use Firefox by default anyway, and only discovered that Chrome didn’t work when helping a a colleague get going.)
Using a static image as a virtual webcam⌗
Depending on the weather, my internet connexion is sometimes unstable. When the line is poor, I’ll kill the camera for conference calls. I wondered about using a static image instead of just killing the stream entirely, and remembered that ffmpeg
can process a series of jpeg or png files in to a video. So it would probably be able to turn a single image in to a virtual webcam.
You’ll (1) need the same packages installed as above, and (2) a loopback video device configured:
-
Install the necessary software:
sudo apt-get install ffmpeg v4l2loopback-utils
-
Load the kernel module:
sudo modprobe v4l2loopback
After some searching and experimenting, the incantation I came up with for a one frame per second video stream was this:
ffmpeg -re -framerate 1 -loop 1 -i image.jpg -f v4l2 -r 1 -pix_fmt yuv420p /dev/video4
This worked initially, but that was down to my lucky choice of picture. When I used a different image, the resulting output was corrupted.
I tried using a different image, but ran in to a brick wall. My eventual doorway through was to run the following before running the ffmpeg
command:
v4l2loopback-ctl set-caps "video/x-raw, format=I420, width=1280, height=720" /dev/video4
Of course now the original image won’t work…
To reduce unneccesary processing overhead I played around with reducing the framerate still further, but I’ve only tested these with vlc
and not (yet) in Google Meet:
ffmpeg -re -framerate 0.1 -loop 1 -i image.jpg -f v4l2 -r 0.1 -pix_fmt yuv420p /dev/video4
And even further, down to 0.01. vlc
worked, but its position bar went in to Breakout paddle mode, sliding erraticly from side to side. YMMV.
It will take ffmpeg
a surprising long time to get going with these reduced frame rates, and it will use a surprising amount of memory, but it does work.
Reduced overheads and image cycling⌗
I originally used a PNG file, but I’ve noticted that using a JPEG is faster.
Better yet, I noticed that even after I stop ffpmeg
, the virtual camera device still emits the image, even for new connexions. That lead me to try using ffmpeg
’s -t <duration>
parameter:
ffmpeg -t 1 -re -framerate 0.1 -loop 1 -i image.jpg -f v4l2 -r 0.1 -pix_fmt yuv420p /dev/video4
Which works marvellously \o/
ffmpeg
now starts up, does what I need, then closes down. Which means I can cycle through a directory of suitable images:
while true; do
for img in *.jpg; do
ffmpeg -t 1 -re -framerate 0.1 -loop 1 -i "${img}" -f v4l2 -r 0.1 -pix_fmt yuv420p /dev/video4
sleep 60
done
done
There may be trouble ahead⌗
Whilst trying to find a command sequence that worked, I wanted to reload the kernel module. This should is normally achieved with rmmod
/modprobe
:
sudo rmmod v4l2loopback && sudo modprobe v4l2loopback
But sometimes, behind the scenes, the application keeps the device open. If you know which application is likely to blame, then just close it and run the rmmod
/modprobe
commands.
Closing the application doesn’t always allow the rmmod
command to work. For example, I’ve found that running v4l2loopback-ctl
will sometimes leave gstreamer
locking the device. You can use the wonderfully named lsof
to list the opener(s) of the file:
❯ lsof /dev/video4
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
vlc 2642406 ebardie mem CHR 81,4 33537251 /dev/video4
vlc 2642406 ebardie 19u CHR 81,4 0t0 33537251 /dev/video4
You can then close the problem app, if it has GUI, or try the following:
pkill vlc
or even (note the PID column of lsof
’s output):
kill 2642406
v4l2loopback-ctl
will sometimes need to be killed by backgrounding it, by pressing <CTRL-Z>
, and then running jobs
to find the job number:
❯ jobs
[1] + suspended /usr/bin/v4l2loopback-ctl set-caps /dev/video4
and finally kill %1
to terminate it (%1
if its job number 1, %2
for 2, etc.). Try kill -9 %1
if it still hasn’t lain down and died.