RTSP?不存在的 -> 前端实时流探索记

标签: 前端 nginx rtsp javascript | 发表时间:2020-06-22 13:10 | 作者:竹之同学
出处:https://segmentfault.com/blogs

作为一个从未接触过实时流(直播流)的人,我之前对实时视频一直没有概念,而最近参与的项目刚好有视频监控的需求,在参与技术选型之前,我对前端实时流的展示进行了一下摸底。

概览

视频有一个流的概念,所以称流媒体。实时视频的流很好理解,因为视频是实时的,需要有一个地方不停地输出视频出来,所以整个视频可以用流来称呼。那么视频可否直接输出到前端页面上呢?可惜,如果可以的话,就没有我这篇文章了。现在摄像头的实时视频流普遍采用的是 RTSP 协议,而前端并不能直接播放 RTSP 的视频流。

  • RTSP(Real-Time Stream Protocol),是 TCP/UDP 协议体系中的一个应用层协议,跟 HTTP 处在同一层。RTSP 在体系结构上位于 RTP 和RTCP 之上,它使用 TCP 或者 RTP 完成数据传输。RTSP 实时效果非常好,适合视频聊天、视频监控等方向。


那么我们就需要一层中间层,来将 RTSP 流转成前端可以支持的协议,这也引申出了目前实时流技术的几种方向:

  • RTSP -> RTMP
  • RTSP -> HLS
  • RTSP -> RTMP -> HTTP-FLV

image.png

RTMP

RTMP(Real Time Messaging Protocol)是属于 Adobe 的一套视频协议,这套方案需要专门的 RTMP 流媒体,并且如果想要在浏览器上播放,无法使用 HTML5 的 video 标签,只能使用 Flash 播放器。(通过使用 [email protected] 以下的版本可以做到用 video 标签进行播放,但仍然需要加载 Flash)。 它的实时性在几种方案中是最好的,但是由于只能使用 Flash 的方案,所以在移动端就直接 GG 了,在 PC 端也是 明日黄花
由于下面的两种方法也需要用到 RTMP,所以这里就展示一下 RTSP 流如何转换成 RTMP ,我们使用 ffmpeg+Nginx+nginx-rtmp-module 来做这件事:

  # 在 http 同一层配置 rtmp 协议的相关字段
rtmp {
    server {
          # 端口
        listen 1935;
            # 路径
        application test {
            # 开启实时流模式
            live on;
            record off;
        }
    }
}
  # bash 上执行 ffmpeg 把 rtsp 转成 rtmp,并推到 1935 这个端口上
ffmpeg -i "rtsp://xxx.xxx.xxx:xxx/1" -vcodec copy -acodec copy -f flv "rtmp://127.0.0.1:1935/live/"

这样我们就得到了一个 RTMP 的流,我们可以直接用 VLC 或者 IINA 来播放这个流。

HLS

HLS(HTTP Live Streaming)是苹果公司提出的基于 HTTP 协议的的流媒体网络传输协议,它的工作原理是把整个流分成一个个小的基于 HTTP 的文件来下载,每次只下载一些。HLS 具有跨平台性,支持 iOS/Android/浏览器,通用性强。但是它的实时性差:苹果官方建议是请求到3个片之后才开始播放。所以一般很少用 HLS 做为互联网直播的传输协议。假设列表里面的包含5个 ts 文件,每个 TS 文件包含5秒的视频内容,那么整体的延迟就是25秒。苹果官方推荐的小文件时长是 10s,所以这样就会有30s(n x 10)的延迟。
下面是 HLS 实时流的整个链路:
image.png
从图中可以看出来我们需要一个服务端作为编码器和流分割器,接受流并不断输出成流片段(stream),然后前端再通过一个索引文件,去访问这些流片段。那么我们同样可以使用 nginx+ffmpeg 来做这件事情。

  # 在 rtmp 的 server 下开启 hls
# 作为上图中的 Server,负责流的处理
application hls{
        live on;
        hls on;
         hls_path xxx/; #保存 hls 文件的文件夹
        hls_fragment 10s;
}
  # 在 http 的 server 中添加 HLS 的配置:
