add raw dash audio source widevine support

This commit is contained in:
Kai 2024-12-18 14:12:38 -06:00
parent 1b8b8f5738
commit 6ec033274f
No known key found for this signature in database
7 changed files with 120 additions and 46 deletions

View file

@ -372,7 +372,7 @@ class VideoUrlWidevineSource extends VideoUrlSource {
super(obj);
this.plugin_type = "VideoUrlWidevineSource";
this.licenseUri = obj.licenseUri;
this.widevineLicenseUri = obj.licenseUri;
if(obj.getLicenseRequestExecutor)
this.getLicenseRequestExecutor = obj.getLicenseRequestExecutor;
}
@ -409,7 +409,7 @@ class AudioUrlWidevineSource extends AudioUrlSource {
super(obj);
this.plugin_type = "AudioUrlWidevineSource";
this.licenseUri = obj.licenseUri;
this.widevineLicenseUri = obj.licenseUri;
if(obj.getLicenseRequestExecutor)
this.getLicenseRequestExecutor = obj.getLicenseRequestExecutor;
@ -469,6 +469,8 @@ class DashSource {
this.language = obj.language;
if(obj.requestModifier)
this.requestModifier = obj.requestModifier;
if(obj.getRequestExecutor)
this.getRequestExecutor = obj.getRequestExecutor;
}
}
class DashWidevineSource extends DashSource {
@ -476,7 +478,7 @@ class DashWidevineSource extends DashSource {
super(obj);
this.plugin_type = "DashWidevineSource";
this.licenseUri = obj.licenseUri;
this.widevineLicenseUri = obj.licenseUri;
if(obj.getLicenseRequestExecutor)
this.getLicenseRequestExecutor = obj.getLicenseRequestExecutor;
}
@ -511,6 +513,9 @@ class DashManifestRawAudioSource {
this.manifest = obj.manifest ?? null;
if(obj.requestModifier)
this.requestModifier = obj.requestModifier;
this.widevineLicenseUri = obj.widevineLicenseUri;
if(obj.getLicenseRequestExecutor)
this.getLicenseRequestExecutor = obj.getLicenseRequestExecutor;
}
}

View file

@ -3,7 +3,7 @@ package com.futo.platformplayer.api.media.models.streams.sources
import com.futo.platformplayer.api.media.platforms.js.models.JSRequestExecutor
interface IWidevineSource {
val licenseUri: String
val widevineLicenseUri: String?
val hasLicenseRequestExecutor: Boolean
fun getLicenseRequestExecutor(): JSRequestExecutor?
}

View file

