Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

camera-fixd

Aviso sobre el historial: este proyecto comienza con un historial de Git nuevo porque en el historial anterior se filtraron datos confidenciales.

camera-fixd es un servicio Linux en espacio de usuario para cámaras V4L2 que solo funcionan de forma fiable con un perfil concreto. Detecta exclusivamente modelos registrados, abre cada cámara física con su perfil conocido, transporta los frames y publica una cámara virtual independiente mediante v4l2loopback.

El proyecto no instala un driver específico dentro del kernel. Utiliza el driver normal de la cámara —habitualmente UVC— y el módulo genérico v4l2loopback. Si el proceso falla, el fallo queda aislado en espacio de usuario y no compromete la estabilidad del kernel.

Esta guía documenta la instalación, toda la configuración disponible, el uso multicámara, la creación de perfiles, los permisos, la operación del servicio y el diagnóstico de problemas.

Índice

Qué hace y qué no hace

camera-fixd:

  • reconoce cámaras mediante atributos USB, sysfs y V4L2;
  • nunca usa un /dev/videoN como identidad persistente;
  • diferencia nodos de captura, metadatos y dispositivos virtuales;
  • permite registrar varios modelos, cada uno con su propio perfil;
  • admite varias cámaras compatibles conectadas simultáneamente;
  • crea una salida virtual independiente y reconocible para cada unidad física;
  • fuerza formato, resolución, frecuencia y número de buffers;
  • monitoriza eventos udev y realiza escaneos periódicos de respaldo;
  • recupera streams tras desconexiones, reinicios USB y cambios de videoN;
  • aplica backoff exponencial a los errores recuperables;
  • aísla el ciclo de vida y los errores de cada cámara;
  • se ejecuta en primer plano y deja la supervisión del proceso a systemd.

Actualmente no:

  • modifica frames: el único procesador es passthrough;
  • convierte formatos, resoluciones ni frecuencias entre entrada y salida;
  • reenvía el audio USB;
  • oculta automáticamente la cámara física;
  • acepta cámaras desconocidas mediante heurísticas;
  • permite elegir perfiles distintos para dos unidades idénticas del mismo modelo.

La corrección actual consiste en obligar a la cámara a utilizar un perfil que ha demostrado ser estable y publicar exactamente ese mismo perfil.

Cámaras soportadas

La compatibilidad con la Trust Trino HD Video Webcam está integrada en el binario y se activa por defecto. Por tanto, camera-fixd puede reconocer y corregir esta cámara incluso aunque no exista un archivo de configuración externo. Es posible añadir otros modelos mediante config.toml o desactivar la definición integrada con disable_builtin_cameras = true.

Modelo público Identificación observada Perfil Procesamiento
Trust Trino HD Video Webcam USB 0c45:6340, fabricante Sonix, producto USB 2.0 Camera, tarjeta USB 2.0 Camera: HD 720P Webcam, interfaz 00, índice V4L2 0 YUYV 1280×720 a 10 fps, 4 buffers passthrough

La Trust Trino está compilada dentro del binario y funciona aunque no exista un archivo de configuración externo. La unidad utilizada durante el desarrollo:

  • no anuncia número de serie USB;
  • expone vídeo en el índice V4L2 0;
  • expone metadatos UVC en otro nodo con índice 1;
  • expone el micrófono mediante interfaces USB de audio separadas.

El nombre Trust no aparece en los descriptores USB de esta unidad. Por eso la definición combina VID/PID, fabricante, producto, nombre de tarjeta, interfaz e índice V4L2.

Conceptos importantes

Modelo, unidad e identidad

Un modelo es una entrada [[cameras]] del registro. Contiene selectores de hardware y un perfil.

Una unidad es una cámara física concreta conectada al ordenador. Puede haber varias unidades del mismo modelo.

La identidad estable se calcula así:

  1. Se utiliza el número de serie USB cuando existe.
  2. Si no existe, se utiliza la ruta de topología USB formada por controlador y puerto físico.
  3. Si varios dispositivos anuncian por error la misma serie, se añade la topología para desambiguarlos.

Esta identidad sobrevive a cambios como /dev/video1/dev/video5. Si una cámara sin serie se mueve a otro puerto USB, vuelve a ser reconocida como modelo compatible, pero se considera otra instancia porque el hardware no proporciona una identidad mejor.

Nodo V4L2 e índice V4L2

/dev/videoN es un nombre asignado dinámicamente por Linux. Puede cambiar en cualquier reinicio o reconexión y no debe copiarse a la configuración.

El campo video_index es diferente: corresponde al atributo index que expone el dispositivo en sysfs. Sirve para diferenciar, por ejemplo, el nodo de vídeo de un nodo de metadatos perteneciente a la misma cámara.

cat /sys/class/video4linux/videoN/index

Cámara virtual y nombre (Fixed)

Cada unidad compatible obtiene una cámara v4l2loopback. Su etiqueta se deriva de display_name, incluye un hash corto de la identidad y termina en (Fixed):

Trust Trino HD 1264dd1f (Fixed)

V4L2 limita la etiqueta a 31 bytes. Los nombres largos se recortan para reservar espacio al hash y a (Fixed). El hash evita que dos unidades iguales escriban en la misma salida.

Selecciona siempre la cámara por el nombre terminado en (Fixed), no por el /dev/videoN observado en ese momento.

Capacidades exclusivas

Las salidas se crean con exclusive_caps=1. Antes de que camera-fixd abra una salida, esta anuncia capacidad de escritura (OUTPUT). Cuando el productor está activo, anuncia capacidad de captura (CAPTURE) y las aplicaciones WebRTC la ven como una webcam normal.

Instalación rápida

En CachyOS, donde el kernel ya puede incluir v4l2loopback:

sudo pacman -S --needed rust make pkgconf clang v4l-utils v4l2loopback-utils
make list
make install
systemctl status camera-fixd
journalctl -u camera-fixd -n 50 --no-pager

Si modinfo v4l2loopback falla, instala también el módulo DKMS y las cabeceras correspondientes a tu kernel; consulta Dependencias.

Después abre OBS, Discord, Chrome u otra aplicación y selecciona la cámara cuyo nombre termina en (Fixed).

Dependencias

Dependencias en ejecución

  • Linux con V4L2, sysfs y udev.
  • systemd para la instalación y supervisión proporcionadas por el Makefile.
  • Módulo del kernel v4l2loopback con administración dinámica.
  • v4l2loopback-ctl para crear y eliminar dispositivos virtuales.
  • Permiso de lectura y escritura sobre las cámaras físicas.
  • Permiso sobre /dev/v4l2loopback; el instalador proporciona una regla udev.

v4l-utils no es usado por el servicio, pero proporciona v4l2-ctl, herramienta fundamental para diagnosticar cámaras y descubrir perfiles.

Dependencias para compilar

  • Rust 1.85 o posterior —la edición 2024 requiere como mínimo esa versión— y Cargo.
  • make.
  • pkg-config o pkgconf.
  • Cabeceras y biblioteca de desarrollo de libudev.
  • Clang/libclang, requerido al compilar las bindings V4L2.

CachyOS

Primero comprueba si el kernel ya contiene el módulo:

modinfo v4l2loopback

Si devuelve la información del módulo, instala únicamente herramientas y dependencias de compilación:

sudo pacman -S --needed rust make pkgconf clang v4l-utils v4l2loopback-utils

No instales otro módulo DKMS si tu kernel ya proporciona uno compatible, salvo que sepas que necesitas sustituirlo.

Arch Linux y derivados sin módulo integrado

Instala las cabeceras que correspondan exactamente al kernel utilizado. Para el kernel estándar de Arch:

sudo pacman -S --needed base-devel rust clang pkgconf v4l-utils \
  linux-headers v4l2loopback-dkms v4l2loopback-utils

Con linux-lts, linux-zen u otro kernel, sustituye linux-headers por sus cabeceras correspondientes.

Comprueba el resultado:

dkms status
modinfo v4l2loopback
v4l2loopback-ctl --version

Debian y Ubuntu

Los nombres habituales de los paquetes son:

sudo apt update
sudo apt install cargo rustc make pkg-config clang libclang-dev libudev-dev \
  v4l-utils v4l2loopback-dkms v4l2loopback-utils \
  "linux-headers-$(uname -r)"

Comprueba rustc --version: las versiones estables antiguas de Debian o Ubuntu pueden no ser suficientes. En ese caso utiliza un toolchain estable más reciente proporcionado por la distribución o por rustup.

En sistemas con Secure Boot, un módulo DKMS puede necesitar firma o autorización MOK antes de que el kernel permita cargarlo. Si modprobe v4l2loopback devuelve un error de clave o firma, resuelve primero la política de Secure Boot de tu distribución.

Otras distribuciones

Instala los equivalentes de:

Rust/Cargo
make
pkg-config
libudev (desarrollo)
Clang/libclang
v4l-utils
v4l2loopback (módulo compatible con el kernel)
v4l2loopback-ctl

La instalación incluida presupone systemd y las rutas habituales /etc y /usr/local.

Comprobaciones antes de instalar

Ejecuta estas comprobaciones desde el repositorio:

rustc --version
cargo --version
make --version
pkg-config --modversion libudev
command -v clang
command -v v4l2-ctl
command -v v4l2loopback-ctl
modinfo v4l2loopback

Comprueba las cámaras visibles:

v4l2-ctl --list-devices
ls -l /dev/v4l/by-id 2>/dev/null
ls -l /dev/video* 2>/dev/null

Después utiliza el diagnóstico del propio proyecto:

make list

make list no crea dispositivos virtuales ni modifica el sistema. Muestra:

  • nodo V4L2 actual;
  • nombre de tarjeta y driver;
  • capacidades de captura, salida y metadatos;
  • índice V4L2;
  • VID/PID USB;
  • modelo compatible, si lo hay;
  • identidad estable calculada;
  • etiqueta virtual propuesta.

Para la Trust Trino se espera un resultado parecido a:

/dev/video0 | USB 2.0 Camera: HD 720P Webcam | ... | index=0 | usb=0c45:6340 | supported (trust-trino-hd720p)
/dev/video2 | USB 2.0 Camera: HD 720P Webcam | ... | metadata=true | index=1 | ... | not supported
association | usb:0c45:6340:path:... | /dev/video0 -> Trust Trino HD ... (Fixed)

Los números concretos pueden ser diferentes y no tienen importancia.

Instalación detallada

1. Compilar y verificar

make check
make build

make check ejecuta formato, Clippy estricto y tests. make build genera target/release/camera-fixd usando Cargo.lock.

2. Instalar

make install

