• rtsp 流媒体播放完整的渲染方案


    前言: 虽然已经有许多前人踩过这个坑了,可惜的是虽然分享了,但并没有那么分享0.0。至少我看的那篇没有:::所以这里也就不贴那篇文章的链接了。看我这篇就足够了。

    效果

    请添加图片描述

    整体思路

    后端取流,实时转换成flv格式,推流到前端通过flv.js进行播放。

    后端

    后端在这里采用 NodeJS。在你想创建的目录下 npm init 一下,安装如下四个包·
    1、express
    2、express-ws
    3、fluent-ffmpeg
    4、websocket-stream
    在后端,只做一件事:将拉取到的rtsp流转换成flv格式,如此我们需要在前端先吧rtsp地址传过来。
    观察下面代码,忽略其他所有代码,关注于setFfmpegPath
    详细说明:
    在这里插入图片描述
    上图摘自fluent-ffmpeg的npm README中, 简单翻译下,如果FFMPEG_PATH 跟FFPROBE_PATH的环境变量都被设置了,则会取这个完整路径取执行ffmpeg。

    • ffmpeg是什么东西?
      ffmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。

    也就是说,这个fluent-ffmpeg 依赖于ffmpeg,你得先下载 ffmpeg
    ffmpeg下载地址
    在这里插入图片描述
    也就是说 windows 用户 假使你不想像下方代码一样显示的设置也可以,在你的环境变量中,声明FFMPEG_PATH 的位置 以及 FFPROBE_PATH的位置就好。当然 不管从哪里来说,能够省去一堆操作,直接写完整路径肯定轻松多了。尤其对于我这种前端码农来说。
    在这里插入图片描述
    上图目录如下代码所示
    给没有用过node的靓仔靓女温馨提示:node执行js文件的命令 为 node ?.js
    这个? 在当前表示为index
    在这里插入图片描述

    const express = require('express');
    const expressWebSocket = require('express-ws');
    const ffmpeg = require('fluent-ffmpeg')
    const webSocketStream = require('websocket-stream/stream')
    ffmpeg.setFfmpegPath(`D:\\Program Files\\rtspDemo\\ffmpeg-2022-07-18-git-cb22d5ea3c-full_build\\bin\\ffmpeg`);
    ffmpeg.setFfprobePath(`D:\\Program Files\\rtspDemo\\ffmpeg-2022-07-18-git-cb22d5ea3c-full_build\\bin\\ffprobe`);
    function localServer() {
        let app = express();
        app.use(express.static(__dirname));
        expressWebSocket(app, null, {
            perMessageDeflate: true
        });
        app.ws("/rtsp/:id/", rtspRequestHandle)
        app.listen(8888);
        console.log("express listened")
    }
    function rtspRequestHandle(ws, req) {
        console.log("rtsp request handle");
        const stream = webSocketStream(ws, {
            binary: true,
            browserBufferTimeout: 1000000
        }, {
            browserBufferTimeout: 1000000
        });
        let url = req.query.url;
        console.log("rtsp url:", url);
        console.log("rtsp params:", req.params);
        try {
            ffmpeg(url)
                // buffer_size 字节长度  如果你需要存储与发送更多的流 将102400 继续加!
                .addInputOption("-rtsp_transport", "tcp", "-buffer_size", "102400")  // 这里可以添加一些 RTSP 优化的参数
                .on("start", function () {
                    console.log(url, "Stream started.");
                })
                .on("codecData", function () {
                    console.log(arguments);
                    console.log(url, "Stream codecData.")
                    // 摄像机在线处理
                })
                .on("error", function (err) {
                    console.log(url, "An error occured: ", err.message);
                })
                .on("end", function () {
                    console.log(url, "Stream end!");
                    // 摄像机断线的处理
                })
                .outputFormat("flv").videoCodec("copy").noAudio().pipe(stream);
        } catch (error) {
            console.log(error);
        }
    }
    localServer()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    如上,后端的转换就已经处理好了,接着我们要按照后端这里设置的规则**/rtsp/:id/** 去处理。实际这样命名的含义也很简单,我们需要确定传输的对应流数据,给唯一确定的ID 发送对应处理的flv格式的流数据。

    其中noAudio是可以更改的,这里仅表示禁用音频流。以及addInputOption 的buffer-size ,这里意味着服务器这里为存储转换的缓冲流的大小。当服务器从流媒体服务器中获取到的流数据大于当前设置的存储大小时会报错,并且停止转换。

    前端

    经过前面从流媒体服务器获取并且实时的对这个流数据处理成flv,websocket发送返回前端,我们得到了一个实时传输的flv流数据。

    npm install flv.js --save

    安装完之后就简单了

    以刚建的vue2 为例子
    APP.VUE

    <template>
      <div id="app">
        <button @click="playVideo">
          加载
        </button>
        <div v-for="(item,index) in rtspList" :key="index">
          <HelloWorld :canPlay="canPlay" :id="item.id" :rtsp="item.url" />
        </div>
      </div>
    </template>
    
    <script>
    import HelloWorld from './components/HelloWorld.vue'
    
    export default {
      name: 'App',
      components: {
        HelloWorld
      },
      data(){
        return {
          rtspList: [
            {
              id: '1',
              url: 'rtsp://?/Streaming/Channels/501'
            },
            {
              id: '2',
              url: 'rtsp://?/Streaming/Channels/601'
            },
            {
              id: '3',
              url: 'rtsp://?/Streaming/Channels/801'
            }
          ],
          canPlay: false,
        }
      },
      methods:{
        playVideo(){
          this.canPlay = true;
        }
      }
    }
    </script>
    
    <style>
    html,body{
      margin: 0;
      padding: 0;
    }
    #app{
      height: 100%;
      width: 100%;
    }
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    HelloWorld.vue

    <template>
      <div>
        <video class="demo-video" ref="player"></video>
      </div>
    </template>
    <script>
    import flvjs from "flv.js";
    export default {
      props: {
        rtsp: String,
        id: String,
        canPlay: Boolean
      },
      watch:{
        canPlay(){
          if(this.canPlay){
            console.log('OK');
            this.player.play()
          }
        }
      },
      /**
       * @returns {{player: flvjs.Player}}
       */
      data() {
        return {
          player: null,
        };
      },
      mounted() {
        if (flvjs.isSupported()) {
          let video = this.$refs.player;
          if (video) {
            this.player = flvjs.createPlayer({
              type: "flv",
              isLive: true,
              url: `ws://localhost:8888/rtsp/${this.id}/?url=${this.rtsp}`,
              hasVideo: true,
            });
            this.player.attachMediaElement(video);
            try {
              // 【重要事件监听】http请求建立好后,该事件会一直监听flvjs实例
              this.player.on(flvjs.Events.STATISTICS_INFO, (res) => {
                console.log("请求数据信息");
              });
    
              this.player.load();
              // this.player.play();
            } catch (error) {
              console.log(error);
            }
          }
        }
      },
      beforeDestroy() {
        this.player.destory();
      },
    };
    </script>
    <style>
    .demo-video {
      max-width: 480px;
      max-height: 360px;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    代码不多,重在实现。尽管比较粗糙:没有太多的处理,毕竟我这的需求很简单,在页面上能播放rtsp流就可以,而不是出于盈利的直播等,对播放的处理要求不多。

    也许可能有的疑问

    为何需要按下加载才开始动呢?
    原因在于现在来说基本浏览器有默认的防噪音策略的限制。 所以理论上来说,直接加载就播放也是可行的,只是你得先把video 标签设置muted属性,表示静音。不过最好还是点一下再说,毕竟又不是什么流氓软件不是,嘿嘿。

  • 相关阅读:
    const常量和基础数据类型
    什么是正向代理和反向代理
    Redis缓存的使用技巧和设计方案(经典典藏版)
    VUE(5) : vue-element-admin[5] : 打包及nginx部署
    使用rust调用c++静态库并编译nodejs包
    【飞桨PaddleSpeech语音技术课程】— 语音识别-流式服务
    C#开发的应用升级更新服务器端工具 - 开源研究系列文章 - 个人小作品
    线性回归原理
    学会和自己相处
    微信小程序自动化框架的搭建python+minium
  • 原文地址:https://blog.csdn.net/q1025387665a/article/details/125887620