0

I want to stream video in node.js through fs module & I have tried the below code.

app.get('/video', function(req, res) {
  const path = 'assets/sample.mp4'
  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)
  }
})

My video 'sample.mp4' size is: 1055736 bytes which is 1 MB. But when I run the code, I found that in starting the start variable is 0, end variable is 1055735 & chunksize is 1055736. And if you look at Content-Range header, which will be equals to below value in starting.

Content-Range : 'bytes 0-1055735/1055736'

I want to stream my video in chunks one after another instead of wait for the whole video to load. But I think above code will not do the same.

Can anyone please tell me, Is this code correct for video streaming?

Thanks in advance!

x00
  • 13,643
  • 3
  • 16
  • 40
Rajat
  • 1
  • 2

1 Answers1

0

Your code is OK, except for the one thing: usually a browser sends you Range: bytes=0- in requests for a video. From this header your code

const start = parseInt(parts[0],10);
const end = parts[1] ? parseInt(parts[1],10) : fileSize-1

gives you start === 0 and end === fileSize-1 and the chunksize of a file size. Because parts[1] is always falsy. So it is you who decides to send the file in one big chunk, not the browser. The browser is ready to receive a 206 Partial Content, but you are not sending it. Try

const preferred_chunksize = 100000
let end = parts[1] ? parseInt(parts[1],10) : start + preferred_chunksize
if( end > fileSize-1 ) end = fileSize-1
const chunksize = end-start+1

The preferred_chunksize can be anything from 1 to fileSize, depending on your network conditions. I tried preferred_chunksize = 1000 - it's slow. I didn't try preferred_chunksize = 1 - because it will be extremely slow and will lead to millions of requests. preferred_chunksize between 100 000 and 10 000 000 looks acceptable.

But, actually, if you'll try your code on a big file, your will notice no excessive memory usage, and that browsers will start playing your video file right away, without waiting

for the whole video to load

Even if you'll skip the end part all together (as it is already done in your version of code), and simplify your code using:

const end = fileSize-1
const chunksize = end-start+1

it will do.
And, as I know from the original version of your question, your are writing a tutorial, so for your purposes it will do just fine.

PS. And of course in production code you'd better check if Range header is malformed

x00
  • 13,643
  • 3
  • 16
  • 40
  • Thank you for the answer, But I don't know what `preferred_chunksize` should I use. Can you please edit your answer and tell me also that? – Rajat Jan 27 '20 at 08:31