From 322947777cd096576d9fa3ec5894306baba6a93e Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 19 Oct 2026 17:32:49 +0200 Subject: [PATCH] Stop invalid encoder output explicitly while preserving transient backpressure retries --- tools/chatgui/src/call_video_encoder.cpp | 1 + tools/chatgui/src/call_video_engine.cpp | 6 +++++- tools/chatgui/tests/test_call_video_gpu.cpp | 14 +++++++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/tools/chatgui/src/call_video_encoder.cpp b/tools/chatgui/src/call_video_encoder.cpp index 9a19a030..d30c8b16 100644 --- a/tools/chatgui/src/call_video_encoder.cpp +++ b/tools/chatgui/src/call_video_encoder.cpp @@ -123,6 +123,7 @@ bool CallVideoEncoder::encode(const QImage& image, uint64_t pts, bool forceKey, } if (m_switching && frame.pts == m_switchPts) { if (!frame.key) { m_error = "Replacement encoder did not start with KEY"; + completion = {m_format.generation, m_request, 0}; DEBUG_ERROR(DEBUG_CATEGORY_CALL, "%s", qPrintable(m_error)); return false; } completion = {m_format.generation, m_request, m_pendingBitrate}; DEBUG_INFO(DEBUG_CATEGORY_CALL, "call video bitrate committed generation=%llu request=%llu bitrate=%u pts=%llu background_ms=%lld", diff --git a/tools/chatgui/src/call_video_engine.cpp b/tools/chatgui/src/call_video_engine.cpp index dd9e6813..34d31311 100644 --- a/tools/chatgui/src/call_video_engine.cpp +++ b/tools/chatgui/src/call_video_engine.cpp @@ -307,7 +307,11 @@ void CallVideoEngine::transmit() { 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)); } - if (!encoded) { forceKey = true; m->report(encoder.error()); continue; } + if (!encoded) { + m->report(encoder.error()); + std::lock_guard guard(m->lock); m->enabled = false; + continue; + } // GPU может вернуть несколько уже готовых кадров: отправляем все, не добавляя очередь в TX worker. if (outputs.isEmpty()) { idle(); continue; } if (outputs.size() > CALL_VIDEO_PORT_TX_FRAMES) { diff --git a/tools/chatgui/tests/test_call_video_gpu.cpp b/tools/chatgui/tests/test_call_video_gpu.cpp index 244337e4..db077b0b 100644 --- a/tools/chatgui/tests/test_call_video_gpu.cpp +++ b/tools/chatgui/tests/test_call_video_gpu.cpp @@ -127,7 +127,7 @@ private slots: for (auto name : {"h264_nvenc", "hevc_nvenc", "h264_amf", "hevc_amf", "h264_qsv", "hevc_qsv"}) QTest::newRow(name) << QByteArray(name) << false << false; QTest::newRow("batch") << QByteArray("h264_nvenc") << true << false; - QTest::newRow("rollback") << QByteArray("h264_nvenc") << false << true; + QTest::newRow("broken-first-key") << QByteArray("h264_nvenc") << false << true; } void pipeline() { QFETCH(QByteArray, backend); QFETCH(bool, batch); QFETCH(bool, reject); @@ -143,7 +143,12 @@ private slots: if (i == 7) { request = 1; QVERIFY(encoder.requestBitrate(1, request, 300000)); } if (request && !completed && !failed) QTest::qWait(5); const uint64_t pts = 1234567 + i * 33333ull; QList output; CallVideoRateCompletion completion; - QVERIFY2(encoder.encode(image, pts, false, output, completion), qPrintable(encoder.error())); + if (!encoder.encode(image, pts, false, output, completion)) { + QVERIFY2(reject, qPrintable(encoder.error())); + QVERIFY(encoder.error().contains("did not start with KEY")); + QCOMPARE(completion.request, request); QCOMPARE(completion.bitrate, 0u); + QVERIFY(output.isEmpty()); failed = true; break; + } if (completion.request) { QCOMPARE(completion.request, request); QCOMPARE(completion.bitrate, reject ? 0u : 300000u); completed = completion.bitrate != 0; failed = !completed; @@ -161,9 +166,8 @@ private slots: last = frame.pts; } } - QVERIFY(shown >= 115); QCOMPARE(completed, !reject); QCOMPARE(failed, reject); - QCOMPARE(shown + encoder.pendingFrames(), 120u); - QCOMPARE(encoder.bitrate(), reject ? 1000000u : 300000u); + QCOMPARE(completed, !reject); QCOMPARE(failed, reject); + if (!reject) { QCOMPARE(shown + encoder.pendingFrames(), 120u); QCOMPARE(encoder.bitrate(), 300000u); } if (batch) QVERIFY(maximumBatch >= 2); qInfo("GPU pipeline %s shown=%u max_batch=%u rollback=%d", selected.constData(), shown, maximumBatch, failed); }