diff options
| author | 2023-11-15 13:45:07 -0500 | |
|---|---|---|
| committer | 2023-11-16 17:01:38 -0500 | |
| commit | 4055a476aafb7b915c649363ccde7ba9b8d864d3 (patch) | |
| tree | 7b0d6d2ebcf4a81ba3eab1829130e529e666c243 /src/video_core/host1x/codecs | |
| parent | Merge pull request #12043 from t895/disable-pip-default (diff) | |
| download | yuzu-4055a476aafb7b915c649363ccde7ba9b8d864d3.tar.gz yuzu-4055a476aafb7b915c649363ccde7ba9b8d864d3.tar.xz yuzu-4055a476aafb7b915c649363ccde7ba9b8d864d3.zip | |
video_core: refactor video frame and packet parsing
Diffstat (limited to 'src/video_core/host1x/codecs')
| -rw-r--r-- | src/video_core/host1x/codecs/codec.cpp | 329 | ||||
| -rw-r--r-- | src/video_core/host1x/codecs/codec.h | 39 | ||||
| -rw-r--r-- | src/video_core/host1x/codecs/h264.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/host1x/codecs/h264.h | 1 |
4 files changed, 34 insertions, 339 deletions
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp index dbcf508e5..1030db681 100644 --- a/src/video_core/host1x/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp | |||
| @@ -1,11 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | ||
| 5 | #include <fstream> | ||
| 6 | #include <vector> | ||
| 7 | #include "common/assert.h" | 4 | #include "common/assert.h" |
| 8 | #include "common/scope_exit.h" | ||
| 9 | #include "common/settings.h" | 5 | #include "common/settings.h" |
| 10 | #include "video_core/host1x/codecs/codec.h" | 6 | #include "video_core/host1x/codecs/codec.h" |
| 11 | #include "video_core/host1x/codecs/h264.h" | 7 | #include "video_core/host1x/codecs/h264.h" |
| @@ -14,242 +10,17 @@ | |||
| 14 | #include "video_core/host1x/host1x.h" | 10 | #include "video_core/host1x/host1x.h" |
| 15 | #include "video_core/memory_manager.h" | 11 | #include "video_core/memory_manager.h" |
| 16 | 12 | ||
| 17 | extern "C" { | ||
| 18 | #include <libavfilter/buffersink.h> | ||
| 19 | #include <libavfilter/buffersrc.h> | ||
| 20 | #include <libavutil/opt.h> | ||
| 21 | #ifdef LIBVA_FOUND | ||
| 22 | // for querying VAAPI driver information | ||
| 23 | #include <libavutil/hwcontext_vaapi.h> | ||
| 24 | #endif | ||
| 25 | } | ||
| 26 | |||
| 27 | namespace Tegra { | 13 | namespace Tegra { |
| 28 | namespace { | ||
| 29 | constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; | ||
| 30 | constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; | ||
| 31 | constexpr std::array PREFERRED_GPU_DECODERS = { | ||
| 32 | AV_HWDEVICE_TYPE_CUDA, | ||
| 33 | #ifdef _WIN32 | ||
| 34 | AV_HWDEVICE_TYPE_D3D11VA, | ||
| 35 | AV_HWDEVICE_TYPE_DXVA2, | ||
| 36 | #elif defined(__unix__) | ||
| 37 | AV_HWDEVICE_TYPE_VAAPI, | ||
| 38 | AV_HWDEVICE_TYPE_VDPAU, | ||
| 39 | #endif | ||
| 40 | // last resort for Linux Flatpak (w/ NVIDIA) | ||
| 41 | AV_HWDEVICE_TYPE_VULKAN, | ||
| 42 | }; | ||
| 43 | |||
| 44 | void AVPacketDeleter(AVPacket* ptr) { | ||
| 45 | av_packet_free(&ptr); | ||
| 46 | } | ||
| 47 | |||
| 48 | using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>; | ||
| 49 | |||
| 50 | AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) { | ||
| 51 | for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { | ||
| 52 | if (*p == av_codec_ctx->pix_fmt) { | ||
| 53 | return av_codec_ctx->pix_fmt; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); | ||
| 57 | av_buffer_unref(&av_codec_ctx->hw_device_ctx); | ||
| 58 | av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; | ||
| 59 | return PREFERRED_CPU_FMT; | ||
| 60 | } | ||
| 61 | |||
| 62 | // List all the currently available hwcontext in ffmpeg | ||
| 63 | std::vector<AVHWDeviceType> ListSupportedContexts() { | ||
| 64 | std::vector<AVHWDeviceType> contexts{}; | ||
| 65 | AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE; | ||
| 66 | do { | ||
| 67 | current_device_type = av_hwdevice_iterate_types(current_device_type); | ||
| 68 | contexts.push_back(current_device_type); | ||
| 69 | } while (current_device_type != AV_HWDEVICE_TYPE_NONE); | ||
| 70 | return contexts; | ||
| 71 | } | ||
| 72 | |||
| 73 | } // namespace | ||
| 74 | |||
| 75 | void AVFrameDeleter(AVFrame* ptr) { | ||
| 76 | av_frame_free(&ptr); | ||
| 77 | } | ||
| 78 | 14 | ||
| 79 | Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) | 15 | Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs) |
| 80 | : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)), | 16 | : host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)), |
| 81 | vp8_decoder(std::make_unique<Decoder::VP8>(host1x)), | 17 | vp8_decoder(std::make_unique<Decoder::VP8>(host1x)), |
| 82 | vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {} | 18 | vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {} |
| 83 | 19 | ||
| 84 | Codec::~Codec() { | 20 | Codec::~Codec() = default; |
| 85 | if (!initialized) { | ||
| 86 | return; | ||
| 87 | } | ||
| 88 | // Free libav memory | ||
| 89 | avcodec_free_context(&av_codec_ctx); | ||
| 90 | av_buffer_unref(&av_gpu_decoder); | ||
| 91 | |||
| 92 | if (filters_initialized) { | ||
| 93 | avfilter_graph_free(&av_filter_graph); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | bool Codec::CreateGpuAvDevice() { | ||
| 98 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; | ||
| 99 | static const auto supported_contexts = ListSupportedContexts(); | ||
| 100 | for (const auto& type : PREFERRED_GPU_DECODERS) { | ||
| 101 | if (std::none_of(supported_contexts.begin(), supported_contexts.end(), | ||
| 102 | [&type](const auto& context) { return context == type; })) { | ||
| 103 | LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); | ||
| 104 | continue; | ||
| 105 | } | ||
| 106 | // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create | ||
| 107 | av_buffer_unref(&av_gpu_decoder); | ||
| 108 | const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); | ||
| 109 | if (hwdevice_res < 0) { | ||
| 110 | LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", | ||
| 111 | av_hwdevice_get_type_name(type), hwdevice_res); | ||
| 112 | continue; | ||
| 113 | } | ||
| 114 | #ifdef LIBVA_FOUND | ||
| 115 | if (type == AV_HWDEVICE_TYPE_VAAPI) { | ||
| 116 | // we need to determine if this is an impersonated VAAPI driver | ||
| 117 | AVHWDeviceContext* hwctx = | ||
| 118 | static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data)); | ||
| 119 | AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx); | ||
| 120 | const char* vendor_name = vaQueryVendorString(vactx->display); | ||
| 121 | if (strstr(vendor_name, "VDPAU backend")) { | ||
| 122 | // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them | ||
| 123 | LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver"); | ||
| 124 | continue; | ||
| 125 | } else { | ||
| 126 | // according to some user testing, certain vaapi driver (Intel?) could be buggy | ||
| 127 | // so let's log the driver name which may help the developers/supporters | ||
| 128 | LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | #endif | ||
| 132 | for (int i = 0;; i++) { | ||
| 133 | const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); | ||
| 134 | if (!config) { | ||
| 135 | LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.", | ||
| 136 | av_codec->name, av_hwdevice_get_type_name(type)); | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { | ||
| 140 | LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); | ||
| 141 | av_codec_ctx->pix_fmt = config->pix_fmt; | ||
| 142 | return true; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | return false; | ||
| 147 | } | ||
| 148 | |||
| 149 | void Codec::InitializeAvCodecContext() { | ||
| 150 | av_codec_ctx = avcodec_alloc_context3(av_codec); | ||
| 151 | av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); | ||
| 152 | av_codec_ctx->thread_count = 0; | ||
| 153 | av_codec_ctx->thread_type &= ~FF_THREAD_FRAME; | ||
| 154 | } | ||
| 155 | |||
| 156 | void Codec::InitializeGpuDecoder() { | ||
| 157 | if (!CreateGpuAvDevice()) { | ||
| 158 | av_buffer_unref(&av_gpu_decoder); | ||
| 159 | return; | ||
| 160 | } | ||
| 161 | auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder); | ||
| 162 | ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); | ||
| 163 | av_codec_ctx->hw_device_ctx = hw_device_ctx; | ||
| 164 | av_codec_ctx->get_format = GetGpuFormat; | ||
| 165 | } | ||
| 166 | |||
| 167 | void Codec::InitializeAvFilters(AVFrame* frame) { | ||
| 168 | const AVFilter* buffer_src = avfilter_get_by_name("buffer"); | ||
| 169 | const AVFilter* buffer_sink = avfilter_get_by_name("buffersink"); | ||
| 170 | AVFilterInOut* inputs = avfilter_inout_alloc(); | ||
| 171 | AVFilterInOut* outputs = avfilter_inout_alloc(); | ||
| 172 | SCOPE_EXIT({ | ||
| 173 | avfilter_inout_free(&inputs); | ||
| 174 | avfilter_inout_free(&outputs); | ||
| 175 | }); | ||
| 176 | |||
| 177 | // Don't know how to get the accurate time_base but it doesn't matter for yadif filter | ||
| 178 | // so just use 1/1 to make buffer filter happy | ||
| 179 | std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame->width, | ||
| 180 | frame->height, frame->format); | ||
| 181 | |||
| 182 | av_filter_graph = avfilter_graph_alloc(); | ||
| 183 | int ret = avfilter_graph_create_filter(&av_filter_src_ctx, buffer_src, "in", args.c_str(), | ||
| 184 | nullptr, av_filter_graph); | ||
| 185 | if (ret < 0) { | ||
| 186 | LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter source error: {}", ret); | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | |||
| 190 | ret = avfilter_graph_create_filter(&av_filter_sink_ctx, buffer_sink, "out", nullptr, nullptr, | ||
| 191 | av_filter_graph); | ||
| 192 | if (ret < 0) { | ||
| 193 | LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter sink error: {}", ret); | ||
| 194 | return; | ||
| 195 | } | ||
| 196 | |||
| 197 | inputs->name = av_strdup("out"); | ||
| 198 | inputs->filter_ctx = av_filter_sink_ctx; | ||
| 199 | inputs->pad_idx = 0; | ||
| 200 | inputs->next = nullptr; | ||
| 201 | |||
| 202 | outputs->name = av_strdup("in"); | ||
| 203 | outputs->filter_ctx = av_filter_src_ctx; | ||
| 204 | outputs->pad_idx = 0; | ||
| 205 | outputs->next = nullptr; | ||
| 206 | |||
| 207 | const char* description = "yadif=1:-1:0"; | ||
| 208 | ret = avfilter_graph_parse_ptr(av_filter_graph, description, &inputs, &outputs, nullptr); | ||
| 209 | if (ret < 0) { | ||
| 210 | LOG_ERROR(Service_NVDRV, "avfilter_graph_parse_ptr error: {}", ret); | ||
| 211 | return; | ||
| 212 | } | ||
| 213 | |||
| 214 | ret = avfilter_graph_config(av_filter_graph, nullptr); | ||
| 215 | if (ret < 0) { | ||
| 216 | LOG_ERROR(Service_NVDRV, "avfilter_graph_config error: {}", ret); | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | |||
| 220 | filters_initialized = true; | ||
| 221 | } | ||
| 222 | 21 | ||
| 223 | void Codec::Initialize() { | 22 | void Codec::Initialize() { |
| 224 | const AVCodecID codec = [&] { | 23 | initialized = decode_api.Initialize(current_codec); |
| 225 | switch (current_codec) { | ||
| 226 | case Host1x::NvdecCommon::VideoCodec::H264: | ||
| 227 | return AV_CODEC_ID_H264; | ||
| 228 | case Host1x::NvdecCommon::VideoCodec::VP8: | ||
| 229 | return AV_CODEC_ID_VP8; | ||
| 230 | case Host1x::NvdecCommon::VideoCodec::VP9: | ||
| 231 | return AV_CODEC_ID_VP9; | ||
| 232 | default: | ||
| 233 | UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); | ||
| 234 | return AV_CODEC_ID_NONE; | ||
| 235 | } | ||
| 236 | }(); | ||
| 237 | av_codec = avcodec_find_decoder(codec); | ||
| 238 | |||
| 239 | InitializeAvCodecContext(); | ||
| 240 | if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) { | ||
| 241 | InitializeGpuDecoder(); | ||
| 242 | } | ||
| 243 | if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { | ||
| 244 | LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); | ||
| 245 | avcodec_free_context(&av_codec_ctx); | ||
| 246 | av_buffer_unref(&av_gpu_decoder); | ||
| 247 | return; | ||
| 248 | } | ||
| 249 | if (!av_codec_ctx->hw_device_ctx) { | ||
| 250 | LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); | ||
| 251 | } | ||
| 252 | initialized = true; | ||
| 253 | } | 24 | } |
| 254 | 25 | ||
| 255 | void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) { | 26 | void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) { |
| @@ -264,14 +35,18 @@ void Codec::Decode() { | |||
| 264 | if (is_first_frame) { | 35 | if (is_first_frame) { |
| 265 | Initialize(); | 36 | Initialize(); |
| 266 | } | 37 | } |
| 38 | |||
| 267 | if (!initialized) { | 39 | if (!initialized) { |
| 268 | return; | 40 | return; |
| 269 | } | 41 | } |
| 42 | |||
| 43 | // Assemble bitstream. | ||
| 270 | bool vp9_hidden_frame = false; | 44 | bool vp9_hidden_frame = false; |
| 271 | const auto& frame_data = [&]() { | 45 | size_t configuration_size = 0; |
| 46 | const auto packet_data = [&]() { | ||
| 272 | switch (current_codec) { | 47 | switch (current_codec) { |
| 273 | case Tegra::Host1x::NvdecCommon::VideoCodec::H264: | 48 | case Tegra::Host1x::NvdecCommon::VideoCodec::H264: |
| 274 | return h264_decoder->ComposeFrame(state, is_first_frame); | 49 | return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame); |
| 275 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: | 50 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP8: |
| 276 | return vp8_decoder->ComposeFrame(state); | 51 | return vp8_decoder->ComposeFrame(state); |
| 277 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: | 52 | case Tegra::Host1x::NvdecCommon::VideoCodec::VP9: |
| @@ -283,89 +58,35 @@ void Codec::Decode() { | |||
| 283 | return std::span<const u8>{}; | 58 | return std::span<const u8>{}; |
| 284 | } | 59 | } |
| 285 | }(); | 60 | }(); |
| 286 | AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; | 61 | |
| 287 | if (!packet) { | 62 | // Send assembled bitstream to decoder. |
| 288 | LOG_ERROR(Service_NVDRV, "av_packet_alloc failed"); | 63 | if (!decode_api.SendPacket(packet_data, configuration_size)) { |
| 289 | return; | ||
| 290 | } | ||
| 291 | packet->data = const_cast<u8*>(frame_data.data()); | ||
| 292 | packet->size = static_cast<s32>(frame_data.size()); | ||
| 293 | if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) { | ||
| 294 | LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res); | ||
| 295 | return; | 64 | return; |
| 296 | } | 65 | } |
| 297 | // Only receive/store visible frames | 66 | |
| 67 | // Only receive/store visible frames. | ||
| 298 | if (vp9_hidden_frame) { | 68 | if (vp9_hidden_frame) { |
| 299 | return; | 69 | return; |
| 300 | } | 70 | } |
| 301 | AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter}; | ||
| 302 | AVFramePtr final_frame{nullptr, AVFrameDeleter}; | ||
| 303 | ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed"); | ||
| 304 | if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) { | ||
| 305 | LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret); | ||
| 306 | return; | ||
| 307 | } | ||
| 308 | if (initial_frame->width == 0 || initial_frame->height == 0) { | ||
| 309 | LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | bool is_interlaced = initial_frame->interlaced_frame != 0; | ||
| 313 | if (av_codec_ctx->hw_device_ctx) { | ||
| 314 | final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; | ||
| 315 | ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); | ||
| 316 | // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp | ||
| 317 | // because Intel drivers crash unless using AV_PIX_FMT_NV12 | ||
| 318 | final_frame->format = PREFERRED_GPU_FMT; | ||
| 319 | const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0); | ||
| 320 | ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret); | ||
| 321 | } else { | ||
| 322 | final_frame = std::move(initial_frame); | ||
| 323 | } | ||
| 324 | if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) { | ||
| 325 | UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); | ||
| 326 | return; | ||
| 327 | } | ||
| 328 | if (!is_interlaced) { | ||
| 329 | av_frames.push(std::move(final_frame)); | ||
| 330 | } else { | ||
| 331 | if (!filters_initialized) { | ||
| 332 | InitializeAvFilters(final_frame.get()); | ||
| 333 | } | ||
| 334 | if (const int ret = av_buffersrc_add_frame_flags(av_filter_src_ctx, final_frame.get(), | ||
| 335 | AV_BUFFERSRC_FLAG_KEEP_REF); | ||
| 336 | ret) { | ||
| 337 | LOG_DEBUG(Service_NVDRV, "av_buffersrc_add_frame_flags error {}", ret); | ||
| 338 | return; | ||
| 339 | } | ||
| 340 | while (true) { | ||
| 341 | auto filter_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; | ||
| 342 | 71 | ||
| 343 | int ret = av_buffersink_get_frame(av_filter_sink_ctx, filter_frame.get()); | 72 | // Receive output frames from decoder. |
| 73 | decode_api.ReceiveFrames(frames); | ||
| 344 | 74 | ||
| 345 | if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) | 75 | while (frames.size() > 10) { |
| 346 | break; | 76 | LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame"); |
| 347 | if (ret < 0) { | 77 | frames.pop(); |
| 348 | LOG_DEBUG(Service_NVDRV, "av_buffersink_get_frame error {}", ret); | ||
| 349 | return; | ||
| 350 | } | ||
| 351 | |||
| 352 | av_frames.push(std::move(filter_frame)); | ||
| 353 | } | ||
| 354 | } | ||
| 355 | while (av_frames.size() > 10) { | ||
| 356 | LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); | ||
| 357 | av_frames.pop(); | ||
| 358 | } | 78 | } |
| 359 | } | 79 | } |
| 360 | 80 | ||
| 361 | AVFramePtr Codec::GetCurrentFrame() { | 81 | std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() { |
| 362 | // Sometimes VIC will request more frames than have been decoded. | 82 | // Sometimes VIC will request more frames than have been decoded. |
| 363 | // in this case, return a nullptr and don't overwrite previous frame data | 83 | // in this case, return a blank frame and don't overwrite previous data. |
| 364 | if (av_frames.empty()) { | 84 | if (frames.empty()) { |
| 365 | return AVFramePtr{nullptr, AVFrameDeleter}; | 85 | return {}; |
| 366 | } | 86 | } |
| 367 | AVFramePtr frame = std::move(av_frames.front()); | 87 | |
| 368 | av_frames.pop(); | 88 | auto frame = std::move(frames.front()); |
| 89 | frames.pop(); | ||
| 369 | return frame; | 90 | return frame; |
| 370 | } | 91 | } |
| 371 | 92 | ||
diff --git a/src/video_core/host1x/codecs/codec.h b/src/video_core/host1x/codecs/codec.h index 06fe00a4b..f700ae129 100644 --- a/src/video_core/host1x/codecs/codec.h +++ b/src/video_core/host1x/codecs/codec.h | |||
| @@ -4,28 +4,15 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <optional> | ||
| 7 | #include <string_view> | 8 | #include <string_view> |
| 8 | #include <queue> | 9 | #include <queue> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "video_core/host1x/ffmpeg/ffmpeg.h" | ||
| 10 | #include "video_core/host1x/nvdec_common.h" | 12 | #include "video_core/host1x/nvdec_common.h" |
| 11 | 13 | ||
| 12 | extern "C" { | ||
| 13 | #if defined(__GNUC__) || defined(__clang__) | ||
| 14 | #pragma GCC diagnostic push | ||
| 15 | #pragma GCC diagnostic ignored "-Wconversion" | ||
| 16 | #endif | ||
| 17 | #include <libavcodec/avcodec.h> | ||
| 18 | #include <libavfilter/avfilter.h> | ||
| 19 | #if defined(__GNUC__) || defined(__clang__) | ||
| 20 | #pragma GCC diagnostic pop | ||
| 21 | #endif | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace Tegra { | 14 | namespace Tegra { |
| 25 | 15 | ||
| 26 | void AVFrameDeleter(AVFrame* ptr); | ||
| 27 | using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>; | ||
| 28 | |||
| 29 | namespace Decoder { | 16 | namespace Decoder { |
| 30 | class H264; | 17 | class H264; |
| 31 | class VP8; | 18 | class VP8; |
| @@ -51,7 +38,7 @@ public: | |||
| 51 | void Decode(); | 38 | void Decode(); |
| 52 | 39 | ||
| 53 | /// Returns next decoded frame | 40 | /// Returns next decoded frame |
| 54 | [[nodiscard]] AVFramePtr GetCurrentFrame(); | 41 | [[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame(); |
| 55 | 42 | ||
| 56 | /// Returns the value of current_codec | 43 | /// Returns the value of current_codec |
| 57 | [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const; | 44 | [[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const; |
| @@ -60,25 +47,9 @@ public: | |||
| 60 | [[nodiscard]] std::string_view GetCurrentCodecName() const; | 47 | [[nodiscard]] std::string_view GetCurrentCodecName() const; |
| 61 | 48 | ||
| 62 | private: | 49 | private: |
| 63 | void InitializeAvCodecContext(); | ||
| 64 | |||
| 65 | void InitializeAvFilters(AVFrame* frame); | ||
| 66 | |||
| 67 | void InitializeGpuDecoder(); | ||
| 68 | |||
| 69 | bool CreateGpuAvDevice(); | ||
| 70 | |||
| 71 | bool initialized{}; | 50 | bool initialized{}; |
| 72 | bool filters_initialized{}; | ||
| 73 | Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; | 51 | Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None}; |
| 74 | 52 | FFmpeg::DecodeApi decode_api; | |
| 75 | const AVCodec* av_codec{nullptr}; | ||
| 76 | AVCodecContext* av_codec_ctx{nullptr}; | ||
| 77 | AVBufferRef* av_gpu_decoder{nullptr}; | ||
| 78 | |||
| 79 | AVFilterContext* av_filter_src_ctx{nullptr}; | ||
| 80 | AVFilterContext* av_filter_sink_ctx{nullptr}; | ||
| 81 | AVFilterGraph* av_filter_graph{nullptr}; | ||
| 82 | 53 | ||
| 83 | Host1x::Host1x& host1x; | 54 | Host1x::Host1x& host1x; |
| 84 | const Host1x::NvdecCommon::NvdecRegisters& state; | 55 | const Host1x::NvdecCommon::NvdecRegisters& state; |
| @@ -86,7 +57,7 @@ private: | |||
| 86 | std::unique_ptr<Decoder::VP8> vp8_decoder; | 57 | std::unique_ptr<Decoder::VP8> vp8_decoder; |
| 87 | std::unique_ptr<Decoder::VP9> vp9_decoder; | 58 | std::unique_ptr<Decoder::VP9> vp9_decoder; |
| 88 | 59 | ||
| 89 | std::queue<AVFramePtr> av_frames{}; | 60 | std::queue<std::unique_ptr<FFmpeg::Frame>> frames{}; |
| 90 | }; | 61 | }; |
| 91 | 62 | ||
| 92 | } // namespace Tegra | 63 | } // namespace Tegra |
diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp index ece79b1e2..309a7f1d5 100644 --- a/src/video_core/host1x/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp | |||
| @@ -30,7 +30,7 @@ H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {} | |||
| 30 | H264::~H264() = default; | 30 | H264::~H264() = default; |
| 31 | 31 | ||
| 32 | std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, | 32 | std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, |
| 33 | bool is_first_frame) { | 33 | size_t* out_configuration_size, bool is_first_frame) { |
| 34 | H264DecoderContext context; | 34 | H264DecoderContext context; |
| 35 | host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, | 35 | host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context, |
| 36 | sizeof(H264DecoderContext)); | 36 | sizeof(H264DecoderContext)); |
| @@ -39,6 +39,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters | |||
| 39 | if (!is_first_frame && frame_number != 0) { | 39 | if (!is_first_frame && frame_number != 0) { |
| 40 | frame.resize_destructive(context.stream_len); | 40 | frame.resize_destructive(context.stream_len); |
| 41 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); | 41 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); |
| 42 | *out_configuration_size = 0; | ||
| 42 | return frame; | 43 | return frame; |
| 43 | } | 44 | } |
| 44 | 45 | ||
| @@ -157,6 +158,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters | |||
| 157 | frame.resize(encoded_header.size() + context.stream_len); | 158 | frame.resize(encoded_header.size() + context.stream_len); |
| 158 | std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); | 159 | std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); |
| 159 | 160 | ||
| 161 | *out_configuration_size = encoded_header.size(); | ||
| 160 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, | 162 | host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, |
| 161 | frame.data() + encoded_header.size(), context.stream_len); | 163 | frame.data() + encoded_header.size(), context.stream_len); |
| 162 | 164 | ||
diff --git a/src/video_core/host1x/codecs/h264.h b/src/video_core/host1x/codecs/h264.h index d6b556322..1deaf4632 100644 --- a/src/video_core/host1x/codecs/h264.h +++ b/src/video_core/host1x/codecs/h264.h | |||
| @@ -67,6 +67,7 @@ public: | |||
| 67 | 67 | ||
| 68 | /// Compose the H264 frame for FFmpeg decoding | 68 | /// Compose the H264 frame for FFmpeg decoding |
| 69 | [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, | 69 | [[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, |
| 70 | size_t* out_configuration_size, | ||
| 70 | bool is_first_frame = false); | 71 | bool is_first_frame = false); |
| 71 | 72 | ||
| 72 | private: | 73 | private: |