摘要:
在本系列上一篇文章《定义和测量延迟》中,介绍了为什么延迟是OTT传输的一个问题以及如何测量端到端延迟中不同传输步骤所占的延迟比重。 本文接下来介绍可能的延迟优化,从编码,打包,CDN交付以及视频播放器这些过程,通过调整其中的参数,可以为观众提供一个经过精心优化的低延迟直播流。
编码(Encoding)
本文仍旧以AWS的产品为例,来说明传输过程中延迟优化的情况。
上一篇文章已经通过使用低延迟模式(Low Latency Mode)参数了解了如何使用AWS Elemental Live优化捕获延迟,但是,此参数可能会导致输入时间戳不连续,丢失更多音频数据包。输入缓冲区大小(Input Buffer Size)参数可用于将输入级缓冲的帧数减少到最小,但存在丢弃某些帧的风险。
图 1. 视频编码相关参数设置
在视频编码部分,有几个参数会影响延迟:
Lookahead: 将其设置为Low将改善延迟,同时降低要求苛刻的场景的输出质量。如果没有或几乎没有场景变化,Low这种模式会工作得较好。 GOP parameters: 建议使用1秒持续时间的GOP,如果需要,可以在2秒内重新打包。 没有B帧的小GOP通常会降低视频质量。 B Frames: 在GOP中使用的B帧越多,为每个添加的B帧增加几帧编码延迟的概率就越高,因为编码引擎将向后看P帧以构建B帧。使用零B帧可以避免这种延迟影响,但是需要提高编码比特率以保持与使用B帧时相同的视频质量。 Temporal Adaptive Quantization: 关闭它会将延迟减少几帧。 其他自适应量化选项不会影响延迟。 Encoder Buffer Size编码器缓冲大小: 默认值是视频比特率的两倍,这会在解码器上产生2秒的延迟。如果设置为1倍比特率,则会产生1秒的延迟并略微影响视频质量。对于要求很高的低延迟目标,缓冲区大小可以设置为比特率的一半,这会导致半个GOP(1/2秒)的延迟,也会使视频质量会受到更大的影响。 Video Preprocessors: 如果需要Deinterlacer处理器,则应选择低延迟插值算法。 就编码阶梯而言,建议在阶梯的下端添加一个轻量级流,切片的大小比通常的要小一些,以便在困难网络条件下,移动设备仍然能够访问流。
打包(Packaging)
对于几乎每个播放器而言,切片(segment)的持续时间对延迟有机械效应。使用1秒的切片,可能会达到5秒的延迟。使用2秒的切片,但这种情况一般不会发生,延迟将始终在7到10秒之间,除非对播放器设置进行严格的优化。1秒的切片将自动生成较小的播放器缓冲区,因此除非播放器提供快速克服空缓冲区的特定机制,否则播放过程的稳健性将会较差。
根据用户的要求选择合适的切片大小非常重要。如果不是绝对需要达到低于7秒的延迟,请不要使用1秒切片,而是使用2秒切片。如果播放器使用2秒切片,那么它也有益于:
将GOP长度从1秒提高到2秒,这样就可以在恒定比特率下提高编码质量。 在origin端摄取时使用2秒切片(如果使用HLS作为摄取格式),可以减少origin处存储和packaging计算的压力。 CDN交付(CDN Delivery)
对于HLS的playlist和DASH的manifest,如果播放器支持此类压缩,则应检查CDN配置是否允许以gzip格式提供。如果在HLS或DASH / SegmentTimeline中使用长DVR窗口,这将简化加载操作。
由于低延迟模式下的播放器与实时边缘时间相比,它们的请求通常更加迫切,因此它们很有可能还未传输就请求切片,从而导致边缘处的404。每个CDN都有一个唯一的默认TTL值用于缓存这些404,并且通常这个值对低延迟流不友好,因此需要对其进行调整。比如对于Amazon CloudFront,可以在配置面板的“错误页面”部分中将其设置为1秒。
与低延迟无关,但对流的传输过程仍然很重要:需要将“origin”的传入标头列入白名单,以便CDN将其转发到origin处,因为它是origin返回的所有下游CORS策略的关键触发器。
最后,如果在CDN端设置了HLS playlist或DASH manifest的TTL,则应验证它们是否短于或等于HLS切片间隔或DASH manifest更新间隔。
视频播放器端的延迟优化
现在看一下最重要的延迟改进领域 - 视频播放器的参数,即使在工作流程的上游优化了工作流参数,但这些优化可能会对未集成低延迟导向机制的视频播放器无效。
视频播放器通常经过优化,可为最终用户提供不间断的播放,这意味着播放器会优先考虑缓冲区长度而不是降低流延迟。但并不意味着完全缺乏启用低延迟的选项,而是在每个播放器的初始化设置中默认不启用这些选项。
以下是一个非详尽的相关设置列表,这些设置会影响播放器为传输提供尽可能低的延迟:
初始缓冲区大小 :大多数播放器设计用于在触发流播放之前缓冲特定数量的切片,秒或一些兆字节(MB)。通常使用1秒和2秒的切片,并且如果播放器不缓冲超过三个切片的时长,则播放器能达到不到10秒的延迟。但是,如果在实时播放列表/清单中呈现长DVR窗口,则某些播放器可能被设计为缓冲特定时间量。在这种情况下,即使切片长度为1秒,最终也会缓冲30到40秒,这会导致较高延迟。这就是为什么应该检查播放器默认缓冲策略,并在播放器过于保守的情况下寻找限制启动时缓冲区长度的方法。通常,将缓冲区限制为3或4秒是延迟和播放稳定性之间的合理折衷。低于3秒可以显着改善延迟,但也会影响用户体验,导致在播放期间会发生定期的重复缓冲阶段。与实时边缘时间 (live edge time)相比,播放器在实时播放中的初始定位(initial position):对于大多数播放器来说,它与前一点相比是多余的,但也并非总是如此。如果利用强制播放头以x切片或延迟x秒开始播放,在播放器设置中设置较低缓冲时间就可能效率比较低。但这是一个补充设置,需要自定义。实时边缘时间粘性 (Live Edge-Time stickiness):即使播放器以预期延迟开始播放,也可能在重新缓冲的情况下,在重新缓冲之前的最后已知时刻恢复播放。这意味着如果播放器只需要100毫秒的时间来重新缓冲,那么在重新缓冲阶段之后,与实时边缘时间相比,将自动延迟相同的时间。这通常是默认情况下在所有播放器中发生的情况,但是一些播放器提供了在空缓冲区后重新加载播放列表/清单的选项(当音频或视频轨道的缓冲区变为零秒并且卡在其上时),或者及时向前寻求播放并且同时关注实时边缘时间。如果优先级是在整个播放会话中保持尽可能低的延迟,并且用户不会在实时会话中浏览每一秒内容,那么如果播放器是开源的话,这是一种可以利用或添加的选项。在任何情况下,如果不希望延迟随着时间的推移而变化,那么在播放器中拥有这一功能至关重要。对不可用切片的恢复能力 (Resilience to segments unavailability):可能是某个特定的媒体切片根本不可用,或者与播放器的期望相比有一些延迟。 在这种情况下,如果在所有重试尝试之后切片还是不可用,则播放器将重试多次加载切片并且可能停止播放连接。对于这种情况,用户可能希望查找播放器选项以增加重试次数,或者切换到较低的比特率,或者跳过时间线中缺少的片段。下面以一些开源播放器为例说明延迟相关的参数设置。
hls.js
这个用于MSE(媒体源扩展Media Source Extensions)环境的开源HLS播放器确实在其config.js初始化文件中公开了许多不同的参数。下面为它可以调整的一些参数:
maxBufferLength(默认值:30秒)这是播放器尝试缓冲的最大秒数 maxBufferSize(默认值:60MB)这是播放器尝试缓冲的最大内容大小(MB) maxStarvationDelay(默认值:4s)这是重新缓冲的最大允许秒数。 减少它可以通过强制播放器切换到较低的比特率来防止较大的重新缓冲阶段。 liveSyncDurationCount(默认值:3)这是启动时最后引用的切片后面的切片数。 降低它将使播放器开始接近实时边缘时间。 在hls.js 0.9.1版本之前,如果需要使用低于一秒的playlist重新加载间隔,那么可以减少level-controller.js中的硬编码1000的值:
dash.js
这种用于MSE环境的开源DASH播放器提供了几种方法来设置与实时边缘时间相比的初始延迟。 player.setLiveDelayFragmentCount(默认值:4)允许指定实时边缘时间后面的切片数,而player.setLiveDelays指定它以秒为单位。可以自定义的其他方法参数是:
player.setFragmentLoaderRetryInterval(默认值:1000毫秒)将失败的片段加载尝试间隔变为切片长度的三分之一 player.setFragmentLoaderRetryAttempts(默认值:3),可以增加以补偿以前的自定义 player.setBandwidthSafetyFactor(默认值:0.9),可以降低到0.5以强制执行更保守的吞吐量计算 player.setStableBufferTime(默认值:12s),可以大幅降低以限制后启动/搜索缓冲区目标并简化更快的比特率切换 player.setBufferToKeep(默认值:20s),可以大幅降低以限制缓冲区长度并允许缓冲比特率变化的更具反应性 player.setBufferTimeAtTopQuality(默认值:30秒),可以大幅降低以在播放最高质量视频时限制内部缓冲区 player.setLowLatencyEnabled(默认值:false):从v.2.6.8开始,可以将此参数设置为“true”,以便利用浏览器提取API而不是传统的XHR加载机制。它对长DVR窗口延迟有非常有效的影响。 Exoplayer
这款适用于Android的开源播放器兼容多种流媒体格式,包括HLS和DASH。 在HLS中,Exoplayer在引用太少切片的playlist时会遇到一些问题。在DASH中,默认情况下会对manifest中包含的recommendedPresentationDelay表示支持。 可以修改一些参数以优化低延迟体验:
可以在HlsMediaSource和DashMediaSource类中增加DEFAULT_MIN_LOADABLE_RETRY_COUNT(default 3),以便允许在404中对切片进行更多请求重试 可以减少ChunkedTrackBlacklistUtil类中的DEFAULT_TRACK_BLACKLIST_MS(default 60000),以降低由于连续404而将比特率变量列入黑名单的可能性 最后,减少DefaultLoadControl类中的默认缓冲区值:DEFAULT_MAX_BUFFER_MS(default 3000)
DEFAULT_BUFFER_FOR_PLAYBACK_MS(default 2500)
DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS(default 5000)
以便更准确地控制在播放会话启动时加载的内容量。 Shakaplayer
这个用于MSE环境的开源HLS和DASH播放器提供了几个可以修改的参数选项,以实现更低的延迟,因为默认值是比较保守的:
streaming.bufferingGoal(默认值:10s)是播放器尝试缓冲的内容秒数,它会影响加载的切片数 streaming.rebufferingGoal(默认值:2s)是播放器在开始播放之前需要缓冲的内容秒数。它适用于启动和重新缓冲阶段。如果DASH的manifest中minBufferTime大于此值,将会覆盖它 retryParameters.maxAttempts(默认值:2)是播放器失败之前给定切片的最大请求数 retryParameters.baseDelay(默认值:1000ms)是重试之间的基本延迟 retryParameters.timeout(默认值:0,等于never)是定义请求超时的ms的延迟 retryParameters.backoffFactor(默认值:2)是应用于baseDelay的乘数,用于放大重试之间的延迟 所有retryParameters也可以应用于manifest中。
参考资料
[1]https://aws.amazon.com/cn/blogs/media/how-to-compete-with-broadcast-latency-using-current-adaptive-bitrate-technologies-part-2/
[2]https://aws.amazon.com/cn/blogs/media/part-3-how-to-compete-with-broadcast-latency-using-current-adaptive-bitrate-technologies/