Diogo Spínola
Jul 24, 2017 · 4 min read
Beverly Nguyen

Someone asked me how to video stream using Node.js. This was something I hadn’t tried yet, so I thought, “why not!” Let me share my findings with you.

The challenge was to create a route which sends a .mp4 file to a page and makes the video available for viewing.

The way I decomposed it:

  • Make a server route to feed the video.
  • Use HTML5 and JS to request the feed.
  • Make the video load in parts instead of it being loaded from the start.

TL;DR — You can find a working demo of video streaming here.


Streams

Videos work by streaming. This means that, instead of sending everything to the front end in a single package, you should send it in little chunks at a time.

Sending it by streams would mean that, instead of having to wait for the page to download the whole video from the server before being able to watch it, you can instead request the server for the first few seconds of the video and download the rest while it plays out.

You can also use this approach for sending big chunks of text. For example, your client wouldn’t have to wait as long for the first few lines of an article to show up.


Some Theory

  • Getting the file size: fs in Node has a method called statSync that will return the stats of a file. Among those stats, it’s the file size we need to know when the currently loaded chunk has reached the end of the file. You can also use stat — in my case, I tried to avoid synchronicity to try and keep the code easier to understand for newcomers.
  • Creating a stream from a file: fs contains another method called createReadStream which will create a stream given a file, the start and end chunks.
const fileChunk = fs.createReadStream(sample.mp4, {start, end});
  • The size of the chunks: The starting chunk will be made available to you in the request. To figure out how much of the file to load, I use this subtraction of the end chunk size (if that’s not available, use the complete file size) and the starting chunk size:
                          endChunk - startChunk
  • HTTP 206: This is used for partial content, which is what we want the header of our connection to be. We continuously feed the front end with chunks and we want to have our starting chunk available when a request is made. You have to at least define:
'Content-Range': 'bytes chunkStart-chunkEnd/chunkSize'
'Accept-Ranges': 'bytes'
'Content-Length': chunkSize
'Content-Type': 'video/mp4'

The Server

Taking those things into account, I ended up with something like this in my route named video. (I’m using Express to create the route.)

That’s quite a bit of code. Don’t worry, you can always debug it further with the demo.

Let me try to explain the flow:

  • When a request is made, we get the file size and send the first few chunks of the video in the else statement.
  • When we start watching the video (by accessing the route via localhost:3000/video or from the front end), subsequent requests are made, this time with the range in the header so that we know the starting point of our next chunk.
  • Read the file again to create another stream, passing along the new value for the start and the end (which will most likely be the current part that came in the request header and the file size of the video).
  • We set our 206 header response to send only part of our newly made stream by applying the formula we talked about earlier.

The Front End

The front end is surprisingly easy with the HTML5 video tag — you just need to add a source route and it will handle the rest for you.

<video id="videoPlayer" controls>  <source src="http://localhost:3000/video" type="video/mp4"></video>

The controls attribute allows you to see the player’s controls.

Player controls, volume, play button and other

Without it, you can instead program those and other properties yourself by accessing the player element; in this case the id videoPlayer .

So, if you have a button on your HTML, you can do something like this to replicate the play/stop button:

In the network tab in your developer tools, you can see the streaming in chunks, especially if you throttle your connection.

Partial example of the stream in a 30MB file

Closing Remarks

I was not expecting this to go so smoothly for a simple implementation. There are of course some flaws in this implementation, such as the starting chunks not always being what is expected (probably because of connection hanging as I’m not handling that in this sample).

Nonetheless, I think it is a good starting point for those wanting to start creating some kind of streaming application. If you have a better or less error-prone approach let me know!

Better Programming

Advice for programmers.

Diogo Spínola

Written by

Learning enthusiast, web developer @nearsoftsolutio

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade