Dealing with text streams is a fundamental skill for the Linux power user. You can sort, merge, and search text files easily from the command line. What if you could do the same thing with video? Well, you can. Maybe you want to add a logo to a webcam feed before sending it to a conference app. Maybe you want to blur, color-correct, or annotate video in real time. Or perhaps you want to inject prerecorded video into Zoom while pretending it is a live camera. Linux can do all of this, and the key ingredient is usually the same: a loopback video device.
The basic idea is simple. Instead of an application reading directly from /dev/video0, you create a fake camera device using the v4l2loopback kernel module. Your software pipeline writes processed video into the fake camera, and applications read from it as if it were a normal webcam. The result is surprisingly powerful.
Loopback Cameras
The first step is to install the loopback driver. On many distributions, this is packaged already. On Debian or Ubuntu, you’d install the v4l2loopback-dkms package. On OpenSUSE it’s probably v4l2loopback-kmp-default if you’re using the usual kernel.
Unless your distro automatically loads the module, you’ll do it yourself and tell the driver how many fake cameras to make. You’ll also need to tell it where to put them. Here, I’m asking for a single camera at /dev/video10 named VirtualCam:
sudo modprobe v4l2loopback devices=1 video_nr=10 card_label="VirtualCam"
After you’ve done this, you can verify it:
v4l2-ctl --list-devices
Applications can now see the fake camera, but there’s nothing coming out of it yet.
Basic Pipeline
Suppose you have a USB webcam at /dev/video0. You can read from it and send the stream directly into the loopback device with FFmpeg:
ffmpeg -f v4l2 -i /dev/video0 -vf format=yuv420p -f v4l2 /dev/video10
Now applications can use /dev/video10 as a webcam source. This alone is useful because it decouples applications from the physical camera. For example, I had an old Intel Lifecam that would randomly throw an error when used with a browser video conference app, but ffmpeg had no problems with it. A similar pipeline lets me videoconference with this camera. But the real fun starts when you insert filters. A fun test site is webcamtoy.com.
Adding a Watermark
FFmpeg’s filter graph system is quite flexible. A watermark is just another video source composited over the main image. Suppose you have a PNG file named wrencher.png with transparency. This command overlays it in the lower-right corner:
ffmpeg -f v4l2 -i /dev/video0 -i wrencher.png -filter_complex "overlay=W-w-20:H-h-20,format=yuv420p" -f v4l2 /dev/video10
Or, you could be more explicit:
ffmpeg -f v4l2 -video_size 1024x720 -framerate 15 -i /dev/video0 -i wrencher.png -filter_complex "overlay=x=main_w-overlay_w-20:y=main_h-overlay_h-20,format=yuv420p" -f v4l2 /dev/video10
Now the logo appears 20 pixels from the bottom-right edge. However, note that if your software “mirrors” your image for local display, the watermark will go to the other side in your view. That makes sense.
If your video fails, or it looks like color garbage or a green screen, you may have to pick a different video conversion other than yuv420p.

Injecting Video
You are not limited to webcams. You can inject a prerecorded video:
ffmpeg -re -stream_loop -1 -i intro.mp4 -vf format=yuv420p -f v4l2 /dev/video10
The -re flag tells FFmpeg to play in real time instead of as fast as possible. The -stream_loop -1 repeats forever.
This is useful for demonstrations, test feeds, signage, appearing awake in meetings, or foiling security cameras in low-budget action movies.
Multiple Filters
Once you understand the filter graph concept, you can stack effects endlessly.
For example:
-filter_complex "eq=contrast=1.2:brightness=0.05, hue=s=0, overlay=W-w-20:H-h-20, format=yuv420p"
This:
- Adjusts contrast
- Brightens slightly
- Converts to grayscale
- Adds the watermark
FFmpeg contains hundreds of filters, including blur, sharpen, edge detection, chroma keying, denoise, LUTs, stabilization, and even AI-assisted processing if built with the right libraries.
At some point, though, you may want a more modular architecture. That is where GStreamer becomes interesting.
Enter GStreamer
FFmpeg excels at direct command-line media processing, but GStreamer is more like a traditional Unix/Linux pipeline. You construct a pipeline out of interconnected processing blocks. The learning curve is steeper, but it enables extremely sophisticated workflows.

