"Después del juego es antes del juego"
Sepp Herberger

jueves, 8 de febrero de 2018

Acceso directo una camara IP Dlink DCS-5000L.

Tenemos una cámara IP DCS-5000L que cumple todas las expectativas de un dispositivo de este tipo: interface web, acceso y control remoto (mediante un motor para girar el objetivo), grabación, detección y aviso de movimiento y/o sonido, visión nocturna....

Además de esto echaba en falta poder acceder desde fuera, con programas mas potentes y versátiles como motion (que ya usamos con OpenWrt anteriormente) o su estupendo frontend para videovigilancia motionEye.

Ya puestos en tarea, por su buena calidad de imagen veía interesante usarla poder usar para videoconferencias o emisiones de vídeo grupales, con Google Hangouts, Skype o similares.

Por todo ello vamos a tratar en este post como conectarnos a ella con aplicaciones diferentes al tradicional navegador e ir probando cosas a ver hasta donde llegamos.

1) Acceso directo al stream de vídeo/audio.

Lo primero es acceder a las imágenes y audios de forma directa, sin pasar por el interface web, el cual tiene este aspecto:


Como vemos la visualización de las imágenes está basada en tecnologías rabiosamente nuevas como Flash, Java y ActiveX. El interface permite ver, oír, mover la cámara y acceder a su configuración.

Normalmente en foros no oficiales aparecen las URL de acceso directo a los streams de audio y vídeo de las cámaras IP de Dlink, pero para esta webcam no había manera de encontrarlos. Afortunadamente mi buen compañero David usó sus oscuras artes de teleco para llegar a acceder y pasarme esto:

  • Foto: http://192.168.0.112:8080/image/jpeg.cgi. Descarga un jpg con una foto fija tomada de forma instántanea con la cámara. Podemos tomarla con :
    wget -O imagen.jpg http://192.168.0.112:8080/image/jpeg.cgi
  • Vídeo: http://192.168.0.112:8080/video.cgi. Emite un stream de vídeo en tiempo real con formato MJPEG y resolución de 640x480 píxeles. Podemos verlo con:
    vlc http://192.168.0.112:8080/video.cgi
  • Audio: http://192.168.0.112:8080/audio.cgi. Si hemos activado el audio en la cámara y subido el volumen del micro lo suficiente accederemos al stream de audio del micrófono. Nos llega como un wav en formato PCM de 16bits de frecuencia 11025Hz. Podemos oírlo en directo con:
    vlc http://192.168.0.112:8080/audio.cgi
De esta manera podríamos monitorizar la cámara con motion usando el parámetro netcam_url:
# cat /etc/motion/motion.conf:
.....
.....
netcam_url http://192.168.0.112:8080/video.cgi
.....
.....
Como vemos aparte de acceder a cámaras locales, motion permite controlar cámaras de red. Incluso podríamos hacer cosas mas interesantes, como activar la grabación de sonido ante un evento detección de movimiento.

2) Reproducción en ventana independiente y grabación.

El siguiente paso es ver si podemos reproducir el flujo de vídeo en una ventana independiente usando esa navaja suiza de los streams multimedia que es gst-launch.

Empecemos con:
gst-launch-1.0  souphttpsrc location=http://192.168.0.112:8080/video.cgi ! jpegdec ! autovideosink
Básicamente cogemos el stream remoto, lo pasamos por el plugin "jpegdec" que decodifica los fotogramas jpeg que forman la imagen y lo volcamos en el sink (un sink es un sumidero donde acaba el stream tras pasar por los plugins) autovideosink, que muestra el vídeo con una fluidez estupenda en una ventana independiente tal que así:


De esta manera logramos ver la cámara en directo prescindiendo de Flash, Java y demás puñetas anteriores al Holoceno.

Gstreamer tiene plugins y filtros de todo tipo, como por ejemplo este para girar la imagen 90º:
gst-launch-1.0  souphttpsrc location=http://192.168.0.112:8080/video.cgi ! jpegdec ! videoflip method=clockwise ! autovideosink
Aquí tenemos varios ejemplos más. Estos son todos los plugins y sinks aplicables.

