From fb27733dd83710a31e3287fbf4e309be861c8803 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Fri, 15 Sep 2023 23:52:34 +0900 Subject: [PATCH] d3d12: Add H.265 decoder Part-of: --- .../sys/d3d12/gstd3d12decoder.cpp | 72 +++- .../sys/d3d12/gstd3d12h265dec.cpp | 316 ++++++++++++++++++ .../sys/d3d12/gstd3d12h265dec.h | 32 ++ .../gst-plugins-bad/sys/d3d12/meson.build | 1 + .../gst-plugins-bad/sys/d3d12/plugin.cpp | 3 + 5 files changed, 416 insertions(+), 8 deletions(-) create mode 100644 subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h265dec.cpp create mode 100644 subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h265dec.h diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp index d3022815d3..957e380c20 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp @@ -49,7 +49,10 @@ struct DecoderFormat }; static const DecoderFormat format_list[] = { - {GST_DXVA_CODEC_H264, D3D12_VIDEO_DECODE_PROFILE_H264, DXGI_FORMAT_NV12} + {GST_DXVA_CODEC_H264, D3D12_VIDEO_DECODE_PROFILE_H264, DXGI_FORMAT_NV12}, + {GST_DXVA_CODEC_H265, D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN, DXGI_FORMAT_NV12}, + {GST_DXVA_CODEC_H265, D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN10, + DXGI_FORMAT_P010}, }; /* *INDENT-OFF* */ @@ -1387,6 +1390,26 @@ struct _GstD3D12DecoderClassData gchar *description; }; +static void +gst_d3d12_decoder_get_profiles (const GUID & profile, + std::vector < std::string > &list) +{ + if (profile == D3D12_VIDEO_DECODE_PROFILE_H264) { + list.push_back ("high"); + list.push_back ("progressive-high"); + list.push_back ("constrained-high"); + list.push_back ("main"); + list.push_back ("constrained-baseline"); + list.push_back ("baseline"); + } else if (profile == D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN) { + list.push_back ("main"); + } else if (profile == D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN10) { + list.push_back ("main-10"); + } else { + g_assert_not_reached (); + } +} + GstD3D12DecoderClassData * gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, ID3D12VideoDevice * video_device, GstDxvaCodec codec) @@ -1397,6 +1420,7 @@ gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, D3D12_VIDEO_DECODE_CONFIGURATION_FLAG_NONE; D3D12_VIDEO_DECODE_TIER tier = D3D12_VIDEO_DECODE_TIER_NOT_SUPPORTED; std::set < DXGI_FORMAT > supported_formats; + std::vector < std::string > profiles; g_return_val_if_fail (GST_IS_D3D12_DEVICE (device), nullptr); g_return_val_if_fail (video_device != nullptr, nullptr); @@ -1414,6 +1438,7 @@ gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, s.FrameRate = { 0, 1 }; s.BitRate = 0; + bool supported = false; for (guint j = 0; j < G_N_ELEMENTS (gst_dxva_resolutions); j++) { s.Width = gst_dxva_resolutions[j].width; s.Height = gst_dxva_resolutions[j].height; @@ -1435,7 +1460,11 @@ gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, supported_formats.insert (format_list[i].format); config_flags = s.ConfigurationFlags; tier = s.DecodeTier; + supported = true; } + + if (supported) + gst_d3d12_decoder_get_profiles (format_list[i].decode_profile, profiles); } if (supported_formats.empty ()) { @@ -1476,22 +1505,49 @@ gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, src_caps_string += format_string; } - GstCaps *src_caps = gst_caps_from_string (src_caps_string.c_str ()); - GstCaps *sink_caps; + std::string profile_string; + /* *INDENT-OFF* */ + if (profiles.size () > 1) { + profile_string = "{ "; + bool first = true; + for (auto it: profiles) { + if (!first) + profile_string += ", "; + + profile_string += it; + first = false; + } + + profile_string += " }"; + } else { + profile_string = profiles[0]; + } + /* *INDENT-ON* */ + + std::string sink_caps_string; switch (codec) { case GST_DXVA_CODEC_H264: - sink_caps = gst_caps_from_string ("video/x-h264, " - "stream-format= (string) { avc, avc3, byte-stream }, " - "alignment= (string) au, " - "profile = (string) { high, progressive-high, constrained-high, main, " - "constrained-baseline, baseline }"); + sink_caps_string = "video/x-h264, " + "stream-format=(string) { avc, avc3, byte-stream }, " + "alignment=(string) au"; + break; + case GST_DXVA_CODEC_H265: + sink_caps_string = "video/x-h265, " + "stream-format=(string) { hev1, hvc1, byte-stream }, " + "alignment=(string) au"; break; default: g_assert_not_reached (); return nullptr; } + sink_caps_string += ", profile=(string) "; + sink_caps_string += profile_string; + + GstCaps *src_caps = gst_caps_from_string (src_caps_string.c_str ()); + GstCaps *sink_caps = gst_caps_from_string (sink_caps_string.c_str ()); + gint max_res = MAX (max_resolution.width, max_resolution.height); gst_caps_set_simple (sink_caps, "width", GST_TYPE_INT_RANGE, 1, max_res, diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h265dec.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h265dec.cpp new file mode 100644 index 0000000000..9bbae142c2 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h265dec.cpp @@ -0,0 +1,316 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02120-1301, USA. + */ + +/** + * SECTION:element-d3d12h265dec + * @title: d3d12h265dec + * + * A Direct3D12 based H.265 video decoder + * + * ## Example launch line + * ``` + * gst-launch-1.0 filesrc location=/path/to/h265/file ! parsebin ! d3d12h265dec ! videoconvert ! autovideosink + * ``` + * + * Since: 1.24 + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstd3d12h265dec.h" +#include "gstd3d12device.h" +#include "gstd3d12utils.h" +#include + +GST_DEBUG_CATEGORY_STATIC (gst_d3d12_h265_dec_debug); +#define GST_CAT_DEFAULT gst_d3d12_h265_dec_debug + +GST_D3D12_DECODER_DEFINE_TYPE (GstD3D12H265Dec, gst_d3d12_h265_dec, + GST, D3D12_H265_DEC, GstDxvaH265Decoder); + +static void +gst_d3d12_h265_dec_class_init (GstD3D12H265DecClass * klass, gpointer data) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass); + GstDxvaH265DecoderClass *dxva_class = GST_DXVA_H265_DECODER_CLASS (klass); + GstD3D12DecoderClassData *cdata = (GstD3D12DecoderClassData *) data; + + gobject_class->get_property = gst_d3d12_h265_dec_get_property; + + element_class->set_context = + GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_set_context); + + parent_class = (GstElementClass *) g_type_class_peek_parent (klass); + gst_d3d12_decoder_class_data_fill_subclass_data (cdata, &klass->class_data); + + gst_d3d12_decoder_proxy_class_init (element_class, cdata, + "Seungha Yang "); + + decoder_class->open = GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_open); + decoder_class->close = GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_close); + decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_negotiate); + decoder_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_decide_allocation); + decoder_class->sink_query = GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_sink_query); + decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_src_query); + + dxva_class->configure = GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_configure); + dxva_class->new_picture = GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_new_picture); + dxva_class->get_picture_id = + GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_get_picture_id); + dxva_class->start_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_start_picture); + dxva_class->end_picture = GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_end_picture); + dxva_class->output_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_h265_dec_output_picture); +} + +static void +gst_d3d12_h265_dec_init (GstD3D12H265Dec * self) +{ +} + +static void +gst_d3d12_h265_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstD3D12H265DecClass *klass = GST_D3D12_H265_DEC_GET_CLASS (object); + GstD3D12DecoderSubClassData *cdata = &klass->class_data; + + gst_d3d12_decoder_proxy_get_property (object, prop_id, value, pspec, cdata); +} + +static void +gst_d3d12_h265_dec_set_context (GstElement * element, GstContext * context) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (element); + GstD3D12H265DecClass *klass = GST_D3D12_H265_DEC_GET_CLASS (self); + GstD3D12DecoderSubClassData *cdata = &klass->class_data; + + gst_d3d12_handle_set_context_for_adapter_luid (element, + context, cdata->adapter_luid, &self->device); + + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + +static gboolean +gst_d3d12_h265_dec_open (GstVideoDecoder * decoder) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + GstD3D12H265DecClass *klass = GST_D3D12_H265_DEC_GET_CLASS (self); + GstD3D12DecoderSubClassData *cdata = &klass->class_data; + + return gst_d3d12_decoder_proxy_open (decoder, cdata, &self->device, + &self->decoder); +} + +static gboolean +gst_d3d12_h265_dec_close (GstVideoDecoder * decoder) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + gst_clear_object (&self->decoder); + gst_clear_object (&self->device); + + return TRUE; +} + +static gboolean +gst_d3d12_h265_dec_negotiate (GstVideoDecoder * decoder) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + if (!gst_d3d12_decoder_negotiate (self->decoder, decoder)) + return FALSE; + + return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); +} + +static gboolean +gst_d3d12_h265_dec_decide_allocation (GstVideoDecoder * decoder, + GstQuery * query) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + if (!gst_d3d12_decoder_decide_allocation (self->decoder, decoder, query)) { + return FALSE; + } + + return GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation + (decoder, query); +} + +static gboolean +gst_d3d12_h265_dec_sink_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + if (gst_d3d12_handle_context_query (GST_ELEMENT (decoder), + query, self->device)) { + return TRUE; + } + break; + default: + break; + } + + return GST_VIDEO_DECODER_CLASS (parent_class)->sink_query (decoder, query); +} + +static gboolean +gst_d3d12_h265_dec_src_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + if (gst_d3d12_handle_context_query (GST_ELEMENT (decoder), + query, self->device)) { + return TRUE; + } + break; + default: + break; + } + + return GST_VIDEO_DECODER_CLASS (parent_class)->src_query (decoder, query); +} + +static GstFlowReturn +gst_d3d12_h265_dec_configure (GstDxvaH265Decoder * decoder, + GstVideoCodecState * input_state, const GstVideoInfo * info, + gint crop_x, gint crop_y, gint coded_width, gint coded_height, + gint max_dpb_size) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + return gst_d3d12_decoder_configure (self->decoder, input_state, info, + crop_x, crop_y, coded_width, coded_height, max_dpb_size); +} + +static GstFlowReturn +gst_d3d12_h265_dec_new_picture (GstDxvaH265Decoder * decoder, + GstCodecPicture * picture) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + return gst_d3d12_decoder_new_picture (self->decoder, + GST_VIDEO_DECODER (decoder), picture); +} + +static guint8 +gst_d3d12_h265_dec_get_picture_id (GstDxvaH265Decoder * decoder, + GstCodecPicture * picture) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + return gst_d3d12_decoder_get_picture_id (self->decoder, picture); +} + +static GstFlowReturn +gst_d3d12_h265_dec_start_picture (GstDxvaH265Decoder * decoder, + GstCodecPicture * picture, guint8 * picture_id) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + return gst_d3d12_decoder_start_picture (self->decoder, picture, picture_id); +} + +static GstFlowReturn +gst_d3d12_h265_dec_end_picture (GstDxvaH265Decoder * decoder, + GstCodecPicture * picture, GPtrArray * ref_pics, + const GstDxvaDecodingArgs * args) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + return gst_d3d12_decoder_end_picture (self->decoder, picture, ref_pics, args); +} + +static GstFlowReturn +gst_d3d12_h265_dec_output_picture (GstDxvaH265Decoder * decoder, + GstVideoCodecFrame * frame, GstCodecPicture * picture, + GstVideoBufferFlags buffer_flags, gint display_width, gint display_height) +{ + GstD3D12H265Dec *self = GST_D3D12_H265_DEC (decoder); + + return gst_d3d12_decoder_output_picture (self->decoder, + GST_VIDEO_DECODER (decoder), frame, picture, + buffer_flags, display_width, display_height); +} + +void +gst_d3d12_h265_dec_register (GstPlugin * plugin, GstD3D12Device * device, + ID3D12VideoDevice * video_device, guint rank) +{ + GType type; + gchar *type_name; + gchar *feature_name; + guint index = 0; + GTypeInfo type_info = { + sizeof (GstD3D12H265DecClass), + nullptr, + nullptr, + (GClassInitFunc) gst_d3d12_h265_dec_class_init, + nullptr, + nullptr, + sizeof (GstD3D12H265Dec), + 0, + (GInstanceInitFunc) gst_d3d12_h265_dec_init, + }; + + GST_DEBUG_CATEGORY_INIT (gst_d3d12_h265_dec_debug, "d3d12h265dec", 0, + "d3d12h265dec"); + + type_info.class_data = + gst_d3d12_decoder_check_feature_support (device, video_device, + GST_DXVA_CODEC_H265); + if (!type_info.class_data) + return; + + type_name = g_strdup ("GstD3D12H265Dec"); + feature_name = g_strdup ("d3d12h265dec"); + + while (g_type_from_name (type_name)) { + index++; + g_free (type_name); + g_free (feature_name); + type_name = g_strdup_printf ("GstD3D12H265Device%dDec", index); + feature_name = g_strdup_printf ("d3d12h265device%ddec", index); + } + + type = g_type_register_static (GST_TYPE_DXVA_H265_DECODER, + type_name, &type_info, (GTypeFlags) 0); + + /* make lower rank than default device */ + if (rank > 0 && index != 0) + rank--; + + if (!gst_element_register (plugin, feature_name, rank, type)) + GST_WARNING ("Failed to register plugin '%s'", type_name); + + g_free (type_name); + g_free (feature_name); +} diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h265dec.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h265dec.h new file mode 100644 index 0000000000..3de9de9260 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12h265dec.h @@ -0,0 +1,32 @@ +/* GStreamer + * Copyright (C) 2023 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "gstd3d12decoder.h" + +G_BEGIN_DECLS + +void gst_d3d12_h265_dec_register (GstPlugin * plugin, + GstD3D12Device * device, + ID3D12VideoDevice * video_device, + guint rank); + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/sys/d3d12/meson.build b/subprojects/gst-plugins-bad/sys/d3d12/meson.build index e9e48a9a73..11d850bb3d 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/meson.build +++ b/subprojects/gst-plugins-bad/sys/d3d12/meson.build @@ -4,6 +4,7 @@ d3d12_sources = [ 'gstd3d12fence.cpp', 'gstd3d12format.cpp', 'gstd3d12h264dec.cpp', + 'gstd3d12h265dec.cpp', 'gstd3d12memory.cpp', 'gstd3d12utils.cpp', 'plugin.cpp', diff --git a/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp index d3432a5dae..3ebd97ecb2 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp @@ -24,6 +24,7 @@ #include #include "gstd3d12device.h" #include "gstd3d12h264dec.h" +#include "gstd3d12h265dec.h" #include @@ -80,6 +81,8 @@ plugin_init (GstPlugin * plugin) gst_d3d12_h264_dec_register (plugin, device, video_device.Get (), GST_RANK_NONE); + gst_d3d12_h265_dec_register (plugin, device, video_device.Get (), + GST_RANK_NONE); gst_object_unref (device); }