📜  如何流式传输大型 .mp4 文件?

📅  最后修改于: 2021-11-08 05:59:10             🧑  作者: Mango

将大型视频文件从服务器流式传输到客户端,这可能是网络应用程序或移动应用程序效率低下的方式!

我们无法将整个视频一次性发送给客户。因为有用户的带宽和数据限制等条件。

例子:

比方说youtube,用户可能会因为某种原因观看了视频的某些部分并离开了,如果他再次打开该视频,则不应从一开始就加载整个视频。

这是我们需要某种机制来处理这种情况的部分。

Nodejs 流 API出现了,它比以传统方式读取文件并等待回调解决更有效。

创建了一个基本的视频流服务器来更好地解释它。

系统流程:

  • 用户访问站点 (index.html)
  • index.html 中的 html5 视频组件指向服务器正在流式传输的视频的 URL
  • 视频请求由服务器通过处理范围标头处理,并将部分视频作为部分内容发送出去

设置项目:

  • 打开终端
  • mkdir <项目名称>
  • cd <项目名称>
  • npm 初始化 -y
  • 打开你最喜欢的文本编辑器
  • 在该文件夹上创建index.html (检查下面的 html 代码,在那里使用它)
  • 创建一个server.js文件(检查下面提供的 js 代码)
  • 运行命令“node server.js”
  • 打开浏览器并导航到“localhost:3000”

完整的源代码:

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

我们需要一个 index.html 文件来显示来自服务器的视频!

HTML



    
    
    
    video player


    


Javascript
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 continuously 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}`);
})


Javascript

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 continuously 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}`);
})

最后我们可以在浏览器中看到结果

最后结果