# node 实现 RTSP 在 web 中播放
# 1、使用 VLC 等工具测试,确保 RTSP 流可连接
[搭建本地rtsp服务器]
# 2、RTSP 方案的对比
方案 | 协议 | 视频格式 | 延迟 | 离线事件汇报 | 最小端口占用 | 依赖 |
---|---|---|---|---|---|---|
1 | HLS | ogg | 网络延迟较高,可达 10 秒以上 | 难 | n | VLC + video.js |
2 | RTMP | flv | 网络延迟较低,5 秒左右 | 难 | n | ffmpeg + nginx + flash + video.js |
3 | WebSocket | mpegts | 网络延迟较低,渲染速度慢 | 易 | 1 | ffmpeg + express + jsmpeg |
4 | HTTP-FLV | flv | 网络延迟较低,渲染速度快 | 易 | 1 | ffmpeg + express + flv.js |
# 3、基于 flv.js 的 RTSP 播放方案
基于 flvjs
,原理是在后端利用 转流工具 FFmpeg 将 rtsp流
转成 flv流
,然后通过 websocket
传输 flv流
,在利用 flvjs
解析成可以在浏览器播放的视频。
# 4、安装依赖包
npm install express express-ws fluent-ffmpeg websocket-stream @ffmpeg-installer/ffmpeg
# 5、安装 ffmpeg
下载后需要将 ffmpeg 的 bin 目录设置为环境变量
# 6、服务器代码
var express = require('express');
var expressWebSocket = require('express-ws');
var ffmpeg = require('fluent-ffmpeg');
const ffmpegInstaller = require('@ffmpeg-installer/ffmpeg');
ffmpeg.setFfmpegPath(ffmpegInstaller.path); // 此处
var webSocketStream = require('websocket-stream/stream');
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)
.addInputOption('-rtsp_transport', 'tcp', '-buffer_size', '102400') // 这里可以添加一些 RTSP 优化的参数
.on('start', function () {
console.log(url, 'Stream started.');
})
.on('codecData', function () {
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
53
54
55
56
57
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
# 7、客户端代码
需要安装 flv.js
pnpm i flv.js
<template>
<div class="home-page">
<button @click="clickbtn">播放</button>
<video class="demo-video" ref="playerRef" muted autoplay></video>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import flvjs from 'flv.js';
const playerRef = ref(null);
let player = null;
const id = '1';
const rtsp =
'rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4';
onMounted(() => {
if (flvjs.isSupported()) {
const video: any = playerRef.value;
if (video) {
player = flvjs.createPlayer({
type: 'flv',
isLive: true,
url: `ws://localhost:8888/rtsp/${id}/?url=${rtsp}`,
});
player.attachMediaElement(video);
player.load();
// try {
// player.load()
// player.play()
// } catch (error) {
// console.log(error)
// }
}
}
});
const clickbtn = () => {
player.play();
};
</script>
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
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
在 vite.config.ts 中设置代理
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
base: './',
plugins: [vue()],
server: {
host: true,
port: 12001,
proxy: {
'^/rtsp': {
// 局域网测试机地址
target: 'http://localhost:8888',
changeOrigin: true,
ws: true,
rewrite: path => path.replace(/^\/rtsp/, 'rtsp'),
},
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
dedupe: ['vue'],
},
})
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
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