El Makefile usa sudo únicamente para operaciones del sistema. Si utilizas otro elevador:

make install SUDO=doas

Antes de modificar el sistema, el objetivo comprueba que exista v4l2loopback-ctl. Si falta, termina con un mensaje claro.

3. Cambios realizados por make install

La instalación:

  1. Compila el binario release.
  2. Crea el grupo de sistema camera-fixd si no existe.
  3. Crea el usuario de sistema camera-fixd si no existe.
  4. Instala el binario en /usr/local/bin/camera-fixd.
  5. Instala esta documentación en /usr/local/share/doc/camera-fixd/.
  6. Instala /etc/systemd/system/camera-fixd.service.
  7. Instala /etc/modprobe.d/camera-fixd.conf.
  8. Instala /etc/modules-load.d/camera-fixd.conf.
  9. Instala /etc/udev/rules.d/70-camera-fixd-v4l2loopback.rules.
  10. Crea /etc/camera-fixd/config.toml desde config.example.toml solo si el archivo no existía.
  11. Carga v4l2loopback.
  12. Ajusta el grupo y modo del nodo de control dinámico.
  13. habilita e inicia camera-fixd.service.

Una configuración existente nunca es sobrescrita por make install.

4. Configuración persistente de v4l2loopback

El módulo queda configurado así:

options v4l2loopback devices=0

No se reserva un /dev/videoN fijo. El servicio crea dinámicamente una salida con exclusive_caps=1 por cada unidad compatible.

Las opciones de modprobe no cambian un módulo que ya estaba cargado. Si antes existía una cámara virtual fija, como /dev/video62 con nombre webcam_proxy, puede seguir visible hasta el próximo reinicio. Es inofensiva: camera-fixd no la utiliza si su etiqueta no coincide con una salida administrada.

No descargues v4l2loopback a la fuerza si OBS u otro programa está usando una cámara virtual.

Primera puesta en marcha

Comprueba la unidad:

systemctl status camera-fixd
systemctl is-enabled camera-fixd
systemctl is-active camera-fixd

Consulta el arranque:

journalctl -u camera-fixd -b -n 100 --no-pager

Para una cámara reconocida deben aparecer mensajes equivalentes a:

compatible physical camera detected
virtual camera ready
camera processing worker started
stream profile configured

El último mensaje muestra nodo físico, nodo virtual, FOURCC, resolución y FPS negociados.

Lista los dispositivos:

v4l2-ctl --list-devices

En tu aplicación selecciona el nombre terminado en (Fixed). No configures la aplicación contra un /dev/videoN concreto.

Prueba de reconexión

Mantén los logs abiertos:

journalctl -u camera-fixd -f

Desconecta y reconecta la cámara. El servicio debe registrar desconexión, reconexión, nueva resolución del nodo y reinicio del worker sin afectar a otras cámaras.

Interfaz de línea de comandos

La sintaxis disponible es:

camera-fixd [OPTIONS] [COMMAND]

Opciones globales

Opción Descripción
--config <ruta> Usa un archivo TOML concreto. Si se proporciona explícitamente, debe existir.
CAMERA_FIXD_CONFIG=<ruta> Alternativa mediante variable de entorno a --config.
--log-filter <filtro> Filtro con sintaxis compatible con tracing/EnvFilter. Por defecto: camera_fixd=info.
--help Muestra ayuda.
--version Muestra la versión.

Coloca las opciones globales antes del subcomando:

camera-fixd --config ./mi-config.toml --log-filter camera_fixd=debug list

run

Ejecuta el supervisor en primer plano:

camera-fixd run

Es también el comando predeterminado, por lo que camera-fixd y camera-fixd run son equivalentes. No se convierte en daemon; systemd se ocupa del segundo plano, reinicio del proceso y journal.

SIGINT y SIGTERM solicitan un cierre limpio: se detienen y esperan todos los workers y se liberan los streams.

list

Realiza descubrimiento y matching sin crear cámaras virtuales:

camera-fixd list

Con la configuración instalada, usa:

sudo /usr/local/bin/camera-fixd \
  --config /etc/camera-fixd/config.toml \
  list

Se utiliza sudo en este ejemplo para poder leer tanto la configuración protegida como cualquier nodo físico con permisos restrictivos. El comando no modifica dispositivos.

validate-config

Carga, fusiona y valida toda la configuración sin abrir hardware:

camera-fixd --config ./mi-config.toml validate-config

Para validar el archivo instalado con los mismos permisos de lectura que el servicio:

sudo -u camera-fixd /usr/local/bin/camera-fixd \
  --config /etc/camera-fixd/config.toml \
  validate-config

print-default-config

Imprime el ejemplo compilado, incluida la definición integrada de la Trino:

camera-fixd print-default-config

Puede guardarse manualmente redirigiendo la salida a un archivo de usuario. El programa no modifica /etc con este subcomando.

Configuración completa

La ruta predeterminada es:

/etc/camera-fixd/config.toml

Si no se pasa --config y esa ruta no existe, el programa utiliza los valores y modelos integrados. Si se pasa una ruta explícita y no existe, el inicio falla.

La referencia editable está en config.example.toml.

Flujo seguro para editar

  1. Crea una copia de seguridad:

    sudo cp -a /etc/camera-fixd/config.toml \
      /etc/camera-fixd/config.toml.backup
  2. Edita:

    sudoedit /etc/camera-fixd/config.toml
  3. Valida sin reiniciar:

    sudo -u camera-fixd /usr/local/bin/camera-fixd \
      --config /etc/camera-fixd/config.toml \
      validate-config
  4. Comprueba matching y perfiles:

    sudo /usr/local/bin/camera-fixd \
      --config /etc/camera-fixd/config.toml \
      list
  5. Aplica solo si ambas comprobaciones terminan bien:

    sudo systemctl restart camera-fixd
    journalctl -u camera-fixd -n 100 --no-pager

Registro integrado y fusión

La opción raíz es:

disable_builtin_cameras = false

Comportamiento:

  • false o ausente: carga las cámaras integradas y después las externas;
  • una cámara externa con un id nuevo amplía el registro;
  • una cámara externa con el mismo id sustituye por completo la integrada;
  • true: elimina primero todas las cámaras integradas.

Después de la fusión debe quedar al menos un modelo. Un archivo con disable_builtin_cameras = true y sin [[cameras]] es inválido.

Opciones de runtime

Todos los tiempos se expresan en milisegundos.

Campo Predeterminado Rango válido Función
scan_interval_ms 1000 100–60000 Intervalo máximo entre reconciliaciones periódicas. Los eventos udev pueden despertar antes al supervisor.
disconnect_grace_ms 5000 0–3600000 Tiempo que se conserva una asociación desconectada antes de retirarla.
retry_initial_ms 1000 100–3600000 Primer retardo tras un error.
retry_max_ms 30000 100–3600000 Límite del backoff exponencial. Debe ser mayor o igual que retry_initial_ms.
retry_reset_after_ms 60000 1000–86400000 Tiempo funcionando de forma estable antes de reiniciar el contador de fallos.
virtual_device_wait_ms 3000 100–60000 Tiempo máximo para que aparezca una virtual recién creada.
remove_virtual_on_disconnect true booleano Elimina la virtual al expirar la gracia y durante el cierre limpio. Con false, la conserva para reutilizarla.

Ejemplo:

[runtime]
scan_interval_ms = 1000
disconnect_grace_ms = 5000
retry_initial_ms = 1000
retry_max_ms = 30000
retry_reset_after_ms = 60000
virtual_device_wait_ms = 3000
remove_virtual_on_disconnect = true

No reduzcas los tiempos para ocultar un problema de hardware. Valores muy bajos generan más aperturas, reintentos y eventos; el backoff ya proporciona una recuperación progresiva.

Referencia de [[cameras]]

Cada bloque registra un modelo.

Campo Obligatorio Predeterminado Descripción
id ID interno único. Solo letras y dígitos ASCII, - y _. Una entrada externa con el mismo ID reemplaza la integrada.
display_name Nombre público usado como base de la cámara (Fixed). No puede estar vacío.
usb_vendor_id Vendor ID hexadecimal como string, por ejemplo "0c45" o "0x0c45".
usb_product_id Product ID hexadecimal como string.
usb_manufacturer No cualquiera Coincidencia exacta con el fabricante USB de sysfs.
usb_product No cualquiera Coincidencia exacta con el producto USB de sysfs.
v4l2_card_names No cualquier nombre Lista de nombres V4L2 exactos admitidos. Una lista vacía actúa como comodín.
interface_number No cualquier interfaz Entero correspondiente a la interfaz USB; por ejemplo, bInterfaceNumber=00 se configura como 0.
video_index No 0 Atributo sysfs index del nodo V4L2 que se debe capturar.
processing No "passthrough" Procesador. Actualmente solo existe passthrough.
profile Tabla con formato, tamaño, FPS y buffers.

Los strings de fabricante, producto y tarjeta se comparan de forma exacta, incluidas mayúsculas, espacios y puntuación. Cópialos desde sysfs/V4L2.

Cuantos más selectores estables proporciones, menor será el riesgo de aceptar otra cámara que comparta VID/PID. No utilices el número de bus o de dispositivo mostrado por lsusb; cambia en cada conexión.

Referencia de [cameras.profile]

Campo Obligatorio Predeterminado Validación
pixel_format FOURCC de exactamente cuatro bytes ASCII imprimibles, por ejemplo YUYV.
width Anchura mayor que cero.
height Altura mayor que cero.
frames_per_second FPS entero mayor que cero. La negociación debe devolver una fracción equivalente exacta.
buffer_count No 4 Entre 2 y 32 buffers MMAP.

El programa solicita el perfil a la cámara física y comprueba lo que devuelve el driver. Hace lo mismo con v4l2loopback. Si cualquiera sustituye formato, resolución o FPS por otro valor, el worker termina con un error de configuración y reintenta con backoff.

buffer_count controla cuántos buffers MMAP se reservan en la entrada. Más buffers consumen más memoria y no solucionan un perfil USB inestable por sí solos. Usa 4 salvo que hayas medido y documentado otra necesidad.

Selectores ambiguos

La validación rechaza dos modelos que puedan coincidir con el mismo dispositivo. Se consideran comodines los selectores opcionales ausentes y una lista vacía de nombres V4L2.

Si dos modelos comparten VID/PID, diferéncialos mediante fabricante, producto o nombres de tarjeta que no se solapen. No intentes resolver la ambigüedad mediante el orden de los bloques: el programa no elige arbitrariamente el primero.

Perfiles y varias cámaras

Varios modelos con perfiles distintos

