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
)