Je suis vraiment coincé à essayer de comprendre la meilleure façon de diffuser la sortie en temps réel de ffmpeg vers un client HTML5 en utilisant node.js, car il y a un certain nombre de variables en jeu et je n'ai pas beaucoup d'expérience dans cet espace, après avoir passé de nombreuses heures à essayer différentes combinaisons.
Mon cas d'utilisation est:
1) Le flux de la caméra vidéo IP RTSP H.264 est capté par FFMPEG et remuxé dans un conteneur mp4 en utilisant les paramètres FFMPEG suivants dans le nœud, sortie vers STDOUT. Ceci n'est exécuté que sur la connexion client initiale, de sorte que les demandes de contenu partielles n'essaient pas de réapparaître FFMPEG.
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) J'utilise le serveur http de noeud pour capturer le STDOUT et le diffuser au client sur demande du client. Lorsque le client se connecte pour la première fois, je génère la ligne de commande FFMPEG ci-dessus, puis je dirige le flux STDOUT vers la réponse HTTP.
liveFFMPEG.stdout.pipe(resp);
J'ai également utilisé l'événement stream pour écrire les données FFMPEG dans la réponse HTTP mais cela ne fait aucune différence
xliveFFMPEG.stdout.on("data",function(data) {
resp.write(data);
}
J'utilise l'en-tête HTTP suivant (qui est également utilisé et fonctionne lors du streaming de fichiers préenregistrés)
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) Le client doit utiliser des balises vidéo HTML5.
Je n'ai aucun problème avec la lecture en continu (en utilisant fs.createReadStream avec 206 HTTP partiel) au client HTML5 un fichier vidéo précédemment enregistré avec la ligne de commande FFMPEG ci-dessus (mais enregistré dans un fichier au lieu de STDOUT), donc je connais le flux FFMPEG est correct, et je peux même voir correctement la vidéo en direct en streaming dans VLC lors de la connexion au serveur de noeud HTTP.
Cependant, essayer de diffuser en direct depuis FFMPEG via le nœud HTTP semble être beaucoup plus difficile car le client affichera une image puis s'arrêtera. Je soupçonne que le problème est que je ne configure pas la connexion HTTP pour être compatible avec le client vidéo HTML5. J'ai essayé une variété de choses comme l'utilisation de HTTP 206 (contenu partiel) et de 200 réponses, en mettant les données dans un tampon puis en les diffusant sans succès, donc je dois revenir aux premiers principes pour m'assurer de bien configurer cela. façon.
Voici ma compréhension de la façon dont cela devrait fonctionner, veuillez me corriger si je me trompe:
1) FFMPEG doit être configuré pour fragmenter la sortie et utiliser un moov vide (drapeaux mov FFMPEG frag_keyframe et empty_moov). Cela signifie que le client n'utilise pas l'atome moov qui se trouve généralement à la fin du fichier, ce qui n'est pas pertinent lors de la diffusion (pas de fin de fichier), mais signifie qu'aucune recherche n'est possible, ce qui est bien pour mon cas d'utilisation.
2) Même si j'utilise des fragments MP4 et des MOOV vides, je dois encore utiliser du contenu partiel HTTP, car le lecteur HTML5 attendra que le flux entier soit téléchargé avant de jouer, ce qui avec un flux en direct ne se termine jamais, donc c'est impossible.
3) Je ne comprends pas pourquoi le transfert du flux STDOUT vers la réponse HTTP ne fonctionne pas encore lors de la diffusion en direct.Si j'enregistre dans un fichier, je peux diffuser ce fichier facilement vers des clients HTML5 en utilisant un code similaire. C'est peut-être un problème de timing car il faut une seconde pour que le spawn FFMPEG démarre, se connecte à la caméra IP et envoie des morceaux au nœud, et les événements de données du nœud sont également irréguliers. Cependant, le bytestream devrait être exactement le même que l'enregistrement dans un fichier, et HTTP devrait être capable de répondre aux retards.
4) Lors de la vérification du journal réseau du client HTTP lors de la diffusion en continu d'un fichier MP4 créé par FFMPEG à partir de la caméra, je vois qu'il y a 3 demandes client: une demande GET générale pour la vidéo, que le serveur HTTP renvoie environ 40 Ko, puis une partie demande de contenu avec une plage d'octets pour les 10K derniers du fichier, puis une demande finale pour les bits du milieu non chargés. Peut-être que le client HTML5, une fois qu'il reçoit la première réponse, demande la dernière partie du fichier pour charger l'atome MP4 MOOV? Si c'est le cas, cela ne fonctionnera pas pour le streaming car il n'y a pas de fichier MOOV et pas de fin de fichier.
5) Lors de la vérification du journal réseau lorsque j'essaie de diffuser en direct, j'obtiens une demande initiale abandonnée avec seulement environ 200 octets reçus, puis une nouvelle demande à nouveau abandonnée avec 200 octets et une troisième demande qui ne fait que 2K de long. Je ne comprends pas pourquoi le client HTML5 abandonnerait la demande car le bytestream est exactement le même que je peux utiliser avec succès lors de la diffusion à partir d'un fichier enregistré. Il semble également que le nœud n'envoie pas le reste du flux FFMPEG au client, mais je peux voir les données FFMPEG dans la routine d'événements .on afin qu'elles parviennent au serveur HTTP du nœud FFMPEG.
6) Bien que je pense que la canalisation du flux STDOUT vers le tampon de réponse HTTP devrait fonctionner, dois-je créer un tampon et un flux intermédiaires qui permettront aux demandes du client de contenu partiel HTTP de fonctionner correctement comme il le fait lorsqu'il lit (avec succès) un fichier ? Je pense que c'est la principale raison de mes problèmes, mais je ne sais pas exactement dans Node comment configurer cela au mieux. Et je ne sais pas comment gérer une demande client pour les données à la fin du fichier car il n'y a pas de fin de fichier.
7) Suis-je sur la mauvaise voie en essayant de gérer 206 demandes de contenu partiel, et cela devrait-il fonctionner avec 200 réponses HTTP normales? Les réponses HTTP 200 fonctionnent bien pour VLC, donc je soupçonne que le client vidéo HTML5 ne fonctionnera qu'avec des demandes de contenu partielles?
Comme j'apprends encore ce genre de choses, il est difficile de travailler à travers les différentes couches de ce problème (FFMPEG, nœud, streaming, HTTP, vidéo HTML5), donc tous les pointeurs seront grandement appréciés. J'ai passé des heures à faire des recherches sur ce site et sur le net, et je n'ai rencontré personne qui a pu faire du streaming en temps réel dans le nœud, mais je ne peux pas être le premier, et je pense que cela devrait pouvoir fonctionner (en quelque sorte !).
Content-Type
votre tête dans votre tête? Utilisez-vous un codage par blocs? C'est là que je commencerais. De plus, HTML5 ne fournit pas nécessairement la fonctionnalité à diffuser, vous pouvez en savoir plus ici . Vous aurez très probablement besoin de mettre en œuvre un moyen de mettre en mémoire tampon et de lire le flux vidéo à l'aide de vos propres moyens ( voir ici ), car cela n'est probablement pas bien pris en charge. Google aussi dans l'API MediaSource.Réponses:
Tout ce qui est en dessous de cette ligne est obsolète. Le garder ici pour la postérité.
Il existe de nombreuses raisons pour lesquelles la vidéo et, en particulier, la vidéo en direct sont très difficiles. (Veuillez noter que la question d'origine spécifiait que la vidéo HTML5 était une exigence, mais le demandeur a déclaré que Flash est possible dans les commentaires. Donc, immédiatement, cette question est trompeuse)
Je vais d'abord reformuler: IL N'Y A PAS DE SUPPORT OFFICIEL POUR LA LIVE STREAMING SUR HTML5 . Il existe des hacks, mais votre kilométrage peut varier.
Ensuite, vous devez comprendre que la vidéo à la demande (VOD) et la vidéo en direct sont très différentes. Oui, ils sont tous les deux vidéo, mais les problèmes sont différents, donc les formats sont différents. Par exemple, si l'horloge de votre ordinateur tourne 1% plus vite qu'elle ne le devrait, vous ne le remarquerez pas sur un VOD. Avec la vidéo en direct, vous essayez de lire la vidéo avant qu'elle ne se produise. Si vous souhaitez rejoindre un flux vidéo en direct en cours, vous avez besoin des données nécessaires pour initialiser le décodeur, il doit donc être répété dans le flux ou envoyé hors bande. Avec la VOD, vous pouvez lire le début du fichier qu'ils recherchent jusqu'au point que vous souhaitez.
Maintenant, creusons un peu.
Plateformes:
Codecs:
Méthodes de livraison courantes pour la vidéo en direct dans les navigateurs:
Méthodes de livraison courantes pour la VOD dans les navigateurs:
balise vidéo html5:
Permet de voir quels navigateurs prennent en charge quels formats
Safari:
Firefox
C'EST À DIRE
Chrome
MP4 ne peut pas être utilisé pour la vidéo en direct (REMARQUE: DASH est un surensemble de MP4, alors ne vous y trompez pas). MP4 est divisé en deux morceaux: moov et mdat. mdat contient les données audio vidéo brutes. Mais il n'est pas indexé, donc sans le moov, c'est inutile. Le moov contient un index de toutes les données du mdat. Mais en raison de son format, il ne peut pas être «aplati» tant que les horodatages et la taille de CHAQUE image ne sont pas connus. Il peut être possible de construire un moov qui «fibe» les tailles de trame, mais c'est très gaspillant en termes de bande passante.
Donc, si vous voulez livrer partout, nous devons trouver le dénominateur le moins commun. Vous verrez qu'il n'y a pas d'écran LCD ici sans avoir recours à l'exemple du flash:
La chose la plus proche d'un LCD est d'utiliser HLS pour obtenir vos utilisateurs iOS et flash pour tout le monde. Mon préféré est d'encoder HLS, puis d'utiliser le flash pour jouer HLS pour tout le monde. Vous pouvez jouer HLS en flash via JW player 6, (ou écrire votre propre HLS en FLV dans AS3 comme je l'ai fait)
Bientôt, la façon la plus courante de le faire sera HLS sur iOS / Mac et DASH via MSE partout ailleurs (c'est ce que Netflix fera bientôt). Mais nous attendons toujours que tout le monde mette à jour son navigateur. Vous aurez également probablement besoin d'un DASH / VP9 distinct pour Firefox (je connais open264; il craint. Il ne peut pas faire de vidéo de profil principal ou élevé. Il est donc actuellement inutile).
la source
Merci à tous, en particulier à Szmarymary, car il s'agit d'une question complexe qui comporte de nombreuses couches, qui doivent toutes fonctionner avant de pouvoir diffuser des vidéos en direct. Pour clarifier ma question d'origine et l'utilisation de la vidéo HTML5 par rapport au flash - mon cas d'utilisation a une forte préférence pour HTML5 car il est générique, facile à implémenter sur le client et l'avenir. Flash est un deuxième meilleur lointain, alors restons avec HTML5 pour cette question.
J'ai beaucoup appris grâce à cet exercice et je suis d'accord pour dire que la diffusion en direct est beaucoup plus difficile que la VOD (qui fonctionne bien avec la vidéo HTML5). Mais j'ai réussi à faire en sorte que cela fonctionne de manière satisfaisante pour mon cas d'utilisation et la solution s'est avérée très simple, après avoir chassé des options plus complexes comme MSE, flash, des schémas de mise en mémoire tampon élaborés dans Node. Le problème était que FFMPEG corrompait le MP4 fragmenté et j'ai dû régler les paramètres FFMPEG, et la redirection de canal de flux de nœud standard sur http que j'ai utilisé à l'origine était tout ce qui était nécessaire.
Dans MP4, il existe une option de «fragmentation» qui divise le mp4 en fragments beaucoup plus petits qui ont son propre index et rend l'option de streaming en direct mp4 viable. Mais pas possible de revenir dans le flux (OK pour mon cas d'utilisation), et les versions ultérieures de FFMPEG prennent en charge la fragmentation.
Le timing des notes peut être un problème, et avec ma solution, j'ai un décalage entre 2 et 6 secondes provoqué par une combinaison du remuxage (en fait, FFMPEG doit recevoir le flux en direct, le remuxer puis l'envoyer au nœud pour servir via HTTP) . On ne peut pas faire grand-chose à ce sujet, mais dans Chrome, la vidéo essaie de rattraper autant que possible, ce qui rend la vidéo un peu nerveuse mais plus actuelle que IE11 (mon client préféré).
Plutôt que d'expliquer comment le code fonctionne dans cet article, consultez le GIST avec des commentaires (le code client n'est pas inclus, c'est une balise vidéo HTML5 standard avec l'adresse du serveur http du nœud). GIST est ici: https://gist.github.com/deandob/9240090
Je n'ai pas pu trouver d'exemples similaires de ce cas d'utilisation, alors j'espère que l'explication et le code ci-dessus aident les autres, d'autant plus que j'ai beaucoup appris de ce site et que je me considère toujours comme un débutant!
Bien que ce soit la réponse à ma question spécifique, j'ai choisi la réponse de szatmary comme étant la réponse acceptée car elle est la plus complète.
la source
Jetez un œil au projet JSMPEG . Il y a une excellente idée implémentée là-bas - pour décoder MPEG dans le navigateur en utilisant JavaScript. Les octets de l'encodeur (FFMPEG, par exemple) peuvent être transférés vers le navigateur en utilisant WebSockets ou Flash, par exemple. Si la communauté rattrape son retard, je pense que ce sera la meilleure solution de streaming vidéo en direct HTML5 pour l'instant.
la source
J'ai écrit un lecteur vidéo HTML5 autour du codec broadway h264 (emscripten) qui peut lire des vidéos h264 en direct (sans délai) sur tous les navigateurs (bureau, iOS, ...).
Le flux vidéo est envoyé via websocket au client, décodé image par image et affiché dans une canva (en utilisant webgl pour l'accélération)
Consultez https://github.com/131/h264-live-player sur github.
la source
Une façon de diffuser en direct une webcam basée sur RTSP vers un client HTML5 (implique un ré-encodage, donc attendez-vous à une perte de qualité et nécessite une certaine puissance CPU):
Sur la machine recevant le flux de la caméra, n'utilisez pas FFMPEG mais gstreamer. Il est capable de recevoir et de décoder le flux RTSP, de le recoder et de le diffuser sur le serveur icecast. Exemple de pipeline (uniquement vidéo, pas d'audio):
=> Vous pouvez ensuite utiliser la balise <video> avec l'URL du icecast-stream ( http://127.0.0.1:12000/cam.webm ) et cela fonctionnera dans tous les navigateurs et appareils prenant en charge webm
la source
Jetez un œil à cette solution . Comme je le sais, Flashphoner permet de lire le flux audio + vidéo en direct dans la page HTML5 pure.
Ils utilisent les codecs MPEG1 et G.711 pour la lecture. Le hack rend la vidéo décodée en élément de canevas HTML5 et lit l'audio décodé via le contexte audio HTML5.
la source
Que diriez-vous d'utiliser la solution jpeg, laissez simplement le serveur distribuer les jpeg un par un au navigateur, puis utilisez l'élément canvas pour dessiner ces jpeg? http://thejackalofjavascript.com/rpi-live-streaming/
la source
Il s'agit d'une idée fausse très courante. Il n'y a pas de support vidéo HTML5 en direct (sauf pour HLS sur iOS et Mac Safari). Vous pouvez peut-être le «pirater» à l'aide d'un conteneur WebM, mais je ne m'attendrais pas à ce qu'il soit universellement pris en charge. Ce que vous recherchez est inclus dans les extensions de source multimédia, où vous pouvez envoyer les fragments au navigateur un par un. mais vous devrez écrire du javascript côté client.
la source
solutions
mais il n'y en a passupport
pour la diffusion en direct. Cela fait directement référence à mon commentaire vu ci-dessus. Et webm est pris en charge sur les principaux navigateurs, principalement la dernière version stable.Essayez binaryjs. C'est exactement comme socket.io mais la seule chose qu'il fait bien, c'est qu'il diffuse de l'audio vidéo. Binaryjs google it
la source