diff options
| author | 2021-08-10 22:12:45 -0400 | |
|---|---|---|
| committer | 2021-08-16 14:40:53 -0400 | |
| commit | bc3efb79cc11a98005a9c036d9474fbf9cb7042f (patch) | |
| tree | 701ac4021fc689253cb71e1c5ec66a5a8b55f469 | |
| parent | cmake: Add VDPAU and NVDEC support to FFmpeg (diff) | |
| download | yuzu-bc3efb79cc11a98005a9c036d9474fbf9cb7042f.tar.gz yuzu-bc3efb79cc11a98005a9c036d9474fbf9cb7042f.tar.xz yuzu-bc3efb79cc11a98005a9c036d9474fbf9cb7042f.zip | |
codec: Fallback to CPU decoding if no compatible GPU format is found
| -rw-r--r-- | src/video_core/command_classes/codecs/codec.cpp | 53 | ||||
| -rw-r--r-- | src/video_core/command_classes/codecs/codec.h | 1 |
2 files changed, 32 insertions, 22 deletions
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index 0ad6162ca..400834129 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp | |||
| @@ -17,6 +17,9 @@ extern "C" { | |||
| 17 | 17 | ||
| 18 | namespace Tegra { | 18 | namespace Tegra { |
| 19 | namespace { | 19 | namespace { |
| 20 | constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; | ||
| 21 | constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; | ||
| 22 | |||
| 20 | AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) { | 23 | AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) { |
| 21 | for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { | 24 | for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { |
| 22 | if (*p == av_codec_ctx->pix_fmt) { | 25 | if (*p == av_codec_ctx->pix_fmt) { |
| @@ -24,7 +27,9 @@ AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pi | |||
| 24 | } | 27 | } |
| 25 | } | 28 | } |
| 26 | LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); | 29 | LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); |
| 27 | return AV_PIX_FMT_NONE; | 30 | av_buffer_unref(&av_codec_ctx->hw_device_ctx); |
| 31 | av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; | ||
| 32 | return PREFERRED_CPU_FMT; | ||
| 28 | } | 33 | } |
| 29 | } // namespace | 34 | } // namespace |
| 30 | 35 | ||
| @@ -83,7 +88,12 @@ bool Codec::CreateGpuAvDevice() { | |||
| 83 | #endif | 88 | #endif |
| 84 | }; | 89 | }; |
| 85 | for (const auto& type : GPU_DECODER_TYPES) { | 90 | for (const auto& type : GPU_DECODER_TYPES) { |
| 86 | av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); | 91 | const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); |
| 92 | if (hwdevice_res < 0) { | ||
| 93 | LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", | ||
| 94 | av_hwdevice_get_type_name(type), hwdevice_res); | ||
| 95 | continue; | ||
| 96 | } | ||
| 87 | for (int i = 0;; i++) { | 97 | for (int i = 0;; i++) { |
| 88 | const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); | 98 | const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); |
| 89 | if (!config) { | 99 | if (!config) { |
| @@ -110,36 +120,34 @@ void Codec::InitializeGpuDecoder() { | |||
| 110 | ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); | 120 | ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); |
| 111 | av_codec_ctx->hw_device_ctx = hw_device_ctx; | 121 | av_codec_ctx->hw_device_ctx = hw_device_ctx; |
| 112 | av_codec_ctx->get_format = GetGpuFormat; | 122 | av_codec_ctx->get_format = GetGpuFormat; |
| 113 | using_gpu_decode = true; | ||
| 114 | } | 123 | } |
| 115 | 124 | ||
| 116 | void Codec::Initialize() { | 125 | void Codec::Initialize() { |
| 117 | AVCodecID codec; | 126 | const AVCodecID codec = [&] { |
| 118 | switch (current_codec) { | 127 | switch (current_codec) { |
| 119 | case NvdecCommon::VideoCodec::H264: | 128 | case NvdecCommon::VideoCodec::H264: |
| 120 | codec = AV_CODEC_ID_H264; | 129 | return AV_CODEC_ID_H264; |
| 121 | break; | 130 | case NvdecCommon::VideoCodec::Vp9: |
| 122 | case NvdecCommon::VideoCodec::Vp9: | 131 | return AV_CODEC_ID_VP9; |
| 123 | codec = AV_CODEC_ID_VP9; | 132 | default: |
| 124 | break; | 133 | UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); |
| 125 | default: | 134 | return AV_CODEC_ID_NONE; |
| 126 | UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); | 135 | } |
| 127 | return; | 136 | }(); |
| 128 | } | ||
| 129 | av_codec = avcodec_find_decoder(codec); | 137 | av_codec = avcodec_find_decoder(codec); |
| 130 | av_codec_ctx = avcodec_alloc_context3(av_codec); | 138 | av_codec_ctx = avcodec_alloc_context3(av_codec); |
| 131 | av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); | 139 | av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); |
| 132 | 140 | ||
| 133 | InitializeGpuDecoder(); | 141 | InitializeGpuDecoder(); |
| 134 | if (!av_codec_ctx->hw_device_ctx) { | ||
| 135 | LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); | ||
| 136 | } | ||
| 137 | if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { | 142 | if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { |
| 138 | LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); | 143 | LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); |
| 139 | avcodec_close(av_codec_ctx); | 144 | avcodec_close(av_codec_ctx); |
| 140 | av_buffer_unref(&av_gpu_decoder); | 145 | av_buffer_unref(&av_gpu_decoder); |
| 141 | return; | 146 | return; |
| 142 | } | 147 | } |
| 148 | if (!av_codec_ctx->hw_device_ctx) { | ||
| 149 | LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); | ||
| 150 | } | ||
| 143 | initialized = true; | 151 | initialized = true; |
| 144 | } | 152 | } |
| 145 | 153 | ||
| @@ -155,6 +163,9 @@ void Codec::Decode() { | |||
| 155 | if (is_first_frame) { | 163 | if (is_first_frame) { |
| 156 | Initialize(); | 164 | Initialize(); |
| 157 | } | 165 | } |
| 166 | if (!initialized) { | ||
| 167 | return; | ||
| 168 | } | ||
| 158 | bool vp9_hidden_frame = false; | 169 | bool vp9_hidden_frame = false; |
| 159 | std::vector<u8> frame_data; | 170 | std::vector<u8> frame_data; |
| 160 | if (current_codec == NvdecCommon::VideoCodec::H264) { | 171 | if (current_codec == NvdecCommon::VideoCodec::H264) { |
| @@ -191,18 +202,18 @@ void Codec::Decode() { | |||
| 191 | LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); | 202 | LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); |
| 192 | return; | 203 | return; |
| 193 | } | 204 | } |
| 194 | if (using_gpu_decode) { | 205 | if (av_codec_ctx->hw_device_ctx) { |
| 195 | final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; | 206 | final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; |
| 196 | ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); | 207 | ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); |
| 197 | // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp | 208 | // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp |
| 198 | // because Intel drivers crash unless using AV_PIX_FMT_NV12 | 209 | // because Intel drivers crash unless using AV_PIX_FMT_NV12 |
| 199 | final_frame->format = AV_PIX_FMT_NV12; | 210 | final_frame->format = PREFERRED_GPU_FMT; |
| 200 | const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0); | 211 | const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0); |
| 201 | ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret); | 212 | ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret); |
| 202 | } else { | 213 | } else { |
| 203 | final_frame = std::move(initial_frame); | 214 | final_frame = std::move(initial_frame); |
| 204 | } | 215 | } |
| 205 | if (final_frame->format != AV_PIX_FMT_YUV420P && final_frame->format != AV_PIX_FMT_NV12) { | 216 | if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) { |
| 206 | UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); | 217 | UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); |
| 207 | return; | 218 | return; |
| 208 | } | 219 | } |
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h index abfe59221..f51ab9df0 100644 --- a/src/video_core/command_classes/codecs/codec.h +++ b/src/video_core/command_classes/codecs/codec.h | |||
| @@ -60,7 +60,6 @@ private: | |||
| 60 | bool CreateGpuAvDevice(); | 60 | bool CreateGpuAvDevice(); |
| 61 | 61 | ||
| 62 | bool initialized{}; | 62 | bool initialized{}; |
| 63 | bool using_gpu_decode{}; | ||
| 64 | NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; | 63 | NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; |
| 65 | 64 | ||
| 66 | AVCodec* av_codec{nullptr}; | 65 | AVCodec* av_codec{nullptr}; |