ExoPlayer 的自定义 Renderer,硬解失败 fallback

ExoPlayer 的自定义 Renderer,硬解失败 fallback

ExoPlayer 的自定义 Renderer,硬解失败 fallback


「ExoPlayer 的自定义 Renderer,硬解失败 fallback」


线上有一个崩溃在 Amlogic S905X3 方案的电视盒子上集中爆发。Android 10,ExoPlayer 2.18.1,播放 HEVC 4K Main 10 的片源,平均在播放到第 5 分钟到第 30 分钟之间,MediaCodec 直接甩出 android.media.MediaCodec$CodecException,native 层的错误码是 0xffffd8f0,对应 ERROR_CODE_UNSPECIFIED。这个异常既不是 recoverable 也不是 transient,ExoPlayer 默认的处理逻辑往上抛成 ExoPlaybackException,播放器进入 STATE_IDLE,用户只能杀掉应用重新进。更麻烦的是,这个问题只在特定码率、特定参考帧结构的片源上出现,QA 用 8bit 的 HEVC 测试片怎么也复现不了,导致上线前根本没测出来。


0xffffd8f0 与默认 Renderer 的致命判断


拿到日志后,我先去翻了这台设备的解码器列表。adb shell pm list features 加上 dumpsys media.player 一看,HEVC 硬解注册的是 OMX.amlogic.hevc.decoder.awesome2,支持到 4K@60fps,表面看完全没问题。但日志里崩溃栈停在 native_queueInputBuffer,说明 codec 已经 configure 成功、甚至已经正常跑了一段时间,突然在某一帧 input buffer 入队时,底层 firmware 直接 abort。这种"半程暴毙"比 configure 阶段就失败难处理得多,因为 ExoPlayer 的 TrackSelector 在准备阶段已经认定这个 codec 可用,并且把资源都投进去了。


接着我拉了 ExoPlayer 2.18.1 的源码,定位到 MediaCodecRenderer.javaonCodecError 方法。默认实现大概长这样:拿到异常后先判断是否是 MediaCodec.CodecException,如果是,再调用 isRecoverable()isTransient()。两个都是 false,那就直接包装成 ExoPlaybackException 扔给上层。0xffffd8f0 这个错误码在 Android 框架里被归类为 unspecified,自然两个布尔值都是 false。也就是说,ExoPlayer 的设计假设是:只要硬件解码器初始化成功了,它就应该是稳定的;运行期非恢复性错误一律按 fatal 处理。这个假设在 Qualcomm、MTK 的主流平台上大体成立,但遇到 Amlogic 这种 firmware 有坑的定制方案,就直接撞墙了。


我当时的第一反应是:ExoPlayer 难道没有硬解失败自动 fallback 到软解的机制吗?像桌面播放器或者 iOS 的 VideoToolbox 那样,硬解挂了自动切软解。结果翻完 MediaCodecVideoRendererMediaCodecRenderer 的完整状态机,发现真没有。ExoPlayer 的 codec 选型发生在 initCodec 阶段,由 MediaCodecSelector 按优先级给出一个 List<MediaCodecInfo>,Renderer 从头到尾只认这一个实例。一旦 start() 之后出异常,整个 Renderer 就废了,不存在内部重新选 decoder 的逻辑。


自定义 Renderer 的接入姿势


既然默认行为救不了,那就只能自己写 Renderer。我继承 MediaCodecVideoRenderer,搞了一个 AmlogicFallbackVideoRenderer。接入点是在 DefaultRenderersFactory,重写 buildVideoRenderers 方法,把默认的视频 Renderer 替换掉。这里有个小细节:ExoPlayer 2.18.1 的 MediaCodecVideoRenderer 构造函数参数很长,除了 MediaCodecSelector,还有 `

Jetpack 库的源码阅读指南,从哪里下手 2026-07-27

评论区