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

jueves, 22 de febrero de 2018

Montemos una IP-Webcam barata - Reloaded III

Este tema es inacabable. Después de todos los posts sobre la cámara IP a partir de una cámara usb y un router ADSL casero con OpenWRT/LEDE todavía me siguen saliendo cositas y ampliaciones cada pocos meses, así que no me queda otra opción que seguir revisitando el tema. Hagamos una retrospectiva:


Ahora el problema es que, recordemos, el guardado de las imágenes de captura se realizaba en una carpeta de un servidor remoto montada por sshfs. No es infrecuente que haya algún problema de red y esa conexión quede interrumpida. No siempre es restaurada esa conexión en tiempo y forma y a veces me encuentro con que llevamos varios días desechando imágenes ya que en el mermado espacio de almacenamiento del router ADSL no se puede guardar nada.

Para evitar esto he añadido código para que cuando se detecte que se ha perdido el montaje remoto por sshfs se envíe un correo especial de aviso, para que al menos pueda ponerle remedio cuanto antes. Marco en negrita la parte con el pequeño fragmento de código nuevo (el resto es como la ha ido quedando las últimas veces):
# cat /root/controlador.sh
#!/bin/ash

log() {
  test $montado -eq 1 && echo "$1" >> /mnt/snapshot/log.txt  
}

email() {
  test $email -eq 1 && echo -e "$1" | sendmail correo.aviso@gmail.com
}

monta_sshfs() {
  sshfs -o ssh_command="ssh -i /root/.ssh/id_rsa -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" -o nonempty openwrt@192.168.0.25:/home/openwrt /mnt
}

envia_secuencia() {

   if [ -e /tmp/motion.txt ]
   then

      #Recopila todos los ficheros de /tmp/motion/txt y los envia en un correo

      ficheros=$(cat /tmp/motion.txt | tr '\n' ' ')
      rm /tmp/motion.txt

      log "Historia de capturas $ficheros"
      zip -9 /mnt/escena.zip $ficheros

      echo "Secuencia $1" > /mnt/mensaje.txt
      test $email -eq 1 && mutt -s "Evento camara" correo.aviso@gmail.com -a /mnt/escena.zip < /mnt/mensaje.txt

      rm /mnt/escena.zip
      #Si no hay acceso al almacenamiento remoto, borramos las capturas para no llenar el disco del router.

      test $montado -eq 0 && rm -rf $destino

   fi

}

#Parametro 1: start, picture, end
#Parametro 2: fecha
#Parametro 3: nombre fichero (opcional)

#Obtenemos fecha y hora por si queremos poner algun filtro sobre ella y solo avisar en determinados
#momentos

time=$(date +%s)
anio=$(date +%Y @$time)
mes=$(date +%m @$time)
dia=$(date +%d @$time)
hora=$(date +%H @$time)
minuto=$(date +%M @$time)
diasemana=$(date +%u @$time) #El 1 es lunes
destino="/mnt/snapshot/$anio-$mes-$dia"
montado=1
email=0

if [ $diasemana -ge 6 -o $hora -le 7 -o $hora -ge 17 ]  # sabado/domingo o cualquier dia antes de las 8:00 o despues de las 17:00
then
   email=1
fi

#Si existe el fichero /mnt/testigo no se ha montado directorio remoto por sshfs,
#Intentamos montarlo y otra vez y luego preguntamos de nuevo.
#Si no se ha montado nada es que no se puede guardar nada de forma permanente ya que estamos escribiendo en la
#memoria interna del router y eso se llena rapido

test -e /mnt/testigo || monta_sshfs

if test -e /mnt/testigo
then
  #No está montado el directorio remoto, no hay donde guardar las imagenes
  montado=0
  #No hay almacenamiento por dias, no vamos a guardar tanto tiempo el fichero.
  destino="/mnt/snapshot"
  #Si no se ha avisado antes se avisa por correo
  if ! test -e /tmp/avisado
  then
     touch /tmp/avisado
     echo -e "Subject: Camara sin almacenamiento\r\n\r\nSe ha perdido la conexión remota con el directorio de almacenamiento de la cámara" | sendmail correo.aviso@gmail.com
  fi
else
  #Tenemos montado el directorio remoto, borramos el testigo de aviso.
  rm -f /tmp/avisado
