summaryrefslogtreecommitdiff
path: root/src/video_core/command_classes
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/command_classes')
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp224
-rw-r--r--src/video_core/command_classes/codecs/codec.h9
-rw-r--r--src/video_core/command_classes/codecs/h264.cpp3
3 files changed, 141 insertions, 95 deletions
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index f798a0053..61966cbfe 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -5,6 +5,7 @@
5#include <fstream> 5#include <fstream>
6#include <vector> 6#include <vector>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/settings.h"
8#include "video_core/command_classes/codecs/codec.h" 9#include "video_core/command_classes/codecs/codec.h"
9#include "video_core/command_classes/codecs/h264.h" 10#include "video_core/command_classes/codecs/h264.h"
10#include "video_core/command_classes/codecs/vp9.h" 11#include "video_core/command_classes/codecs/vp9.h"
@@ -16,108 +17,146 @@ extern "C" {
16} 17}
17 18
18namespace Tegra { 19namespace Tegra {
19#if defined(LIBVA_FOUND)
20// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c originally under MIT license
21namespace { 20namespace {
22constexpr std::array<const char*, 2> VAAPI_DRIVERS = { 21constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
23 "i915", 22constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
24 "amdgpu", 23
25}; 24void AVPacketDeleter(AVPacket* ptr) {
25 av_packet_free(&ptr);
26}
26 27
27AVPixelFormat GetHwFormat(AVCodecContext*, const AVPixelFormat* pix_fmts) { 28using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>;
29
30AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
28 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { 31 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
29 if (*p == AV_PIX_FMT_VAAPI) { 32 if (*p == av_codec_ctx->pix_fmt) {
30 return AV_PIX_FMT_VAAPI; 33 return av_codec_ctx->pix_fmt;
31 } 34 }
32 } 35 }
33 LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); 36 LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
34 return *pix_fmts; 37 av_buffer_unref(&av_codec_ctx->hw_device_ctx);
38 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
39 return PREFERRED_CPU_FMT;
40}
41} // namespace
42
43void AVFrameDeleter(AVFrame* ptr) {
44 av_frame_free(&ptr);
35} 45}
36 46
37bool CreateVaapiHwdevice(AVBufferRef** av_hw_device) { 47Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs)
48 : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)),
49 vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {}
50
51Codec::~Codec() {
52 if (!initialized) {
53 return;
54 }
55 // Free libav memory
56 avcodec_free_context(&av_codec_ctx);
57 av_buffer_unref(&av_gpu_decoder);
58}
59
60bool Codec::CreateGpuAvDevice() {
61#if defined(LIBVA_FOUND)
62 static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
63 "i915",
64 "iHD",
65 "amdgpu",
66 };
38 AVDictionary* hwdevice_options = nullptr; 67 AVDictionary* hwdevice_options = nullptr;
39 av_dict_set(&hwdevice_options, "connection_type", "drm", 0); 68 av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
40 for (const auto& driver : VAAPI_DRIVERS) { 69 for (const auto& driver : VAAPI_DRIVERS) {
41 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); 70 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, 71 const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
43 nullptr, hwdevice_options, 0); 72 nullptr, hwdevice_options, 0);
44 if (hwdevice_error >= 0) { 73 if (hwdevice_error >= 0) {
45 LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver); 74 LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
46 av_dict_free(&hwdevice_options); 75 av_dict_free(&hwdevice_options);
76 av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI;
47 return true; 77 return true;
48 } 78 }
49 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error); 79 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
50 } 80 }
51 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers"); 81 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
52 av_dict_free(&hwdevice_options); 82 av_dict_free(&hwdevice_options);
53 return false;
54}
55} // namespace
56#endif 83#endif
57 84 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
58void AVFrameDeleter(AVFrame* ptr) { 85 static constexpr std::array GPU_DECODER_TYPES{
59 av_frame_free(&ptr); 86 AV_HWDEVICE_TYPE_CUDA,
87#ifdef _WIN32
88 AV_HWDEVICE_TYPE_D3D11VA,
89#else
90 AV_HWDEVICE_TYPE_VDPAU,
91#endif
92 };
93 for (const auto& type : GPU_DECODER_TYPES) {
94 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
95 if (hwdevice_res < 0) {
96 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
97 av_hwdevice_get_type_name(type), hwdevice_res);
98 continue;
99 }
100 for (int i = 0;; i++) {
101 const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
102 if (!config) {
103 LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.",
104 av_codec->name, av_hwdevice_get_type_name(type));
105 break;
106 }
107 if (config->methods & HW_CONFIG_METHOD && config->device_type == type) {
108 av_codec_ctx->pix_fmt = config->pix_fmt;
109 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
110 return true;
111 }
112 }
113 }
114 return false;
60} 115}
61 116
62Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs) 117void Codec::InitializeAvCodecContext() {
63 : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)), 118 av_codec_ctx = avcodec_alloc_context3(av_codec);
64 vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {} 119 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
65
66Codec::~Codec() {
67 if (!initialized) {
68 return;
69 }
70 // Free libav memory
71 avcodec_send_packet(av_codec_ctx, nullptr);
72 AVFrame* av_frame = av_frame_alloc();
73 avcodec_receive_frame(av_codec_ctx, av_frame);
74 avcodec_flush_buffers(av_codec_ctx);
75 av_frame_free(&av_frame);
76 avcodec_close(av_codec_ctx);
77 av_buffer_unref(&av_hw_device);
78} 120}
79 121
80void Codec::InitializeHwdec() { 122void Codec::InitializeGpuDecoder() {
81 // Prioritize integrated GPU to mitigate bandwidth bottlenecks 123 if (!CreateGpuAvDevice()) {
82#if defined(LIBVA_FOUND) 124 av_buffer_unref(&av_gpu_decoder);
83 if (CreateVaapiHwdevice(&av_hw_device)) {
84 const auto hw_device_ctx = av_buffer_ref(av_hw_device);
85 ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
86 av_codec_ctx->hw_device_ctx = hw_device_ctx;
87 av_codec_ctx->get_format = GetHwFormat;
88 return; 125 return;
89 } 126 }
90#endif 127 auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder);
91 // TODO more GPU accelerated decoders 128 ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
129 av_codec_ctx->hw_device_ctx = hw_device_ctx;
130 av_codec_ctx->get_format = GetGpuFormat;
92} 131}
93 132
94void Codec::Initialize() { 133void Codec::Initialize() {
95 AVCodecID codec; 134 const AVCodecID codec = [&] {
96 switch (current_codec) { 135 switch (current_codec) {
97 case NvdecCommon::VideoCodec::H264: 136 case NvdecCommon::VideoCodec::H264:
98 codec = AV_CODEC_ID_H264; 137 return AV_CODEC_ID_H264;
99 break; 138 case NvdecCommon::VideoCodec::Vp9:
100 case NvdecCommon::VideoCodec::Vp9: 139 return AV_CODEC_ID_VP9;
101 codec = AV_CODEC_ID_VP9; 140 default:
102 break; 141 UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
103 default: 142 return AV_CODEC_ID_NONE;
104 UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); 143 }
144 }();
145 av_codec = avcodec_find_decoder(codec);
146
147 InitializeAvCodecContext();
148 if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::GPU) {
149 InitializeGpuDecoder();
150 }
151 if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
152 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res);
153 avcodec_free_context(&av_codec_ctx);
154 av_buffer_unref(&av_gpu_decoder);
105 return; 155 return;
106 } 156 }
107 av_codec = avcodec_find_decoder(codec);
108 av_codec_ctx = avcodec_alloc_context3(av_codec);
109 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
110 InitializeHwdec();
111 if (!av_codec_ctx->hw_device_ctx) { 157 if (!av_codec_ctx->hw_device_ctx) {
112 LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); 158 LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
113 } 159 }
114 const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
115 if (av_error < 0) {
116 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
117 avcodec_close(av_codec_ctx);
118 av_buffer_unref(&av_hw_device);
119 return;
120 }
121 initialized = true; 160 initialized = true;
122} 161}
123 162
@@ -133,6 +172,9 @@ void Codec::Decode() {
133 if (is_first_frame) { 172 if (is_first_frame) {
134 Initialize(); 173 Initialize();
135 } 174 }
175 if (!initialized) {
176 return;
177 }
136 bool vp9_hidden_frame = false; 178 bool vp9_hidden_frame = false;
137 std::vector<u8> frame_data; 179 std::vector<u8> frame_data;
138 if (current_codec == NvdecCommon::VideoCodec::H264) { 180 if (current_codec == NvdecCommon::VideoCodec::H264) {
@@ -141,50 +183,48 @@ void Codec::Decode() {
141 frame_data = vp9_decoder->ComposeFrameHeader(state); 183 frame_data = vp9_decoder->ComposeFrameHeader(state);
142 vp9_hidden_frame = vp9_decoder->WasFrameHidden(); 184 vp9_hidden_frame = vp9_decoder->WasFrameHidden();
143 } 185 }
144 AVPacket packet{}; 186 AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter};
145 av_init_packet(&packet); 187 if (!packet) {
146 packet.data = frame_data.data(); 188 LOG_ERROR(Service_NVDRV, "av_packet_alloc failed");
147 packet.size = static_cast<s32>(frame_data.size()); 189 return;
148 if (const int ret = avcodec_send_packet(av_codec_ctx, &packet); ret) { 190 }
149 LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", ret); 191 packet->data = frame_data.data();
192 packet->size = static_cast<s32>(frame_data.size());
193 if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) {
194 LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res);
150 return; 195 return;
151 } 196 }
152 // Only receive/store visible frames 197 // Only receive/store visible frames
153 if (vp9_hidden_frame) { 198 if (vp9_hidden_frame) {
154 return; 199 return;
155 } 200 }
156 AVFrame* hw_frame = av_frame_alloc(); 201 AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter};
157 AVFrame* sw_frame = hw_frame; 202 AVFramePtr final_frame{nullptr, AVFrameDeleter};
158 ASSERT_MSG(hw_frame, "av_frame_alloc hw_frame failed"); 203 ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed");
159 if (const int ret = avcodec_receive_frame(av_codec_ctx, hw_frame); ret) { 204 if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) {
160 LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret); 205 LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
161 av_frame_free(&hw_frame);
162 return; 206 return;
163 } 207 }
164 if (!hw_frame->width || !hw_frame->height) { 208 if (initial_frame->width == 0 || initial_frame->height == 0) {
165 LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); 209 LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
166 av_frame_free(&hw_frame);
167 return; 210 return;
168 } 211 }
169#if defined(LIBVA_FOUND) 212 if (av_codec_ctx->hw_device_ctx) {
170 // Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c under MIT license 213 final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
171 if (hw_frame->format == AV_PIX_FMT_VAAPI) { 214 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 215 // 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 216 // because Intel drivers crash unless using AV_PIX_FMT_NV12
176 sw_frame->format = AV_PIX_FMT_NV12; 217 final_frame->format = PREFERRED_GPU_FMT;
177 const int transfer_data_ret = av_hwframe_transfer_data(sw_frame, hw_frame, 0); 218 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); 219 ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
179 av_frame_free(&hw_frame); 220 } else {
221 final_frame = std::move(initial_frame);
180 } 222 }
181#endif 223 if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) {
182 if (sw_frame->format != AV_PIX_FMT_YUV420P && sw_frame->format != AV_PIX_FMT_NV12) { 224 UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
183 UNIMPLEMENTED_MSG("Unexpected video format from host graphics: {}", sw_frame->format);
184 av_frame_free(&sw_frame);
185 return; 225 return;
186 } 226 }
187 av_frames.push(AVFramePtr{sw_frame, AVFrameDeleter}); 227 av_frames.push(std::move(final_frame));
188 if (av_frames.size() > 10) { 228 if (av_frames.size() > 10) {
189 LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); 229 LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
190 av_frames.pop(); 230 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..1508d36c2 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -50,18 +50,23 @@ 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 InitializeAvCodecContext();
59
60 void InitializeGpuDecoder();
61
62 bool CreateGpuAvDevice();
58 63
59 bool initialized{}; 64 bool initialized{};
60 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; 65 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
61 66
62 AVCodec* av_codec{nullptr}; 67 AVCodec* av_codec{nullptr};
63 AVBufferRef* av_hw_device{nullptr};
64 AVCodecContext* av_codec_ctx{nullptr}; 68 AVCodecContext* av_codec_ctx{nullptr};
69 AVBufferRef* av_gpu_decoder{nullptr};
65 70
66 GPU& gpu; 71 GPU& gpu;
67 const NvdecCommon::NvdecRegisters& state; 72 const NvdecCommon::NvdecRegisters& state;
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp
index 5fb6d45ee..51ee14c13 100644
--- a/src/video_core/command_classes/codecs/h264.cpp
+++ b/src/video_core/command_classes/codecs/h264.cpp
@@ -95,7 +95,8 @@ const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegister
95 const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units / 95 const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units /
96 (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2); 96 (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
97 97
98 writer.WriteUe(16); 98 // TODO (ameerj): Where do we get this number, it seems to be particular for each stream
99 writer.WriteUe(6); // Max number of reference frames
99 writer.WriteBit(false); 100 writer.WriteBit(false);
100 writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); 101 writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
101 writer.WriteUe(pic_height - 1); 102 writer.WriteUe(pic_height - 1);