A simple camera-to-loopback pipeline looks like this:
gst-launch-1.0 v4l2src device=/dev/video0 ! videoconvert ! v4l2sink device=dev/video10
That simply duplicates the webcam into the virtual device.
Suppose you want to add text:
gst-launch-1.0 v4l2src device=/dev/video0 ! videoconvert ! textoverlay text="Hackaday!" valignment=bottom halignment=right ! v4l2sink device=/dev/video10
Inspecting GStreamer
One thing that intimidates new GStreamer users is how enormous the framework feels. Fortunately, there’s a tool specifically designed to explore what is available: gst-inspect-1.0
Run without arguments, it dumps every installed plugin and element on your system. The list can be surprisingly long. Using grep on the output can help.
More useful is inspecting a specific element:
gst-inspect-1.0 v4l2src
This shows:
- Supported capabilities
- Accepted formats
- Properties
- Pad types
- Configuration options
For example, inspecting textoverlay reveals options for font selection, alignment, shading, and transparency. Inspecting videobalance shows controls for brightness, hue, saturation, and contrast.
This becomes essential because GStreamer pipelines are often constructed experimentally. You discover an element, inspect its capabilities, then wire it into the pipeline.
Using Images in GStreamer
Overlaying an image is slightly more involved because GStreamer separates streams explicitly. One common approach uses gdkpixbufoverlay (here, placing a logo at the lower right):
gst-launch-1.0 v4l2src device=/dev/video0 ! videoconvert ! gdkpixbufoverlay location=logo.png offset-x=20 offset-y=20 ! videoconvert ! v4l2sink device=/dev/video10
You can keep adding modules until you get what you want. A more elaborate pipeline might:
- Read a webcam
- Add a logo
- Blur the background
- Encode H.264
- Stream over RTSP
- Simultaneously feed a loopback webcam
GStreamer can also integrate with many hardware codecs for hardware encoding and decoding.
Practical Considerations
There are several things that tend to trip people up.
Video applications can be extremely picky about formats. MJPEG, YUYV, RGB, and YUV420 all appear frequently. If things fail mysteriously, format conversion is often the culprit.
Tools like v4l2-ctl can help:
v4l2-ctl --list-formats-ext
Another issue is that many applications reject unusual sizes. Sticking with standard resolutions like 1280×720 or 1920×1080 avoids many headaches.
Real-time video processing can consume substantial CPU resources. Hardware acceleration helps, but some filters force software processing anyway. Similarly, virtual camera pipelines can accumulate delay. Low-latency flags and queue tuning sometimes become necessary for interactive use.
Beyond Watermarks
Once you have a loopback pipeline running, you can get creative. You could create a retro CRT simulation, a fake thermal camera, or detect motion. Maybe insert a timestamp or a live video feed from your oscilloscope or 3D printer. Because the output looks like a normal webcam, almost any Linux application can use it. They simply see another camera.
There are many more advanced techniques possible with GStreamer pipelines, including branching streams with tee, synchronizing multiple sources, GPU-accelerated effects, network streaming, and live compositing. If you want a deeper dive into practical virtual-camera workflows, this video provides an excellent starting point:
Another tip: if you write shell scripts, using things like /dev/video0 will get you in trouble unless your configuration never changes. Instead, use the stable symlinks:
ls -l /dev/v4l/by-id/
You’ll see things like:
usb-046d_HD_Pro_Webcam_C920-video-index0
Then use that path directly in FFmpeg or GStreamer.
Cleaning Up
When you are finished with the virtual camera, remember that the loopback device persists until the kernel module is unloaded. Stop any applications using the fake camera first, then remove the module:
sudo modprobe -r v4l2loopback
That tears down the virtual camera devices and removes them. If you created multiple fake cameras, unloading the module removes them all at once.
We’ve used Gstrteamer before for remote driving.



You must be logged in to post a comment Login