androidx / media

Jetpack Media3 support libraries for media use cases, including ExoPlayer, an extensible media player for Android
Apache License 2.0
1.37k stars 322 forks source link

Motorola Moto G20 bug: Offline Widevine DRM playback error gives "Crypto key not available" #1307

Open thomasjoulin opened 2 months ago

thomasjoulin commented 2 months ago

I'm trying to play a downloaded DASH stream protected by Widevine DRM. Player shows the expected duration but playback won't start, I get this error:

androidx.media3.exoplayer.ExoPlaybackException: MediaCodecVideoRenderer error, index=0, format=Format(2, null, null, video/avc, avc1.42C028, 1007100, null, [852, 480, 25.00036, ColorInfo(BT709, Limited range, SDR SMPTE 170M, false, 8bit Luma, 8bit Chroma)], [-1, -1]), format_supported=YES
    at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:620)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:240)
    at android.os.HandlerThread.run(HandlerThread.java:67)
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:2821)
    at androidx.media3.exoplayer.mediacodec.SynchronousMediaCodecAdapter.queueSecureInputBuffer(SynchronousMediaCodecAdapter.java:151)
    at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.feedInputBuffer(MediaCodecRenderer.java:1448)
    at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:829)
    at androidx.media3.exoplayer.video.MediaCodecVideoRenderer.render(MediaCodecVideoRenderer.java:940)
    at androidx.media3.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:1102)
    at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:541)
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:240) 
    at android.os.HandlerThread.run(HandlerThread.java:67) 

I suppose the issue is in the way I configure the DRM session? Here is my code:

val cacheDataSourceFactory: DataSource.Factory =
    CacheDataSource.Factory()
        .setCache(downloadCache)
        .setUpstreamDataSourceFactory(DefaultHttpDataSource.Factory())
        .setCacheWriteDataSinkFactory(null)

val player =
    ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory),
        )
        .build()

var downloads =
    downloadManager.downloadIndex.getDownloads(Download.STATE_COMPLETED)
downloads.moveToFirst()

var mediaItem = downloads.download.request.toMediaItem()

val drmSessionManager = DefaultDrmSessionManager.Builder()
    .build(
        HttpMediaDrmCallback(
            null,
            false,
            DefaultHttpDataSource.Factory()
        )
    )

drmSessionManager.setMode(DefaultDrmSessionManager.MODE_PLAYBACK, keySetId);

val mediaSource =
    DashMediaSource.Factory(cacheDataSourceFactory)
        .setDrmSessionManagerProvider { drmSessionManager }
        .createMediaSource(mediaItem)

player?.setMediaSource(mediaSource, 0, true)

I think the code to download the DRM key is correct because after downloading it, I can print the license expiration and it matches my expectation. Here is my code just in case:

val dataSourceFactory =
    getDataSourceFactory(context, playerDependencies)
val dataSource = dataSourceFactory.createDataSource()
val dashManifest = DashUtil.loadManifest(dataSource, Uri.parse(mainStream.url))
val drmInitData =
    DashUtil.loadFormatWithDrmInitData(dataSource, dashManifest.getPeriod(0))

drmInitData?.let {
val offlineLicenseHelper = OfflineLicenseHelper.newWidevineInstance(
    drmLicenseUrl.url,
    dataSourceFactory as HttpDataSource.Factory,
    DrmSessionEventListener.EventDispatcher(),
)
val keySetId = offlineLicenseHelper.downloadLicense(it)

// validation license expiration is ok
// val licenseExpiration = System.currentTimeMillis() + (offlineLicenseHelper.getLicenseDurationRemainingSec(keySetId).first * 1000)

val downloadRequest = DownloadRequest.Builder(mediaResource.id, Uri.parse(mainStream.url))
    .setKeySetId(keySetId)
    .build()

DownloadService.sendAddDownload(
    context,
    MyDownloadService::class.java,
    downloadRequest,
    /* foreground= */ false
)
thomasjoulin commented 2 months ago

update: this seems like a device issue after all. The above code works on Samsung Galaxy S24, Tab A, Note 9. Is there anything I can do though? Or any logs to know further?

tianyif commented 2 months ago

Hi @thomasjoulin,

Could you please send the full bug reports to android-media-github@google.com with the subject Issue #1307?

thomasjoulin commented 2 months ago

@tianyif I have sent the bug report as requested. Let me know if you need any additional information.

Also let me know if you have any suggestions on how I could detect this issue (any listener callback / error code) so I can collect more data on devices affected