fi

test -d $destino || mkdir -p $destino

case $1 in

  "start")
     log "Detectado inicio de movimiento $2" 
     email "Subject: Evento camara\r\n\r\nDetectado inicio de movimiento $2"
     #El fichero motion.txt tiene dos finalidades: 1) testigo para indicar que estamos detectando movimiento 
     #                                             2) guarda los nombres de los ficheros de captura de las fotos
     touch /tmp/motion.txt
     ;;
  "picture")
     fichero=$(basename $3)
     log "Guardando imagen $2 : $destino/$fichero" 

     #Si estamos en una ráfaga de movimiento detectado, guardamos el nombre de fichero con la foto.
     test -e /tmp/motion.txt && echo "$destino/$fichero" >> /tmp/motion.txt

     #Se guarda el fichero en el almacenamiento destino
     mv "$3" "$destino/$fichero"

     #Si no está montado el almacenamiento remoto y vemos que llevamos mas de 10 imagenes en
     #movimiento tenemos que borrarlas y enviarlas ya mismo, para no saturar el espacio.
     if [ $montado -eq 0 ]
     then
        #Si estamos en una ráfaga de movimiento detectado....
        if [ -e /tmp/motion.txt ]
        then
          lineas=$(wc -l /tmp/motion.txt | cut -d" " -f1)
          test $lineas -gt 10 && envia_secuencia $2
        else
          #Si no lo estamos, es una captura *snapshot* regular sin interés, la borramos.
          rm -f "$destino/$fichero"
        fi  
     fi
     ;;
  "end") 
     log "Detectado fin de movimiento $2"
     envia_secuencia $2
     ;;
   *) log "Evento $1"
esac


#Borra fichero "sent" creado por sendmail si existe, para liberar espacio
rm -rf /root/sent
rm -rf /sent
Para evitar enviar el mensaje en cada invocación del script se usa un fichero "avisado" en /tmp/ como bandera para verificar si se ha enviado o no ese mensaje. Ese fichero se borra cuando se detecta que se ha montado el directorio remoto o al reiniciar el equipo (todo el contenido de /tmp se borra en cada reinicio).

Bueno, pues seguro que vuelve la burra al trigo pronto y estamos otra vez aquí.



Hasta siempre Forges, me has hecho reírme de mí mismo muchas veces.

lunes, 19 de febrero de 2018

Ejecutar en los infolabs aplicaciones gráficas entrando con "ssh -X"

Esta mañana tenía que instalar Adobe Air y una aplicación .air remotamente en varios infolabs y como el instalador es "al estilo Windows" debía entrar por "ssh -X" y lanzar el ejecutable gráfico de instalación.

Me ha sorprendido bastante que fallase con el error de que no se podía abrir el display, como si no tuviera permisos para ejecutar aplicaciones X. La verdad es que nunca había tenido necesidad de ejecutar aplicaciones gráficas en remoto entrando por ssh en los infolab. He probado varias aplicaciones y en todas había problemas del tipo:
Failed to open display
o
Error: GDK_BACKEND does not match available displays
Lo primero ha sido mirar en /etc/ssh/sshd_config y ver que aparece:
X11Forwarding yes
Ante esto he quedado desconcertado un rato, hasta que he visto que ya lo solucionó mi compañero Manuel del IES Eugenio Hermoso. Hay que añadir a /etc/ssh/sshd_config la línea:
X11UseLocalHost no
Y reiniciar ssh en el infolab. ¡Gracias, Manolo!

¿Por qué esta línea no es necesaria en otros equipos como los Siatic y compañía? Pues no sabemos.

domingo, 18 de febrero de 2018

Windows: actualiza como puedas.

Vaya por delante que opino que usar Windows en un centro educativo es, salvo casos muy justificados, un disparate económico, ético y tecnológico. Pero por desgracia esa puerta quedó abierta en tiempos pasados y debemos lidiar con la situación como buenamente podamos.

1. Parte de guerra.

Una vez tienes instalados tus Windows "pelados" aparte de los problemas típicos de gestión de usuarios y administración te encuentras con la cuestión de sus actualizaciones, que tiene una idiosincrasia especial en un entorno como el nuestro. Repasemos:

Primero. Cualquier usuario, incluidos los defensores mas acérrimos, habrán comprobado que Windows tiene la irritante costumbre de actualizarse sin control y sin contemplaciones de forma extemporánea: al iniciar, al apagar o en medio de la operación de un PC. De una extraña manera, estas actualizaciones incluso no dudan en arrasar cuando les parece con el arranque dual haciendo desaparecer Linux del menú de arranque. Esto en mi opinión roza lo delictivo.

Segundo. Windows tiene un sistema de actualizaciones totalmente ilógico: si dejas un equipo sin actualizar 6 meses al lanzar el update no instala lo último, sino que pasa por casi toda la secuencia de actualizaciones que se han quedado atrás (pocas veces una actualización es descartada en favor de otra posterior), reiniciando el equipo varias veces. Eso, para la gente razonable acostumbrada a derivados de Debian y a las rolling relaease como mi querida Manjaro es sencillamente inconcebible. Me ha llegado a pasar que instalas un Windows 7 en una hora y luego te pasas 20 horas descargando actualizaciones por fases de descarga-instalación-reinicio hasta tenerlo al día.

Por otro lado, el proceso de actualización es tremendamente oscuro y nunca sabes que está pasando y si el sistema se ha quedado parado en alguna operación. Sabes cuando empieza y cuando acaba, pero no como avanza. Para los acostumbrados a la verbosidad de las actualizaciones en Linux este mutismo es incomprensible.

Tercero. Como ya avisamos en su momento a la gente que decide, para poner orden en este caos hace falta un sistema que nos permita administrar y dosificar el flujo de actualizaciones. Eso implica un Windows Server y un servicio WSUS por centro junto con el hardware para ello, lo cual representa un sensible gasto adicional en dinero y tiempo para administrarlo. Esto no se tuvo en cuenta por las mentes pensantes que decidieron adquirir licencias de Windows sin tino y nos dejaron el papelón a los de abajo.

También puedo hablar según mi experiencia: habiendo servido durante varios años en ICM, la antigua Agencia de Informática y Comunicaciones de la Comunidad de Madrid (muy conocida por colaborar con las tramas corruptas Púnica y Lezo; tranquilos: yo soy inocente, todo eso pasó mayormente mientras estaba de excedencia) donde todo se basaba en Windows. Incluso con toda la infraestructura de clientes y servidores Windows montada para gestionar las actualizaciones de forma controlada los problemas en los clientes eran constantes: parches que se enrocaban en una instalación iniciada e interrumpida infinitamente, equipos en que fallaba algo y se quedaban atrás sin actualizar, errores y pantallazos azules en arranques tras actualizaciones fallidas... y todo esto con Windows XP, en los que el que ritmo de actualizaciones era mucho mas reducido que en los Windows posteriores.

Cuarto. Para mas INRI, esto último no sirve nada tan pronto como comprendes la realidad de funcionamiento de los PC de los centros educativos. Esto no es una oficina al uso donde los PC se encienden de 8 a 15 o de 9 a 17. Un PC en un centro educativo se enciende y se apaga de forma aleatoria a lo largo del día según el uso en las materias y horarios de clase. Incluso podemos encontrarnos con PC que no se encienden durante muchos días y de repente luego se encienden una mañana entera con 3 reinicios al cambiar de clase. Evidentemente disparar unas actualizaciones que pueden tardar horas en un entorno tan aleatorio es un proceso abocado a la catástrofe: tanto por el consumo de ancho de banda que se incrementa de golpe al encender un grupo de PC de alumnos, así como por el ralentizamiento del PC asociado a la actualización y los problemas derivados de apagarlos cuando están a medias.

Cinco. Podríamos pensar que los PC de uso de los profesores (salas comunes, aulas, departamentos) si que están encendidos de forma continua. Falso: son ordenadores de uso compartido y una de las pequeñas luchas que tenemos los administradores informáticos es limitar la pulsión que tienen muchos usuarios de apagar el sistema operativo cuando se acaba su sesión, máxime teniendo en cuenta que en 5 minutos va a haber otro docente sentado en ese mismo sitio. Pues bien: no hay manera, cuando alguien quiere apagar, apaga.