# 作为上图中的 Distribution,负责分片文件和索引文件的输出
location /hls {
      # 提供 HLS 片段,声明类型
      types {
        application/vnd.apple.mpegurl m3u8;
        video/mp2t ts;
      }
      root /Users/mark/Desktop/hls; #访问切片文件保存的文件夹
      # Cache-Controll no-cache;
      expires -1;
}

然后同样使用 ffmpeg 推流到 hls 路径上:

  ffmpeg -i "rtsp://xxx.xxx.xxx:xxx/1" -vcodec copy -acodec copy -f flv rtmp://127.0.0.1:1935/hls

这个时候可以看到文件夹里已经有许多流文件存在,且不停地更新:
image.png
然后我们可以使用 video.js+video.js-contrib-hls 来播放这个视频:

  <html>
<head>
<title>video</title>
<!-- 引入css -->
<link href="https://unpkg.com/video.js/dist/video-js.min.css" rel="stylesheet">


</head>
<body>
<div class="videoBox">
    <video id="video" class="video-js vjs-default-skin" controls>
        <source src="http://localhost:8080/hls/test.m3u8" type="application/x-mpegURL"> 
    </video>
</div>

</body>
</html>
<script src="https://unpkg.com/video.js/dist/video.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/videojs-contrib-hls/5.15.0/videojs-contrib-hls.min.js"></script>
<script>
videojs.options.flash.swf = "./videojs/video-js.swf"
        videojs('video', {"autoplay":true}).play();
</script>

在我的测试下,HLS 的延迟在10-20秒左右,我们可以通过调整切片的大小来减少延迟,但是由于架构的限制,延迟是一个不可忽视的问题。

HTTP-FLV

接下来就是重头戏 HTTP-FLV 了,它集合了 HLS 的通用性和 RTMP 的实时性,可以做到在浏览器上用 HTML5 的 video 标签,以较低的延时播放实时流。HTTP-FLV 依靠 MIME 的特性,根据协议中的 Content-Type 来选择相应的程序去处理相应的内容,使得流媒体可以通过 HTTP 传输。除此之外,它可以通过 HTTP 302 跳转灵活调度/负载均衡,支持使用 HTTPS 加密传输,也能够兼容支持 Android,iOS 等移动端。 HTTP-FLV 本质上是将流转成 HTTP 协议下的 flv 文件,在 Nginx 上我们可以使用 nginx-http-flv-module 来将 RTMP 流转成 HTTP 流。

其实 flv 格式依然是 Adobe 家的格式,原生 Video 标签无法直接播放,但是好在我们有 bilibili 家的 flv.js,它可以将 FLV 文件流转码复用成 ISO BMFF(MP4 碎片)片段,然后通过 Media Source Extensions 将 MP4 片段喂进浏览器。


在支持浏览器的协议里,延迟排序是这样的: RTMP = HTTP-FLV = WebSocket-FLV < HLS
而性能排序是这样的: RTMP > HTTP-FLV = WebSocket-FLV > HLS


说了这么多,不如直接上手看看吧:

  1. 首先我们需要一个新的 nginx 插件: nginx-http-flv-module
  2. 在 nginx.conf 中进行一些新的配置:
  # rtmp server
application myvideo {
      live on;
      gop_cache: on; #减少首屏等待时间
}

# http server
location /live {
      flv_live on;
}
  1. 依然用 ffmpeg 来推流,使用上面 RTMP 的命令
  2. 前端 import flv.js,然后使用它来播放
  // 前端使用 flv.js,开启实时模式,然后访问这个 nginx 地址下的路径即可
import flvJs from 'flv.js';

export function playVideo(elementId, src) {
  const videoElement = document.getElementById(elementId);
  const flvPlayer = flvJs.createPlayer({
    isLive: true,
    type: 'flv',
    url: src,
  });
  flvPlayer.attachMediaElement(videoElement);
  flvPlayer.load();
}

playVideo('#video', 'http://localhost:8080/live?port=1985&app=myvideo&stream=streamname')

