前言: 虽然已经有许多前人踩过这个坑了,可惜的是虽然分享了,但并没有那么分享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。
也就是说,这个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()
如上,后端的转换就已经处理好了,接着我们要按照后端这里设置的规则**/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>
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>
代码不多,重在实现。尽管比较粗糙:没有太多的处理,毕竟我这的需求很简单,在页面上能播放rtsp流就可以,而不是出于盈利的直播等,对播放的处理要求不多。
为何需要按下加载才开始动呢?
原因在于现在来说基本浏览器有默认的防噪音策略的限制。 所以理论上来说,直接加载就播放也是可行的,只是你得先把video 标签设置muted属性,表示静音。不过最好还是点一下再说,毕竟又不是什么流氓软件不是,嘿嘿。