2010-12-05 790 views
43

Estoy intentando configurar un servidor web que admita la transmisión de video a una etiqueta de video HTML5 usando node.js. Aquí está mi código hasta ahora:Transmisión de video con HTML 5 a través de node.js

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

Donde "petición" representa la petición HTTP, el tipo es "application/ogg" o "video/ogg" (He intentado ambos) y "archivo" es el. archivo ogv que se ha leído desde el sistema de archivos. Aquí están las cabeceras de respuesta:

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

He examinado las cabeceras de respuesta y este código parece estar funcionando bien, pero hay un par de problemas:

  1. El vídeo se ve a cargar muy lentamente por estar en una red local. Por lo que puedo decir al examinar la respuesta usando Firebug, el archivo parece ser transmitido a aproximadamente 150 kb/seg.
  2. El video no se reproduce en absoluto. Incluso si espero a que se cargue todo, la etiqueta de video HTML 5 solo muestra una gran "x" en lugar de una película en Firefox.

¿Alguien tiene alguna idea de lo que puedo hacer para que la transmisión de video funcione a través de node.js?

Gracias!
Chris

Respuesta

18

pude conseguir que esto funcione con la ayuda de los foros nodejs:

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

más destacado de la Google Grupos hilo:

Google Chrome se sabe que primero haga una solicitud con el rango 0-1024 y luego solicite el rango "1024-".

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

continuación:

yo era capaz de conseguir el vídeo para reproducir sin problemas en Firefox mediante el establecimiento de la "conexión" de cabecera "cerrar"

continuación:

Parece que no se está calculando la longitud del contenido:

var chunksize = (end-start) +1;

Si comienzo es 0 y al final es 1, en su caso chunksize es 2, y debe ser 1.

+0

Creo que el chunksize era correcta. De acuerdo con https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html: 'El valor de la primera posición de byte en una especificación de rango de bytes proporciona el desplazamiento de bytes del primer byte en un rango. El valor de último byte pos da el byte-offset del último byte en el rango; es decir, las posiciones de bytes especificadas son inclusivas. Las compensaciones de bytes comienzan en cero. – sthomps

23

Sé que esto es una pregunta muy viejo, pero como parece que Google me gusta pensé que valdría la pena señalar que escribí un Node.js video streaming module (Github, o vía NPM) que vale la pena echarle un vistazo también.

+4

¡Me encantan ese tipo de respuestas! Gracias. :) –

+0

¡Me alegro de que haya sido útil! – meloncholy

+0

Volviendo a ese tema después de 2 años ... :) ¿Hay alguna forma de usar esa secuencia de comandos para enviar datos en vivo (al navegador) que ha recibido udp? – randomuser1

4

Estoy usando las velas MVC framework .js en la parte superior de Node.js y me las arreglé para conseguir que funcione bien con el siguiente código:

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

    }); 

    } 

}; 

Esperanza esto ayuda

+1

ns.fs.statSync (../file/path/ ...) de npm se ocupa de los detalles. Ver https://gist.github.com/westonplatter/7559003. – westonplatter

+2

Esta solución no se escalará: extrae todo el archivo de video en la memoria para que sirva solo una pequeña parte. '' fs.createReadStream (theFile, {start: $ START, end: #END}) '' te permitirá canalizar la transmisión de la respuesta y no necesitar cargar todo el archivo de video en la memoria (imagina si 1000 usuarios lo hacen en al mismo tiempo). –

3

me encontré con esta solución, que parece ser más simple y (a diferencia de la respuesta comprobada) funciona para mí. (He intentado adaptar la solución CoffeeScript al final de ese hilo y como que trabajé una vez que tuve que lidiar con el hecho de que la solicitud inicial (para "bytes = 0-") sopla para arriba.

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

mi aplicación real:

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

Esto hace que los medios fluyan bien ... pero necesitaría procesar request.headers para reaccionar a las solicitudes de widgets del lado del cliente, salteando adelante/atrás en los medios de origen ... buen trabajo –

+0

Buen punto. Debería refinarlo. – podperson

9

Esta solución hace una lectura asíncrona de un video del lado del servidor o un archivo multimedia de audio ... que hace girar un servidor nodejs en la URL visible en

http://localhost:8888/

también se usa bien HTML5 lado del cliente (navegador/aplicación) los movimientos de deslizamiento hacia delante del aparato UI/atrás

Guardar a continuación, como fragmento de código del lado del servidor de archivos:

media_server.js 

... ejecutarlo en el servidor lado usando

node media_server.js 

disfrutar

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

¿Hay alguna manera de transmitir el archivo de video al navegador no desde el archivo de origen, sino desde el flujo de udp? – randomuser1

+0

¿Podemos hacerlo desde express? Nodo Noob aquí :) –

5

Sobre la base de la respuesta de Sam9291, Reescribí la función usando createReadStream() y fijar algunos problemas:

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

    }); 

} 
Cuestiones relacionadas