|
@@ -16,44 +16,17 @@ extern "C" {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
namespace Tegra {
|
|
namespace Tegra {
|
|
|
-#if defined(LIBVA_FOUND)
|
|
|
|
|
-// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c originally under MIT license
|
|
|
|
|
namespace {
|
|
namespace {
|
|
|
-constexpr std::array<const char*, 2> VAAPI_DRIVERS = {
|
|
|
|
|
- "i915",
|
|
|
|
|
- "amdgpu",
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-AVPixelFormat GetHwFormat(AVCodecContext*, 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_PIX_FMT_VAAPI) {
|
|
|
|
|
- return AV_PIX_FMT_VAAPI;
|
|
|
|
|
|
|
+ if (*p == av_codec_ctx->pix_fmt) {
|
|
|
|
|
+ return av_codec_ctx->pix_fmt;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
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 *pix_fmts;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-bool CreateVaapiHwdevice(AVBufferRef** av_hw_device) {
|
|
|
|
|
- AVDictionary* hwdevice_options = nullptr;
|
|
|
|
|
- av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
|
|
|
|
|
- for (const auto& driver : VAAPI_DRIVERS) {
|
|
|
|
|
- av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
|
|
|
|
|
- const int hwdevice_error = av_hwdevice_ctx_create(av_hw_device, AV_HWDEVICE_TYPE_VAAPI,
|
|
|
|
|
- nullptr, hwdevice_options, 0);
|
|
|
|
|
- if (hwdevice_error >= 0) {
|
|
|
|
|
- LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
|
|
|
|
|
- av_dict_free(&hwdevice_options);
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
|
|
|
|
|
- }
|
|
|
|
|
- LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
|
|
|
|
|
- av_dict_free(&hwdevice_options);
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ return AV_PIX_FMT_NONE;
|
|
|
}
|
|
}
|
|
|
} // namespace
|
|
} // namespace
|
|
|
-#endif
|
|
|
|
|
|
|
|
|
|
void AVFrameDeleter(AVFrame* ptr) {
|
|
void AVFrameDeleter(AVFrame* ptr) {
|
|
|
av_frame_free(&ptr);
|
|
av_frame_free(&ptr);
|
|
@@ -69,26 +42,75 @@ Codec::~Codec() {
|
|
|
}
|
|
}
|
|
|
// Free libav memory
|
|
// Free libav memory
|
|
|
avcodec_send_packet(av_codec_ctx, nullptr);
|
|
avcodec_send_packet(av_codec_ctx, nullptr);
|
|
|
- AVFrame* av_frame = av_frame_alloc();
|
|
|
|
|
- avcodec_receive_frame(av_codec_ctx, av_frame);
|
|
|
|
|
|
|
+ AVFramePtr av_frame{av_frame_alloc(), AVFrameDeleter};
|
|
|
|
|
+ avcodec_receive_frame(av_codec_ctx, av_frame.get());
|
|
|
avcodec_flush_buffers(av_codec_ctx);
|
|
avcodec_flush_buffers(av_codec_ctx);
|
|
|
- av_frame_free(&av_frame);
|
|
|
|
|
avcodec_close(av_codec_ctx);
|
|
avcodec_close(av_codec_ctx);
|
|
|
- av_buffer_unref(&av_hw_device);
|
|
|
|
|
|
|
+ av_buffer_unref(&av_gpu_decoder);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void Codec::InitializeHwdec() {
|
|
|
|
|
- // Prioritize integrated GPU to mitigate bandwidth bottlenecks
|
|
|
|
|
|
|
+bool Codec::CreateGpuAvDevice() {
|
|
|
#if defined(LIBVA_FOUND)
|
|
#if defined(LIBVA_FOUND)
|
|
|
- if (CreateVaapiHwdevice(&av_hw_device)) {
|
|
|
|
|
- const auto hw_device_ctx = av_buffer_ref(av_hw_device);
|
|
|
|
|
- ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
|
|
|
|
|
- av_codec_ctx->hw_device_ctx = hw_device_ctx;
|
|
|
|
|
- av_codec_ctx->get_format = GetHwFormat;
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
|
|
|
|
|
+ "i915",
|
|
|
|
|
+ "iHD",
|
|
|
|
|
+ "amdgpu",
|
|
|
|
|
+ };
|
|
|
|
|
+ AVDictionary* hwdevice_options = nullptr;
|
|
|
|
|
+ av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
|
|
|
|
|
+ for (const auto& driver : VAAPI_DRIVERS) {
|
|
|
|
|
+ av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
|
|
|
|
|
+ const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
|
|
|
|
|
+ nullptr, hwdevice_options, 0);
|
|
|
|
|
+ if (hwdevice_error >= 0) {
|
|
|
|
|
+ LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
|
|
|
|
|
+ av_dict_free(&hwdevice_options);
|
|
|
|
|
+ av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI;
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
|
|
|
}
|
|
}
|
|
|
|
|
+ LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
|
|
|
|
|
+ av_dict_free(&hwdevice_options);
|
|
|
|
|
+#endif
|
|
|
|
|
+ static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
|
|
|
|
|
+ static constexpr std::array GPU_DECODER_TYPES{
|
|
|
|
|
+ AV_HWDEVICE_TYPE_CUDA,
|
|
|
|
|
+#ifdef _WIN32
|
|
|
|
|
+ AV_HWDEVICE_TYPE_D3D11VA,
|
|
|
|
|
+#else
|
|
|
|
|
+ AV_HWDEVICE_TYPE_VDPAU,
|
|
|
#endif
|
|
#endif
|
|
|
- // TODO more GPU accelerated decoders
|
|
|
|
|
|
|
+ };
|
|
|
|
|
+ for (const auto& type : GPU_DECODER_TYPES) {
|
|
|
|
|
+ av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
|
|
|
|
|
+ for (int i = 0;; i++) {
|
|
|
|
|
+ const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
|
|
|
|
|
+ if (!config) {
|
|
|
|
|
+ LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.",
|
|
|
|
|
+ av_codec->name, av_hwdevice_get_type_name(type));
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (config->methods & HW_CONFIG_METHOD && config->device_type == type) {
|
|
|
|
|
+ av_codec_ctx->pix_fmt = config->pix_fmt;
|
|
|
|
|
+ LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Codec::InitializeGpuDecoder() {
|
|
|
|
|
+ if (!CreateGpuAvDevice()) {
|
|
|
|
|
+ av_buffer_unref(&av_gpu_decoder);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder);
|
|
|
|
|
+ ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
|
|
|
|
|
+ av_codec_ctx->hw_device_ctx = hw_device_ctx;
|
|
|
|
|
+ av_codec_ctx->get_format = GetGpuFormat;
|
|
|
|
|
+ using_gpu_decode = true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Codec::Initialize() {
|
|
void Codec::Initialize() {
|
|
@@ -107,7 +129,8 @@ void Codec::Initialize() {
|
|
|
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);
|
|
|
- InitializeHwdec();
|
|
|
|
|
|
|
+
|
|
|
|
|
+ InitializeGpuDecoder();
|
|
|
if (!av_codec_ctx->hw_device_ctx) {
|
|
if (!av_codec_ctx->hw_device_ctx) {
|
|
|
LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
|
|
LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
|
|
|
}
|
|
}
|
|
@@ -115,7 +138,7 @@ void Codec::Initialize() {
|
|
|
if (av_error < 0) {
|
|
if (av_error < 0) {
|
|
|
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
|
|
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
|
|
|
avcodec_close(av_codec_ctx);
|
|
avcodec_close(av_codec_ctx);
|
|
|
- av_buffer_unref(&av_hw_device);
|
|
|
|
|
|
|
+ av_buffer_unref(&av_gpu_decoder);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
initialized = true;
|
|
initialized = true;
|
|
@@ -153,38 +176,33 @@ void Codec::Decode() {
|
|
|
if (vp9_hidden_frame) {
|
|
if (vp9_hidden_frame) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- AVFrame* hw_frame = av_frame_alloc();
|
|
|
|
|
- AVFrame* sw_frame = hw_frame;
|
|
|
|
|
- ASSERT_MSG(hw_frame, "av_frame_alloc hw_frame failed");
|
|
|
|
|
- if (const int ret = avcodec_receive_frame(av_codec_ctx, hw_frame); ret) {
|
|
|
|
|
|
|
+ AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter};
|
|
|
|
|
+ AVFramePtr final_frame{nullptr, AVFrameDeleter};
|
|
|
|
|
+ ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed");
|
|
|
|
|
+ if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) {
|
|
|
LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
|
|
LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
|
|
|
- av_frame_free(&hw_frame);
|
|
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- if (!hw_frame->width || !hw_frame->height) {
|
|
|
|
|
|
|
+ if (initial_frame->width == 0 || initial_frame->height == 0) {
|
|
|
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
|
|
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
|
|
|
- av_frame_free(&hw_frame);
|
|
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
-#if defined(LIBVA_FOUND)
|
|
|
|
|
- // Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c under MIT license
|
|
|
|
|
- if (hw_frame->format == AV_PIX_FMT_VAAPI) {
|
|
|
|
|
- sw_frame = av_frame_alloc();
|
|
|
|
|
- ASSERT_MSG(sw_frame, "av_frame_alloc sw_frame failed");
|
|
|
|
|
|
|
+ if (using_gpu_decode) {
|
|
|
|
|
+ final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
|
|
|
|
|
+ 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
|
|
|
- sw_frame->format = AV_PIX_FMT_NV12;
|
|
|
|
|
- const int transfer_data_ret = av_hwframe_transfer_data(sw_frame, hw_frame, 0);
|
|
|
|
|
- ASSERT_MSG(!transfer_data_ret, "av_hwframe_transfer_data error {}", transfer_data_ret);
|
|
|
|
|
- av_frame_free(&hw_frame);
|
|
|
|
|
- }
|
|
|
|
|
-#endif
|
|
|
|
|
- if (sw_frame->format != AV_PIX_FMT_YUV420P && sw_frame->format != AV_PIX_FMT_NV12) {
|
|
|
|
|
- UNIMPLEMENTED_MSG("Unexpected video format from host graphics: {}", sw_frame->format);
|
|
|
|
|
- av_frame_free(&sw_frame);
|
|
|
|
|
|
|
+ final_frame->format = AV_PIX_FMT_NV12;
|
|
|
|
|
+ const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
|
|
|
|
|
+ ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ final_frame = std::move(initial_frame);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (final_frame->format != AV_PIX_FMT_YUV420P && final_frame->format != AV_PIX_FMT_NV12) {
|
|
|
|
|
+ UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- av_frames.push(AVFramePtr{sw_frame, AVFrameDeleter});
|
|
|
|
|
|
|
+ av_frames.push(std::move(final_frame));
|
|
|
if (av_frames.size() > 10) {
|
|
if (av_frames.size() > 10) {
|
|
|
LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
|
|
LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
|
|
|
av_frames.pop();
|
|
av_frames.pop();
|