[Video]: Add timeout protection for hardware frame transfer

This commit is contained in:
liberodark 2025-12-06 16:25:30 +01:00
parent 9847eeb2af
commit 1289ee2e98
2 changed files with 32 additions and 6 deletions

View file

@ -4,6 +4,9 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include <future>
#include <chrono>
#include <thread>
#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<Frame> 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<Frame> DecoderContext::ReceiveFrame() {
}
m_temp_frame->SetFormat(PreferredGpuFormat);
if (const int ret = av_hwframe_transfer_data(m_temp_frame->GetFrame(),
// 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);
ret < 0) {
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
});
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 {};
}

View file

@ -8,6 +8,7 @@
#include <span>
#include <vector>
#include <queue>
#include <atomic>
#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<Frame> m_temp_frame{};
std::atomic<bool> m_hw_decoder_failed{false};
bool m_decode_order{};
};