Der beste Ansatz für Echtzeit-HTTP-Streaming zum HTML5-Video-Client

Ich bin wirklich dabei, zu versuchen, den besten Weg zu verstehen, Echtzeit-Ausgabe von ffmpeg zu einem HTML5-Client mit node.js zu streamen, da es eine Reihe von Variablen im Spiel gibt und ich nicht viel Erfahrung in diesem Raum habe, Ich habe viele Stunden damit verbracht, verschiedene Kombinationen auszuprobieren.

Mein Anwendungsfall ist:

1) IP-Videokamera Der RTSP H.264-Stream wird von FFMPEG aufgenommen und in einen mp4-Container remuxed, wobei die folgenden FFMPEG-Einstellungen im Knoten verwendet und an STDOUT ausgegeben werden. Dies wird nur bei der ersten Clientverbindung ausgeführt, sodass partielle Inhaltsanforderungen nicht versuchen, FFMPEG erneut zu erstellen.

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

2) Ich benutze den Knoten http-Server, um den STDOUT zu erfassen und ihn bei einer Client-Anfrage zurück zum Client zu streamen. Wenn der Client zum ersten Mal eine Verbindung herstellt, spawne ich die obige FFMPEG-Befehlszeile und pipe den STDOUT-Stream zur HTTP-Antwort.

 liveFFMPEG.stdout.pipe(resp); 

Ich habe auch das stream-Ereignis verwendet, um die FFMPEG-Daten in die HTTP-Antwort zu schreiben, macht aber keinen Unterschied

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

Ich verwende den folgenden HTTP-Header (der auch beim Streamen von aufgezeichneten Dateien verwendet wird)

 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) Der Kunde muss HTML5-Video-Tags verwenden.

Ich habe keine Probleme mit Streaming-Wiedergabe (mit fs.createReadStream mit 206 HTTP-Partial-Content) an den HTML5-Client eine zuvor mit der obigen FFMPEG-Befehlszeile aufgenommene Videodatei (aber in einer Datei statt STDOUT gespeichert), so kenne ich den FFMPEG-Stream ist korrekt, und ich kann sogar das Video-Live-Streaming in VLC sehen, wenn ich mich mit dem HTTP-Node-Server verbinde.

Allerdings scheint es viel schwieriger zu sein, live von FFMPEG über den Knoten HTTP zu streamen, da der Client einen Frame anzeigt und dann stoppt. Ich vermute, dass das Problem darin besteht, dass ich die HTTP-Verbindung nicht so einrichte, dass sie mit dem HTML5-Video-Client kompatibel ist. Ich habe eine Menge Dinge ausprobiert, wie HTTP 206 (teilweise Inhalt) und 200 Antworten, die Daten in einen Puffer zu schreiben und dann ohne Glück zu streamen, also muss ich zu den ersten Prinzipien zurückkehren, um sicherzustellen, dass ich das richtig mache Weg.

Hier ist mein Verständnis, wie das funktionieren sollte, bitte korrigiert mich, wenn ich falsch liege:

1) FFMPEG sollte eingerichtet werden, um die Ausgabe zu fragmentieren und ein leeres MOOV zu verwenden (FFMPEG frag_keyframe und empty_moov mov flags). Dies bedeutet, dass der Client nicht das Moov-Atom verwendet, das normalerweise am Ende der Datei steht, was beim Streaming nicht relevant ist (kein Dateiende), aber bedeutet, dass keine Suche möglich ist, was für meinen Anwendungsfall in Ordnung ist.

2) Obwohl ich MP4-Fragmente verwende und MOOV leugne, muss ich immer noch HTTP-Partial-Content verwenden, da der HTML5-Player wartet, bis der gesamte Stream vor dem Abspielen heruntergeladen wird, was bei einem Live-Stream niemals endet.

