From 1289ee2e98f9466047eed83df71eafe56e0b79df Mon Sep 17 00:00:00 2001 From: liberodark Date: Sat, 6 Dec 2025 16:25:30 +0100 Subject: [PATCH] [Video]: Add timeout protection for hardware frame transfer --- src/video_core/host1x/ffmpeg/ffmpeg.cpp | 36 ++++++++++++++++++++----- src/video_core/host1x/ffmpeg/ffmpeg.h | 2 ++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp index 6655399c6..22096d38c 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp +++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp @@ -4,6 +4,9 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/scope_exit.h" +#include +#include +#include #include "common/settings.h" #include "core/memory.h" #include "video_core/host1x/ffmpeg/ffmpeg.h" @@ -22,6 +25,8 @@ namespace { constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12; constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P; +// Timeout for hardware frame transfer to prevent driver freezes +constexpr auto HW_TRANSFER_TIMEOUT_MS = std::chrono::milliseconds(500); constexpr std::array PreferredGpuDecoders = { AV_HWDEVICE_TYPE_CUDA, #ifdef _WIN32 @@ -440,7 +445,7 @@ std::shared_ptr DecoderContext::ReceiveFrame() { return true; }; - if (m_codec_context->hw_device_ctx) { + if (m_codec_context->hw_device_ctx && !m_hw_decoder_failed.load()) { // If we have a hardware context, make a separate frame here to receive the // hardware result before sending it to the output. Frame intermediate_frame; @@ -450,14 +455,33 @@ std::shared_ptr DecoderContext::ReceiveFrame() { } m_temp_frame->SetFormat(PreferredGpuFormat); - if (const int ret = av_hwframe_transfer_data(m_temp_frame->GetFrame(), - intermediate_frame.GetFrame(), 0); - ret < 0) { - LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret)); + + // Execute hardware transfer with timeout to prevent driver freezes + auto transfer_future = std::async(std::launch::async, [&]() { + return av_hwframe_transfer_data(m_temp_frame->GetFrame(), + intermediate_frame.GetFrame(), 0); + }); + + const auto status = transfer_future.wait_for(HW_TRANSFER_TIMEOUT_MS); + + if (status == std::future_status::timeout) { + LOG_ERROR(HW_GPU, + "av_hwframe_transfer_data timed out after {}ms, disabling hardware decoder", + HW_TRANSFER_TIMEOUT_MS.count()); + m_hw_decoder_failed.store(true); + return {}; + } + + const int ret = transfer_future.get(); + if (ret < 0) { + LOG_ERROR(HW_GPU, + "av_hwframe_transfer_data error: {}, falling back to software decoder", + AVError(ret)); + m_hw_decoder_failed.store(true); return {}; } } else { - // Otherwise, decode the frame as normal. + // Otherwise, decode the frame as normal (or HW decoder failed). if (!ReceiveImpl(m_temp_frame->GetFrame())) { return {}; } diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.h b/src/video_core/host1x/ffmpeg/ffmpeg.h index bd0ad97ad..b65a13fb1 100644 --- a/src/video_core/host1x/ffmpeg/ffmpeg.h +++ b/src/video_core/host1x/ffmpeg/ffmpeg.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "common/common_funcs.h" #include "common/common_types.h" @@ -190,6 +191,7 @@ private: AVCodecContext* m_codec_context{}; s32 m_got_frame{}; std::shared_ptr m_temp_frame{}; + std::atomic m_hw_decoder_failed{false}; bool m_decode_order{}; };