前端简单实现视频监控直播技术

3,462 阅读7分钟

前提

  • 监控或者直播的大概流程就是需要一个流媒体服务器,再通过流媒体的传输协议,进行推流和拉流实现效果
  • 本次尝试源自于公司的车载视频监控的需求

image.png

常见的直播协议

  • 文章开始之前首先介绍一下几种常见的直播协议
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 配置
      参数类型效果
      typestring表示媒体类型,'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 结构中的 durationfilesizeurl 字段将被忽略。
    • segments 配置
      参数类型效果
      durationnumber必填字段,以毫秒为单位表示段持续时间
      filesize?number可选字段,以字节为单位表示段文件大小
      urlstring必填字段,表示段文件 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 服务器搭建的文章,有兴趣的朋友们可以关注一波