web播放rtsp方案(实测可行)

前言

项目上又遇到了播放rtsp、rtmp等原生流的需求,因为现在的浏览器早已抛弃flash,所以无法直接播放rtsp、rtmp这种格式的直播流,之前在某个项目上处理过,用的是后台调用ffmpeg转流,最终经过nginx处理转为m3u8格式让前端来播放,虽然最终能播放,但是实际延迟很大,开多了,服务器资源、带宽占用都非常大,不是一个好的解决方案,所以下面来说说另一个种可行的方案

原理
  • RTSP 流 -> WebSocket / WebRTC
  • WebSocket / WebRTC -> WebVideo
方案I - 基于 ffmpeg 的 Node 后端推流方案 + 基于 jspmeg / flvjs 的前端视频展示
后端部分:基于 ffmpeg 的 node 推流方案

这种方案应该是网上能搜索到的文章中最主流的方案,其中 Nodejs 部分可选择的包就有很多种:

上面几个包最后都能实现我们所要的效果,但是唯一都有一个前提条件:必须在系统里安装 FFMPEG,因为 RTSP 流转 WebSocket 就是通过底层调用 FFMPEG 来实现的。因为这些包都是经过封装后的,所以使用起来非常的简单,我这里就以第一个 node-rtsp-stream 为例,来说一下如可启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
js复制代码const Stream = require('node-rtsp-stream');

// Name of the stream, used to identify it in the API
new Stream({
name: 'socket',
streamUrl: 'rtsp://localhost:554/test',
wsPort: 9999,
// ffmpeg 的一些配置参数,比如转换分辨率等,大家可以去 ffmpeg 官网自行查询
ffmpegOptions: {
'-stats': '',
'-r': 20,
'-s': '1280 720'
}
});

你没有看错,就是这么简单,你只需要把你要接入的 RTSP 流地址填写进去即可,然后在前端你需要展示的就是转换过后的 WebSocket 地址,前端如何显示,在下方,耐心观看。

关于本地可测试的 RTSP 推流的创建过程,可以参考本文最后章节 其他 - 创建本地 RTSP 推流

前端部分

上面后端的部分真的可谓是非常简单,那么我们前端也不能落后,其实都是站在巨人的肩膀上前行,前面也提到了,我们的 video 大法在这种场景不好用了,因此我们就得考虑别的方案,调研一圈发现了两个比较成熟且验证过后比较靠谱的方案,笔者一一给大家介绍使用姿势。

  • jsmpeg.js 或 jsmpeg-player

这两个包是一样的,只不过如果你是原生 HTML 你就用 jsmpeg.js,如果你用的 React/Vue 这种框架,你就用 jsmpeg-player,因为现在大部分前端都是使用框架进行开发,所以笔者直接用 jsmpeg-player 进行演示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { useEffect } from "react";
import JSMpeg from "@cycjimmy/jsmpeg-player";

export default function JsmpegPlayer() {

useEffect(() => {
// 根据你后端 RTSP 推流服务转的 WebSocket 地址修改
new JSMpeg.VideoElement('#video', 'ws://localhost:9999');
}, []);

return (
<div>
<h1>Jsmpeg Player</h1>
<div>
<video id="video" width="640" height="480"></video>
</div>
</div>
);
}

  • flv.js

FLV 相关代码也是类似的,直接贴代码了

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
import React, { useEffect, useRef } from 'react';
import flvjs from 'flv.js';

export default function FlvPlayer() {
const flvRef = useRef(null);
useEffect(() => {
// 根据你后端 RTSP 推流服务转的 WebSocket 地址修改
const videoElement = document.getElementById('videoElement');
if (flvjs.isSupported()) {
const flvPlayer = flvjs.createPlayer({
type: 'flv',
isLive: true,
url: 'ws://localhost:9998',
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
flvRef.current = flvPlayer;
// flvPlayer.play();
}
}, []);

return (
<div>
<h1>Flv.js Player</h1>
<div style={{ width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<video id="videoElement" controls></video>
<button onClick={() => {flvRef?.current?.play()}}>Play</button>
</div>
</div>
);
}

方案II - 基于 WebRTC 的推流方案(推荐)

基于 WebRTC 实现 RTSP 流的推送,需要优先下载 webrtc-streamer,官方提供了各种系统各种版本的安装包,大家下载安装即可。

WebRTC Streamer 是用于 V4L2 捕获设备、RTSP源 和屏幕捕获的 WebRTC 流媒体。**关于这个东西底层原理,其实大家不需要了解,笔者也不知道。我们只需要知道它能捕获 RTSP 流在前端使用播放出来就可以了。

前端部分

前端代码看起来比较复杂,但是实际上都是复制粘贴,因为核心代码片段 WebRTC Streamer 官方已经给我们提供了足够多的基础组件,笔者在这里就以其中一个为例展示。

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
html复制代码<html>
<head>
<script type="module" src="./js/webrtc-streamer-element.js"></script>
<style>
h2 {
margin: 0 auto;
text-align: center;
}
</style>
</head>
<body>
<h2 id="message"></h2>
<webrtc-streamer id="stream" options="rtptransport=tcp&timeout=60&width=0&height=0&bitrate=0&rotation=0" style="display:none"></webrtc-streamer>
<p id="hint">默认从url连接上获取 video || audio 参数进行播放,本示例如果没有会选择本地 RTSP 推流</p>
<p style="color: red">【注意】:WebRTC 播放 RTSP 流需要前置启动 webrtc-streamer 客户端,否则推流无法播放</p>
<script>
let messageElement = document.getElementById("message");
customElements.whenDefined('webrtc-streamer').then(() => {
let streamElement = document.getElementById("stream");

var params = new URLSearchParams(location.search);
if (params.has("options")) {
streamElement.setAttribute('options', params.get("options"));
}
let url = {
video:params.get("video") || 'rtsp://localhost:554/test',
};
streamElement.setAttribute('url', JSON.stringify(url));
streamElement.style.display = "block"
}).catch( (e) => {
messageElement.innerText = "webrtc-streamer webcomponent fails to initialize error:" + e
})
</script>
</body>
</html>

更详细的代码已经存放到仓库,大家可以 clone 下来运行一下,需要注意的就是要想能看到正常播放视频,要满足两个条件:

  • 必须启动 WebRTC Streamer
  • 必须有一个可用的 RTSP 推流(关于本地可测试的 RTSP 推流的创建过程,可以参考本文最后章节 其他 - 创建本地 RTSP 推流。)
结语

推荐使用方案2,因为我两种方案都测试过,方案1延迟较大,局域网内延迟大概在5秒左右,首次播放较慢!

方案2实测播放速度很快,首次播放无需等待,基本可以说无延迟!