@ -8,7 +8,7 @@ import com.futo.platformplayer.engine.V8Plugin
import com.futo.platformplayer.getOrThrow
class JSAudioUrlWidevineSource : JSAudioUrlSource, IAudioUrlWidevineSource {
override val licenseUri: String
override val widevineLicenseUri: String
override val hasLicenseRequestExecutor: Boolean
@Suppress("ConvertSecondaryConstructorToPrimary")
@ -16,7 +16,7 @@ class JSAudioUrlWidevineSource : JSAudioUrlSource, IAudioUrlWidevineSource {
val contextName = "JSAudioUrlWidevineSource"
val config = plugin.config
licenseUri = _obj.getOrThrow(config, "licenseUri", contextName)
widevineLicenseUri = _obj.getOrThrow(config, "widevineLicenseUri", contextName)
hasLicenseRequestExecutor = obj.has("getLicenseRequestExecutor")
}
@ -36,6 +36,6 @@ class JSAudioUrlWidevineSource : JSAudioUrlSource, IAudioUrlWidevineSource {
override fun toString(): String {
val url = getAudioUrl()
return "(name=$name, container=$container, bitrate=$bitrate, codec=$codec, url=$url, language=$language, duration=$duration, hasLicenseRequestExecutor=${hasLicenseRequestExecutor}, licenseUri=$licenseUri)"
return "(name=$name, container=$container, bitrate=$bitrate, codec=$codec, url=$url, language=$language, duration=$duration, hasLicenseRequestExecutor=${hasLicenseRequestExecutor}, widevineLicenseUri=$widevineLicenseUri)"
}
}

View file

@ -2,19 +2,17 @@ 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.IAudioSource
import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource
import com.futo.platformplayer.api.media.models.streams.sources.IWidevineSource
import com.futo.platformplayer.api.media.platforms.js.DevJSClient
import com.futo.platformplayer.api.media.platforms.js.JSClient
import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.api.media.platforms.js.models.JSRequestExecutor
import com.futo.platformplayer.engine.V8Plugin
import com.futo.platformplayer.getOrDefault
import com.futo.platformplayer.getOrNull
import com.futo.platformplayer.getOrThrow
import com.futo.platformplayer.others.Language
import com.futo.platformplayer.states.StateDeveloper
class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawSource {
class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawSource, IWidevineSource {
override val container : String = "application/dash+xml";
override val name : String;
override val codec: String;
@ -29,6 +27,9 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS
override val hasGenerate: Boolean;
override val widevineLicenseUri: String?
override val hasLicenseRequestExecutor: Boolean
constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_DASH_RAW, plugin, obj) {
val contextName = "DashRawSource";
val config = plugin.config;
@ -41,6 +42,23 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS
priority = _obj.getOrDefault(config, "priority", contextName, false) ?: false;
language = _obj.getOrDefault(config, "language", contextName, Language.UNKNOWN) ?: Language.UNKNOWN;
hasGenerate = _obj.has("generate");
widevineLicenseUri = _obj.getOrThrow(config, "widevineLicenseUri", contextName)
hasLicenseRequestExecutor = obj.has("getLicenseRequestExecutor")
}
override fun getLicenseRequestExecutor(): JSRequestExecutor? {
if (!hasLicenseRequestExecutor || _obj.isClosed)
return null
val result = V8Plugin.catchScriptErrors<Any>(_config, "[${_config.name}] JSDashManifestRawAudioSource", "obj.getLicenseRequestExecutor()") {
_obj.invoke("getLicenseRequestExecutor", arrayOf<Any>())
}
if (result !is V8ValueObject)
return null
return JSRequestExecutor(_plugin, result)
}
override fun generate(): String? {
@ -61,4 +79,4 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS
_obj.invokeString("generate");
}
}
}
}

View file

@ -23,7 +23,7 @@ class JSDashManifestWidevineSource : IVideoUrlSource, IDashManifestSource,
override var priority: Boolean = false
override val licenseUri: String
override val widevineLicenseUri: String
override val hasLicenseRequestExecutor: Boolean
@Suppress("ConvertSecondaryConstructorToPrimary")
@ -36,7 +36,7 @@ class JSDashManifestWidevineSource : IVideoUrlSource, IDashManifestSource,
priority = obj.getOrNull(config, "priority", contextName) ?: false
licenseUri = _obj.getOrThrow(config, "licenseUri", contextName)
widevineLicenseUri = _obj.getOrThrow(config, "widevineLicenseUri", contextName)
hasLicenseRequestExecutor = obj.has("getLicenseRequestExecutor")
}

View file

@ -8,7 +8,7 @@ import com.futo.platformplayer.engine.V8Plugin
import com.futo.platformplayer.getOrThrow
class JSVideoUrlWidevineSource : JSVideoUrlSource, IVideoUrlWidevineSource {
override val licenseUri: String
override val widevineLicenseUri: String
override val hasLicenseRequestExecutor: Boolean
@Suppress("ConvertSecondaryConstructorToPrimary")
@ -16,7 +16,7 @@ class JSVideoUrlWidevineSource : JSVideoUrlSource, IVideoUrlWidevineSource {
val contextName = "JSAudioUrlWidevineSource"
val config = plugin.config
licenseUri = _obj.getOrThrow(config, "licenseUri", contextName)
widevineLicenseUri = _obj.getOrThrow(config, "widevineLicenseUri", contextName)
hasLicenseRequestExecutor = obj.has("getLicenseRequestExecutor")
}
@ -36,6 +36,6 @@ class JSVideoUrlWidevineSource : JSVideoUrlSource, IVideoUrlWidevineSource {
override fun toString(): String {
val url = getVideoUrl()
return "(width=$width, height=$height, container=$container, codec=$codec, name=$name, bitrate=$bitrate, duration=$duration, url=$url, hasLicenseRequestExecutor=$hasLicenseRequestExecutor, licenseUri=$licenseUri)"
return "(width=$width, height=$height, container=$container, codec=$codec, name=$name, bitrate=$bitrate, duration=$duration, url=$url, hasLicenseRequestExecutor=$hasLicenseRequestExecutor, widevineLicenseUri=$widevineLicenseUri)"
}
}

View file

@ -1,6 +1,7 @@
package com.futo.platformplayer.views.video
import android.content.Context
import android.media.MediaDrm
import android.net.Uri
import android.util.AttributeSet
import android.widget.RelativeLayout
@ -57,8 +58,8 @@ 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.PluginMediaDrmCallback
import com.futo.platformplayer.views.video.datasources.JSHttpDataSource
import com.futo.platformplayer.views.video.datasources.PluginMediaDrmCallback
import getHttpDataSourceFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -483,15 +484,20 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
@OptIn(UnstableApi::class)
private fun swapVideoSourceUrlWidevine(videoSource: IVideoUrlWidevineSource) {
Logger.i(TAG, "Loading VideoSource [UrlWidevine]");
if (!MediaDrm.isCryptoSchemeSupported(C.WIDEVINE_UUID)) {
throw IllegalArgumentException("Device does not support Widevine")
}
val dataSource = if(videoSource is JSSource && videoSource.requiresCustomDatasource)
videoSource.getHttpDataSourceFactory()
else
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT)
val baseCallback = HttpMediaDrmCallback(videoSource.licenseUri, dataSource)
val baseCallback = HttpMediaDrmCallback(videoSource.widevineLicenseUri, dataSource)
val callback = if (videoSource.hasLicenseRequestExecutor) {
PluginMediaDrmCallback(baseCallback, videoSource.getLicenseRequestExecutor()!!, videoSource.licenseUri)
PluginMediaDrmCallback(baseCallback, videoSource.getLicenseRequestExecutor()!!, videoSource.widevineLicenseUri!!)
} else {
baseCallback
}
@ -519,14 +525,19 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
@OptIn(UnstableApi::class)
private fun swapVideoSourceDashWidevine(videoSource: IDashManifestWidevineSource) {
Logger.i(TAG, "Loading VideoSource [DashWidevine]")
if (!MediaDrm.isCryptoSchemeSupported(C.WIDEVINE_UUID)) {
throw IllegalArgumentException("Device does not support Widevine")
}
val dataSource =
if (videoSource is JSSource && (videoSource.requiresCustomDatasource)) videoSource.getHttpDataSourceFactory()
else DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT)
val baseCallback = HttpMediaDrmCallback(videoSource.licenseUri, dataSource)
val baseCallback = HttpMediaDrmCallback(videoSource.widevineLicenseUri, dataSource)
val callback = if (videoSource.hasLicenseRequestExecutor) {
PluginMediaDrmCallback(baseCallback, videoSource.getLicenseRequestExecutor()!!, videoSource.licenseUri)
PluginMediaDrmCallback(baseCallback, videoSource.getLicenseRequestExecutor()!!, videoSource.widevineLicenseUri!!)
} else {
baseCallback
}
@ -651,49 +662,89 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
@OptIn(UnstableApi::class)
private fun swapAudioSourceDashRaw(audioSource: JSDashManifestRawAudioSource, play: Boolean, resume: Boolean): Boolean {
Logger.i(TAG, "Loading AudioSource [DashRaw]");
val dataSource = if(audioSource is JSSource && (audioSource.requiresCustomDatasource))
Logger.i(TAG, "Loading AudioSource [DashRaw]")
val dataSource = if (audioSource.requiresCustomDatasource)
audioSource.getHttpDataSourceFactory()
else
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT);
if(audioSource.hasGenerate) {
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT)
val baseCallback = HttpMediaDrmCallback(audioSource.widevineLicenseUri, dataSource)
val callback =
if (audioSource.hasLicenseRequestExecutor && audioSource.widevineLicenseUri != null) {
PluginMediaDrmCallback(baseCallback, audioSource.getLicenseRequestExecutor()!!, audioSource.widevineLicenseUri)
} else {
baseCallback
}
_lastVideoMediaSource = DashMediaSource.Factory(dataSource).setDrmSessionManagerProvider {
DefaultDrmSessionManager.Builder().setMultiSession(true).build(callback)
}.createMediaSource(MediaItem.fromUri(audioSource.url))
if (audioSource.hasGenerate) {
findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) {
val generated = audioSource.generate();
if(generated != null) {
val generated = audioSource.generate()
if (generated != null) {
withContext(Dispatchers.Main) {
_lastVideoMediaSource = DashMediaSource.Factory(dataSource)
.createMediaSource(DashManifestParser().parse(Uri.parse(audioSource.url),
ByteArrayInputStream(generated?.toByteArray() ?: ByteArray(0))));
loadSelectedSources(play, resume);
val factory = DashMediaSource.Factory(dataSource)
if (audioSource.widevineLicenseUri != null) {
if (!MediaDrm.isCryptoSchemeSupported(C.WIDEVINE_UUID)) {
throw IllegalArgumentException("Device does not support Widevine")
}
factory.setDrmSessionManagerProvider {
DefaultDrmSessionManager.Builder().setMultiSession(true)
.build(callback)
}
}
_lastVideoMediaSource = factory.createMediaSource(
DashManifestParser().parse(
Uri.parse(audioSource.url),
ByteArrayInputStream(generated.toByteArray() ?: ByteArray(0))
)
)
loadSelectedSources(play, resume)
}
}
}
return false;
}
else {
_lastVideoMediaSource = DashMediaSource.Factory(dataSource)
.createMediaSource(
DashManifestParser().parse(
Uri.parse(audioSource.url),
ByteArrayInputStream(audioSource.manifest?.toByteArray() ?: ByteArray(0))
)
);
return true;
return false
} else {
val factory = DashMediaSource.Factory(dataSource)
if (audioSource.widevineLicenseUri != null) {
if (!MediaDrm.isCryptoSchemeSupported(C.WIDEVINE_UUID)) {
throw IllegalArgumentException("Device does not support Widevine")
}
factory.setDrmSessionManagerProvider {
DefaultDrmSessionManager.Builder().setMultiSession(true)
.build(callback)
}
}
_lastVideoMediaSource = factory.createMediaSource(
DashManifestParser().parse(
Uri.parse(audioSource.url),
ByteArrayInputStream(audioSource.manifest?.toByteArray() ?: ByteArray(0))
)
)
return true
}
}
@OptIn(UnstableApi::class)
private fun swapAudioSourceUrlWidevine(audioSource: IAudioUrlWidevineSource) {
Logger.i(TAG, "Loading AudioSource [UrlWidevine]")
if (!MediaDrm.isCryptoSchemeSupported(C.WIDEVINE_UUID)) {
throw IllegalArgumentException("Device does not support Widevine")
}
val dataSource = if (audioSource is JSSource && audioSource.requiresCustomDatasource)
audioSource.getHttpDataSourceFactory()
else
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT)
val baseCallback = HttpMediaDrmCallback(audioSource.licenseUri, dataSource)
val baseCallback = HttpMediaDrmCallback(audioSource.widevineLicenseUri, dataSource)
val callback = if (audioSource.hasLicenseRequestExecutor) {
PluginMediaDrmCallback(baseCallback, audioSource.getLicenseRequestExecutor()!!, audioSource.licenseUri)
PluginMediaDrmCallback(baseCallback, audioSource.getLicenseRequestExecutor()!!, audioSource.widevineLicenseUri!!)
} else {
baseCallback
}