reproductor reproducir para insertar etiqueta codigo html5 node.js ffmpeg streaming

reproducir - video html5 autoplay



El mejor enfoque para la transmisión http en tiempo real al cliente de video HTML5 (9)

EDIT 3: A partir de IOS 10, HLS admitirá archivos mp4 fragmentados. La respuesta ahora es crear activos mp4 fragmentados, con un manifiesto DASH y HLS. > Pretend flash, iOS9 e inferior e IE 10 e inferior no existen.

Todo lo que está debajo de esta línea está fuera de fecha. Manteniéndolo aquí para la posteridad.

EDIT 2: Como las personas en los comentarios están señalando, las cosas cambian. Casi todos los navegadores soportarán codecs AVC / AAC. iOS aún requiere HLS. Pero a través de adaptadores como hls.js puedes jugar HLS en MSE. La nueva respuesta es HLS + hls.js si necesita iOS. o simplemente Fragmentado MP4 (es decir, DASH) si no lo hace

Hay muchas razones por las que el video y, específicamente, el video en vivo es muy difícil. (Tenga en cuenta que la pregunta original especificaba que el video HTML5 es un requisito, pero el autor de la pregunta dijo que Flash es posible en los comentarios. Entonces, de inmediato, esta pregunta es engañosa)

Primero repito: NO HAY SOPORTE OFICIAL PARA STREAMING EN VIVO SOBRE HTML5 . Hay hacks, pero su kilometraje puede variar.

EDITAR: desde que escribí esta respuesta, Media Source Extensions ha madurado, y ahora están muy cerca de convertirse en una opción viable. Son compatibles con la mayoría de los principales navegadores. IOS sigue siendo una espera.

A continuación, debe comprender que el video a pedido (VOD) y el video en vivo son muy diferentes. Sí, ambos son video, pero los problemas son diferentes, por lo tanto, los formatos son diferentes. Por ejemplo, si el reloj de su computadora funciona 1% más rápido de lo que debería, no lo notará en un VOD. Con el video en vivo, estarás intentando reproducir el video antes de que suceda. Si desea unirse a un flujo de video en vivo en progreso, necesita los datos necesarios para inicializar el decodificador, por lo que debe repetirse en el flujo o enviarse fuera de banda. Con VOD, puede leer el principio del archivo y buscar el punto que desee.

Ahora vamos a profundizar un poco.

Plataformas:

  • iOS
  • ordenador personal
  • Mac
  • Androide

Codecs:

  • vp8 / 9
  • h.264
  • thora (vp3)

Métodos comunes de entrega de video en vivo en los navegadores:

  • DASH (HTTP)
  • HLS (HTTP)
  • flash (RTMP)
  • flash (HDS)

Métodos de entrega comunes para VOD en los navegadores:

  • DASH (HTTP Streaming)
  • HLS (transmisión HTTP)
  • flash (RTMP)
  • flash (transmisión HTTP)
  • MP4 (pseudo streaming HTTP)
  • No voy a hablar sobre MKV y OOG porque no los conozco muy bien.

etiqueta de vídeo html5:

  • MP4
  • webm
  • ogg

Veamos qué navegadores soportan qué formatos

Safari:

  • HLS (solo iOS y mac)
  • h.264
  • MP4

Firefox

  • DASH (a través de MSE pero no h.264)
  • h.264 a través de Flash solamente!
  • VP9
  • MP4
  • OGG
  • Webm

ES DECIR

  • Destello
  • DASH (solo a través de MSE IE 11+)
  • h.264
  • MP4

Cromo

  • Destello
  • DASH (a través de MSE)
  • h.264
  • VP9
  • MP4
  • webm
  • ogg

MP4 no se puede usar para video en vivo (NOTA: DASH es un superconjunto de MP4, así que no te confundas con eso). MP4 se divide en dos piezas: moov y mdat. mdat contiene los datos de video de audio en bruto. Pero no está indexado, así que sin el moov, es inútil. El moov contiene un índice de todos los datos en el mdat. Pero debido a su formato, no se puede "aplanar" hasta que se conozcan las marcas de tiempo y el tamaño de CADA fotograma. Puede ser posible construir un moov que "modifique" el tamaño del marco, pero es un ancho de banda muy inútil.

Entonces, si desea realizar entregas en cualquier lugar, necesitamos encontrar el mínimo denominador común. Verá que no hay LCD aquí sin recurrir al ejemplo de flash:

  • iOS solo soporta video h.264. y solo soporta HLS para live.
  • Firefox no es compatible con h.264, a menos que uses flash
  • Flash no funciona en iOS