3) Ich verstehe nicht, warum das Streamen des STDOUT-Streams zu der HTTP-Antwort nicht funktioniert, wenn ich live streame, aber wenn ich in eine Datei speichere, kann ich diese Datei problemlos zu HTML5-Clients mit ähnlichem Code streamen. Vielleicht ist es ein Timing-Problem, da es eine Sekunde dauert, bis der FFMPEG-Spawn startet, eine Verbindung mit der IP-Kamera herstellt und Chunks an Knoten sendet, und auch die Knoten-Datenereignisse sind unregelmäßig. Der Bytestream sollte jedoch genauso wie das Speichern in einer Datei sein, und HTTP sollte in der Lage sein, Verzögerungen zu berücksichtigen.

4) Beim Überprüfen des Netzwerkprotokolls vom HTTP-Client beim Streamen einer von FFMPEG erstellten MP4-Datei von der Kamera, sehe ich 3 Clientanforderungen: Eine allgemeine GET-Anforderung für das Video, die der HTTP-Server etwa 40 KB zurückgibt, dann eine partielle Inhaltsanforderung mit einem Bytebereich für die letzten 10K der Datei, dann wird eine letzte Anforderung für die Bits in der Mitte nicht geladen. Vielleicht fragt der HTML5-Client nach Erhalt der ersten Antwort nach dem letzten Teil der Datei, um das MP4-MOOV-Atom zu laden. Wenn dies der Fall ist, funktioniert es nicht für das Streaming, da es keine MOOV-Datei und kein Ende der Datei gibt.

5) Beim Überprüfen des Netzwerkprotokolls beim Versuch, live zu streamen, erhalte ich eine abgebrochene erste Anfrage mit nur etwa 200 Bytes, dann eine erneute Anfrage mit 200 Bytes und eine dritte Anfrage, die nur 2K lang ist. Ich verstehe nicht, warum der HTML5-Client die Anfrage abbrechen würde, da der Bytestrom genau derselbe ist, den ich beim Streaming von einer aufgezeichneten Datei verwenden kann. Es scheint auch, dass Knoten den Rest des FFMPEG-Streams nicht an den Client sendet, aber ich kann die FFMPEG-Daten in der .on-Ereignisroutine sehen, so dass es zum HTTP-Server des FFMPEG-Knotens gelangt.

6) Obwohl ich denke, den STDOUT-Stream an den HTTP-Response-Puffer zu leiten, muss ich einen Zwischenpuffer und Stream erstellen, der es den HTTP-Partial-Content-Client-Anforderungen ermöglicht, ordnungsgemäß zu arbeiten, wenn sie eine Datei (erfolgreich) liest ? Ich denke, das ist der Hauptgrund für meine Probleme, aber ich bin mir bei Node nicht sicher, wie ich das am besten einrichten soll. Und ich weiß nicht, wie ich mit einer Client-Anfrage nach den Daten am Ende der Datei umgehen soll, da es kein Dateiende gibt.

7) Bin ich auf dem Holzweg, wenn ich versuche, 206 Teilinhaltsanforderungen zu bearbeiten, und sollte dies mit normalen 200 HTTP-Antworten funktionieren? HTTP 200-Antworten funktionieren gut für VLC, daher vermute ich, dass der HTML5-Videoclient nur mit unvollständigen Inhaltsanforderungen funktioniert?

Da ich immer noch dieses Zeug lerne, ist es schwierig, die verschiedenen Schichten dieses Problems (FFMPEG, Node, Streaming, HTTP, HTML5-Video) zu bearbeiten, so dass alle pointers sehr geschätzt werden. Ich habe Stunden damit verbracht, auf dieser Seite und im Internet zu recherchieren, und ich bin nicht auf jemanden gestoßen, der in Echtzeit in Knoten streamen konnte, aber ich kann nicht der Erste sein, und ich denke, das sollte funktionieren können (irgendwie !).

