From 10bda3a12a0f61cbab24c96850b6755a864398dc Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Fri, 15 Sep 2023 23:53:24 +0900 Subject: [PATCH] d3d12: Add VP9 decoder Part-of: --- .../sys/d3d12/gstd3d12decoder.cpp | 83 +++-- .../sys/d3d12/gstd3d12vp9dec.cpp | 327 ++++++++++++++++++ .../sys/d3d12/gstd3d12vp9dec.h | 32 ++ .../gst-plugins-bad/sys/d3d12/meson.build | 1 + .../gst-plugins-bad/sys/d3d12/plugin.cpp | 3 + 5 files changed, 422 insertions(+), 24 deletions(-) create mode 100644 subprojects/gst-plugins-bad/sys/d3d12/gstd3d12vp9dec.cpp create mode 100644 subprojects/gst-plugins-bad/sys/d3d12/gstd3d12vp9dec.h diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp index 957e380c20..475548ada2 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12decoder.cpp @@ -53,6 +53,9 @@ static const DecoderFormat format_list[] = { {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}, + {GST_DXVA_CODEC_VP9, D3D12_VIDEO_DECODE_PROFILE_VP9, DXGI_FORMAT_NV12}, + {GST_DXVA_CODEC_VP9, D3D12_VIDEO_DECODE_PROFILE_VP9_10BIT_PROFILE2, + DXGI_FORMAT_P010}, }; /* *INDENT-OFF* */ @@ -1009,15 +1012,26 @@ gst_d3d12_decoder_output_picture (GstD3D12Decoder * decoder, goto error; } - if (picture->discont_state) { - g_clear_pointer (&priv->input_state, gst_video_codec_state_unref); - priv->input_state = gst_video_codec_state_ref (picture->discont_state); + if (display_width != GST_VIDEO_INFO_WIDTH (&priv->output_info) || + display_height != GST_VIDEO_INFO_HEIGHT (&priv->output_info)) { + GST_INFO_OBJECT (videodec, "Frame size changed, do renegotiate"); + + gst_video_info_set_format (&priv->output_info, + GST_VIDEO_INFO_FORMAT (&priv->info), display_width, display_height); + GST_VIDEO_INFO_INTERLACE_MODE (&priv->output_info) = + GST_VIDEO_INFO_INTERLACE_MODE (&priv->info); if (!gst_video_decoder_negotiate (videodec)) { GST_ERROR_OBJECT (videodec, "Failed to re-negotiate with new frame size"); ret = GST_FLOW_NOT_NEGOTIATED; goto error; } + } else if (picture->discont_state) { + if (!gst_video_decoder_negotiate (videodec)) { + GST_ERROR_OBJECT (videodec, "Could not re-negotiate with updated state"); + ret = GST_FLOW_NOT_NEGOTIATED; + goto error; + } } ret = gst_video_decoder_allocate_output_frame (videodec, frame); @@ -1405,6 +1419,10 @@ gst_d3d12_decoder_get_profiles (const GUID & profile, list.push_back ("main"); } else if (profile == D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN10) { list.push_back ("main-10"); + } else if (profile == D3D12_VIDEO_DECODE_PROFILE_VP9) { + list.push_back ("0"); + } else if (profile == D3D12_VIDEO_DECODE_PROFILE_VP9_10BIT_PROFILE2) { + list.push_back ("2"); } else { g_assert_not_reached (); } @@ -1505,26 +1523,8 @@ gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, src_caps_string += format_string; } - 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; + std::string profile_string; switch (codec) { case GST_DXVA_CODEC_H264: @@ -1537,13 +1537,48 @@ gst_d3d12_decoder_check_feature_support (GstD3D12Device * device, "stream-format=(string) { hev1, hvc1, byte-stream }, " "alignment=(string) au"; break; + case GST_DXVA_CODEC_VP9: + if (profiles.size () > 1) { + sink_caps_string = + "video/x-vp9, alignment = (string) frame, profile = (string) 0; " + "video/x-vp9, alignment = (string) frame, profile = (string) 2, " + "bit-depth-luma = (uint) 10, bit-depth-chroma = (uint) 10"; + } else if (profiles[0] == "0") { + sink_caps_string = + "video/x-vp9, alignment = (string) frame, profile = (string) 0"; + } else { + sink_caps_string = + "video/x-vp9, alignment = (string) frame, profile = (string) 2, " + "bit-depth-luma = (uint) 10, bit-depth-chroma = (uint) 10"; + } + break; default: g_assert_not_reached (); return nullptr; } - sink_caps_string += ", profile=(string) "; - sink_caps_string += profile_string; + /* *INDENT-OFF* */ + if (codec != GST_DXVA_CODEC_VP9) { + 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]; + } + + sink_caps_string += ", profile=(string) "; + sink_caps_string += profile_string; + } + /* *INDENT-ON* */ GstCaps *src_caps = gst_caps_from_string (src_caps_string.c_str ()); GstCaps *sink_caps = gst_caps_from_string (sink_caps_string.c_str ()); diff --git a/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12vp9dec.cpp b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12vp9dec.cpp new file mode 100644 index 0000000000..61c128f246 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12vp9dec.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-d3d12vp9dec + * @title: d3d12vp9dec + * + * A Direct3D12 based VP9 video decoder + * + * ## Example launch line + * ``` + * gst-launch-1.0 filesrc location=/path/to/vp9/file ! parsebin ! d3d12vp9dec ! videoconvert ! autovideosink + * ``` + * + * Since: 1.24 + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstd3d12vp9dec.h" +#include "gstd3d12device.h" +#include "gstd3d12utils.h" +#include + +GST_DEBUG_CATEGORY_STATIC (gst_d3d12_vp9_dec_debug); +#define GST_CAT_DEFAULT gst_d3d12_vp9_dec_debug + +GST_D3D12_DECODER_DEFINE_TYPE_FULL (GstD3D12Vp9Dec, gst_d3d12_vp9_dec, + GST, D3D12_VP9_DEC, GstDxvaVp9Decoder); + +static void +gst_d3d12_vp9_dec_class_init (GstD3D12Vp9DecClass * 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); + GstDxvaVp9DecoderClass *dxva_class = GST_DXVA_VP9_DECODER_CLASS (klass); + GstD3D12DecoderClassData *cdata = (GstD3D12DecoderClassData *) data; + + gobject_class->get_property = gst_d3d12_vp9_dec_get_property; + + element_class->set_context = + GST_DEBUG_FUNCPTR (gst_d3d12_vp9_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_vp9_dec_open); + decoder_class->close = GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_close); + decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_negotiate); + decoder_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_decide_allocation); + decoder_class->sink_query = GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_sink_query); + decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_src_query); + + dxva_class->configure = GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_configure); + dxva_class->new_picture = GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_new_picture); + dxva_class->duplicate_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_duplicate_picture); + dxva_class->get_picture_id = + GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_get_picture_id); + dxva_class->start_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_start_picture); + dxva_class->end_picture = GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_end_picture); + dxva_class->output_picture = + GST_DEBUG_FUNCPTR (gst_d3d12_vp9_dec_output_picture); +} + +static void +gst_d3d12_vp9_dec_init (GstD3D12Vp9Dec * self) +{ +} + +static void +gst_d3d12_vp9_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstD3D12Vp9DecClass *klass = GST_D3D12_VP9_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_vp9_dec_set_context (GstElement * element, GstContext * context) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_DEC (element); + GstD3D12Vp9DecClass *klass = GST_D3D12_VP9_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_vp9_dec_open (GstVideoDecoder * decoder) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_DEC (decoder); + GstD3D12Vp9DecClass *klass = GST_D3D12_VP9_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_vp9_dec_close (GstVideoDecoder * decoder) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_DEC (decoder); + + gst_clear_object (&self->decoder); + gst_clear_object (&self->device); + + return TRUE; +} + +static gboolean +gst_d3d12_vp9_dec_negotiate (GstVideoDecoder * decoder) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_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_vp9_dec_decide_allocation (GstVideoDecoder * decoder, + GstQuery * query) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_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_vp9_dec_sink_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_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_vp9_dec_src_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_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_vp9_dec_configure (GstDxvaVp9Decoder * decoder, + GstVideoCodecState * input_state, const GstVideoInfo * info, + gint crop_x, gint crop_y, gint coded_width, gint coded_height, + gint max_dpb_size) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_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_vp9_dec_new_picture (GstDxvaVp9Decoder * decoder, + GstCodecPicture * picture) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_DEC (decoder); + + return gst_d3d12_decoder_new_picture (self->decoder, + GST_VIDEO_DECODER (decoder), picture); +} + +static GstFlowReturn +gst_d3d12_vp9_dec_duplicate_picture (GstDxvaVp9Decoder * decoder, + GstCodecPicture * src, GstCodecPicture * dst) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_DEC (decoder); + + return gst_d3d12_decoder_duplicate_picture (self->decoder, src, dst); +} + +static guint8 +gst_d3d12_vp9_dec_get_picture_id (GstDxvaVp9Decoder * decoder, + GstCodecPicture * picture) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_DEC (decoder); + + return gst_d3d12_decoder_get_picture_id (self->decoder, picture); +} + +static GstFlowReturn +gst_d3d12_vp9_dec_start_picture (GstDxvaVp9Decoder * decoder, + GstCodecPicture * picture, guint8 * picture_id) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_DEC (decoder); + + return gst_d3d12_decoder_start_picture (self->decoder, picture, picture_id); +} + +static GstFlowReturn +gst_d3d12_vp9_dec_end_picture (GstDxvaVp9Decoder * decoder, + GstCodecPicture * picture, GPtrArray * ref_pics, + const GstDxvaDecodingArgs * args) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_DEC (decoder); + + return gst_d3d12_decoder_end_picture (self->decoder, picture, ref_pics, args); +} + +static GstFlowReturn +gst_d3d12_vp9_dec_output_picture (GstDxvaVp9Decoder * decoder, + GstVideoCodecFrame * frame, GstCodecPicture * picture, + GstVideoBufferFlags buffer_flags, gint display_width, gint display_height) +{ + GstD3D12Vp9Dec *self = GST_D3D12_VP9_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_vp9_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 (GstD3D12Vp9DecClass), + nullptr, + nullptr, + (GClassInitFunc) gst_d3d12_vp9_dec_class_init, + nullptr, + nullptr, + sizeof (GstD3D12Vp9Dec), + 0, + (GInstanceInitFunc) gst_d3d12_vp9_dec_init, + }; + + GST_DEBUG_CATEGORY_INIT (gst_d3d12_vp9_dec_debug, "d3d12vp9dec", 0, + "d3d12vp9dec"); + + type_info.class_data = + gst_d3d12_decoder_check_feature_support (device, video_device, + GST_DXVA_CODEC_VP9); + if (!type_info.class_data) + return; + + type_name = g_strdup ("GstD3D12Vp9Dec"); + feature_name = g_strdup ("d3d12vp9dec"); + + 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 ("d3d12vp9device%ddec", index); + } + + type = g_type_register_static (GST_TYPE_DXVA_VP9_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/gstd3d12vp9dec.h b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12vp9dec.h new file mode 100644 index 0000000000..63089ddda0 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/d3d12/gstd3d12vp9dec.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_vp9_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 11d850bb3d..141d4d73d1 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/meson.build +++ b/subprojects/gst-plugins-bad/sys/d3d12/meson.build @@ -7,6 +7,7 @@ d3d12_sources = [ 'gstd3d12h265dec.cpp', 'gstd3d12memory.cpp', 'gstd3d12utils.cpp', + 'gstd3d12vp9dec.cpp', 'plugin.cpp', ] diff --git a/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp index 3ebd97ecb2..2c190ff14e 100644 --- a/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp +++ b/subprojects/gst-plugins-bad/sys/d3d12/plugin.cpp @@ -25,6 +25,7 @@ #include "gstd3d12device.h" #include "gstd3d12h264dec.h" #include "gstd3d12h265dec.h" +#include "gstd3d12vp9dec.h" #include @@ -83,6 +84,8 @@ plugin_init (GstPlugin * plugin) GST_RANK_NONE); gst_d3d12_h265_dec_register (plugin, device, video_device.Get (), GST_RANK_NONE); + gst_d3d12_vp9_dec_register (plugin, device, video_device.Get (), + GST_RANK_NONE); gst_object_unref (device); }