可以看到 flv.js 使用了 video/x-flv 这个 MIME 返回数据。
image.png
如果对延迟有更高的要求,可以尝试下面的操作:

  1. 可以配置 flv.js 的 enableStashBuffer 字段,它是 flv.js 用于控制缓存 buffer 的开关,关闭了之后可以做到最小延迟,但由于没有缓存,可能会看到网络抖动带来的视频卡顿。
  2. 可以尝试关闭 nginx 的 http 配置里的 gop_cache 。 gop_cache 又称关键帧缓存,其意义是控制视频的关键帧之间的缓存是否开启。

这里引入了一个关键帧的概念:我们使用最广泛的 H.264 视频压缩格式,它采用了诸如帧内预测压缩/帧间预测压缩等压缩方案,最后得到了 BPI 三种帧:

  • I 帧:关键帧,采用帧内压缩技术。
  • P 帧:向前参考帧,在压缩时,只参考前面已经处理的帧,表示的是当前帧画面与前一帧(前一帧可能是 I 帧也可能是 P 帧)的差别。采用帧间压缩技术。
  • B 帧:双向参考帧,在压缩时,它即参考前面的帧,又参考它后面的帧。B 帧记录的是本帧与前后帧的差别。采用帧间压缩技术。

带有 I 帧、B 帧和 P 帧的典型视频序列。P 帧只需要参考前面的 I 帧或 P 帧,而 B 帧则需要同时参考前面和后面的 I 帧或 P 帧。由于 P/B 帧对于 I 帧都有直接或者间接的依赖关系,所以播放器要解码一个视频帧序列,并进行播放,必须首先解码出 I 帧。假设 GOP(就是视频流中两个I帧的时间距离) 是 10 秒,也就是每隔 10 秒才有关键帧,如果用户在第 5 秒时开始播放,就无法拿到当前的关键帧了。这个时候 gop_cache 就起作用了: gop_cache 可以控制是否缓存最近的一个关键帧。开启 gop_cache 可以让客户端开始播放时,立即收到一个关键帧,显示出画面,当然,由于增加了对上一个帧的缓存,所以延时自然就变大了。如果对延时有更高的要求,而对于首屏时间/播放流畅度的要求没那么高的话,那么可以尝试关闭 gop_cache,来达到低延时的效果。

思考

延迟与卡顿

实时视频的延时与卡顿是视频质量中最重要的两项指标。 然而,这两项指标从理论上来说,是一对矛盾的关系——需要更低的延时,则表明服务器端和播放端的缓冲区都必须更短,来自网络的异常抖动容易引起卡顿;业务可以接受较高的延时时,服务端和播放端都可以有较长的缓冲区,以应对来自网络的抖动,提供更流畅的体验。

直播厂商是怎么做的?

现在各个直播平台基本上都放弃了以上这些比较传统的方式,使用了云服务商提供的 CDN,但还是离不开前文所说的几种协议与方式。如下图是阿里云的直播服务图。可以看到其流程大概分为这几步:

  1. 采集视频流(主播端使用 RTMP 进行推流)
  2. 推流到 CDN 节点(上传流)
  3. CDN 节点转到直播中心,直播中心类似于强大的具有计算能力的中间源,可以提供额外服务诸如落存(录制/录制到云存储/点播),转码,审核,多种协议的输出等。
  4. 直播中间分发到 CDN 节点
  5. 播放(阿里云支持 RTMP、FLV 及 HLS 三种播流协议)

image.png

PS:如果你已经看到这儿了,觉得我写得还行的话,麻烦给个赞,谢谢!

相关 [rtsp 存在 前端] 推荐:

RTSP?不存在的 -> 前端实时流探索记

- - SegmentFault 最新的文章
作为一个从未接触过实时流(直播流)的人,我之前对实时视频一直没有概念,而最近参与的项目刚好有视频监控的需求,在参与技术选型之前,我对前端实时流的展示进行了一下摸底. 视频有一个流的概念,所以称流媒体. 实时视频的流很好理解,因为视频是实时的,需要有一个地方不停地输出视频出来,所以整个视频可以用流来称呼.

RTSP协议分析

- - CSDN博客推荐文章
        RTSP(Real Time Streaming Protocol)实时流传输协议,是TCP/IP协议体系中的一个基于文本的应用层协议,由哥伦比亚大学、网景和RealNetworks公司提交的IETF RFC2326标准. 该协议定义了一对多应用程序如何有效地通过IP网络传送多媒体数据.