Seis. ¿Puede empeorar la cosa? Si: las aulas de portátiles. Nada mas terrible que ir a guardar un portátil en un armario de carga porque acaba la clase y encontrarnos que nos dice que esperemos porque hay que instalar 13 actualizaciones. O encenderlo para empezar la clase y esperar 10 minutos a que se actualice. Pero eso no es todo: tenemos 20 portátiles conectados a un punto de acceso wifi normal y corriente pugnando por actualizar, descargando megas y megas cada uno e impidiendo el uso normal de todos los dispositivos conectados a la misma wifi. Como seguramente en una hora de clase no de tiempo a descargar todo en la próxima sesión volveremos a la casilla de salida.


2. Estrategias paliativas.

Ante este panorama no nos queda otra que lidiar como buenamente podamos con todo ello. Si quedamos las actualizaciones a su libre albedrío la tragedia en forma de sistemas dañados por el apagado en medio de una actualización o redes colapsadas está garantizada. Parafraseando el proverbio chino: simplemente tenemos que sentarnos a la orilla del río y esperar hasta que pasen los Windows muertos flotando corriente abajo. El problema es que al final esos Windows tenemos que resucitarlos nosotros.

En mi caso he optado por dos estrategias para evitar la debacle:

  1. Cuando es posible por la potencia del equipo cliente virtualizo los Windows y los dejo con las actualizaciones desactivadas. Tengo un master que actualizo de vez en cuando en un PC concreto y distribuyo luego el fichero .VDI (imagen del disco duro) a todos los demás.
  2. Si no es posible virtualizar, porque los PC andan justitos de procesador y memoria opto por dejar un arranque dual y: desactivar en la medida de lo posible las actualizaciones automáticas de Windows, usar un sistema basado en wsusoffline, como recomienda nuestro compañero Esteban, para actualizar contra un repositorio interno al IES y no contra Internet (con lo cual ahorramos tráfico de red) y cada cierto tiempo me toca perder varios días actualizando semi-manualmente los equipos.

Vamos a ver esto con detalle.

3. Parar, reanudar y limitar las actualizaciones.

No es plato de buen gusto parar las actualizaciones de un PC, se supone que incluyen mejoras y parches contra agujeros de seguridad como Wannacry, Meltdown o Spectre. Pero cuando la alternativa es tener el aula parada y los usuarios malhumorados porque todo el tiempo y ancho de banda se va en actualizar dices "¡pero que coño!" y paras todo.

La forma más efectiva que he encontrado de parar las actualizaciones automáticas es con estos comandos que se pueden meter en un script:
sc config wuauserv start= disabled
net stop wuauserv
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU  /v NoAutoUpdate /t REG_DWORD /d 1 /f
Con esto el Windows queda congelado en el tiempo salvo que Microsoft use algún truco artero para colarnos algo, tal como hace con esas instalaciones de Cortana u OneDrive que reaparecen después de haberlos eliminado una y otra vez. Si queremos volver a activar haremos los pasos contrarios:
sc config wuauserv start= auto
net start wuauserv
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU  /v NoAutoUpdate /t REG_DWORD /d 0 /f
Otra opción que puede resultar interesante es usar una característica que tiene Windows de definir una conexión inalámbrica de red como "metered" o de uso medido. Eso viene a significar que le avisamos de que esa conexión nos cuesta dinero cada megabyte y que por tanto, tenga ojito con lo que se descarga por ella. Pensemos por ejemplo en una conexión móvil 4G, un operador de wifi o el futuro Starlink de Elon Musk, que nos facturase por datos transferidos.

Para ello debemos establecer la conexión a la red y una vez hecha tecleamos:
netsh wlan show profiles
Esto nos mostrará el nombre de la conexiones wlan (redes wifi o 4G), por ejemplo algo así como "Airtel-WRTR301GN-8897_1". Una vez sabido el nombre, con:
netsh wlan show profile name="Airtel-WRTR301GN-8897_1"
Vemos los parámetros de la conexió. En ellos el parámetro "Cost" puede valer Unrestricted (no limitado) o Fixed (limitado). Por tanto, si queremos indicar que la conexión es metered hacemos:
netsh wlan set profileparameter name=”Airtel-WRTR301GN-8897_1″ cost=Fixed
Esto hará que Windows se lo piense antes de consumir ancho de banda en una actualización por esa conexión. Pero, ojo, esto no descarta todas las actualizaciones y las que se consideren críticas se realizarán. Aunque está concebido para conexiones inalámbricas, también podemos activarlo en conexiones cableadas trasteando el registro.

