From a4a687be4cae20c3a2a0896cbdddac12b135f2c3 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 19 Oct 2026 19:14:03 +0200 Subject: [PATCH] Recover real GPU decoder failures on CPU without treating ordinary EAGAIN as frame loss --- tools/chatgui/src/call_video_decoder.cpp | 7 +++++++ tools/chatgui/src/call_video_decoder.h | 1 + tools/chatgui/src/call_video_engine.cpp | 22 +++++++++++++++++----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/tools/chatgui/src/call_video_decoder.cpp b/tools/chatgui/src/call_video_decoder.cpp index 205c7631..6a488707 100644 --- a/tools/chatgui/src/call_video_decoder.cpp +++ b/tools/chatgui/src/call_video_decoder.cpp @@ -82,6 +82,11 @@ unsigned CallVideoDecoder::pendingFrames() const { return static_cast( unsigned CallVideoDecoder::pipelineFrames() const { return m->pipeline; } bool CallVideoDecoder::hardware() const { return m->gpu; } QString CallVideoDecoder::error() const { return m->error; } +void CallVideoDecoder::useSoftware() { + DEBUG_WARN(DEBUG_CATEGORY_CALL, "video decoder switching to CPU after device failure pending=%zu; next input must be KEY", + m->pending.size()); + close(); m->backend = Backend::SoftwareOnly; +} void CallVideoDecoder::close() { #ifdef HAVE_FFMPEG if (m->codec) DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "video decoder close generation=%llu pending=%zu eos=%d", @@ -107,6 +112,8 @@ int CallVideoDecoder::submit(const call_video_frame& f) { #ifdef HAVE_FFMPEG if (!call_video_format_valid(&f.format) || !f.data || !f.size || f.size > CALL_VIDEO_MAX_FRAME) return m->failed("input format", AVERROR_INVALIDDATA); + if (!m->pending.empty() && f.pts_us <= m->pending.back().pts_us) + return m->failed("non-monotonic input PTS", AVERROR_INVALIDDATA); const uint8_t* data = f.data; unsigned size = f.size; if (f.flags & CALL_VIDEO_KEY) { if (call_video_check_key(&f.format, data, size)) return m->failed("KEY bitstream", AVERROR_INVALIDDATA); diff --git a/tools/chatgui/src/call_video_decoder.h b/tools/chatgui/src/call_video_decoder.h index b5a35d77..a3eba27d 100644 --- a/tools/chatgui/src/call_video_decoder.h +++ b/tools/chatgui/src/call_video_decoder.h @@ -19,6 +19,7 @@ public: unsigned pipelineFrames() const; // Минимальный входной pipeline, измеренный отдельной paced пробой. bool hardware() const; QString error() const; + void useSoftware(); // После реального отказа устройства: следующий KEY открывает CPU, без повторной GPU пробы. void close(); static call_video_caps capabilities(); private: diff --git a/tools/chatgui/src/call_video_engine.cpp b/tools/chatgui/src/call_video_engine.cpp index 626b6b9b..bf36594e 100644 --- a/tools/chatgui/src/call_video_engine.cpp +++ b/tools/chatgui/src/call_video_engine.cpp @@ -385,7 +385,7 @@ void CallVideoEngine::receive() { unsigned submitted = 0, acknowledged = 0; uint64_t reportedGeneration = 0, rejectedGeneration = 0; call_video_frame input{}; - bool wakeBound = false; + bool wakeBound = false, waitingKey = false; while (m->running) { const uint64_t ticket = m->rxWake.ticket(); if (requestedWake) wakeLateMax = std::max(wakeLateMax, nowUs() - requestedWake); @@ -416,7 +416,11 @@ void CallVideoEngine::receive() { const int64_t started = nowUs(); int result = decoder.receive(packet, image); decodeMax = std::max(decodeMax, nowUs() - started); if (result < 0) { - m->report(decoder.error()); decoder.close(); call_video_port_request_key(m->id); break; + DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video decoder recovery call=%llu lost_inputs=%u retained_ready=%zu awaiting=%d error=%s", + (unsigned long long)m->id, decoder.pendingFrames(), pictures.size(), playout.awaiting, qPrintable(decoder.error())); + m->report(decoder.error()); + if (decoder.hardware()) decoder.useSoftware(); else decoder.close(); + waitingKey = true; call_video_port_request_key(m->id); break; } if (!result) break; ++decoded; worked = true; rxAgeMax = std::max(rxAgeMax, nowUs() - packet.received_us); @@ -428,15 +432,23 @@ void CallVideoEngine::receive() { if (pictures.size() + playout.awaiting < 3 && decoder.pendingFrames() < 8) { if (!input.data) call_video_port_pull(m->id, &input); if (input.data) { + if (input.format.generation == rejectedGeneration || (waitingKey && !(input.flags & CALL_VIDEO_KEY))) { + u_free((void*)input.data); input = {}; requestedWake = 0; continue; + } const int64_t started = nowUs(); int result = decoder.submit(input); decodeMax = std::max(decodeMax, nowUs() - started); if (result) { worked = true; - if (result > 0) ++submitted; + if (result > 0) { ++submitted; waitingKey = false; } else { - rejectedGeneration = input.format.generation; - call_video_port_decoded(m->id, rejectedGeneration, 0); m->report(decoder.error()); decoder.close(); + m->report(decoder.error()); + if (decoder.hardware()) decoder.useSoftware(); + else { + rejectedGeneration = input.format.generation; + call_video_port_decoded(m->id, rejectedGeneration, 0); decoder.close(); + } + waitingKey = true; call_video_port_request_key(m->id); } u_free((void*)input.data); input = {}; }