letmaik/pyvirtualcam

"No v4l2 loopback device found" after using pyvirtualcam once with `exclusive_caps=1`

Opened this issue · 17 comments

  • Operating system: Arch Linux
  • pyvirtualcam version: 0.8.0
  • Virtual camera (OBS, v4l2loopback, UnityCapture): v4l2loopback
  • Virtual camera version: 0.12.5-1

Describe the bug
After running a script using pyvirtualcam the next run causes this error:

RuntimeError: 'v4l2loopback' backend: No v4l2 loopback device found at /dev/video[0-99]. Did you run 'modprobe v4l2loopback'? See also pyvirtualcam's documentation.

Only after unloading and reloading the kernel module I can run another script using pyvirtualcam.

To Reproduce
Every single sample code causes this error.

#!/usr/bin/env python3
import pyvirtualcam
import numpy as np

with pyvirtualcam.Camera(width=1280, height=720, fps=20) as cam:
    print(f'Using virtual camera: {cam.device}')
    frame = np.zeros((cam.height, cam.width, 3), np.uint8)  # RGB
    while True:
        frame[:] = cam.frames_sent % 255  # grayscale animation
        cam.send(frame)
        cam.sleep_until_next_frame()

I even rewrote a script to not use the with statement and call cam.close() (and even cam._backend.close()) directly.

I'm unable to reproduce this on Ubuntu 18.04 (kernel 4.15.0-143) using the same v4l2loopback version with/without running ffplay /dev/video0. Do you see the issue even without having an app connected to the cam? If it only happens with an app, which one is it?

Also, if you choose a specific device you will see a better error message, for example:

with pyvirtualcam.Camera(width=1280, height=720, fps=20, device="/dev/video0") as cam:

This must match the device created by v4l2loopback obviously.

Thanks for your quick response.

This issue occurs even without any consumer connected at any point. I just started a script and quit it (Ctrl+c) and then tried to restart it, resulting in above error.

I also tried specifying the device to use which resulted in this error:

RuntimeError: 'v4l2loopback' backend: Device /dev/video10 is not a video output device.

Until now I didn't try connecting another producer after having run a pyvirtualcam script. I just tried with OBS, and OBS can connect to the loopback device. I even added another v4l2 source in OBS and selected my loopback device, and it worked.

Yesterday, I tried the virtualvideo module which uses ffmpeg for accessing the loopback device. I just tried running their showFish.py sample after having used pyvirtualcam and it worked, too.

Neither dmesg -w nor journalctl -f show any messages related to v4l2.

Basically, it looks like pyvirtualcam leaves the loopback device in a state that makes it unaccessible for pyvirtualcam again but not for any other application.

I attached straces of the same script file working and failing.
pyvirtualcam_working.trace.txt
pyvirtualcam_failed.trace.txt
Beginning at line 3736 the execution diverges. The working case shows:

ioctl(5, VIDIOC_QUERYCAP, {driver="v4l2 loopback", card="Overlay", bus_info="platform:v4l2loopback-000", version=KERNEL_VERSION(5, 12, 7), capabilities=V4L2_CAP_VIDEO_OUTPUT|V4L2_CAP_VIDEO_M2M|V4L2_CAP_EXT_PIX_FORMAT|V4L2_CAP_READWRITE|V4L2_CAP_STREAMING|V4L2_CAP_DEVICE_CAPS, device_caps=V4L2_CAP_VIDEO_OUTPUT|V4L2_CAP_VIDEO_M2M|V4L2_CAP_EXT_PIX_FORMAT|V4L2_CAP_READWRITE|V4L2_CAP_STREAMING}) = 0

while in the failing case it shows:

ioctl(5, VIDIOC_QUERYCAP, {driver="v4l2 loopback", card="Overlay", bus_info="platform:v4l2loopback-000", version=KERNEL_VERSION(5, 12, 7), capabilities=V4L2_CAP_VIDEO_M2M|V4L2_CAP_EXT_PIX_FORMAT|V4L2_CAP_READWRITE|V4L2_CAP_STREAMING|V4L2_CAP_DEVICE_CAPS, device_caps=V4L2_CAP_VIDEO_M2M|V4L2_CAP_EXT_PIX_FORMAT|V4L2_CAP_READWRITE|V4L2_CAP_STREAMING}) = 0

Interestingly, pyvirtualcam doesn't detect V4L2_CAP_VIDEO_OUTPUT capability anymore. Which, concidentally, is the same that v4l2-ctl detects (before):

[masin@luna webcam]$ v4l2-ctl -d /dev/video10 -D
Driver Info:
	Driver name      : v4l2 loopback
	Card type        : Overlay
	Bus info         : platform:v4l2loopback-000
	Driver version   : 5.12.7
	Capabilities     : 0x85208002
		Video Output
		Video Memory-to-Memory
		Read/Write
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x05208002
		Video Output
		Video Memory-to-Memory
		Read/Write
		Streaming
		Extended Pix Format

(after):

[masin@luna webcam]$ v4l2-ctl -d /dev/video10 -D
Driver Info:
	Driver name      : v4l2 loopback
	Card type        : Overlay
	Bus info         : platform:v4l2loopback-000
	Driver version   : 5.12.7
	Capabilities     : 0x85208000
		Video Memory-to-Memory
		Read/Write
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x05208000
		Video Memory-to-Memory
		Read/Write
		Streaming
		Extended Pix Format

I am not especially apt in reading straces but in the working case's trace log there's an "Inappropriate ioctl for device" in line 3824. I can't tell if that's important or somewhere in its vicinity.

Not exactly sure what's going on. How do you create the device? Are you using exclusive_caps=1 by any chance? Even if, it should still work...

sudo modprobe -r v4l2loopback && sudo modprobe v4l2loopback devices=1 video_nr=10 card_label="Overlay" exclusive_caps=1 max_buffers=2

If I remove exclusive_caps=1 I don't have to unload and reload the kernel module anymore. Thanks a lot! That solves my immediate problem.

There's still the question why every other application but pyvirtualcam isn't bothered by having the loopback device been used before. But that's more out of curiosity than urgency.

On my end I still can't reproduce it with the same modprobe line you've given, with exclusive_caps=1.

For me, before running the pyvirtualcam script, v4l2-ctl shows "Video Output". While running it, it shows "Video Capture". After exiting the script with ctrl-c it has "Video Output" again. This is what exclusive_caps=1 does. And if you leave it out, then it always advertises both capture and output. In your case, the weird thing is that you see neither after the script exits, which is something that shouldn't be possible.

I do face the same issue on gentoo linux (pyvirtualcam==0.8.0).

After modprobe, befor using:
Driver Info:
Driver name : v4l2 loopback
Card type : Webcam
Bus info : platform:v4l2loopback-000
Driver version : 5.10.61
Capabilities : 0x85208002
Video Output
Video Memory-to-Memory
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x05208002
Video Output
Video Memory-to-Memory
Read/Write
Streaming
Extended Pix Format

After terminating:
Driver Info:
Driver name : v4l2 loopback
Card type : Webcam
Bus info : platform:v4l2loopback-000
Driver version : 5.10.61
Capabilities : 0x85208000
Video Memory-to-Memory
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x05208000
Video Memory-to-Memory
Read/Write
Streaming
Extended Pix Format

I am using Ubuntu 20.04.4 LTS x86_64 with pyvirtualcam at v0.9.1 and face the same problem. The program throws an exception when it can't detect the device as output ( if v4l2_capability hasn't V4L2_CAP_VIDEO_OUTPUT) when using case exlusive_caps=1. Removing the throw where it checks this capability circumvents the issue.

Since in my case exlusive_caps=1 is essential, I will temporarily create a fork where I ignore whenever the OUTPUT flag fails.

If there is any other additional information that you need to recreate the issue, please ask me : )

I'm experiencing a very similar (perhaps, identical?) problem. If I load v4l2 by running:

modprobe v4l2loopback devices=1 video_nr=9

I can only start pyvirtualcamera once. The second time I do it, I get the following error:

Traceback (most recent call last): File "/home/jedrek/alternatywy/projects/Playground/VirtualCamera/cam_test.py", line 4, in <module> with pyvirtualcam.Camera(width=1280, height=720, fps=20, device="/dev/video9") as cam: File "/home/jedrek/alternatywy/projects/Playground/venv/lib/python3.10/site-packages/pyvirtualcam/camera.py", line 219, in __init__ raise RuntimeError('\n'.join(errors)) RuntimeError: 'v4l2loopback' backend: std::exception

