Video – Why Can Multiple Consumers Access a Single v4l2-Loopback Stream?

cameraffmpegv4lv4l2loopbackvideo

I recently needed a single webcam to be shared simultaneously by 3 applications (a web browser, a videoconferencing app, and ffmpeg to save the stream).

It's not possible to simply share the /dev/video* stream because as soon as one application is using it, the others cannot, and anything else will get a "device or resource busy" or equivalent.

So I turned to v4l2-loopback with the intention of mirroring the webcam to 3 loopbacks.

Using 3 loopbacks does work as expected, but what has really surprised me is it turns out I don't actually need 3 loopbacks, but only 1.

If I create a single loopback and feed it with ffmpeg, then the single mirrored loopback can be used by all 3 applications at the same time, with no "device or resource busy" issue.

So this is even better than I planned, and there is no practical problem I need help with.

But my question is, how is this possible with the loopback? And why not using the original source directly?

Example command to create the single loopback:

sudo modprobe v4l2loopback video_nr=30 exclusive_caps=1 card_label="loopback cam"

Example command using ffmpeg to mirror /dev/video5 to the loopback (/dev/video30). This will default to raw, but recent builds of ffmpeg can use an alternative stream like MJPEG, the behaviour is the same regardless:

ffmpeg -f v4l2 -i /dev/video5 -codec copy -f v4l2 /dev/video30

After doing this, try to access /dev/video30 with multiple applications, here are some examples:

ffmpeg -f v4l2 -i /dev/video30 -codec libx264 recordstream.mp4
ffplay -f video4linux2 -i /dev/video30

System info in case it's relevant:

  • Ubuntu 20.04
  • Kernel: 5.4.0-31-generic
  • package: v4l2loopback-dkms 0.12.3-1

Best Answer

It's by design. First, let it be said that multiple processes can open the /dev/video0 device, but only one of them will be able to issue certain controls (ioctl()) until streaming starts.

Those V4L2 controls define things like bitrate. After you start streaming, the kernel will not let you change them and returns EBUSY (Device or resource busy) if you try. See this note in the kernel source. That effectively blocks other consumers, since you should set those before you start streaming.

What does v4l2loopback do differently? It adds logic and data structures for multiple openers and by default will not try to apply new controls by provinding its own setter.

Note that v4l2loopback needs to have multiple openers, at least two to be useful. One reader and one writer.

Related Question