现场的第一场现场直播大约在70年前在俄罗斯出现,并通过移动电视台(PTS)进行播放,该移动电视台看起来像无轨电车,可以从演播室外面进行广播。 仅在三年前,潜望镜允许使用手机代替“无轨电车”。
但是此应用程序存在许多问题,例如与广播延迟,无法观看高质量的广播等有关。

六个月后,即2016年夏天,Odnoklassniki推出了他们的OK Live流媒体移动应用程序,他们试图在其中解决这些问题。
Alexander Tobol负责Odnoklassniki中视频的技术部分,在Highload ++ 2017上,他谈到了如何编写UDP协议以及为什么需要这样做。
从他的报告的笔录中,您将了解有关其他视频流协议的所有信息,细微差别以及有时需要哪些技巧。
他们说我们应该始终从体系结构和传统知识开始-据说没有这一切是不可能的! 因此,让我们开始吧。
建筑与传统知识
在下面的幻灯片上,是任何流服务的体系结构图:
视频被馈送到输入,转换并传输到输出 。 我们对该架构增加了一些要求:视频应从台式机和手机提供,输出应发送到相同的台式机,手机,smartTV,Chromcast,AppleTV和其他设备-所有视频都可以播放。

接下来,我们继续研究职权范围。 如果您有客户,那么您就有TK。 如果您是社交网络,则没有TK。 如何弥补?
您当然可以采访用户并找出他们想要的一切。 但这将是一堆与人们真正需要无关的欲望。
我们决定采取相反的方式,看到用户不想从广播服务中看到的内容。
- 用户不希望的第一件事是在广播开始时看到延迟 。
- 用户不想看到低质量的流图像 。
- 如果当用户与听众交流(即将进行的直播,通话等)时广播是交互式的,那么他就不想看到流媒体和观众之间的延迟 。
这就是普通的流服务。 让我们看看如何做常规的流服务而不是通常的流服务。

您可以先查看所有流协议,然后选择最有趣的协议并进行比较。 但是我们做了不同的事情。
竞争对手有什么?
我们从探索竞争对手的服务开始。
打开潜望镜-它们有什么?与往常一样,最主要的是建筑。

潜望镜的首席工程师Sara Hyder写道,他们使用Wowza作为后端。 如果阅读更多文章,那么我们将看到它们使用
RTMP协议流式传输的内容,并将其分发到RTMP或HLS中。 让我们看看这些协议是什么以及它们如何工作。
我们在三个主要要求上测试了Periscope。

它们具有可接受的
启动速度 (在良好的网络上不到1秒),大约600 px(不是HD)的恒定
质量 ,并且同时
延迟可能长达12秒 。
顺便说一下,如何测量广播的延迟?

这是延迟测量的照片。 有带计时器的手机。 我们打开广播,然后在屏幕上看到此手机的图像。 在0.15毫秒内,图像掉落在相机的传感器上,并从视频存储器移至手机屏幕。 之后,我们打开浏览器并观看广播。
! 她有点落后-大约12秒。
为了找到延迟的原因,我们介绍了视频流。

因此,有一部手机,视频从摄像机传到视频缓冲区。 在这里,延迟是最小的(≈0.15ms)。 然后,编码器对信号进行编码,将其打包在一个数据包中,然后将其发送到套接字缓冲区。 一切都飞向网络。 此外,在接收设备上发生相同的事情。
基本上,有两个主要的难点要考虑:
视频编码/解码
我会讲一些编码。 如果您进行低延迟实时流传输,无论如何都会遇到它。

什么是视频? 这是一组框架,但不是很简单。 帧分为三种类型:I,P和B帧:
- I帧只是jpg。 实际上,这是一个参考框架,它不依赖任何人,并且包含清晰的图片。
- P帧仅取决于先前的帧。
- 棘手的B帧可能取决于未来。 这意味着,为了计算b帧,将来的帧也必须来自相机。 只有这样,才能延迟b帧的解码。
这表明
B 帧是有害的 。 让我们尝试删除它们。
- 如果从移动设备流式传输,则可以尝试启用基准配置文件 。 它将禁用B帧。
- 您可以尝试配置编解码器,并减少以后帧的延迟,以便更快地到达帧。
- 调整编解码器的另一重要事项是包含CBR (恒定比特率)。

