From 0e53a9f5dd3cb8a21f9adb8b5fad9b2757900972 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 19 Oct 2026 17:31:15 +0200 Subject: [PATCH] Log retained video batches and verify every GPU input across bitrate changes --- src/call/call_video_port.c | 13 +++++++++++++ tools/chatgui/tests/test_call_video_gpu.cpp | 13 ++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/call/call_video_port.c b/src/call/call_video_port.c index 2ba0a943..ff897831 100644 --- a/src/call/call_video_port.c +++ b/src/call/call_video_port.c @@ -27,6 +27,8 @@ static struct { struct call_video_frame tx[CALL_VIDEO_PORT_TX_FRAMES], rx[8]; unsigned tx_count; int tx_processing; + int64_t tx_wait_started; + unsigned tx_retries; unsigned count, bytes; uint64_t decoded_generation; int decoded_pending, decoded_accepted; @@ -43,6 +45,7 @@ static int schedule(void) { static void clear_frames(void) { for (unsigned i = 0; i < port.tx_count; ++i) u_free((void *)port.tx[i].data); port.tx_count = 0; + port.tx_wait_started = 0; port.tx_retries = 0; for (unsigned i = 0; i < port.count; ++i) u_free((void *)port.rx[i].data); port.count = port.bytes = 0; } @@ -170,6 +173,16 @@ static void process(void *arg) { if (!sent && active) { port.tx_count = tx_count - sent_count; memcpy(port.tx, tx + sent_count, port.tx_count * sizeof(*tx)); + if (!port.tx_wait_started) { + port.tx_wait_started = call_video_playout_now_us(); + DEBUG_WARN(DEBUG_CATEGORY_CALL, "video port TX paused call=%llu retained=%u first=%llu; chain preserved", + (unsigned long long)id, port.tx_count, (unsigned long long)port.tx[0].frame_id); + } + port.tx_retries++; + } else if (port.tx_wait_started) { + DEBUG_INFO(DEBUG_CATEGORY_CALL, "video port TX resumed call=%llu wait_us=%lld retries=%u result=%d", + (unsigned long long)id, (long long)(call_video_playout_now_us() - port.tx_wait_started), port.tx_retries, sent); + port.tx_wait_started = 0; port.tx_retries = 0; } if (sent < 0) ++port.status.key_serial; } diff --git a/tools/chatgui/tests/test_call_video_gpu.cpp b/tools/chatgui/tests/test_call_video_gpu.cpp index f465fa98..244337e4 100644 --- a/tools/chatgui/tests/test_call_video_gpu.cpp +++ b/tools/chatgui/tests/test_call_video_gpu.cpp @@ -17,6 +17,7 @@ extern "C" { struct Pending { std::deque packets; unsigned inputs = 0, serial = 0; + bool draining = false; }; static std::mutex lock; static std::map contexts; @@ -74,7 +75,10 @@ extern "C" int __wrap_avcodec_open2(AVCodecContext* context, const AVCodec* code extern "C" int __real_avcodec_send_frame(AVCodecContext*, const AVFrame*); extern "C" int __wrap_avcodec_send_frame(AVCodecContext* context, const AVFrame* frame) { int result = __real_avcodec_send_frame(context, frame); - if (result >= 0 && frame) { std::lock_guard guard(lock); ++contexts[context].inputs; } + if (result >= 0) { + std::lock_guard guard(lock); + if (frame) ++contexts[context].inputs; else contexts[context].draining = true; + } return result; } extern "C" int __real_avcodec_receive_packet(AVCodecContext*, AVPacket*); @@ -87,8 +91,9 @@ extern "C" int __wrap_avcodec_receive_packet(AVCodecContext* context, AVPacket* if (badReplacement && state.serial >= 4) packet->flags &= ~AV_PKT_FLAG_KEY; state.packets.push_back(packet); } else av_packet_free(&packet); - if (result < 0 && result != AVERROR(EAGAIN)) return result; - bool ready = burst ? state.inputs % 3 == 0 : state.packets.size() > size_t(depth); + if (result < 0 && result != AVERROR(EAGAIN) && result != AVERROR_EOF) return result; + if (result == AVERROR_EOF && state.packets.empty()) return result; + bool ready = state.draining || (burst ? state.inputs % 3 == 0 : state.packets.size() > size_t(depth)); if (!ready || state.packets.empty()) return AVERROR(EAGAIN); av_packet_move_ref(output, state.packets.front()); av_packet_free(&state.packets.front()); state.packets.pop_front(); return 0; } @@ -145,6 +150,7 @@ private slots: } maximumBatch = std::max(maximumBatch, unsigned(output.size())); for (const auto& frame : output) { + QCOMPARE(frame.pts, 1234567 + shown * 33333ull); // Ни одного потерянного PTS при замене encoder. QVERIFY(frame.pts > last); QVERIFY(frame.pts <= pts); QCOMPARE((frame.pts - 1234567) % 33333, uint64_t(0)); QVERIFY(pts - frame.pts <= (batch ? 2u : 1u) * 33333); @@ -156,6 +162,7 @@ private slots: } } QVERIFY(shown >= 115); QCOMPARE(completed, !reject); QCOMPARE(failed, reject); + QCOMPARE(shown + encoder.pendingFrames(), 120u); QCOMPARE(encoder.bitrate(), reject ? 1000000u : 300000u); if (batch) QVERIFY(maximumBatch >= 2); qInfo("GPU pipeline %s shown=%u max_batch=%u rollback=%d", selected.constData(), shown, maximumBatch, failed);