From 1e46949dd6011cd8fb1c2fe09dfa1163701db36e Mon Sep 17 00:00:00 2001 From: Kai DeLorenzo Date: Tue, 26 Nov 2024 18:52:15 -0500 Subject: [PATCH] added dash widevine support and made audio url widevine more flexible --- app/src/main/assets/scripts/source.js | 22 +++- .../AdvancedOrientationListener.kt | 122 ------------------ .../sources/IAudioUrlWidevineSource.kt | 5 +- .../sources/IDashManifestWidevineSource.kt | 5 + .../models/streams/sources/IWidevineSource.kt | 10 ++ .../sources/JSAudioUrlWidevineSource.kt | 10 +- .../sources/JSDashManifestWidevineSource.kt | 48 +++++++ .../platforms/js/models/sources/JSSource.kt | 2 + .../views/video/FutoVideoPlayerBase.kt | 69 ++++++---- .../datasources/Base64MediaDrmCallback.kt | 22 ++++ 10 files changed, 163 insertions(+), 152 deletions(-) delete mode 100644 app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt create mode 100644 app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IDashManifestWidevineSource.kt create mode 100644 app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IWidevineSource.kt create mode 100644 app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestWidevineSource.kt create mode 100644 app/src/main/java/com/futo/platformplayer/views/video/datasources/Base64MediaDrmCallback.kt diff --git a/app/src/main/assets/scripts/source.js b/app/src/main/assets/scripts/source.js index fba714fc..ffa75609 100644 --- a/app/src/main/assets/scripts/source.js +++ b/app/src/main/assets/scripts/source.js @@ -399,8 +399,16 @@ class AudioUrlWidevineSource extends AudioUrlSource { super(obj); this.plugin_type = "AudioUrlWidevineSource"; - this.bearerToken = obj.bearerToken; + if(obj.bearerToken) { + this.licenseHeaders = { + Authorization: `Bearer ${obj.bearerToken}` + } + } this.licenseUri = obj.licenseUri; + if(obj.licenseHeaders) { + this.licenseHeaders = obj.licenseHeaders; + } + this.decodeLicenseResponse = obj.decodeLicenseResponse } } class AudioUrlRangeSource extends AudioUrlSource { @@ -443,6 +451,18 @@ class DashSource { this.requestModifier = obj.requestModifier; } } +class DashWidevineSource extends DashSource { + constructor(obj) { + super(obj); + this.plugin_type = "DashWidevineSource"; + + this.licenseUri = obj.licenseUri; + if(obj.licenseHeaders) { + this.licenseHeaders = obj.licenseHeaders; + } + this.decodeLicenseResponse = obj.decodeLicenseResponse + } +} class DashManifestRawSource { constructor(obj) { obj = obj ?? {}; diff --git a/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt b/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt deleted file mode 100644 index 2b0897d5..00000000 --- a/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt +++ /dev/null @@ -1,122 +0,0 @@ -import android.app.Activity -import android.content.Context -import android.content.pm.ActivityInfo -import android.hardware.Sensor -import android.hardware.SensorEvent -import android.hardware.SensorEventListener -import android.hardware.SensorManager -import com.futo.platformplayer.constructs.Event1 -import com.futo.platformplayer.logging.Logger -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch - - -class AdvancedOrientationListener(private val activity: Activity, private val lifecycleScope: CoroutineScope) { - private val sensorManager: SensorManager = activity.getSystemService(Context.SENSOR_SERVICE) as SensorManager - private val accelerometer: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) - private val magnetometer: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) - - private var lastOrientation: Int = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED - private var lastStableOrientation: Int = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED - private var lastOrientationChangeTime = 0L - private val debounceTime = 200L - private val stabilityThresholdTime = 800L - private var deviceAspectRatio: Float = 1.0f - - private val gravity = FloatArray(3) - private val geomagnetic = FloatArray(3) - private val rotationMatrix = FloatArray(9) - private val orientationAngles = FloatArray(3) - - val onOrientationChanged = Event1() - - private val sensorListener = object : SensorEventListener { - override fun onSensorChanged(event: SensorEvent) { - when (event.sensor.type) { - Sensor.TYPE_ACCELEROMETER -> { - System.arraycopy(event.values, 0, gravity, 0, gravity.size) - } - Sensor.TYPE_MAGNETIC_FIELD -> { - System.arraycopy(event.values, 0, geomagnetic, 0, geomagnetic.size) - } - } - - if (gravity.isNotEmpty() && geomagnetic.isNotEmpty()) { - val success = SensorManager.getRotationMatrix(rotationMatrix, null, gravity, geomagnetic) - if (success) { - SensorManager.getOrientation(rotationMatrix, orientationAngles) - - val azimuth = Math.toDegrees(orientationAngles[0].toDouble()).toFloat() - val pitch = Math.toDegrees(orientationAngles[1].toDouble()).toFloat() - val roll = Math.toDegrees(orientationAngles[2].toDouble()).toFloat() - - val newOrientation = when { - roll in -155f .. -15f && isWithinThreshold(pitch, 0f, 30.0) -> { - ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE - } - roll in 15f .. 155f && isWithinThreshold(pitch, 0f, 30.0) -> { - ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE - } - isWithinThreshold(pitch, -90f, 30.0 * deviceAspectRatio) && roll in -15f .. 15f -> { - ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - } - isWithinThreshold(pitch, 90f, 30.0 * deviceAspectRatio) && roll in -15f .. 15f -> { - ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT - } - else -> lastOrientation - } - - //Logger.i("AdvancedOrientationListener", "newOrientation = ${newOrientation}, roll = ${roll}, pitch = ${pitch}, azimuth = ${azimuth}") - - if (newOrientation != lastStableOrientation) { - val currentTime = System.currentTimeMillis() - if (currentTime - lastOrientationChangeTime > debounceTime) { - lastOrientationChangeTime = currentTime - lastStableOrientation = newOrientation - - lifecycleScope.launch(Dispatchers.Main) { - try { - delay(stabilityThresholdTime) - if (newOrientation == lastStableOrientation) { - lastOrientation = newOrientation - onOrientationChanged.emit(newOrientation) - } - } catch (e: Throwable) { - Logger.i(TAG, "Failed to trigger onOrientationChanged", e) - } - } - } - } - } - } - } - - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} - } - - private fun isWithinThreshold(value: Float, target: Float, threshold: Double): Boolean { - return Math.abs(value - target) <= threshold - } - - init { - sensorManager.registerListener(sensorListener, accelerometer, SensorManager.SENSOR_DELAY_GAME) - sensorManager.registerListener(sensorListener, magnetometer, SensorManager.SENSOR_DELAY_GAME) - - val metrics = activity.resources.displayMetrics - deviceAspectRatio = (metrics.heightPixels.toFloat() / metrics.widthPixels.toFloat()) - if (deviceAspectRatio == 0.0f) - deviceAspectRatio = 1.0f - - lastOrientation = activity.resources.configuration.orientation - } - - fun stopListening() { - sensorManager.unregisterListener(sensorListener) - } - - companion object { - private val TAG = "AdvancedOrientationListener" - } -} diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioUrlWidevineSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioUrlWidevineSource.kt index 311c5ed0..33d4fa54 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioUrlWidevineSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioUrlWidevineSource.kt @@ -1,6 +1,3 @@ package com.futo.platformplayer.api.media.models.streams.sources -interface IAudioUrlWidevineSource : IAudioUrlSource { - val bearerToken: String - val licenseUri: String -} +interface IAudioUrlWidevineSource : IAudioUrlSource, IWidevineSource diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IDashManifestWidevineSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IDashManifestWidevineSource.kt new file mode 100644 index 00000000..c1b5ea35 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IDashManifestWidevineSource.kt @@ -0,0 +1,5 @@ +package com.futo.platformplayer.api.media.models.streams.sources + +interface IDashManifestWidevineSource : IWidevineSource { + val url: String +} diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IWidevineSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IWidevineSource.kt new file mode 100644 index 00000000..5fbc410f --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IWidevineSource.kt @@ -0,0 +1,10 @@ +package com.futo.platformplayer.api.media.models.streams.sources + +interface IWidevineSource { + val licenseUri: String + val licenseHeaders: Map? + /** + * Set this to true if the license response is Base64 encoded + */ + val decodeLicenseResponse: Boolean +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlWidevineSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlWidevineSource.kt index dcacdb72..569432d6 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlWidevineSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlWidevineSource.kt @@ -3,22 +3,26 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlWidevineSource import com.futo.platformplayer.api.media.platforms.js.JSClient +import com.futo.platformplayer.getOrDefault import com.futo.platformplayer.getOrThrow class JSAudioUrlWidevineSource : JSAudioUrlSource, IAudioUrlWidevineSource { - override val bearerToken: String + override val licenseHeaders: Map? override val licenseUri: String + override val decodeLicenseResponse: Boolean @Suppress("ConvertSecondaryConstructorToPrimary") constructor(plugin: JSClient, obj: V8ValueObject) : super(plugin, obj) { val contextName = "JSAudioUrlWidevineSource" val config = plugin.config - bearerToken = _obj.getOrThrow(config, "bearerToken", contextName) + licenseHeaders = + obj.getOrDefault>(config, "licenseHeaders", contextName, null) licenseUri = _obj.getOrThrow(config, "licenseUri", contextName) + decodeLicenseResponse = _obj.getOrThrow(config, "decodeLicenseResponse", contextName) } override fun toString(): String { val url = getAudioUrl() - return "(name=$name, container=$container, bitrate=$bitrate, codec=$codec, url=$url, language=$language, duration=$duration, bearerToken=$bearerToken, licenseUri=$licenseUri)" + return "(name=$name, container=$container, bitrate=$bitrate, codec=$codec, url=$url, language=$language, duration=$duration, licenseHeaders=${licenseHeaders.toString()}, licenseUri=$licenseUri)" } } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestWidevineSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestWidevineSource.kt new file mode 100644 index 00000000..d9fd3bb6 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestWidevineSource.kt @@ -0,0 +1,48 @@ +package com.futo.platformplayer.api.media.platforms.js.models.sources + +import com.caoccao.javet.values.reference.V8ValueObject +import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource +import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestWidevineSource +import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource +import com.futo.platformplayer.api.media.platforms.js.JSClient +import com.futo.platformplayer.getOrDefault +import com.futo.platformplayer.getOrNull +import com.futo.platformplayer.getOrThrow + +class JSDashManifestWidevineSource : IVideoUrlSource, IDashManifestSource, + IDashManifestWidevineSource, JSSource { + override val width: Int = 0 + override val height: Int = 0 + override val container: String = "application/dash+xml" + override val codec: String = "Dash" + override val name: String + override val bitrate: Int? = null + override val url: String + override val duration: Long + + override var priority: Boolean = false + + override val licenseHeaders: Map? + override val licenseUri: String + override val decodeLicenseResponse: Boolean + + @Suppress("ConvertSecondaryConstructorToPrimary") + constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_DASH, plugin, obj) { + val contextName = "DashWidevineSource" + val config = plugin.config + name = _obj.getOrThrow(config, "name", contextName) + url = _obj.getOrThrow(config, "url", contextName) + duration = _obj.getOrThrow(config, "duration", contextName) + + priority = obj.getOrNull(config, "priority", contextName) ?: false + + licenseHeaders = + obj.getOrDefault>(config, "licenseHeaders", contextName, null) + licenseUri = _obj.getOrThrow(config, "licenseUri", contextName) + decodeLicenseResponse = _obj.getOrThrow(config, "decodeLicenseResponse", contextName) + } + + override fun getVideoUrl(): String { + return url + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt index 45ace168..54f9d7ea 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt @@ -98,6 +98,7 @@ abstract class JSSource { const val TYPE_AUDIO_WITH_METADATA = "AudioUrlRangeSource"; const val TYPE_VIDEO_WITH_METADATA = "VideoUrlRangeSource"; const val TYPE_DASH = "DashSource"; + const val TYPE_DASH_WIDEVINE = "DashWidevineSource"; const val TYPE_DASH_RAW = "DashRawSource"; const val TYPE_DASH_RAW_AUDIO = "DashRawAudioSource"; const val TYPE_HLS = "HLSSource"; @@ -110,6 +111,7 @@ abstract class JSSource { TYPE_VIDEOURL -> JSVideoUrlSource(plugin, obj); TYPE_VIDEO_WITH_METADATA -> JSVideoUrlRangeSource(plugin, obj); TYPE_HLS -> fromV8HLS(plugin, obj); + TYPE_DASH_WIDEVINE -> JSDashManifestWidevineSource(plugin, obj) TYPE_DASH -> fromV8Dash(plugin, obj); TYPE_DASH_RAW -> fromV8DashRaw(plugin, obj); else -> { diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt index fcc6dc6d..4fcf5a3f 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt @@ -3,14 +3,11 @@ package com.futo.platformplayer.views.video import android.content.Context import android.net.Uri import android.util.AttributeSet -import android.util.Xml import android.widget.RelativeLayout import androidx.annotation.OptIn -import androidx.fragment.app.findFragment import androidx.lifecycle.coroutineScope import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.media3.common.C -import androidx.media3.common.C.Encoding import androidx.media3.common.MediaItem import androidx.media3.common.PlaybackException import androidx.media3.common.Player @@ -22,9 +19,9 @@ import androidx.media3.datasource.DefaultHttpDataSource import androidx.media3.datasource.HttpDataSource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.dash.DashMediaSource -import androidx.media3.exoplayer.dash.manifest.DashManifest import androidx.media3.exoplayer.dash.manifest.DashManifestParser -import androidx.media3.exoplayer.drm.DefaultDrmSessionManagerProvider +import androidx.media3.exoplayer.drm.DefaultDrmSessionManager +import androidx.media3.exoplayer.drm.HttpMediaDrmCallback import androidx.media3.exoplayer.hls.HlsMediaSource import androidx.media3.exoplayer.source.MediaSource import androidx.media3.exoplayer.source.MergingMediaSource @@ -34,18 +31,17 @@ import androidx.media3.exoplayer.trackselection.DefaultTrackSelector import com.futo.platformplayer.Settings import com.futo.platformplayer.api.media.models.chapters.IChapter import com.futo.platformplayer.api.media.models.streams.VideoMuxedSourceDescriptor -import com.futo.platformplayer.api.media.models.streams.sources.AudioUrlSource import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlWidevineSource import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource +import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestWidevineSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource import com.futo.platformplayer.api.media.models.streams.sources.LocalAudioSource import com.futo.platformplayer.api.media.models.streams.sources.LocalVideoSource -import com.futo.platformplayer.api.media.models.streams.sources.VideoUrlSource import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlRangeSource @@ -55,15 +51,13 @@ import com.futo.platformplayer.api.media.platforms.js.models.sources.JSDashManif import com.futo.platformplayer.api.media.platforms.js.models.sources.JSHLSManifestAudioSource import com.futo.platformplayer.api.media.platforms.js.models.sources.JSSource import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource -import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlSource import com.futo.platformplayer.constructs.Event1 -import com.futo.platformplayer.engine.dev.V8RemoteObject import com.futo.platformplayer.helpers.VideoHelper import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.video.PlayerManager +import com.futo.platformplayer.views.video.datasources.Base64MediaDrmCallback import com.futo.platformplayer.views.video.datasources.JSHttpDataSource -import com.google.gson.Gson import getHttpDataSourceFactory import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -417,6 +411,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { val didSet = when(videoSource) { is LocalVideoSource -> { swapVideoSourceLocal(videoSource); true; } is JSVideoUrlRangeSource -> { swapVideoSourceUrlRange(videoSource); true; } + is IDashManifestWidevineSource -> { swapVideoSourceDashWidevine(videoSource); true } is IDashManifestSource -> { swapVideoSourceDash(videoSource); true;} is JSDashManifestRawSource -> swapVideoSourceDashRaw(videoSource, play, resume); is IHLSManifestSource -> { swapVideoSourceHLS(videoSource); true; } @@ -494,6 +489,29 @@ abstract class FutoVideoPlayerBase : RelativeLayout { .createMediaSource(MediaItem.fromUri(videoSource.url)) } @OptIn(UnstableApi::class) + private fun swapVideoSourceDashWidevine(videoSource: IDashManifestWidevineSource) { + Logger.i(TAG, "Loading VideoSource [DashWidevine]") + val dataSource = + if (videoSource is JSSource && (videoSource.requiresCustomDatasource)) videoSource.getHttpDataSourceFactory() + else DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT) + + val baseCallback = HttpMediaDrmCallback(videoSource.licenseUri, dataSource) + + videoSource.licenseHeaders?.forEach { (key, value) -> + baseCallback.setKeyRequestProperty(key, value) + } + + val callback = if (videoSource.decodeLicenseResponse) { + Base64MediaDrmCallback(baseCallback) + } else { + baseCallback + } + + _lastVideoMediaSource = DashMediaSource.Factory(dataSource).setDrmSessionManagerProvider { + DefaultDrmSessionManager.Builder().setMultiSession(true).build(callback) + }.createMediaSource(MediaItem.fromUri(videoSource.url)) + } + @OptIn(UnstableApi::class) private fun swapVideoSourceDashRaw(videoSource: JSDashManifestRawSource, play: Boolean, resume: Boolean): Boolean { Logger.i(TAG, "Loading VideoSource [Dash]"); @@ -639,6 +657,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { return true; } } + @OptIn(UnstableApi::class) private fun swapAudioSourceUrlWidevine(audioSource: IAudioUrlWidevineSource) { Logger.i(TAG, "Loading AudioSource [UrlWidevine]") @@ -647,20 +666,26 @@ abstract class FutoVideoPlayerBase : RelativeLayout { else DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT) - val httpRequestHeaders = mapOf("Authorization" to "Bearer " + audioSource.bearerToken) - val provider = DefaultDrmSessionManagerProvider() - provider.setDrmHttpDataSourceFactory(dataSource) + val baseCallback = HttpMediaDrmCallback(audioSource.licenseUri, dataSource) + + audioSource.licenseHeaders?.forEach { (key, value) -> + baseCallback.setKeyRequestProperty(key, value) + } + + val callback = if(audioSource.decodeLicenseResponse){ + Base64MediaDrmCallback(baseCallback) + }else{ + baseCallback + } + _lastAudioMediaSource = ProgressiveMediaSource.Factory(dataSource) - .setDrmSessionManagerProvider(provider) + .setDrmSessionManagerProvider { + DefaultDrmSessionManager.Builder() + .setMultiSession(true) + .build(callback) + } .createMediaSource( - MediaItem.Builder() - .setUri(audioSource.getAudioUrl()).setDrmConfiguration( - MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID) - .setLicenseUri(audioSource.licenseUri) - .setMultiSession(true) - .setLicenseRequestHeaders(httpRequestHeaders) - .build() - ).build() + MediaItem.fromUri(audioSource.getAudioUrl()) ) } diff --git a/app/src/main/java/com/futo/platformplayer/views/video/datasources/Base64MediaDrmCallback.kt b/app/src/main/java/com/futo/platformplayer/views/video/datasources/Base64MediaDrmCallback.kt new file mode 100644 index 00000000..eda67a73 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/views/video/datasources/Base64MediaDrmCallback.kt @@ -0,0 +1,22 @@ +package com.futo.platformplayer.views.video.datasources + +import androidx.media3.common.util.UnstableApi +import androidx.media3.exoplayer.drm.ExoMediaDrm +import androidx.media3.exoplayer.drm.MediaDrmCallback +import java.util.UUID +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi + +@UnstableApi +class Base64MediaDrmCallback( + private val delegate: MediaDrmCallback, +) : MediaDrmCallback by delegate { + + @ExperimentalEncodingApi + override fun executeKeyRequest(uuid: UUID, request: ExoMediaDrm.KeyRequest): ByteArray { + val originalResponse = delegate.executeKeyRequest(uuid, request) + val decodedData: ByteArray = Base64.decode(originalResponse) + + return decodedData + } +}