加密密钥不可用 - ExoPlayer

Crypto Key not Available - ExoPlayer

所以我正在尝试播放 Dash 文件 (.mpd) 中的加密内容,该内容使用 Shaka Packager 打包并使用 CENC 方法加密。媒体在 Shaka Player 上播放非常好,但我无法在 Android 的 ExoPlayer 上播放它。播放时,Logcat 显示以下错误:

Caused by: android.media.MediaCodec$CryptoException: Crypto key not available
    at android.media.MediaCodec.native_queueSecureInputBuffer(Native Method)
    at android.media.MediaCodec.queueSecureInputBuffer(MediaCodec.java:2699)
    at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.feedInputBuffer(MediaCodecRenderer.java:1188)
    at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:719) 
    at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:599) 
    at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:329) 
    at android.os.Handler.dispatchMessage(Handler.java:103) 
    at android.os.Looper.loop(Looper.java:237) 
    at android.os.HandlerThread.run(HandlerThread.java:67) 

Build.Gradle 的最小 Sdk 限制为 API 21,因此签出,使用的代码是:

    player = new SimpleExoPlayer.Builder(this).build();
    ep.setPlayer(player);

    DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "AppName"));
    Uri uri = Uri.parse(Tools.baseAddress+"VIDEO/"+i.getStringExtra("ModuleID")+"/index.php").buildUpon().appendQueryParameter("token", Tools.token).build();

    String keyString = "{\"keys\":[{\"kty\":\"oct\",\"k\":\"76a6c65c5ea762046bd749a2e632ccbb\",\"kid\":\"a7e61c373e219033c21091fa607bf3b8\"}],'type':\"temporary\"}";

    LocalMediaDrmCallback drmCallback = new LocalMediaDrmCallback(keyString.getBytes());
    DrmSessionManager manager=null;
    manager = new DefaultDrmSessionManager.Builder()
            .setPlayClearSamplesWithoutKeys(true)
            .setMultiSession(false)
            .setUuidAndExoMediaDrmProvider(C.CLEARKEY_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER)
            .build(drmCallback);

    MediaSource dashMediaSource = new DashMediaSource.Factory(dataSourceFactory).setDrmSessionManager(manager).createMediaSource(uri);

    player = new SimpleExoPlayer.Builder(this).build();

    ep.setPlayer(player);
    player.prepare(dashMediaSource);

并且使用 Shaka Packager 打包 MP4 视频时使用的命令是:

.\packager input=videoplayback.mp4,stream=video,output=video.mp4 input=videoplayback.mp4,stream=audio,output=audio.mp4 --enable_raw_key_encryption --keys key_id=a7e61c373e219033c21091fa607bf3b8:key=76a6c65c5ea762046bd749a2e632ccbb --clear_lead 0 --mpd_output dash.mpd

我不确定密钥格式是否正确或 DRM 会话管理器是否已正确初始化。 如果有任何帮助,我将不胜感激。
提前致谢。

最可能的原因是 LocalMediaDrmCallback 方法需要密钥,并且 key_id 采用 base64url 编码。

您可以使用以下在线工具将您的密钥和 key_id 隐藏到此:

您还可以在此 GitHub 问题讨论中看到一个编程示例:https://github.com/google/ExoPlayer/issues/3856#issuecomment-366197586

我花了很长时间研究“CryptoKey 不可用”异常。

我发现这个异常发生在一些错误的事情上。

  1. MediaDrmCallback 有问题。使用 ClearKey 系统时,使用 LocalMediaDrmCallback 或 sub 类,不能使用网络。

  2. key response (kid & k) base64url实现不好,不包括'/' '=' '\n' '+'

  3. 一些媒体文件用clearlead时间加密,我的视频设置参数是30s所以它总是发生异常,我认为Android DRM会话或密钥在内存中超时。

  4. DrmSessionManager 配置设置错误。注意“setMultiSession”它会破坏你的设置。

  5. 我用以下方法修复了这个异常:

    1. setMultiSession(true),true是针对一个reqreturn一键,不要用"false"
    2. 替换实现其他MediaDrmCallback,用Map
    3. 实现“One Req return One key”

原因:

  1. 我认为某些设备或 DRM 会话实现超时,如果您的视频有清晰的引导时间,DRM 将播放清晰内容和加密内容。当 Video load 开始播放和第一个加密内容播放时,DRM session 将获得两次 Key。所以使用的第一个键无法加载视频加载键。

  2. 当您的 DASH manifest 文件包含一些具有不同密钥的视频,AUDIO,HD,SD 或其他,当网络速度低或高时,播放的视频将改变并且 DRM 会话将与其他视频一起解密键,但我认为更改将找不到正确的键。

  3. 当我使用 setMultiSession(false) -> 这意味着“一个请求响应所有键”,并且使用 LocalMediaDrmCallback(responseJson) 时,我的视频会播放错误或播放良好,我想一些时间设备加载 json 并找到第一个密钥,或者其他时候用错误的密钥。

我已经在我的网站上写了一些关于这个问题的代码和信息:https://blackfire.mobi(中文),看看它来解决这个问题。

我认为“CryptoKey 不可用”异常非常严重,因此为您写下此回复。