Do you want to stream video in your app without needing users to download the entire video? Here's how to do exactly that using NodeJS.
Final Result
Here's the end result of what we're gonna make.
Notice that light grey bar on the video timeline? That's the HTML5 Video Element buffering the video from our NodeJS server!
If you want to git clone the code and play with it yourself, here's the link to my GitHub Repo! https://github.com/Abdisalan/blog-code-examples/tree/master/http-video-stream
Part 1: Setup npm project
You'll need to install NodeJS and run:
mkdir http-video-stream
cd http-video-stream
npm init
npm install --save express nodemon
Part 2: index.html
We need to create a HTML5 Video element, and set the source as "/video"
, which is where server's endpoint is.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HTTP Video Stream</title>
</head>
<body>
<video id="videoPlayer" width="650" controls muted="muted" autoplay>
<source src="/video" type="video/mp4" />
</video>
</body>
</html>
Part 3: index.js
Now lets setup our node server so that on "/"
endpoint it serves our index.html page.
const express = require("express");
const app = express();
app.get("/", function (req, res) {
res.sendFile(__dirname + "/index.html");
});
app.listen(8000, function () {
console.log("Listening on port 8000!");
});
Part 4: package.json -- Run our server
Add a start
script to your package.json
so that we can run our server using npm start
command.
There's more in your package.json
file but I just want you to copy this start script. It uses nodemon
to run index.js
and restarts the server every time you save the index.js
file so you don't need to restart the server yourself!
{
"scripts": {
"start": "nodemon index.js"
}
}
Now you should be able to run
npm start
and see our app running on port 8000. Open your browser and go to http://localhost:8000
to see if it worked.
Part 5: index.js (Again)
We're almost there!
For this final stage, you'll need to either find an mp4 video file, or download the one I've provided in my GitHub project link.
https://github.com/Abdisalan/blog-code-examples/tree/master/http-video-stream
Here's the "/video"
endpoint for our server.
// in the imports above
const fs = require("fs");
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 61MB)
const videoPath = "bigbuck.mp4";
const videoSize = fs.statSync("bigbuck.mp4").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);
});
The HTML5 video element makes a request to the /video
endpoint, and the server returns a file stream of the video, along with headers to tell which part of the video we're sending over.
For a chunk size, I've decided 1MB but you could change that to whatever you like! Another great benefit of this is that we don't need to code the stream to continuously deliver the video data, the browser handles that gracefully for us.
For an in-depth line by line playback on how this works, consider watching my YouTube video on this topic.
Now, you've got a working video streaming server using NodeJS!
Happy Streaming! ✌
Top comments (29)
right, makes sense. That's what happens when you have a small hiccup in the network and the stream lowers the quality for a moment and resets back to a better quality when it gets better.
Thanks for your reply.
Hey Abdisalan! Great one!
I made the project from scratch from the article, and then noticed it was missing the "require fs". in the repo that's all ok, just the article misses it.
Other than that, perfect start. I'm starting my journey into video and streaming, and was an excellent start! Is this a main interest of yours?
I love it! Glad to have helped you get started! I'll try to fix that issue!
I'm not able to stream the video. I'm getting the
"Requires Range header"
response.I tried doing
const range = "bytes=0-1023";
but that didnt work too. The player just appears and doesnt play the videoi also got same erroot
how it is solve .did u got it?
I ran into the same error. The fix seems to be to set your
Content-Type
header before checking forRange
.wouldn't that require extra processing power? I thought what they did was generate multiple versions of the file and pick one accordingly to the required bandwidth and quality
Thanks for this. The way it's written works in Chrome but not Safari. To make it work in Safari I had to modify the code from this:
To this:
Just came across this post while searching for exactly this, and I'm happy to report that all has gone smoothly! I refactored the code a bit to work with Node's built-in
http
module, since that's what the rest of my project is using, and made a couple changes to ensure downloading (via the default video player's Download option) works correctly.Great articles! Really insightful to me since i'm starting curious in streaming
I wonder that if this stream way uses http protocol, does this mean we use tcp protocol ?
I just recently learned about tcp & udp protocols
where one of the usecases of udp is to serve connectionless data transfer (which is perfect in this video streaming case)
"For a chunk size, I've decided 1MB but you could change that to whatever you like!"
Is there any info on how to decide a chunk size? I've seen 1MB in a few articles but I haven't seen any reasons for it.
Wow I never knew, that’s cool!
does
fs.createReadStream
usessockets
under the hood?