mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-27 20:58:40 +00:00
WIP rewrite generate to async
This commit is contained in:
parent
b69402dfe9
commit
8fb0826d69
5 changed files with 189 additions and 19 deletions
|
@ -13,10 +13,18 @@ import com.futo.platformplayer.engine.exceptions.ScriptExecutionException
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
|
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.selects.SelectClause0
|
||||||
|
import kotlinx.coroutines.selects.SelectClause1
|
||||||
import java.util.concurrent.CancellationException
|
import java.util.concurrent.CancellationException
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import kotlin.coroutines.AbstractCoroutineContextElement
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.reflect.jvm.internal.impl.load.kotlin.JvmType
|
import kotlin.reflect.jvm.internal.impl.load.kotlin.JvmType
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,23 +230,30 @@ fun <T: V8Value> V8ValuePromise.toV8ValueBlocking(plugin: V8Plugin): T {
|
||||||
throw promiseException!!;
|
throw promiseException!!;
|
||||||
return promiseResult!!;
|
return promiseResult!!;
|
||||||
}
|
}
|
||||||
fun <T: V8Value> V8ValuePromise.toV8ValueAsync(plugin: V8Plugin): Deferred<T> {
|
fun <T: V8Value> V8ValuePromise.toV8ValueAsync(plugin: V8Plugin): V8Deferred<T> {
|
||||||
val def = CompletableDeferred<T>();
|
val def = if(this.has("estDuration"))
|
||||||
|
V8Deferred(CompletableDeferred<T>(),
|
||||||
|
this.getOrDefault(plugin.config, "estDuration", "toV8ValueAsync", -1) ?: -1);
|
||||||
|
else
|
||||||
|
V8Deferred<T>(CompletableDeferred<T>());
|
||||||
|
|
||||||
val promise = this;
|
val promise = this;
|
||||||
this.register(object: IV8ValuePromise.IListener {
|
plugin.busy {
|
||||||
override fun onFulfilled(p0: V8Value?) {
|
this.register(object: IV8ValuePromise.IListener {
|
||||||
plugin.resolvePromise(promise);
|
override fun onFulfilled(p0: V8Value?) {
|
||||||
def.complete(p0 as T);
|
plugin.resolvePromise(promise);
|
||||||
}
|
def.complete(p0 as T);
|
||||||
override fun onRejected(p0: V8Value?) {
|
}
|
||||||
plugin.resolvePromise(promise);
|
override fun onRejected(p0: V8Value?) {
|
||||||
def.completeExceptionally(NotImplementedError("onRejected promise not implemented.."));
|
plugin.resolvePromise(promise);
|
||||||
}
|
def.completeExceptionally(NotImplementedError("onRejected promise not implemented.."));
|
||||||
override fun onCatch(p0: V8Value?) {
|
}
|
||||||
plugin.resolvePromise(promise);
|
override fun onCatch(p0: V8Value?) {
|
||||||
def.completeExceptionally(NotImplementedError("onCatch promise not implemented.."));
|
plugin.resolvePromise(promise);
|
||||||
}
|
def.completeExceptionally(NotImplementedError("onCatch promise not implemented.."));
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
plugin.registerPromise(promise) {
|
plugin.registerPromise(promise) {
|
||||||
if(def.isActive)
|
if(def.isActive)
|
||||||
def.cancel("Cancelled by system");
|
def.cancel("Cancelled by system");
|
||||||
|
@ -246,6 +261,37 @@ fun <T: V8Value> V8ValuePromise.toV8ValueAsync(plugin: V8Plugin): Deferred<T> {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class V8Deferred<T>(val deferred: Deferred<T>, val estDuration: Int = -1): Deferred<T> by deferred {
|
||||||
|
|
||||||
|
fun <R> convert(conversion: (result: T)->R): V8Deferred<R>{
|
||||||
|
val newDef = CompletableDeferred<R>()
|
||||||
|
this.invokeOnCompletion {
|
||||||
|
if(it != null)
|
||||||
|
newDef.completeExceptionally(it);
|
||||||
|
else
|
||||||
|
newDef.complete(conversion(this@V8Deferred.getCompleted()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return V8Deferred<R>(newDef, estDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun <R> merge(scope: CoroutineScope, defs: List<V8Deferred<T>> conversion: (result: List<T>)->R): V8Deferred<R> {
|
||||||
|
|
||||||
|
var amount = -1;
|
||||||
|
for(def in defs)
|
||||||
|
amount = Math.max(amount, def.estDuration);
|
||||||
|
|
||||||
|
val def = scope.async {
|
||||||
|
val results = defs.map { it.await() };
|
||||||
|
return@async conversion(results);
|
||||||
|
}
|
||||||
|
return V8Deferred(def, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun <T: V8Value> V8ValueObject.invokeV8(method: String, vararg obj: Any): T {
|
fun <T: V8Value> V8ValueObject.invokeV8(method: String, vararg obj: Any): T {
|
||||||
var result = this.invoke<V8Value>(method, *obj);
|
var result = this.invoke<V8Value>(method, *obj);
|
||||||
|
@ -254,3 +300,10 @@ fun <T: V8Value> V8ValueObject.invokeV8(method: String, vararg obj: Any): T {
|
||||||
}
|
}
|
||||||
return result as T;
|
return result as T;
|
||||||
}
|
}
|
||||||
|
fun <T: V8Value> V8ValueObject.invokeV8Async(method: String, vararg obj: Any): V8Deferred<T> {
|
||||||
|
var result = this.invoke<V8Value>(method, *obj);
|
||||||
|
if(result is V8ValuePromise) {
|
||||||
|
return result.toV8ValueAsync(this.getSourcePlugin()!!);
|
||||||
|
}
|
||||||
|
return V8Deferred(CompletableDeferred(result as T));
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources
|
||||||
|
|
||||||
import com.caoccao.javet.values.primitive.V8ValueString
|
import com.caoccao.javet.values.primitive.V8ValueString
|
||||||
import com.caoccao.javet.values.reference.V8ValueObject
|
import com.caoccao.javet.values.reference.V8ValueObject
|
||||||
|
import com.futo.platformplayer.V8Deferred
|
||||||
import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
|
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.IDashManifestSource
|
||||||
import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource
|
import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource
|
||||||
|
@ -15,8 +16,12 @@ import com.futo.platformplayer.getOrDefault
|
||||||
import com.futo.platformplayer.getOrNull
|
import com.futo.platformplayer.getOrNull
|
||||||
import com.futo.platformplayer.getOrThrow
|
import com.futo.platformplayer.getOrThrow
|
||||||
import com.futo.platformplayer.invokeV8
|
import com.futo.platformplayer.invokeV8
|
||||||
|
import com.futo.platformplayer.invokeV8Async
|
||||||
import com.futo.platformplayer.others.Language
|
import com.futo.platformplayer.others.Language
|
||||||
import com.futo.platformplayer.states.StateDeveloper
|
import com.futo.platformplayer.states.StateDeveloper
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Deferred
|
||||||
|
|
||||||
class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawSource, IStreamMetaDataSource {
|
class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawSource, IStreamMetaDataSource {
|
||||||
override val container : String;
|
override val container : String;
|
||||||
|
@ -52,6 +57,44 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS
|
||||||
hasGenerate = _obj.has("generate");
|
hasGenerate = _obj.has("generate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun generateAsync(scope: CoroutineScope): V8Deferred<String?> {
|
||||||
|
if(!hasGenerate)
|
||||||
|
return V8Deferred(CompletableDeferred(manifest));
|
||||||
|
if(_obj.isClosed)
|
||||||
|
throw IllegalStateException("Source object already closed");
|
||||||
|
|
||||||
|
val plugin = _plugin.getUnderlyingPlugin();
|
||||||
|
|
||||||
|
var result: V8Deferred<V8ValueString>? = null;
|
||||||
|
if(_plugin is DevJSClient)
|
||||||
|
result = StateDeveloper.instance.handleDevCall(_plugin.devID, "DashManifestRaw", false) {
|
||||||
|
_plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw", "dashManifestRaw.generate()") {
|
||||||
|
_plugin.isBusyWith("dashAudio.generate") {
|
||||||
|
_obj.invokeV8Async<V8ValueString>("generate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw", "dashManifestRaw.generate()") {
|
||||||
|
_plugin.isBusyWith("dashAudio.generate") {
|
||||||
|
_obj.invokeV8Async<V8ValueString>("generate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin.busy {
|
||||||
|
val initStart = _obj.getOrDefault<Int>(_config, "initStart", "JSDashManifestRawSource", null) ?: 0;
|
||||||
|
val initEnd = _obj.getOrDefault<Int>(_config, "initEnd", "JSDashManifestRawSource", null) ?: 0;
|
||||||
|
val indexStart = _obj.getOrDefault<Int>(_config, "indexStart", "JSDashManifestRawSource", null) ?: 0;
|
||||||
|
val indexEnd = _obj.getOrDefault<Int>(_config, "indexEnd", "JSDashManifestRawSource", null) ?: 0;
|
||||||
|
if(initEnd > 0 && indexStart > 0 && indexEnd > 0) {
|
||||||
|
streamMetaData = StreamMetaData(initStart, initEnd, indexStart, indexEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return@busy result.convert {
|
||||||
|
it.value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
override fun generate(): String? {
|
override fun generate(): String? {
|
||||||
if(!hasGenerate)
|
if(!hasGenerate)
|
||||||
return manifest;
|
return manifest;
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources
|
||||||
import com.caoccao.javet.values.V8Value
|
import com.caoccao.javet.values.V8Value
|
||||||
import com.caoccao.javet.values.primitive.V8ValueString
|
import com.caoccao.javet.values.primitive.V8ValueString
|
||||||
import com.caoccao.javet.values.reference.V8ValueObject
|
import com.caoccao.javet.values.reference.V8ValueObject
|
||||||
|
import com.futo.platformplayer.V8Deferred
|
||||||
import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource
|
import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource
|
||||||
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
|
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.IVideoUrlSource
|
||||||
|
@ -16,11 +17,17 @@ import com.futo.platformplayer.getOrDefault
|
||||||
import com.futo.platformplayer.getOrNull
|
import com.futo.platformplayer.getOrNull
|
||||||
import com.futo.platformplayer.getOrThrow
|
import com.futo.platformplayer.getOrThrow
|
||||||
import com.futo.platformplayer.invokeV8
|
import com.futo.platformplayer.invokeV8
|
||||||
|
import com.futo.platformplayer.invokeV8Async
|
||||||
import com.futo.platformplayer.states.StateDeveloper
|
import com.futo.platformplayer.states.StateDeveloper
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Deferred
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
|
||||||
interface IJSDashManifestRawSource {
|
interface IJSDashManifestRawSource {
|
||||||
val hasGenerate: Boolean;
|
val hasGenerate: Boolean;
|
||||||
var manifest: String?;
|
var manifest: String?;
|
||||||
|
fun generateAsync(scope: CoroutineScope): Deferred<String?>;
|
||||||
fun generate(): String?;
|
fun generate(): String?;
|
||||||
}
|
}
|
||||||
open class JSDashManifestRawSource: JSSource, IVideoSource, IJSDashManifestRawSource, IStreamMetaDataSource {
|
open class JSDashManifestRawSource: JSSource, IVideoSource, IJSDashManifestRawSource, IStreamMetaDataSource {
|
||||||
|
@ -58,6 +65,45 @@ open class JSDashManifestRawSource: JSSource, IVideoSource, IJSDashManifestRawSo
|
||||||
hasGenerate = _obj.has("generate");
|
hasGenerate = _obj.has("generate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun generateAsync(scope: CoroutineScope): V8Deferred<String?> {
|
||||||
|
if(!hasGenerate)
|
||||||
|
return V8Deferred(CompletableDeferred(manifest));
|
||||||
|
if(_obj.isClosed)
|
||||||
|
throw IllegalStateException("Source object already closed");
|
||||||
|
|
||||||
|
val plugin = _plugin.getUnderlyingPlugin();
|
||||||
|
|
||||||
|
var result: V8Deferred<V8ValueString>? = null;
|
||||||
|
if(_plugin is DevJSClient) {
|
||||||
|
result = StateDeveloper.instance.handleDevCall(_plugin.devID, "DashManifestRawSource.generate()") {
|
||||||
|
_plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw.generate", "generate()", {
|
||||||
|
_plugin.isBusyWith("dashVideo.generate") {
|
||||||
|
_obj.invokeV8Async<V8ValueString>("generate");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw.generate", "generate()", {
|
||||||
|
_plugin.isBusyWith("dashVideo.generate") {
|
||||||
|
_obj.invokeV8Async<V8ValueString>("generate");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return plugin.busy {
|
||||||
|
val initStart = _obj.getOrDefault<Int>(_config, "initStart", "JSDashManifestRawSource", null) ?: 0;
|
||||||
|
val initEnd = _obj.getOrDefault<Int>(_config, "initEnd", "JSDashManifestRawSource", null) ?: 0;
|
||||||
|
val indexStart = _obj.getOrDefault<Int>(_config, "indexStart", "JSDashManifestRawSource", null) ?: 0;
|
||||||
|
val indexEnd = _obj.getOrDefault<Int>(_config, "indexEnd", "JSDashManifestRawSource", null) ?: 0;
|
||||||
|
if(initEnd > 0 && indexStart > 0 && indexEnd > 0) {
|
||||||
|
streamMetaData = StreamMetaData(initStart, initEnd, indexStart, indexEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return@busy result.convert {
|
||||||
|
it.value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
override open fun generate(): String? {
|
override open fun generate(): String? {
|
||||||
if(!hasGenerate)
|
if(!hasGenerate)
|
||||||
return manifest;
|
return manifest;
|
||||||
|
@ -117,6 +163,32 @@ class JSDashManifestMergingRawSource(
|
||||||
override val priority: Boolean
|
override val priority: Boolean
|
||||||
get() = video.priority;
|
get() = video.priority;
|
||||||
|
|
||||||
|
override fun generateAsync(scope: CoroutineScope): V8Deferred<String?> {
|
||||||
|
val videoDashDef = video.generateAsync(scope);
|
||||||
|
val audioDashDef = audio.generateAsync(scope);
|
||||||
|
|
||||||
|
return V8Deferred.merge(scope, listOf(videoDashDef, audioDashDef)) {
|
||||||
|
val (videoDash: String?, audioDash: String?) = it;
|
||||||
|
|
||||||
|
if (videoDash != null && audioDash == null) return@merge videoDash;
|
||||||
|
if (audioDash != null && videoDash == null) return@merge audioDash;
|
||||||
|
if (videoDash == null) return@merge null;
|
||||||
|
|
||||||
|
//TODO: Temporary simple solution..make more reliable version
|
||||||
|
|
||||||
|
var result: String? = null;
|
||||||
|
val audioAdaptationSet = adaptationSetRegex.find(audioDash!!);
|
||||||
|
if (audioAdaptationSet != null) {
|
||||||
|
result = videoDash.replace(
|
||||||
|
"</AdaptationSet>",
|
||||||
|
"</AdaptationSet>\n" + audioAdaptationSet.value
|
||||||
|
)
|
||||||
|
} else
|
||||||
|
result = videoDash;
|
||||||
|
|
||||||
|
return@merge result;
|
||||||
|
};
|
||||||
|
}
|
||||||
override fun generate(): String? {
|
override fun generate(): String? {
|
||||||
val videoDash = video.generate();
|
val videoDash = video.generate();
|
||||||
val audioDash = audio.generate();
|
val audioDash = audio.generate();
|
||||||
|
|
|
@ -277,7 +277,7 @@ class V8Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T : V8Value> executeTypedAsync(js: String) : Deferred<T> {
|
suspend fun <T : V8Value> executeTypedAsync(js: String) : Deferred<T> {
|
||||||
warnIfMainThread("V8Plugin.executeTyped");
|
warnIfMainThread("V8Plugin.executeTypedAsync");
|
||||||
if(isStopped)
|
if(isStopped)
|
||||||
throw PluginEngineStoppedException(config, "Instance is stopped", js);
|
throw PluginEngineStoppedException(config, "Instance is stopped", js);
|
||||||
|
|
||||||
|
@ -358,7 +358,7 @@ class V8Plugin {
|
||||||
_promises.remove(promise);
|
_promises.remove(promise);
|
||||||
return@synchronized found;
|
return@synchronized found;
|
||||||
};
|
};
|
||||||
if(found != null)
|
if(found != null && cancelled)
|
||||||
found(promise);
|
found(promise);
|
||||||
}
|
}
|
||||||
fun cancelAllPromises(){
|
fun cancelAllPromises(){
|
||||||
|
|
|
@ -567,11 +567,13 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
|
||||||
|
|
||||||
if(videoSource.hasGenerate) {
|
if(videoSource.hasGenerate) {
|
||||||
findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) {
|
findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) {
|
||||||
|
val scope = this;
|
||||||
var startId = -1;
|
var startId = -1;
|
||||||
try {
|
try {
|
||||||
val plugin = videoSource.getUnderlyingPlugin() ?: return@launch;
|
val plugin = videoSource.getUnderlyingPlugin() ?: return@launch;
|
||||||
startId = plugin.getUnderlyingPlugin()?.runtimeId ?: -1;
|
startId = plugin.getUnderlyingPlugin()?.runtimeId ?: -1;
|
||||||
val generated = plugin.busy { videoSource.generate(); };
|
val generatedDef = plugin.busy { videoSource.generateAsync(scope); };
|
||||||
|
val generated = generatedDef.await();
|
||||||
if (generated != null) {
|
if (generated != null) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
val dataSource = if(videoSource is JSSource && (videoSource.requiresCustomDatasource))
|
val dataSource = if(videoSource is JSSource && (videoSource.requiresCustomDatasource))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue