Search code examples
python-3.xffmpegv4l2python-imageioffmpeg-python

Pipe numpy array to virtual video device


I want to pipe images to a virtual video device (e.g. /dev/video0), the images are created inside a loop with the desired frame rate.

In this minimal example i only two arrays which alternate in the cv2 window. Now i look for a good solution to pipe the arrays to the virtual device.

I saw that ffmpeg-python can run asynchronous with ffmpeg.run_async(), but so far i could not make anything work with this package.

example code without the ffmpeg stuff:

#!/usr/bin/env python3

import cv2
import numpy as np
import time

window_name = 'virtual-camera'
cv2.namedWindow(window_name, cv2.WINDOW_GUI_EXPANDED)

img1 = np.random.uniform(0, 255, (1080, 1440, 3)).astype('uint8')
img2 = np.random.uniform(0, 255, (1080, 1440, 3)).astype('uint8')

for i in range(125):
    time.sleep(0.04)
    if i % 2:
        img = img1
    else:
        img = img2
    cv2.imshow(window_name, img)
    cv2.waitKey(1)
cv2.destroyAllWindows()

Solution

  • As Programmer wrote in his answer, it is possible to create a dummy device with the package v4l2loopback. To publish images, videos or the desktop to the dummy device was already easy with ffmpeg, but i want to pipe it directly from the python script - where i capture the images - to the dummy device. I still think it's possible with ffmpeg-python, but i found this great answer from Alp which sheds light on the darkness. The package pyfakewebcam is a perfect solution for the problem.

    For the sake of completeness, here is my extended minimal working example:

    #!/usr/bin/env python3
    
    import time
    
    import cv2
    import numpy as np
    import pyfakewebcam
    
    WIDTH = 1440
    HEIGHT = 1080
    DEVICE = '/dev/video0'
    
    fake_cam = pyfakewebcam.FakeWebcam(DEVICE, WIDTH, HEIGHT)
    
    window_name = 'virtual-camera'
    cv2.namedWindow(window_name, cv2.WINDOW_GUI_EXPANDED)
    
    img1 = np.random.uniform(0, 255, (HEIGHT, WIDTH, 3)).astype('uint8')
    img2 = np.random.uniform(0, 255, (HEIGHT, WIDTH, 3)).astype('uint8')
    
    for i in range(125):
        time.sleep(0.04)
        if i % 2:
            img = img1
        else:
            img = img2
        fake_cam.schedule_frame(img)
        cv2.imshow(window_name, img)
        cv2.waitKey(1)
    cv2.destroyAllWindows()