近期有项目需求前端播放rtsp视频流,项目是由electron+vue3搭建,没有后端,因此需要在前端实现解析rtsp流及播放,经过多方考察最后选用了ffmpeg+jsmpeg方案实现,具体实现是参考了@牧也の旅行 大佬的这篇文章 原文地址 实现的,感谢@牧也の旅行 一直在回复我的各种小白问题,这里记录一下做这个功能时遇到的问题和解决方法:
主进程中的程序及其他配置参见原文,这里就不赘述了。
1,原文是单路播放,我的需求需要多路播放:
本来预想是展示16路,查资料也是说可以显示16路,但实际发现浏览器中只能稳定显示8路,超过后浏览器经常会报WARNING: Too many active WebGL contexts. Oldest context will be lost.
并自动从第一路开始销毁,无法恢复(这里一直没有找到解决办法):
采用的方式是在vue组件中预先设置好canvas,id就是rtsp流的通道号
<div v-for="i in 8" :key="i" style="padding:5px;" >
<canvas class="preview-video-canvas" :id="'chn'+(i-1).toString(16)"></canvas>
</div>
编写一个video.js 来播放多路
const {ipcRenderer} = require('electron');
import MpegPlayer from 'jsmpeg-player'
export class videoRtspPlayer {
#pc;
constructor(elements, opts = {}) {
this.opts = opts;
this.#pc = [];
this.videoElement = Array.from(elements);
this.baseUrl = this.init(this.opts);
}
/**
* 创建视频播放url
* @param {Object} opts 传入rtsp地址
* @return {Promise<void>}
*/
init(opts){
return `rtsp://${opts.addr}:666/live/`;
}
/**
* 播放
* @return {Promise<void>}
*/
play() {
this.videoElement.forEach(async (video,index)=>{
setTimeout(()=>{
let pc;
const url =`${this.baseUrl}${video.id}`;
const res = ipcRenderer.sendSync('openRtsp', url,index);
if (res.code === 200) {
// pc = new MpegPlayer.VideoElement(video, res.ws); 这里由于我的播放位置是固定的,因此使用 MpegPlayer.Player方法 ,与原文不同
pc = new MpegPlayer.Player(res.ws,{
canvas:video,
videoBufferSize:1024*1024, //增加一些缓存
})
pc['rtsp'] = url;
}
pc && this.#pc.push(pc);
},index*100)
})
}
/**
* 停止
*/
stop() {
console.log(this.#pc)
this.#pc.forEach(pc=>{
pc.stop();
ipcRenderer.sendSync('closeRtsp', pc.rtsp);
})
this.#pc = [];
}
}
electron/main.js中稍作改动,端口改为固定8个,不再使用addPort++;
vue中调用
import {videoRtspPlayer} from "../../common/Video";
let videoPlayer;
const netState = {
addr: 你的rtsp地址
}
const initVideo = () =>{
//开启视频预览
let videos = document.getElementsByClassName('preview-video-canvas');
videoPlayer = new videoRtspPlayer(videos,netState);
}
onMounted(() => {
initVideo();
videoPlayer.play()
})
onUnmounted(()=>{
videoPlayer.stop();
})
至此,在开发环境,我可以正常开启8路rtsp视频预览了,通过配置Stream的尺寸,减小播放窗口,我的内存和cpu占用还算理想。
2、接着我就遇到了这个项目最大的坑:打包。打包后的程序,安装完运行起来一直报:
A JavaScript error occurred in the main process
Uncaught Exception:Error: spawn D: ldeaProjects md scs dist electron ffmpeg.exe ENOENTat ChildProcess.handle.onexit (node:internal/child process:283:19)at onErrorNT (node:internal/child process:478:16)at process.processTicksAndRejections (node:internal/process/task queues:83:21),
总的意思就是找不到 ffmpeg.exe 这个文件,查了一整天资料研究打包修改配置,我是使用electron-builder打包,配置文件在vue.config.js中,关键步骤如下:
1)将 ffmpeg.exe 拷贝到项目根目录;
2)配置中添加:
electronBuilder: {
...
builderOptions:{
...
"asar": true,
"extraResources":['ffmpeg.exe'],
...
}
...
}
这样打包安装后,ffmpeg.exe就会出现在安装目录的resources/下,
electron/main.js中下面这句需要修改
// ffmpegPath: app.isPackaged ? ffmpegPath.replace('app.asar', 'app.asar.unpacked') : ffmpegPath, //改为下面那句
ffmpegPath: app.isPackaged ? ffmpegPath.replace('app.asar', '') : ffmpegPath,
之后打包-安装-播放成功~