webentwicklung-frage-antwort-db.com.de

Video-Streaming mit HTML 5 über node.js

Ich versuche, einen Webserver einzurichten, der das Streaming von Videos zu einem HTML5-Videotag mithilfe von node.js unterstützt. Hier ist mein Code:

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

Wenn "request" die http-Anfrage darstellt, lautet der Typ entweder "application/ogg" oder "video/ogg" (ich habe beide ausprobiert) und "file" ist die .ogv-Datei, die aus dem Dateisystem gelesen wurde. Hier sind die Antwort-Header:

Content-Range   bytes 0-14270463/14270464
Accept-Ranges   bytes
Content-Length   14270464
Connection     keep-alive
Content-Type     video/ogg

Ich habe die Antwort-Header überprüft und dieser Code scheint gut zu funktionieren, aber es gibt ein paar Probleme:

  1. Das Video wird anscheinend sehr langsam geladen, da es sich in einem lokalen Netzwerk befindet. Soweit ich die Antwort mit Hilfe von Firebug untersuchen kann, scheint die Datei mit etwa 150 kb/s gestreamt zu werden.
  2. Das Video wird überhaupt nicht abgespielt. Selbst wenn ich darauf warte, dass das Ganze geladen wird, zeigt das HTML 5-Video-Tag nur ein großes "x" anstelle eines Films in Firefox.

Hat jemand eine Idee, was ich tun kann, um Video-Streaming über node.js zum Laufen zu bringen?

Vielen Dank!
Chris

43

Ich konnte dies mit Hilfe der nodejs-Foren zum Laufen bringen:

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

Highlights aus dem Google Groups-Thread:

Es ist bekannt, dass Google chrome zuerst eine Anfrage mit dem Bereich 0-1024 stellt und dann den Bereich "1024-" anfordert.

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

Dann:

Ich konnte das Video in Firefox problemlos abspielen, indem ich den Header "connection" auf "close" stellte

Dann:

Scheint, dass Sie die Länge des Inhalts falsch berechnen:

var chunksize = (End-Start) +1;

Wenn start 0 und end 1 ist, ist chunksize in Ihrem Fall 2 und sollte 1 sein.

18

Ich weiß, dass dies eine sehr alte Frage ist, aber da Google sie zu mögen scheint, dachte ich, es wäre erwähnenswert, darauf hinzuweisen, dass ich eine Node.js Video-Streaming-Modul (Github oder über NPM) geschrieben habe, was hoffentlich ist auch einen Blick wert.

23
meloncholy

Diese Lösung führt ein asynchrones Lesen einer serverseitigen Video- oder Audio-Mediendatei durch. Dabei wird ein Node-Server unter der URL gestartet, die unter angezeigt wird

http: // localhost: 8888 /

außerdem werden clientseitige HTML5-Bewegungen (Browser/App) des Widget-Schiebereglers für die Benutzeroberfläche vorwärts/rückwärts korrekt verarbeitet

speichern Sie das folgende Code-Snippet als serverseitige Datei:

media_server.js

... führe es serverseitig mit aus

node media_server.js

genießen

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

Basierend auf der Antwort von Sam9291 habe ich die Funktion mit createReadStream() umgeschrieben und einige Probleme behoben:

/**
 * 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);    

  });

}
5
Udo G

Ich verwende das MVC-Framework sails.js über Node.js und habe es geschafft, mit dem folgenden Code einwandfrei zu funktionieren:

/**
 * 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);

    });

  }

};

Hoffe das hilft

4
Samuel Poirier

Ich fand diese Lösung, die einfacher zu sein scheint und (im Gegensatz zur überprüften Antwort) für mich funktioniert. (Ich habe versucht, die Coffeescript-Lösung am Ende dieses Threads anzupassen, und es hat irgendwie funktioniert, als ich mich mit der Tatsache befasst habe, dass die anfängliche Anforderung (für "Bytes = 0-") sie in die Luft sprengt.

http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/

Meine eigentliche Umsetzung:

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

wenn Sie Express verwenden, geben Sie dies in Ihre Datei media_server.js oder index.js ein, die die Medien auf Port 3000 bedienen

const express = require('express')
const fs = require('fs')
const path = require('path')
const app = express()

app.use(express.static(path.join(__dirname, 'public')))

app.get('/', function(req, res) {
  res.sendFile(path.join(__dirname + '/index.html'))
})

app.get('/video', function(req, res) {
  const path = 'assets/sample.mp4'// your video path
  const stat = fs.statSync(path)
  const fileSize = stat.size
  const range = req.headers.range

  if (range) {
    const parts = range.replace(/bytes=/, "").split("-")
    const start = parseInt(parts[0], 10)
    const end = parts[1]
      ? parseInt(parts[1], 10)
      : fileSize-1

    const chunksize = (end-start)+1
    const file = fs.createReadStream(path, {start, end})
    const head = {
      'Content-Range': `bytes ${start}-${end}/${fileSize}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunksize,
      'Content-Type': 'video/mp4',
    }

    res.writeHead(206, head)
    file.pipe(res)
  } else {
    const head = {
      'Content-Length': fileSize,
      'Content-Type': 'video/mp4',
    }
    res.writeHead(200, head)
    fs.createReadStream(path).pipe(res)
  }
})

app.listen(3000, function () {
  console.log('Listening on port 3000!')
})

dann in deiner index.html

<html>
  <head>
    <title>Video stream sample</title>
  </head>
  <body>
    <video id="videoPlayer" controls muted="muted" autoplay> 
      <source src="http://localhost:3000/video" type="video/mp4">
    </video>
  </body>
</html>
0
Kevin Muchwat