mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
codec: Fallback to CPU decoding if no compatible GPU format is found
This commit is contained in:
parent
92bc51b66a
commit
bc3efb79cc
2 changed files with 32 additions and 22 deletions
|
@ -17,6 +17,9 @@ extern "C" {
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
namespace {
|
namespace {
|
||||||
|
constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
|
||||||
|
constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
|
||||||
|
|
||||||
AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
|
AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
|
||||||
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
|
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
|
||||||
if (*p == av_codec_ctx->pix_fmt) {
|
if (*p == av_codec_ctx->pix_fmt) {
|
||||||
|
@ -24,7 +27,9 @@ AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
|
LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
|
||||||
return AV_PIX_FMT_NONE;
|
av_buffer_unref(&av_codec_ctx->hw_device_ctx);
|
||||||
|
av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
|
||||||
|
return PREFERRED_CPU_FMT;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -83,7 +88,12 @@ bool Codec::CreateGpuAvDevice() {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
for (const auto& type : GPU_DECODER_TYPES) {
|
for (const auto& type : GPU_DECODER_TYPES) {
|
||||||
av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
|
const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
|
||||||
|
if (hwdevice_res < 0) {
|
||||||
|
LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
|
||||||
|
av_hwdevice_get_type_name(type), hwdevice_res);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (int i = 0;; i++) {
|
for (int i = 0;; i++) {
|
||||||
const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
|
const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
|
||||||
if (!config) {
|
if (!config) {
|
||||||
|
@ -110,36 +120,34 @@ void Codec::InitializeGpuDecoder() {
|
||||||
ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
|
ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
|
||||||
av_codec_ctx->hw_device_ctx = hw_device_ctx;
|
av_codec_ctx->hw_device_ctx = hw_device_ctx;
|
||||||
av_codec_ctx->get_format = GetGpuFormat;
|
av_codec_ctx->get_format = GetGpuFormat;
|
||||||
using_gpu_decode = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Codec::Initialize() {
|
void Codec::Initialize() {
|
||||||
AVCodecID codec;
|
const AVCodecID codec = [&] {
|
||||||
switch (current_codec) {
|
switch (current_codec) {
|
||||||
case NvdecCommon::VideoCodec::H264:
|
case NvdecCommon::VideoCodec::H264:
|
||||||
codec = AV_CODEC_ID_H264;
|
return AV_CODEC_ID_H264;
|
||||||
break;
|
|
||||||
case NvdecCommon::VideoCodec::Vp9:
|
case NvdecCommon::VideoCodec::Vp9:
|
||||||
codec = AV_CODEC_ID_VP9;
|
return AV_CODEC_ID_VP9;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
|
UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
|
||||||
return;
|
return AV_CODEC_ID_NONE;
|
||||||
}
|
}
|
||||||
|
}();
|
||||||
av_codec = avcodec_find_decoder(codec);
|
av_codec = avcodec_find_decoder(codec);
|
||||||
av_codec_ctx = avcodec_alloc_context3(av_codec);
|
av_codec_ctx = avcodec_alloc_context3(av_codec);
|
||||||
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
|
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
|
||||||
|
|
||||||
InitializeGpuDecoder();
|
InitializeGpuDecoder();
|
||||||
if (!av_codec_ctx->hw_device_ctx) {
|
|
||||||
LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
|
|
||||||
}
|
|
||||||
if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
|
if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
|
||||||
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res);
|
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res);
|
||||||
avcodec_close(av_codec_ctx);
|
avcodec_close(av_codec_ctx);
|
||||||
av_buffer_unref(&av_gpu_decoder);
|
av_buffer_unref(&av_gpu_decoder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!av_codec_ctx->hw_device_ctx) {
|
||||||
|
LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
|
||||||
|
}
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +163,9 @@ void Codec::Decode() {
|
||||||
if (is_first_frame) {
|
if (is_first_frame) {
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
if (!initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
bool vp9_hidden_frame = false;
|
bool vp9_hidden_frame = false;
|
||||||
std::vector<u8> frame_data;
|
std::vector<u8> frame_data;
|
||||||
if (current_codec == NvdecCommon::VideoCodec::H264) {
|
if (current_codec == NvdecCommon::VideoCodec::H264) {
|
||||||
|
@ -191,18 +202,18 @@ void Codec::Decode() {
|
||||||
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
|
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (using_gpu_decode) {
|
if (av_codec_ctx->hw_device_ctx) {
|
||||||
final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
|
final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
|
||||||
ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
|
ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
|
||||||
// Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
|
// Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
|
||||||
// because Intel drivers crash unless using AV_PIX_FMT_NV12
|
// because Intel drivers crash unless using AV_PIX_FMT_NV12
|
||||||
final_frame->format = AV_PIX_FMT_NV12;
|
final_frame->format = PREFERRED_GPU_FMT;
|
||||||
const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
|
const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
|
||||||
ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
|
ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
|
||||||
} else {
|
} else {
|
||||||
final_frame = std::move(initial_frame);
|
final_frame = std::move(initial_frame);
|
||||||
}
|
}
|
||||||
if (final_frame->format != AV_PIX_FMT_YUV420P && final_frame->format != AV_PIX_FMT_NV12) {
|
if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) {
|
||||||
UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
|
UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,6 @@ private:
|
||||||
bool CreateGpuAvDevice();
|
bool CreateGpuAvDevice();
|
||||||
|
|
||||||
bool initialized{};
|
bool initialized{};
|
||||||
bool using_gpu_decode{};
|
|
||||||
NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
|
NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
|
||||||
|
|
||||||
AVCodec* av_codec{nullptr};
|
AVCodec* av_codec{nullptr};
|
||||||
|
|
Loading…
Reference in a new issue