4. Encauzar las actualizaciones con WSUS Offline Update.

WSUS Offline Update es un sistema montado para realizar las actualizaciones de los Windows/Office usando la red local y minimizando el tráfico externo. Incluso permite actualizar Windows/Office sin conexión a Internet, descargando las actualizaciones a un pendrive y actualizando luego con él. Yo lo descubrí, como tantas otras cosas, gracias a mi compañero Esteban.

Todo rota en torno a 3 elementos:

  • Una aplicación/script que descarga las actualizaciones y parches disponibles para los sistemas seleccionados.
  • Un repositorio donde guardar lo descargado. Traduciendo: una carpeta local o de red donde guardar los archivos.
  • Una aplicación/script que examina el sistema Windows y actualiza con los parches pendientes.

Una visión detallada del sistema la podemos encontrar aquí.

En mi caso no he usado las aplicaciones gráficas para realizar las tareas, ya que como se deducirá me va mas la línea de comandos. Además he intentado que las dos primeras partes se realicen con Linux y un servidor samba. Solo la parte de aplicación de las actualizaciones es, evidentemente, sobre Windows. Para ello me he guiado por este manual.

En un servidor auxiliar creo una carpeta /datos/wsus, con permisos 777 para todo su contenido. Sobre esa carpeta descomprimo la última versión de wsusoffline, que a fecha de escribir este post es la 11.1.1.

Una vez descomprimido el .zip escribo el script que me hará la descarga de los parches y actualizaciones para los sistemas que le indique:
# cat /datos/wsus/update.sh
bash /datos/wsus/wsusoffline/sh/download-updates.bash w100 esn /includedotnet /includemsse /includewddefs /verify
bash /datos/wsus/wsusoffline/sh/download-updates.bash w63-x64  esn /includedotnet /includemsse /includewddefs /verify
bash /datos/wsus/wsusoffline/sh/download-updates.bash w100-x64 esn /includedotnet /includemsse /includewddefs /verify
# chmod +x datos/wsus/update.sh
Este script descarga lo que haya de Windows 10 (64 y 32 bits) y Windows 8 (64 bits). Mirar la documentación para ver todas las posibles opciones de descarga (Windows 7, Office, Visual C++, .NET, ...). Lanzamos la ejecución del script y nos vamos a hacer otras cosas, porque tarda un rato en descargar todo:
# /datos/wsus/update.sh
Cuando termina, vemos que ha hecho:
# du -sh /datos/wsus/wsusoffline/client
2,6G w100
4,8G w100-x64
1,9G w63-x64
# ls -1 /datos/wsus/wsusoffline/client/w100-x64/glb/
windows10.0-kb3172729-x64_bb12a14ec3891ec0a9e24edb529632263783d389.cab
windows10.0-kb3172729-x64_f4fc9775baa98c176f43e87c40088231a884122b.cab
windows10.0-kb3173427-x64_d95e56e499e2c281a1f59585221dc891253414c7.cab
windows10.0-kb3173427-x64_e3da65fe753d24a1759cdd029028cde743a62a23.msu
windows10.0-kb3173428-x64_52fa3686737353fae20ab55fa9c924bd90558a31.cab
windows10.0-kb4035632-x64_ea26f11d518e5e363fe9681b290a56a6afe15a81.msu
windows10.0-kb4049011-x64_a58e4c340939a7b23b86055a18abdc9c59308094.msu
windows10.0-kb4049065-x64_f92abbe03d011154d52cf13be7fb60e2c6feb35b.msu
windows10.0-kb4056887-x64_0fcf1ff448cea9474ea089a3d866beeb7212cec1.cab
windows10.0-kb4056887-x64_12a57a1104466ca878eb096fefa6c6babe520810.cab
windows10.0-kb4056887-x64_1e5c03724988979684c35a5493829e4abe9bdbfc.cab
windows10.0-kb4056887-x64_57d92c622280f34d0ee271a9c58fdce91c3c6808.cab
windows10.0-kb4056887-x64_8ad98c5fb14f2938536d794074a5b901b6eb0d72.cab
windows10.0-kb4056888-x64_5943823d032a25e40f8eee1613aa849d3963bd1d.cab
windows10.0-kb4056890-x64_2d0216b0e6b49f457e5111fbd7d766984af3f6cf.cab
windows10.0-kb4056891-x64_99b9dadbd6a71c8c9ca58b8e53285f8320dd32e3.cab
windows10.0-kb4056892-x64_39198b373a07390a3afdf2a64d19f24d59fca7a6.cab
windows10.0-kb4056893-x64_f025805bafd32c5f9d7ca37eb255a894dfb025d8.cab
windows10.0-kb4058702-x64_4ed8d0df4f3c190d3e423bf287d7e95a21a9124a.msu
Y ahí vemos todos los ficheros con las distintas actualizaciones y parches descargados esperando para ser aplicados según necesite el cliente Windows. Este script lo ejecutaremos de vez en cuando o al menos cuando se actualice wsusoffline.

