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.
- Qué hace y qué no hace
- Cámaras soportadas
- Conceptos importantes
- Instalación rápida
- Dependencias
- Comprobaciones antes de instalar
- Instalación detallada
- Primera puesta en marcha
- Interfaz de línea de comandos
- Configuración completa
- Perfiles y varias cámaras
- Tutorial para añadir una cámara nueva
- Permisos y cámara física
- Desconexiones, reintentos y recuperación
- Logging y observabilidad
- Resolución de problemas
- Desarrollo y comprobaciones
- Actualizar y desinstalar
- Limitaciones conocidas
camera-fixd:
- reconoce cámaras mediante atributos USB, sysfs y V4L2;
- nunca usa un
/dev/videoNcomo 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.
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.
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í:
- Se utiliza el número de serie USB cuando existe.
- Si no existe, se utiliza la ruta de topología USB formada por controlador y puerto físico.
- 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.
/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/indexCada 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.
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.
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-pagerSi 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).
- Linux con V4L2, sysfs y udev.
- systemd para la instalación y supervisión proporcionadas por el Makefile.
- Módulo del kernel
v4l2loopbackcon administración dinámica. v4l2loopback-ctlpara 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.
- Rust 1.85 o posterior —la edición 2024 requiere como mínimo esa versión— y Cargo.
make.pkg-configopkgconf.- Cabeceras y biblioteca de desarrollo de
libudev. - Clang/libclang, requerido al compilar las bindings V4L2.
Primero comprueba si el kernel ya contiene el módulo:
modinfo v4l2loopbackSi 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-utilsNo instales otro módulo DKMS si tu kernel ya proporciona uno compatible, salvo que sepas que necesitas sustituirlo.
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-utilsCon linux-lts, linux-zen u otro kernel, sustituye linux-headers por sus
cabeceras correspondientes.
Comprueba el resultado:
dkms status
modinfo v4l2loopback
v4l2loopback-ctl --versionLos 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.
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.
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 v4l2loopbackComprueba las cámaras visibles:
v4l2-ctl --list-devices
ls -l /dev/v4l/by-id 2>/dev/null
ls -l /dev/video* 2>/dev/nullDespués utiliza el diagnóstico del propio proyecto:
make listmake 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.
make check
make buildmake check ejecuta formato, Clippy estricto y tests. make build genera
target/release/camera-fixd usando Cargo.lock.
make installEl Makefile usa sudo únicamente para operaciones del sistema. Si utilizas otro
elevador:
make install SUDO=doasAntes de modificar el sistema, el objetivo comprueba que exista
v4l2loopback-ctl. Si falta, termina con un mensaje claro.
La instalación:
- Compila el binario release.
- Crea el grupo de sistema
camera-fixdsi no existe. - Crea el usuario de sistema
camera-fixdsi no existe. - Instala el binario en
/usr/local/bin/camera-fixd. - Instala esta documentación en
/usr/local/share/doc/camera-fixd/. - Instala
/etc/systemd/system/camera-fixd.service. - Instala
/etc/modprobe.d/camera-fixd.conf. - Instala
/etc/modules-load.d/camera-fixd.conf. - Instala
/etc/udev/rules.d/70-camera-fixd-v4l2loopback.rules. - Crea
/etc/camera-fixd/config.tomldesdeconfig.example.tomlsolo si el archivo no existía. - Carga
v4l2loopback. - Ajusta el grupo y modo del nodo de control dinámico.
- habilita e inicia
camera-fixd.service.
Una configuración existente nunca es sobrescrita por make install.
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.
Comprueba la unidad:
systemctl status camera-fixd
systemctl is-enabled camera-fixd
systemctl is-active camera-fixdConsulta el arranque:
journalctl -u camera-fixd -b -n 100 --no-pagerPara 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-devicesEn tu aplicación selecciona el nombre terminado en (Fixed). No configures la
aplicación contra un /dev/videoN concreto.
Mantén los logs abiertos:
journalctl -u camera-fixd -fDesconecta 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.
La sintaxis disponible es:
camera-fixd [OPTIONS] [COMMAND]
| 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 listEjecuta el supervisor en primer plano:
camera-fixd runEs 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.
Realiza descubrimiento y matching sin crear cámaras virtuales:
camera-fixd listCon la configuración instalada, usa:
sudo /usr/local/bin/camera-fixd \
--config /etc/camera-fixd/config.toml \
listSe 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.
Carga, fusiona y valida toda la configuración sin abrir hardware:
camera-fixd --config ./mi-config.toml validate-configPara 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-configImprime el ejemplo compilado, incluida la definición integrada de la Trino:
camera-fixd print-default-configPuede guardarse manualmente redirigiendo la salida a un archivo de usuario. El
programa no modifica /etc con este subcomando.
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.
-
Crea una copia de seguridad:
sudo cp -a /etc/camera-fixd/config.toml \ /etc/camera-fixd/config.toml.backup
-
Edita:
sudoedit /etc/camera-fixd/config.toml
-
Valida sin reiniciar:
sudo -u camera-fixd /usr/local/bin/camera-fixd \ --config /etc/camera-fixd/config.toml \ validate-config
-
Comprueba matching y perfiles:
sudo /usr/local/bin/camera-fixd \ --config /etc/camera-fixd/config.toml \ list
-
Aplica solo si ambas comprobaciones terminan bien:
sudo systemctl restart camera-fixd journalctl -u camera-fixd -n 100 --no-pager
La opción raíz es:
disable_builtin_cameras = falseComportamiento:
falseo ausente: carga las cámaras integradas y después las externas;- una cámara externa con un
idnuevo amplía el registro; - una cámara externa con el mismo
idsustituye 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.
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 = trueNo 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.
Cada bloque registra un modelo.
| Campo | Obligatorio | Predeterminado | Descripción |
|---|---|---|---|
id |
Sí | — | ID interno único. Solo letras y dígitos ASCII, - y _. Una entrada externa con el mismo ID reemplaza la integrada. |
display_name |
Sí | — | Nombre público usado como base de la cámara (Fixed). No puede estar vacío. |
usb_vendor_id |
Sí | — | Vendor ID hexadecimal como string, por ejemplo "0c45" o "0x0c45". |
usb_product_id |
Sí | — | 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 |
Sí | — | 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.
| Campo | Obligatorio | Predeterminado | Validación |
|---|---|---|---|
pixel_format |
Sí | — | FOURCC de exactamente cuatro bytes ASCII imprimibles, por ejemplo YUYV. |
width |
Sí | — | Anchura mayor que cero. |
height |
Sí | — | Altura mayor que cero. |
frames_per_second |
Sí | — | 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.
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.
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 = 6Resultado 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.
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.
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.
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.
Este procedimiento está pensado para una cámara cuya corrección consiste en forzar un perfil y copiar sus frames sin transformación.
Si la cámara pudiera coincidir con una definición existente, detén temporalmente el servicio antes de probar perfiles:
sudo systemctl stop camera-fixdRecuerda iniciarlo al terminar:
sudo systemctl start camera-fixdv4l2-ctl --list-devices
ls -l /dev/v4l/by-id 2>/dev/null
ls -l /dev/v4l/by-path 2>/dev/nullUna 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/indexElige un nodo que anuncie Video Capture de un solo plano. Descarta nodos que
solo anuncien Metadata Capture.
Obtén VID/PID y descriptores:
lsusb
udevadm info --query=property --name=/dev/videoN | sort
udevadm info --attribute-walk --name=/dev/videoNBusca 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 --infov4l2-ctl --device=/dev/videoN --list-formats-extLa 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.
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 \
--verboseSi 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-parmRepite conexiones, puertos y pruebas largas si el problema original era intermitente. Un perfil solo debe registrarse después de demostrar estabilidad.
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 = 4Recomendaciones:
- usa un
idestable y descriptivo, no una ruta de dispositivo; - usa como
display_nameel 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
NdevideoN; - mantén
buffer_count = 4salvo evidencia que justifique cambiarlo; - usa únicamente
processing = "passthrough"en la implementación actual.
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 \
listLa 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.
sudo systemctl restart camera-fixd
journalctl -u camera-fixd -fVerifica:
- detección física;
- identidad estable;
- nodo seleccionado;
- creación virtual;
- asociación física/virtual;
- perfil negociado;
- imagen en una aplicación consumidora;
- desconexión y reconexión;
- funcionamiento simultáneo con las demás cámaras.
Una definición externa es suficiente para uso local. Para distribuir soporte predeterminado en el proyecto:
- añade la definición integrada en
src/config.rs; - mantenla como datos, sin introducir condiciones específicas en descubrimiento o supervisor;
- añade tests de matching en
src/discovery.rs; - añade tests de validación si introduces casos nuevos;
- actualiza la tabla de cámaras soportadas;
- ejecuta
make check; - documenta qué unidad y perfil se verificaron físicamente.
Si la corrección requiere modificar frames, no declares falsamente
passthrough. Será necesario:
- añadir un modo a
ProcessingKind; - implementar un
FrameProcessorensrc/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.
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.
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:
- sustituye VID/PID por los correctos;
- comprueba que esos IDs no pertenecen a otra cámara;
- añade selectores del mismo padre USB si necesitas mayor precisión;
- no uses
/dev/videoN; - no retires
uaccessal nodo virtual.
Después recarga reglas y reconecta la cámara:
sudo udevadm control --reload-rulesEn 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.
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:
- Al aparecer, se identifica y se resuelve su nodo actual.
- Se crea o reutiliza una virtual con etiqueta estable.
- Se lanza su worker.
- Si el nodo cambia, se detiene el worker anterior y se inicia otro con la ruta nueva.
- Si desaparece, se solicita parada inmediata del worker.
- La asociación se conserva durante
disconnect_grace_ms. - Si reaparece con la misma identidad, se recupera.
- 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.
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 runCampos útiles:
camera_id: identidad estable calculada;model: ID de la definición;physical_nodeyvirtual_node: rutas actuales, solo para diagnóstico;error_class:device_gone,configuration,device_protocol,processing,iooworker_panic;retry_ms: espera hasta el siguiente intento;requires_intervention=true: falta una herramienta, permiso o configuración que probablemente necesita acción administrativa.
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-utilsVerifica:
v4l2loopback-ctl --versionEl kernel no contiene el módulo y DKMS no lo ha construido para el kernel actual. Comprueba:
uname -r
dkms status
modinfo v4l2loopbackInstala el módulo y las cabeceras exactas de ese kernel. Reinicia si acabas de cambiar de kernel.
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.
Comprueba:
ls -l /dev/v4l2loopback
getent group camera-fixd
systemctl cat camera-fixdReinstala la regla y recarga udev mediante make install. La unidad necesita
CAP_SYS_ADMIN además del permiso del nodo.
Comprueba el nodo y los grupos:
ls -l /dev/videoN
getent group video
systemctl show camera-fixd -p User -p Group -p SupplementaryGroupsPuede 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.
Ejecuta:
sudo /usr/local/bin/camera-fixd \
--config /etc/camera-fixd/config.toml \
listCompara los selectores exactos con:
udevadm info --query=property --name=/dev/videoN | sort
cat /sys/class/video4linux/videoN/index
v4l2-ctl --device=/dev/videoN --infoComprueba especialmente espacios, puntuación, interfaz e índice.
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.
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-parmEl 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.
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/videoNNo ejecutes simultáneamente make dev y camera-fixd.service.
-
Confirma que el worker está activo en el journal.
-
Comprueba que la virtual anuncia captura mientras el productor está abierto:
v4l2-ctl --device=/dev/videoN --all
-
Comprueba que la salida fue creada con capacidades exclusivas.
-
Reinicia la aplicación consumidora; algunas enumeran cámaras solo al iniciar.
-
Revisa PipeWire y el portal del escritorio si la aplicación no accede directamente a V4L2.
Es normal. El nombre y la identidad son estables; el número no. Selecciona la
etiqueta (Fixed) en la aplicación.
Es el límite de 31 bytes de V4L2. El servicio conserva un fragmento reconocible,
el hash de identidad y (Fixed).
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.
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.
Consulta el error completo:
systemctl status camera-fixd
journalctl -u camera-fixd -b -n 200 --no-pagerValida 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.
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 \
runFinaliza con Ctrl-C y restaura el servicio:
sudo systemctl start camera-fixdLos 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.
Conserva primero la configuración:
sudo cp -a /etc/camera-fixd/config.toml \
/etc/camera-fixd/config.toml.backupDespués actualiza el código, verifica e instala:
make check
make install
sudo systemctl restart camera-fixdmake 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-configmake uninstallLa 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.
- 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.
v4l2loopbacksigue 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.