前提
- 监控或者直播的大概流程就是需要一个流媒体服务器,再通过流媒体的传输协议,进行推流和拉流实现效果
- 本次尝试源自于公司的车载视频监控的需求
常见的直播协议
- 文章开始之前首先介绍一下几种常见的直播协议
HTTP-FLV 协议
- FLV(Flash Video)是一种流媒体格式
- 直接发起长链接,下载对应的 FLV 文件
- 基于 HTTP 流式 IO 传输 FLV,依赖浏览器支持播放 FLV
- 在手机浏览器上的支持非常有限,但是用作手机端 APP 直播协议却异常合适
HLS 协议
- HLS(HTTP Live Streaming)苹果提出的流媒体播放协议
- 将视频分成 5-10 秒的视频小分片,然后用 m3u8 索引表进行管理
- 由于客户端下载到的视频都是 5-10 秒的完整数据,故视频的流畅性很好,但也同样引入了很大的延迟(HLS 的一般延迟在 10-30s 左右)
- 相比于 FLV,HLS 在 iPhone 和大部分 android 手机浏览器上的支持非常给力
- HLS 协议客户端支持简单, 只需要支持 HTTP 请求即可, HTTP 协议无状态, 只需要按顺序下载媒体片段即可,而且网络兼容性好, HTTP 数据包也可以方便地通过防火墙或者代理服务器
- 相比 RTMP 这类长连接协议, 用到互动直播场景延时较高
- HTML5 可以直接打开播放
- 对于视频播放,可以使用 HLS 协议播放直播流,iOS 和 Android 都天然支持这种协议,配置简单,直接使用 video 标签即可
RTP 协议
- RTP(Real-time Transport Protocol)实时传输协议
- 默认基于 UDP 协议传输数据,延迟可以低到毫秒
- 实行有序传送,RTP 中的序列号允许接收方重组发送方的包序列,同时序列号也能用于决定适当的包位置
- 应用于流媒体相关的通讯和娱乐,包括电话、视频会议、电视和基于网络的一键通业务(类似对讲机的通话)
RTCP 协议
- RTCP(RTCP:RTP Control Protocol)RTP 控制协议
- RTCP 主要功能是为 RTP 所提供的服务质量提供反馈,收集相关媒体连接的统计信息
- RTCP 是 RTP 的配套协议,是和 RTP 一起协作将多媒体数据包打包和发送,定期在多媒体流会话参与者之间传输控制数据
RTMP 协议
- RTMP(Real Time Messaging Protocol)实时消息传输协议,用来进行实时数据通信
- 一般传输的音频格式是 FLV、F4V,web 上通过 flash 播放器支持,移动端基本不支持
- 主要用来在 Flash/AIR 平台和支持 RTMP 协议的流媒体/交互服务器之间进行音视频和数据通信
RTSP 协议
- RTSP(Real Time Streaming Protocol)实时流传输协议
- 用于控制声音或影像多个数据的连接,服务器端可以自行选择使用 TCP 或 UDP 来传送串流内容
- 不特别强调时间同步,所以可能会网络延迟,但是 RTSP 具有重新导向功能,可以根据实际负载情况来切换提供服务的服务器,因此可在一定程度上可避免服务器承载过大而造成的延迟
视频监控使用的什么技术
-
需要支持的环境
- 项目为 pc 端项目,只需要支持谷歌浏览器
-
使用 HTTP-FLV 协议
-
使用 html5 播放器(仅支持播放 mp4/webm 格式)
-
使用 b 站开源的 Flv.js
Flv.js
git 地址
https://github.com/Bilibili/flv.js/
- 我在 README.md 看到,大意就是 flv.js 不维护了,请移步 mpegts.js
For FLV live stream playback, please consider mpegts.js which is under active development. This project will become rarely maintained.
工作原理
- flv.js 的工作原理是将 FLV 文件流转换为 ISO BMFF(分段 MP4)片段,然后通过媒体源扩展 API(Media Source Extensions API) 将 mp4 片段提供给 HTML5 video 元素
- ES6 编写的,通过 Babel Compiler 编译成 ES5,使用 Browserify 打包
- 也就是说只要是支持 Media Source Extensions 和 ECMAScript 5 的浏览器都是兼容 flv.js 的
部分 api 介绍
- flvjs.createPlayer()
function createPlayer(mediaDataSource: MediaDataSource, config?: Config): Player;- 根据 mediaDataSource 中指示的 type 字段创建播放器实例,可选配置
- mediaDataSource 配置
参数 类型 效果 type string 表示媒体类型,'flv' 或 'mp4' isLive? boolean 数据源是否为直播流 cors? boolean 是否为 http 获取启用 CORS withCredentials? boolean 是否为 http 获取启用 CORS hasAudio? boolean 是否使用 cookie 进行 http 获取 hasVideo? boolean 流是否有音轨 duration? boolean 总媒体持续时间,以毫秒为单位 filesize? boolean 媒体文件的总文件大小,以字节为单位 url? string 表示媒体 URL,可以以 'https(s)' 或 'ws(s)' (WebSocket) 开头 segments? Array 多部分播放的可选字段,看下面的 MediaSegment - 如果存在 segments 字段,transmuxer 会将此 MediaDataSource 视为多部分源,在此模式下,MediaDataSource 结构中的
duration、filesize、url字段将被忽略。 - segments 配置
参数 类型 效果 duration number 必填字段,以毫秒为单位表示段持续时间 filesize? number 可选字段,以字节为单位表示段文件大小 url string 必填字段,表示段文件 URL - Config 配置:这边的配置都是有默认值的(可以前往文档自行查看:github.com/bilibili/fl…
- flvjs.isSupported() 返回 boolean,判断浏览器是否支持
- interface Player (abstract) 创建的 player 实例方法,比如 play、load、unload、pause 等
interface Player { constructor(mediaDataSource: MediaDataSource, config?: Config): Player; destroy(): void; on(event: string, listener: Function): void; off(event: string, listener: Function): void; attachMediaElement(mediaElement: HTMLMediaElement): void; detachMediaElement(): void; load(): void; unload(): void; play(): Promise<void>; pause(): void; type: string; buffered: TimeRanges; duration: number; volume: number; muted: boolean; currentTime: number; mediaInfo: Object; statisticsInfo: Object; } - 其他更多的 api 可以去看文档(github.com/bilibili/fl…
Media Source Extensions API 介绍
- 媒体源扩展 API(MSE) 提供了实现无插件且基于 Web 的流媒体的功能。使用 MSE,媒体串流能够通过 JavaScript 创建,并且能通过使用 audio 和 video 元素进行播放
WebSocket
- 这边大概讲解一下它的特点,监控系列的下一篇文章将会详细的阐述一下 webSocket
- 它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于
服务器推送技术的一种 - 建立在 TCP 协议之上,服务器端的实现比较容易
- 与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器
- 数据格式比较轻量,性能开销小,通信高效
- 可以发送文本,也可以发送二进制数据
- 没有同源限制,客户端可以与任意服务器通信
- 协议标识符是 ws(如果加密,则为 wss),服务器网址就是 URL
EventSource
概念介绍
- 是服务器推送的一个网络事件接口。一个 EventSource 实例会对 HTTP 服务开启一个持久化的连接,以 text/event-stream 格式发送事件, 会一直保持开启直到被要求关闭
- 与 WebSockets,不同的是,服务端推送是单向的。数据信息被单向从服务端到客户端分发. 当不需要以消息形式将数据从客户端发送到服务器时,这使它们成为绝佳的选择
- 一个客户端去从服务器端订阅一条“流”,之后服务端可以发送消息给客户端直到服务端或者客户端关闭该“流”
部分 api 介绍
-
构造函数 new EventSource() 创建实例
-
事件监听
-
错误事件监听 onerror
-
接收到消息时候的监听 onmessage
-
连接刚刚打开时候的监听 onopen
-
也可以用 addEventListener 来监听事件
-
示例,其实就是正常的事件监听
let evtSource = new EventSource('sse.php'); let eventList = document.querySelector('ul'); evtSource.onmessage = function(e) { let newElement = document.createElement("li"); newElement.textContent = "message: " + e.data; eventList.appendChild(newElement); } -
方法 EventSource.close()
- 如果存在,则关闭连接,并且设置 readyState 属性为 CLOSED。如果连接已经被关闭,此方法不会再进行任何操作。
-
前端模拟代码
<video controls>
Your browser is too old which doesn't support HTML5 video.
</video>
注意引入一下 flv.js
const video = document.querySelectorAll("video")[0];
let player = null;
const url = ""; // 设备流地址
const mediaDataSource = {
cors: true,
hasAudio: true,
hasVideo: true,
isLive: true,
type: "flv",
url: `ws://localhost:8888/${url}`, // websocket服务地址 + 设备流地址(注意:这边也可以使用上面提到的EventSource)
withCredentials: false,
};
// 创建实例
if (flvjs.isSupported) {
let player = flvjs.createPlayer(mediaDataSource, {
enableWorker: false,
lazyLoadMaxDuration: 3 * 60,
seekType: "range",
});
if (video) {
player.attachMediaElement(video); // 挂载
}
player.load();
player.play();
} else {
console.log('Your browser is not support flv.js');
}
后续必读
- 后面会更新 node + webSocket 服务器搭建的文章,有兴趣的朋友们可以关注一波