来源:Global Video Tech Meetup:London 主讲人:Pieter-Jan Speelmans 内容整理:尹文沛 在这篇文章中,主讲人简要介绍了他开发低延时 HLS 的一些经历。
目录
- 发展历史
- 苹果的低延时 HLS
- 业界研究
- ABR 部分
发展历史
苹果的低延时 HLS
- 在 2019 年 6 月,苹果发布了低延时 HLS 的操作指南,你已经可以使用该低延时 HLS 实现一些实例,比如说苹果已经将低延时 HLS 部署在 IOS 系统上,你可以用苹果手机来进行测试。
- 在 2020 年 1 月, 他们认为低延时 HLS 所使用的 HTTP 推送不是他们最终要采用的策略,但他们更改了规范以支持预加载。
- 在 2020 年 6 他们认为关于低延时 HLS 的工作应该持续推进下去。
- 在 2020 年 9 月,他们发布了新的 HLS 规范。
对大多数的低延时 HLS 而言,他们正式开始发展的时间可以说是在 2020 年 6 月。
业界研究
但是对我们而言,我们对低延时的研究从 5 年前,也就是 2016 年就开始了。
在 2016 年,我们那时候和 Periscope (Periscope 是由 Kayvon Beykpour 和 Joe Bernstein 开发并被 Twitter 收购的适用于 Android 和 iOS 的美国直播视频流应用程序) 合作,低延时正是他们所研究的事情之一。他们修改了 HLS 来做渐进式下载块传输代码。我们与 Periscope 合作以进一步优化它,为网页播放器提供解决方案。
在 2016 年 11 月,我们正式发布了 HTML5 播放器和低延时 HLS。
在 2017 年 7 月左右,Periscope 决定分享一些关于 HTML5 播放器和低延时 HLS 的一些技术细节,率先在他的员工中公开了这些技术的实现细节。我们遇到了很多从事播放器和低延迟工作的人可能都熟悉的问题。比如说,在网络空闲期间发送的数据在块之间被引入的问题,这个问题迫使我们保留带宽预测和 ABR 机制。但是还有一些问题非常特殊。
回到那个时候,数据必须尽可能快地通过管道传输到解码器。但是在过去,大多数播放器所做的处理是只会下载整个片段,完成解码后将其发送到缓冲区。基本上是小块视频,但不是那么小,以至于你会在收到几个字节之后立即发送它们。除此之外,在网络不稳定时,播放器会尽可能多地挤压缓冲区。但是这样做肯定是不好的,因为你需要保证 QOE 尽可能的高。我们当时也考虑一个这样的问题,是否应该等待下一个分段的到来,然后在它变得可用时启动,以便在启动时具有尽可能低的延迟,但启动时间稍长。然后就是一些现在看来很常见的问题,比如说卡顿,慢启动等等问题,但是在 2016 年处理起来还是很困难,因为在之前没有任何人进行这方面的研究。
到了 2017 年,低延时得到了更多的关注。2018 年的时候,低延时 dash 正式发布。与此同时,也有一些会议提到了之后是否会有低延时 HLS 的出现。但所有这些都发生在整个 WWDC 故事之前。因为在那之后 WWDC 举办,苹果使用 Http push 的方法让我们很多人感到惊讶。我认为他们最终实际上采用了这种方法,因为这是苹果公司第一次开始公开讨论这个方法。
于是在 2019 年,我们开始实现第一个版本的低延时 HLS。我们认为 Apple 需要大约一年的时间才能真正上线,然后以一种不会像测试版本那样的方式将其放入 iOS,这样就有了相当多的时间来做好准备。所以我们实现了一个想要的低延时 HLS。不再是一个主循环,去处理过去用并行清单 (manifest) 、播放列表和片段做的事情。
非常有趣的是,我们在 2020 年 1 月实现了可用的第一个版本。并宣布开始使用预加载方法, 即 HLS 现在正在使用的延迟功能的阻塞请求。当时的一个很大的问题是的,规范正在改变,所以我们投入了很多工作,这个是重头开始并做得很好。我们实现了现在都知道的低延迟 HLS 规范,并最终在 9 月发布了这些内容。
ABR 部分
过去,你会有一个片段,它在 HLS 中始终在源上完全可用。这个片段发布到播放列表中,你可以完全以一个稳定且快的下载速度获取它。所以基本上可以记录响应发送和启动请求的时间。因此可以通过以下公式得到一个简单又有效的对带宽的估计:
这个公式对低延时 HLS 也是有效的。HLS 每个部分在播放列表更新时都完全可用。所以可以这么想,带宽的估计仍然很简单,但是这其中有一个重要的问题。这个问题在低延时 dash 中也存在。因为它是阻塞响应,这不是一件坏事,但是这是在进行低延迟时需要牢记的事情。
HLS 预加载方法是你加载大部分数据的方式。你不得不为下一个播放列表更新发送一个请求。其他请求会被阻塞,直到播放列表更新的数据到达播放器。你也可以同时启动多个音频视频请求。在阻塞响应的情况下,简单的将响应结束时间减去请求开始时间就不再适用了。可以做的事情是,(尤其是在浏览器环境中),可以知道第一个视频块何时到达。这样你可以将标准响应时间减去第一块数据到达的时间,然后计算自那个时间点以来到达的所有数据,这样上述公式依然有效。如果运气不好,你只得到一个视频块的信息,但是造成这种情况的原因却有很多。
因为很多浏览器会简化视频的表示 (presentation) 信息,他们会做一些讨厌的事情。比如说,大多数浏览器都会把表示分块,然后以一种很好的方式发送出去,但是可能会发生很多重新分块。针对低延迟 HLS,你需要更改带宽测量的方法(就像上面所说的那样)。但另一个问题是如果你使用字节范围会发生什么。对低延迟 HLS 的每个部分,你都可以对其命名并发布自己的请求。但是如果你使用字节范围,再去对低延迟 HLS 的每个部分命令和发布请求,这么做效率并不高,所以最好不要使用字节范围。
不使用字节范围的缺点是,如果你有多个客户同时在请求,就像图 1 所示。这样每个块之间都有阻塞时间,这在低延时 dash 中也是一个很难解决的问题。
图1
在实现低延迟 HLS 之前,我们已经解决过低延时 dash 的很多问题。低延时 dash 在网络状况突然崩溃的情况下表现得并不好,响应很慢,而且对带宽估计并不准确。于是我们考虑是不是能在低延时 HLS 中做的更好。
低延时 HLS 一个最大的优势在于,你知道正在以线性速度传输的是哪一个字节,这个字节被呈现在播放列表的更新信息中。因此,当传输该部分时,你获得第一块数据的同时,还会在请求中获得该播放列表的更新信息。更新信息中则包含该部分字节准确的开始和结束时间。如果你记录了这些时间,你就可以确定突发性流开始和结束的时间。而且你不需要任何低通滤波或者任何跟踪算法去实现带宽估计,因为有一个更好的方法可以做到这一点。
但正如我已经提到的,一些浏览器仍然会存在一些问题,可能会将已经到达的视频块挂起,这个时间比它们理论上应该被挂起的时间更长,因为浏览器想在它们进入应用程序之前去填充缓冲区。这种情况会发生在网络质量突然下降的时候。
图2 ABR 机制流程
除此之外,你仍需要了解另外一些在低延时 HLS 中比较重要的事情。比如说,低延时 HLS 的缓冲区很小,所以在进行质量切换时,我们需要评估现有的网络状况,如果没有足够高的带宽,就将视频块的质量降低。如果有足够高的带宽,我们会判断我们能不能将传输视频块的质量提高,如果不能,就保持原样,如果可以,则传输更高质量的视频块。
虽然上述机制看起来很简单,但是在低延时的 HLS 中并没有我们想象中的这么简单。当你想要切换传输视频块的质量时,你需要请求和下载播放列表的更新,这样会有更新数据往返的额外开销。只有在你更新播放列表后,你才知道应该从哪里开始下载。
在一个合适的场景下,如果下一个视频块时以一个独立的帧开始,那么你可以直接下载该块,但是大多数情况下下一个视频块并不会以一个独立的帧开始。如果是其他情况,你可以在切换开始后使用关键帧获取片段。
图3
另外一个需要特殊介绍的就是 INDEPENDENT 标记,这在规范里面有明确规定。带有 INDEPENDENT 标记的部分并不会以一个关键帧开头,但是该部分包含一个关键帧,详情见规范中的说明。除此之外,即使你知道了从哪一个部分可以正确启动,但是在 dash 中你仍然没有一个正确的时间参考。然而,在 HLS 中就不需要考虑时间参考的问题。
总体的流程如如图 4 所示。