Virtual camera and audio on Linux
(The below content was made for NixOS, but the actual configuration should apply to any Linux distribution with Pipewire and other related packages)
OBS provides a virtual camera which makes it easy to send video output into other apps (such as Discord, Slack, etc).
This requires the v4l2loopback
module which can be configured using the following. I also use this module for DSLR capture so have included that too:
# Configure virtual cameras for OBS and DSLR output
boot.kernelModules = [ "v4l2loopback" ];
boot.extraModprobeConfig = ''
options v4l2loopback exclusive_caps=1 devices=2 video_nr=0,1 card_label="OBS Studio,Canon DSLR"
'';
boot.extraModulePackages = [ pkgs.linuxPackages_latest.v4l2loopback ];
For the DSLR, I have a user service for starting the capture using gphoto2 and ffmpeg:
systemd.user.services.dslr-capture = {
enable = true;
description = "Canon DSLR Capture";
script = ''
${pkgs.gphoto2}/bin/gphoto2 --capture-movie --stdout | ${pkgs.ffmpeg-full}/bin/ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 /dev/video1
'';
};
This can be started on demand with systemctl --user start dslr-capture
.
I always wanted to do something similar with a microphone, and it turns out that is quite easy with Pipewire and NixOS.
environment.etc."pipewire/pipewire.conf.d/extra-sinks-sources.conf".text = ''
context.objects = [
{
# A default dummy driver. This handles nodes marked with the "node.always-driver"
# properyty when no other driver is currently active. JACK clients need this.
factory = spa-node-factory
args = {
factory.name = support.node.driver
node.name = "Dummy-Driver"
priority.driver = 8000
}
}
{
factory = adapter
args = {
factory.name = support.null-audio-sink
node.name = "Microphone-Proxy"
node.description = "Microphone Proxy"
media.class = Audio/Source/Virtual
audio.position = MONO
}
}
{
factory = adapter
args = {
factory.name = support.null-audio-sink
node.name = "OBS-Track1-Proxy"
node.description = "OBS Track 1"
media.class = Audio/Sink
audio.position = FL,FR
}
}
{
factory = adapter
args = {
factory.name = support.null-audio-sink
node.name = "OBS-Track2-Proxy"
node.description = "OBS Track 2"
media.class = Audio/Sink
audio.position = FL,FR
}
}
]
'';
This adds an extra "Microphone Proxy" that can be used as an input in your applications, and allows you to connect other sources to it through a patchbay such as helvum
(e.g. your real microphone, but potentially other application sounds, etc. by dragging them into the microphone proxy inside Helvum to link them. You can make the same connection again to unlink it).
This also adds two sinks that can be used however you like, I use this for keeping application/game audio separate from Discord audio when recording in OBS, hence the naming (those sinks are also set in OBS to record to different audio tracks). Again I make use of Helvum to redirect this output as I see fit.
Doing this with NixOS allows for a highly repeatable and reproducible setup. Unfortunately on Windows this still requires third party applications/drivers and a bit of manual setup, although it is possible to achieve a similar result there too.