调用博主最近登录时间
生活中的HYGGE
解决RTSP推流前端切换视频导致播放黑屏频闪问题

解决RTSP推流前端切换视频导致播放黑屏频闪问题

hygge
2023-07-13 / 0 评论 / 156 阅读 / 正在检测是否收录...

解决RTSP推流前端切换视频导致播放黑屏频闪问题

背景

供应商那边设备类似于摄像头,推送的视频流格式是rtsp格式,需要进行转换到前端播放。

实现效果是摄像头在前端页面展示类似实时视频直播,问题是一个页面有多个视频源,在多个源进行切换时会导致视频画面黑屏频闪

复现

前提条件

  • 两个视频文件*.mp4
  • nodejs
  • ffmpeg及配置环境变量

客户端web页面

lk108nej.png

1.依赖准备

前端播放视频流需要导入jsmpeg-player

packages.json

...
"dependencies": {
    "axios": "^1.4.0",
    "element-plus": "^2.3.5",
    "jsmpeg": "^1.0.0",
    "jsmpeg-player": "^3.0.3",
    "vue": "^3.2.47"
},
...

App.vue

<script setup>
import { ref, watch, getCurrentInstance } from "vue";
import JsMpeg from "jsmpeg-player";

const ws = ref(null);
const { proxy } = getCurrentInstance();

const videoObject = ref({}); // 用于存储当前正在播放的视频数据

/**
* 视频的url构成规则为: rtsp://本机IP/自定义地址标识,后续将通过ffmpeg将对应视频转换为RTSP流,并通过TCP传输于该地址。
* 视频的port意义为: 不同的端口对应不同的视频,RTSP流通过TCP传输的地址前端不能直接对接,会将url发送给服务端,服务端进行接入后再于该端口进行ws推送。
*/
const videoList = [
  {
    label: "视频1-一人之下",
    name: "sp1",
    url: "rtsp://192.168.0.107/test",
    port: 8834,
  },
  {
    label: "视频2-小猫咪",
    name: "sp2",
    url: "rtsp://192.168.0.107/test2",
    port: 8812,
  },
];
// ...
</script>

2.页面准备

重要:视频推流的展示是需要canvas标签,但是一个canvas标签多次复用就会导致黑屏频闪问题!!!

所以将dom中的canvas标签移除,修改为每次点击按钮播放都动态创建一个canvas标签

<template>
  <el-button
    v-for="item in videoList"
    :key="item.name"
    @click="playerVideo(item)"
    >{{ item.label }}</el-button
  >
</template>

<style scoped></style>

3.播放实现

const useWs = (data) => {
  // 建立一个新的ws视频流播放时先对旧的ws视频流进行释放
  if (ws.value) ws.value.close();
  // 移除所有canvas
  const canvasList = document.querySelectorAll("canvas");
  canvasList.forEach((item) => {
    item.remove();
  });
  // 追加一个canvas#sp
  const sp = document.createElement("canvas");
  sp.setAttribute("id", "sp");
  document.body.appendChild(sp);
  
  // 与服务端建立ws通信,并未开始播放
  ws.value = new WebSocket("ws://localhost:5001");
    
  videoObject.value = data;
};

// 向服务端发送信息是通过watch监听实现,当videoObject发生变化时调用,即当前视频对象切换
watch(
  () => videoObject.value,
  (newV, oldV) => {
    // 当ws连接打开时回调 
    ws.value.onopen = function () {
      // 向服务端发送欲播放的视频数据,服务端接收后会进行推流
      ws.value.send(JSON.stringify(newV));
      // 使用框架 建立ws视频流播放,不同的端口对应不同的视频。
      new JsMpeg.Player(`ws://localhost:${newV.port}`, {
        canvas: document.getElementById('sp'),
      });
    };
  }
);
const playerVideo = (e) => {
  useWs(e);
};

服务端node实现

lk108ydo.png

重要:ffmpeg环境变量的配置+运行目录放一个ffmpeg.exe

1.依赖准备

packages.json

"dependencies": {
    "express": "^4.18.2",
    "node-ffmpeg-stream": "^1.1.0",
    "node-rtsp-stream": "^0.0.9",
    "node-rtsp-stream-jsmpeg": "^0.0.2",
    "ws": "^8.13.0"
},

index.js

