From 521dce5528013153d0cf4026fb076c2b82f3f4e7 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 20 Oct 2026 00:21:21 +0200 Subject: [PATCH] Use verified Android encoder levels and expose negotiated frame rate --- .../src/main/java/com/utun/chat/data/CallVideoEngine.kt | 4 +++- tools/chatgui/src/call_video_engine.cpp | 7 ++++--- tools/chatgui/tests/test_call_video_encoder.cpp | 7 +++++-- 3 files changed, 12 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 67f3d2fe..79048b06 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 @@ -283,7 +283,9 @@ class CallVideoEngine(private val context: Context, private val id: Long) { val selected = CallVideoNative.choose(caps, peer, desired.array()) ?: continue var candidate: CallVideoCodec? = null try { - candidate = CallVideoCodec(CallVideoFormat.from(selected), bitrate, implementation.name) + val verified = CallVideoFormat.from(selected).let { it.copy(level = maxOf(it.level, implementation.level)) } + check(CallVideoNative.supported(caps, peer, verified.array())) { "Verified encoder level exceeds peer capabilities" } + candidate = CallVideoCodec(verified, bitrate, implementation.name) candidate.prepare() check(CallVideoNative.supported(caps, peer, candidate.format.array())) { "Actual encoder format exceeds peer capabilities" } return candidate diff --git a/tools/chatgui/src/call_video_engine.cpp b/tools/chatgui/src/call_video_engine.cpp index d8b06d1c..05baa788 100644 --- a/tools/chatgui/src/call_video_engine.cpp +++ b/tools/chatgui/src/call_video_engine.cpp @@ -370,7 +370,8 @@ void CallVideoEngine::transmit() { } format = next; appliedRevision = revision; forceKey = true; request = 0; if (call_video_port_format(m->id, &format, encoder.bitrate()) < 0) break; - m->report(QString("%1 · %2×%3 · %4 kbit/s").arg(format.codec == 2 ? "H.265" : "H.264").arg(width).arg(height).arg(encoder.bitrate() / 1000)); + m->report(QString("%1 · %2×%3 · %4 fps · %5 kbit/s").arg(format.codec == 2 ? "H.265" : "H.264") + .arg(width).arg(height).arg(format.fps).arg(encoder.bitrate() / 1000)); } if (state.configured_generation != format.generation) { idle(); continue; } if (state.key_serial != keySerial) { keySerial = state.key_serial; forceKey = true; } @@ -393,8 +394,8 @@ void CallVideoEngine::transmit() { encodeMax = std::max(encodeMax, nowUs() - encodeStarted); if (completion.request) { call_video_port_applied(m->id, completion.generation, completion.request, completion.bitrate); - m->report(QString("%1 · %2×%3 · %4 kbit/s").arg(format.codec == 2 ? "H.265" : "H.264") - .arg(format.width).arg(format.height).arg(encoder.bitrate() / 1000)); + m->report(QString("%1 · %2×%3 · %4 fps · %5 kbit/s").arg(format.codec == 2 ? "H.265" : "H.264") + .arg(format.width).arg(format.height).arg(format.fps).arg(encoder.bitrate() / 1000)); } if (!encoded) { m->report(encoder.error()); diff --git a/tools/chatgui/tests/test_call_video_encoder.cpp b/tools/chatgui/tests/test_call_video_encoder.cpp index d96dd3ef..dbd8aad5 100644 --- a/tools/chatgui/tests/test_call_video_encoder.cpp +++ b/tools/chatgui/tests/test_call_video_encoder.cpp @@ -2,6 +2,7 @@ * один decoder на весь поток, непрерывные capture PTS и измеренный bitrate. */ #include #include +#include #include #include "../src/call_video_encoder.h" #include "../src/call_video_decoder.h" @@ -144,10 +145,12 @@ private slots: QVERIFY(!call_video_inspect_key(codec, reinterpret_cast(bytes.constData()), bytes.size(), &actual)); call_video_frame f{}; f.call_id = 1; f.flags = CALL_VIDEO_KEY; f.size = f.length = bytes.size(); f.data = reinterpret_cast(bytes.constData()); - f.format = {1, (uint16_t)actual.width, (uint16_t)actual.height, 30, 0, (uint8_t)codec, + const auto rate = QRegularExpression(QStringLiteral("-fps(30|50|60)-")).match(name); + const unsigned fps = rate.hasMatch() ? rate.captured(1).toUInt() : 30; + f.format = {1, (uint16_t)actual.width, (uint16_t)actual.height, (uint16_t)fps, 0, (uint8_t)codec, (uint8_t)actual.level, 4000000, (uint8_t)actual.profile}; CallVideoDecoder decoder(CallVideoDecoder::Backend::SoftwareOnly); - for (unsigned i = 0; i < 4; ++i) { f.frame_id = i + 1; f.pts_us = 1000000 + i * 33333; + for (unsigned i = 0; i < 8; ++i) { f.frame_id = i + 1; f.pts_us = 1000000 + i * 1000000ll / fps; QImage image; call_video_frame output{}; QCOMPARE(decoder.submit(f), 1); QCOMPARE(decoder.receive(output, image), 1); QCOMPARE(output.pts_us, f.pts_us); } DEBUG_INFO(DEBUG_CATEGORY_CALL, "hardware stream decoded file=%s", qPrintable(name));