Cada modelo tiene su propia tabla [cameras.profile]. El supervisor puede ejecutarlos simultáneamente:

disable_builtin_cameras = false

[[cameras]]
id = "camera-model-a"
display_name = "Camera Model A"
usb_vendor_id = "1234"
usb_product_id = "0001"
v4l2_card_names = ["Camera Model A"]
interface_number = 0
video_index = 0
processing = "passthrough"

[cameras.profile]
pixel_format = "YUYV"
width = 1280
height = 720
frames_per_second = 30
buffer_count = 4

[[cameras]]
id = "camera-model-b"
display_name = "Camera Model B"
usb_vendor_id = "5678"
usb_product_id = "0002"
v4l2_card_names = ["Camera Model B"]
interface_number = 0
video_index = 0
processing = "passthrough"

[cameras.profile]
pixel_format = "YUYV"
width = 1920
height = 1080
frames_per_second = 15
buffer_count = 6

Resultado conceptual:

Camera Model A -> YUYV 1280x720@30 -> Camera Model A ... (Fixed)
Camera Model B -> YUYV 1920x1080@15 -> Camera Model B ... (Fixed)

Cada asociación mantiene worker, salida, errores, reintentos y desconexión de forma independiente.

Varias unidades del mismo modelo

Una sola definición admite varias unidades físicas del mismo modelo:

Camera Model A, unidad 1 -> Camera Model A <hash-1> (Fixed)
Camera Model A, unidad 2 -> Camera Model A <hash-2> (Fixed)

Todas utilizan el perfil del modelo, pero tienen identidad, nodo virtual y worker independientes.

Unidades idénticas con perfiles diferentes

No está soportado actualmente. La configuración selecciona por modelo y no tiene selectores por serie o topología para asignar perfiles por unidad. Dos entradas que puedan reconocer el mismo hardware son rechazadas como ambiguas.

Si necesitas que dos unidades idénticas usen perfiles distintos, hay que ampliar el esquema y el matching con selectores por instancia. No intentes usar /dev/videoN para diferenciarlas.

Perfil de entrada y salida

El perfil registrado se utiliza tanto para la entrada física como para la salida virtual:

YUYV 1280x720@10 -> passthrough -> YUYV 1280x720@10

No existe una configuración separada de entrada y salida. Casos como estos requieren desarrollo adicional:

MJPG -> YUYV
1920x1080 -> 1280x720
30 fps -> 15 fps
rotación, recorte, espejo o reparación de píxeles

El pipeline actual espera captura V4L2 de un solo plano (VIDEO_CAPTURE). Un dispositivo exclusivamente multiplanar no puede procesarse sin ampliar el backend de streaming.

Tutorial para añadir una cámara nueva

Este procedimiento está pensado para una cámara cuya corrección consiste en forzar un perfil y copiar sus frames sin transformación.

1. Trabajar sin competir con el servicio

Si la cámara pudiera coincidir con una definición existente, detén temporalmente el servicio antes de probar perfiles:

sudo systemctl stop camera-fixd

Recuerda iniciarlo al terminar:

sudo systemctl start camera-fixd

2. Localizar todos los nodos

v4l2-ctl --list-devices
ls -l /dev/v4l/by-id 2>/dev/null
ls -l /dev/v4l/by-path 2>/dev/null

Una webcam puede exponer varios /dev/videoN. No asumas que el primero es vídeo ni que todos sirven para capturar imágenes.

Para cada candidato:

v4l2-ctl --device=/dev/videoN --all
cat /sys/class/video4linux/videoN/index

Elige un nodo que anuncie Video Capture de un solo plano. Descarta nodos que solo anuncien Metadata Capture.

3. Recopilar identificación estable

Obtén VID/PID y descriptores:

lsusb
udevadm info --query=property --name=/dev/videoN | sort
udevadm info --attribute-walk --name=/dev/videoN

Busca y anota:

ID_VENDOR_ID / ATTRS{idVendor}
ID_MODEL_ID / ATTRS{idProduct}
ID_USB_VENDOR o ATTRS{manufacturer}
ID_USB_MODEL o ATTRS{product}
ID_V4L_PRODUCT o ATTR{name}
ATTR{index}
ATTRS{bInterfaceNumber}
serial, si existe

Usa valores del dispositivo USB de la cámara, no de un hub o del controlador PCI situado más arriba en --attribute-walk.

También puedes ver la identidad V4L2 con:

v4l2-ctl --device=/dev/videoN --info

4. Enumerar perfiles ofrecidos

v4l2-ctl --device=/dev/videoN --list-formats-ext

La salida relaciona cada FOURCC y resolución con los intervalos/FPS admitidos. No inventes combinaciones que el dispositivo no enumera.

Ejemplo conceptual:

'YUYV' 1280x720 Interval 0.100s (10.000 fps)
'MJPG' 1280x720 Interval 0.033s (30.000 fps)

Que un perfil aparezca en la lista no garantiza que sea fiable. Prueba el que quieras registrar durante suficientes frames.

5. Probar el perfil físicamente

Este ejemplo solicita YUYV 1280x720@10, captura mediante MMAP y descarta 300 frames:

v4l2-ctl --device=/dev/videoN \
  --set-fmt-video=width=1280,height=720,pixelformat=YUYV \
  --set-parm=10 \
  --stream-mmap=4 \
  --stream-count=300 \
  --stream-to=/dev/null \
  --verbose