Lo más parecido a una pantalla LCD es usar HLS para obtener usuarios de iOS y flash para todos los demás. Mi favorito personal es codificar HLS, luego usar flash para reproducir HLS para todos los demás. Puedes jugar HLS en flash a través de JW player 6, (o escribir tu propio HLS a FLV en AS3 como lo hice)

Pronto, la forma más común de hacer esto será HLS en iOS / Mac y DASH a través de MSE en cualquier otro lugar (esto es lo que Netflix estará haciendo pronto). Pero todavía estamos esperando que todos actualicen sus navegadores. También es probable que necesites un DASH / VP9 separado para Firefox (conozco open264; apesta. No puede hacer video en perfil principal o alto. Por lo tanto, actualmente es inútil).

Estoy realmente atascado tratando de entender la mejor manera de transmitir la salida en tiempo real de ffmpeg a un cliente HTML5 usando node.js, ya que hay varias variables en juego y no tengo mucha experiencia en este espacio. Habiendo pasado muchas horas probando diferentes combinaciones.

Mi caso de uso es:

1) La transmisión de la cámara de video IP RTSP H.264 es captada por FFMPEG y envuelta en un contenedor mp4 usando las siguientes configuraciones de FFMPEG en el nodo, salida a STDOUT. Esto solo se ejecuta en la conexión inicial del cliente, de modo que las solicitudes de contenido parciales no intenten generar FFMPEG nuevamente.

liveFFMPEG = child_process.spawn("ffmpeg", [ "-i", "rtsp://admin:[email protected]:554" , "-vcodec", "copy", "-f", "mp4", "-reset_timestamps", "1", "-movflags", "frag_keyframe+empty_moov", "-" // output to stdout ], {detached: false});

2) Utilizo el servidor http del nodo para capturar el STDOUT y lo transmito al cliente a petición del cliente. Cuando el cliente se conecta por primera vez, engendro la línea de comando FFMPEG anterior y luego canalizo la secuencia STDOUT a la respuesta HTTP.

liveFFMPEG.stdout.pipe(resp);

También he usado el evento de flujo para escribir los datos FFMPEG en la respuesta HTTP, pero no hay diferencia

xliveFFMPEG.stdout.on("data",function(data) { resp.write(data); }

Uso el siguiente encabezado HTTP (que también se usa y funciona cuando se transmiten archivos pregrabados)

var total = 999999999 // fake a large file var partialstart = 0 var partialend = total - 1 if (range !== undefined) { var parts = range.replace(/bytes=/, "").split("-"); var partialstart = parts[0]; var partialend = parts[1]; } var start = parseInt(partialstart, 10); var end = partialend ? parseInt(partialend, 10) : total; // fake a large file if no range reques var chunksize = (end-start)+1; resp.writeHead(206, { ''Transfer-Encoding'': ''chunked'' , ''Content-Type'': ''video/mp4'' , ''Content-Length'': chunksize // large size to fake a file , ''Accept-Ranges'': ''bytes '' + start + "-" + end + "/" + total });

3) El cliente tiene que usar etiquetas de video HTML5.

No tengo problemas con la reproducción de secuencias (usando fs.createReadStream con 206 contenido parcial HTTP) en el cliente HTML5 un archivo de video grabado previamente con la línea de comando FFMPEG anterior (pero guardado en un archivo en lugar de STDOUT), así que conozco la transmisión FFMPEG es correcto, e incluso puedo ver correctamente la transmisión de video en vivo en VLC cuando me conecto al servidor de nodo HTTP.

Sin embargo, intentar transmitir en vivo desde FFMPEG a través del nodo HTTP parece ser mucho más difícil, ya que el cliente mostrará un cuadro y luego se detendrá. Sospecho que el problema es que no estoy configurando la conexión HTTP para que sea compatible con el cliente de video HTML5. He intentado una variedad de cosas como usar HTTP 206 (contenido parcial) y 200 respuestas, poner los datos en un búfer y luego transmitirlos sin suerte, por lo que debo volver a los primeros principios para asegurarme de que estoy configurando la configuración correcta. camino.

Aquí está mi comprensión de cómo debería funcionar esto, corríjame si me equivoco:

1) FFMPEG debe configurarse para fragmentar la salida y usar un moov vacío (FFMPEG frag_keyframe y empty_moov mov flags). Esto significa que el cliente no usa el átomo de moov, que normalmente está al final del archivo, lo cual no es relevante cuando se transmite (sin final del archivo), pero significa que no es posible buscar, lo cual está bien para mi caso de uso.

