徐立,七牛创始合伙人兼产品副总裁,负责七牛直播云的总体研发,是国内 Go / Docker / Container 技术早期布道者,Go / Containers / Distributed Systems 技术的忠实爱好者和实践者。曾合著国内第一本 Go 语言图书《Go 语言编程》,翻译《Go 语言程序设计》。nginx
现今移动直播技术上的挑战要远远难于传统设备或电脑直播,其完整的处理环节包括但不限于:音视频采集、美颜/滤镜/特效处理、编码、封包、推流、转码、分发、解码/渲染/播放等。算法
直播常见的问题包括编程
-
主播在不稳定的网络环境下如何稳定推流?缓存
-
偏远地区的观众如何高清流畅观看直播?性能优化
-
直播卡顿时如何智能切换线路?服务器
-
如何精确度量直播质量指标并实时调整?微信
-
移动设备上不一样的芯片平台如何高性能编码和渲染视频?网络
-
美颜等滤镜特效处理怎么作?多线程
-
如何实现播放秒开?架构
-
如何保障直播持续播放流畅不卡顿?
本次分享将为你们揭开移动直播核心技术的神秘面纱。
视频、直播等基础知识
什么是视频?
首先咱们须要理解一个最基本的概念:视频。从感性的角度来看,视频就是一部充满趣味的影片,能够是电影,能够是短片,是一连贯的视觉冲击力表现丰富的画面和音频。但从理性的角度来看,视频是一种有结构的数据,用工程的语言解释,咱们能够把视频剖析成以下结构:
内容元素 ( Content )
-
图像 ( Image )
-
音频 ( Audio )
-
元信息 ( Metadata )
编码格式 ( Codec )
-
Video : H.264,H.265, …
-
Audio : AAC, HE-AAC, …
容器封装 (Container)
-
MP4,MOV,FLV,RM,RMVB,AVI,…
任何一个视频 Video 文件,从结构上讲,都是这样一种组成方式:
-
由图像和音频构成最基本的内容元素;
-
图像通过视频编码压缩格式处理(一般是 H.264);
-
音频通过音频编码压缩格式处理(例如 AAC);
-
注明相应的元信息(Metadata);
最后通过一遍容器(Container)封装打包(例如 MP4),构成一个完整的视频文件。
若是以为难以理解,能够想象成一瓶番茄酱。最外层的瓶子比如这个容器封装(Container),瓶子上注明的原材料和加工厂地等信息比如元信息(Metadata),瓶盖打开(解封装)后,番茄酱自己比如通过压缩处理事后的编码内容,番茄和调料加工成番茄酱的过程就比如编码(Codec),而原材料番茄和调料则比如最本来的内容元素(Content)。
视频的实时传输
简而言之,理性的认知视频的结构后,有助于咱们理解视频直播。若是视频是一种“有结构的数据”,那么视频直播无疑是实时传输这种“有结构的数据”(视频)的方式。
那么一个显而易见的问题是:如何实时(Real-Time)传输这种“有结构的数据”(视频)呢?
这里边一个悖论是:一个通过容器(Container)封装后的视频,必定是不可变的 ( Immutable ) 视频文件,不可变的 ( Immutable ) 的视频文件已是一个生产结果,根据“相对论”,而这个生产结果显然不可能精确到实时的程度,它已是一段时空的记忆。
所以视频直播,必定是一个 “边生产,边传输,边消费”的过程。这意味着,咱们须要更近一步了解视频从原始的内容元素 ( 图像和音频 ) 到成品 ( 视频文件 ) 以前的中间过程 ( 编码 )。
视频编码压缩
不妨让咱们来深刻浅出理解视频编码压缩技术。
为了便于视频内容的存储和传输,一般须要减小视频内容的体积,也就是须要将原始的内容元素(图像和音频)通过压缩,压缩算法也简称编码格式。例如视频里边的原始图像数据会采用 H.264 编码格式进行压缩,音频采样数据会采用 AAC 编码格式进行压缩。
视频内容通过编码压缩后,确实有利于存储和传输; 不过当要观看播放时,相应地也须要解码过程。所以编码和解码之间,显然须要约定一种编码器和解码器均可以理解的约定。就视频图像编码和解码而言,这种约定很简单:
编码器将多张图像进行编码后生产成一段一段的 GOP ( Group of Pictures ) , 解码器在播放时则是读取一段一段的 GOP 进行解码后读取画面再渲染显示。
GOP ( Group of Pictures ) 是一组连续的画面,由一张 I 帧和数张 B / P 帧组成,是视频图像编码器和解码器存取的基本单位,它的排列顺序将会一直重复到影像结束。
I 帧是内部编码帧(也称为关键帧),P 帧是前向预测帧(前向参考帧),B 帧是双向内插帧(双向参考帧)。简单地讲,I 帧是一个完整的画面,而 P 帧和 B 帧记录的是相对于 I 帧的变化。
若是没有 I 帧,P 帧和 B 帧就没法解码。
小结一下,一个视频 ( Video ) ,其图像部分的数据是一组 GOP 的集合, 而单个 GOP 则是一组 I / P / B 帧图像的集合。
在这样的一种几何关系中,Video 比如一个 “物体”,GOP 比如 “分子”,I / P / B 帧的图像则比如 “原子”。
想象一下,若是咱们把传输一个 “物体”,改为传输一个一个的 “原子”,将最小颗粒以光速传送,那么以人的生物肉眼来感知,将是一种怎样的体验?
什么是视频直播?
不难脑洞大开一下,直播就是这样的一种体验。视频直播技术,就是将视频内容的最小颗粒 ( I / P / B 帧,…),基于时间序列,以光速进行传送的一种技术。
简而言之,直播就是将每一帧数据 ( Video / Audio / Data Frame ),打上时序标签 ( Timestamp ) 后进行流式传输的过程。发送端源源不断的采集音视频数据,通过编码、封包、推流,再通过中继分发网络进行扩散传播,播放端再源源不断地下载数据并按时序进行解码播放。如此就实现了 “边生产、边传输、边消费” 的直播过程。
理解以上两个关于 视频和直播两个基础概念后,接下来咱们就能够一窥直播的业务逻辑了。
直播的业务逻辑
以下是一个最精简的一对多直播业务模型,以及各个层级之间的协议。
各协议差别对好比下
以上就是关于直播技术的一些基础概念。下面咱们进一步了解下影响人们视觉体验的直播性能指标。
影响视觉体验的直播性能指标
直播第一个性能指标是延迟,延迟是数据从信息源发送到目的地所需的时间。
根据爱因斯坦的狭义相对论,光速是全部能量、物质和信息运动所能达到的最高速度,这个结论给传播速度设定了上限。所以,即使咱们肉眼感受到的实时,实际上也是有必定的延迟。
因为 RTMP/HLS 是基于 TCP 之上的应用层协议,TCP 三次握手,四次挥手,慢启动过程当中的每一次往返来回,都会加上一次往返耗时 ( RTT ),这些交互过程都会增长延迟。
其次根据 TCP 丢包重传特性,网络抖动可能致使丢包重传,也会间接致使延迟加大。
一个完整的直播过程,包括但不限于如下环节:采集、处理、编码、封包、推流、传输、转码、分发、拉流、解码、播放。从推流到播放,再通过中间转发环节,延迟越低,则用户体验越好。
第二个直播性能指标卡顿,是指视频播放过程当中出现画面滞帧,让人们明显感受到“卡”。单位时间内的播放卡顿次数统计称之为卡顿率。
形成卡顿的因素有多是推流端发送数据中断,也有多是公网传输拥塞或网络抖动异常,也有多是终端设备的解码性能太差。卡顿频次越少或没有,则说明用户体验越好。
第三个直播性能指标首屏耗时,指第一次点击播放后,肉眼看到画面所等待的时间。技术上指播放器解码第一帧渲染显示画面所花的耗时。一般说的 “秒开”,指点击播放后,一秒内便可看到播放画面。首屏打开越快,说明用户体验越好。
如上三个直播性能指标,分别对应一个低延迟、高清流畅、极速秒开 的用户体验诉求。了解这三个性能指标,对优化移动直播 APP 的用户体验相当重要。
那么移动直播场景下具体而言有哪些常见的坑呢?
根据实践总结下来的经验,移动平台上视频直播的坑主要能够总结为两方面:设备差别,以及网络环境这些场景下带来的技术考验。
移动直播场景的坑与规避措施
不一样芯片平台上的编码差别
iOS 平台上不管硬编仍是软编,因为是 Apple 一家公司出厂,几乎不存在由于芯片平台不一样而致使的编码差别。
然而,在 Android 平台上,Android Framework SDK 提供的 MediaCodec 编码器,在不一样的芯片平台上,差别表现很大, 不一样的厂家使用不一样的芯片,而不一样的芯片平台上 Android MediaCodec 表现略有差别,一般实现全平台兼容的成本不低。
另外就是 Android MediaCodec 硬编层面的 H.264 编码画质参数是固定的 baseline,因此画质一般也通常。所以,在 Android 平台下,推荐是用软编,好处是画质可调控,兼容性也更好。
低端设备如何上高性能地采集和编码?
例如 Camera 采集输出的多是图片,一张图的体积并不会小,若是采集的频次很高,编码的帧率很高,每张图都通过编码器,那么编码器又可能会出现过载。
这个时候,能够考虑在编码前,不影响画质的前提下(前面咱们讲过帧率的微观意义),进行选择性丢帧,以此下降编码环节的功耗开销。
弱网下如何保障高清流畅推流
移动网络下,一般容易遇到网络不稳定,链接被重置,断线重连,一方面频繁重连,创建链接须要开销。另外一方面尤为是发生 GPRS / 2G / 3G / 4G 切换时,带宽可能出现瓶颈。当带宽不够,帧率较高/码率较高的内容较难发送出去,这个时候就须要可变码率支持。
即在推流端,可检测网络状态和简单测速,动态来切换码率,以保障网络切换时的推流流畅。
其次编码、封包、推流 这一部分的逻辑也能够作微调,能够尝试选择性丢帧,好比优先丢视频参考帧(不丢 I 帧和音频帧 ),这样也能够减小要传输的数据内容,但同时又达到了不影响画质和版视听流畅的目的。
须要区分直播流的状态和业务状态
直播是媒体流、APP 的交互是 API 信令流,二者的状态不能混为一谈。尤为是不能基于 APP 的交互的 API 状态来判断直播流的状态。
以上是移动直播场景下常见的几个坑和规避措施。
移动直播场景其余优化措施
1、怎么优化打开速度,达到传说中的 “秒开”?
你们可能会看到,市面上某些手机直播 APP 的打开速度很是快,一点就开。而某些手机直播 APP,点击播放后要等好几秒之后才能播放。是什么缘由致使如此的天壤之别呢?
大部分播放器都是拿到一个完成的 GOP 后才能解码播放,基于 FFmpeg 移植的播放器甚至须要等待音画时间戳同步后才能播放(若是一个直播里边没有音频只有视频至关于要等待音频超时后才能播放画面)。
“秒开”能够从如下几个方面考虑:
1. 改写播放器逻辑让播放器拿到第一个关键帧后就给予显示。
GOP 的第一帧一般都是关键帧,因为加载的数据较少,能够达到 “首帧秒开”。
若是直播服务器支持 GOP 缓存,意味着播放器在和服务器创建链接后可当即拿到数据,从而省却跨地域和跨运营商的回源传输时间。
GOP 体现了关键帧的周期,也就是两个关键帧之间的距离,即一个帧组的最大帧数。假设一个视频的恒定帧率是 24fps(即1秒24帧图像),关键帧周期为 2s,那么一个 GOP 就是 48 张图像。通常而言,每一秒视频至少须要使用一个关键帧。
增长关键帧个数可改善画质(GOP 一般为 FPS 的倍数),可是同时增长了带宽和网络负载。这意味着,客户端播放器下载一个 GOP,毕竟该 GOP 存在必定的数据体积,若是播放端网络不佳,有可能不是可以快速在秒级之内下载完该 GOP,进而影响观感体验。
若是不能更改播放器行为逻辑为首帧秒开,直播服务器也能够作一些取巧处理,好比从缓存 GOP 改为缓存双关键帧(减小图像数量),这样能够极大程度地减小播放器加载 GOP 要传输的内容体积。
2. 在 APP 业务逻辑层面方面优化。
好比提早作好 DNS 解析(省却几十毫秒),和提早作好测速选线(择取最优线路)。通过这样的预处理后,在点击播放按钮时,将极大提升下载性能。
一方面,能够围绕传输层面作性能优化;另外一方面,能够围绕客户播放行为作业务逻辑优化。二者能够有效的互为补充,做为秒开的优化空间。
2、美颜等滤镜如何处理?
在手机直播场景下,这就是一个刚需。没有美颜功能的手机直播 APP,主播基本不爱用。能够在采集画面后,将数据送给编码器以前,将数据源回调给滤镜处理程序,原始数据通过滤镜处理完后,再送回给编码器进行编码便可。
除了移动端能够作体验优化以外,直播流媒体服务端架构也能够下降延迟。例如收流服务器主动推送 GOP 至边缘节点,边缘节点缓存 GOP,播放端则能够快速加载,减小回源延迟。
其次,能够贴近终端就近处理和分发
3、如何保障直播持续播放流畅不卡顿?
“秒开”解决的是直播首次加载的播放体验,如何保障直播持续播放过程当中的画面和声音视听流畅呢?由于,一个直播毕竟不是一个 HTTP 同样的一次性请求,而是一个 Socket 层面的长链接维持,直到直到主播主动终止推流。
上述咱们讲过卡顿的定义:即播放时画面滞帧,触发了人们的视觉感觉。在不考虑终端设备性能差别的状况下,针对网络传输层面的缘由,咱们看看如何保障一个持续的直播不卡顿。
这实际上是一个直播过程当中传输网络不可靠时的容错问题。例如,播放端临时断网了,但又快速恢复了,针对这种场景,播放端若是不作容错处理,很难不出现黑屏或是从新加载播放的现象。
为了容忍这种网络错误,并达到让终端用户无感知,客户端播放器能够考虑构建一个FIFO(先进先出)的缓冲队列,解码器从播放缓存队列读取数据,缓存队列从直播服务器源源不断的下载数据。一般,缓存队列的容量是以时间为单位(好比3s),在播放端网络不可靠时,客户端缓存区能够起到“断网无感”的过渡做用。
显然,这只是一个“缓兵之计”,若是直播服务器边缘节点出现故障,而此时客户端播放器又是长链接,在没法收到对端的链接断开信号,客户端的缓冲区容量再大也无论用了,这个时候就须要结合客户端业务逻辑来作调度。
重要的是客户端结合服务端,能够作精准调度。在初始化直播推流以前,例如基于 IP 地理位置和运营商的精确调度,分配线路质量最优的边缘接入节点。在直播推流的过程当中,能够实时监测帧率反馈等质量数据,基于直播流的质量动态调整线路。
Q & A
1. 关键帧设置频率通常是多少?有没有根据接入动态设置?过长首屏秒会很难作到。
徐立:关键帧间隔越长,也就是 GOP 越长,理论上画面越高清。可是生成 HLS 直播时,最小切割粒度也是一个 GOP,因此针对交互直播,一般不建议 GOP 设置太长。直播通常 2 个关键帧间隔便可。好比帧率是 24fps, 那么 2 个关键帧的间隔就是 48fps ,这个 GOP 就是2s。
2. 七牛这个直播是用的网宿加速?有遇到什么坑没?
徐立:七牛在直播方面主要是自建节点,也支持融合众多第三方 CDN 服务商,多样化的线路组合为客户提供更优质的服务。在和第三方 CDN 合做的过程当中遇到的问题等有机会再作更细粒度的交流和分享。
3. RTMP 直播流除了优化线路外,还有什么加速手段吗?
徐立:物理上优化线路,逻辑上优化策略,好比选择性丢帧,不影响编码画质的前提下减轻传输体积。
4. OBS 推流,播放端 HLS 出现视/音频不一样步是哪一个环节的问题?怎么优化?
徐立:有多是采集端的问题,若是是采集端编码环节就出现音画不一样步,能够在收流服务器上作音画时间戳同步,这样是全局的校对。若是是播放端解码性能问题,那么须要调节播放逻辑,好比保证音画时间戳强一致性的前提下,选择性丢一部帧。
5. PPT 前几页中一个概念好像错了,I 帧不是关键帧,IDR 帧才是。IDR 帧是 I 帧,可是 I 帧不必定是 IDR 帧。只有 IDR 帧才是可重入的。
徐立:中文都把 I 帧翻译成关键帧了,不过既然提到了 IDR 帧,能够展开说明一下。全部的 IDR 帧都是 I 帧,可是并非全部 I 帧都是 IDR 帧,IDR 帧是 I 帧的子集。I 帧严格定义是帧内编码帧,因为是一个全帧压缩编码帧,一般用 I 帧表示 “关键帧”。IDR 是基于 I 帧的一个 “扩展”,带了控制逻辑,IDR 图像都是 I 帧图像,当解码器解码到 IDR 图像时,会当即将参考帧队列清空,将已解码的数据所有输出或抛弃。从新查找参数集,开始一个新的序列。这样若是前一个序列出现重大错误,在这里能够得到从新同步的机会。IDR 图像以后的图像永远不会使用 IDR 以前的图像的数据来解码。
6. 有没有调研过 nginx rtmp module,为何没有用,对它有什么评价?
徐立:有调研过,nginx_rtmp_module 是单进程多线程,非 go 这种轻量级线程/协程用并发天然语义的方式编写流业务。nginx 本来的代码量较大(约 16 万行,但和直播业务相关的功能并非不少)。且主要靠写 nginx.conf 作配置租户,一般单租户能够,但业务可扩展性方面不是很灵活,可知足基本需求,不知足高级功能。
7. 用到了那些开源软件?编码用的是 x264 吗?直播服务器大家本身开发仍是开源的?
徐立:直播服务器用 go 开发的,移动端编码优先硬编,软编用 x264
8. 请教一下用 OBS 推流到 nginx_rtmp_module 的时候是已经作了视频压缩了仍是须要基于 OBS 再开发?
徐立:OBS 把编码压缩都作了,不须要再开发。
9. 视频直播想在 HLS 流中无缝插入一段广告的 ts 文件,有问题想请教一下:一、这段 ts 的分辨率是否必定要和以前的视频流一致?二、pts 时间戳是否要和上一个 ts 递增?
徐立:一、能够不一致。这种状况两段视频彻底是独立状态,能够没有任何关系,只须要插入 discontinue 标记,播放器在识别到这个标记以后重置解码器参数就能够无缝播放,画面会很平滑的切换。二、不须要递增。举个例子,视频 A 正在直播,播放到 pts 在 5s 的时候,插入一个视频 B,须要先插入一个 discontinue,再插入 B,等 B 播放完以后,再插入一个 discontinue,再插入 A,这个时候 A 的 pts 能够和以前递增,也能够按照中间插入的 B 的时长作偏移,通常作点播和时移的时候 pts 会连续递增,直播的话会算上 B 的时长。
因为移动直播在实践上还有很是多细节,本文未能所有覆盖,感兴趣的朋友欢迎在文章最后留言讨论。
最后欢迎读者将「高可用架构」在订阅号置顶,更方便浏览高可用架构全部文章。
本文策划 Tim、刘芸,主持人丁一琼,编辑王杰,更多直播架构请关注公众号。转载请注明来自高可用架构「ArchNotes」微信公众号及包含如下二维码。
高可用架构
改变互联网的构建方式
长按二维码 关注「高可用架构」公众号