Si el usuario actual no tiene acceso al nodo, ejecuta la prueba con sudo.

Comprueba después el valor realmente negociado:

v4l2-ctl --device=/dev/videoN --get-fmt-video
v4l2-ctl --device=/dev/videoN --get-parm

Repite conexiones, puertos y pruebas largas si el problema original era intermitente. Un perfil solo debe registrarse después de demostrar estabilidad.

6. Crear la definición TOML

Edita /etc/camera-fixd/config.toml y añade:

[[cameras]]
id = "fabricante-modelo-720p"
display_name = "Fabricante Modelo Webcam"
usb_vendor_id = "1234"
usb_product_id = "5678"
usb_manufacturer = "Fabricante USB exacto"
usb_product = "Producto USB exacto"
v4l2_card_names = ["Nombre V4L2 exacto"]
interface_number = 0
video_index = 0
processing = "passthrough"

[cameras.profile]
pixel_format = "YUYV"
width = 1280
height = 720
frames_per_second = 10
buffer_count = 4

Recomendaciones:

  • usa un id estable y descriptivo, no una ruta de dispositivo;
  • usa como display_name el nombre comercial que el usuario reconocerá;
  • conserva VID/PID como cuatro dígitos hexadecimales;
  • incluye fabricante, producto y tarjeta si son fiables;
  • registra el índice del nodo de captura, no el número N de videoN;
  • mantén buffer_count = 4 salvo evidencia que justifique cambiarlo;
  • usa únicamente processing = "passthrough" en la implementación actual.

7. Validar antes de reiniciar

sudo -u camera-fixd /usr/local/bin/camera-fixd \
  --config /etc/camera-fixd/config.toml \
  validate-config

sudo /usr/local/bin/camera-fixd \
  --config /etc/camera-fixd/config.toml \
  list

La cámara debe aparecer como:

supported (fabricante-modelo-720p)

Si aparece not supported, compara uno por uno VID/PID, fabricante, producto, tarjeta, interfaz e índice.

8. Arrancar y verificar el flujo completo

sudo systemctl restart camera-fixd
journalctl -u camera-fixd -f

Verifica:

  1. detección física;
  2. identidad estable;
  3. nodo seleccionado;
  4. creación virtual;
  5. asociación física/virtual;
  6. perfil negociado;
  7. imagen en una aplicación consumidora;
  8. desconexión y reconexión;
  9. funcionamiento simultáneo con las demás cámaras.

9. Añadir soporte integrado al repositorio

Una definición externa es suficiente para uso local. Para distribuir soporte predeterminado en el proyecto:

  1. añade la definición integrada en src/config.rs;
  2. mantenla como datos, sin introducir condiciones específicas en descubrimiento o supervisor;
  3. añade tests de matching en src/discovery.rs;
  4. añade tests de validación si introduces casos nuevos;
  5. actualiza la tabla de cámaras soportadas;
  6. ejecuta make check;
  7. documenta qué unidad y perfil se verificaron físicamente.

10. Cuándo la configuración no basta

Si la corrección requiere modificar frames, no declares falsamente passthrough. Será necesario:

  • añadir un modo a ProcessingKind;
  • implementar un FrameProcessor en src/processing.rs;
  • seleccionar el procesador en create_processor;
  • añadir pruebas deterministas;
  • si cambia el formato, separar perfiles de entrada y salida y adaptar src/stream.rs.

Esos cambios amplían el programa; no pueden expresarse con el esquema TOML actual.

Permisos y cámara física

Cuenta del servicio

La unidad se ejecuta con:

User=camera-fixd
Group=camera-fixd
SupplementaryGroups=video

Los nodos V4L2 suelen pertenecer al grupo video. La unidad añade ese grupo en tiempo de ejecución sin añadir permanentemente la cuenta a /etc/group.

Crear y eliminar dispositivos dinámicos requiere CAP_SYS_ADMIN en v4l2loopback. La unidad limita su conjunto de capacidades a esa capacidad y aplica endurecimiento systemd. Una regla udev concede al grupo camera-fixd acceso a /dev/v4l2loopback:

SUBSYSTEM=="misc", KERNEL=="v4l2loopback", GROUP="camera-fixd", MODE="0660"

El binario nunca ejecuta sudo ni solicita contraseña.

La cámara física sigue apareciendo

Esto es normal. El servicio necesita mantener abierto el nodo físico para leer frames. v4l2loopback solo controla la salida virtual.

Para impedir que aplicaciones de escritorio abran la física, puede instalarse una regla udev específica del modelo:

SUBSYSTEM=="video4linux", KERNEL=="video*", ATTR{index}=="0", \
  ATTRS{idVendor}=="0c45", ATTRS{idProduct}=="6340", \
  GROUP="camera-fixd", MODE="0660", TAG-="uaccess"

Antes de instalarla:

  1. sustituye VID/PID por los correctos;
  2. comprueba que esos IDs no pertenecen a otra cámara;
  3. añade selectores del mismo padre USB si necesitas mayor precisión;
  4. no uses /dev/videoN;
  5. no retires uaccess al nodo virtual.

Después recarga reglas y reconecta la cámara:

sudo udevadm control --reload-rules

En escritorios con PipeWire o portales puede ser necesario restringir también la fuente física allí. Ocultar la física es una política del sistema, no una función automática de camera-fixd.