2) A pesar de que uso fragmentos MP4 y MOOV vacío, todavía tengo que usar contenido parcial HTTP, ya que el reproductor HTML5 esperará hasta que se descargue toda la transmisión antes de reproducir, que con una transmisión en vivo nunca termina, por lo que no es viable.

3) No entiendo por qué canalizar la transmisión STDOUT a la respuesta HTTP no funciona cuando se transmite en vivo todavía si guardo en un archivo puedo transmitir este archivo fácilmente a clientes HTML5 utilizando un código similar. Tal vez sea un problema de tiempo, ya que el inicio de FFMPEG demora un segundo en iniciarse, conectarse a la cámara IP y enviar fragmentos al nodo, y los eventos de datos del nodo también son irregulares. Sin embargo, el bytestream debería ser exactamente lo mismo que guardar en un archivo, y HTTP debería poder atender los retrasos.

4) Cuando verifico el registro de red del cliente HTTP al transmitir un archivo MP4 creado por FFMPEG desde la cámara, veo que hay 3 solicitudes de clientes: una solicitud GET general para el video, que el servidor HTTP devuelve alrededor de 40Kb, luego una parcial solicitud de contenido con un rango de bytes para los últimos 10K del archivo, luego una solicitud final para los bits en el medio no cargados. ¿Quizás el cliente HTML5, una vez que reciba la primera respuesta, solicite la última parte del archivo para cargar el átomo MP4 MOOV? Si este es el caso, no funcionará para la transmisión, ya que no hay un archivo MOOV ni un final del archivo.

5) Cuando verifico el registro de la red cuando intento transmitir en vivo, recibo una solicitud inicial abortada con solo 200 bytes recibidos, luego una solicitud nuevamente se cancela con 200 bytes y una tercera solicitud que solo tiene 2K de longitud. No entiendo por qué el cliente HTML5 abortaría la solicitud, ya que Bytestream es exactamente el mismo que puedo usar con éxito al transmitir desde un archivo grabado. También parece que el nodo no está enviando el resto de la secuencia FFMPEG al cliente, pero puedo ver los datos FFMPEG en la rutina de eventos .on, por lo que está llegando al servidor HTTP del nodo FFMPEG.

6) Aunque creo que la transmisión de la secuencia STDOUT al búfer de respuesta HTTP debería funcionar, tengo que crear un búfer intermedio que permita que las solicitudes del cliente de contenido parcial HTTP funcionen correctamente como lo hace cuando (con éxito) lee un archivo ? Creo que esta es la razón principal de mis problemas, sin embargo, en Node no estoy seguro de cómo configurarlos de la mejor manera. Y no sé cómo manejar una solicitud de cliente para los datos al final del archivo ya que no hay final de archivo.

7) ¿Estoy en el camino equivocado al tratar de manejar 206 solicitudes de contenido parciales, y debería funcionar con las 200 respuestas HTTP normales? Las respuestas HTTP 200 funcionan bien para VLC, así que sospecho que el cliente de video HTML5 solo funcionará con solicitudes de contenido parcial.

Como todavía estoy aprendiendo esto, es difícil trabajar con las diversas capas de este problema (FFMPEG, nodo, transmisión, HTTP, video HTML5), por lo que cualquier puntero será muy apreciado. He pasado horas investigando en este sitio y en la red, y no he encontrado a nadie que haya podido hacer streaming en tiempo real en el nodo pero no puedo ser el primero, y creo que esto debería poder funcionar (de alguna manera) !).



Echa un vistazo a esta solución . Como sé, Flashphoner permite reproducir secuencias de audio y video en vivo en la página de HTML5 puro.

Utilizan los códecs MPEG1 y G.711 para la reproducción. El truco está renderizando el video decodificado al elemento lienzo HTML5 y reproduciendo audio decodificado a través del contexto de audio HTML5.


Echa un vistazo al proyecto JSMPEG . Hay una gran idea implementada allí: descifrar MPEG en el navegador usando JavaScript. Los bytes del codificador (FFMPEG, por ejemplo) se pueden transferir al navegador usando WebSockets o Flash, por ejemplo. Si la comunidad se pone al día, creo que será la mejor solución de transmisión de video en vivo HTML5 por ahora.


Escribí un reproductor de video HTML5 alrededor del codec h264 de broadway (emscripten) que puede reproducir video h264 en vivo (sin demora) en todos los navegadores (de escritorio, iOS, ...).

