Do you want to create a video streaming app and store videos in MongoDB? If you JUST want to learn about the streaming part, I made another (kind of successful) post on it using purely NodeJS.
BUT!
If you also want to know how to store your videos in MongoDB and stream them from there through NodeJS, this is the post for you!
Final Result
Just like the pure NodeJS solution, our final result will be a single HTML5 video that's streaming video from our server. Look at the grey bar in the timeline! That's buffering! 😃
If you want to git clone the project, here's the link to the repo https://github.com/Abdisalan/blog-code-examples/tree/master/mongo-http-video
Easy Mode
If you have docker installed, you can use my docker compose file to run the project without needing to install any packages. You just need to run these docker compose commands as well as copy the bigbuck.mp4
file from the http-video-stream
folder to the mongo-http-video
folder.
docker-compose up -d
# When you're done with it, you can tear it down using
docker-compose down -v
And the project will be running on localhost:8000
The rest of the article is instructions on how to build it from scratch. Enjoy!
Part 1: MongoDB
MongoDB does not support Windows Subsystem for Linux (WSL), so you're better off using docker if you want to use WSL. Otherwise this should work in Windows just fine.
Install MongoDB, and if you're on windows these next commands (exe files) should be located in C:\Program Files\MongoDB\Server\4.4\bin
Open a terminal in that folder OR add it to your PATH and start the mongod
process.
mongod
Part 2: Setup Node Project
In another terminal, these commands will create a project folder and start your node project.
mkdir mongo-video-stream
cd mongo-video-stream
npm init
npm install --save express nodemon mongodb
Part 3: index.html
We need to create an page with an HTML5 Video element and set the source to "/mongo-video"
where our server will stream the video from mongoDB.
<!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 From MongoDB</title>
</head>
<body>
<video id="videoPlayer" width="650" controls muted="muted" autoplay>
<source src="/mongo-video" type="video/mp4" />
</video>
</body>
</html>
Part 4: Index.js
Lets setup our node server so that the root "/"
endpoint 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 5: package.json -- Run our server
Add a start
script to your package.json
so that we can run our server using npm start
command.
{
"scripts": {
"start": "nodemon index.js"
}
}
Now you should be able to run npm start
. Open your browser and go to http://localhost:8000
to see if it worked!
Midway Check In
How are you doing? Maybe drink some water, fix your posture, and relax your shoulders 😁
You're about to get to the good part!
Part 6: index.js (Uploading)
We add an endpoint where we can upload a local video to mongodb. The video file I'm using is bigbuck.mp4
and can be found on my github at https://github.com/Abdisalan/blog-code-examples/tree/master/http-video-stream
However you can use your own video file!
const express = require("express");
const app = express();
const fs = require("fs");
const mongodb = require('mongodb');
const url = 'mongodb://localhost:27017';
app.get("/", function (req, res) {
res.sendFile(__dirname + "/index.html");
});
// Sorry about this monstrosity -- just for demo purposes
app.get('/init-video', function (req, res) {
mongodb.MongoClient.connect(url, function (error, client) {
if (error) {
res.json(error);
return;
}
// connect to the videos database
const db = client.db('videos');
// Create GridFS bucket to upload a large file
const bucket = new mongodb.GridFSBucket(db);
// create upload stream using GridFS bucket
const videoUploadStream = bucket.openUploadStream('bigbuck');
// You can put your file instead of bigbuck.mp4
const videoReadStream = fs.createReadStream('./bigbuck.mp4');
// Finally Upload!
videoReadStream.pipe(videoUploadStream);
// All done!
res.status(200).send("Done...");
});
});
app.listen(8000, function () {
console.log("Listening on port 8000!");
});
After saving the index.js file, your server should restart (because we're using nodemon). Once you have the video ready, you can go to localhost:8000/init-video
in your browser and it should have uploaded your local file to mongodb!
Almost there!
If you want to double check that the file was uploaded, open another terminal and connect to mongodb.
mongo
Then go to the videos database and count the number of documents in the fs.files
collection by GridFS.
use videos
db.fs.files.count()
The count should be how many times you went to localhost:8000/init-video
because it uploads our video file every time you load /init-video
.
Part 7: index.js (Streaming)
Lastly, we're gonna add the /mongo-video
endpoint to stream our video!
app.get("/mongo-video", function (req, res) {
mongodb.MongoClient.connect(url, function (error, client) {
if (error) {
res.status(500).json(error);
return;
}
// Check for range headers to find our start time
const range = req.headers.range;
if (!range) {
res.status(400).send("Requires Range header");
}
const db = client.db('videos');
// GridFS Collection
db.collection('fs.files').findOne({}, (err, video) => {
if (!video) {
res.status(404).send("No video uploaded!");
return;
}
// Create response headers
const videoSize = video.length;
const start = Number(range.replace(/\D/g, ""));
const end = videoSize - 1;
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);
// Get the bucket and download stream from GridFS
const bucket = new mongodb.GridFSBucket(db);
const downloadStream = bucket.openDownloadStreamByName('bigbuck', {
start
});
// Finally pipe video to response
downloadStream.pipe(res);
});
});
});
Save the file and go to localhost:8000
one more time and the video should be streaming!
Conclusion
With this, you can make your own basic YouTube or Netflix app!
For an in-depth line by line explanation with theory on how this works, consider watching my YouTube video.
This was a basic overview of how to get this working, if you want any in-depth blog posts on any of these topics (mongodb, streaming theory), feel free to comment down below!
Happy Streaming! ✌
Disclaimer
You probably shouldn't use this in production because its not the most optimized architecture 😋
Top comments (10)
Interesting.
I've never thought to use GridFS for this. Missing in your article is a reason why someone would do this instead of using object storage like S3. Its important so that some new developers who stumble upon this article don't rush to implement this as an actual solution without understanding the cost/performance benefits of a solution like this.
This is a bit misleading and I think you should definitely explain realistic use cases for this method.
Otherwise great article and I've learned something new!
How can One Use S3 to store Video format , can you provide me some article ??
Thanks! You bring a good point!
Hmm. Don't do this EVER. Its a good way to learn MongoDB GridFS but its not scalable at all. Good hack but will most probably choke the DB if connections increase.
Try it locally but not for real world scenario.
Thanks! You're absolutely right, and this should just be used for fun/demo purposes if at all. This was my first time really using MongoDB so I just hacked it together 😂
Thanks! Worked for me using GridFs, so I tried to take a different approach using S3. However, I'm running into issues (such as video stops streaming/playing) when reading from the stream. Do you know why that might happen?
did you fix this?
amazing
wow, so cool, i will give this a try!
Glad you like it! 😁
Some comments have been hidden by the post's author - find out more