Tl; Dr - La pregunta:
¿Cuál es la forma correcta de manejar la transmisión de un archivo de video a un reproductor de video html5 con Node.js para que los controles de video continúen funcionando?
Yo creo que tiene que ver con la forma en que los encabezados se manejan. De todos modos, aquí está la información de fondo. El código es un poco extenso, sin embargo, es bastante sencillo.
Transmitir pequeños archivos de video a video HTML5 con Node es fácil
Aprendí a transmitir archivos de video pequeños a un reproductor de video HTML5 muy fácilmente. Con esta configuración, los controles funcionan sin ningún trabajo de mi parte, y el video se transmite sin problemas. Una copia de trabajo del código completamente funcional con un video de muestra está aquí, para descargar en 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>
Servidor:
// 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);
Pero este método está limitado a archivos de menos de 1 GB de tamaño.
Transmisión de archivos de video (de cualquier tamaño) con fs.createReadStream
Al utilizarlo fs.createReadStream()
, el servidor puede leer el archivo en una secuencia en lugar de leerlo todo en la memoria a la vez. Esta parece la forma correcta de hacer las cosas, y la sintaxis es extremadamente simple:
Fragmento de servidor:
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);
});
¡Esto transmite el video perfectamente! Pero los controles de video ya no funcionan.
writeHead()
código comentado, pero ahí por si sirve de ayuda. ¿Debo eliminar eso para que el fragmento de código sea más legible?