Merge branch 'drm' into 'master'

add initial widevine drm support for audio url sources

See merge request videostreaming/grayjay!16
This commit is contained in:
Kelvin 2024-05-20 13:33:59 +00:00
commit 481a0cda99
5 changed files with 69 additions and 0 deletions

View file

@ -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);

View file

@ -0,0 +1,6 @@
package com.futo.platformplayer.api.media.models.streams.sources
interface IAudioUrlWidevineSource : IAudioUrlSource {
val bearerToken: String
val licenseUri: String
}

View file

@ -0,0 +1,24 @@
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 : JSAudioUrlSource, IAudioUrlWidevineSource {
override val bearerToken: String
override val licenseUri: String
@Suppress("ConvertSecondaryConstructorToPrimary")
constructor(plugin: JSClient, obj: V8ValueObject) : super(plugin, obj) {
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)"
}
}

View file

@ -66,6 +66,7 @@ abstract class JSSource {
const val TYPE_VIDEO_WITH_METADATA = "VideoUrlRangeSource";
const val TYPE_DASH = "DashSource";
const val TYPE_HLS = "HLSSource";
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 {
@ -88,6 +89,7 @@ abstract class JSSource {
return when(type) {
TYPE_HLS -> JSHLSManifestAudioSource.fromV8HLS(plugin, obj);
TYPE_AUDIOURL -> JSAudioUrlSource(plugin, obj);
TYPE_AUDIOURL_WIDEVINE -> JSAudioUrlWidevineSource(plugin, obj);
TYPE_AUDIO_WITH_METADATA -> JSAudioUrlRangeSource(plugin, obj);
else -> throw NotImplementedError("Unknown type ${type}");
}

View file

@ -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.bearerToken)
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.licenseUri)
.setMultiSession(true)
.setLicenseRequestHeaders(httpRequestHeaders)
.build()
).build()
)
}
//Prefered source selection
fun getPreferredVideoSource(video: IPlatformVideoDetails, targetPixels: Int = -1): IVideoSource? {