RTSP录像的几种方式

- - 博客园_首页
这里介绍几种保存RTSP视频流的几种方式,欢迎补充. 首先介绍下环境:操作系统是64位的CentOS 6.2 ,服务器采用的是Darwin Streaming Server 6.0.3 ,摄像头数据源用FFMPEG 0.6进行采集. 下面我以H.264编码为例进行介绍,其他编码格式读者参考相关文档进行扩展.

海康RTSP客户端连接分析

- - CSDN博客互联网推荐文章
海康相机RTSP连接代码分析. 最近在做海康相机rtsp连接获取音视频的工作,现在介绍一下分析过程和源码. 【源码在我上传的共享资料中: http://download.csdn.net/detail/zhouyongku/8203521】.  RTSP客户端去连接服务器的最基本步骤如下:   .

[转]RTP /RTSP /RTCP 介绍 以及 它们的区别

- - 小鸥的博客
RTP:实时传输协议(Real Time Transport Protocol) . 实时传输协议(RTP)为数据提供了具有实时特征的端对端传送服务,如在组播或单播网络服务下的交互式视频音频或模拟数据. 应用程序通常在 UDP 上运行 RTP 以便使用其多路结点和校验服务;这两种协议都提供了传输层协议的功能.

流媒体协议介绍(rtp/rtcp/rtsp/rtmp/mms/hls)

- - CSDN博客互联网推荐文章
          参考文档  RFC3550/RFC3551.          Real-time Transport Protocol)是用于Internet上针对多媒体数据流的一种传输层协议. RTP协议详细说明了在互联网上传递音频和视频的标准数据包格式. RTP协议常用于流媒体系统(配合RTCP协议),视频会议和一键通(Push to Talk)系统(配合H.323或SIP),使它成为IP电话产业的技术基础.

前端技术

- - CSDN博客综合推荐文章
随着互联网产业的爆炸式增长,与之伴生的Web前端技术也在历经洗礼和蜕变. 尤其是近几年随着移动终端的发展,越来越多的人开始投身或转行至新领域,这更为当今的IT产业注入了新的活力. 尽管Web前端技术诞生至今时日并不长,但随着Web技术的逐渐深入,今后将会在以下几方面发力. JavaScript的兄弟们.

Web前端优化

- - JavaScript - Web前端 - ITeye博客
优点:直接使用浏览器内存的缓存数据,减少网站后台压力,用户体验(速度)好. 缺点:对于时时变化的动态页面,这种情况就不能容忍了,因为每次访问的都是第一次访问的内容,这样即使所请求的页面已经变化了,用户也不可能知道,所以此场景必须要消除这种缓存的影响. 延迟加载,将资源延迟到需要的时候的加载,例如detail页面,相关产品推荐,当用户浏览更多的信息往下拉动滚动时,才进行加载,异步加载可以大幅减少对后端资源的使用,在需要的时候加载,是资源合理使用常用的方式,但是也带来一个问题,当往下拉才去加载,如果性能不够好,用户的体验其实是不好的,“菊花”转动的时间会比较长,同时异步加载对前端性能的作用也是非常明显的,渲染的节点数量大幅减少.

Web 前端测试

- - Web前端 - ITeye博客
Web 网站测试流程和方法(转载). 进行正式测试之前,应先确定如何开展测试,不可盲目的测试. 一般网站的测试,应按以下流程来进行:. 1)使用HTML Link Validator将网站中的错误链接找出来;. 2)测试的顺序为:自顶向下、从左到右;. 3)查看页面title是否正确. (不只首页,所有页面都要查看);.

前端xss攻击

- - SegmentFault 最新的文章
实习的时候在项目中有接触过关于xss攻击的内容,并且使用了项目组中推荐的一些常用的防xss攻击的方法对项目进行了防攻击的完善. 但一直没有时间深入了解这东西,在此,做一个简单的梳理. xss跨站脚本攻击(Cross Site Scripting),是一种经常出现在web应用中的计算机安全漏洞,它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入的恶意html代码会被执行,从而达到恶意用户的特殊目的.