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:

  1. Install the necessary software:

     sudo apt-get install ffmpeg v4l2loopback-utils
    
  2. Load the kernel module:

     sudo modprobe v4l2loopback
    
  3. 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:

  1. Install the necessary software:

     sudo apt-get install ffmpeg v4l2loopback-utils
    
  2. 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.