Stream video using HTML 5 via node.js

I am trying to set up a web server that will support streaming video in an HTML5 tag using node.js. Here is my 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); 

Where "request" represents an HTTP request, enter either "application / ogg" or "video / ogg" (I tried both), and the "file" is the .ogv file that was read from the file system. Here are the response headers:

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

I reviewed the response headers and this code works fine, but there are a few problems:

  • The video loads very slowly for the local network. From what I can tell about analyzing the response using firebug, the file is apparently being broadcast at a speed of about 150 kbps.
  • Video does not play at all. Even if I waited for all of this to load, the HTML 5 tag just shows a big β€œx”, not a movie in firefox.

Does anyone have any ideas as to what I can do to make video streaming work through node.js?

Thank!
Chris

+43
html5 stream video
Dec 05 '10 at 17:15
source share
7 answers

I managed to get this to work with some help on the nodejs forums:

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

Basic information from the Google Groups stream:

Google chrome, as you know, first makes a request with a range of 0-1024 and then request a range of "1024-".

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

Then:

I managed to get the video to play no problems in firefox by setting the β€œconnection” header to β€œclose”

Then:

It seems you are not calculating the length of the content correctly:

var chunksize = (end-start) +1;

If start is 0 and end is 1, in your case chunksize is 2, and it should be 1.

+18
Dec 13 '10 at 23:06
source share

I know this is a really old question, but since Google seems to like it, I thought it was worth noting that I wrote Node.js a video streaming module (Github, or via NPM), which I hope is also worth a look.

+23
May 18 '12 at 15:30
source share

This solution performs asynchronous reading of a video file or audio file on the server side ... it spins the nodejs server at the URL apparently at

http: // localhost: 8888 /

it also correctly handles the movements of the widget slider in HTML5 format (browser / application) on the client side

save below code snippet as a file on the server side:

 media_server.js 

... execute it on the server side using

 node media_server.js 

to use

 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
Mar 10 '15 at 19:06
source share

Based on Sam9291's answer, I rewrote the function using createReadStream() and fixed some problems:

 /** * 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
Mar 18 '15 at 15:32
source share

I am using the MVC sails.js framework on top of Node.js and I managed to get it working fine with the following code:

 /** * VideoController * * @module :: Controller * @description :: Contains logic for handling requests. */ var fs = require('fs'); module.exports = { /* eg 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); }); } }; 

Hope this helps

+4
Aug 14 '13 at 20:00
source share

I found this solution that seems simpler and (unlike the verified answer) works for me. (I tried to adapt the coffeescript solution at the end of this thread, and it worked when I came across the fact that the original request (for "bytes = 0-") blew it up.

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

My actual implementation:

 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
Oct 08 '13 at
source share

when using Express, specify this in the media_server.js or index.js file that will serve multimedia through port 3000

 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!') }) 

then in your 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
Apr 7 '19 at 19:25
source share



All Articles