const Stream = require('node-ffmpeg-stream').Stream;
const WebSocket = require('ws');
const ws = new WebSocket.Server({ port: 5001 });
const streams = new Map(); // 存储视频流的 Map

2.端口推流

ws.on('connection', (client) => {
  client.on('message', (msg) => {
    const data = JSON.parse(msg);
    console.log('开始播放');
    // 下面固定格式
    const stream = new Stream({
      name: data.name,
      url: data.url, // eg:   rtsp://192.168.0.107/test
      wsPort: data.port, // eg:  8834
      options: {
        '-stats': '', // 没有必要值的选项使用空字符串
        '-r': 30, // 具有必需值的选项指定键后面的值<br>    
      }
    });
    streams.set(data.name, stream);

    // 前端视频流切换时 + 页面刷新或关闭时触发,通知服务端停止推送当前流
    client.on('close', () => {
      if (streams.has(data.name)) {
        const stream = streams.get(data.name);
        stream.stopStream();
        streams.delete(data.name);
        console.log('连接已关闭');
      }
    });
  });
});

ffmpeg转换流并进行传输

lk10abce.png

这段代码使用 ffmpeg 工具来将本地的视频文件(test.mp4)转换为 RTSP 流,并将其通过 TCP 传输。

让我逐行解释这段代码的含义:

ffmpeg -stream_loop -1 -re -i "C:\Users\Administrator\Downloads\Video\test.mp4" -rtsp_transport tcp -vcodec h264 -f rtsp rtsp://localhost/test
  • ffmpeg: 这是命令行中调用 ffmpeg 工具的命令。
  • -stream_loop -1: 这个选项告诉 ffmpeg 无限循环输入文件。即使视频文件结束,它也会重新开始播放。
  • -re: 这个选项告诉 ffmpeg 使用实时模式,以原始速度读取输入文件。在实时模式下,ffmpeg 将尽力按照视频的实际帧率发送流数据。
  • -i "C:\Users\Administrator\Downloads\Video\test.mp4": 这是输入文件的路径。ffmpeg 将读取该文件作为输入。
  • -rtsp_transport tcp: 这个选项指定了 RTSP 流的传输协议为 TCP。通过 TCP 传输可以提供更稳定的连接。
  • -vcodec h264: 这个选项指定了视频编解码器为 H.264(AVC)。它将使用 H.264 编码视频流。
  • -f rtsp: 这个选项指定了输出格式为 RTSP。
  • rtsp://localhost/test: 这是输出的 RTSP 流的地址。ffmpeg 将流式传输的视频流发布到该地址。

综上所述,这段代码的作用是使用 ffmpeg 将本地的视频文件转换为 RTSP 流,并通过 TCP 传输发布到 rtsp://localhost/test 地址上。这样其他支持 RTSP 协议的设备或应用程序就可以通过该地址来接收和播放该视频流

输入命令如果卡着不动的话需要配合EasyDarwin流媒体服务,直接启动EasyDarwin后就可以了。

EasyDarwin

lk10ahsy.png

EasyDarwin 是一个开源的流媒体服务器软件,用于实现音视频流的传输和处理。它提供了一套完整的流媒体解决方案,包括流媒体推流、录制、转发、播放等功能。

EasyDarwin 可以用于搭建自己的流媒体服务器,支持常见的音视频编码格式和传输协议,如 RTSP、RTMP、HLS 等。它具有跨平台的特性,可以在 Windows、Linux、macOS 等操作系统上运行。

使用 EasyDarwin,您可以搭建一个可靠的流媒体服务器,从摄像头、音频设备或其他音视频源推送实时流,并将其传输到支持的客户端应用程序或播放器上进行播放。它也可以用于构建视频监控系统、直播平台、音视频会议等应用场景。

EasyDarwin 的开源性质使得它具有灵活性和可定制性,您可以根据自己的需求进行定制和扩展。同时,它还提供了一些管理工具和 Web 控制台,方便用户进行配置和管理流媒体服务器。

总的来说,EasyDarwin 是一个功能强大的开源流媒体服务器软件,可以帮助用户快速搭建自己的流媒体平台,并实现高质量的音视频流传输和处理。

引用

1.node-ffmpeg-stream:https://www.npmjs.com/package/node-ffmpeg-stream

2.ffmpeg实现将视频文件转换成rtsp流:https://blog.csdn.net/weixin_44591652/article/details/123004247

0

评论 (0)

取消