TIL: v4l2loopback recipes
v4l2loopback
is a kernel module which creates virtual video devices. It allows you to use video streams from various sources as if they were input from a physical camera.
Load the module
This is how OBS does it. card_label
will be what Firefox shows in its “allow camera” popup.
sudo modprobe v4l2loopback exclusive_caps=1 card_label='OBS Virtual Camera'
Other module options:
video_nr=X
, will create/dev/videoX
Stream a video file to the device
ffmpeg -readrate 1 -i video.mov -f v4l2 /dev/video9
-readrate 1
(or -re 1
) means to stream the input file in real-time speed.
Without it it will pass in a blink of an eye.
Loop a video file indefinitely
This is how you do the Mission Impossible trick - record an empty corridor (or yourself sitting at a video call and nodding attentively) and play it back in a loop:
ffmpeg -readrate 1 -stream_loop -1 -i video.mov -f v4l2 /dev/video9
Stream camera data 1:1 to the loopback device
This passes the mjpeg frames directly from /dev/video0
(real camera) to /dev/video9
(v4l2loopback device).
Key is -c:v copy
.
ffmpeg -f v4l2 \
-input_format mjpeg \
-video_size 1920x1080 \
-framerate 30 \
-i /dev/video0 \
-c:v copy \
-f v4l2 \
/dev/video9
Stream camera to loopback device with ffmpeg re-encoding
ffmpeg -f v4l2 \
-input_format mjpeg \
-video_size 1920x1080 \
-framerate 30 \
-i /dev/video0 \
-f v4l2 \
-pix_fmt yuyv422 \
/dev/video9
Apply basic filters
Flip upside down and blur.
ffmpeg -f v4l2 \
-input_format mjpeg \
-video_size 1280x720 \
-framerate 30 \
-i /dev/video0 \
-filter:v "vflip,gblur=sigma=5" \
-f v4l2 \
-pix_fmt yuyv422 \
/dev/video9
Interesting filters to check out:
-filter:v "lagfun"
-filter:v "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2"
More elaborate examples
These effectively combine two inputs (note two -i
flags) into s single stream using split
and maskedmerge
.
Apply blur only to left half of the image
ffmpeg \
-f v4l2 \
-input_format mjpeg -video_size 1280x720 -framerate 30 -fflags nobuffer -i /dev/video0 \
-f lavfi \
-i "gradients=size=1280x720:c0=white:c1=black:nb_colors=2:type=linear:x0=639:y0=0:x1=641:y1=0:speed=0" \
-filter_complex "split [main][tmp]; [tmp] boxblur=luma_radius=10 [blurred]; [main][blurred][1:v]maskedmerge" \
-f v4l2 -codec:v mjpeg -preset zerolatency -q:v 2 -pix_fmt yuvj420p /dev/video9
Blur background
This can be used for simple background blur in video conferencing.
Needs a silhouette.png
(1280x720px) which will be the mask for the filter.
ffmpeg \
-f v4l2 \
-input_format mjpeg -video_size 1280x720 -framerate 30 -fflags nobuffer -i /dev/video0 \
-i silhouette.png \
-filter_complex "split [main][tmp]; [tmp] boxblur=luma_radius=10 [blurred]; [main][blurred][1:v]maskedmerge" \
-f v4l2 -codec:v mjpeg -preset zerolatency -q:v 2 -pix_fmt yuvj420p /dev/video9
Pixellize background
Same as above, but pixellize instead of blur
ffmpeg \
-f v4l2 \
-input_format mjpeg -video_size 1280x720 -framerate 30 -fflags nobuffer -i /dev/video0 \
-i silhouette.png \
-filter_complex "split [main][tmp]; [tmp] scale=iw/20:ih/20:flags=neighbor, scale=iw*20:ih*20:flags=neighbor [blurred]; [main][blurred][1:v]maskedmerge" \
-f v4l2 -codec:v mjpeg -preset zerolatency -q:v 10 -pix_fmt yuvj420p /dev/video9
Aside: play webcam stream with lowest latency
My camera is /dev/video0
. First check what formats the camera supports:
ffplay -f v4l2 -list_formats all /dev/video0
# ...
[video4linux2,v4l2 @ 0x60ffdd8c20c0] Raw : yuyv422 : YUYV 4:2:2 : 640x480 160x120 176x144 320x240 432x240 352x288 640x360 800x448 864x480 1024x576 800x600 960x720 1280x720 1600x896 1920x1080
[video4linux2,v4l2 @ 0x60ffdd8c20c0] Compressed: mjpeg : Motion-JPEG : 640x480 160x120 176x144 320x240 432x240 352x288 640x360 800x448 864x480 1024x576 800x600 960x720 1280x720 1600x896 1920x1080
# ...
Or more detailed with v4l2-ctl
:
v4l2-ctl --list-formats-ext -d /dev/video0
By default my Logitech C615 streams yuyv422, but at a low framerate. To play mjpeg instead:
ffplay -fflags nobuffer \
-f v4l2 \
-input_format mjpeg \
-video_size 1920x1080 \
-framerate 30 \
/dev/video0
Here -fflags nobuffer
prioritizes low latency playback.
With mpv
:
mpv --demuxer-lavf-o=video_size=1280x720,input_format=mjpeg,framerate=30 \
--profile=low-latency \
--untimed \
/dev/video0