[javascript] Streaming di un file video su un video player html5 con Node.js in modo che i controlli video continuino a funzionare?



Answers

La risposta accettata a questa domanda è eccezionale e dovrebbe rimanere la risposta accettata. Tuttavia, ho riscontrato un problema con il codice in cui il flusso di lettura non veniva sempre chiuso / chiuso. Parte della soluzione era l'invio di autoClose: true e start:start, end:end nel secondo createReadStream arg.

L'altra parte della soluzione era limitare il chunksize massimo inviato nella risposta. L'altra risposta end così:

var end = positions[1] ? parseInt(positions[1], 10) : total - 1;

... che ha l'effetto di inviare il resto del file dalla posizione iniziale richiesta attraverso il suo ultimo byte, indipendentemente dal numero di byte che possono essere. Tuttavia, il browser client ha la possibilità di leggere solo una porzione di quel flusso e, se non ha ancora bisogno di tutti i byte, lo farà. Ciò causerà il blocco dello streaming fino a quando il browser non deciderà che è tempo di ottenere più dati (ad esempio un'azione dell'utente come seek / scrub o semplicemente riproducendo il flusso).

Avevo bisogno che questo flusso venisse chiuso perché stavo visualizzando l'elemento <video> su una pagina che permetteva all'utente di eliminare il file video. Tuttavia, il file non è stato rimosso dal filesystem fino a quando il client (o il server) non ha chiuso la connessione, perché questo è l'unico modo in cui il flusso veniva terminato / chiuso.

La mia soluzione era solo quella di impostare una variabile di configurazione maxChunk , impostarla su 1MB e non maxChunk mai pipe a un flusso di oltre 1 MB alla volta per la risposta.

// same code as accepted answer
var end = positions[1] ? parseInt(positions[1], 10) : total - 1;
var chunksize = (end - start) + 1;

// poor hack to send smaller chunks to the browser
var maxChunk = 1024 * 1024; // 1MB at a time
if (chunksize > maxChunk) {
  end = start + maxChunk - 1;
  chunksize = (end - start) + 1;
}

Ciò ha l'effetto di assicurare che il flusso di lettura sia terminato / chiuso dopo ogni richiesta e non tenuto in vita dal browser.

Ho anche scritto una question e una answer StackOverflow separate che trattano questo problema.

Question

Tl; Dr - The Question:

Qual è il modo giusto per gestire lo streaming di un file video su un video player html5 con Node.js in modo che i controlli video continuino a funzionare?

Penso che abbia a che fare con il modo in cui vengono gestite le intestazioni. Ad ogni modo, ecco le informazioni di base. Il codice è un po ' lungo, tuttavia, è piuttosto semplice.

Lo streaming di piccoli file video in video HTML5 con Node è facile

Ho imparato come eseguire lo streaming di piccoli file video in un video player HTML5 molto facilmente. Con questa configurazione, i controlli funzionano senza alcun intervento da parte mia e il video scorre in modo impeccabile. Una copia funzionante del codice completamente funzionante con video di esempio è qui, per il download su Google Docs .

Cliente:

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

Server:

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

Ma questo metodo è limitato a file di dimensioni <1 GB.

Streaming (qualsiasi dimensione) di file video con fs.createReadStream

Utilizzando fs.createReadStream() , il server può leggere il file in un flusso anziché leggerlo tutto in memoria in una sola volta. Questo suona come il modo giusto di fare le cose, e la sintassi è estremamente semplice:

Snippet del server:

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

Questo streaming il video va bene! Ma i controlli video non funzionano più.




Links