J’essaie de configurer un serveur Web prenant en charge la diffusion vidéo en continu sur une balise vidéo HTML5 à l’aide de node.js. Voici mon code jusqu’à présent:
var range = request.headers.range; var total = file.length; 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-1; var chunksize = (end-start)+1; response.writeHead(206, { "Content-Range": "bytes " + start + "-" + end + "/" + total, "Accept-Ranges": "bytes", "Content-Length": chunksize, "Content-Type": type }); response.end(file);
Où “request” représente la requête http, le type est “application / ogg” ou “video / ogg” (j’ai essayé les deux) et “file” est le fichier .ogv lu depuis le système de fichiers. Voici les en-têtes de réponse:
Content-Range bytes 0-14270463/14270464 Accept-Ranges bytes Content-Length 14270464 Connection keep-alive Content-Type video/ogg
J’ai examiné les en-têtes de réponse et ce code semble fonctionner correctement, mais il y a quelques problèmes:
Est-ce que quelqu’un a des idées sur ce que je peux faire pour que le streaming vidéo fonctionne via node.js?
Merci!
Chris
Je sais que c’est une très vieille question, mais comme Google semble l’apprécier, j’ai pensé qu’il serait utile de préciser que j’ai écrit un module de streaming vidéo Node.js (Github, ou via NPM) qui devrait être aussi intéressant.
J’ai pu obtenir ceci avec l’aide des forums de nodejs:
http://groups.google.com/group/nodejs/browse_thread/thread/8339e0dc825c057f/822b2dd48f36e890
Faits saillants du fil de discussion Google Groupes:
On sait que Google Chrome effectue d’abord une requête avec la plage 0-1024, puis demande la plage “1024-“.
response.end (file.slice (start, chunksize), “binary”);
Alors:
J’ai pu faire en sorte que la vidéo ne pose aucun problème dans firefox en définissant l’en-tête “connection” sur “close”
Alors:
Semble que vous calculez incorrectement la longueur du contenu:
var chunksize = (end-start) +1;
Si start a la valeur 0 et que end est 1, dans votre cas, la taille est 2 et devrait être 1.
Cette solution effectue une lecture asynchrone d’un fichier multimédia audio ou vidéo côté serveur … elle active un serveur nodejs à l’URL visible à
http: // localhost: 8888 /
il gère également correctement les mouvements de curseur d’interface utilisateur du widget HTML5 (navigateur / application) côté client
enregistrer sous l’extrait de code en tant que fichier côté serveur:
media_server.js
… l’exécuter côté serveur en utilisant
node media_server.js
prendre plaisir
var http = require('http'), fs = require('fs'), util = require('util'); var path = "/path/to/local/video/or/audio/file/on/server.mp4"; var port = 8888; var host = "localhost"; http.createServer(function (req, res) { var stat = fs.statSync(path); var total = stat.size; if (req.headers.range) { // meaning client (browser) has moved the forward/back slider // which has sent this request back to this server logic ... cool var range = req.headers.range; 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-1; var chunksize = (end-start)+1; console.log('RANGE: ' + start + ' - ' + end + ' = ' + chunksize); var file = fs.createReadStream(path, {start: start, end: end}); res.writeHead(206, { 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/mp4' }); file.pipe(res); } else { console.log('ALL: ' + total); res.writeHead(200, { 'Content-Length': total, 'Content-Type': 'video/mp4' }); fs.createReadStream(path).pipe(res); } }).listen(port, host); console.log("Server running at http://" + host + ":" + port + "/");
Sur la base de la réponse de Sam9291, j’ai réécrit la fonction en utilisant createReadStream()
et en corrigeant certains problèmes:
/** * Sends a static file to the HTTP client, supporting partial transfers. * * @req HTTP request object * @res HTTP response object * @fn Path to file that should be sent * @contentType MIME type for the response (defaults to HTML) */ function sendFile(req, res, fn, contentType) { contentType = contentType || "text/html"; fs.stat(fn, function(err, stats) { var headers; if (err) { res.writeHead(404, {"Content-Type":"text/plain"}); res.end("Could not read file"); return; } var range = req.headers.range || ""; var total = stats.size; if (range) { 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-1; var chunksize = (end-start)+1; headers = { "Content-Range": "bytes " + start + "-" + end + "/" + total, "Accept-Ranges": "bytes", "Content-Length": chunksize, "Content-Type": contentType }; res.writeHead(206, headers); } else { headers = { "Accept-Ranges": "bytes", "Content-Length": stats.size, "Content-Type": contentType }; res.writeHead(200, headers); } var readStream = fs.createReadStream(fn, {start:start, end:end}); readStream.pipe(res); }); }
J’utilise le framework MVC sails.js au dessus de Node.js et j’ai réussi à le faire fonctionner avec le code suivant:
/** * VideoController * * @module :: Controller * @description :: Contains logic for handling requests. */ var fs = require('fs'); module.exports = { /* eg sayHello: function (req, res) { res.send('hello world!'); } */ /** * /video/stream */ stream: function (req,res) { // This will render the view: // C:\Users\sam\Documents\Dev\Fun\mymoviebank/views/video/stream.ejs res.view(); }, play: function (req,res) { fs.readFile('/Users/sam/Videos/big_buck_bunny.mp4', function (err, data) { if (err) throw err; var range = req.headers.range; var total = data.length; 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-1; var chunksize = (end-start)+1; res.writeHead(206, { "Content-Range": "bytes " + start + "-" + end + "/" + total, "Accept-Ranges": "bytes", "Content-Length": chunksize, "Content-Type": 'video/mp4' }); res.end(data); }); } };
J’espère que cela t’aides
J’ai trouvé cette solution qui semble plus simple et (contrairement à la réponse cochée) fonctionne pour moi. (J’ai essayé d’adapter la solution coffeescript à la fin de ce thread et cela a fonctionné une fois que j’ai traité le fait que la requête initiale (pour “octets = 0-“) le fait exploser.
http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/
Ma mise en œuvre réelle:
function stream_response( res, file_path, content_type ){ var readStream = fs.createReadStream(file_path); readStream.on('data', function(data) { var flushed = res.write(data); // Pause the read stream when the write stream gets saturated console.log( 'streaming data', file_path ); if(!flushed){ readStream.pause(); } }); res.on('drain', function() { // Resume the read stream when the write stream gets hungry readStream.resume(); }); readStream.on('end', function() { res.end(); }); readStream.on('error', function(err) { console.error('Exception', err, 'while streaming', file_path); res.end(); }); res.writeHead(200, {'Content-Type': content_type}); }