De la diffusion d'un fichier vidéo dans un lecteur vidéo html5 avec Node.js de sorte que les commandes vidéo de continuer à travailler?
Tl;Dr - La Question:
Quelle est la bonne façon de gérer la diffusion d'un fichier vidéo dans un lecteur vidéo html5 avec Node.js de sorte que les commandes vidéo de continuer à travailler?
Je pense il a à voir avec la façon dont les en-têtes sont traitées. De toute façon, voici les informations d'arrière-plan. Le code est un peu longue, cependant, c'est assez simple.
Streaming petite vidéo fichiers vidéo HTML5 avec Nœud est facile
J'ai appris comment diffuser de petits fichiers vidéo d'un lecteur vidéo HTML5 très facilement. Avec cette configuration, les commandes fonctionnent sans aucun effort de ma part, et le flux vidéo à la perfection. Une copie de travail du code du travail avec l'exemple de la vidéo est ici, pour télécharger sur Google Docs.
Client:
<html>
<title>Welcome</title>
<body>
<video controls>
<source src="movie.mp4" type="video/mp4"/>
<source src="movie.webm" type="video/webm"/>
<source src="movie.ogg" type="video/ogg"/>
<!-- fallback -->
Your browser does not support the <code>video</code> element.
</video>
</body>
</html>
Serveur:
//Declare Vars & Read Files
var fs = require('fs'),
http = require('http'),
url = require('url'),
path = require('path');
var movie_webm, movie_mp4, movie_ogg;
//... [snip] ... (Read index page)
fs.readFile(path.resolve(__dirname,"movie.mp4"), function (err, data) {
if (err) {
throw err;
}
movie_mp4 = data;
});
//... [snip] ... (Read two other formats for the video)
//Serve & Stream Video
http.createServer(function (req, res) {
//... [snip] ... (Serve client files)
var total;
if (reqResource == "/movie.mp4") {
total = movie_mp4.length;
}
//... [snip] ... handle two other formats for the video
var range = req.headers.range;
var positions = range.replace(/bytes=/, "").split("-");
var start = parseInt(positions[0], 10);
var end = positions[1] ? parseInt(positions[1], 10) : total - 1;
var chunksize = (end - start) + 1;
if (reqResource == "/movie.mp4") {
res.writeHead(206, {
"Content-Range": "bytes " + start + "-" + end + "/" + total,
"Accept-Ranges": "bytes",
"Content-Length": chunksize,
"Content-Type": "video/mp4"
});
res.end(movie_mp4.slice(start, end + 1), "binary");
}
//... [snip] ... handle two other formats for the video
}).listen(8888);
Mais cette méthode est limitée aux fichiers < 1 go en taille.
Streaming (de toute taille) des fichiers vidéo avec fs.createReadStream
En utilisant fs.createReadStream()
, le serveur peut lire le fichier dans un flux plutôt que de lire tout cela en mémoire à la fois. Cela ressemble à la bonne façon de faire les choses, et la syntaxe est très simple:
Serveur Extrait De:
movieStream = fs.createReadStream(pathToFile);
movieStream.on('open', function () {
res.writeHead(206, {
"Content-Range": "bytes " + start + "-" + end + "/" + total,
"Accept-Ranges": "bytes",
"Content-Length": chunksize,
"Content-Type": "video/mp4"
});
//This just pipes the read stream to the response object (which goes
//to the client)
movieStream.pipe(res);
});
movieStream.on('error', function (err) {
res.end(err);
});
Ce flux de la vidéo il suffit de bien! Mais la vidéo, les contrôles ne fonctionnent plus.
- J'ai quitté la
writeHead()
code commenté, mais là au cas où ça aide. Dois-je le supprimer que de faire de l'extrait de code plus lisible? - d'où vient req.les en-têtes.gamme viennent de? Je reçois pas défini lorsque j'essaie de faire le remplacement de la méthode. Merci.
Vous devez vous connecter pour publier un commentaire.
La
Accept Ranges
en-tête (le bit danswriteHead()
) est nécessaire pour la vidéo HTML5 contrôles de travailler.Je pense qu'au lieu de seulement aveuglément envoyer le dossier complet, vous devriez d'abord vérifier la
Accept Ranges
en-tête de la DEMANDE, puis de lire et d'envoyer juste un peu.fs.createReadStream
soutienstart
, etend
option pour que.J'ai donc essayé un exemple et il fonctionne. Le code n'est pas joli mais c'est facile à comprendre. Nous avons d'abord procédé de la gamme d'en-tête pour obtenir la position de début/fin. Ensuite, nous utilisons
fs.stat
pour obtenir la taille du fichier sans avoir à lire tout le fichier en mémoire. Enfin, l'utilisationfs.createReadStream
pour envoyer la partie demandée au client.La accepté de répondre à cette question est génial et doit rester la a accepté de répondre. Cependant j'ai rencontré un problème avec le code où la lecture de flux n'était pas toujours terminés/fermé. Une partie de la solution a été d'envoyer
autoClose: true
avecstart:start, end:end
dans la deuxièmecreateReadStream
arg.L'autre partie de la solution a été de limiter au max
chunksize
d'être envoyé dans la réponse. Les autres réponsesend
comme suit:...ce qui a pour effet d'envoyer le reste du fichier de la demande de la position de départ par le biais de son dernier octet, peu importe combien d'octets qui peuvent être. Toutefois, le navigateur du client a la possibilité de lire seulement une partie de ce flux, et sera, si elle n'a pas besoin de tous les octets encore. Cela va entraîner le flux de lire pour avoir bloqué jusqu'à ce que le navigateur décide qu'il est temps d'obtenir plus de données (par exemple une action de l'utilisateur comme seek/gommage, ou tout simplement par la lecture du flux).
J'avais besoin de ce flux soit fermé parce que j'étais l'affichage de la
<video>
élément sur une page qui permettait à l'utilisateur de supprimer le fichier vidéo. Toutefois, le fichier n'a pas été supprimé depuis le système de fichiers jusqu'à ce que le client (ou le serveur) a fermé la connexion, parce que c'est la seule façon que le flux a été prise en clos et fermé.Ma solution était juste pour définir un
maxChunk
variable de configuration, réglez-le à 1 mo, et jamais tuyaux de lire un flux de plus de 1 mo à un moment de la réponse.Ce qui a pour effet de faire en sorte que la lecture de flux est terminé/fermé après chaque demande, et pas maintenu en vie par le navigateur.
J'ai aussi écrit un distinct StackOverflow question et réponse couvrant cette question.