EDIT 3: Ab IOS 10 wird HLS fragmentierte mp4-Dateien unterstützen. Die Antwort ist jetzt, fragmentierte MP4-Assets mit einem DASH- und HLS-Manifest zu erstellen. > Vortäuschen Flash, iOS9 und darunter und IE 10 und darunter existieren nicht.

Alles unter dieser Linie ist veraltet. Halte es hier für die Nachwelt.


EDIT 2: Wie Leute in den Kommentaren darauf hinweisen, ändern sich die Dinge. Fast alle Browser unterstützen AVC / AAC-Codecs. iOS benötigt immer noch HLS. Aber über Adapter wie hls.js können Sie HLS in MSE spielen. Die neue Antwort lautet HLS + hls.js, wenn Sie iOS benötigen. oder einfach Fragmentiertes MP4 (dh DASH), wenn du es nicht tust

Es gibt viele Gründe, warum Video und insbesondere Live-Video sehr schwierig ist. (Bitte beachten Sie, dass die ursprüngliche Frage angegeben hat, dass HTML5-Video eine Voraussetzung ist, aber der Fragesteller erklärt, dass Flash in den Kommentaren möglich ist. Diese Frage ist also sofort irreführend)

Zuerst werde ich wiederholen: Es gibt keine offizielle Unterstützung für Live-Streaming über HTML5 . Es gibt Hacks, aber Ihre Laufleistung kann variieren.

EDIT: seit ich diese Antwort geschrieben habe Media Source Extensions sind gereift, und sind jetzt sehr nahe daran, eine praktikable Option. Sie werden von den meisten gängigen Browsern unterstützt. IOS ist weiterhin ein Durchhaltevermögen.

Als Nächstes müssen Sie verstehen, dass Video on Demand (VOD) und Live-Video sehr unterschiedlich sind. Ja, sie sind beide Video, aber die Probleme sind unterschiedlich, daher sind die Formate unterschiedlich. Zum Beispiel, wenn die Uhr in Ihrem Computer 1% schneller läuft als es sollte, werden Sie nicht auf einem VOD bemerken. Mit Live-Video werden Sie versuchen, Video abzuspielen, bevor es passiert. Wenn Sie an einem laufenden Live-Videostream teilnehmen möchten, benötigen Sie die Daten, die zum Initialisieren des Decoders erforderlich sind, sodass er im Stream wiederholt oder out-of-band gesendet werden muss. Mit VOD können Sie den Anfang der Datei lesen, die sie suchen, zu welchem ​​Punkt auch immer Sie wünschen.

Jetzt lass uns ein wenig nachdenken.

Plattformen:

  • iOS
  • PC
  • Mac
  • Android

Codecs:

  • VP8 / 9
  • h.264
  • Thora (vp3)

Allgemeine Bereitstellungsmethoden für Live-Videos in Browsern:

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

Allgemeine Bereitstellungsmethoden für VOD in Browsern:

  • DASH (HTTP-Streaming)
  • HLS (HTTP-Streaming)
  • flash (RTMP)
  • Flash (HTTP-Streaming)
  • MP4 (HTTP-Pseudostreaming)
  • Ich werde nicht über MKV und OOG reden, weil ich sie nicht gut kenne.

html5 video tag:

  • MP4
  • webm
  • ogg

Schauen wir uns an, welche Browser welche Formate unterstützen

Safari:

  • HLS (nur iOS und Mac)
  • h.264
  • MP4

Feuerfuchs

  • DASH (über MSE aber kein h.264)
  • h.264 nur über Flash!
  • VP9
  • MP4
  • OGG
  • Webm

IE

  • flash
  • DASH (nur über MSE IE 11+)
  • h.264
  • MP4

Chrom

  • flash
  • DASH (über MSE)
  • h.264
  • VP9
  • MP4
  • webm
  • ogg

