How to stream large .mp4 files?

Streaming large video files from the server to the client which might be a web app or mobile apps inefficient manner!

We can’t send the entire video once to a client. because there are conditions like the bandwidth and data restrictions of the user.

Example:

Let’s say youtube, the user may watch some part of the video and went away for some reason, if he opened that video again, it should not load the entire video from start.

Here is the part we need some mechanism to handle this situation.



Nodejs stream API comes into picture, its more efficient than reading a file in conventional manner and waiting for the callbacks to resolve.

Created a basic video streaming server to explain it little better. 

    The flow of the system:

  • User visits the site (index.html)

  • The html5 video component in index.html is pointed to URL of the video that is streaming by server

  • The video request is handler by the server by processing range header and sends out part of the video as partial content

    Setting up project:

  • Open terminal



  • mkdir <project name>

  • cd <project name>

  • npm init -y

  • Open your favourite  text editor

  • Create index.html on that folder (check below html code, use it there)

  • Create a server.js file(check the js code provided below)

  • Run the command “node server.js”

  • Open browser and navigate to “localhost:3000”

Complete source code:

 https://github.com/varaprasadh/gfg-blogs/tree/master/streamvideo

we will need a index.html file show the video that coming from the server!

filter_none

edit
close

play_arrow

link
brightness_4
code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" 
          content="width=device-width, 
                   initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" 
            content="ie=edge">
    <title>video player</title>
</head>
<body>
    <video src="http://localhost:3000/video" controls>
   </video>
</body>
</html>

chevron_right


filter_none

edit
close

play_arrow

link
brightness_4
code

const http=require('http');
const fs=require("fs");
const path=require("path");
  
/* http.createServer takes a handler 
   function and returns a server instance;*/
const server=http.createServer((req, res)=>{
    // return res.end(req.url+req.method);
    if(req.method==='GET' && req.url==="/"){
        /*we will send a index.html page when 
          user visits "/" endpoint*/
        /*index.html will have video component 
          that displays the video*/
          
        fs.createReadStream(path.resolve(
                        "index.html")).pipe(res);
        return;
    }
    //if video content is requesting
    if(req.method==='GET' && req.url==="/video"){
        const filepath = path.resolve("video.mp4");
        const stat = fs.statSync(filepath)
        const fileSize = stat.size
        const range = req.headers.range
        /*when we seek the video it will put 
          range header to the request*/
        /*if range header exists send some 
            part of video*/
        if (range) {
            //range format is "bytes=start-end", 
            const parts = 
                range.replace(/bytes=/, "").split("-");
             
            const start = parseInt(parts[0], 10)
            /*in some cases end may not exists, if its 
                          not exists make it end of file*/
            const end = 
                 parts[1] ?parseInt(parts[1], 10) :fileSize - 1
              
            //chunk size is what the part of video we are sending.
            const chunksize = (end - start) + 1
            /*we can provide offset values as options to
           the fs.createReadStream to read part of content*/
            const file = fs.createReadStream(filepath, {start, end})
              
            const head = {
                'Content-Range': `bytes ${start}-${end}/${fileSize}`,
                'Accept-Ranges': 'bytes',
                'Content-Length': chunksize,
                'Content-Type': 'video/mp4',
            }
            /*we should set status code as 206 which is
                    for partial content*/
            // because video is continuosly fetched part by part 
            res.writeHead(206, head);
          file.pipe(res);
            
        }else{
          
        //if not send the video from start. 
        /* anyway html5 video player play content
          when sufficient frames available*/
        // It doesn't wait for the entire video to load.
          
           const head = {
               'Content-Length': fileSize,
               'Content-Type': 'video/mp4',
           }
           res.writeHead(200, head);
           fs.createReadStream(path).pipe(res);
        }
    }
    /*if anything other than handler routes then send
      400 status code, is for bad request*/
    else{
        res.writeHead(400);
        res.end("bad request");
    }
})
  
/*check if system has environment variable 
   for the port, otherwise defaults to 3000*/
const PORT = process.env.PORT || 3000;
  
//start the server
server.listen(PORT, () => {
  console.log(`server listening on port:${PORT}`);
})

chevron_right


Finally we can see the result in the browser

Final Result

full-stack-img




My Personal Notes arrow_drop_up


If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.