Estos ficheros hay que compartirlos usando una carpeta samba de solo lectura accesible desde los Windows:
# cat /etc/samba/smb.conf
...
...
[wsus]
comment = WSUS Windows Updates
path = /datos/wsus
writable = no
browseable = yes
guest ok = yes
force directory mode = 0777
...
...
Reiniciamos samba y desde un Windows podemos abrir una ventana de comandos con credendiales de usuario administrador y teclear:
net use z: \\ip-servidor\wsus /persistent:no
Z:\wsusoffline\client\cmd\DoUpdate.cmd
Con lo cual empezará el laaaargo proceso de actualizaciones, que puede durar varias horas. En la ventana de terminal nos va informando escuetamente de cada parche aplicado. No nos desanimemos: si fuese mediante actualizaciones online podría tardar varios días y saturar la conexión de red del centro. De esta manera al menos tenemos controlado lo que se va haciendo y que se va actualizando.

5. Cosas a tener en cuenta.

Esto no es un camino de rosas, simplemente es algo para aminorar los problemas. Por eso debemos tener en cuenta varias cosas:

  • El wsusoffline es el sistema mas rápido si tenemos varios PC con Windows y el que menos tráfico de red genera, pero va siempre desactualizado respecto a los repositorios oficiales. Hay que vigilar si hay versiones nuevas de vez en cuando para actualizalo. A fecha de escribir esto la última actualización descargada es KB4058702, pero en Microsoft van por la KB4074608, que además es incompatible con la anterior, lo cual puede causar problemas en el script.
  • El update mas fiable es el realizado online contra los repositorios de Microsoft. El problema es que puede tardar mucho tiempo, descargar varios gigas y exigir varios reinicios. Multiplica eso por el número de Windows de tu red. De todas formas, si no nos queda otro remedio que hacerlo para salir de alguna situación complicada podemos abrir de forma directa la ventana para actualizaciones haciendo en un intérprete de comandos de Windows:
    C:\Windows\System32\control.exe /name Microsoft.WindowsUpdate 
    
  • Una alternativa a lo anterior o un método para salir de un atasco en que todas las actualizaciones fallan porque hay que subir de versión (por ejemplo, pasar de la 1703 a la 1709 en Windows 10) es descargar la última ISO de Windows (a día de hoy la 1709), montarla con wincdemu y actualizar desde allí.

Después de todas estas actualizaciones es bueno saber en que versión tenemos el Windows. Por ahí se comenta que ejecutando en una consola Powershell:
Get-CimInstance Win32_OperatingSystem | Select-Object buildnumber,version
Se muestra la versión, pero es falso ya que no sale completo todo el número de compilación. La manera de saberlo es con:
winver
Nos muestra:


Este Windows tiene por tanto en el momento en que escribimos esto la versión 1709, compilación 16299.192. Esta bastante actualizado a día de hoy, si buscamos en internet el "Historial de actualizaciones de Windows 10" vemos:


Podemos ver que nuestro Windows está en la compilación .192 y según la página de Microsoft posteriormente "sólo" han salido la 201, 214 y la 248. Cuando empecé a elaborar esta entrada ibamos por la 214 y ya ha salido otra, ahí es nada. Es verlo y me da la bajona... vaya cruz.

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.