[Video]: Add timeout protection for hardware frame transfer
This commit is contained in:
parent
9847eeb2af
commit
8c58d15dba
2 changed files with 34 additions and 8 deletions
|
|
@ -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,24 +445,43 @@ 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;
|
||||
auto intermediate_frame = std::make_shared<Frame>();
|
||||
|
||||
if (!ReceiveImpl(intermediate_frame.GetFrame())) {
|
||||
if (!ReceiveImpl(intermediate_frame->GetFrame())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
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, [this, intermediate_frame]() {
|
||||
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 {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{};
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue