百度APP视频播放中的解码优化

318次阅读  |  发布于2年以前

背景

在全民视频的时代,百度APP中视频播放是十分重要的业务。随着 5G 的到来,视频播放已经不满足以前的标清/高清,超清乃至于 4K 已经是旧时王谢堂前燕飞入寻常百姓家。越来越清晰的视频源,越来越复杂的视频编码,对 APP 的视频解码能力也有越来越高的要求。

与此同时,大家的手机性能越来越好,很多手机都逐步提供了强悍的硬件解码能力;而软件解码发展多年,也有其不可替代的优势。所以,如何合理利用手机的软/硬件解码能力,充分发挥其各自优势,为用户们提供更加优质的视频播放体验,就成为了我们重点优化的方向。

软/硬件解码优缺点

解码器有两种模式:软件解码与硬件解码。 软件解码目前业界有比较成熟的 FFmpeg ,利用 CPU 进行解码。 硬件解码发展起步较晚,在 Android 手机上,利用专用解码芯片进行解码。系统提供 MediaCodec ,用于访问底层硬件解码器。

事物都有两面性,两种解码模式各有优缺点,在很多播放器中,两种模式并存。软硬解码的优缺点,对音视频开发者其实算老生常谈了。

模式 优点 缺点
软件解码 支持格式多,兼容性强 CPU负载重,功耗高,性能较差(相对硬解)
硬件解码 性能好,CPU 负载/功耗/内存消耗都更优 支持格式少,兼容性差。解码出首帧速度慢(相对软解)

说明:在 Android 上,MediaCodec 更加具体来说,是 Google 提供的一套框架,因为各个芯片厂商,手机厂商实现差异,所以经常出现兼容性问题。另外 MediaCodec 的初始化流程长,且一些手机上,需要内部缓存多个帧后才对外输出第一个帧,这两个因素导致硬解在首帧解码速度上明显比软解慢。

效率对比

1. 软件解码:使用 FFmpeg ,解码后得到 YUV 数据,需要通过 libyuv 转换为 RGB ,渲染上屏。

2. 硬解码 buffer 模式:使用 MediaCodec ,解码后从 buffer 中得到 YUV 数据,需要通过 libyuv 转换为 RGB ,渲染上屏。

3. 硬解码 surface 模式:使用 MediaCodec ,官方说明中 surface 模式为最高效的模式:解码时绑定 surface ,解码后可通过系统 API 直接上屏到 surface。

(百度APP版本V11.20.0.14,数据日期:2020年03月20日)

模式 软解码 硬解码 buffer 模式 硬解码 surface 模式
帧率 29.4fps 55.0fps 58.8fps
CPU 占用(峰值) 79% 23% 12%

CPU 如下图:

由此可以看出,在视频播放上,MediaCodec surface 模式是效率最高的模式,既充分利用了硬解码的优势,又因为系统直接上屏降低了数据拷贝和 YUV 转换 RGB 的耗时,有效降低了 CPU 负载和对内存的消耗。

痛点

综上所述,在视频播放中,理想状态是尽可能地去用硬解码 surface 模式,其次是使用硬解码 buffer 模式,最后再考虑软解码。但同时需要兼顾首屏解码速度,硬解码的机型兼容性,在这些场景下需要优先使用软解码。

对此,我们需要解决以下痛点:

  1. 怎么完善硬解码的兼容性判断?
  2. 怎么在保证首屏解码速度的情况下,尽可能使用硬件解码?

我们的方案

痛点 1:怎么完善硬解码的兼容性判断?

痛点 2:怎么在保证首屏解码速度的情况下,尽可能使用硬件解码?

如上述,下文具体介绍这 2 个方案。

方案 1:解码器监控

1. 解码器监控模块设计

2. 流程

  1. 起播时,在 prepared 阶段,先通过静态硬解码黑名单,再细分到 编码类型 & profile & level,从解码监控模块看视频源编码方式硬解是否崩溃—>硬解是否异常过多,判断硬解码兼容性是否满足。
  2. 从解码监控模块获取当前视频源编码方式使用软硬解码首帧耗时进行首屏耗时的预测,硬解码满足特定首屏耗时时,优先使用硬解码,反之则选软件解码起播。
  3. 视频播放后,将本次的首帧解码耗时、解码器运行情况(是否崩溃、是否有异常、每帧平均耗时)更新到监控模块,用于下次播放预测。

对于硬件解码有崩溃、异常过多的情况,我们判定硬件解码存在兼容性问题,用软件解码播放完整个视频。

对于硬件解码首屏耗时超过阈值的,其实兼容性是OK的,那么在用软件解码快速起播后,我们可以用方案2进一步优化。

方案 2:软硬件解码器无缝切换

1. 解码通路的统一

为什么要统一?工欲善其事,必先利其器。

如果有一个统一的解码模块,封装软/硬解码器(包括三种解码方式),对外提供统一接入接口,那么对于 Player 仍然像使用一个普通解码器一样使用。在整个架构实现上更加合理,维护扩展也方便。

模块内部维护前后台解码器,内部状态,切换追帧等逻辑,对外无感。而I帧标识,可切换标识等,均可携带在pkt中传入,这样也不需要对解码模块增加一些解码无关的接口,接口设计更加合理。

2. 解码器切换逻辑

两种时机可以切换:1)播放解码到第二个 GOP;2)Player 发生 seek。

播放到第二个 GOP 切换:

  1. 播放开始时,解码模块内打开软解码器作为前台解码器;同时创建后台硬解线程,处于等待状态,并不会阻塞住前台解码任务。
  2. Player(播放器)开始播放,把第一个 GOP 的 pkt(视频包)给解码模块,利用前台解码器(软解)的优势,快速解码首个视频帧用于渲染显示,实现快速起播。
  3. 4-5秒后,第二个 GOP 到来,pkt(视频包)携带可切换的flag通知解码模块,同时输入 GOP2 的多个 pkt 给硬解码器在后台解码,进入追帧状态。前台软解码器解码保持不变,输入一个 pkt ,解码一个帧。
  4. 当后台硬解码器 PTS 追上软解码器的 PTS ,即可关闭硬解码线程,前后台解码器切换。此过程需保证帧的连续,到达无缝切换,用户无感。
  5. GOP2 的后续的 pkt 和后续的 GOP3/4/5……都会使用 MediaCodec buffer 模式。这样在利用软解保证首帧解码速度的同时,也最大限度的利用了 MediaCodec 的解码优势。

Player 发生 seek 切换: 这种场景逻辑比较简单,在 Player seek 时,需要调用 decoder 的 flush,我们趁此机会把前台解码器切换为硬解码器,后续一直用 MediaCodec buffer 模式即可。

3. 保证解码器无缝切换

追帧和解码器切换过程中有两种情况:

结语

目前百度APP Android端,在保障首屏速度和解码错误率没有退化的前提下,视频播放中硬件解码占比已达到 87%,如下:

在目前视频业务百花齐放的时代,编解码也在不断发展进步,各种新的编码方式层出不穷,端上也在这个方向上不断强化自身解码能力。解码作为视频播放中重要的一环,可以预见的是,后续我们仍会在端上解码不断进行探索、优化,为用户提供更优的体验。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8