diff --git a/sys/d3d11/gstd3d11av1dec.cpp b/sys/d3d11/gstd3d11av1dec.cpp new file mode 100644 index 0000000000..c8bc369715 --- /dev/null +++ b/sys/d3d11/gstd3d11av1dec.cpp @@ -0,0 +1,1505 @@ +/* GStreamer + * Copyright (C) 2021 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. + */ + +/** + * SECTION:element-d3d11av19dec + * @title: d3d11av1dec + * + * A Direct3D11/DXVA based AV1 video decoder + * + * ## Example launch line + * ``` + * gst-launch-1.0 filesrc location=/path/to/av1/file ! parsebin ! d3d11av1dec ! d3d11videosink + * ``` + * + * Since: 1.20 + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstd3d11av1dec.h" + +#include +#include + +/* HACK: to expose dxva data structure on UWP */ +#ifdef WINAPI_PARTITION_DESKTOP +#undef WINAPI_PARTITION_DESKTOP +#endif +#define WINAPI_PARTITION_DESKTOP 1 +#include +#include + +/* Might not be defined in dxva.h, copied from DXVA AV1 spec available at + * https://www.microsoft.com/en-us/download/confirmation.aspx?id=101577 + * and modified with "GST_" prefix + */ +#pragma pack(push, 1) +typedef struct _GST_DXVA_PicEntry_AV1 +{ + UINT width; + UINT height; + + // Global motion parameters + INT wmmat[6]; + union + { + struct + { + UCHAR wminvalid:1; + UCHAR wmtype:2; + UCHAR Reserved:5; + }; + UCHAR wGlobalMotionFlags; + }; + + UCHAR Index; + USHORT Reserved16Bits; + +} GST_DXVA_PicEntry_AV1; + +/* AV1 picture parameters structure */ +typedef struct _GST_DXVA_PicParams_AV1 +{ + UINT width; + UINT height; + + UINT max_width; + UINT max_height; + + UCHAR CurrPicTextureIndex; + UCHAR superres_denom; + UCHAR bitdepth; + UCHAR seq_profile; + + // Tiles: + struct + { + UCHAR cols; + UCHAR rows; + USHORT context_update_id; + USHORT widths[64]; + USHORT heights[64]; + } tiles; + + // Coding Tools + union + { + struct + { + UINT use_128x128_superblock:1; + UINT intra_edge_filter:1; + UINT interintra_compound:1; + UINT masked_compound:1; + UINT warped_motion:1; + UINT dual_filter:1; + UINT jnt_comp:1; + UINT screen_content_tools:1; + UINT integer_mv:1; + UINT cdef:1; + UINT restoration:1; + UINT film_grain:1; + UINT intrabc:1; + UINT high_precision_mv:1; + UINT switchable_motion_mode:1; + UINT filter_intra:1; + UINT disable_frame_end_update_cdf:1; + UINT disable_cdf_update:1; + UINT reference_mode:1; + UINT skip_mode:1; + UINT reduced_tx_set:1; + UINT superres:1; + UINT tx_mode:2; + UINT use_ref_frame_mvs:1; + UINT enable_ref_frame_mvs:1; + UINT reference_frame_update:1; + UINT Reserved:5; + }; + UINT32 CodingParamToolFlags; + } coding; + + // Format & Picture Info flags + union + { + struct + { + UCHAR frame_type:2; + UCHAR show_frame:1; + UCHAR showable_frame:1; + UCHAR subsampling_x:1; + UCHAR subsampling_y:1; + UCHAR mono_chrome:1; + UCHAR Reserved:1; + }; + UCHAR FormatAndPictureInfoFlags; + } format; + + // References + UCHAR primary_ref_frame; + UCHAR order_hint; + UCHAR order_hint_bits; + + GST_DXVA_PicEntry_AV1 frame_refs[7]; + UCHAR RefFrameMapTextureIndex[8]; + + // Loop filter parameters + struct + { + UCHAR filter_level[2]; + UCHAR filter_level_u; + UCHAR filter_level_v; + + UCHAR sharpness_level; + union + { + struct + { + UCHAR mode_ref_delta_enabled:1; + UCHAR mode_ref_delta_update:1; + UCHAR delta_lf_multi:1; + UCHAR delta_lf_present:1; + UCHAR Reserved:4; + }; + UCHAR ControlFlags; + } DUMMYUNIONNAME; + CHAR ref_deltas[8]; + CHAR mode_deltas[2]; + UCHAR delta_lf_res; + UCHAR frame_restoration_type[3]; + USHORT log2_restoration_unit_size[3]; + UINT16 Reserved16Bits; + } loop_filter; + + // Quantization + struct + { + union + { + struct + { + UCHAR delta_q_present:1; + UCHAR delta_q_res:2; + UCHAR Reserved:5; + }; + UCHAR ControlFlags; + } DUMMYUNIONNAME; + + UCHAR base_qindex; + CHAR y_dc_delta_q; + CHAR u_dc_delta_q; + CHAR v_dc_delta_q; + CHAR u_ac_delta_q; + CHAR v_ac_delta_q; + // using_qmatrix: + UCHAR qm_y; + UCHAR qm_u; + UCHAR qm_v; + UINT16 Reserved16Bits; + } quantization; + + // Cdef parameters + struct + { + union + { + struct + { + UCHAR damping:2; + UCHAR bits:2; + UCHAR Reserved:4; + }; + UCHAR ControlFlags; + } DUMMYUNIONNAME; + + union + { + struct + { + UCHAR primary:6; + UCHAR secondary:2; + }; + UCHAR combined; + } y_strengths[8]; + + union + { + struct + { + UCHAR primary:6; + UCHAR secondary:2; + }; + UCHAR combined; + } uv_strengths[8]; + + } cdef; + + UCHAR interp_filter; + + // Segmentation + struct + { + union + { + struct + { + UCHAR enabled:1; + UCHAR update_map:1; + UCHAR update_data:1; + UCHAR temporal_update:1; + UCHAR Reserved:4; + }; + UCHAR ControlFlags; + } DUMMYUNIONNAME; + UCHAR Reserved24Bits[3]; + + union + { + struct + { + UCHAR alt_q:1; + UCHAR alt_lf_y_v:1; + UCHAR alt_lf_y_h:1; + UCHAR alt_lf_u:1; + UCHAR alt_lf_v:1; + UCHAR ref_frame:1; + UCHAR skip:1; + UCHAR globalmv:1; + }; + UCHAR mask; + } feature_mask[8]; + + SHORT feature_data[8][8]; + + } segmentation; + + struct + { + union + { + struct + { + USHORT apply_grain:1; + USHORT scaling_shift_minus8:2; + USHORT chroma_scaling_from_luma:1; + USHORT ar_coeff_lag:2; + USHORT ar_coeff_shift_minus6:2; + USHORT grain_scale_shift:2; + USHORT overlap_flag:1; + USHORT clip_to_restricted_range:1; + USHORT matrix_coeff_is_identity:1; + USHORT Reserved:3; + }; + USHORT ControlFlags; + } DUMMYUNIONNAME; + + USHORT grain_seed; + UCHAR scaling_points_y[14][2]; + UCHAR num_y_points; + UCHAR scaling_points_cb[10][2]; + UCHAR num_cb_points; + UCHAR scaling_points_cr[10][2]; + UCHAR num_cr_points; + UCHAR ar_coeffs_y[24]; + UCHAR ar_coeffs_cb[25]; + UCHAR ar_coeffs_cr[25]; + UCHAR cb_mult; + UCHAR cb_luma_mult; + UCHAR cr_mult; + UCHAR cr_luma_mult; + UCHAR Reserved8Bits; + SHORT cb_offset; + SHORT cr_offset; + } film_grain; + + UINT Reserved32Bits; + UINT StatusReportFeedbackNumber; +} GST_DXVA_PicParams_AV1; + +typedef struct _GST_DXVA_Tile_AV1 +{ + UINT DataOffset; + UINT DataSize; + USHORT row; + USHORT column; + USHORT Reserved16Bits; + UCHAR anchor_frame; + UCHAR Reserved8Bits; +} GST_DXVA_Tile_AV1; + +#pragma pack(pop) + +/* *INDENT-OFF* */ + +G_BEGIN_DECLS + +GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_av1_dec_debug); +#define GST_CAT_DEFAULT gst_d3d11_av1_dec_debug + +G_END_DECLS +/* *INDENT-ON* */ + +enum +{ + PROP_0, + PROP_ADAPTER, + PROP_DEVICE_ID, + PROP_VENDOR_ID, +}; + +/* reference list 8 + 4 margin */ +#define NUM_OUTPUT_VIEW 12 + +typedef struct _GstD3D11AV1Dec +{ + GstAV1Decoder parent; + + GstD3D11Device *device; + GstD3D11Decoder *d3d11_decoder; + + GstAV1SequenceHeaderOBU seq_hdr; + + GST_DXVA_PicParams_AV1 pic_params; + /* Array of GST_DXVA_Tile_AV1 */ + GArray *tile_list; + + guint written_buffer_size; + guint remaining_buffer_size; + guint8 *bitstream_buffer_data; + + guint max_width; + guint max_height; + guint bitdepth; +} GstD3D11AV1Dec; + +typedef struct _GstD3D11AV1DecClass +{ + GstAV1DecoderClass parent_class; + guint adapter; + guint device_id; + guint vendor_id; +} GstD3D11AV1DecClass; + +static GstElementClass *parent_class = NULL; + +#define GST_D3D11_AV1_DEC(object) ((GstD3D11AV1Dec *) (object)) +#define GST_D3D11_AV1_DEC_GET_CLASS(object) \ + (G_TYPE_INSTANCE_GET_CLASS ((object),G_TYPE_FROM_INSTANCE (object),GstD3D11AV1DecClass)) + +static void gst_d3d11_av1_dec_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static void gst_d3d11_av1_dec_dispose (GObject * object); + +static void gst_d3d11_av1_dec_set_context (GstElement * element, + GstContext * context); + +static gboolean gst_d3d11_av1_dec_open (GstVideoDecoder * decoder); +static gboolean gst_d3d11_av1_dec_close (GstVideoDecoder * decoder); +static gboolean gst_d3d11_av1_dec_negotiate (GstVideoDecoder * decoder); +static gboolean gst_d3d11_av1_dec_decide_allocation (GstVideoDecoder * + decoder, GstQuery * query); +static gboolean gst_d3d11_av1_dec_src_query (GstVideoDecoder * decoder, + GstQuery * query); +static gboolean gst_d3d11_av1_dec_sink_event (GstVideoDecoder * decoder, + GstEvent * event); + +/* GstAV1Decoder */ +static gboolean gst_d3d11_av1_dec_new_sequence (GstAV1Decoder * decoder, + const GstAV1SequenceHeaderOBU * seq_hdr); +static gboolean gst_d3d11_av1_dec_new_picture (GstAV1Decoder * decoder, + GstVideoCodecFrame * frame, GstAV1Picture * picture); +static GstAV1Picture *gst_d3d11_av1_dec_duplicate_picture (GstAV1Decoder * + decoder, GstAV1Picture * picture); +static gboolean gst_d3d11_av1_dec_start_picture (GstAV1Decoder * decoder, + GstAV1Picture * picture, GstAV1Dpb * dpb); +static gboolean gst_d3d11_av1_dec_decode_tile (GstAV1Decoder * decoder, + GstAV1Picture * picture, GstAV1Tile * tile); +static gboolean gst_d3d11_av1_dec_end_picture (GstAV1Decoder * decoder, + GstAV1Picture * picture); +static GstFlowReturn gst_d3d11_av1_dec_output_picture (GstAV1Decoder * + decoder, GstVideoCodecFrame * frame, GstAV1Picture * picture); + +static void +gst_d3d11_av1_dec_class_init (GstD3D11AV1DecClass * 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); + GstAV1DecoderClass *av1decoder_class = GST_AV1_DECODER_CLASS (klass); + GstD3D11DecoderClassData *cdata = (GstD3D11DecoderClassData *) data; + gchar *long_name; + + gobject_class->get_property = gst_d3d11_av1_dec_get_property; + gobject_class->dispose = gst_d3d11_av1_dec_dispose; + + g_object_class_install_property (gobject_class, PROP_ADAPTER, + g_param_spec_uint ("adapter", "Adapter", + "DXGI Adapter index for creating device", + 0, G_MAXUINT32, cdata->adapter, + (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, PROP_DEVICE_ID, + g_param_spec_uint ("device-id", "Device Id", + "DXGI Device ID", 0, G_MAXUINT32, 0, + (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, PROP_VENDOR_ID, + g_param_spec_uint ("vendor-id", "Vendor Id", + "DXGI Vendor ID", 0, G_MAXUINT32, 0, + (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + + parent_class = (GstElementClass *) g_type_class_peek_parent (klass); + + klass->adapter = cdata->adapter; + klass->device_id = cdata->device_id; + klass->vendor_id = cdata->vendor_id; + + element_class->set_context = + GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_set_context); + + long_name = + g_strdup_printf ("Direct3D11/DXVA AV1 %s Decoder", cdata->description); + gst_element_class_set_metadata (element_class, long_name, + "Codec/Decoder/Video/Hardware", "A Direct3D11/DXVA AV1 video decoder", + "Seungha Yang "); + g_free (long_name); + + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + cdata->sink_caps)); + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + cdata->src_caps)); + gst_d3d11_decoder_class_data_free (cdata); + + decoder_class->open = GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_open); + decoder_class->close = GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_close); + decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_negotiate); + decoder_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_decide_allocation); + decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_src_query); + decoder_class->sink_event = GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_sink_event); + + av1decoder_class->new_sequence = + GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_new_sequence); + av1decoder_class->new_picture = + GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_new_picture); + av1decoder_class->duplicate_picture = + GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_duplicate_picture); + av1decoder_class->start_picture = + GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_start_picture); + av1decoder_class->decode_tile = + GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_decode_tile); + av1decoder_class->end_picture = + GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_end_picture); + av1decoder_class->output_picture = + GST_DEBUG_FUNCPTR (gst_d3d11_av1_dec_output_picture); +} + +static void +gst_d3d11_av1_dec_init (GstD3D11AV1Dec * self) +{ + self->tile_list = g_array_new (FALSE, TRUE, sizeof (GST_DXVA_Tile_AV1)); +} + +static void +gst_d3d11_av1_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstD3D11AV1DecClass *klass = GST_D3D11_AV1_DEC_GET_CLASS (object); + + switch (prop_id) { + case PROP_ADAPTER: + g_value_set_uint (value, klass->adapter); + break; + case PROP_DEVICE_ID: + g_value_set_uint (value, klass->device_id); + break; + case PROP_VENDOR_ID: + g_value_set_uint (value, klass->vendor_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_d3d11_av1_dec_dispose (GObject * object) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (object); + + g_clear_pointer (&self->tile_list, g_array_unref); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_d3d11_av1_dec_set_context (GstElement * element, GstContext * context) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (element); + GstD3D11AV1DecClass *klass = GST_D3D11_AV1_DEC_GET_CLASS (self); + + gst_d3d11_handle_set_context (element, context, klass->adapter, + &self->device); + + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + +static gboolean +gst_d3d11_av1_dec_open (GstVideoDecoder * decoder) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + GstD3D11AV1DecClass *klass = GST_D3D11_AV1_DEC_GET_CLASS (self); + + if (!gst_d3d11_ensure_element_data (GST_ELEMENT_CAST (self), klass->adapter, + &self->device)) { + GST_ERROR_OBJECT (self, "Cannot create d3d11device"); + return FALSE; + } + + self->d3d11_decoder = gst_d3d11_decoder_new (self->device); + + if (!self->d3d11_decoder) { + GST_ERROR_OBJECT (self, "Cannot create d3d11 decoder"); + gst_clear_object (&self->device); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_d3d11_av1_dec_close (GstVideoDecoder * decoder) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + + gst_clear_object (&self->d3d11_decoder); + gst_clear_object (&self->device); + + return TRUE; +} + +static gboolean +gst_d3d11_av1_dec_negotiate (GstVideoDecoder * decoder) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + + if (!gst_d3d11_decoder_negotiate (self->d3d11_decoder, decoder)) + return FALSE; + + return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); +} + +static gboolean +gst_d3d11_av1_dec_decide_allocation (GstVideoDecoder * decoder, + GstQuery * query) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + + if (!gst_d3d11_decoder_decide_allocation (self->d3d11_decoder, + decoder, query)) { + return FALSE; + } + + return GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation + (decoder, query); +} + +static gboolean +gst_d3d11_av1_dec_src_query (GstVideoDecoder * decoder, GstQuery * query) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + if (gst_d3d11_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 gboolean +gst_d3d11_av1_dec_sink_event (GstVideoDecoder * decoder, GstEvent * event) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + if (self->d3d11_decoder) + gst_d3d11_decoder_set_flushing (self->d3d11_decoder, decoder, TRUE); + break; + case GST_EVENT_FLUSH_STOP: + if (self->d3d11_decoder) + gst_d3d11_decoder_set_flushing (self->d3d11_decoder, decoder, FALSE); + break; + default: + break; + } + + return GST_VIDEO_DECODER_CLASS (parent_class)->sink_event (decoder, event); +} + +static gboolean +gst_d3d11_av1_dec_new_sequence (GstAV1Decoder * decoder, + const GstAV1SequenceHeaderOBU * seq_hdr) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + gboolean modified = FALSE; + guint max_width, max_height; + + GST_LOG_OBJECT (self, "new sequence"); + + if (seq_hdr->seq_profile != GST_AV1_PROFILE_0) { + GST_WARNING_OBJECT (self, "Unsupported profile %d", seq_hdr->seq_profile); + return FALSE; + } + + if (seq_hdr->num_planes != 3) { + GST_WARNING_OBJECT (self, "Monochrome is not supported"); + return FALSE; + } + + self->seq_hdr = *seq_hdr; + + if (self->bitdepth != seq_hdr->bit_depth) { + GST_INFO_OBJECT (self, "Bitdepth changed %d -> %d", self->bitdepth, + seq_hdr->bit_depth); + self->bitdepth = seq_hdr->bit_depth; + modified = TRUE; + } + + max_width = seq_hdr->max_frame_width_minus_1 + 1; + max_height = seq_hdr->max_frame_height_minus_1 + 1; + + if (self->max_width != max_width || self->max_height != max_height) { + GST_INFO_OBJECT (self, "Resolution changed %dx%d -> %dx%d", + self->max_width, self->max_height, max_width, max_height); + self->max_width = max_width; + self->max_height = max_height; + modified = TRUE; + } + + if (modified || !gst_d3d11_decoder_is_configured (self->d3d11_decoder)) { + GstVideoInfo info; + GstVideoFormat out_format = GST_VIDEO_FORMAT_UNKNOWN; + + if (self->bitdepth == 8) { + out_format = GST_VIDEO_FORMAT_NV12; + } else if (self->bitdepth == 10) { + out_format = GST_VIDEO_FORMAT_P010_10LE; + } else { + GST_WARNING_OBJECT (self, "Invalid bit-depth %d", seq_hdr->bit_depth); + return FALSE; + } + + gst_video_info_set_format (&info, + out_format, self->max_width, self->max_height); + + if (!gst_d3d11_decoder_configure (self->d3d11_decoder, GST_D3D11_CODEC_AV1, + decoder->input_state, &info, (gint) self->max_width, + (gint) self->max_height, NUM_OUTPUT_VIEW)) { + GST_ERROR_OBJECT (self, "Failed to create decoder"); + return FALSE; + } + + if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) { + GST_ERROR_OBJECT (self, "Failed to negotiate with downstream"); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +gst_d3d11_av1_dec_new_picture (GstAV1Decoder * decoder, + GstVideoCodecFrame * frame, GstAV1Picture * picture) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + GstBuffer *view_buffer; + + view_buffer = gst_d3d11_decoder_get_output_view_buffer (self->d3d11_decoder, + GST_VIDEO_DECODER (decoder)); + if (!view_buffer) { + GST_DEBUG_OBJECT (self, "No available output view buffer"); + return FALSE; + } + + GST_LOG_OBJECT (self, "New output view buffer %" GST_PTR_FORMAT, view_buffer); + + gst_av1_picture_set_user_data (picture, + view_buffer, (GDestroyNotify) gst_buffer_unref); + + GST_LOG_OBJECT (self, "New AV1 picture %p", picture); + + return TRUE; +} + +static GstAV1Picture * +gst_d3d11_av1_dec_duplicate_picture (GstAV1Decoder * decoder, + GstAV1Picture * picture) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + GstBuffer *view_buffer; + GstAV1Picture *new_picture; + + view_buffer = (GstBuffer *) gst_av1_picture_get_user_data (picture); + + if (!view_buffer) { + GST_ERROR_OBJECT (self, "Parent picture does not have output view buffer"); + return NULL; + } + + new_picture = gst_av1_picture_new (); + + GST_LOG_OBJECT (self, "Duplicate output with buffer %" GST_PTR_FORMAT, + view_buffer); + + gst_av1_picture_set_user_data (new_picture, + gst_buffer_ref (view_buffer), (GDestroyNotify) gst_buffer_unref); + + return new_picture; +} + +static gboolean +gst_d3d11_av1_dec_get_bitstream_buffer (GstD3D11AV1Dec * self) +{ + GST_TRACE_OBJECT (self, "Getting bitstream buffer"); + if (!gst_d3d11_decoder_get_decoder_buffer (self->d3d11_decoder, + D3D11_VIDEO_DECODER_BUFFER_BITSTREAM, &self->remaining_buffer_size, + (gpointer *) & self->bitstream_buffer_data)) { + GST_ERROR_OBJECT (self, "Faild to get bitstream buffer"); + return FALSE; + } + + GST_TRACE_OBJECT (self, "Got bitstream buffer %p with size %d", + self->bitstream_buffer_data, self->remaining_buffer_size); + self->written_buffer_size = 0; + + return TRUE; +} + +static ID3D11VideoDecoderOutputView * +gst_d3d11_av1_dec_get_output_view_from_picture (GstD3D11AV1Dec * self, + GstAV1Picture * picture, guint8 * view_id) +{ + GstBuffer *view_buffer; + ID3D11VideoDecoderOutputView *view; + + view_buffer = (GstBuffer *) gst_av1_picture_get_user_data (picture); + if (!view_buffer) { + GST_DEBUG_OBJECT (self, "current picture does not have output view buffer"); + return NULL; + } + + view = + gst_d3d11_decoder_get_output_view_from_buffer (self->d3d11_decoder, + view_buffer, view_id); + if (!view) { + GST_DEBUG_OBJECT (self, "current picture does not have output view handle"); + return NULL; + } + + return view; +} + +static gboolean +gst_d3d11_av1_dec_start_picture (GstAV1Decoder * decoder, + GstAV1Picture * picture, GstAV1Dpb * dpb) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + const GstAV1SequenceHeaderOBU *seq_hdr = &self->seq_hdr; + const GstAV1FrameHeaderOBU *frame_hdr = &picture->frame_hdr; + ID3D11VideoDecoderOutputView *view; + GST_DXVA_PicParams_AV1 *pic_params = &self->pic_params; + guint8 view_id = 0xff; + guint i, j; + + view = gst_d3d11_av1_dec_get_output_view_from_picture (self, picture, + &view_id); + if (!view) { + GST_ERROR_OBJECT (self, "current picture does not have output view handle"); + return FALSE; + } + + GST_TRACE_OBJECT (self, "Begin frame"); + + if (!gst_d3d11_decoder_begin_frame (self->d3d11_decoder, view, 0, NULL)) { + GST_ERROR_OBJECT (self, "Failed to begin frame"); + return FALSE; + } + + memset (pic_params, 0, sizeof (GST_DXVA_PicParams_AV1)); + + pic_params->width = frame_hdr->frame_width; + pic_params->height = frame_hdr->frame_height; + + pic_params->max_width = seq_hdr->max_frame_width_minus_1 + 1; + pic_params->max_height = seq_hdr->max_frame_height_minus_1 + 1; + + pic_params->CurrPicTextureIndex = view_id; + pic_params->superres_denom = frame_hdr->superres_denom; + pic_params->bitdepth = seq_hdr->bit_depth; + pic_params->seq_profile = seq_hdr->seq_profile; + + /* TILES */ + pic_params->tiles.cols = frame_hdr->tile_info.tile_cols; + pic_params->tiles.rows = frame_hdr->tile_info.tile_rows; + pic_params->tiles.context_update_id = + frame_hdr->tile_info.context_update_tile_id; + + for (i = 0; i < pic_params->tiles.cols; i++) { + pic_params->tiles.widths[i] = + frame_hdr->tile_info.width_in_sbs_minus_1[i] + 1; + } + + for (i = 0; i < pic_params->tiles.rows; i++) { + pic_params->tiles.heights[i] = + frame_hdr->tile_info.height_in_sbs_minus_1[i] + 1; + } + + /* CODING TOOLS */ + pic_params->coding.use_128x128_superblock = seq_hdr->use_128x128_superblock; + pic_params->coding.intra_edge_filter = seq_hdr->enable_filter_intra; + pic_params->coding.interintra_compound = seq_hdr->enable_interintra_compound; + pic_params->coding.masked_compound = seq_hdr->enable_masked_compound; + pic_params->coding.warped_motion = frame_hdr->allow_warped_motion; + pic_params->coding.dual_filter = seq_hdr->enable_dual_filter; + pic_params->coding.jnt_comp = seq_hdr->enable_jnt_comp; + pic_params->coding.screen_content_tools = + frame_hdr->allow_screen_content_tools; + pic_params->coding.integer_mv = frame_hdr->force_integer_mv; + pic_params->coding.cdef = seq_hdr->enable_cdef; + pic_params->coding.restoration = seq_hdr->enable_restoration; + pic_params->coding.film_grain = seq_hdr->film_grain_params_present; + pic_params->coding.intrabc = frame_hdr->allow_intrabc; + pic_params->coding.high_precision_mv = frame_hdr->allow_high_precision_mv; + pic_params->coding.switchable_motion_mode = + frame_hdr->is_motion_mode_switchable; + pic_params->coding.filter_intra = seq_hdr->enable_filter_intra; + pic_params->coding.disable_frame_end_update_cdf = + frame_hdr->disable_frame_end_update_cdf; + pic_params->coding.disable_cdf_update = frame_hdr->disable_cdf_update; + pic_params->coding.reference_mode = frame_hdr->reference_select; + pic_params->coding.skip_mode = frame_hdr->skip_mode_present; + pic_params->coding.reduced_tx_set = frame_hdr->reduced_tx_set; + pic_params->coding.superres = frame_hdr->use_superres; + pic_params->coding.tx_mode = frame_hdr->tx_mode; + pic_params->coding.use_ref_frame_mvs = frame_hdr->use_ref_frame_mvs; + pic_params->coding.enable_ref_frame_mvs = seq_hdr->enable_ref_frame_mvs; + pic_params->coding.reference_frame_update = 1; + + /* FORMAT */ + pic_params->format.frame_type = frame_hdr->frame_type; + pic_params->format.show_frame = frame_hdr->show_frame; + pic_params->format.showable_frame = frame_hdr->showable_frame; + pic_params->format.subsampling_x = seq_hdr->color_config.subsampling_x; + pic_params->format.subsampling_y = seq_hdr->color_config.subsampling_y; + pic_params->format.mono_chrome = seq_hdr->color_config.mono_chrome; + + /* REFERENCES */ + pic_params->primary_ref_frame = frame_hdr->primary_ref_frame; + pic_params->order_hint = frame_hdr->order_hint; + if (seq_hdr->enable_order_hint) { + pic_params->order_hint_bits = seq_hdr->order_hint_bits_minus_1 + 1; + } else { + pic_params->order_hint_bits = 0; + } + + for (i = 0; i < GST_AV1_REFS_PER_FRAME; i++) { + if (dpb->pic_list[i]) { + GstAV1Picture *other_pic = dpb->pic_list[i]; + const GstAV1GlobalMotionParams *gmp = &frame_hdr->global_motion_params; + + pic_params->frame_refs[i].width = other_pic->frame_hdr.frame_width; + pic_params->frame_refs[i].height = other_pic->frame_hdr.frame_height; + for (j = 0; j < 6; j++) { + pic_params->frame_refs[i].wmmat[j] = + gmp->gm_params[GST_AV1_REF_LAST_FRAME + i][j]; + } + pic_params->frame_refs[i].wminvalid = + (gmp->gm_type[GST_AV1_REF_LAST_FRAME + i] == + GST_AV1_WARP_MODEL_IDENTITY); + pic_params->frame_refs[i].wmtype = + gmp->gm_type[GST_AV1_REF_LAST_FRAME + i]; + pic_params->frame_refs[i].Index = frame_hdr->ref_frame_idx[i]; + } else { + pic_params->frame_refs[i].Index = 0xff; + } + } + + for (i = 0; i < GST_AV1_NUM_REF_FRAMES; i++) { + if (dpb->pic_list[i]) { + GstAV1Picture *other_pic = dpb->pic_list[i]; + ID3D11VideoDecoderOutputView *other_view; + guint8 other_view_id = 0xff; + + other_view = gst_d3d11_av1_dec_get_output_view_from_picture (self, + other_pic, &other_view_id); + if (!other_view) { + GST_ERROR_OBJECT (self, + "current picture does not have output view handle"); + return FALSE; + } + + pic_params->RefFrameMapTextureIndex[i] = other_view_id; + } else { + pic_params->RefFrameMapTextureIndex[i] = 0xff; + } + } + + /* LOOP FILTER PARAMS */ + pic_params->loop_filter.filter_level[0] = + frame_hdr->loop_filter_params.loop_filter_level[0]; + pic_params->loop_filter.filter_level[1] = + frame_hdr->loop_filter_params.loop_filter_level[1]; + pic_params->loop_filter.filter_level_u = + frame_hdr->loop_filter_params.loop_filter_level[2]; + pic_params->loop_filter.filter_level_v = + frame_hdr->loop_filter_params.loop_filter_level[3]; + pic_params->loop_filter.sharpness_level = + frame_hdr->loop_filter_params.loop_filter_sharpness; + pic_params->loop_filter.mode_ref_delta_enabled = + frame_hdr->loop_filter_params.loop_filter_delta_enabled; + pic_params->loop_filter.mode_ref_delta_update = + frame_hdr->loop_filter_params.loop_filter_delta_update; + pic_params->loop_filter.delta_lf_multi = + frame_hdr->loop_filter_params.delta_lf_multi; + pic_params->loop_filter.delta_lf_present = + frame_hdr->loop_filter_params.delta_lf_present; + + for (i = 0; i < GST_AV1_TOTAL_REFS_PER_FRAME; i++) { + pic_params->loop_filter.ref_deltas[i] = + frame_hdr->loop_filter_params.loop_filter_ref_deltas[i]; + } + + for (i = 0; i < 2; i++) { + pic_params->loop_filter.mode_deltas[i] = + frame_hdr->loop_filter_params.loop_filter_mode_deltas[i]; + } + + pic_params->loop_filter.delta_lf_res = + frame_hdr->loop_filter_params.delta_lf_res; + + for (i = 0; i < GST_AV1_MAX_NUM_PLANES; i++) { + pic_params->loop_filter.frame_restoration_type[i] = + frame_hdr->loop_restoration_params.frame_restoration_type[i]; + } + + if (frame_hdr->loop_restoration_params.uses_lr) { + pic_params->loop_filter.log2_restoration_unit_size[0] = + (6 + frame_hdr->loop_restoration_params.lr_unit_shift); + pic_params->loop_filter.log2_restoration_unit_size[1] = + pic_params->loop_filter.log2_restoration_unit_size[2] = + (6 + frame_hdr->loop_restoration_params.lr_unit_shift - + frame_hdr->loop_restoration_params.lr_uv_shift); + } else { + pic_params->loop_filter.log2_restoration_unit_size[0] = + pic_params->loop_filter.log2_restoration_unit_size[1] = + pic_params->loop_filter.log2_restoration_unit_size[2] = 8; + } + + /* QUANTIZATION */ + pic_params->quantization.delta_q_present = + frame_hdr->quantization_params.delta_q_present; + pic_params->quantization.delta_q_res = + frame_hdr->quantization_params.delta_q_res; + pic_params->quantization.base_qindex = + frame_hdr->quantization_params.base_q_idx; + pic_params->quantization.y_dc_delta_q = + frame_hdr->quantization_params.delta_q_y_dc; + pic_params->quantization.u_dc_delta_q = + frame_hdr->quantization_params.delta_q_u_dc; + pic_params->quantization.v_dc_delta_q = + frame_hdr->quantization_params.delta_q_v_dc; + pic_params->quantization.u_ac_delta_q = + frame_hdr->quantization_params.delta_q_u_ac; + pic_params->quantization.v_ac_delta_q = + frame_hdr->quantization_params.delta_q_v_ac; + if (frame_hdr->quantization_params.using_qmatrix) { + pic_params->quantization.qm_y = frame_hdr->quantization_params.qm_y; + pic_params->quantization.qm_u = frame_hdr->quantization_params.qm_u; + pic_params->quantization.qm_v = frame_hdr->quantization_params.qm_v; + } else { + pic_params->quantization.qm_y = 0xff; + pic_params->quantization.qm_u = 0xff; + pic_params->quantization.qm_v = 0xff; + } + + /* Cdef params */ + pic_params->cdef.damping = frame_hdr->cdef_params.cdef_damping - 3; + pic_params->cdef.bits = frame_hdr->cdef_params.cdef_bits; + + for (i = 0; i < GST_AV1_CDEF_MAX; i++) { + pic_params->cdef.y_strengths[i].primary = + frame_hdr->cdef_params.cdef_y_pri_strength[i]; + pic_params->cdef.y_strengths[i].secondary = + frame_hdr->cdef_params.cdef_y_sec_strength[i]; + + pic_params->cdef.uv_strengths[i].primary = + frame_hdr->cdef_params.cdef_uv_pri_strength[i]; + pic_params->cdef.uv_strengths[i].secondary = + frame_hdr->cdef_params.cdef_uv_sec_strength[i]; + } + + pic_params->interp_filter = frame_hdr->interpolation_filter; + + /* SEGMENTATION */ + pic_params->segmentation.enabled = + frame_hdr->segmentation_params.segmentation_enabled; + pic_params->segmentation.update_map = + frame_hdr->segmentation_params.segmentation_update_map; + pic_params->segmentation.update_data = + frame_hdr->segmentation_params.segmentation_update_data; + pic_params->segmentation.temporal_update = + frame_hdr->segmentation_params.segmentation_temporal_update; + + for (i = 0; i < GST_AV1_MAX_SEGMENTS; i++) { + for (j = 0; j < GST_AV1_SEG_LVL_MAX; j++) { + pic_params->segmentation.feature_mask[i].mask |= + (frame_hdr->segmentation_params.feature_enabled[i][j] << j); + pic_params->segmentation.feature_data[i][j] = + frame_hdr->segmentation_params.feature_data[i][j]; + } + } + + /* FILM GRAIN */ + if (frame_hdr->film_grain_params.apply_grain) { + pic_params->film_grain.apply_grain = 1; + pic_params->film_grain.scaling_shift_minus8 = + frame_hdr->film_grain_params.grain_scaling_minus_8; + pic_params->film_grain.chroma_scaling_from_luma = + frame_hdr->film_grain_params.chroma_scaling_from_luma; + pic_params->film_grain.ar_coeff_lag = + frame_hdr->film_grain_params.ar_coeff_lag; + pic_params->film_grain.ar_coeff_shift_minus6 = + frame_hdr->film_grain_params.ar_coeff_shift_minus_6; + pic_params->film_grain.grain_scale_shift = + frame_hdr->film_grain_params.grain_scale_shift; + pic_params->film_grain.overlap_flag = + frame_hdr->film_grain_params.overlap_flag; + pic_params->film_grain.clip_to_restricted_range = + frame_hdr->film_grain_params.clip_to_restricted_range; + pic_params->film_grain.matrix_coeff_is_identity = + (seq_hdr->color_config.matrix_coefficients == GST_AV1_MC_IDENTITY); + pic_params->film_grain.grain_seed = frame_hdr->film_grain_params.grain_seed; + for (i = 0; i < frame_hdr->film_grain_params.num_y_points && i < 14; i++) { + pic_params->film_grain.scaling_points_y[i][0] = + frame_hdr->film_grain_params.point_y_value[i]; + pic_params->film_grain.scaling_points_y[i][1] = + frame_hdr->film_grain_params.point_y_scaling[i]; + } + pic_params->film_grain.num_y_points = + frame_hdr->film_grain_params.num_y_points; + + for (i = 0; i < frame_hdr->film_grain_params.num_cb_points && i < 10; i++) { + pic_params->film_grain.scaling_points_cb[i][0] = + frame_hdr->film_grain_params.point_cb_value[i]; + pic_params->film_grain.scaling_points_cb[i][1] = + frame_hdr->film_grain_params.point_cb_scaling[i]; + } + pic_params->film_grain.num_cb_points = + frame_hdr->film_grain_params.num_cb_points; + + for (i = 0; i < frame_hdr->film_grain_params.num_cr_points && i < 10; i++) { + pic_params->film_grain.scaling_points_cr[i][0] = + frame_hdr->film_grain_params.point_cr_value[i]; + pic_params->film_grain.scaling_points_cr[i][1] = + frame_hdr->film_grain_params.point_cr_scaling[i]; + } + pic_params->film_grain.num_cr_points = + frame_hdr->film_grain_params.num_cr_points; + + for (i = 0; i < 24; i++) { + pic_params->film_grain.ar_coeffs_y[i] = + frame_hdr->film_grain_params.ar_coeffs_y_plus_128[i]; + } + + for (i = 0; i < 25; i++) { + pic_params->film_grain.ar_coeffs_cb[i] = + frame_hdr->film_grain_params.ar_coeffs_cb_plus_128[i]; + pic_params->film_grain.ar_coeffs_cr[i] = + frame_hdr->film_grain_params.ar_coeffs_cr_plus_128[i]; + } + + pic_params->film_grain.cb_mult = frame_hdr->film_grain_params.cb_mult; + pic_params->film_grain.cb_luma_mult = + frame_hdr->film_grain_params.cb_luma_mult; + pic_params->film_grain.cr_mult = frame_hdr->film_grain_params.cr_mult; + pic_params->film_grain.cr_luma_mult = + frame_hdr->film_grain_params.cr_luma_mult; + pic_params->film_grain.cb_offset = frame_hdr->film_grain_params.cb_offset; + pic_params->film_grain.cr_offset = frame_hdr->film_grain_params.cr_offset; + } + + return gst_d3d11_av1_dec_get_bitstream_buffer (self); +} + +static gboolean +gst_d3d11_av1_dec_decode_tile (GstAV1Decoder * decoder, + GstAV1Picture * picture, GstAV1Tile * tile) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + GstAV1TileGroupOBU *tile_group = &tile->tile_group; + ID3D11VideoDecoderOutputView *view; + guint8 view_id = 0xff; + guint i; + + view = gst_d3d11_av1_dec_get_output_view_from_picture (self, picture, + &view_id); + if (!view) { + GST_ERROR_OBJECT (self, "current picture does not have output view handle"); + return FALSE; + } + + if (tile_group->num_tiles > self->tile_list->len) + g_array_set_size (self->tile_list, tile_group->num_tiles); + + g_assert (tile_group->tg_end < self->tile_list->len); + + if (tile->obu.obu_size > self->remaining_buffer_size) { + GST_ERROR_OBJECT (self, "Too large OBU size"); + return FALSE; + } + + GST_LOG_OBJECT (self, "Decode tile, tile count %d (start: %d - end: %d)", + tile_group->num_tiles, tile_group->tg_start, tile_group->tg_end); + + for (i = tile_group->tg_start; i <= tile_group->tg_end; i++) { + GST_DXVA_Tile_AV1 *dxva_tile = + &g_array_index (self->tile_list, GST_DXVA_Tile_AV1, i); + + GST_TRACE_OBJECT (self, + "Written size %d, Tile offset %d, size %d, row %d, col %d", + self->written_buffer_size, + tile_group->entry[i].tile_offset, tile_group->entry[i].tile_size, + tile_group->entry[i].tile_row, tile_group->entry[i].tile_col); + + dxva_tile->DataOffset = + self->written_buffer_size + tile_group->entry[i].tile_offset; + dxva_tile->DataSize = tile_group->entry[i].tile_size; + dxva_tile->row = tile_group->entry[i].tile_row; + dxva_tile->column = tile_group->entry[i].tile_col; + /* TODO: used for tile list OBU */ + dxva_tile->anchor_frame = 0xff; + } + + GST_TRACE_OBJECT (self, "OBU size %d", tile->obu.obu_size); + + memcpy (self->bitstream_buffer_data, tile->obu.data, tile->obu.obu_size); + self->remaining_buffer_size -= tile->obu.obu_size; + self->bitstream_buffer_data += tile->obu.obu_size; + self->written_buffer_size += tile->obu.obu_size; + + return TRUE; +} + +static gboolean +gst_d3d11_av1_dec_end_picture (GstAV1Decoder * decoder, GstAV1Picture * picture) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + guint d3d11_buffer_size; + gpointer d3d11_buffer; + guint padding; + D3D11_VIDEO_DECODER_BUFFER_DESC buffer_desc[3]; + guint i; + guint8 *data; + gsize offset = 0; + + GST_TRACE_OBJECT (self, "Getting picture params buffer"); + if (!gst_d3d11_decoder_get_decoder_buffer (self->d3d11_decoder, + D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS, &d3d11_buffer_size, + &d3d11_buffer)) { + GST_ERROR_OBJECT (self, + "Failed to get decoder buffer for picture parameters"); + return FALSE; + } + + if (d3d11_buffer_size < sizeof (GST_DXVA_PicParams_AV1)) { + GST_ERROR_OBJECT (self, + "Too small picture param buffer %d", d3d11_buffer_size); + + gst_d3d11_decoder_release_decoder_buffer (self->d3d11_decoder, + D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS); + return FALSE; + } + + memcpy (d3d11_buffer, &self->pic_params, sizeof (GST_DXVA_PicParams_AV1)); + + GST_TRACE_OBJECT (self, "Release picture param decoder buffer"); + + if (!gst_d3d11_decoder_release_decoder_buffer (self->d3d11_decoder, + D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS)) { + GST_ERROR_OBJECT (self, "Failed to release decoder buffer"); + return FALSE; + } + + GST_TRACE_OBJECT (self, "Getting slice control buffer"); + + if (!gst_d3d11_decoder_get_decoder_buffer (self->d3d11_decoder, + D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL, &d3d11_buffer_size, + &d3d11_buffer)) { + GST_ERROR_OBJECT (self, "Couldn't get slice control buffer"); + return FALSE; + } + + if (d3d11_buffer_size < sizeof (GST_DXVA_Tile_AV1) * self->tile_list->len) { + GST_ERROR_OBJECT (self, "Too small slice control buffer %d", + d3d11_buffer_size); + gst_d3d11_decoder_release_decoder_buffer (self->d3d11_decoder, + D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL); + return FALSE; + } + + data = (guint8 *) d3d11_buffer; + for (i = 0; i < self->tile_list->len; i++) { + GST_DXVA_Tile_AV1 *dxva_tile = + &g_array_index (self->tile_list, GST_DXVA_Tile_AV1, i); + + memcpy (data + offset, dxva_tile, sizeof (GST_DXVA_Tile_AV1)); + offset += sizeof (GST_DXVA_Tile_AV1); + } + + if (!gst_d3d11_decoder_release_decoder_buffer (self->d3d11_decoder, + D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL)) { + GST_ERROR_OBJECT (self, "Failed to release slice control buffer"); + return FALSE; + } + + padding = MIN (GST_ROUND_UP_128 (self->written_buffer_size) - + self->written_buffer_size, self->remaining_buffer_size); + if (padding) { + memset (self->bitstream_buffer_data, 0, padding); + self->written_buffer_size += padding; + } + + if (!gst_d3d11_decoder_release_decoder_buffer (self->d3d11_decoder, + D3D11_VIDEO_DECODER_BUFFER_BITSTREAM)) { + GST_ERROR_OBJECT (self, "Failed to release bitstream buffer"); + + return FALSE; + } + + buffer_desc[0].BufferType = D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS; + buffer_desc[0].DataOffset = 0; + buffer_desc[0].DataSize = sizeof (GST_DXVA_PicParams_AV1); + + buffer_desc[1].BufferType = D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL; + buffer_desc[1].DataOffset = 0; + buffer_desc[1].DataSize = sizeof (GST_DXVA_Tile_AV1) * self->tile_list->len; + + buffer_desc[2].BufferType = D3D11_VIDEO_DECODER_BUFFER_BITSTREAM; + buffer_desc[2].DataOffset = 0; + buffer_desc[2].DataSize = self->written_buffer_size; + + if (!gst_d3d11_decoder_submit_decoder_buffers (self->d3d11_decoder, + 3, buffer_desc)) { + GST_ERROR_OBJECT (self, "Couldn't submit decoder buffers"); + return FALSE; + } + + if (!gst_d3d11_decoder_end_frame (self->d3d11_decoder)) { + GST_ERROR_OBJECT (self, "Failed to EndFrame"); + return FALSE; + } + + return TRUE; +} + +static GstFlowReturn +gst_d3d11_av1_dec_output_picture (GstAV1Decoder * decoder, + GstVideoCodecFrame * frame, GstAV1Picture * picture) +{ + GstD3D11AV1Dec *self = GST_D3D11_AV1_DEC (decoder); + GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder); + GstBuffer *view_buffer; + + GST_LOG_OBJECT (self, "Outputting picture %p, %dx%d", picture, + picture->frame_hdr.render_width, picture->frame_hdr.render_height); + + view_buffer = (GstBuffer *) gst_av1_picture_get_user_data (picture); + + if (!view_buffer) { + GST_ERROR_OBJECT (self, "Could not get output view"); + goto error; + } + + if (!gst_d3d11_decoder_process_output (self->d3d11_decoder, vdec, + picture->frame_hdr.render_width, picture->frame_hdr.render_height, + view_buffer, &frame->output_buffer)) { + GST_ERROR_OBJECT (self, "Failed to copy buffer"); + goto error; + } + + gst_av1_picture_unref (picture); + + return gst_video_decoder_finish_frame (vdec, frame); + +error: + gst_av1_picture_unref (picture); + gst_video_decoder_drop_frame (vdec, frame); + + return GST_FLOW_ERROR; +} + +typedef struct +{ + guint width; + guint height; +} GstD3D11AV1DecResolution; + +void +gst_d3d11_av1_dec_register (GstPlugin * plugin, GstD3D11Device * device, + GstD3D11Decoder * decoder, guint rank) +{ + GType type; + gchar *type_name; + gchar *feature_name; + guint index = 0; + guint i; + GTypeInfo type_info = { + sizeof (GstD3D11AV1DecClass), + NULL, + NULL, + (GClassInitFunc) gst_d3d11_av1_dec_class_init, + NULL, + NULL, + sizeof (GstD3D11AV1Dec), + 0, + (GInstanceInitFunc) gst_d3d11_av1_dec_init, + }; + const GUID *profile_guid = NULL; + /* values were taken from chromium. See supported_profile_helper.cc */ + GstD3D11AV1DecResolution resolutions_to_check[] = { + {4096, 2160}, {4096, 2304}, {7680, 4320}, {8192, 4320}, {8192, 8192} + }; + GstCaps *sink_caps = NULL; + GstCaps *src_caps = NULL; + guint max_width = 0; + guint max_height = 0; + guint resolution; + gboolean have_p010 = FALSE; + gboolean have_gray = FALSE; + gboolean have_gray10 = FALSE; + + if (!gst_d3d11_decoder_get_supported_decoder_profile (decoder, + GST_D3D11_CODEC_AV1, GST_VIDEO_FORMAT_NV12, &profile_guid)) { + GST_INFO_OBJECT (device, "device does not support VP8 decoding"); + return; + } + + have_p010 = gst_d3d11_decoder_supports_format (decoder, + profile_guid, DXGI_FORMAT_P010); + have_gray = gst_d3d11_decoder_supports_format (decoder, + profile_guid, DXGI_FORMAT_R8_UNORM); + have_gray10 = gst_d3d11_decoder_supports_format (decoder, + profile_guid, DXGI_FORMAT_R16_UNORM); + + GST_INFO_OBJECT (device, "Decoder support P010: %d, R8: %d, R16: %d", + have_p010, have_gray, have_gray10); + + /* TODO: add test monochrome formats */ + for (i = 0; i < G_N_ELEMENTS (resolutions_to_check); i++) { + if (gst_d3d11_decoder_supports_resolution (decoder, profile_guid, + DXGI_FORMAT_NV12, resolutions_to_check[i].width, + resolutions_to_check[i].height)) { + max_width = resolutions_to_check[i].width; + max_height = resolutions_to_check[i].height; + + GST_DEBUG_OBJECT (device, + "device support resolution %dx%d", max_width, max_height); + } else { + break; + } + } + + if (max_width == 0 || max_height == 0) { + GST_WARNING_OBJECT (device, "Couldn't query supported resolution"); + return; + } + + sink_caps = + gst_caps_from_string ("video/x-av1, " + "alignment = (string) frame, profile = (string) 0"); + src_caps = gst_caps_from_string ("video/x-raw(" + GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY "); video/x-raw"); + + if (have_p010) { + GValue format_list = G_VALUE_INIT; + GValue format_value = G_VALUE_INIT; + + g_value_init (&format_list, GST_TYPE_LIST); + + g_value_init (&format_value, G_TYPE_STRING); + g_value_set_string (&format_value, "NV12"); + gst_value_list_append_and_take_value (&format_list, &format_value); + + g_value_init (&format_value, G_TYPE_STRING); + g_value_set_string (&format_value, "P010_10LE"); + gst_value_list_append_and_take_value (&format_list, &format_value); + + gst_caps_set_value (src_caps, "format", &format_list); + g_value_unset (&format_list); + } else { + gst_caps_set_simple (src_caps, "format", G_TYPE_STRING, "NV12", NULL); + } + + /* To cover both landscape and portrait, select max value */ + resolution = MAX (max_width, max_height); + gst_caps_set_simple (sink_caps, + "width", GST_TYPE_INT_RANGE, 1, resolution, + "height", GST_TYPE_INT_RANGE, 1, resolution, NULL); + gst_caps_set_simple (src_caps, + "width", GST_TYPE_INT_RANGE, 1, resolution, + "height", GST_TYPE_INT_RANGE, 1, resolution, NULL); + + type_info.class_data = + gst_d3d11_decoder_class_data_new (device, sink_caps, src_caps); + + type_name = g_strdup ("GstD3D11AV1Dec"); + feature_name = g_strdup ("d3d11av1dec"); + + while (g_type_from_name (type_name)) { + index++; + g_free (type_name); + g_free (feature_name); + type_name = g_strdup_printf ("GstD3D11AV1Device%dDec", index); + feature_name = g_strdup_printf ("d3d11av1device%ddec", index); + } + + type = g_type_register_static (GST_TYPE_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/sys/d3d11/gstd3d11av1dec.h b/sys/d3d11/gstd3d11av1dec.h new file mode 100644 index 0000000000..7c51743001 --- /dev/null +++ b/sys/d3d11/gstd3d11av1dec.h @@ -0,0 +1,34 @@ +/* GStreamer + * Copyright (C) 2021 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. + */ + +#ifndef __GST_D3D11_AV1_DEC_H__ +#define __GST_D3D11_AV1_DEC_H__ + +#include "gstd3d11decoder.h" + +G_BEGIN_DECLS + +void gst_d3d11_av1_dec_register (GstPlugin * plugin, + GstD3D11Device * device, + GstD3D11Decoder * decoder, + guint rank); + +G_END_DECLS + +#endif /* __GST_D3D11_AV1_DEC_H__ */ diff --git a/sys/d3d11/gstd3d11decoder.cpp b/sys/d3d11/gstd3d11decoder.cpp index 5a8b04ab79..6a1d96aac4 100644 --- a/sys/d3d11/gstd3d11decoder.cpp +++ b/sys/d3d11/gstd3d11decoder.cpp @@ -84,6 +84,8 @@ DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_MPEG2_VLD, 0xee27417f, 0x5e28, 0x4e65, 0xbe, 0xea, 0x1d, 0x26, 0xb5, 0x08, 0xad, 0xc9); DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_MPEG2and1_VLD, 0x86695f12, 0x340e, 0x4f04, 0x9f, 0xd3, 0x92, 0x53, 0xdd, 0x32, 0x74, 0x60); +DEFINE_GUID (GST_GUID_D3D11_DECODER_PROFILE_AV1_VLD_PROFILE0, 0xb8be4ccb, + 0xcf53, 0x46ba, 0x8d, 0x59, 0xd6, 0xb8, 0xa6, 0xda, 0x5d, 0x2a); static const GUID *profile_h264_list[] = { &GST_GUID_D3D11_DECODER_PROFILE_H264_IDCT_FGT, @@ -116,6 +118,11 @@ static const GUID *profile_mpeg2_list[] = { &GST_GUID_D3D11_DECODER_PROFILE_MPEG2and1_VLD }; +static const GUID *profile_av1_list[] = { + &GST_GUID_D3D11_DECODER_PROFILE_AV1_VLD_PROFILE0, + /* TODO: add more profile */ +}; + enum { PROP_0, @@ -544,6 +551,8 @@ gst_d3d11_codec_to_string (GstD3D11Codec codec) return "VP8"; case GST_D3D11_CODEC_MPEG2: return "MPEG2"; + case GST_D3D11_CODEC_AV1: + return "AV1"; default: g_assert_not_reached (); break; @@ -605,6 +614,10 @@ gst_d3d11_decoder_get_supported_decoder_profile (GstD3D11Decoder * decoder, profile_size = G_N_ELEMENTS (profile_mpeg2_list); } break; + case GST_D3D11_CODEC_AV1: + profile_list = profile_av1_list; + profile_size = G_N_ELEMENTS (profile_av1_list); + break; default: break; } @@ -857,6 +870,7 @@ gst_d3d11_decoder_open (GstD3D11Decoder * self) * But... where it is? */ switch (self->codec) { case GST_D3D11_CODEC_H265: + case GST_D3D11_CODEC_AV1: /* See directx_va_Setup() impl. in vlc */ if (vendor != GST_D3D11_DEVICE_VENDOR_XBOX) alignment = 128; @@ -922,6 +936,7 @@ gst_d3d11_decoder_open (GstD3D11Decoder * self) case GST_D3D11_CODEC_VP9: case GST_D3D11_CODEC_VP8: case GST_D3D11_CODEC_MPEG2: + case GST_D3D11_CODEC_AV1: if (config_list[i].ConfigBitstreamRaw == 1) best_config = &config_list[i]; break; diff --git a/sys/d3d11/gstd3d11decoder.h b/sys/d3d11/gstd3d11decoder.h index ada1368426..7d49def4be 100644 --- a/sys/d3d11/gstd3d11decoder.h +++ b/sys/d3d11/gstd3d11decoder.h @@ -40,6 +40,7 @@ typedef enum GST_D3D11_CODEC_H265, GST_D3D11_CODEC_VP8, GST_D3D11_CODEC_MPEG2, + GST_D3D11_CODEC_AV1, /* the last of supported codec */ GST_D3D11_CODEC_LAST diff --git a/sys/d3d11/meson.build b/sys/d3d11/meson.build index 902748fb2e..24f2debf8d 100644 --- a/sys/d3d11/meson.build +++ b/sys/d3d11/meson.build @@ -17,6 +17,7 @@ d3d11_sources = [ ] d3d11_dec_sources = [ + 'gstd3d11av1dec.cpp', 'gstd3d11decoder.cpp', 'gstd3d11h264dec.cpp', 'gstd3d11vp9dec.cpp', diff --git a/sys/d3d11/plugin.c b/sys/d3d11/plugin.c index 18521debd6..c0894283aa 100644 --- a/sys/d3d11/plugin.c +++ b/sys/d3d11/plugin.c @@ -36,6 +36,7 @@ #include "gstd3d11vp9dec.h" #include "gstd3d11vp8dec.h" #include "gstd3d11mpeg2dec.h" +#include "gstd3d11av1dec.h" #endif #ifdef HAVE_DXGI_DESKTOP_DUP #include "gstd3d11desktopdupsrc.h" @@ -61,6 +62,7 @@ GST_DEBUG_CATEGORY (gst_d3d11_h265_dec_debug); GST_DEBUG_CATEGORY (gst_d3d11_vp9_dec_debug); GST_DEBUG_CATEGORY (gst_d3d11_vp8_dec_debug); GST_DEBUG_CATEGORY (gst_d3d11_mpeg2_dec_debug); +GST_DEBUG_CATEGORY (gst_d3d11_av1_dec_debug); #endif #ifdef HAVE_DXGI_DESKTOP_DUP @@ -113,6 +115,8 @@ plugin_init (GstPlugin * plugin) "d3d11vp8dec", 0, "Direct3D11 VP8 Decoder"); GST_DEBUG_CATEGORY_INIT (gst_d3d11_mpeg2_dec_debug, "d3d11mpeg2dec", 0, "Direct3D11 MPEG2 Decoder"); + GST_DEBUG_CATEGORY_INIT (gst_d3d11_av1_dec_debug, + "d3d11av1dec", 0, "Direct3D11 AV1 Decoder"); } #endif @@ -167,6 +171,8 @@ plugin_init (GstPlugin * plugin) GST_RANK_SECONDARY); gst_d3d11_mpeg2_dec_register (plugin, device, decoder, GST_RANK_SECONDARY); + gst_d3d11_av1_dec_register (plugin, device, decoder, + GST_RANK_SECONDARY); } done: