diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12av1dec.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12av1dec.cpp new file mode 100644 index 0000000000..eef480b27c --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12av1dec.cpp @@ -0,0 +1,327 @@ +/* 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-d3d12av1dec + * @title: d3d12av1dec + * + * A Direct3D12 based AV1 video decoder + * + * ## Example launch line + * ``` + * gst-launch-1.0 filesrc location=/path/to/av1/file ! parsebin ! d3d12av1dec ! videoconvert ! autovideosink + * ``` + * + * Since: 1.24 + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstd3d12av1dec.h" +#include "gstd3d12device.h" +#include "gstd3d12utils.h" +#include + +GST_DEBUG_CATEGORY_STATIC (gst_d3d12_av1_dec_debug); +#define GST_CAT_DEFAULT gst_d3d12_av1_dec_debug + +GST_D3D12_DECODER_DEFINE_TYPE_FULL (GstD3D12AV1Dec, gst_d3d12_av1_dec, + GST, D3D12_AV1_DEC, GstDxvaAV1Decoder); + +static void +gst_d3d12_av1_dec_class_init (GstD3D12AV1DecClass * 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); + GstDxvaAV1DecoderClass *dxva_class = GST_DXVA_AV1_DECODER_CLASS (klass); + GstD3D12DecoderClassData *cdata = (GstD3D12DecoderClassData *) data; + + gobject_class->get_property = gst_d3d12_av1_dec_get_property; + + element_class->set_context = + GST_DEBUG_FUNCPTR (gst_d3d12_av1_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_av1_dec_open); + decoder_class->close = GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_close); + decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_negotiate); + decoder_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_decide_allocation); + decoder_class->sink_query = GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_sink_query); + decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_src_query); + + dxva_class->configure = GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_configure); + dxva_class->new_picture = GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_new_picture); + dxva_class->duplicate_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_duplicate_picture); + dxva_class->get_picture_id = + GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_get_picture_id); + dxva_class->start_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_start_picture); + dxva_class->end_picture = GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_end_picture); + dxva_class->output_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_av1_dec_output_picture); +} + +static void +gst_d3d12_av1_dec_init (GstD3D12AV1Dec * self) +{ +} + +static void +gst_d3d12_av1_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstD3D12AV1DecClass *klass = GST_D3D12_AV1_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_av1_dec_set_context (GstElement * element, GstContext * context) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_DEC (element); + GstD3D12AV1DecClass *klass = GST_D3D12_AV1_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_av1_dec_open (GstVideoDecoder * decoder) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_DEC (decoder); + GstD3D12AV1DecClass *klass = GST_D3D12_AV1_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_av1_dec_close (GstVideoDecoder * decoder) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_DEC (decoder); + + gst_clear_object (&self->decoder); + gst_clear_object (&self->device); + + return TRUE; +} + +static gboolean +gst_d3d12_av1_dec_negotiate (GstVideoDecoder * decoder) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_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_av1_dec_decide_allocation (GstVideoDecoder * decoder, + GstQuery * query) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_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_av1_dec_sink_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_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_av1_dec_src_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_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_av1_dec_configure (GstDxvaAV1Decoder * decoder, + GstVideoCodecState * input_state, const GstVideoInfo * info, + gint crop_x, gint crop_y, gint coded_width, gint coded_height, + gint max_dpb_size) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_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_av1_dec_new_picture (GstDxvaAV1Decoder * decoder, + GstCodecPicture * picture) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_DEC (decoder); + + return gst_d3d12_decoder_new_picture (self->decoder, + GST_VIDEO_DECODER (decoder), picture); +} + +static GstFlowReturn +gst_d3d12_av1_dec_duplicate_picture (GstDxvaAV1Decoder * decoder, + GstCodecPicture * src, GstCodecPicture * dst) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_DEC (decoder); + + return gst_d3d12_decoder_duplicate_picture (self->decoder, src, dst); +} + +static guint8 +gst_d3d12_av1_dec_get_picture_id (GstDxvaAV1Decoder * decoder, + GstCodecPicture * picture) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_DEC (decoder); + + return gst_d3d12_decoder_get_picture_id (self->decoder, picture); +} + +static GstFlowReturn +gst_d3d12_av1_dec_start_picture (GstDxvaAV1Decoder * decoder, + GstCodecPicture * picture, guint8 * picture_id) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_DEC (decoder); + + return gst_d3d12_decoder_start_picture (self->decoder, picture, picture_id); +} + +static GstFlowReturn +gst_d3d12_av1_dec_end_picture (GstDxvaAV1Decoder * decoder, + GstCodecPicture * picture, GPtrArray * ref_pics, + const GstDxvaDecodingArgs * args) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_DEC (decoder); + + return gst_d3d12_decoder_end_picture (self->decoder, picture, ref_pics, args); +} + +static GstFlowReturn +gst_d3d12_av1_dec_output_picture (GstDxvaAV1Decoder * decoder, + GstVideoCodecFrame * frame, GstCodecPicture * picture, + GstVideoBufferFlags buffer_flags, gint display_width, gint display_height) +{ + GstD3D12AV1Dec *self = GST_D3D12_AV1_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_av1_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 (GstD3D12AV1DecClass), + nullptr, + nullptr, + (GClassInitFunc) gst_d3d12_av1_dec_class_init, + nullptr, + nullptr, + sizeof (GstD3D12AV1Dec), + 0, + (GInstanceInitFunc) gst_d3d12_av1_dec_init, + }; + + GST_DEBUG_CATEGORY_INIT (gst_d3d12_av1_dec_debug, "d3d12av1dec", 0, + "d3d12av1dec"); + + type_info.class_data = + gst_d3d12_decoder_check_feature_support (device, video_device, + GST_DXVA_CODEC_AV1); + if (!type_info.class_data) + return; + + type_name = g_strdup ("GstD3D12AV1Dec"); + feature_name = g_strdup ("d3d12av1dec"); + + while (g_type_from_name (type_name)) { + index++; + g_free (type_name); + g_free (feature_name); + type_name = g_strdup_printf ("GstD3D12Vp9Device%dDec", index); + feature_name = g_strdup_printf ("d3d12av1device%ddec", index); + } + + type = g_type_register_static (GST_TYPE_DXVA_AV1_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/gstd3d12av1dec.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12av1dec.h new file mode 100644 index 0000000000..7d977ddb8e --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12av1dec.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_av1_dec_register (GstPlugin * plugin, + GstD3D12Device * device, + ID3D12VideoDevice * video_device, + guint rank); + +G_END_DECLS + diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp index 475548ada2..e25f83bcc7 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp @@ -37,6 +37,7 @@ #include #include #include +#include GST_DEBUG_CATEGORY_EXTERN (gst_d3d12_decoder_debug); #define GST_CAT_DEFAULT gst_d3d12_decoder_debug @@ -45,17 +46,22 @@ struct DecoderFormat { GstDxvaCodec codec; const GUID decode_profile; - DXGI_FORMAT format; + DXGI_FORMAT format[3]; }; static const DecoderFormat format_list[] = { - {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_H264, D3D12_VIDEO_DECODE_PROFILE_H264, + {DXGI_FORMAT_NV12, DXGI_FORMAT_UNKNOWN,}}, + {GST_DXVA_CODEC_H265, D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN, + {DXGI_FORMAT_NV12, DXGI_FORMAT_UNKNOWN,}}, {GST_DXVA_CODEC_H265, D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN10, DXGI_FORMAT_P010}, - {GST_DXVA_CODEC_VP9, D3D12_VIDEO_DECODE_PROFILE_VP9, DXGI_FORMAT_NV12}, + {GST_DXVA_CODEC_VP9, D3D12_VIDEO_DECODE_PROFILE_VP9, + {DXGI_FORMAT_NV12, DXGI_FORMAT_UNKNOWN,}}, {GST_DXVA_CODEC_VP9, D3D12_VIDEO_DECODE_PROFILE_VP9_10BIT_PROFILE2, - DXGI_FORMAT_P010}, + {DXGI_FORMAT_P010, DXGI_FORMAT_UNKNOWN,}}, + {GST_DXVA_CODEC_AV1, D3D12_VIDEO_DECODE_PROFILE_AV1_PROFILE0, + {DXGI_FORMAT_NV12, DXGI_FORMAT_P010}}, }; /* *INDENT-OFF* */ @@ -766,6 +772,7 @@ gst_d3d12_decoder_end_picture (GstD3D12Decoder * decoder, ID3D12Fence *fence_handle; std::vector < D3D12_RESOURCE_BARRIER > pre_barriers; std::vector < D3D12_RESOURCE_BARRIER > post_barriers; + std::vector < GstD3D12DecoderPicture * >configured_ref_pics; if (!decoder_pic) { GST_ERROR_OBJECT (decoder, "No attached decoder picture"); @@ -777,6 +784,9 @@ gst_d3d12_decoder_end_picture (GstD3D12Decoder * decoder, return GST_FLOW_ERROR; } + GST_LOG_OBJECT (decoder, "End picture with dxva-id %d, num-ref-pics %u", + decoder_pic->view_id, ref_pics->len); + /* Wait for previous fence if needed */ gst_d3d12_fence_wait (priv->fence); @@ -809,6 +819,13 @@ gst_d3d12_decoder_end_picture (GstD3D12Decoder * decoder, if (!ref_dec_pic || ref_dec_pic == decoder_pic) continue; + if (std::find (configured_ref_pics.begin (), configured_ref_pics.end (), + ref_dec_pic) != configured_ref_pics.end ()) { + continue; + } + + configured_ref_pics.push_back (ref_dec_pic); + dmem = (GstD3D12Memory *) ref_dec_pic->mem; resource = gst_d3d12_memory_get_resource_handle (dmem); @@ -1185,11 +1202,26 @@ gst_d3d12_decoder_open (GstD3D12Decoder * self) const DecoderFormat *decoder_foramt = nullptr; for (guint i = 0; i < G_N_ELEMENTS (format_list); i++) { - if (format_list[i].codec != priv->codec || - format_list[i].format != priv->decoder_format) { + decoder_foramt = nullptr; + + if (format_list[i].codec != priv->codec) continue; + + for (guint j = 0; j < G_N_ELEMENTS (format_list[i].format); j++) { + DXGI_FORMAT format = format_list[i].format[j]; + + if (format == DXGI_FORMAT_UNKNOWN) + break; + + if (format == priv->decoder_format) { + decoder_foramt = &format_list[i]; + break; + } } + if (!decoder_foramt) + continue; + D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT s; s.NodeIndex = 0; @@ -1220,7 +1252,6 @@ gst_d3d12_decoder_open (GstD3D12Decoder * self) continue; } - decoder_foramt = &format_list[i]; support = s; GST_INFO_OBJECT (self, @@ -1423,6 +1454,8 @@ gst_d3d12_decoder_get_profiles (const GUID & profile, list.push_back ("0"); } else if (profile == D3D12_VIDEO_DECODE_PROFILE_VP9_10BIT_PROFILE2) { list.push_back ("2"); + } else if (profile == D3D12_VIDEO_DECODE_PROFILE_AV1_PROFILE0) { + list.push_back ("main"); } else { g_assert_not_reached (); } @@ -1452,33 +1485,38 @@ gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, s.Configuration.DecodeProfile = format_list[i].decode_profile; s.Configuration.BitstreamEncryption = D3D12_BITSTREAM_ENCRYPTION_TYPE_NONE; s.Configuration.InterlaceType = D3D12_VIDEO_FRAME_CODED_INTERLACE_TYPE_NONE; - s.DecodeFormat = format_list[i].format; 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; - - hr = video_device->CheckFeatureSupport - (D3D12_FEATURE_VIDEO_DECODE_SUPPORT, &s, - sizeof (D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT)); - if (FAILED (hr)) + for (guint j = 0; j < G_N_ELEMENTS (format_list[i].format); j++) { + s.DecodeFormat = format_list[i].format[j]; + if (s.DecodeFormat == DXGI_FORMAT_UNKNOWN) break; - if ((s.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED) == 0) - break; + for (guint k = 0; k < G_N_ELEMENTS (gst_dxva_resolutions); k++) { + s.Width = gst_dxva_resolutions[k].width; + s.Height = gst_dxva_resolutions[k].height; - if (max_resolution.width < gst_dxva_resolutions[j].width) - max_resolution.width = gst_dxva_resolutions[j].width; - if (max_resolution.height < gst_dxva_resolutions[j].height) - max_resolution.height = gst_dxva_resolutions[j].height; + hr = video_device->CheckFeatureSupport + (D3D12_FEATURE_VIDEO_DECODE_SUPPORT, &s, + sizeof (D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT)); + if (FAILED (hr)) + break; - supported_formats.insert (format_list[i].format); - config_flags = s.ConfigurationFlags; - tier = s.DecodeTier; - supported = true; + if ((s.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED) == 0) + break; + + if (max_resolution.width < gst_dxva_resolutions[k].width) + max_resolution.width = gst_dxva_resolutions[k].width; + if (max_resolution.height < gst_dxva_resolutions[k].height) + max_resolution.height = gst_dxva_resolutions[k].height; + + supported_formats.insert (format_list[i].format[j]); + config_flags = s.ConfigurationFlags; + tier = s.DecodeTier; + supported = true; + } } if (supported) @@ -1552,6 +1590,9 @@ gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, "bit-depth-luma = (uint) 10, bit-depth-chroma = (uint) 10"; } break; + case GST_DXVA_CODEC_AV1: + sink_caps_string = "video/x-av1, alignment = (string) frame"; + break; default: g_assert_not_reached (); return nullptr; diff --git a/subprojects/gst-plugins-bad/sys/d3d12/meson.build b/subprojects/gst-plugins-bad/sys/d3d12/meson.build index 141d4d73d1..95c9fae153 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/meson.build +++ b/subprojects/gst-plugins-bad/sys/d3d12/meson.build @@ -1,4 +1,5 @@ d3d12_sources = [ + 'gstd3d12av1dec.cpp', 'gstd3d12decoder.cpp', 'gstd3d12device.cpp', 'gstd3d12fence.cpp', diff --git a/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp index 2c190ff14e..1c352717b7 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp @@ -26,6 +26,7 @@ #include "gstd3d12h264dec.h" #include "gstd3d12h265dec.h" #include "gstd3d12vp9dec.h" +#include "gstd3d12av1dec.h" #include @@ -86,6 +87,8 @@ plugin_init (GstPlugin * plugin) GST_RANK_NONE); gst_d3d12_vp9_dec_register (plugin, device, video_device.Get (), GST_RANK_NONE); + gst_d3d12_av1_dec_register (plugin, device, video_device.Get (), + GST_RANK_NONE); gst_object_unref (device); }