上面的幻灯片中说明了编解码器的工作方式。 在此示例中,视频是静态图片,其编码可节省磁盘空间,因为 那里几乎没有任何变化,并且视频比特率很低。 变化正在发生-熵在增长,视频比特率也在增长-存储到磁盘非常好。
但是,当主动变化开始并且比特率增加时,很可能所有数据都不会潜入网络。 当您进行视频通话并开始转弯,而订户放慢图片的速度时,就会发生这种情况。 这是由于以下事实:网络没有时间来适应更改比特率。
一个必须包括CBR 。 并非所有的Android编解码器都会正确支持它,但他们会为此努力。 就是说,您需要了解,使用CBR,您将无法获得最完美的世界图景,如底部图所示,但仍然值得将其打开。
4.在后端,您需要将
零延迟编解码器添加到
H264-这将使您将来不再依赖帧。
视频传输协议
考虑行业提供什么样的流协议。 我有条件地将它们分为两种类型:
- 流协议;
- 段协议。
流协议是来自p2p调用世界的协议:
RTMP,webRTC,RTSP / RTP 。 它们的不同之处在于,用户同意他们拥有的信道,然后根据信道选择编解码器比特率。 他们还拥有其他这类团队,例如“给我参考”。 如果丢失了帧,则在这些协议中可以再次请求。
段协议之间的区别在于,没有人与任何人协商。 他们将视频切成片段,以各种质量存储每个片段,客户可以选择观看哪个片段。 每个段均以参考帧开始。
更详细地考虑协议。 让我们从流传输协议开始,并弄清楚如果我们使用流传输协议进行广播流传输会遇到什么问题。
流协议
潜望镜使用RTMP。 该协议出现在2009年,起初Adobe并未完全指定它。 然后,他对Adobe只想出售其服务器感到有点困难。 也就是说,RTMP开发相当困难。 他的主要问题是
他使用TCP ,但是出于某种原因,Periscope选择了他。

如果您仔细阅读,结果发现Periscope使用RTMP来与少量观众进行广播。 只是这样的广播,如果您的频道不足,很可能您将无法观看。

考虑一个具体的例子。 有一个通信渠道狭窄的用户正在观看您的广播。 您在RTMP上同意他关于低比特率的观点,并开始亲自为他直播。
拥有凉爽互联网的用户来找您,您也拥有凉爽的互联网,但是您已经与某人达成了低质量协议,事实证明,拥有凉爽互联网的第三个用户正在观看质量较差的视频流,尽管事实上看起来不错
我们决定解决此问题。 我们使RTMP可以针对每个客户端分别被截断,即,流媒体与服务器协商,以尽可能高的质量进行流传输,并且每个客户端都获得网络允许的质量。
哇!
但是,我们仍然拥有基于TCP的RTMP,并且没有人向我们保证阻止队列的开始。

如图所示:我们接收音频和视频帧,RTMP打包它们,也许它们以某种方式混合,然后飞到网络。
但是,假设我们丢了一个小包。 可能会丢弃相同的黄色丢失数据包(通常是先前某个数据包中的一个P帧)。 也许至少可以播放音频。 但是TCP不会给我们其余的数据包,因为它
保证了数据包的
传送和顺序 。 我们必须以某种方式处理这个问题。

在流中使用TCP协议还有另一个问题。
假设我们有一个缓冲区和高网络带宽。 我们从那里的编解码器生成高分辨率数据包。 然后-拜托! -网络开始无法正常工作。 在编解码器上,我们已经表明需要降低比特率,但是现成的数据包已经在队列中,
您不能以任何方式
将它们从那里删除 。 TCP迫切希望通过我们的3G推送高清数据包。
我们没有缓冲区管理,没有优先级,因此
TCP非常不适合流式传输 。

现在让我们看一下移动网络。 对于首府城市的居民来说,这可能令人惊讶,但是我们的平均移动网络看起来像这样:
- 1.1 Mbps流量;
- 丢包0.1%;
- 平均RTT 300毫秒。
而且,如果您查看某些地区和特定运营商,则他们的
平均每日数据包丢失百分比超过3% ,并且从600毫秒开始的RTT是正常的。
一方面,TCP是一种很酷的协议-教汽车要在高速公路和越野路上立即行驶非常困难。 但是要教她当时也要通过无线网络飞行非常困难。

即使丢失0.001%的数据包,吞吐量也会降低30%。 也就是说,由于TCP协议在具有随机数据包丢失的网络中效率低下,因此我们的用户无法利用30%的信道。
在某些区域,数据包丢失达到1%,则用户拥有大约10%的带宽。
因此,
我们不会做TCP 。
让我们看看从UDP流传输中还有什么。
WebRTC协议对于p2p调用非常有效。 在非常受欢迎的网站上,他们写道,使用它进行通话非常酷,但是对于传递视频和音乐来说却不是很好。
他的主要问题是他
忽略了损失 。 在所有奇怪的情况下,他只是掉下来。
他对通话的依恋仍然存在一些问题,事实是他对所有内容进行了加密。 因此,如果您广播广播,而无需通过启动WebRTC来加密整个音频/视频流,则无论如何都会使您的处理器紧张。 您可能不需要这个。
RTP流是通过UDP传输数据的基本协议。 右侧幻灯片的下方是一组扩展和RFC,必须在WebRTC中实现这些扩展和RFC,以使此协议适用于呼叫。 原则上,您可以尝试执行以下操作-拨打RTP的一组扩展名并获得UDP流。
但这很难 。
第二个问题是,如果您的一个客户端不支持任何扩展,则该协议将不起作用。

段协议
分段视频协议的一个很好的例子是
MPEG-Dash 。 它由您在门户上发布的清单文件组成。 它包含指向不同质量文件的链接,在文件的开头有一些索引,指示哪个文件段从哪个位置开始。

整个视频分为多个片段,例如3秒钟,每个片段均以参考帧开始。 如果您观看了这样的视频并且比特率发生了变化,那么您只是在客户端就可以开始获得所需质量的细分。
分段流的另一个示例是
HLS 。
MPEG-Dash是Google的解决方案,它在Android上运行良好,而Apple解决方案较旧,它具有许多缺点。

第一个是主清单包含到二级清单的链接,每种特定质量的二级清单包含到每个单独段的链接,并且每个单独段由一个单独的文件表示。
如果您看得更详细,则在每个段的内部都是MPEG2-TS。 该协议也是针对卫星制定的;其数据包大小为
188个字节 。 打包这种尺寸的视频非常不便,尤其是因为您一直都在为它提供一个小的标题。
实际上,这不仅对于服务器来说是困难的,服务器要处理40 GB的流量必须收集
2600万个数据包 ,而且在客户端上也很困难。 因此,当我们在MPEG-Dash上重写iOS播放器时,甚至看到了一些性能提升。

但是苹果并没有停滞不前。 在2016年,他们终于宣布,他们有机会在HLS中推销MPEG4的片段。 然后他们承诺只为开发人员添加此功能,但是现在似乎应该出现了对macOS和iOS的支持。
也就是说,片段流似乎很方便-来吧,获取所需的片段,从参考帧开始-可以。
减:显然,您从中开始的参考框架不是流的人现在拥有的框架。 因此,
总会有延迟 。
通常,可以将HLS延迟到大约5秒的延迟,有人说他设法获得4,但是原则上,
使用片段流进行翻译的
决定不是很好 。

难度与延迟
让我们看一下所有可用的协议,并按两个参数对它们进行排序:
协议保证的延迟越短,则越复杂。

我们想要什么?
我们希望制作一个从1到N的流传输的UDP协议,其延迟可与p2p通信相提并论,并根据个人广播还是公共广播来选择是否对数据包进行加密。
还有什么其他选择? 您可以等待,例如,当Google发布其QUIC时。
我会告诉你这是什么。 Google将Google QUIC定位为TCP(TCP 2.0的一种)的替代品。 它自2013年以来就进行了开发,现在还没有规范,但可以在Google Chrome浏览器中完全使用,在我看来,有时他们会向某些用户开放以查看其工作原理。 原则上,您可以进入设置,打开QUIC,转到任何Google网站并通过UDP获取此资源。
我们决定不等他们都指定后再提交决定。
协议要求:
- 多线程 ,也就是说,我们有多个流-控制,视频,音频。
- 可选的交付保证 -控制流具有100%的保证,我们最少需要的视频-我们可以将帧放到那里,我们仍然希望音频。
- 对流进行优先级排序 -使音频前进,并且管理器通常会飞行。
- 可选加密 :所有数据,或仅标头和关键数据。

