Recover real GPU decoder failures on CPU without treating ordinary EAGAIN as frame loss

This commit is contained in:
evgeny committed 2026-10-19 19:14:03 +02:00
1 parent f9348ce36f
commit a4a687be4c
3 files changed
+25 -5

No files matched your search

+7
View File
@@ -82,6 +82,11 @@ unsigned CallVideoDecoder::pendingFrames() const { return static_cast<unsigned>(
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);
+1
View File
@@ -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:
+17 -5
View File
@@ -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 = {};
}