46 votes

Streaming vidéo avec HTML 5 via node.js

J'essaie de mettre en place un serveur web qui prendra en charge le streaming vidéo vers une balise vidéo HTML5 en utilisant 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 soit "application/ogg" soit "video/ogg" (j'ai essayé les deux) et "file" est le fichier .ogv qui a été 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 avec firebug, le fichier semble être diffusé à environ 150 kb/sec.
  2. La vidéo ne passe pas du tout. Même si j'attends le chargement complet de la vidéo, la balise vidéo HTML 5 affiche un grand "x" au lieu d'une vidéo dans Firefox.

Quelqu'un a-t-il une idée de ce que je peux faire pour que le streaming vidéo fonctionne via node.js ?

Merci !
Chris

24voto

meloncholy Points 840

Je sais que c'est une très vieille question, mais comme Google semble l'apprécier, j'ai pensé que cela vaudrait la peine de signaler que j'ai écrit un programme Node.js. module de streaming vidéo (Github, ou via NPM) qui, je l'espère, vaut aussi le coup d'œil.

6 votes

J'adore ce genre de réponses ! Merci. :)

0 votes

Je reviens donc sur ce sujet après 2 ans... :) Y a-t-il un moyen d'utiliser ce script pour envoyer des données en direct (au navigateur) qui ont été reçues par udp ?

0 votes

@randomuser1 Non, j'ai bien peur que non. Le streaming en direct est plus compliqué (par exemple, il faut segmenter l'entrée, supporter les fichiers d'index) que le script ne fait pas. J'aimerais supporter cela, mais je n'ai pas eu le temps d'y travailler malheureusement. Désolé.

19voto

Chris Points 465

J'ai réussi à faire fonctionner ce système avec l'aide des forums nodejs :

http://groups.google.com/group/nodejs/browse_thread/thread/8339e0dc825c057f/822b2dd48f36e890

Extraits du fil de discussion de Google Groups :

Google chrome est connu pour faire d'abord une demande avec la plage 0-1024 et demande ensuite la plage "1024-".

response.end(file.slice(start, chunksize), "binary") ;

Ensuite :

J'ai pu faire en sorte que la vidéo soit lue sans problème dans firefox en paramétrant l'en-tête "connection" à "close".

Ensuite :

Il semble que le calcul de la longueur du contenu soit incorrect :

var chunksize = (end-start)+1 ;

Si start est 0 et end est 1, dans votre cas chunksize est 2, et il devrait être 1. être 1.

0 votes

Je crois que la taille des morceaux était correcte. D'après w3.org/Protocoles/rfc2616/rfc2616-sec14.html : The first-byte-pos value in a byte-range-spec gives the byte-offset of the first byte in a range. The last-byte-pos value gives the byte-offset of the last byte in the range; that is, the byte positions specified are inclusive. Byte offsets start at zero.

14voto

Scott Stensland Points 993

Cette solution effectue une lecture asynchrone d'un fichier multimédia vidéo ou audio côté serveur ... elle lance un serveur nodejs à l'URL visible à l'adresse

http://localhost:8888/

Il gère également correctement les mouvements de curseurs de widgets d'interface utilisateur HTML5 côté client (navigateur/application) en avant/arrière.

sauvegarder l'extrait de code ci-dessous comme fichier côté serveur :

media_server.js

... l'exécuter du côté serveur en utilisant

node media_server.js

Profitez de

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 + "/");

2 votes

Existe-t-il un moyen de diffuser le fichier vidéo vers le navigateur non pas à partir du fichier source, mais à partir du flux udp ?

0 votes

Peut-on le faire depuis l'express ? Je suis un noob de Node :)

5voto

Udo G Points 2254

En me basant sur la réponse de Sam9291, j'ai réécrit la fonction en utilisant createReadStream() et de régler 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);    

  });

}

4voto

Sam9291 Points 382

J'utilise le cadre MVC sails.js sur le dessus de Node.js et j'ai réussi à le faire fonctionner correctement avec le code suivant :

/**
 * VideoController
 *
 * @module      :: Controller
 * @description :: Contains logic for handling requests.
 */

 var fs = require('fs');

module.exports = {

  /* e.g.
  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 vous aidera

1 votes

La fonction fs.statSync(../file/path/...) de npm s'occupe des détails. Voir gist.github.com/westonplatter/7559003 .

3 votes

Cette solution n'est pas évolutive, car elle met en mémoire la totalité du fichier vidéo pour n'en servir qu'une petite partie. fs.createReadStream(theFile, { start : $START, end : #END }) vous permettra d'acheminer le flux vers la réponse sans avoir à charger l'intégralité du fichier vidéo en mémoire (imaginez que 1000 utilisateurs fassent cela en même temps).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X