One workaround is to load the module with exclusive_caps=0, then everything is fine and I can start it multiple times (but I don't see the camera in Chrome).

Details of my system:
Linux version 5.18.12-1-default (geeko@buildhost) (gcc (SUSE Linux) 12.1.1 20220721 [revision 4f15d2234608e82159d030dadb17af678cfad626], GNU ld (GNU Binutils; openSUSE Tumbleweed) 2.38.20220525-6) #1 SMP PREEMPT_DYNAMIC Fri Jul 15 12:08:33 UTC 2022 (3198c22)

I'm using Python 3.10.4.

Same issue for me under Ubuntu 22

with pyvirtualcam.Camera(width = 640, height = 480, fps = 25) as camera:
  File "/home/henry/PycharmProjects/facefusion/venv/lib/python3.10/site-packages/pyvirtualcam/camera.py", line 219, in __init__
    raise RuntimeError('\n'.join(errors))
RuntimeError: 'v4l2loopback' backend: std::exception
sbab commented

Similar issue for me running Ubuntu 22.04.3 LTS:

  • If I create the device with sudo modprobe v4l2loopback devices=1 video_nr=0 exclusive_caps=1, my code crashes if I run it twice in a row (without even actually using the device) and destroying and recreating it with sudo modprobe fixes it temporarily. Upon creation the device is detected as output only, and after running the code is detected as neither input nor output.
  • Recreating the device with sudo modprobe v4l2loopback devices=1 video_nr=0 makes everything work and I can also view the device output in VLC, even though some values in the output of v4l2-ctl get modified (Width/Height, Pixel Format, Bytes per Line, Size Image, Quantization). The device is also always detected as both input and output.

This is the code I'm running, it's the basic greyscale animation with a SIGINT handler for a graceful exit (which I thought was the issue...):

main.py
import signal
import pyvirtualcam
import numpy as np

GLOBAL_SIGINT_RECEIVED = False

def onSigint(sig, frame):
    global GLOBAL_SIGINT_RECEIVED
    GLOBAL_SIGINT_RECEIVED = True

signal.signal(signal.SIGINT, onSigint)

with pyvirtualcam.Camera(width=1280, height=720, fps=20, device="/dev/video0") as cam:
    print(f'Using virtual camera: {cam.device}')
    frame = np.zeros((cam.height, cam.width, 3), np.uint8)  # RGB
    while True:
        frame[:] = cam.frames_sent * 4 % 255  # grayscale animation
        cam.send(frame)
        cam.sleep_until_next_frame()
        
        if (GLOBAL_SIGINT_RECEIVED) :
            break

This is the output of v4l2-ctl --all -d /dev/video0 when I create the device with before and after the first run of my program:

Output of v4l2-ctl BEFORE running the code, WITH exclusive_caps=1
Driver Info:
	Driver name      : v4l2 loopback
	Card type        : Dummy video device (0x0000)
	Bus info         : platform:v4l2loopback-000
	Driver version   : 6.2.16
	Capabilities     : 0x85200002
		Video Output
		Read/Write
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x05200002
		Video Output
		Read/Write
		Streaming
		Extended Pix Format
Priority: 2
Video output: 0 (loopback in)
Format Video Output:
	Width/Height      : 0/0
	Pixel Format      : 'BGR4' (32-bit BGRA/X 8-8-8-8)
	Field             : None
	Bytes per Line    : 0
	Size Image        : 0
	Colorspace        : sRGB
	Transfer Function : Default (maps to sRGB)
	YCbCr/HSV Encoding: Default (maps to ITU-R 601)
	Quantization      : Default (maps to Full Range)
	Flags             : 
Streaming Parameters Video Capture:
	Frames per second: 30.000 (30/1)
	Read buffers     : 2
Streaming Parameters Video Output:
	Frames per second: 30.000 (30/1)
	Write buffers    : 2

User Controls

                    keep_format 0x0098f900 (bool)   : default=0 value=0
              sustain_framerate 0x0098f901 (bool)   : default=0 value=0
                        timeout 0x0098f902 (int)    : min=0 max=100000 step=1 default=0 value=0
               timeout_image_io 0x0098f903 (bool)   : default=0 value=0
Output of v4l2-ctl AFTER running the code, WITH exclusive_caps=1
Driver Info:
	Driver name      : v4l2 loopback
	Card type        : Dummy video device (0x0000)
	Bus info         : platform:v4l2loopback-000
	Driver version   : 6.2.16
	Capabilities     : 0x85200000
		Read/Write
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x05200000
		Read/Write
		Streaming
		Extended Pix Format
Priority: 2

User Controls

                    keep_format 0x0098f900 (bool)   : default=0 value=0
              sustain_framerate 0x0098f901 (bool)   : default=0 value=0
                        timeout 0x0098f902 (int)    : min=0 max=100000 step=1 default=0 value=0
               timeout_image_io 0x0098f903 (bool)   : default=0 value=0

This is the exception raised by my program:

Screenshot from 2023-09-26 02-26-22

And this is the output i have creating the device without exclusive_caps=1, before and after running the code:

Output of v4l2-ctl BEFORE running the code, WITHOUT exclusive_caps=1
Driver Info:
	Driver name      : v4l2 loopback
	Card type        : Dummy video device (0x0000)
	Bus info         : platform:v4l2loopback-000
	Driver version   : 6.2.16
	Capabilities     : 0x85200003
		Video Capture
		Video Output
		Read/Write
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x05200003
		Video Capture
		Video Output
		Read/Write
		Streaming
		Extended Pix Format
Priority: 2
Video input : 0 (loopback: ok)
Video output: 0 (loopback in)
Format Video Output:
	Width/Height      : 0/0
	Pixel Format      : 'BGR4' (32-bit BGRA/X 8-8-8-8)
	Field             : None
	Bytes per Line    : 0
	Size Image        : 0
	Colorspace        : sRGB
	Transfer Function : Default (maps to sRGB)
	YCbCr/HSV Encoding: Default (maps to ITU-R 601)
	Quantization      : Default (maps to Full Range)
	Flags             : 
Streaming Parameters Video Capture:
	Frames per second: 30.000 (30/1)
	Read buffers     : 2
Streaming Parameters Video Output:
	Frames per second: 30.000 (30/1)
	Write buffers    : 2

User Controls

                    keep_format 0x0098f900 (bool)   : default=0 value=0
              sustain_framerate 0x0098f901 (bool)   : default=0 value=0
                        timeout 0x0098f902 (int)    : min=0 max=100000 step=1 default=0 value=0
               timeout_image_io 0x0098f903 (bool)   : default=0 value=0
Output of v4l2-ctl AFTER running the code, WITHOUT exclusive_caps=1
Driver Info:
	Driver name      : v4l2 loopback
	Card type        : Dummy video device (0x0000)
	Bus info         : platform:v4l2loopback-000
	Driver version   : 6.2.16
	Capabilities     : 0x85200003
		Video Capture
		Video Output
		Read/Write
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x05200003
		Video Capture
		Video Output
		Read/Write
		Streaming
		Extended Pix Format
Priority: 2
Video input : 0 (loopback: ok)
Video output: 0 (loopback in)
Format Video Output:
	Width/Height      : 1280/720
	Pixel Format      : 'YU12' (Planar YUV 4:2:0)
	Field             : None
	Bytes per Line    : 1280
	Size Image        : 1382400
	Colorspace        : sRGB
	Transfer Function : Default (maps to sRGB)
	YCbCr/HSV Encoding: Default (maps to ITU-R 601)
	Quantization      : Default (maps to Limited Range)
	Flags             : 
Streaming Parameters Video Capture:
	Frames per second: 30.000 (30/1)
	Read buffers     : 2
Streaming Parameters Video Output:
	Frames per second: 30.000 (30/1)
	Write buffers    : 2

User Controls

                    keep_format 0x0098f900 (bool)   : default=0 value=0
              sustain_framerate 0x0098f901 (bool)   : default=0 value=0
                        timeout 0x0098f902 (int)    : min=0 max=100000 step=1 default=0 value=0
               timeout_image_io 0x0098f903 (bool)   : default=0 value=0

The first execution of the same camera is normal, but the execution after ctrl+c is abnormal.image

Fix: Stop the video stream before closing the device, e.g: ioctl(vcam->device, VIDIOC_STREAMOFF, &parm)
Source: obsproject/obs-studio#7078

A fix might be to insert this before 'close(_camera_fd);':

        struct v4l2_streamparm parm = {0};
        parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
        if (ioctl(_camera_fd, VIDIOC_STREAMOFF, &parm) < 0) {
            fprintf(stderr, "Failed to stop streaming on video device: %s\n", strerror(errno));
        }

Maybe as an extra function, as the close() is used 3 times.

@GermanWarez Are you able to test this and create a PR if it works?

@letmaik
I could add a test that would be executed by github actions, that should fail without the fix and pass with the extra line. But I have no linux device to test. Would be sufficient to accept a PR?

TODO: Add ioctl to set both VIDIOC_STREAMON and VIDIOC_STREAMOFF, using the fix for obs as reference.

I hit the same issue. Wait, nobody tested the PR since March?