Ich bin wirklich festgefahren, um zu verstehen, wie man die Echtzeitausgabe von ffmpeg mit node.js am besten auf einen HTML5-Client streamen kann, da eine Reihe von Variablen im Spiel sind und ich nicht viel Erfahrung in diesem Bereich habe. Ich habe viele Stunden damit verbracht, verschiedene Kombinationen auszuprobieren.
Mein Anwendungsfall ist:
1) Der RTSP H.264-Stream der IP-Videokamera wird von FFMPEG aufgenommen und unter Verwendung der folgenden FFMPEG-Einstellungen im Knoten in einen MP4-Container übertragen und an STDOUT ausgegeben. Dies wird nur bei der ersten Clientverbindung ausgeführt, damit Teilinhaltsanforderungen nicht erneut versuchen, FFMPEG zu erzeugen.
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) Ich verwende den Knoten-HTTP-Server, um das STDOUT zu erfassen und es auf Clientanforderung an den Client zurückzusenden. Wenn der Client zum ersten Mal eine Verbindung herstellt, spawne ich die obige FFMPEG-Befehlszeile und leite dann den STDOUT-Stream an die HTTP-Antwort weiter.
liveFFMPEG.stdout.pipe(resp);
Ich habe das Stream-Ereignis auch verwendet, um die FFMPEG-Daten in die HTTP-Antwort zu schreiben, aber es macht 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 und funktioniert).
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 Client muss HTML5-Video-Tags verwenden.
Ich habe keine Probleme mit der Streaming-Wiedergabe (unter Verwendung von fs.createReadStream mit 206 HTTP-Teilinhalten) auf dem HTML5-Client eine Videodatei, die zuvor mit der obigen FFMPEG-Befehlszeile aufgezeichnet wurde (aber in einer Datei anstelle von STDOUT gespeichert wurde), sodass ich den FFMPEG-Stream kenne ist korrekt, und ich kann das Video-Live-Streaming in VLC sogar korrekt sehen, wenn eine Verbindung zum HTTP-Knotenserver hergestellt wird.
Der Versuch, live von FFMPEG über den Knoten HTTP zu streamen, scheint jedoch viel schwieriger zu sein, da der Client einen Frame anzeigt und dann stoppt. Ich vermute, das Problem ist, dass ich die HTTP-Verbindung nicht so einrichte, dass sie mit dem HTML5-Video-Client kompatibel ist. Ich habe eine Vielzahl von Dingen ausprobiert, z. B. die Verwendung von HTTP 206 (Teilinhalt) und 200 Antworten, das Speichern der Daten in einen Puffer und das Streaming ohne Glück. Daher muss ich zu den ersten Prinzipien zurückkehren, um sicherzustellen, dass ich dies richtig einrichte Weg.
Hier ist mein Verständnis, wie dies funktionieren sollte. Bitte korrigieren Sie mich, wenn ich falsch liege:
1) FFMPEG sollte so eingerichtet werden, dass die Ausgabe fragmentiert wird und ein leerer Moov verwendet wird (FFMPEG frag_keyframe und empty_moov mov Flags). Dies bedeutet, dass der Client nicht das moov-Atom verwendet, das sich normalerweise am Ende der Datei befindet, was beim Streaming nicht relevant ist (kein Dateiende), sondern dass keine Suche möglich ist, was für meinen Anwendungsfall in Ordnung ist.
2) Obwohl ich MP4-Fragmente und leeres MOOV verwende, muss ich immer noch HTTP-Teilinhalt verwenden, da der HTML5-Player vor dem Abspielen wartet, bis der gesamte Stream heruntergeladen ist, was bei einem Live-Stream niemals endet und daher nicht funktioniert.
3) Ich verstehe nicht, warum das Weiterleiten des STDOUT-Streams an die HTTP-Antwort beim Live-Streaming noch nicht funktioniert. Wenn ich in einer Datei speichere, kann ich diese Datei mit ähnlichem Code problemlos an HTML5-Clients streamen. Möglicherweise handelt es sich um ein Zeitproblem, da es eine Sekunde dauert, bis der FFMPEG-Spawn gestartet, eine Verbindung zur IP-Kamera hergestellt und Chunks an den Knoten gesendet werden. Die Knotendatenereignisse sind ebenfalls unregelmäßig. Der Bytestream sollte jedoch genau dem Speichern in einer Datei entsprechen, und HTTP sollte Verzögerungen berücksichtigen können.
4) Beim Überprüfen des Netzwerkprotokolls vom HTTP-Client beim Streamen einer von FFMPEG erstellten MP4-Datei von der Kamera werden drei Clientanforderungen angezeigt: Eine allgemeine GET-Anforderung für das Video, die der HTTP-Server etwa 40 KB zurückgibt, dann eine teilweise Inhaltsanforderung mit einem Bytebereich für die letzten 10 KB der Datei, dann eine endgültige Anforderung für die nicht geladenen Bits in der Mitte. Vielleicht fragt der HTML5-Client nach Erhalt der ersten Antwort nach dem letzten Teil der Datei, um das MP4-MOOV-Atom zu laden? In diesem Fall funktioniert das Streaming nicht, da keine MOOV-Datei und kein Dateiende vorhanden ist.
5) Wenn ich das Netzwerkprotokoll überprüfe, wenn ich versuche, live zu streamen, erhalte ich eine abgebrochene erste Anforderung mit nur etwa 200 empfangenen Bytes, dann eine erneut abgebrochene erneute Anforderung mit 200 Bytes und eine dritte Anforderung, die nur 2 KB lang ist. Ich verstehe nicht, warum der HTML5-Client die Anforderung abbrechen würde, da der Bytestream genau der gleiche ist, den ich beim Streaming von einer aufgezeichneten Datei erfolgreich verwenden kann. Es scheint auch, dass der Knoten den Rest des FFMPEG-Streams nicht an den Client sendet, aber ich kann die FFMPEG-Daten in der Ereignisroutine .on sehen, sodass er zum HTTP-Server des FFMPEG-Knotens gelangt.
6) Obwohl ich denke, dass das Weiterleiten des STDOUT-Streams an den HTTP-Antwortpuffer funktionieren sollte, muss ich einen Zwischenpuffer und einen Stream erstellen, damit die HTTP-Clientanforderungen für Teilinhalte ordnungsgemäß funktionieren, wenn eine Datei (erfolgreich) gelesen wird ? Ich denke, dies ist der Hauptgrund für meine Probleme, aber ich bin mir in Node nicht ganz sicher, wie ich das am besten einrichten soll. Und ich weiß nicht, wie ich eine Client-Anfrage für die Daten am Ende der Datei behandeln soll, da es kein Dateiende gibt.
7) Bin ich auf dem falschen Weg, wenn ich versuche, 206 Teilinhaltsanforderungen zu bearbeiten, und sollte dies mit normalen 200 HTTP-Antworten funktionieren? HTTP 200-Antworten funktionieren für VLC einwandfrei. Ich vermute also, dass der HTML5-Video-Client nur mit Teilinhaltsanforderungen funktioniert.
Da ich dieses Zeug noch lerne, ist es schwierig, die verschiedenen Ebenen dieses Problems (FFMPEG, Knoten, Streaming, HTTP, HTML5-Video) zu bearbeiten, sodass alle Hinweise sehr geschätzt werden. Ich habe stundenlang auf dieser Website und im Internet recherchiert und bin auf niemanden gestoßen, der in der Lage war, Echtzeit-Streaming in Node durchzuführen, aber ich kann nicht der Erste sein, und ich denke, dies sollte funktionieren (irgendwie) !).
Content-Type
in deinen Kopf gesetzt? Verwenden Sie die Chunk-Codierung? Dort würde ich anfangen. Außerdem bietet HTML5 nicht unbedingt die Funktionalität zum Streamen. Mehr dazu lesen Sie hier . Sie müssen höchstwahrscheinlich eine Möglichkeit implementieren, den Videostream mit Ihren eigenen Mitteln zu puffern und abzuspielen ( siehe hier ), obwohl dies wahrscheinlich nicht gut unterstützt wird. Google auch in MediaSource API.Antworten:
Alles unter dieser Zeile ist veraltet. Für die Nachwelt hier aufbewahren.
Es gibt viele Gründe, warum Videos und insbesondere Live-Videos sehr schwierig sind. (Bitte beachten Sie, dass in der ursprünglichen Frage angegeben wurde, dass HTML5-Video erforderlich ist, der Fragesteller jedoch in den Kommentaren angegeben hat, dass Flash möglich ist. Diese Frage ist also sofort irreführend.)
Zuerst möchte ich noch einmal wiederholen: Es gibt keine offizielle Unterstützung für Live-Streaming über HTML5 . Es gibt Hacks, aber Ihr Kilometerstand kann variieren.
Als nächstes müssen Sie verstehen, dass Video on Demand (VOD) und Live-Video sehr unterschiedlich sind. Ja, beide sind Videos, aber die Probleme sind unterschiedlich, daher sind die Formate unterschiedlich. Wenn beispielsweise die Uhr in Ihrem Computer 1% schneller läuft als sie sollte, werden Sie auf einem VoD nichts bemerken. Bei Live-Videos versuchen Sie, Videos abzuspielen, bevor dies geschieht. Wenn Sie einem laufenden Live-Videostream beitreten möchten, benötigen Sie die zum Initialisieren des Decoders erforderlichen Daten. Sie müssen daher im Stream wiederholt oder außerhalb des Bandes gesendet werden. Mit VOD können Sie den Anfang der gesuchten Datei bis zu einem beliebigen Punkt lesen.
Lassen Sie uns jetzt ein wenig graben.
Plattformen:
Codecs:
Allgemeine Übermittlungsmethoden für Live-Videos in Browsern:
Allgemeine Übermittlungsmethoden für VoD in Browsern:
HTML5-Video-Tag:
Schauen wir uns an, welche Browser welche Formate unterstützen
Safari:
Feuerfuchs
IE
Chrom
MP4 kann nicht für Live-Videos verwendet werden (HINWEIS: DASH ist eine Obermenge von MP4, also verwechseln Sie sich nicht damit). MP4 ist in zwei Teile geteilt: moov und mdat. mdat enthält die Audio-Video-Rohdaten. Aber es ist nicht indiziert, also ist es ohne das Moov nutzlos. Das moov enthält einen Index aller Daten im mdat. Aufgrund seines Formats kann es jedoch nicht "abgeflacht" werden, bis die Zeitstempel und die Größe JEDES Frames bekannt sind. Es kann möglich sein, ein Moov zu konstruieren, das die Rahmengrößen "fibriert", aber in Bezug auf die Bandbreite sehr verschwenderisch ist.
Wenn Sie also überall liefern möchten, müssen wir den kleinsten gemeinsamen Nenner finden. Sie werden sehen, dass es hier kein LCD gibt, ohne auf das Blitzbeispiel zurückzugreifen:
Das, was einem LCD am nächsten kommt, ist die Verwendung von HLS, um Ihre iOS-Benutzer zu erreichen, und das Blinken für alle anderen. Mein persönlicher Favorit ist es, HLS zu codieren und dann mit Flash HLS für alle anderen zu spielen. Sie können HLS in Flash über JW Player 6 spielen (oder wie ich Ihr eigenes HLS in AS3 in FLV schreiben).
In Kürze wird HLS unter iOS / Mac und DASH über MSE überall am häufigsten verwendet (dies wird Netflix in Kürze tun). Wir warten jedoch 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 Bescheid; es ist scheiße. Es kann keine Videos in Haupt- oder High-Profile-Qualität machen. Daher ist es derzeit nutzlos).
quelle
Vielen Dank an alle, besonders an szatmary, da dies eine komplexe Frage ist und viele Ebenen hat, die alle funktionieren müssen, bevor Sie Live-Videos streamen können. Um meine ursprüngliche Frage und die Verwendung von HTML5-Videos im Vergleich zu Flash zu klären, hat mein Anwendungsfall eine starke Präferenz für HTML5, da es allgemein gehalten und auf dem Client und in der Zukunft einfach zu implementieren ist. Flash ist ein entfernter Zweitbester, also bleiben wir bei HTML5 für diese Frage.
Ich habe durch diese Übung viel gelernt und bin mir einig, dass Live-Streaming viel schwieriger ist als VOD (was gut mit HTML5-Videos funktioniert). Aber ich habe dafür gesorgt, dass dies für meinen Anwendungsfall zufriedenstellend funktioniert, und die Lösung hat sich als sehr einfach herausgestellt, nachdem ich komplexere Optionen wie MSE, Flash und ausgefeilte Pufferungsschemata in Node verfolgt habe. Das Problem war, dass FFMPEG das fragmentierte MP4 beschädigte und ich die FFMPEG-Parameter anpassen musste und die ursprünglich verwendete Standard-Node-Stream-Pipe-Umleitung über http alles war, was benötigt wurde.
In MP4 gibt es eine "Fragmentierungs" -Option, die die mp4 in viel kleinere Fragmente aufteilt, die einen eigenen Index hat und die mp4-Live-Streaming-Option funktionsfähig macht. Es ist jedoch nicht möglich, in den Stream zurückzukehren (OK für meinen Anwendungsfall), und spätere Versionen von FFMPEG unterstützen die Fragmentierung.
Beachten Sie, dass das Timing ein Problem sein kann, und bei meiner Lösung habe ich eine Verzögerung zwischen 2 und 6 Sekunden, die durch eine Kombination des Remuxing verursacht wird (effektiv muss FFMPEG den Live-Stream empfangen, remuxen und dann an den Knoten senden, um ihn über HTTP bereitzustellen). . Hier kann nicht viel getan werden, aber in Chrome versucht das Video so viel wie möglich aufzuholen, was das Video etwas nervös, aber aktueller als IE11 (mein bevorzugter Client) macht.
Anstatt zu erklären, wie der Code in diesem Beitrag funktioniert, lesen Sie die GIST mit Kommentaren (der Client-Code ist nicht enthalten, es handelt sich um ein Standard-HTML5-Video-Tag mit der HTTP-Server-Adresse 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 obige Code anderen helfen, zumal ich auf dieser Website so viel gelernt habe und mich immer noch als Anfänger betrachte!
Obwohl dies die Antwort auf meine spezifische Frage ist, habe ich die Antwort von szatmary als die akzeptierte ausgewählt, da sie die umfassendste ist.
quelle
Schauen Sie sich das JSMPEG- Projekt an. Dort ist eine großartige Idee implementiert - MPEG im Browser mit JavaScript zu dekodieren. Bytes vom Encoder (z. B. FFMPEG) können beispielsweise mit WebSockets oder Flash an den Browser übertragen werden. Wenn die Community aufholt, wird es meiner Meinung nach die derzeit beste HTML5-Live-Video-Streaming-Lösung sein.
quelle
Ich habe einen HTML5-Video-Player rund um den Broadway-H264-Codec (emscripten) geschrieben, der H264-Videos in allen Browsern (Desktop, iOS, ...) live (ohne Verzögerung) wiedergeben kann.
Der Videostream wird über einen Websocket an den Client gesendet, Frame für Frame dekodiert und in einem Canva angezeigt (mit Webgl zur Beschleunigung).
Schauen Sie sich https://github.com/131/h264-live-player auf github an.
quelle
Eine Möglichkeit, eine RTSP-basierte Webcam live auf einen HTML5-Client zu streamen (beinhaltet eine Neucodierung, also Qualitätsverlust erwarten und etwas CPU-Leistung benötigen):
Verwenden Sie auf dem Computer, der den Stream von der Kamera empfängt, nicht FFMPEG, sondern gstreamer. Es kann den RTSP-Stream empfangen und dekodieren, neu codieren und an den Icecast-Server streamen. Beispiel-Pipeline (nur Video, kein Audio):
=> Sie können dann das <video> -Tag mit der URL des icecast-Streams ( http://127.0.0.1:12000/cam.webm ) verwenden und es funktioniert in jedem Browser und Gerät, das webm unterstützt
quelle
Schauen Sie sich diese Lösung an . Wie ich weiß, ermöglicht Flashphoner die Wiedergabe von Live-Audio- und Videostreams 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-Audiokontext ab.
quelle
Wie wäre es mit einer JPEG-Lösung? Lassen Sie den Server JPEG nacheinander an den Browser verteilen und zeichnen Sie diese JPEGs mit dem Canvas-Element. http://thejackalofjavascript.com/rpi-live-streaming/
quelle
Dies ist ein sehr häufiges Missverständnis. Es gibt keine Unterstützung für Live-HTML5-Videos (außer HLS unter iOS und Mac Safari). Möglicherweise können Sie es mit einem WebM-Container "hacken", aber ich würde nicht erwarten, dass dies allgemein unterstützt wird. Was Sie suchen, ist in den Medienquellenerweiterungen enthalten, in denen Sie die Fragmente einzeln dem Browser zuführen können. Sie müssen jedoch clientseitiges Javascript schreiben.
quelle
solutions
aber keinesupport
für Live-Streaming. Dies bezieht sich direkt auf meinen Kommentar oben. Und webm wird in den wichtigsten Browsern unterstützt, hauptsächlich in der neuesten stabilen Version.Versuchen Sie binaryjs. Es ist genau wie socket.io, aber das einzige, was es gut macht, ist, dass es Audio-Video streamen kann. Binaryjs googeln es
quelle