Video buffering and streaming in NodeJS

In this tutorial, we are going to teach you, how you can create video streaming and buffering like YouTube in NodeJS. In YouTube, you have noticed that the whole video is not downloaded in the browser at once. Only 10-15 seconds of the video has been downloaded in the browser. And more will be downloaded as user continues to watch.

Same technique it applied by Netflix where you watch just the 10-15 seconds of video and the next 10 seconds are fetched as you watch. This technique is useful in 2 terms:

  1. First, it provides a layer of security. You do not write the direct path of video in the <video> tag. You write the API route, and the API will fetch the video data.
  2. Second, it saves bandwidth. If a file is of 100 MB in size, user can download just 1 MB of at start. User can start watching the 1 MB and the next MBs will automatically gets downloaded periodically. User does not have to wait for whole 100 MBs to get downloaded.

Let’s get started

Start by creating an empty folder in your computer. Make sure you have NodeJS installed in your system. Open command prompt or terminal in that folder and run the following command in it:

> npm install express fs ejs

express module is used for routing. fs stands for File System, this module will be used to read the video file. ejs is a templating engine that will be used to render the HTML file. We will display the video in a <video> tag in an HTML file.

Once you ran the above commands, create a file named server.js. This is where we will do all the streaming and buffering work. Write the following code in that file:

const app = require("express")()
const fs = require("fs")

app.set("view engine", "ejs")

app.get("/", function (request, result) {
  result.render("video");
})

app.listen(3000, function () {
  console.log("Server started.")
})

Save the file and run the URL: http://localhost:3000 in the browser. You might see an error that the file video.ejs in not found in the views folder. You need to create a folder named views and inside this folder, create a file named video.ejs.

Following will be the code of views/video.ejs file.

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

You can see that we have not set the direct path of the video. Instead we wrote the path of our API endpoint that will return the video data.

Buffer the video

Now we need to create an API endpoint that will handle this request. Create the following GET route in your server.js file.

app.get("/video", function (req, res) {
  // Ensure there is a range given for the video
  const range = req.headers.range
  if (!range) {
    res.status(400).send("Requires Range header")
  }

  // get video stats (about 100MB)
  const videoPath = "video.mp4"
  const videoSize = fs.statSync(videoPath).size

  // Parse Range
  // Example: "bytes=32324-"
  const CHUNK_SIZE = 10 ** 6 // 1MB
  const start = Number(range.replace(/\D/g, ""))
  const end = Math.min(start + CHUNK_SIZE, videoSize - 1)

  // Create headers
  const contentLength = end - start + 1
  const headers = {
    "Content-Range": `bytes ${start}-${end}/${videoSize}`,
    "Accept-Ranges": "bytes",
    "Content-Length": contentLength,
    "Content-Type": "video/mp4",
  }

  // HTTP Status 206 for Partial Content
  res.writeHead(206, headers)

  // create video read stream for this particular chunk
  const videoStream = fs.createReadStream(videoPath, { start, end })

  // Stream the video chunk to the client
  videoStream.pipe(res)
})

** operator is used to raise the first operand to the power of second operand. That’s how you can create a video streaming and buffering engine in NodeJS. Comments has been added with each line for explanation. If you refresh the page now, you will start seeing the video with buffer on it. If you face any problem in following this, kindly do let me know.