Introducing Low-Latency HLS

HLS 简介

HLS (HTTP Live Streaming)是Apple的动态码率自适应技术。主要用于PC和Apple终端的音视频服务。HLS的优点是性能高、兼容性好,缺点是实时性差。适合使用在点播和实时性要求不高的场景。苹果今年新发布了一个低延迟HLS规范,目前它是作为单独的草案来构建的。苹果新的低延时的HLS草案设计目标是1到2秒的延时。

常规HLS时延产生的原因

HLS 被设计为简单的和健壮的。但这种简单性是有代价的,HLS 通常会带来较高的延时。

切片和发现机制增加了延时

我们从框架开始,采集的数据首先进行编码,然后被放到一个segment里,苹果推荐6秒大小的segment。因为编码是实时的,这意味着在6秒内,CDN上没有任何数据。当segment编码完成,客户端需要知道这个segment完成了,现在的HLS采用轮询机制来获取最新的playlist,轮询会在(6,12)区间内获取到包含第一个切片的playlist。之后,客户端发起一个新的请求去获取真正的segment。这里的每一个请求都需要一个RTT,在移动网络上,这个可能增加数百毫秒的延迟。现在,在最坏情况下,延时已经达到了12秒,如果客户端需要2个以上的Segment才能播放,那延时会更大。

CDN的缓存机制加长了延时

如上图所示,源站已经前进到第四个segment,但是CDN边缘节点还缓存着上一个版本(只包含3个片段)的playlist,CDN无法知道该播放列表已在源站上更新。这个时候不能直接回源,如果每个端上的请求到达CDN边缘节点时都去找源站要最新版本,源站就可能会被流量冲垮。因此,CDN 必须缓存一段时间,这就是TTL,必须等TTL过期边缘节点才会回源,所以最差情况下又要多等待一个TTL。

苹果的低延时方案

对于像一些直播类到场景来说,延时的黄金标准是2到8秒。为了将延迟降低到2秒以下,以上问题都需要解决。但有一些因素需要考虑。首先,HTTP仍然是同时通过互联网向数十万人提供相同媒体的最佳方式。所以应该坚持HTTP。但是,这样做意味着我们必须坚持HTTP交付模型。这就是将离散的部分、离散的资源块分配给客户。如果我们要花6秒钟的时间来完成这部分工作,这已经超出了设计目标。那么我们通过HTTP分发的内容必须缩小,在某些情况下要缩短更多。其次,CDN本质上是HTTP代理缓存,它们将做缓存所做的事情。我们应该与之合作而不是反对使用缓存。最后,当播放进度离视频的最前沿太近时,我们只有一点点缓冲,在卡顿之前必须采取一些措施,比如切换码率,而且切换机制应该尽可能高效。基于这些考虑,苹果提出了5点改进:

  • 减少片段发布延迟
  • 优化片段发现机制
  • 消除片段请求时间
  • m3u8采用增量升级机制
  • 加速不同码率直播流切换速度

下面针对每个改进做一个简单介绍:

减少Segment发布延迟

减少发布延时的方式是允许提前发布Segment的一小部分。为了减少发布延迟,向HLS引入一个部分段的概念。这就是EXT-X-PART和EXT-X-PART-INF Tag。如下:

1
2
3
4
The new EXT-X-PART Tag
#EXTM3U
#EXT-X-TARGETDURATION:6.0
#EXT-X-PART-INF:PART-TARGET=0.5

一个部分段本质上只是常规段的一个子集。CMAF称之为FMP4内容的CMAF块。因此,可以使用CMAF块作为HLS中的部分段。您还可以为部分段使用少量传输流或任何其他已定义的HLS段格式。他们的主要特点是比较短小。例如,他们可能不到一个完整的GOP。所以这意味着你可以有半秒的部分片段,并且仍然保持你的两秒GOPs。每次创建新的部分片段时,都会将其添加到播放列表中。这意味着,如果您有半秒的部分片段,那么您可以在内容到达生产后端大约半秒后将其发布到您的CDN。这就是减少发布延迟的程度。部分片段与常规片段流并行添加到播放列表中,但它们不会在播放列表中停留很长时间。这是因为当你在播放的最前沿时部分片段起主要作用。它们允许客户在媒体到达时立即发现媒体。而且,这些部分段的细粒度可寻址性允许加入这些流的客户机将它们连接到更接近视频最前沿地方。

但是,当部分片段远离视频前沿并且它们的父片段在播放列表中建立之后,客户机实际上能更好地载入父片段。因此部分片段将从播放列表中删除。这有助于保持我们的播放列表紧凑。所以,它的工作方式是,当你产生你的片段时,你是并行地产生部分片段。过了一段时间,当这些部分段离活动边缘越来越远或足够远时,它们将被移除,并在视频前沿被新的部分段替换。让我们来看看在实际的HLS播放列表中的效果。我们要注意到的第一件事是,就像普通的播放列表一样,它有一个目标持续时间,这就是我们的片段可以持续多长时间。部分片段具有相同的类型,称为部分目标持续时间。这就是说,播放列表中的部分片段的最长持续时间是5秒半。也就是说部分段只用于描述视频的最前沿(Live edge),当部分段数据不再是最前沿的直播内容时就被合并删除(如下图),这就是我们使用部分片段来降低发布延迟的方法。

优化Segment发现机制

优化片段发现机制的方法是改变客户端更新Playlist的方式。方法时采用阻塞式m3u8加载。它的工作方式是服务器通过下发CAN-BLOCK-RELOAD=YES标记,来声明它能够处理阻塞式m3u8加载。当客户端看到这一点时,就知道它可以在实际准备就绪之前请求更新下一个m3u8。此时,服务器接收到一个请求,意识到它还没有一个被请求的播放列表更新,所以它会一直保持到完成为止。客户机使用HLS的一个特性,称为媒体序列号,向需要更新的服务器指定,它希望使用特定的播放列表更新其中的特定段。HLS播放列表中的每个片段都有一个唯一的序列号。播放列表第一段的序列号是该媒体序列标记的值。如图示,在这个例子中是1800。下一段的媒体序列号只是加1801。这意味着客户机持有这个播放列表,并且知道下一次更新的时候,下一段的序列号是什么。

例如,我们可以告诉服务器,“嘿,请给我一个播放列表更新,我想要一个包含媒体序列号1803的播放列表更新。”你可以看到它要求在M3U8上直播。我们有一个查询参数,_hls_msn=1803。这就是客户机告诉服务器的方式,我想要这个特定的播放列表更新,它包含这个媒体序列号。收到后,它会立即发送1804年的下一个更新请求。对于一个cdn来说,这些URL看起来完全不同,即使对于一个cdn来说只有一个查询参数是不同的,但它是一个完全不同的缓存实体。所以,这就给了我们高速缓存业务。

1
2
3
4
5
# Blocking Playlist Reload
# Block until Media Sequence Number 1803 is in Playlist
GET https://example.com/live.m3u8?_HLS_msn=1803
# Block until first part of Media Sequence Number 1803 is in Playlist
GET https://example.com/live.m3u8?_HLS_msn=1803&_HLS_part=0&_HLS_push=1

消除Segment请求时间

要消除Segment的RTT,就要采用Server push的方式。这是HLS协议升级的一个很大的改变,需要服务器支持HTTP/2。请求m3u8的时候就直接将Segment/part的内容一起push下来,减少一个RTT。

m3u8采用增量升级机制

对于一个3到5小时的m3u8文件,即使使用gzip也会变得很大。由于m3u8的请求可能高达每秒钟3-4次,为了减少网络传输开销,苹果引入了增量更新机制。工作的方式是,服务器再次向客户机声明它可以提供增量更新。它通过下发一个can-skip-until属性来实现这一点,该属性告诉客户如果需要增量更新,它将跳过所有段,直到距离视频前沿一定的秒数。

1
2
3
#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=36.0 
#EXT-X-SKIP:SKIPPED-SEGMENTS=1700
...

如果客户机看到了这一点,并且知道最后一次更新播放列表的时间,它就可以计算出增量更新,而不会错过任何信息。然后它可以在下次为增量更新播放列表时发出显式请求。这个更新只包含播放列表中的最后几个片段,这些片段最接近视频前沿。它跳过了客户机已经拥有的播放列表的早期部分。下面是一个例子:

1
2
# The new EXT-X-SKIP Tag
GET https://example.com/1M/live.m3u8?_HLS_skip=YES

提高不同码率直播流切换速度

前面提到,在视频即将卡顿之前我们应该采取一些措施,比如切换码率,而且切换机制应该尽可能高效。
提高不同码率直播流切换速度的实现方式是在m3u8的最后带上其它码率直播流的当前进展(Segment序列号和part序列号)和加载地址。

1
#EXT-X-RENDITION-REPORT:URI="/2M/live.m3u8",LAST-MSN=1801,LAST-PART=0

当客户端决定要切换到另一个直播流上的时候,不用发起新的连接,只要直接在原来的连接上请求即可。

1
2
# Requesting and receiving Rendition Reports
GET https://example.com/1M/live.m3u8?_HLS_report=/2M/live.m3u8

这就是苹果提出的低延迟HLS技术草案,苹果也提供了参考实现用于测试和演示。