El flujo de video se envía a través de websocket al cliente, se decodifica fotograma por fotograma y se muestra en un canva (usando webgl para la aceleración)

Echa un vistazo a https://github.com/131/h264-live-player en github.


Esto es un malentendido muy común. No hay soporte para video HTML5 en vivo (excepto para HLS en iOS y Mac Safari). Es posible que pueda "piratearlo" utilizando un contenedor webm, pero no espero que sea universalmente compatible. Lo que está buscando está incluido en las Extensiones de origen de medios, donde puede enviar los fragmentos al navegador de uno en uno. pero tendrá que escribir algún javascript del lado del cliente.


Gracias a todos, especialmente a szatmary, ya que esta es una pregunta compleja y tiene muchas capas, todas las cuales tienen que estar funcionando antes de poder transmitir video en vivo. Para aclarar mi pregunta original y el uso de video HTML5 vs flash: mi caso de uso tiene una fuerte preferencia por HTML5 porque es genérico, fácil de implementar en el cliente y en el futuro. Flash es el segundo mejor, así que sigamos con HTML5 para esta pregunta.

Aprendí mucho a través de este ejercicio y estoy de acuerdo en que la transmisión en vivo es mucho más difícil que VOD (que funciona bien con el video HTML5). Pero conseguí que esto funcionara satisfactoriamente para mi caso de uso y la solución resultó ser muy simple, después de buscar opciones más complejas como MSE, flash, esquemas de almacenamiento en búfer en Node. El problema fue que FFMPEG estaba corrompiendo el MP4 fragmentado y tuve que ajustar los parámetros de FFMPEG, y todo lo que se necesitaba era la redirección de flujo de nodo estándar a través de http que usé originalmente.

En MP4 hay una opción de ''fragmentación'' que rompe el mp4 en fragmentos mucho más pequeños que tiene su propio índice y hace que la opción de transmisión en vivo de mp4 sea viable. Pero no es posible volver a buscar en el flujo (OK para mi caso de uso), y las versiones posteriores de FFMPEG admiten la fragmentación.

Tenga en cuenta que el tiempo puede ser un problema, y ​​con mi solución tengo un retraso de entre 2 y 6 segundos debido a una combinación de la remuxing (efectivamente, FFMPEG tiene que recibir la transmisión en vivo, debe enviarla al nodo para su servicio a través de HTTP) . No se puede hacer mucho al respecto; sin embargo, en Chrome, el video trata de ponerse al día tanto como puede, lo que hace que el video sea un poco nervioso pero más actual que IE11 (mi cliente preferido).

En lugar de explicar cómo funciona el código en esta publicación, echa un vistazo al GIST con comentarios (el código del cliente no está incluido, es una etiqueta de video HTML5 estándar con la dirección del servidor http del nodo). GIST está aquí: https://gist.github.com/deandob/9240090

No he podido encontrar ejemplos similares de este caso de uso, así que espero que la explicación y el código anteriores ayuden a otros, especialmente porque aprendí mucho de este sitio y aún me considero un principiante.

Aunque esta es la respuesta a mi pregunta específica, he seleccionado la respuesta de szatmary como la aceptada, ya que es la más completa.


Pruebe binaryjs. Es como socket.io, pero lo único que hace bien es que transmite video de audio. Binaryjs google


Una forma de transmitir en vivo una cámara web basada en RTSP a un cliente HTML5 (implica la recodificación, por lo que debe esperarse una pérdida de calidad y necesita algo de potencia de CPU):

  • Configure un servidor icecast (podría estar en la misma máquina en la que se encuentra el servidor web o en la máquina que recibe la transmisión RTSP de la cámara)
  • En la máquina que recibe la transmisión de la cámara, no use FFMPEG sino gstreamer. Puede recibir y decodificar el flujo RTSP, volver a codificarlo y transmitirlo al servidor icecast. Ejemplo de canalización (solo vídeo, sin audio):

    gst-launch-1.0 rtspsrc location=rtsp://192.168.1.234:554 user-id=admin user-pw=123456 ! rtph264depay ! avdec_h264 ! vp8enc threads=2 deadline=10000 ! webmmux streamable=true ! shout2send password=pass ip=<IP_OF_ICECAST_SERVER> port=12000 mount=cam.webm

=> Luego puede usar la etiqueta <video> con la URL de icecast-stream ( http://127.0.0.1:12000/cam.webm ) y funcionará en cada navegador y dispositivo que admita webm