Ahora vamos a por el stream de audio. Por ejemplo, veremos como grabar el audio a un fichero, primero con mplayer:
mplayer http://192.168.0.112:8080/audio.cgi  -ao pcm:file=/tmp/mystream.wav -vc dummy
Y ahora con gst-launch:
gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! filesink location=/tmp/mystream.wav
Ahora oímos el sonido en directo (ojo, si estamos en la misma habitación el sonido acaba acoplándose con un chirrido irritante), como si fuese un micrófono espía :
gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! wavparse ! autoaudiosink
Lo mismo, pero reproduciendo el sonido por pulseaudio:
gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! wavparse ! pulsesink 
Ahora juntamos ambos streams (audio y vídeo) para reproducirlos a la vez:
gst-launch-1.0  souphttpsrc location=http://192.168.0.112:8080/video.cgi ! jpegdec ! autovideosink  souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! wavparse ! autoaudiosink
Si hacemos pruebas veremos que el audio va con un retraso de varios segundos respecto al vídeo. Después de varias pruebas concluyo que es algo ineludible, tiene pinta de ser una feature de la cámara.

3) Vídeo remoto como local: dispositivo /dev/videoX y loopback.

Vamos un paso mas allá: ¿y si queremos usar la cámara para una aplicación de videoconferencia, tipo Skype o Hangouts? ¿Y si queremos hacer una captura del vídeo para algún tipo de procesado con cheese? Estas aplicaciones no trabajan con cámaras remotas, sino que lo hacen con dispositivos físicos /dev/videoX creados al conectar una fuente de vídeo USB o PCI.

¿Podemos hacer que la cámara IP sea accedida en /dev/videoX como una local? Pues sí, Linux es maravilloso. Todo pasa por crear un dispositivo de vídeo virtual, que hará aparecer un /dev/videoX y luego podremos conectar el stream de vídeo de la cámara a dicho dispositivo. Esto se consigue con el driver v4l2loopback.

Para Ubuntu 14.04 ésta es la versión ya compilada (para Manjaro/Arch sería ésta). Los pasos para descargarlo y cargar en memoria el módulo son:
# wget https://launchpad.net/ubuntu/+archive/primary/+files/v4l2loopback-dkms_0.10.0-1_all.deb
# apt-get install v4l-utils
# dpkg -i v4l2loopback-dkms_0.10.0-1_all.deb
# rmmod v4l2loopback
# modprobe v4l2loopback -r
# modprobe v4l2loopback devices=1 exclusive_caps=1 video_nr=1 card_label="virtualvideo1"
Con esto se carga el módulo v4l2loopback en memoria y se crea un dispositivo virtual /dev/video1. Podemos comprobarlo con:
# v4l2-ctl --list-devices
De momento este dispositivo está vacío. Si abrimos vlc o cheese leyendo de él no se muestra nada. Hay que conectar el stream remoto de la cámara con el módulo v4l2loopback, el cual a su vez lo hará emerger por /dev/video1.

Esto lo hacemos con gst-launch usando el sink "v4l2sink" para enviar los datos a /dev/video1:
gst-launch-1.0  souphttpsrc location=http://192.168.0.112:8080/video.cgi ! jpegdec ! tee ! v4l2sink device=/dev/video1 sync=false
O también podemos usar ffmpeg (en ubuntu se instala antes desde un PPA).
ffmpeg -re -f mjpeg -i http://192.168.0.112:8080/video.cgi  -f v4l2 -vcodec rawvideo -pix_fmt yuv420p /dev/video1
En las pruebas realizadas con Google Hangouts, usando ffmepg la transmisión de vídeo hacia el dispositivo virtual y al cliente remoto es más fiable, estable y fluida que con gst-launch. Recomendamos por tanto usar ffmpeg sobre gst-launch para enviar el stream de la cámara al dispositivo v4l2loopback.

Para ampliar, aquí tenemos documentados varios métodos de enviar fuentes de vídeo a v4l2loopback.

Una vez creado /dev/video1 estas son las pruebas que he hecho:
  • Con vlc leyendo de /dev/video1 ("vlc v4l2:///dev/video1") la imagen se ve sin problemas.
  • Con cheese no funciona a la primera, pero quitando "exclusive_caps=1" al cargar el módulo si funciona.


  • Con Google Hangouts: he tenido problemas (no detección de la cámara) según la versión de Google Chrome y Firefox. En Chromium si ha funciona correctamente.


  • Con Skype: no lo he probado por los problemas que da con Linux últimamente, pero debe funcionar según el enlace comentado anteriormente.

Resumiendo: podemos usar la cámara IP cómo si fuera una cámara local, tanto para programas de captura de vídeo tipo cheese como para una videoconferencia con Hangouts o Skype. Las ventajas son:
  • Tiene mejor calidad que una cámara USB normal.
  • Puede colocarse en sitios que permiten tomas panorámicas para videconferencias grupales.
  • Conectados al interfaz web estándar de la webcam podemos girar/mover la cámara remotamente durante la emisión.




4) Videoconferencia con sonido.

El módulo v4l2loopback transmite vídeo, pero no sonido. En el apartado 1 y 2 hemos oído el sonido remoto recogido por la cámara en el PC local, pero para videconferencias habría que tratar el sonido remoto como una fuente de sonido (vamos, que el sistema vea el stream entrante como un micrófono). Después de varias pruebas con pulsesink y distintas configuraciones de pulseaudio según este ejemplo se consigue conectar el audio remoto con ciertas aplicaciones:

gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/audio.cgi ! wavparse ! pulsesink 

En este ejemplo anterior redirigimos el sonido del stream por la tarjeta HDMI y luego emerge en los Dispositivos de Salida, permitiendo realizar una grabación con un tercer programa (un grabador de audio en nuestro caso).

¿Esto podría usarse para videoconferencias? En teoría si, en la práctica no porque los programas de videoconferencia que he probado solo aceptan entrada de sonido de micrófonos reales, no de streams que provienen de una fuente remota o de un "monitor of ..." en pulseaudio.

Seguramente hay una manera de convertir el stream en un micrófono virtual (como hicimos con el vídeo y /dev/videoX) encadenando módulos loobpacks y null de pulseaudio, o como se comenta en diversos foros usando jackaudio , pero realmente no merece la pena. El sonido de la cámara llega con tanto retraso que desmerece el conjunto y hace vano el esfuerzo.

Mi conclusión es que es mucho más cómodo usar un micro local estándar conectado al PC desde donde hacemos la videoconferencia, de tal manera que el vídeo proviene de la cámara mediante v4l2loopback, pero el audio se recoge de forma directa por el PC, despreciando el stream de audio que genera la cámara.

Aquí una captura de pantalla de una videoconferencia de Google Hangouts mediante Chromium Browser. La imagen "ojo de pez" es la imagen de la cámara web y el sonido se recoge con un micro normal y corriente.


En la parte inferior derecha en pequeñito está la imagen tomada por el receptor, que es un móvil apuntando a la cámara web :-).

Otra imagen usando visión nocturna:


Recordemos, como dijimos en el anterior apartado, que para videoconferencias con Hangouts da mucho mejor resultado usar ffmpeg (y no gst-launch) para enviar el flujo de vídeo a v4l2loopback.

5) Emisión RTSP.

Por último, también puede ser interesante usar los streams de audio y vídeo para crear una emisión RTSP emitiendo por la red lo capturado por la cámara de forma que cualquier receptor pueda "sintonizarnos".