MP4 kann nicht für Live-Video verwendet werden (HINWEIS: DASH ist eine Obermenge von MP4, also verwechseln Sie das nicht). MP4 ist in zwei Teile aufgeteilt: Moov und Mdat. mdat enthält die rohen Audio-Videodaten. Aber es ist nicht indexiert, also ohne den MOOV, es ist nutzlos. Der MOOV enthält einen Index aller Daten im MDAT. Aber aufgrund seines Formats kann es nicht “abgeflacht” werden, bis die Zeitstempel und die Größe von JEDEM Rahmen bekannt sind. Es kann möglich sein, einen Moov zu konstruieren, der die Frame-Größen “funkt”, aber in der Bandbreite sehr verschwenderisch ist.

Wenn Sie also überall hin liefern wollen, müssen wir den kleinsten gemeinsamen Nenner finden. Sie werden sehen, dass es hier kein LCD gibt, ohne auf Flash-Beispiel zurückzugreifen:

  • iOS unterstützt nur h.264-Video. und es unterstützt nur HLS für Live.
  • Firefox unterstützt h.264 überhaupt nicht, es sei denn, Sie verwenden Flash
  • Flash funktioniert nicht in iOS

Das nächste, was einem LCD am nächsten kommt, ist die Verwendung von HLS, um Ihre iOS-Nutzer zu erreichen, und das Flash für alle anderen. Mein persönlicher Favorit ist, HLS zu kodieren und dann Flash zu verwenden, um HLS für alle anderen zu spielen. Du kannst HLS im Flash über den JW Player 6 spielen (oder schreibe deinen eigenen HLS in AS3 auf FLV wie ich)

Bald wird der häufigste Weg dies sein HLS auf iOS / Mac und DASH via MSE überall sonst (Das ist, was Netflix in Kürze tun wird). Aber wir warten immer noch darauf, dass alle ihre Browser aktualisieren. Sie werden wahrscheinlich auch ein separates DASH / VP9 für Firefox benötigen (ich weiß über open264; es ist nervtötend. Es kann kein Video in Haupt- oder hohem Profil machen. So ist es zur Zeit nutzlos).

Danke an alle, vor allem, da dies eine komplexe Frage ist und viele Ebenen hat, die alle funktionieren müssen, bevor Sie Live-Video streamen können. Um meine ursprüngliche Frage und HTML5-Video zu verdeutlichen, verwenden Sie vs Flash – mein Anwendungsfall hat eine starke Vorliebe für HTML5, weil es generisch ist, einfach auf dem Client und der Zukunft zu implementieren. Flash ist ein entferntes Zweitbestes, also bleiben wir bei HTML5 für diese Frage.

Ich habe durch diese Übung viel gelernt und stimme zu, dass Live-Streaming viel schwieriger ist als VOD (was gut mit HTML5-Video funktioniert). Aber ich habe das für meinen Anwendungsfall zufriedenstellend getriggers, und die Lösung erwies sich als sehr einfach, nachdem ich komplexere Optionen wie MSE, Flash, ausgeklügelte Pufferschemata in Node gejagt hatte. Das Problem war, dass FFMPEG das fragmentierte MP4 korrumpierte und ich die FFMPEG-Parameter einstellen musste, und die Standard-Node-Stream-Pipe-Umleitung über HTTP, die ich ursprünglich verwendete, war alles, was benötigt wurde.

In MP4 gibt es eine “Fragmentierungs” -Option, die mp4 in viel kleinere Fragmente zerlegt, die ihren eigenen Index hat und die mp4-Live-Streaming-Option realisierbar macht. Aber nicht möglich, in den Stream zurück zu suchen (OK für meinen Anwendungsfall), und spätere Versionen von FFMPEG unterstützen Fragmentierung.

Hinweis Timing kann ein Problem sein, und mit meiner Lösung habe ich eine Verzögerung von zwischen 2 und 6 Sekunden durch eine Kombination der Remuxing verursacht (effektiv FFMPEG muss den Livestream erhalten, remux es dann an den Knoten zum Serving über HTTP senden) . Es kann nicht viel getan werden, aber in Chrome versucht das Video so viel wie möglich aufzuholen, was das Video ein bisschen nervös macht, aber aktueller als IE11 (mein bevorzugter Client).

