From 5f16c795590f79587bd9d8cabba002d8ce3346d8 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 23:19:18 +0200 Subject: [PATCH] Preserve bitrate bounds on format changes and discard late desktop pictures --- .../src/main/java/com/utun/chat/data/CallVideoEngine.kt | 7 +++++-- tools/chatgui/src/call_video_engine.cpp | 9 +++++++-- 2 files changed, 12 insertions(+), 4 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 8b0e9b57..6b9e0f87 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 @@ -155,6 +155,8 @@ class CallVideoEngine(private val context: Context, private val id: Long) { val desired = CallVideoFormat(++generation, wanted.width, wanted.height, rotation = rotation, codec = wanted.codec) val previous = encoder; val previousFormat = format; val previousSettings = active val previousName = previous?.name; val previousRate = previous?.bitrate + val range = mutable.value.range + val initialRate = (previousRate ?: 1_000_000).coerceIn(range.minimum * 1000, range.maximum * 1000) previous?.close(); encoder = null for (codec in if (wanted.codec == 0) listOf(2, 1) else listOf(wanted.codec)) { if (codec in excluded) continue @@ -162,7 +164,7 @@ class CallVideoEngine(private val context: Context, private val id: Long) { val selected = CallVideoNative.choose(caps, peer, desired.copy(codec = codec).array()) ?: continue var candidate: CallVideoCodec? = null try { - candidate = CallVideoCodec(CallVideoFormat.from(selected), 1_000_000, implementation.name) + candidate = CallVideoCodec(CallVideoFormat.from(selected), initialRate, implementation.name) candidate.prepare() val next = candidate.format check(CallVideoNative.supported(caps, peer, next.array())) { "Actual encoder format exceeds peer capabilities: $next" } @@ -178,7 +180,8 @@ class CallVideoEngine(private val context: Context, private val id: Long) { } } if (previousFormat != null && previousFormat.codec !in excluded && previousSettings != null && previousName != null && previousRate != null) { - encoder = CallVideoCodec(previousFormat, previousRate, previousName).also { it.prepare(); check(it.format == previousFormat) } + encoder = CallVideoCodec(previousFormat, previousRate, previousName) + encoder!!.prepare(); check(encoder!!.format == previousFormat) wanted = previousSettings; needKey = true mutable.value = mutable.value.copy(settings = wanted) report("Selected mode unavailable; previous video restored") diff --git a/tools/chatgui/src/call_video_engine.cpp b/tools/chatgui/src/call_video_engine.cpp index 9e10ee05..85fd5f36 100644 --- a/tools/chatgui/src/call_video_engine.cpp +++ b/tools/chatgui/src/call_video_engine.cpp @@ -179,10 +179,13 @@ void CallVideoEngine::transmit() { if (lastKey >= 0 && pts - lastKey < 1000000) { idle(); continue; } call_video_format next{format.generation + 1, (uint16_t)width, (uint16_t)height, 30, 0, (uint8_t)codec, 0, 4000000, 0}; bool opened = false; + uint32_t minimum = state.minimum_bps ? state.minimum_bps : 300000; + uint32_t maximum = state.maximum_bps ? state.maximum_bps : 4000000; + uint32_t initialRate = std::max(minimum, std::min(maximum, encoder.bitrate() ? encoder.bitrate() : 1000000)); for (int candidate = 2; candidate >= 1; --candidate) { if ((codec && codec != candidate) || (excluded & (1u << candidate))) continue; next.codec = candidate; next.level = next.profile = 0; - if (call_video_choose(&caps, &state.peer, &next) > 0 && encoder.open(next, 1000000)) { opened = true; break; } + if (call_video_choose(&caps, &state.peer, &next) > 0 && encoder.open(next, initialRate)) { opened = true; break; } } if (!opened) { m->report(tr("Selected video mode is unavailable to the peer")); @@ -240,7 +243,9 @@ void CallVideoEngine::receive() { sourceEpoch = clock.source_epoch; QImage selected; while (!pictures.empty() && pictures.front().pts <= clock.pts_us) { - if (pictures.front().pts > lastShown) { selected = pictures.front().image; lastShown = pictures.front().pts; } + if (pictures.front().pts > lastShown && pictures.front().pts >= clock.pts_us - 500000) { + selected = pictures.front().image; lastShown = pictures.front().pts; + } pictures.pop_front(); } if (!selected.isNull()) { std::lock_guard guard(m->lock); m->shown = selected; ++m->shownSerial; ++shown; }