Cómo emitir webcams en vivo con un servidor Linux y en formato FLV

Hace un tiempo tuve un cliente que quería visualizar las cámaras que había instalado en su oficina desde su casa. En aquel momento eran cámaras IP cableadas. Este tipo de cámaras tienen un servidor web integrado desde donde puedes configurar su dirección IP además de ver el vídeo que transmiten. Por lo general estas cámaras transmiten en formato mjpeg, un flujo de imágenes JPG normales, aunque también podría ser que lo hiciesen directamente en mpeg. Firefox es capaz de reproducir este formato directamente, no así Internet Explorer que necesita de un applet Java para hacerlo.

El reto consistía, por tanto, en transmitir las cámaras en un formato que se pudiese reproducir fácilmente y que se pudiese integrar en una página web, sin programas de terceros para visualizarlas. Así llegamos a la solución que hoy os presento y que no es exclusiva de cámaras IP sino que puedes utilizar cualquier webcam USB que tengas por casa. El formato que hemos escogido para el flujo de vídeo será flv,  con lo que con una sencilla aplicación Flash podremos visualizar cada una de nuestras cámaras evitando, de paso, los applets Java.

Para el proyecto de hoy necesitaremos:

  • Servidor Linux, imprescindible, en mi caso Centos5
  • Apache instalado en el servidor.
  • ffmpeg, para hacer la conversión de formatos
  • ffserver, parte del paquete ffmpeg, para transmitir elvídeo

Si no tienes el software necesario, el primer paso es instalarlo. En mi caso:

yum install ffmpeg apache

Comenzaremos configurando ffserver desde /etc/ffserver.conf:

Port 8090
BindAddress 0.0.0.0
MaxClients 1000
MaxBandwidth 10000            

<Feed feed1.ffm>
  File /tmp/feed1.ffm
  FileMaxSize 5M
</Feed>
<Feed feed2.ffm>
  File /tmp/feed2.ffm
  FileMaxSize 5M
</Feed>            

<Stream camara1.flv>
  Feed feed1.ffm
  Format flv
  VideoCodec flv
  VideoBitRate 128
  VideoBufferSize 500
  VideoFrameRate 5
  VideoSize 320x240
  NoAudio
  Preroll 5
</Stream>            

<Stream camara2.flv>
  Feed feed2.ffm
  Format flv
  VideoCodec flv
  VideoBitRate 128
  VideoBufferSize 500
  VideoFrameRate 5
  VideoSize 320x240
  NoAudio
  Preroll 5
</Stream>            

<Stream stat.html>
  Format status
</Stream>

ffserver funciona como un servidor, transmitiendo en el puerto que le indiquemos lo que recibe en los archivos que se indican en los feed’s. A continuación se indican los flujos de vídeos que vamos a utilizar (feed’s), en mi caso dos cámaras. Finalmente definimos los streams que será a lo que realmente nos conectaremos nosotros, un stream por cada feed en formato flv. Podríamos crear tantos streams de cada feed como creamos oportuno en distintos formatos, consulta la ayuda de ffserver y ffmpeg.  Finalmente creamos un stream especial, el de status, donde veremos información sobre el estado del servidor y clientes contectados.

Lanzamos el servidor ffserver tal cual.

ffserver

Con esta configuración tendremos acceso a dos flujos de vídeo en formato FLV en las direcciones:

http://localhost:8090/camara1.flv
http://localhost:8090/camara2.flv

Vale, sí, aún no hemos transmitido el vídeo, eso viene ahora. Debemos enlazar el flujo de vídeo de nuestras cámaras con ffserver para que éste haga la conversión de formato y lo emita por las urls indicadas. Para hacerlo recurrimos al inseparable compañero de ffserver, ffmpeg.

Si tus cámaras están conectadas al servidor bien sea por USB o por capturadoras de vídeo (debes tenerlas configuradas previamente):

ffmpeg -f video4linux -s 320x240 -r 5 -i /dev/video0 http://localhost:8090/feed1.ffm
ffmpeg -f video4linux -s 320x240 -r 5 -i /dev/video1 http://localhost:8090/feed2.ffm

Si tus cámaras fuesen IP habría que capturar primero el vídeo desde la cámara y pasárselo a ffmpeg, por ejemplo:

curl http://ip-de-tu-camara:8080 | ffmpeg -er 4 -y -v quiet -an -f mjpeg -r 4 -i - http://localhost:8090/feed1.ffm

Sea como sea el formato de tus cámaras, y si todo ha ido bien, tendrás a ffmpeg enviando el vídeo a ffserver para que lo convierta a FLV y lo publique en las URL’s anteriormente indicadas.

Debes tener cuidado con un detalle importante si conectas más de una cámara USB, debes enchufarlas a concentradores distintos de tu equipo para diversificar adecuadamente los flujos de vídeo y el ancho de banda que ocupan, si conectases dos cámaras al mismo concentrador no tendrían ancho de banda suficiente y se bloquearían entre ellas.

Veamos ahora como visualizar los flujos de vídeo. Necesitaremos el editor de Flash de toda la vida. Creamos un nuevo documento y arrastramos al escenario un objeto FLVPlayback. Como nombre de instancia le ponemos “video” y en sus propiedades ponemos isLive a true. En el primer frame añadimos el siguiente código:

video.contentPath=path_video;

De esta manera podremos indicar desde html la url del flujo de vídeo sin tener que tener un swf por cada cámara. Compila y publica el proyecto puesto que ya hemos terminado.

No me voy a preocupar ahora del código html generado, eso es cosa tuya. Dentro del archivo html que te genera el editor de flash tendrás algo como:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="550" height="400" id="webcam1" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="webcam.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="webcam.swf" mce_src="webcam.swf" quality="high" bgcolor="#ffffff" width="550" height="400" name="webcam1" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>

Vamos a convertirlo en esto:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="550" height="400" id="webcam1" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="webcam.swf?path_video=http://192.168.90.1:8090/camara1.flv" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="webcam.swf?path_video=http://192.168.90.1:8090/camara1.flv" mce_src="webcam.swf?path_video=http://192.168.3.2:8090/camara1.flv" quality="high" bgcolor="#ffffff" width="550" height="400" name="webcam1" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>

Es decir, añadimos al parámetro de la película la variable path_video con la url del flujo tal y como lo hemos generado antes. Debe ser una URL a la que se pueda acceder externamente pues el swf se conectará a ella para recoger el flujo flv generado por ffserver, si tienes un router o un firewall de por medio, deberás abrir ese puerto. Copiaremos este código tantas veces como cámaras tengamos modificando la URL del flujo de vídeo.

Voila! esto es todo, ya tienes publicadas tus cámaras en un formato sencillo de visualizar como es el FLV.  Abre la página html que has creado y comenzarás a visualizar tus cámaras.

Pero… vayamos un paso más allá. Con lo que hemos visto hasta ahora tendremos que abrir el puerto 8090 de buestra máquina para permitir el acceso a los flujos de vídeo, pero nosotros no queremos hacer esto, ya tenemos el puerto 80 con Apache a la escucha. ¿Qué hacemos? Sencillo, mod_proxy viene a nuestro rescate. Asegúrate de tenerlo habilitado en la configuración de tu Apache. Después simplemente habrá que añadir las siguientes líneas a la configuración del virtual host en el que quieres publicar los streams:

ProxyPass /camara1.flv http://127.0.0.1:8090/camara1.flv
ProxyPassReverse /camara1.flv http://127.0.0.1:8090/camara1.flv        

ProxyPass /camara2.flv http://127.0.0.1:8090/camara2.flv
ProxyPassReverse /camara2.flv http://127.0.0.1:8090/camara2.flv

Con esto estaremos enviando automáticamente cada stream desde su url original en el puerto 8090 a una url de tu virtual host del tipo http://tudominio.com/camaraX.flv. Esa ruta no existe físicamente, es virtual y Apache sabe que debe redirigirla al stream del ffserver.

Finalmente queda un detalle importante. Hasta ahora habíamos incializado ffserver por un lado y los flujos de las cámaras por otro con ffmpeg. Esto es un rollo y no nos gusta, queremos que ffserver lo inicie todo automáticamente. Pues vale, añadamos una directiva Launch a cada feed de la configuración de ffserver con los parámetros que utilizábamos para llamarlo desde la línea de comandos. En el ejemplo de nuestra primera cámara quedaría:

<Feed feed1.ffm>
  File /tmp/feed1.ffm
  FileMaxSize 5M
  Launch  ffmpeg -f video4linux -s 320x240 -r 5 -i /dev/video0
</Feed>

A partir de ahora, cada vez que lancemos ffserver tecleando simplemente ese comando, se inciarán, además del propio servidor, los streams que hayamos definido.

Hasta aquí hemos llegado por hoy. De una manera extremadamente sencilla hemos publicado nuestras webcams. Es responsabilidad tuya y sólamente tuya el asegurar el acceso a los flujos y a la página de visualización que hemos creado. Otro día quizás veamos cómo montar un sistema de videovigilancia casero con detector de presencia 😉 .

Os dejo el código de la aplicación Flash de visualización de los streams.

21 comentarios en “Cómo emitir webcams en vivo con un servidor Linux y en formato FLV

  1. Partiendo de que:

    1º, el problema estaba medio resuelto de partida ya que gecko y webkit (2 de 4 motores HTML, y además multiplataforma) soportan el stream mjpeg de manera nativa sin transcodear. Y

    2º, que prácticamente no hay diferencia en el funcionamiento del tag OBJECT con Java respecto al funcionamiento del tag OBJECT con Flash desde el punto de vista de alguien navegando una página web…

    Pienso que transcodear el video a flv sólo para dar soporte a IE y Opera mediante Flash es:

    1º, un pelin excesivo en cuanto a consumo de recursos en el servidor: es más ‘barato’ en consumo de CPU, memoria y tiempo de proceso discriminar por navegador y servir un applet a quien lo necesite, que transcodear todo el video en otro formato. Aunque claro, CPU y memoria suelen ser cada vez más baratas… de precio, y el video de las webcams tampoco suele tener demasiada resolución.

    Y 2º, un pelin snob por descartar Java en favor de Flash: las objeciones que has puesto a Java en el artículo se aplican a Flash exactamente igual (integración en una página web, y ser aplicación de terceros).

    Un saludo.

  2. Hola maeghith !

    ¿Has visto el título del artículo? La idea era hacerlo en FLV. Ya menciono en el artículo que otros navegadores soportan el flujo mjpeg, pero también sabes quién es el mayoritario y quién no lo soporta ¿no?

    Por otro lado en ningún momento, corrígeme si me equivoco, hablo de pros/contras de Java ni de objeciones, simplemente digo que prefiero prescindir de los applets Java. ¿Por qué? porque he visto a cientos y cientos de personas que no pueden verlos o que les saltan errores al hacerlo. Sí, muchos más que con Flash. No es cuestión de snobismo sino de practicismo.

    De todos modos, insisto, la idea era explicar como obtener un stream, convertirlo a otros formatos y visualizarlo. Guste o no hoy en día flv es el más cómodo para utilizar en web, es la única razón de haberlo elegido, pero también puedes emitirlo en mpeg y utilizar otro plugin. Lo interesante es la idea en sí misma del sistema.

    Gracias por tu comentario :)-

  3. Hola oranguntan,

    Mjpeg no es un formato de vídeo como tal sino una sucesión de imágenes jpeg independientes entre sí que a ser reproducidas una tras otra da la sensación de vídeo.
    El ancho de banda dependerá fundamentalmente de los fps y de la calidad de cada frame en el caso del mjpeg y del bitrate en el caso de cualquier otro formato de vídeo.
    De todos modos mjpeg SIEMPRE generará archivos de mayor tamaño que cualquier formato de vídeo puesto que los frames son independientes entre sí, luego necesitará mayor ancho de banda. En un formato de vídeo clásico (mpeg1, mpeg4, flv…) los frames se relacionan eliminando información redundante.

    Saludos
    Osus

  4. Sí, claro que he visto el título. Pero los títulos describen el contenido del artículo que, en este caso, refleja una experiencia. No escribiste el título y luego tuviste la experiencia de acuerdo con él. Precisamente trataba de rebatir la conclusión del análisis que describes en el artículo de que “tenía que ser flv”, que me pareció algo sesgada 🙂

    También se quien es el mayoritario, por eso comento que de los aproximadamente 4 engines existentes (IE, gecko, webkit y opera), hay 2 que sí lo soportan… a ver si colaba (si no se hace presión para que cambien las cosas por este lado, no van a cambiar así que hay que empujar un poco a cada ocasión) 😀

    No he visto tanta diferencia entre los problemas de Java (principalmente debidos a la VM de MS y fácilmente solucionables instalando la de Sun, especialmente a estas alturas) frente a los de Flash (sonido que desaparece por que sí o problemas con versiones de 64bits en algunos sistemas). Y esos errores de Java que comentas ahora, no estaban en el artículo telépatas no somos 🙂

    A mi me ha parecido que los comentarios sobre Java en el artículo eran más del lado de las objeciones: si no hay algo que objetarle a java ¿por qué querer evitarlo (sí dices que queríais hacerlo “evitando […] los applets java”)?, sobre todo pensando en el costo de la conversión de formatos que comento, así que he mapeado los factores pro-Flash del artículo a la lista de objeciones a Java.

    Pero bueno, si parte de la idea era precisamente hacer transcoding en un servidor, nada, nada, transcodea lo que quieras, que no lo había entendido y por eso intentaba evitarlo XD

  5. Hola de nuevo maeghith,

    Creo que no me he explicado bien del todo.

    Mjpeg no es un sistema de vídeo fluído. Intenta crear un flujo a 15fps y después visualizarlo con soltura 😉 .

    Cuando el cliente nos planteó el asunto este de las cámaras (hará 4 o 5 años), no sabíamos cómo lo íbamos a hacer hasta que nos metimos en faena. Mjpeg, por razones obvias, fue la primera elección, pero terminamos decidiendo lo del flv porque resultaba mejor opción en todos los aspectos.

    Sigo pensando, y esto sí que ya es opinión personal, que los applets Java dan muchísimos más problemas que los objetos Flash, no es una cuestión de quién es mejor o quién es peor. Ni siquiera es una cuestión de que tenga una línea de tiempo y hagas dibujitos 😉 , ¿has probado Flex?. Sólo es que el rendimiento y el funcionamiento son, con creces, mejores. Tú me dirás, bueno, pues a mí me van igual de bien (o de mal). Yo te digo, bueno, pues en mi Centos5 de 64bits funciona el plugin de flash sin problemas.

    Del mismo modo, cuando decía “sin aplicaciones de terceros”, me refería a no utilizar algo que no hubíesemos hecho nosotros. No es lo mismo programar un cambozola.jar desde cero que el swf que hemos hecho en el artículo en 30 segundos con una línea de código. No era por incluir o no un objeto, también podríamos haber sacado el stream en asf y que se abriese con el Windows Media Player, pero sabemos que sería la peor opción 😛 .

    En resumen, maeghith, el artículo es un ejemplo de conversión “on the fly” personalizada en este proyecto donde, como sabes, intentar convencer al cliente de que use otro navegador es algo casi imposible.

  6. Hoola holita 🙂

    “””

    Mjpeg no es un sistema de vídeo fluído. Intenta crear un flujo a 15fps y después visualizarlo con soltura 😉 .

    “””

    ¿Te refieres a la soltura a la hora de la reproducción?, por que si el flujo de origen de la cámara tiene 15fps, por mucho que transcodees, no vas a mejorar eso (seguro que lo sabes). Pero si te referiás al rendimiento que había hace 4 ó 5 años a la hora de reproducirlos ahí ya no entro (eh, que ahora mismo los ordenadores de mi casa tienen 5 años o más XD no se me olvida como funciona(ba)n XD ).

    La ventaja que sí tiene transcodear es la reducción del ancho de banda. Como le comentas a orangutan, el mjpg no tiene en cuenta la información compartida de cuadro a cuadro, cosa que otros codecs sí tienen. Con las conexiones de hace 4 ó 5 años, probablemente el flujo de 15fps se viera reducido a 4 ó 5fps por el ancho de banda.

    Lo de los problemas entiendo que por aquel entonces fuese algo a tener en cuenta, hoy en día en ambos casos la solución es sencilla. Lo de los dibujitos y la timeline es más ‘histórico’ que otra cosa.

    Lo que ahora me parece un poco exagerado es decir que el player lo habéis programado vosotros (si no llegas a insistir en esto se me habría pasado), cuando el objeto FLVPlayback es un componente estándar programado por adobe/macromedia, donde lo único que habéis hecho es incluirlo en el proyecto y configurarlo. Incluir+configurar y programar son cosas distintas. El componente FLVPlayback es tan código de 3º como el cambozola.jar.

    “””

    intentar convencer al cliente de que use otro navegador es algo casi imposible.

    “””

    Tampoco estaba yo intentando convencer al cliente de que usara otro navegador, precisamente usar Java no requeriría que el cliente lo cambiase.

    De hecho, no habría objetado nada si hubieses explicado de verdad los porqués de no usar java (en el artículo dices “java no” pero no lo explicas, lo explicas respondiendo a mi comentario), y del ahorro en ancho de banda de hacer el transcoding.

  7. Pues eso, hablamos de lo mismo y sin embargo no nos entendemos.

    La diferencia del componente de flash es que podríamos haber hecho más cosas que simplemente emitir el vídeo, cosa que con el otro no podrías, por eso en el artículo tampoco digo en ningún momento “programar”, o mejor dicho, lo hago sólo para referirme precisamente al java 😛 .

    Finalmente en el artículo no explicaba nada de esto porque en el artículo se explicaba lo otro 😛 , es decir, no era un planteamiento de si hacer esto o aquello sino que directamente se planteaba hacer aquello. La discusión la lanzaste tú y por eso lo estamos discutiendo.

    Lo dicho anteriormente, que a partir del ejemplo el lector puede decidir como hacerlo, doy las indicaciones. De hecho hoy en día, y si tienes una buena cámara, se podría hacer en H264 con apenas cambios en todo el proceso y el resultado sería espectacular en cuanto a calidad 🙂 .

  8. Hola norma,

    Debería ser tu hijo el que preguntase ¿no?
    La pregunta es demasiado genérica. ¿cómo lo tiene que hacer? ¿se ha documentado ya un poco? ¿qué dudas tiene?

  9. Oe man muy buena voz, nos ayudaste mucho a mi y mi cuma, como no te conosco para imbitarte una cerveza …
    Me voy a dar un tiempo para publicar el manual desde la compilacion de modulos para el reconocimiento de las camaras..
    Un saludo y gracias a ti y al papa tux XD…

  10. Exelente articulo, es algo que estaba buscando, o sea no usar ningún applet de java para visualizar una webcam en un sitio web, motivos? creo que es una complicación para cualquier usuario tener que instalar un plug in y ni hablar de java, no olvidemos que un usuario con conocimientos básicos no necesita ni quiere complicaciones , ya se muchos me van a decir que instalar java no es ninguna complicación , bueno eso para vos…para mis clientes es complicado escribir un @….(…alt grande + q don roberto…).

    Bueno para mi esta fuera de discución, el uso de flash en vez de java es simplemente bellisimo, sobre todo por la simplicidad para desarrollar aplicaciones en ese entorno y lo difundido que esta ese plug in…a quien le pese.

    Lo único que me preocupa, es lo comentado por el amigo maeghith, que resalta el posible problema de uso exesivo de cpu y demas. Es mucho el requerimiento de cpu para recodificar el video con este metodo? poner varios usuarios con este sistema es viable?

    Muchisimas gracias por el artículo y por compartir conocimiento.

  11. Hola Juan,

    La verdad es que no te puedo ayudar mucho, no sé exactamente cual puede ser el problema
    Podrías revisar la documentación y confirmar primero que el flujo de vídeo funciona correctamente.
    Siento no poder darte más información.

  12. muchas gracias ya lo solucione el problema del flujo de video utilizando video4linux2 (version 2) y la resolucion vga, pero parece que tu pagina donde usas el applet/flash no funciona, carga el objeto de flash pero no veo ningun video

    gracias

  13. Hola Osus, muy interesante y completo tu articulo.

    Yo tengo un servidor CENTOS remoto (yo estoy en Colombia y el servidor en Canadá), ya he instalado el FFMPEG y funciona bien, de hecho para conversion de archivos ya lo he probado y funciona perfecto en mi CLIP-BUCKET (you-tube clone).

    Mi pregunta es la siguiente:

    Desde mi PC Windows, ¿cómo puedo transmitir el video de mi webcam USB o de mi videocamara FireWire al FFserver para que otras personas puedan visualizarlo en mi pagina web?

  14. ¿Conoces alguna aplicacion para windows con la que pueda enviar el flujo de video? ¿cómo lo hago?

    Pues el comando:

    ffmpeg -f video4linux -s 320×240 -r 5 -i /dev/video1 http://localhost:8090/feed2.ffm

    se refiere a un dispositivo local y como te escribi antes necesito enviar la imagen de forma remota y con cámaras que no tienen una IP fija.

    Traté de usar VLC, pero no entendí como hacerlo.

    Agradezco tu respuesta.

  15. ¿Sería posible obtener además de la señal de vídeo de una cámara IP, una señal de audio de la misma cámara y mezclarla con la de vídeo? Según creo ffmpeg sí puede mezclar distintas señales, pero creo que cURL no puede descargar dos señales en paralelo.
    ¿Conoces alguna herramienta alternativa a cURL para poder descargar en paralelo y con la posibilidad de redirigir la salida a ffmpeg?

  16. Creo que maeghith esta defendiendo lo indefendible, el tiempo y la tecnologia dieron la razon a Osus (El autor del post), alli teneis a youtube y a tantas paginas de video streaming que trabajan en flv, y por demas muy populares.

Responder a Facundo Cancelar la respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *