summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp154
-rw-r--r--src/video_core/command_classes/codecs/codec.h8
2 files changed, 92 insertions, 70 deletions
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index f798a0053..e4ee63e31 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -16,44 +16,17 @@ extern "C" {
16} 16}
17 17
18namespace Tegra { 18namespace Tegra {
19#if defined(LIBVA_FOUND)
20// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c originally under MIT license
21namespace { 19namespace {
22constexpr std::array<const char*, 2> VAAPI_DRIVERS = { 20AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
23 "i915",
24 "amdgpu",
25};
26
27AVPixelFormat GetHwFormat(AVCodecContext*, const AVPixelFormat* pix_fmts) {
28 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { 21 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
29 if (*p == AV_PIX_FMT_VAAPI) { 22 if (*p == av_codec_ctx->pix_fmt) {
30 return AV_PIX_FMT_VAAPI; 23 return av_codec_ctx->pix_fmt;
31 } 24 }
32 } 25 }
33 LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); 26 LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
34 return *pix_fmts; 27 return AV_PIX_FMT_NONE;
35}
36
37bool CreateVaapiHwdevice(AVBufferRef** av_hw_device) {
38 AVDictionary* hwdevice_options = nullptr;
39 av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
40 for (const auto& driver : VAAPI_DRIVERS) {
41 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
42 const int hwdevice_error = av_hwdevice_ctx_create(av_hw_device, AV_HWDEVICE_TYPE_VAAPI,
43 nullptr, hwdevice_options, 0);
44 if (hwdevice_error >= 0) {
45 LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
46 av_dict_free(&hwdevice_options);
47 return true;
48 }
49 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
50 }
51 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
52 av_dict_free(&hwdevice_options);
53 return false;
54} 28}
55} // namespace 29} // namespace
56#endif
57 30
58void AVFrameDeleter(AVFrame* ptr) { 31void AVFrameDeleter(AVFrame* ptr) {
59 av_frame_free(&ptr); 32 av_frame_free(&ptr);
@@ -69,26 +42,75 @@ Codec::~Codec() {
69 } 42 }
70 // Free libav memory 43 // Free libav memory
71 avcodec_send_packet(av_codec_ctx, nullptr); 44 avcodec_send_packet(av_codec_ctx, nullptr);
72 AVFrame* av_frame = av_frame_alloc(); 45 AVFramePtr av_frame{av_frame_alloc(), AVFrameDeleter};
73 avcodec_receive_frame(av_codec_ctx, av_frame); 46 avcodec_receive_frame(av_codec_ctx, av_frame.get());
74 avcodec_flush_buffers(av_codec_ctx); 47 avcodec_flush_buffers(av_codec_ctx);
75 av_frame_free(&av_frame);
76 avcodec_close(av_codec_ctx); 48 avcodec_close(av_codec_ctx);
77 av_buffer_unref(&av_hw_device); 49 av_buffer_unref(&av_gpu_decoder);
78} 50}
79 51
80void Codec::InitializeHwdec() { 52bool Codec::CreateGpuAvDevice() {
81 // Prioritize integrated GPU to mitigate bandwidth bottlenecks
82#if defined(LIBVA_FOUND) 53#if defined(LIBVA_FOUND)
83 if (CreateVaapiHwdevice(&av_hw_device)) { 54 static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
84 const auto hw_device_ctx = av_buffer_ref(av_hw_device); 55 "i915",
85 ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); 56 "iHD",
86 av_codec_ctx->hw_device_ctx = hw_device_ctx; 57 "amdgpu",
87 av_codec_ctx->get_format = GetHwFormat; 58 };
88 return; 59 AVDictionary* hwdevice_options = nullptr;
60 av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
61 for (const auto& driver : VAAPI_DRIVERS) {
62 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
63 const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
64 nullptr, hwdevice_options, 0);
65 if (hwdevice_error >= 0) {
66 LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
67 av_dict_free(&hwdevice_options);
68 av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI;
69 return true;
70 }
71 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
89 } 72 }
73 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
74 av_dict_free(&hwdevice_options);
75#endif
76 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
77 static constexpr std::array GPU_DECODER_TYPES{
78 AV_HWDEVICE_TYPE_CUDA,
79#ifdef _WIN32
80 AV_HWDEVICE_TYPE_D3D11VA,
81#else
82 AV_HWDEVICE_TYPE_VDPAU,
90#endif 83#endif
91 // TODO more GPU accelerated decoders 84 };
85 for (const auto& type : GPU_DECODER_TYPES) {
86 av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
87 for (int i = 0;; i++) {
88 const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
89 if (!config) {
90 LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.",
91 av_codec->name, av_hwdevice_get_type_name(type));
92 break;
93 }
94 if (config->methods & HW_CONFIG_METHOD && config->device_type == type) {
95 av_codec_ctx->pix_fmt = config->pix_fmt;
96 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
97 return true;
98 }
99 }
100 }
101 return false;
102}
103
104void Codec::InitializeGpuDecoder() {
105 if (!CreateGpuAvDevice()) {
106 av_buffer_unref(&av_gpu_decoder);
107 return;
108 }
109 auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder);
110 ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
111 av_codec_ctx->hw_device_ctx = hw_device_ctx;
112 av_codec_ctx->get_format = GetGpuFormat;
113 using_gpu_decode = true;
92} 114}
93 115
94void Codec::Initialize() { 116void Codec::Initialize() {
@@ -107,7 +129,8 @@ void Codec::Initialize() {
107 av_codec = avcodec_find_decoder(codec); 129 av_codec = avcodec_find_decoder(codec);
108 av_codec_ctx = avcodec_alloc_context3(av_codec); 130 av_codec_ctx = avcodec_alloc_context3(av_codec);
109 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); 131 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
110 InitializeHwdec(); 132
133 InitializeGpuDecoder();
111 if (!av_codec_ctx->hw_device_ctx) { 134 if (!av_codec_ctx->hw_device_ctx) {
112 LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); 135 LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
113 } 136 }
@@ -115,7 +138,7 @@ void Codec::Initialize() {
115 if (av_error < 0) { 138 if (av_error < 0) {
116 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed."); 139 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
117 avcodec_close(av_codec_ctx); 140 avcodec_close(av_codec_ctx);
118 av_buffer_unref(&av_hw_device); 141 av_buffer_unref(&av_gpu_decoder);
119 return; 142 return;
120 } 143 }
121 initialized = true; 144 initialized = true;
@@ -153,38 +176,33 @@ void Codec::Decode() {
153 if (vp9_hidden_frame) { 176 if (vp9_hidden_frame) {
154 return; 177 return;
155 } 178 }
156 AVFrame* hw_frame = av_frame_alloc(); 179 AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter};
157 AVFrame* sw_frame = hw_frame; 180 AVFramePtr final_frame{nullptr, AVFrameDeleter};
158 ASSERT_MSG(hw_frame, "av_frame_alloc hw_frame failed"); 181 ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed");
159 if (const int ret = avcodec_receive_frame(av_codec_ctx, hw_frame); ret) { 182 if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) {
160 LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret); 183 LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
161 av_frame_free(&hw_frame);
162 return; 184 return;
163 } 185 }
164 if (!hw_frame->width || !hw_frame->height) { 186 if (initial_frame->width == 0 || initial_frame->height == 0) {
165 LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); 187 LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
166 av_frame_free(&hw_frame);
167 return; 188 return;
168 } 189 }
169#if defined(LIBVA_FOUND) 190 if (using_gpu_decode) {
170 // Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c under MIT license 191 final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
171 if (hw_frame->format == AV_PIX_FMT_VAAPI) { 192 ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
172 sw_frame = av_frame_alloc();
173 ASSERT_MSG(sw_frame, "av_frame_alloc sw_frame failed");
174 // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp 193 // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
175 // because Intel drivers crash unless using AV_PIX_FMT_NV12 194 // because Intel drivers crash unless using AV_PIX_FMT_NV12
176 sw_frame->format = AV_PIX_FMT_NV12; 195 final_frame->format = AV_PIX_FMT_NV12;
177 const int transfer_data_ret = av_hwframe_transfer_data(sw_frame, hw_frame, 0); 196 const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
178 ASSERT_MSG(!transfer_data_ret, "av_hwframe_transfer_data error {}", transfer_data_ret); 197 ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
179 av_frame_free(&hw_frame); 198 } else {
180 } 199 final_frame = std::move(initial_frame);
181#endif 200 }
182 if (sw_frame->format != AV_PIX_FMT_YUV420P && sw_frame->format != AV_PIX_FMT_NV12) { 201 if (final_frame->format != AV_PIX_FMT_YUV420P && final_frame->format != AV_PIX_FMT_NV12) {
183 UNIMPLEMENTED_MSG("Unexpected video format from host graphics: {}", sw_frame->format); 202 UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
184 av_frame_free(&sw_frame);
185 return; 203 return;
186 } 204 }
187 av_frames.push(AVFramePtr{sw_frame, AVFrameDeleter}); 205 av_frames.push(std::move(final_frame));
188 if (av_frames.size() > 10) { 206 if (av_frames.size() > 10) {
189 LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); 207 LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
190 av_frames.pop(); 208 av_frames.pop();
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
index 71936203f..abfe59221 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -50,18 +50,22 @@ public:
50 50
51 /// Returns the value of current_codec 51 /// Returns the value of current_codec
52 [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; 52 [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const;
53
53 /// Return name of the current codec 54 /// Return name of the current codec
54 [[nodiscard]] std::string_view GetCurrentCodecName() const; 55 [[nodiscard]] std::string_view GetCurrentCodecName() const;
55 56
56private: 57private:
57 void InitializeHwdec(); 58 void InitializeGpuDecoder();
59
60 bool CreateGpuAvDevice();
58 61
59 bool initialized{}; 62 bool initialized{};
63 bool using_gpu_decode{};
60 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; 64 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
61 65
62 AVCodec* av_codec{nullptr}; 66 AVCodec* av_codec{nullptr};
63 AVBufferRef* av_hw_device{nullptr};
64 AVCodecContext* av_codec_ctx{nullptr}; 67 AVCodecContext* av_codec_ctx{nullptr};
68 AVBufferRef* av_gpu_decoder{nullptr};
65 69
66 GPU& gpu; 70 GPU& gpu;
67 const NvdecCommon::NvdecRegisters& state; 71 const NvdecCommon::NvdecRegisters& state;