From 88c8dbcb7c67a16c798e426591931abbe6ec69d7 Mon Sep 17 00:00:00 2001 From: Kai DeLorenzo Date: Mon, 29 Apr 2024 13:58:00 -0500 Subject: [PATCH 1/3] added initial drm support for audio url sources --- app/src/main/assets/scripts/source.js | 9 +++ .../streams/sources/AudioUrlWidevineSource.kt | 56 +++++++++++++++++++ .../sources/IAudioUrlWidevineSource.kt | 6 ++ .../sources/JSAudioUrlWidevineSource.kt | 32 +++++++++++ .../platforms/js/models/sources/JSSource.kt | 2 + .../views/video/FutoVideoPlayerBase.kt | 28 ++++++++++ 6 files changed, 133 insertions(+) create mode 100644 app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlWidevineSource.kt create mode 100644 app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioUrlWidevineSource.kt create mode 100644 app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlWidevineSource.kt diff --git a/app/src/main/assets/scripts/source.js b/app/src/main/assets/scripts/source.js index f254370e..7e619b3e 100644 --- a/app/src/main/assets/scripts/source.js +++ b/app/src/main/assets/scripts/source.js @@ -357,6 +357,15 @@ class AudioUrlSource { this.requestModifier = obj.requestModifier; } } +class AudioUrlWidevineSource extends AudioUrlSource { + constructor(obj) { + super(obj); + this.plugin_type = "AudioUrlWidevineSource"; + + this.bearerToken = obj.bearerToken; + this.licenseUri = obj.licenseUri; + } +} class AudioUrlRangeSource extends AudioUrlSource { constructor(obj) { super(obj); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlWidevineSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlWidevineSource.kt new file mode 100644 index 00000000..dd2e36ab --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlWidevineSource.kt @@ -0,0 +1,56 @@ +package com.futo.platformplayer.api.media.models.streams.sources + +import com.futo.platformplayer.api.media.models.streams.sources.other.IStreamMetaDataSource +import com.futo.platformplayer.api.media.models.streams.sources.other.StreamMetaData +import com.futo.platformplayer.others.Language + +class AudioUrlWidevineSource( + override val streamMetaData: StreamMetaData?, + override val name: String, + val url: String, + override val bitrate: Int, + override val container: String = "", + override val codec: String = "", + override val language: String = Language.UNKNOWN, + override val duration: Long? = null, + override var priority: Boolean = false, + private val bearerToken: String, + private val licenseUri: String +) : IAudioUrlWidevineSource, IStreamMetaDataSource { + override fun getAudioUrl(): String { + return url + } + + override fun getBearerToken(): String { + return bearerToken + } + + override fun getLicenseUri(): String { + return licenseUri + } + + companion object { + fun fromUrlSource(source: IAudioUrlWidevineSource?): AudioUrlWidevineSource? { + if (source == null) + return null + + val streamData = if (source is IStreamMetaDataSource) + source.streamMetaData else null + + return AudioUrlWidevineSource( + streamData, + source.name, + source.getAudioUrl(), + source.bitrate, + source.container, + source.codec, + source.language, + source.duration, + source.priority, + source.getBearerToken(), + source.getLicenseUri() + ) + } + } + +} \ No newline at end of file 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 new file mode 100644 index 00000000..fb77028e --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioUrlWidevineSource.kt @@ -0,0 +1,6 @@ +package com.futo.platformplayer.api.media.models.streams.sources + +interface IAudioUrlWidevineSource : IAudioUrlSource { + fun getBearerToken(): String + fun getLicenseUri(): String +} 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 new file mode 100644 index 00000000..fdd87e70 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlWidevineSource.kt @@ -0,0 +1,32 @@ +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.getOrThrow + +class JSAudioUrlWidevineSource(plugin: JSClient, obj: V8ValueObject) : IAudioUrlWidevineSource, + JSAudioUrlSource(plugin, obj) { + private val bearerToken: String + private val licenseUri: String + + override fun getBearerToken(): String { + return bearerToken + } + + override fun getLicenseUri(): String { + return licenseUri + } + + init { + val contextName = "JSAudioUrlWidevineSource" + val config = plugin.config + bearerToken = _obj.getOrThrow(config, "bearerToken", contextName) + licenseUri = _obj.getOrThrow(config, "licenseUri", 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)" + } +} \ 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 9074e28e..b0322c21 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 @@ -66,6 +66,7 @@ abstract class JSSource { const val TYPE_VIDEO_WITH_METADATA = "VideoUrlRangeSource"; const val TYPE_DASH = "DashSource"; const val TYPE_HLS = "HLSSource"; + private const val TYPE_AUDIO_WIDEVINE = "AudioUrlWidevineSource" fun fromV8VideoNullable(plugin: JSClient, obj: V8Value?) : IVideoSource? = obj.orNull { fromV8Video(plugin, it as V8ValueObject) }; fun fromV8Video(plugin: JSClient, obj: V8ValueObject) : IVideoSource { @@ -88,6 +89,7 @@ abstract class JSSource { return when(type) { TYPE_HLS -> JSHLSManifestAudioSource.fromV8HLS(plugin, obj); TYPE_AUDIOURL -> JSAudioUrlSource(plugin, obj); + TYPE_AUDIO_WIDEVINE -> JSAudioUrlWidevineSource(plugin, obj); TYPE_AUDIO_WITH_METADATA -> JSAudioUrlRangeSource(plugin, obj); else -> throw NotImplementedError("Unknown type ${type}"); } 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 3179e282..1bb381d9 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 @@ -16,6 +16,7 @@ import androidx.media3.datasource.DefaultDataSource import androidx.media3.datasource.DefaultHttpDataSource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.dash.DashMediaSource +import androidx.media3.exoplayer.drm.DefaultDrmSessionManagerProvider import androidx.media3.exoplayer.hls.HlsMediaSource import androidx.media3.exoplayer.source.MediaSource import androidx.media3.exoplayer.source.MergingMediaSource @@ -27,6 +28,7 @@ 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.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.IHLSManifestAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource @@ -389,6 +391,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { is LocalAudioSource -> swapAudioSourceLocal(audioSource); is JSAudioUrlRangeSource -> swapAudioSourceUrlRange(audioSource); is JSHLSManifestAudioSource -> swapAudioSourceHLS(audioSource); + is IAudioUrlWidevineSource -> swapAudioSourceUrlWidevine(audioSource) is IAudioUrlSource -> swapAudioSourceUrl(audioSource); null -> _lastAudioMediaSource = null; else -> throw IllegalArgumentException("Unsupported video source [${audioSource.javaClass.simpleName}]"); @@ -508,6 +511,31 @@ abstract class FutoVideoPlayerBase : RelativeLayout { .createMediaSource(MediaItem.fromUri(audioSource.url)); } + @OptIn(UnstableApi::class) + private fun swapAudioSourceUrlWidevine(audioSource: IAudioUrlWidevineSource) { + Logger.i(TAG, "Loading AudioSource [UrlWidevine]") + val dataSource = if (audioSource is JSSource && audioSource.hasRequestModifier) + audioSource.getHttpDataSourceFactory() + else + DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT) + + val httpRequestHeaders = mapOf("Authorization" to "Bearer " + audioSource.getBearerToken()) + val provider = DefaultDrmSessionManagerProvider() + provider.setDrmHttpDataSourceFactory(dataSource) + _lastAudioMediaSource = ProgressiveMediaSource.Factory(dataSource) + .setDrmSessionManagerProvider(provider) + .createMediaSource( + MediaItem.Builder() + .setUri(audioSource.getAudioUrl()).setDrmConfiguration( + MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID) + .setLicenseUri(audioSource.getLicenseUri()) + .setMultiSession(true) + .setLicenseRequestHeaders(httpRequestHeaders) + .build() + ).build() + ) + } + //Prefered source selection fun getPreferredVideoSource(video: IPlatformVideoDetails, targetPixels: Int = -1): IVideoSource? { From ce0f98055f8740da9b6cd7be59561aae084364fe Mon Sep 17 00:00:00 2001 From: Kai DeLorenzo Date: Fri, 17 May 2024 18:45:44 -0400 Subject: [PATCH 2/3] added initial drm support for audio url sources --- .../streams/sources/AudioUrlWidevineSource.kt | 56 ------------------- .../sources/IAudioUrlWidevineSource.kt | 4 +- .../sources/JSAudioUrlWidevineSource.kt | 20 ++----- .../platforms/js/models/sources/JSSource.kt | 4 +- .../views/video/FutoVideoPlayerBase.kt | 4 +- 5 files changed, 12 insertions(+), 76 deletions(-) delete mode 100644 app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlWidevineSource.kt diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlWidevineSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlWidevineSource.kt deleted file mode 100644 index dd2e36ab..00000000 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlWidevineSource.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.futo.platformplayer.api.media.models.streams.sources - -import com.futo.platformplayer.api.media.models.streams.sources.other.IStreamMetaDataSource -import com.futo.platformplayer.api.media.models.streams.sources.other.StreamMetaData -import com.futo.platformplayer.others.Language - -class AudioUrlWidevineSource( - override val streamMetaData: StreamMetaData?, - override val name: String, - val url: String, - override val bitrate: Int, - override val container: String = "", - override val codec: String = "", - override val language: String = Language.UNKNOWN, - override val duration: Long? = null, - override var priority: Boolean = false, - private val bearerToken: String, - private val licenseUri: String -) : IAudioUrlWidevineSource, IStreamMetaDataSource { - override fun getAudioUrl(): String { - return url - } - - override fun getBearerToken(): String { - return bearerToken - } - - override fun getLicenseUri(): String { - return licenseUri - } - - companion object { - fun fromUrlSource(source: IAudioUrlWidevineSource?): AudioUrlWidevineSource? { - if (source == null) - return null - - val streamData = if (source is IStreamMetaDataSource) - source.streamMetaData else null - - return AudioUrlWidevineSource( - streamData, - source.name, - source.getAudioUrl(), - source.bitrate, - source.container, - source.codec, - source.language, - source.duration, - source.priority, - source.getBearerToken(), - source.getLicenseUri() - ) - } - } - -} \ No newline at end of file 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 fb77028e..311c5ed0 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,6 @@ package com.futo.platformplayer.api.media.models.streams.sources interface IAudioUrlWidevineSource : IAudioUrlSource { - fun getBearerToken(): String - fun getLicenseUri(): String + val bearerToken: String + val licenseUri: String } 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 fdd87e70..dcacdb72 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 @@ -5,20 +5,12 @@ import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlWidevin import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.getOrThrow -class JSAudioUrlWidevineSource(plugin: JSClient, obj: V8ValueObject) : IAudioUrlWidevineSource, - JSAudioUrlSource(plugin, obj) { - private val bearerToken: String - private val licenseUri: String +class JSAudioUrlWidevineSource : JSAudioUrlSource, IAudioUrlWidevineSource { + override val bearerToken: String + override val licenseUri: String - override fun getBearerToken(): String { - return bearerToken - } - - override fun getLicenseUri(): String { - return licenseUri - } - - init { + @Suppress("ConvertSecondaryConstructorToPrimary") + constructor(plugin: JSClient, obj: V8ValueObject) : super(plugin, obj) { val contextName = "JSAudioUrlWidevineSource" val config = plugin.config bearerToken = _obj.getOrThrow(config, "bearerToken", contextName) @@ -29,4 +21,4 @@ class JSAudioUrlWidevineSource(plugin: JSClient, obj: V8ValueObject) : IAudioUrl val url = getAudioUrl() return "(name=$name, container=$container, bitrate=$bitrate, codec=$codec, url=$url, language=$language, duration=$duration, bearerToken=$bearerToken, licenseUri=$licenseUri)" } -} \ 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 b0322c21..eaad0203 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 @@ -66,7 +66,7 @@ abstract class JSSource { const val TYPE_VIDEO_WITH_METADATA = "VideoUrlRangeSource"; const val TYPE_DASH = "DashSource"; const val TYPE_HLS = "HLSSource"; - private const val TYPE_AUDIO_WIDEVINE = "AudioUrlWidevineSource" + private const val TYPE_AUDIOURL_WIDEVINE = "AudioUrlWidevineSource" fun fromV8VideoNullable(plugin: JSClient, obj: V8Value?) : IVideoSource? = obj.orNull { fromV8Video(plugin, it as V8ValueObject) }; fun fromV8Video(plugin: JSClient, obj: V8ValueObject) : IVideoSource { @@ -89,7 +89,7 @@ abstract class JSSource { return when(type) { TYPE_HLS -> JSHLSManifestAudioSource.fromV8HLS(plugin, obj); TYPE_AUDIOURL -> JSAudioUrlSource(plugin, obj); - TYPE_AUDIO_WIDEVINE -> JSAudioUrlWidevineSource(plugin, obj); + TYPE_AUDIOURL_WIDEVINE -> JSAudioUrlWidevineSource(plugin, obj); TYPE_AUDIO_WITH_METADATA -> JSAudioUrlRangeSource(plugin, obj); else -> throw NotImplementedError("Unknown type ${type}"); } 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 1bb381d9..519ab6ec 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 @@ -519,7 +519,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { else DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT) - val httpRequestHeaders = mapOf("Authorization" to "Bearer " + audioSource.getBearerToken()) + val httpRequestHeaders = mapOf("Authorization" to "Bearer " + audioSource.bearerToken) val provider = DefaultDrmSessionManagerProvider() provider.setDrmHttpDataSourceFactory(dataSource) _lastAudioMediaSource = ProgressiveMediaSource.Factory(dataSource) @@ -528,7 +528,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { MediaItem.Builder() .setUri(audioSource.getAudioUrl()).setDrmConfiguration( MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID) - .setLicenseUri(audioSource.getLicenseUri()) + .setLicenseUri(audioSource.licenseUri) .setMultiSession(true) .setLicenseRequestHeaders(httpRequestHeaders) .build() From b39b89e908558dcc9ca29eafc2b4b1e4a94adb94 Mon Sep 17 00:00:00 2001 From: Kelvin <41-kelvin@users.noreply.gitlab.futo.org> Date: Mon, 20 May 2024 13:33:06 +0000 Subject: [PATCH 3/3] Make type constant public --- .../api/media/platforms/js/models/sources/JSSource.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 eaad0203..862a53a5 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 @@ -66,7 +66,7 @@ abstract class JSSource { const val TYPE_VIDEO_WITH_METADATA = "VideoUrlRangeSource"; const val TYPE_DASH = "DashSource"; const val TYPE_HLS = "HLSSource"; - private const val TYPE_AUDIOURL_WIDEVINE = "AudioUrlWidevineSource" + const val TYPE_AUDIOURL_WIDEVINE = "AudioUrlWidevineSource" fun fromV8VideoNullable(plugin: JSClient, obj: V8Value?) : IVideoSource? = obj.orNull { fromV8Video(plugin, it as V8ValueObject) }; fun fromV8Video(plugin: JSClient, obj: V8ValueObject) : IVideoSource {