From 5666da885912ffbc209dd5a67b316081157b629f Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 15 Oct 2026 02:11:45 +0200 Subject: [PATCH] Bound Android bitrate wait by requested keyframe cadence --- .../java/com/utun/chat/data/CallVideoEngine.kt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 d41e5a6a..e372d3a5 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 @@ -56,7 +56,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) { private var generation = 0L private var frame = 0L private var lastPts = -1L - private var lastKey = -1L + private var lastKeyRequest = -1L private var keySerial = -1L private var needKey = true private var request = 0L @@ -151,6 +151,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) { } if (s[1] == format?.generation && s[2] > request) { request = s[2]; pendingRequest = request; pendingRate = s[3].toInt() + LogManager.addLog("DEBUG", "call", "video bitrate queued id=$id request=$request target=$pendingRate lastKeyRequest=$lastKeyRequest") } if (!wanted.enabled) { if (active != null) { @@ -159,7 +160,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) { confirmingRequest = 0; pendingRequest = 0 closeCamera(); encoder?.close(); encoder = null; active = null; report("Video off") } - } else if (wanted != active && !opening && !closing && (lastKey < 0 || System.nanoTime() / 1000 - lastKey >= 1_000_000)) { + } else if (wanted != active && !opening && !closing && (lastKeyRequest < 0 || System.nanoTime() / 1000 - lastKeyRequest >= 1_000_000)) { val peer = CallVideoNative.peer(id) if (peer != null) change(peer) } @@ -190,7 +191,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) { check(CallVideoNative.supported(caps, peer, next.array())) { "Actual encoder format exceeds peer capabilities: $next" } check(CallVideoNative.format(id, next.array(), candidate.bitrate)) { "Video format rejected" } closeCamera(); encoder = candidate; format = next; active = wanted - pendingRequest = 0; confirmingRequest = 0; request = 0; lastKey = -1; needKey = true + pendingRequest = 0; confirmingRequest = 0; request = 0; lastKeyRequest = -1; needKey = true mutable.value = mutable.value.copy(bitrate = candidate.bitrate) if (!closing) openCamera() return @@ -229,7 +230,8 @@ class CallVideoEngine(private val context: Context, private val id: Long) { val pts = (image.timestamp + offset) / 1000 // Camera2 уже ограничена 30 fps. Порог 33333 мкс отбрасывал каждый второй кадр при дрожании часов. if (pts <= lastPts) return@setOnImageAvailableListener - val key = needKey || confirmingRequest != 0L || lastKey < 0 || pts - lastKey >= 1_000_000 + // Автоматический IDR кодека не переносит нашу границу: иначе смена bitrate может ждать бесконечно. + val key = needKey || confirmingRequest != 0L || lastKeyRequest < 0 || pts - lastKeyRequest >= 1_000_000 if (key && pendingRequest != 0L) { val requestId = pendingRequest; pendingRequest = 0 val old = checkNotNull(encoder); val oldRate = old.bitrate; val name = old.name @@ -252,7 +254,10 @@ class CallVideoEngine(private val context: Context, private val id: Long) { } } } - if (encoder?.submit(image, pts, key) == true) lastPts = pts + if (encoder?.submit(image, pts, key) == true) { + lastPts = pts + if (key) lastKeyRequest = pts + } drain() } catch (e: Exception) { fail("Camera encode: ${e.message}") } finally { image.close() } @@ -335,7 +340,6 @@ class CallVideoEngine(private val context: Context, private val id: Long) { val codec = encoder ?: return; val f = format ?: return for (p in codec.drain()) { encodedBytes += p.bytes.size; encodedFrames++ - if (p.key) lastKey = p.pts if (confirmingRequest != 0L && p.key && p.pts >= confirmingPts) { CallVideoNative.applied(id, f.generation, confirmingRequest, codec.bitrate); confirmingRequest = 0 mutable.value = mutable.value.copy(bitrate = codec.bitrate)