Streaming vidéo avec HTML 5 via node.js

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:

  1. La vidéo semble se charger très lentement pour être sur un réseau local. D’après ce que je peux dire en examinant la réponse en utilisant Firebug, le fichier semble être diffusé à environ 150 kb / sec.
  2. La vidéo ne joue pas du tout. Même si j’attend que tout se charge, la balise vidéo HTML 5 affiche juste un grand “x” au lieu d’un film dans Firefox.

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}); }