How to Code a Video Streaming Server using MongoDB

Abdisalan - Nov 28 '20 - - Dev Community

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 😋

. . . . . . . . . . . . . .
Terabox Video Player