DEV Community

Cover image for How to Code a Video Streaming Server using MongoDB
Abdisalan
Abdisalan

Posted on • Updated on

How to Code a Video Streaming Server using MongoDB

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

Buffing video of a bird

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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!");
});
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now you should be able to run npm start. Open your browser and go to http://localhost:8000 to see if it worked!

missing video

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!");
});
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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);
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
emmanuelnk profile image
Emmanuel K

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.

With this, you can make your own basic YouTube or Netflix app!

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!

Collapse
 
its_apex profile image
Aryan

How can One Use S3 to store Video format , can you provide me some article ??

Collapse
 
abdisalan_js profile image
Abdisalan

Thanks! You bring a good point!

Collapse
 
warlockdn profile image
Info Comment hidden by post author - thread only accessible via permalink
warlockD

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.

Collapse
 
abdisalan_js profile image
Abdisalan

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 😂

Collapse
 
ryanmiranda98 profile image
Ryan Miranda

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?

Collapse
 
sammelo2000 profile image
SAMUEL WILLIAMS

did you fix this?

Collapse
 
mugishaisaac profile image
Mugisha-isaac

amazing

Collapse
 
bias profile image
Tobias Nickel

wow, so cool, i will give this a try!

Collapse
 
abdisalan_js profile image
Abdisalan

Glad you like it! 😁

Some comments have been hidden by the post's author - find out more