Esto son solo varias pinceladas para abrir las posibles vías de prueba:
  • Usando v4l2rtspserver: al parecer genera una emisión rtsp a partir de un dispositivo v4l2 (como v4l2loopback) y una fuente de sonido. Los paquetes .deb están aquí. Ademas necesita v4ltools, que hay que compilar a mano.
    El github de Michel Promonet, de donde viene esto, tiene varios proyectos muy interesantes para la emisión de vídeo.
  • Usando gstreamer: por desgracia gst-launch por si solo no es capaz de redigir el vídeo hacia rtsp. Existe una solución de pago en la que se usa un sink llamado gstrtspsink que es a su vez un servidor rtsp. Podemos pedirles una versión de evaluación.
  • Usando gstreamer para emisión rtp, mas sencilla que la de rtsp:
    # gst-launch-1.0 souphttpsrc location=http://192.168.0.112:8080/video.cgi !  jpegdec ! rndbuffersize max=1316 min=1316  ! udpsink host=127.0.0.1 port=5000  
    
    Para la recepción hace falta un fichero .sdp, pero no le he dedicado mucho tiempo a hacerlo funcionar.
  • Usando una utilidad específica para montar un rtsp server: el problema es que la mayoría son sistemas profesionales de pago (por poner un ejemplo: este o este otro). He encontrado algo sencillo y gratuito hecho en perl. Compilamos según las instrucciones y arrancamos el servidor:
    # /usr/local/share/perl/5.18.2/RTSP/rtsp-server.pl
    Arrancamos la fuente de vídeo/audio:
    # ffmpeg -re -f mjpeg -i http://192.168.0.112:8080/video.cgi -i http://192.168.0.112:8080/audio.cgi -f rtsp  -muxdelay 0.1 rtsp://127.0.0.1:5545/abc
    
    La recepción se haría:
    # vlc rtsp://127.0.0.1/abc
    En las pruebas realizadas la emisión se ve pixelada y el audio viene con retraso. Utilizando audio con micro local y jugando con parámetros de ffmpeg seguramente se pueda mejorar.
  • Usando ffserver, una herramienta más de la suite FFmpeg. Tomamos este texto como guía:
    # cat /etc/ffserver.conf
    
    Port 8090
    BindAddress 0.0.0.0
    MaxClients 1000
    MaxBandwidth 10000         
    
    <feed feed1.ffm>
    File /tmp/feed1.ffm
    FileMaxSize 5M
    </Feed>
    
    <stream camara1.avi>
    Feed feed1.ffm
    Format mpjpeg
    VideoFrameRate 25
    VideoIntraOnly
    VideoSize 640x480
    NoAudio
    Strict -1
    </Stream>
    
    <stream stat.html>
    Format status
    </Stream>
    
    Arrancamos el servidor:
    # ffmpeg -re -f mjpeg -i http://192.168.0.112:8080/video.cgi  http://localhost:8090/feed1.ffm
    
    La recepción se haría en la URL:
    http://localhost:8090/camara1.avi
    Esto es sin sonido, para añadir audio debemos emitir en formato .webm.
    # cat /etc/ffserver.conf
    
    Port 8090
    BindAddress 0.0.0.0
    MaxClients 1000
    MaxBandwidth 10000         
    
    <feed feed1.ffm>
    File /tmp/feed1.ffm
    FileMaxSize 5M
    </Feed>
    
    <stream camara1.flv>
    Feed feed1.ffm
    Format webm
    
    # Audio settings
    AudioCodec vorbis
    AudioBitRate 64          # Audio bitrate
    
    # Video settings
    VideoCodec libvpx
    VideoSize 640x480        # Video resolution
    VideoFrameRate 25        # Video FPS
    AVOptionVideo flags +global_header  # Parameters passed to encoder
    # (same as ffmpeg command-line parameters)
    cpu-used 0
    AVOptionVideo qmin 10
    AVOptionVideo qmax 42
    AVOptionVideo quality good
    AVOptionAudio flags +global_header
    PreRoll 15
    StartSendOnKey
    VideoBitRate 400         # Video bitrate
    
    </Stream>         
    
    <stream stat.html>
    Format status
    </Stream>
    
    Empezamos emisión:
    # ffmpeg -re -f mjpeg -i http://192.168.0.112:8080/video.cgi -i  http://192.168.0.112:8080/audio.cgi
    
    Y conectamos con el navegador:
    http://localhost:8090/feed1.ffm
    Como antes, se ve pixelado y con el audio retrasado. La solución sería la planteada en su momento: audio local con micrófono y ajuste de opciones de ffmpeg.

Hasta aquí todo por ahora, creo que le hemos sacado a la cámara todo el jugo que le podíamos sacar.

No puedo evitar despedirme con una de las imágenes mas emocionantes de los últimos años: los dos side boosters del Falcon Heavy de SpaceX aterrizando al unísono:


Gracias Elon y buen viaje a Starman en su Tesla hacia la órbita de Marte.



No hay comentarios:

Publicar un comentario