概述
所有现代浏览器都不原生支持播放RTSP(实时流协议)流,而这是许多流行IP摄像头的常用格式。因此长期以来,如果你想在网页上显示RTSP IP摄像头流,就必须使用中间转码服务器。这些服务器会接收RTSP流,解码并转换成浏览器可接受的格式,如HLS、MPEG-DASH或MP4。这种解决方案虽然可行,但由于需要转码和转换视频,可能会导致较大的延迟和性能不佳,而且输出视频质量往往会受到影响。
这个直接基于标准HTML5 <video>
元素的自定义HTML5播放器开发采用了不同的理念。它不是在中间服务器上解码RTSP流,而是将这个繁重的任务转移到最终用户的浏览器上,在那里解码单个流的任务微不足道。这意味着你的中间服务器不再需要承担所有负载,你可以将负载分散到所有观看RTSP流的人之间。另一个优点是,最终用户的浏览器将始终接收到来自IP摄像头的完整质量、未经改变的画面。
Streamedian是一个JavaScript库,它实现了RTSP客户端,用于在浏览器中观看实时流。它需要支持带有媒体源扩展的HTML5视频进行播放。此外,播放器还依赖于服务器端的WebSocket代理来将RTSP流重新传输到浏览器。
它通过将RTP H264和AAC有效载荷封装成ISO BMFF (MP4)片段来工作。
Streamedian使用ECMAScript 2015标准编写。
实时测试流
演示可在https://streamedian.com上查看
浏览器支持(ES5转译版本):
- Firefox v.42+
- Chrome v.23+
- OSX Safari v.8+
- MS Edge v.13+
- Opera v.15+
- Android浏览器 v.5.0+
- IE Mobile v.11+
不支持iOS Safari和Internet Explorer
安装
npm install git://github.com/Streamedian/html5_rtsp_player.git
使用方法
浏览器端
附加带有RTSP URL的HTML视频
<video id="test_video" controls autoplay src="rtsp://your_rtsp_stream/url"></video>
或
<video id="test_video" controls autoplay>
<source src="rtsp://your_rtsp_stream/url">
</video>
你可以通过传递data-ignore="true"来忽略源:
<video id="test_video" controls autoplay>
<source src="natively_supported_video_url" data-ignore="true">
<source src="rtsp://your_rtsp_stream/url">
</video>
如果浏览器可以播放该源,则不会为此元素初始化播放器。
在你的js中设置播放器:
import * as streamedian from 'streamedian/player.js';
// import WebsocketTransport from 'wsp/transport/websocket';
// import RTSPClient from 'wsp/client/rtsp/client';
let mediaElement = rtsp.attach(document.getElementById('test_video'));
let player = new streamedian.WSPlayer(mediaElement, {
// url: `${STREAM_URL}`, // 覆盖mediaElement的源
modules: [
{
// 客户端模块构造函数。应为BaseClient的子类或默认的RTSPClient
// client: RTSPClient,
transport: {
// 客户端模块构造函数。应为BaseTransport的子类或默认的WebsocketTransport
// constructor: WebsocketTransport,
options: {
// 下面描述的websocket代理地址。默认为ws${location.protocol=='https:'?'s':''}://${location.host}/ws/
socket: "ws://websocket_proxy_address/ws",
// 处理播放器异常的函数
errorHandler (e) {
alert(`启动播放器失败: ${e.message}`);
},
// 获取受保护流凭证的函数
queryCredentials() {
return new Promise((resolve, reject)=>{
let c = prompt('以user:password格式输入凭证');
if (c) {
this.setCredentials.apply(this, c.split(':'));
resolve();
} else {
reject();
}
});
}
}
}
},
]
});
需要ES6模块支持。你可以使用rollup来构建这个脚本:
rollup.config.js
import buble from 'rollup-plugin-buble';
import alias from 'rollup-plugin-alias';
const path = require('path');
export default {
entry: path.join(__dirname, 'example.js'),
targets: [
{dest: path.join(__dirname, 'example/streamedian.js'), format: 'es'}
],
sourceMap: true,
plugins: [
// 如果你想转译成es5,取消下面的注释
//buble({
//exclude: 'node_modules/**'
//}),
alias({
bp_logger: path.join(__dirname,'node_modules/bp_logger/logger'),
bp_event: path.join(__dirname,'node_modules/bp_event/event'),
bp_statemachine: path.join(__dirname,'node_modules/bp_statemachine/statemachine'),
//jsencrypt: path.join(__dirname,'node_modules/jsencrypt/src/jsencrypt.js'),
streamedian: path.join(__dirname,'src')
})
]
}
> npm install bp_event bp_logger bp_statemachine
> rollup -c
将编译后的脚本包含到你的HTML中:
<script src="streamedian.js"></script>
RTSP认证
Streamedian播放器支持基本认证和摘要认证。当你连接到流时,会自动检测认证类型。 你可以在rtsp url中直接指定用户名/密码,格式为rtsp://username@password:stream.url/resource/, 或者将_queryCredentials()函数传递给_streamedian.WSPlayer。这个函数应该返回一个promise, 调用_this.setCredentials(user, password)_。
服务器端
-
安装websocket代理
在http://streamedian.com注册并安装websocket代理
注册你想要使用此播放器的域名
将许可文件下载到你的服务器 请注意,此软件包依赖于systemd和gcc5+运行时,因此只能安装在较新的发行版上。
-
在/etc/ws_rtsp.ini中配置端口和许可证文件路径
此端口应在防火墙中开放。您也可以通过代理将请求传递到此端口。(例如:http://nginx.org/en/docs/http/websocket.html)
-
运行它
> service ws_rtsp start
播放器架构
播放器包括三个主要模块:传输、客户端和重新封装器。
传输负责数据传递和数据排队。它应触发_connected_、_disconnected_和_data_事件。 一旦接收到数据,传输应将其推入dataQueue并触发_data_事件。传输的基类可以在core/base_transport中找到。 默认情况下,实现了依赖于websocket代理的WebsocketTransport。
客户端监听数据事件,将其解析为基本流数据包,并将解析后的数据排入自己的队列。客户端可以 通过_samples_事件将排队的缓冲区传递给重新封装器。为识别流轨道,客户端应触发_tracks_事件。准备就绪时,可以使用_flush_ 事件构造媒体片段并将其传递给视频呈现器。传输的基类可以在core/base_client中找到。 默认客户端是基于websocket传输的RTSP客户端。
重新封装器为视频呈现器准备媒体片段并推送它。目前只有一个基于媒体源扩展的视频呈现器。 重新封装器将数据收集到mp4媒体片段(moof+mdat)中,并将其传递到源缓冲区。
RTSP代理如何工作?
RTSP播放器使用以下协议与代理建立连接:
-
通过指定"rtsp"协议连接websocket来连接RTSP通道,并获取连接ID
c>s: WSP/1.1 INIT\r\n seq: <用于响应跟踪的序列ID> host: <RTSP流主机>\r\n port: <RTSP流端口>\r\n\r\n s>c: WSP/1.1 200 OK\r\n seq: <用于响应跟踪的序列ID> channel: <通道ID>\r\n\r\n 错误代码 >= 400 表示错误
-
通过连接"rtp"协议的websocket来连接RTP通道
c>s: WSP/1.1 JOIN\r\n seq: <用于响应跟踪的序列ID>\r\n channel: <从RTSP套接字初始化获得的通道ID>\r\n\r\n s>c: WSP/1.1 200 OK\r\n seq: <用于响应跟踪的序列ID>\r\n\r\n 错误代码 >= 400 表示错误
-
发送包装在WSP协议中的RTSP命令:
c>s: WSP/1.1 WRAP\r\n seq: <用于响应跟踪的序列ID>\r\n \r\n <RTSP协议数据> s>c: WSP/1.1 200 OK\r\n channel: <通道ID>\r\n \r\n <RTSP协议响应> 错误代码 >= 400 表示错误
-
RTP通道应发送带有4字节头($<通道><大小>)的交错数据。目前不支持单独的RTP。
负载均衡
RTSP代理本身不支持负载均衡,但可以轻松集成到nginx等负载均衡器中。 您可以在不同的套接字上启动多个代理实例,并配置nginx上游。
有任何建议来改进我们的播放器吗? 欢迎发送评论或想法至streamedian.player@gmail.com