这是一个标准的三角形:如果网络良好,那么高质量和低延迟。 一旦出现不稳定的网络,数据包就会开始消失,我们将在质量和延迟之间取得平衡。 我们可以选择:要么等待网络变得更好,然后发送所有已累积的数据,要么停止运行并以某种方式生存。
如果按照此原则对协议进行排序,那么可以看出
等待时间越短,质量越差 -结论相当简单。
我们希望将我们的协议加入延迟接近WebRTC的区域,但同时又可以稍加延迟,因为毕竟我们没有通话,而是广播。 用户希望最终接收质量流。
发展历程
让我们开始编写UDP协议,但首先查看统计信息。

这些是我们对移动网络的统计。 在这里,您可以看到平均Internet略大于兆位,大约1%的数据包丢失是正常的,并且RTT在600 ms左右-在3G上,这只是平均水平。
编写协议时,我们将专注于此-开始吧!
UDP协议
我们打开套接字UDP,取出数据,打包,发送。 我们从编解码器中取出第二包,我们仍然发送。 一切似乎都很棒!

但是,我们得到了如下图:如果我们开始将UDP数据包随机发送到套接字,那么根据统计数据,到第21个数据包,到达该数据包的可能性仅为85%。 也就是说,丢包率已经是15%,这是不好的。 这需要解决。

这是标准配置。 该图说明了没有Pacer的生活和有
Pacer的生活。
起搏器可以及时散布数据包并控制其丢失; 它查看现在是什么丢包,并根据此情况适应信道速度。
我们记得,对于移动网络,通常有1-3%的数据包丢失。 因此,我们必须以某种方式进行处理。 如果我们丢了包裹怎么办?
重传

如您所知,在TCP中,有一个快速重传算法:我们发送一个数据包,第二个数据包,如果该数据包丢失,则在一段时间(重传周期)后,我们发送相同的数据包。
有什么优势? 没问题,没有冗余,但是有一个负数-
重传周期 。

这似乎很简单:一段时间后,如果您尚未收到确认包,则需要重新包装。 从逻辑上讲,这可能是等于ping时间的时间。 ping — , RTT time , , .
, , , , jitter: ping-. , , 46 . jitter — 50.

. RTT , , acknowledge . , RFC6298, TCP , .
jitter. jitter ping 15%. , retransmit period , , 20% , RTT.

retransmit. acknowledge . , , . retransmit period, . , .
, retransmit . , , packet loss 5%, 400 , 400 1 packet-drop, , retransmit period , .
, . , , acknowledge . , — , , speculative retransmit .
retransmit, .
. ,
Forward Error Correction ? , , XOR. , , .

! round trip, .
, , ? XOR — , Reed-Solomon, Fountain codes .. : K , N , N .
!
, , , Forward Error Correction negative acknowledgement.
NACK

, parity protection ( ) , .
NACK:
- , negative acknowledgement, .
- FEC.
, :
- , FEC + NACK;
- , Fast retransmit.
, .

, , ( ). , , 11 , 60-80 . , , .
6 .

, , . , . , Wi-Fi 22 5 , 3G 34 8 .

: , 90% packet loss 10 , gap 25 , — FEC + NACK Fast retransmit?
, , , Google, QUIC 2013 , Forward Error Correction , , . 2015 .
FEC + NACK, .
, .

, , c :
- 1 / ;
- 1% packet loss;
- 300 RTT;
- 1 000 — ;
- 1 000 .
10 . packet loss 1% 1 000 10. — 100 1 — , 2 , .
, . 500- , 10 .
:
- 500 Forward Error Correction. , .
- NACK, , .
- Fast Retransmit, .
Forward Error Correction , — gap 200-300 .
Fast Retransmit
: , 10 , , , retransmit period , .

, retransmit period 350 , packet gap — 25-30 , 100. , , retransmit , .
, .
UDP , .

, , p/b-. . , .
, , , , , , 0,5 .
, , , , p/b, , , .
MTU
由于我们自己编写协议,因此我们将不得不处理IP分段问题。 我想很多人都知道这一点,但是以防万一,我会简要地告诉你。

我们有一台服务器,它将一些数据包发送到网络,它们到达路由器,并且在其级别,MTU(最大传输单元)变得比到达的数据包小。 它将数据包分为大大小小(此处为1100和400字节)并发送。
原则上,没有问题,所有问题都将聚集在客户端上并且可以正常工作。 但是,如果丢失1个数据包,则会丢弃所有数据包,此外还会获得额外的数据包头费用。 因此,如果您正在编写协议,则以MTU量工作是理想的。
怎么算呢?实际上,Google不会打扰,不会在QUIC中放入大约1200个字节,也不会选择它,因为IP碎片会收集所有数据包。

我们做的完全一样-首先我们设置一些默认大小并开始发送数据包-让它分段。
并行运行一个单独的线程,并为所有数据包创建一个带有碎片禁止标志的套接字。 如果路由器遇到这样的数据包并且无法对该数据进行分段,则它将丢弃该数据包,并可能通过ICMP向您发送有问题的消息,但很可能ICMP将被关闭,并且这种情况不会发生。 因此,例如,我们简单地尝试三遍以一定间隔发送一定大小的数据包。 如果未达到,则我们认为已超过MTU,并进一步降低了MTU。
因此,有了设备上Internet接口的MTU和最小MTU,我们只需通过一维搜索选择正确的MTU。 之后,我们调整协议中的数据包大小。
实际上,它有时会改变。 我们感到惊讶,但是在切换Wi-Fi等过程中。MTU正在改变。 最好不要停止此并行过程并时不时纠正MTU。

世界上更高的MTU分布。 门户网站上大约有1100个字节。
加密方式
我们说过,我们希望有选择地管理加密。 我们做出最简单的选择-椭圆曲线上的Diffie-Hellman。 我们可以选择执行此操作-我们仅加密控制数据包和标头,以使中间人无法接收广播密钥,对其进行拦截等等。

如果广播是私人广播,那么我们还可以添加所有数据的加密。
数据包使用AES-256独立加密,因此数据包丢弃不会影响进一步的数据包加密。
优先次序
记住,我们希望从协议中获得更多优先级。

我们有元数据,音频和视频帧,我们已将它们成功发送到网络。 然后,我们的网络在地狱中烧毁,并且长时间无法正常工作-我们知道我们需要丢弃数据包。
我们主要丢弃视频数据包,然后尝试丢弃音频,并且从不触摸控制数据包,因为诸如分辨率更改和其他重要问题之类的数据可能会通过它们。
关于UDP的其他包子
例如,如果要使用双向通信编写UDP协议,则需要了解NAT解除绑定以及无法从服务器找到客户端的可能性。

在幻灯片上,有时无法通过UDP从服务器访问客户端。
许多怀疑论者说,路由器的设计方式使NAT解除绑定主要排挤了UDP路由。 但是上面显示,如果“保持活动”或ping少于30秒,则有可能以99%的概率到达客户端。
全球移动设备上的UDP可用性

谷歌说6%,但是事实证明,有
7%的移动用户无法使用UDP 。 在这种情况下,我们仅在TCP上保留优先级,加密和所有内容,为我们美丽的协议。
在UDP上,VOIP现在可以在WebRTC,Google QUIC上运行,并且许多游戏都可以在UDP上运行。 因此,我不相信UDP将在移动设备上关闭。
结果,我们:
- 将拖缆和观察者之间的延迟减少到1 s。
- 我们摆脱了缓冲区中的累积效应,也就是说,广播不会落后。
- 观众的摊位数量减少了 。
- 他们能够在移动设备FullHD上支持流式传输。

- 我们的OK Live移动应用程序的延迟时间比摄像头扫描仪的延迟时间为25毫秒-10毫秒,但并不那么可怕。
- 网络广播显示只有690 ms的延迟-空间!
Odnoklassniki还能播放其他内容

- 从移动设备接受我们的OKMP协议;
- 可以接受RTMP和WebRTC;
- 发出HLS,MPEG-Dash等。
如果您小心的话,您会注意到我说过我们可以从用户那里获取WebRTC,例如,将其转换为RTMP。

有细微差别。 实际上,WebRTC是面向数据包的协议,并使用OPUS音频编解码器。 您不能在RTMP中使用OPUS。
在后端服务器上,我们到处都使用RTMP。 因此,我们必须对FF MPEG进行更多修复,才能将OPUS推入RTMP,将其转换为AAC,并提供给已经使用HLS或其他工具的用户。
在我们里面看起来像什么?

- 使用其中一种协议的用户将原始视频上传到我们的上传服务器。
- 我们在那里部署了协议。
- 我们将RTMP发送到视频转换服务器之一。
- 原稿始终存储在分布式存储中,因此不会丢失任何内容。
- 之后,所有视频都将发送到分发服务器。
对于铁,我们有以下内容:

我将告诉您有关容错的更多信息:
- 上载服务器分布在不同的数据中心,位于不同的IP后面。
- 用户来,在DNS上接收IP。
- 上传服务器将视频发送到切片服务器,它们被剪切并提供给分发服务器。
- 对于更流行的广播,我们开始添加大量的分发服务器。
- 我们将来自用户的所有内容都保存在存储库中,以便以后我们可以创建广播归档文件而不会丢失任何内容。
- 跨三个数据中心分布的容错存储。
为了确定当前负责翻译的服务器,我们使用
ZooKeeper 。 对于每个翻译,我们存储一个节点并为每个服务器创建临时节点。 实际上,这种情况允许流创建将要处理的服务器队列。 此行中的当前领导者总是参与流处理。
我们将快速测试容错能力。 我们首先从整个数据中心的消失开始。
会发生什么?- DNS用户将使用另一个上载服务器的下一个IP。
- 到此时,ZooKeeper将了解该数据中心中的服务器已失效,并将为另一服务器选择切片服务器。
- 下载服务器将找出现在由谁负责此流的转换并将其分发。
原则上,所有这些都会以最小的延迟发生。
在产品中使用协议
我们制作了OK Live流媒体移动应用程序。 它与门户完全集成。 那里的用户可以交流,进行实时广播,有广播的地图,受欢迎的广播列表-通常,您想要的一切。

我们还添加了以FullHD广播的功能。 您可以将Android上的运动相机连接到Android设备。

现在,我们有了一种允许直播的机制。 例如,我们通过OK Live与总统划清了界限,并
在全国播出。 观看并通过迎面而来的用户可以直播并提出他们的问题。
也就是说,实际上,两个最小延迟的即将到来的流提供了某种形式的公共会议。
实际上,我们在2秒钟内在某个地方相遇-一秒钟在那里,一秒钟回来。 请记住,我在文章开头提到的“手推车”-现在看起来像两辆大卡车。 对于电视广播,将其从相机中取出并仅将所有内容混合约1-2 s的延迟是完全正常的。
实际上,我们设法重现了与当前现代TCP相当的东西。

直播是当前的趋势。 在过去的一年半中,OK门户网站的用户数量增加了两倍(并非没有OK Live应用程序的帮助)。

默认情况下会记录所有广播。 我们每天大约有5万个数据流,每天产生约17 TB的流量,但通常门户网站上的所有视频每月都产生约PB的数据。

我们得到了什么:
- 可以保证彩带和观众之间的延迟持续时间。
- 我们制作了第一个移动FullHD应用程序,用于在动态变化的移动Internet频道上进行流式传输。
- 我们有机会丢失数据中心,同时不要中断广播
您学到了什么:
- 什么是视频以及如何流式传输。
- 如果您确定自己有一个非常特定的任务和特定的用户,则可以编写UDP协议。
- 关于任何流服务的体系结构-视频输入,转换和退出。
在Highload ++ Siberia, Alexander Tobol承诺谈论 OK呼叫服务,从本文讨论的内容中知道要应用的内容以及必须再次完全实现的内容将是一件很有趣的事情。
在同一部分中,计划针对高度专业化的主题编写报告: