diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt index 8de20e7e..90e95399 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallVideoEngine.kt @@ -685,7 +685,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) { } catch (e: Exception) { pendingEncoders.add(owned) LogManager.addLog("ERROR", "call", "video encoder release failed id=$id; retained for retry: $e") - queueCleanup(250); false + queueCleanup(cleanupDelay); false } /** Вызывается на TX: закрыть независимые владельцы, не теряя refs до SDK ACK/callback. */ @@ -714,8 +714,9 @@ class CallVideoEngine(private val context: Context, private val id: Long) { if (ownedReaders.isEmpty() && sessions.isEmpty() && ownedSessions.isEmpty()) closing = false } if (camera != null && camera !in closingCameras && !opening) closing = false - for (owned in pendingEncoders.toList()) releaseEncoder(owned) - if (!wanted.enabled) encoder?.let { releaseEncoder(it) } + val release = pendingEncoders.toMutableSet() + if (!wanted.enabled) encoder?.let { release.add(it) } + for (owned in release) releaseEncoder(owned) val pending = opening || closing || closingCameras.isNotEmpty() || closingSessions.isNotEmpty() || pendingEncoders.isNotEmpty() || (!wanted.enabled && (encoder != null || orientationEnabled)) if (stopped && !pending) { diff --git a/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/data/VideoHost.kt b/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/data/VideoHost.kt index b3580157..8a67bb1e 100644 --- a/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/data/VideoHost.kt +++ b/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/data/VideoHost.kt @@ -83,12 +83,13 @@ internal object CallVideoNative { val ports = ConcurrentHashMap.newKeySet() val portOperations = CopyOnWriteArrayList() @Volatile var configured = 0L + @Volatile var statusError = 0L @Volatile var onOpen: () -> Unit = {} @Volatile var onClose: () -> Unit = {} fun reset() { frames.clear(); live.clear(); rejected.clear(); accepted.clear(); presented.clear(); destroys.clear() failures = emptySet(); onCreate = {}; onPull = {}; keyRequests.set(0) - ports.clear(); portOperations.clear(); configured = 0; onOpen = {}; onClose = {} + ports.clear(); portOperations.clear(); configured = 0; statusError = 0; onOpen = {}; onClose = {} } private fun access(operation: String) { check(operation !in failures) { "native $operation failed" } } fun playoutCreate(id: Long): Long { @@ -118,10 +119,11 @@ internal object CallVideoNative { fun close(id: Long) { onClose(); access("close"); ports.remove(id); portOperations.add("close:$id") } fun range(id: Long, minimum: Int, maximum: Int): Boolean { access("range"); return "range reject" !in failures } fun peer(id: Long): ByteArray? = if (id in ports) ByteArray(64) else null - fun status(id: Long): LongArray? = if (id in ports) longArrayOf(1, 0, 0, 1_000_000, configured, 0, 300_000, 4_000_000, 0, 0, 1, 1) else null + fun status(id: Long): LongArray? = if (id in ports && "status missing" !in failures) + longArrayOf(1, 0, 0, 1_000_000, configured, statusError, 300_000, 4_000_000, 0, 0, 1, 1) else null fun choose(local: ByteArray, peer: ByteArray, desired: LongArray): LongArray? = desired.copyOf() fun supported(local: ByteArray, peer: ByteArray, desired: LongArray): Boolean = true - fun format(id: Long, format: LongArray, bitrate: Int): Boolean { access("format"); configured = format[0]; return true } + fun format(id: Long, format: LongArray, bitrate: Int): Boolean { access("format"); configured = format[0]; statusError = 0; return true } fun applied(id: Long, generation: Long, request: Long, bitrate: Int) { access("applied") } fun send(id: Long, metadata: LongArray, bytes: ByteArray): Int { access("send"); return 1 } }