Anstatt zu erklären, wie der Code in diesem Beitrag funktioniert, sehen Sie sich das GIST mit Kommentaren an (der Client-Code ist nicht enthalten, es ist ein Standard-HTML5-Video-Tag mit der HTTP-Serveradresse des Knotens). GIST ist hier: https://gist.github.com/deandob/9240090

Ich konnte keine ähnlichen Beispiele für diesen Anwendungsfall finden, daher hoffe ich, dass die obige Erklärung und der Code anderen hilft, zumal ich so viel von dieser Seite gelernt habe und mich immer noch als Anfänger betrachte!

Obwohl dies die Antwort auf meine spezifische Frage ist, habe ich szatmary’s Antwort als die akzeptierte gewählt, da sie die umfassendste ist.

Sehen Sie sich das JSMPEG- Projekt an. Dort ist eine großartige Idee implementiert – MPEG mit JavaScript im Browser zu dekodieren. Bytes aus dem Encoder (zum Beispiel FFMPEG) können über WebSockets oder Flash in den Browser übertragen werden. Wenn die Community aufholen wird, denke ich, wird es die beste HTML5 Live-Video-Streaming-Lösung sein.

Eine Möglichkeit, eine RTSP-basierte Webcam live auf einen HTML5-Client zu streamen (erfordert Re-Encoding, also Qualitätsverlust und CPU-performance):

  • Richten Sie einen Icecast-Server ein (möglicherweise auf demselben Computer, auf dem sich der Webserver befindet oder auf dem Computer, der den RTSP-Stream von der Kamera empfängt).
  • Verwenden Sie auf dem Gerät, das den Stream von der Kamera empfängt, nicht FFMPEG, sondern gstreamer. Es kann den RTSP-Stream empfangen und dekodieren, neu codieren und auf den Icecast-Server streamen. Beispielpipeline (nur Video, kein 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= port=12000 mount=cam.webm 

=> Sie können dann das

Ich habe einen HTML5-Video-Player rund um Broadway h264 Codec (emscripten) geschrieben, die live (ohne Verzögerung) h264 Video auf allen Browsern (Desktop, iOS, …) spielen kann.

Der Videostream wird über den Websocket an den Client gesendet, decodierter Frame pro Frame und in einem Canva angezeigt (mit webgl zur Beschleunigung)

Sehen Sie sich https://github.com/131/h264-live-player auf github an.

Sehen Sie sich diese Lösung an . Wie ich weiß, ermöglicht Flashphoner das Abspielen von Live Audio + Video Stream auf der reinen HTML5 Seite.

Sie verwenden MPEG1 und G.711 Codecs für die Wiedergabe. Der Hack rendert dekodiertes Video in ein HTML5-Canvas-Element und spielt dekodiertes Audio über den HTML5-Audio-Kontext ab.

Wie wäre es mit jpeg Lösung, lassen Sie einfach Server jpeg eins nach dem anderen Browser zu verteilen, dann verwenden Sie Canvas-Element, um diese JPEGS zu zeichnen? http://thejackalofjavascript.com/rpi-live-streaming/

Dies ist ein sehr häufiges Missverständnis. Es gibt keine Live-HTML5-Videounterstützung (mit Ausnahme von HLS auf iOS und Mac Safari). Sie können es möglicherweise mit einem Webm-Container “hacken”, aber ich würde nicht erwarten, dass dies allgemein unterstützt wird. Was Sie suchen, ist in den Media Source Extensions enthalten, wo Sie die Fragmente einzeln dem Browser zuführen können. aber Sie müssen einige Client-Seite Javascript schreiben.

Versuchen Sie binaryjs. Es ist genau wie socket.io, aber es ist nur gut, dass es Audio-Video streamt. Binaryjs google es