Desconexiones, reintentos y recuperación

El supervisor combina eventos udev con un escaneo completo periódico. Si udev no puede inicializarse o falla, los escaneos periódicos continúan activos.

Cada cámara tiene estado independiente:

  1. Al aparecer, se identifica y se resuelve su nodo actual.
  2. Se crea o reutiliza una virtual con etiqueta estable.
  3. Se lanza su worker.
  4. Si el nodo cambia, se detiene el worker anterior y se inicia otro con la ruta nueva.
  5. Si desaparece, se solicita parada inmediata del worker.
  6. La asociación se conserva durante disconnect_grace_ms.
  7. Si reaparece con la misma identidad, se recupera.
  8. Si expira la gracia, se retira la asociación y, por defecto, la virtual.

Los fallos de stream, apertura, procesamiento o creación virtual usan backoff:

1 s -> 2 s -> 4 s -> 8 s -> ... -> 30 s máximo

Los valores concretos dependen de la configuración. Tras funcionar durante retry_reset_after_ms, el contador vuelve al inicio.

Los comandos auxiliares modprobe y v4l2loopback-ctl tienen un timeout interno de 10 segundos y se recolectan para no dejar procesos huérfanos.

Un error de una cámara no termina los workers de las demás.

Logging y observabilidad

El servicio registra como mínimo:

  • modelo e identidad de cada física detectada;
  • VID/PID y nodo físico actual;
  • etiqueta y nodo virtual;
  • asociación física/virtual;
  • perfil negociado;
  • inicio y fin del worker;
  • desconexión, reconexión y cambio de ruta;
  • clasificación del error;
  • retardo antes del próximo intento;
  • errores que requieren intervención manual.

No se genera un log por frame. Los avisos de descubrimiento idénticos se deduplican y se informa cuando desaparecen.

Comandos habituales:

journalctl -u camera-fixd -f
journalctl -u camera-fixd -b --no-pager
journalctl -u camera-fixd --since "10 minutes ago"

Para elevar el detalle durante una ejecución manual:

camera-fixd --log-filter camera_fixd=debug run

Campos útiles:

  • camera_id: identidad estable calculada;
  • model: ID de la definición;
  • physical_node y virtual_node: rutas actuales, solo para diagnóstico;
  • error_class: device_gone, configuration, device_protocol, processing, io o worker_panic;
  • retry_ms: espera hasta el siguiente intento;
  • requires_intervention=true: falta una herramienta, permiso o configuración que probablemente necesita acción administrativa.

Resolución de problemas

v4l2loopback-ctl is required

El módulo y la utilidad son componentes distintos. Instala las utilidades:

# CachyOS/Arch
sudo pacman -S v4l2loopback-utils

# Debian/Ubuntu
sudo apt install v4l2loopback-utils

Verifica:

v4l2loopback-ctl --version

modprobe: FATAL: Module v4l2loopback not found

El kernel no contiene el módulo y DKMS no lo ha construido para el kernel actual. Comprueba:

uname -r
dkms status
modinfo v4l2loopback

Instala el módulo y las cabeceras exactas de ese kernel. Reinicia si acabas de cambiar de kernel.

El módulo no carga con Secure Boot

Busca errores:

journalctl -k -b | grep -i -E 'v4l2loopback|module|key|signature'

Firma o autoriza el módulo DKMS siguiendo el procedimiento de tu distribución.

No se puede abrir /dev/v4l2loopback

Comprueba:

ls -l /dev/v4l2loopback
getent group camera-fixd
systemctl cat camera-fixd

Reinstala la regla y recarga udev mediante make install. La unidad necesita CAP_SYS_ADMIN además del permiso del nodo.

could not probe /dev/videoN: Permission denied

Comprueba el nodo y los grupos:

ls -l /dev/videoN
getent group video
systemctl show camera-fixd -p User -p Group -p SupplementaryGroups

Puede aparecer brevemente durante una reconexión antes de que udev termine de aplicar permisos. Si el aviso se limpia y después aparece compatible physical camera detected, la recuperación ha funcionado.

Si persiste, corrige la regla udev o los grupos. No hagas el nodo globalmente escribible con chmod 666.

La cámara aparece como not supported

Ejecuta:

sudo /usr/local/bin/camera-fixd \
  --config /etc/camera-fixd/config.toml \
  list

Compara los selectores exactos con:

udevadm info --query=property --name=/dev/videoN | sort
cat /sys/class/video4linux/videoN/index
v4l2-ctl --device=/dev/videoN --info

Comprueba especialmente espacios, puntuación, interfaz e índice.

overlapping device selectors

Dos modelos podrían reconocer el mismo nodo. Añade selectores estables que los diferencien o elimina la entrada duplicada. Cambiar el orden del TOML no lo resuelve.

La física es detectada pero el perfil se rechaza

Busca en los logs el perfil solicitado y el negociado. Confirma:

v4l2-ctl --device=/dev/videoN --list-formats-ext
v4l2-ctl --device=/dev/videoN --get-fmt-video
v4l2-ctl --device=/dev/videoN --get-parm

El FOURCC, resolución y FPS deben coincidir exactamente con la definición. Para FPS se acepta una fracción equivalente, como 20/2 para 10 fps.

Device or resource busy

Otra aplicación tiene abierta la cámara física o virtual. Cierra OBS, navegador, Discord y cualquier proceso de cámara. Puedes investigar con:

fuser -v /dev/videoN

No ejecutes simultáneamente make dev y camera-fixd.service.

La virtual no aparece en Chrome, Discord o una aplicación WebRTC

  1. Confirma que el worker está activo en el journal.

  2. Comprueba que la virtual anuncia captura mientras el productor está abierto:

    v4l2-ctl --device=/dev/videoN --all
  3. Comprueba que la salida fue creada con capacidades exclusivas.

  4. Reinicia la aplicación consumidora; algunas enumeran cámaras solo al iniciar.

  5. Revisa PipeWire y el portal del escritorio si la aplicación no accede directamente a V4L2.

La cámara virtual cambia de /dev/videoN

Es normal. El nombre y la identidad son estables; el número no. Selecciona la etiqueta (Fixed) en la aplicación.

El nombre virtual aparece recortado

Es el límite de 31 bytes de V4L2. El servicio conserva un fragmento reconocible, el hash de identidad y (Fixed).

Queda una virtual antigua como webcam_proxy

Probablemente el módulo ya estaba cargado con la configuración del proyecto anterior. La nueva opción devices=0 se aplicará en la próxima carga del módulo, normalmente tras reiniciar. Esa salida antigua no es utilizada por camera-fixd si su etiqueta no coincide.

La cámara sin serie cambia de nombre virtual al moverla de puerto

Es esperado. Sin serie USB, la topología es la única identidad disponible. La cámara sigue siendo reconocida, pero el nuevo puerto genera otra identidad y otro hash.

El servicio reinicia continuamente

Consulta el error completo:

systemctl status camera-fixd
journalctl -u camera-fixd -b -n 200 --no-pager

Valida la configuración manualmente. Un error general de configuración termina el proceso y systemd lo reinicia; un error de una cámara concreta se gestiona dentro del supervisor y no reinicia el servicio completo.

Desarrollo y comprobaciones

Objetivos disponibles:

make help
make build
make dev
make list
make test
make lint
make check
make install
make uninstall
Objetivo Función
make build Compila release con Cargo.lock.
make dev Compila release y ejecuta run en la terminal actual.
make list Ejecuta diagnóstico con la configuración predeterminada.
make test Ejecuta todos los tests sin exigir hardware real.
make lint Comprueba rustfmt y Clippy con warnings como errores.
make check Ejecuta lint y tests.
make install Instala y activa el servicio.
make uninstall Detiene y retira los archivos instalados.

make dev necesita acceso a cámaras, al nodo de control y la capacidad para administrar v4l2loopback. Para evitar competir con la instalación:

sudo systemctl stop camera-fixd
sudo ./target/release/camera-fixd \
  --config ./config.example.toml \
  --log-filter camera_fixd=debug \
  run

Finaliza con Ctrl-C y restaura el servicio:

sudo systemctl start camera-fixd

Los tests cubren configuración, matching, identidades, etiquetas, perfiles, backoff, aislamiento, cambios de nodo y ciclo de vida sin depender de una webcam. La prueba final de un modelo nuevo debe incluir hardware real.

La distribución interna se explica en ARCHITECTURE.md.

Actualizar y desinstalar

Actualizar

Conserva primero la configuración:

sudo cp -a /etc/camera-fixd/config.toml \
  /etc/camera-fixd/config.toml.backup

Después actualiza el código, verifica e instala:

make check
make install
sudo systemctl restart camera-fixd

make install no reemplaza un config.toml existente. El reinicio explícito garantiza que un servicio que ya estaba activo cargue el binario nuevo.

Valida el archivo porque una versión futura podría ampliar el esquema:

sudo -u camera-fixd /usr/local/bin/camera-fixd \
  --config /etc/camera-fixd/config.toml \
  validate-config

Desinstalar

make uninstall

La desinstalación:

  • deshabilita y detiene camera-fixd.service;
  • elimina binario, unidad, documentación, reglas propias y configuración del módulo;
  • recarga udev y systemd;
  • conserva /etc/camera-fixd/config.toml;
  • conserva usuario y grupo camera-fixd;
  • no descarga v4l2loopback;
  • no elimina recursos ajenos que puedan estar usando el módulo.

La conservación deliberada permite reinstalar sin perder perfiles y evita alterar recursos compartidos.

Limitaciones conocidas

  • Solo se actúa sobre modelos registrados explícitamente.
  • El único procesador disponible es passthrough.
  • Entrada y salida deben usar el mismo FOURCC, resolución y FPS.
  • El pipeline actual requiere captura V4L2 de un solo plano.
  • No hay selección de perfil por número de serie ni topología configurables; el perfil pertenece al modelo.
  • Todas las unidades del mismo modelo comparten perfil.
  • No se reenvía audio USB.
  • La cámara física permanece visible salvo que el administrador aplique una política udev/PipeWire adicional.
  • Una cámara sin serie cambia de identidad al moverla de puerto.
  • v4l2loopback sigue siendo un módulo genérico del kernel y debe ser compatible con el kernel instalado.
  • Las herramientas de instalación incluidas están orientadas a systemd y rutas tradicionales de Linux.

Estas limitaciones son explícitas: no deben ocultarse mediante selectores inestables, perfiles que el hardware no acepte o rutas /dev/videoN fijas.

About

Linux userspace daemon that stabilizes supported V4L2 webcams with known profiles and exposes them as virtual cameras via v4l2loopback.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages