ExoPlayer播放本地DRM视频报Failed to restore keys错误排查
问题描述
受DRM保护的视频已成功下载至本地存储,尝试播放该本地视频时,ExoPlayer抛出DRM session错误,需要调试DRM模块定位Failed to restore keys错误的根因。
预期结果
本地下载的DRM保护视频可正常播放。
ExoPlayer版本
2.14.2
复现问题的设备
- 搭载Android 11系统的Nokia G20
- 搭载Android 10系统的Realme 5 Pro
异常日志
E/DefaultDrmSession: DRM session error java.lang.IllegalArgumentException: Failed to restore keys at android.media.MediaDrm.restoreKeys(Native Method) at com.google.android.exoplayer2.drm.FrameworkMediaDrm.restoreKeys(FrameworkMediaDrm.java:262) at com.google.android.exoplayer2.drm.DefaultDrmSession.restoreKeys(DefaultDrmSession.java:437) at com.google.android.exoplayer2.drm.DefaultDrmSession.doLicense(DefaultDrmSession.java:401) at com.google.android.exoplayer2.drm.DefaultDrmSession.acquire(DefaultDrmSession.java:294) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.createAndAcquireSession(DefaultDrmSessionManager.java:775) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.createAndAcquireSessionWithRetry(DefaultDrmSessionManager.java:684) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.acquireSession(DefaultDrmSessionManager.java:563) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.acquireSession(DefaultDrmSessionManager.java:510) at com.google.android.exoplayer2.source.SampleQueue.onFormatResult(SampleQueue.java:918) at com.google.android.exoplayer2.source.SampleQueue.peekSampleMetadata(SampleQueue.java:686) at com.google.android.exoplayer2.source.SampleQueue.read(SampleQueue.java:412) at com.google.android.exoplayer2.source.chunk.ChunkSampleStream.readData(ChunkSampleStream.java:398) at com.google.android.exoplayer2.BaseRenderer.readSource(BaseRenderer.java:395) at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.readSourceOmittingSampleData(MediaCodecRenderer.java:996) at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:830) at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:945) at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:478) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:223) at android.os.HandlerThread.run(HandlerThread.java:67) E/VideoDisplayComponent: onDrmSessionManagerError java.lang.IllegalArgumentException: Failed to restore keys at android.media.MediaDrm.restoreKeys(Native Method) at com.google.android.exoplayer2.drm.FrameworkMediaDrm.restoreKeys(FrameworkMediaDrm.java:262) at com.google.android.exoplayer2.drm.DefaultDrmSession.restoreKeys(DefaultDrmSession.java:437) at com.google.android.exoplayer2.drm.DefaultDrmSession.doLicense(DefaultDrmSession.java:401) at com.google.android.exoplayer2.drm.DefaultDrmSession.acquire(DefaultDrmSession.java:294) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.createAndAcquireSession(DefaultDrmSessionManager.java:775) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.createAndAcquireSessionWithRetry(DefaultDrmSessionManager.java:684) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.acquireSession(DefaultDrmSessionManager.java:563) at com.google.android.exoplayer2.drm.DefaultDrmSessionManager.acquireSession(DefaultDrmSession.java:510) at com.google.android.exoplayer2.source.SampleQueue.onFormatResult(SampleQueue.java:918) at com.google.android.exoplayer2.source.SampleQueue.peekSampleMetadata(SampleQueue.java:686) at com.google.android.exoplayer2.source.SampleQueue.read(SampleQueue.java:412) at com.google.android.exoplayer2.source.chunk.ChunkSampleStream.readData(ChunkSampleStream.java:398) at com.google.android.exoplayer2.BaseRenderer.readSource(BaseRenderer.java:395) at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.readSourceOmittingSampleData(MediaCodecRenderer.java:996) at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:830) at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:945) at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:478) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:223) at android.os.HandlerThread.run(HandlerThread.java:67)
排查与解决步骤
1. 校验离线密钥持久化逻辑
restoreKeys调用失败90%以上由离线许可证存储异常导致,按顺序检查:
- 下载DRM视频时,必须等
onKeyStatusChange回调触发、密钥状态为USABLE之后,再持久化保存离线密钥集。如果在许可证请求未完成时提前保存空/不完整的密钥数据,恢复时必然报错。 - 检查本地存储的密钥集字节数组是否完整,排查读写逻辑是否存在写入截断、编码转换错误(比如把字节数组转成String再转回字节数组导致数据损坏),密钥数据必须以原始字节格式存储。
- 保存密钥时绑定的
sessionId、mediaId、keySetId必须和播放时传入的参数完全一致,参数不匹配会直接导致系统DRM服务找不到对应密钥。
2. 开启DRM模块详细日志定位具体错误
ExoPlayer 2.14.2默认关闭DRM模块调试日志,初始化播放器前手动开启:
DefaultDrmSessionManager.setLogSessionIdEnabled(true);
同时给DefaultDrmSessionManager添加事件监听器,打印所有密钥状态变化、底层DRM错误码:
- 如果错误码对应
ERROR_DRM_KEY_EXPIRED,说明离线许可证已经过期,需要重新向许可证服务器申请新的离线密钥。 - 如果错误码对应
ERROR_DRM_CANNOT_HANDLE,说明设备DRM组件不支持当前加密方案的离线恢复,检查视频使用的Widevine安全级别,部分低端设备仅支持在线播放,不支持离线持久化密钥。
3. 排查系统DRM服务兼容性问题
两台测试机搭载的Android 10、11版本存在已知DRM兼容坑:
- 检查是否在设备重启/应用清除数据后首次播放触发错误:Android 11部分厂商ROM会在设备重启后重置DRM密钥库,之前持久化的离线密钥会直接失效,这种情况需要捕获
restoreKeys错误后自动触发在线许可证重请求流程,不要直接抛出播放错误。 - 排查Widevine安全级别降级问题:如果设备解锁过Bootloader,Widevine安全级别会从L1降到L3,之前用L1级别申请的离线密钥无法在L3级别下恢复,需要重新申请对应安全级别的密钥。
4. 校验离线播放配置
播放本地DRM视频时,确认DefaultDrmSessionManager配置符合要求:
multiSession配置要和下载时的配置保持一致,下载时开启多会话则播放时也要开启,否则密钥恢复逻辑无法正常执行。- 离线播放场景不要配置强制在线许可证请求参数,
DrmCallback实现中播放本地视频时要直接返回本地存储的离线密钥响应,不要走网络请求返回空数据。 - 如果使用
OfflineLicenseHelper下载密钥,确认调用downloadLicense时传入的playbackLooper和播放时使用的Looper一致,避免线程上下文错误导致密钥数据解析失败。
内容的提问来源于stack exchange,提问作者Palani A
相关产品推荐
相关产品推荐

