dxva: Add DXVA decoder baseclass implementation

Extract Direct3D version independent common DXVA logic from d3d11
decoder so that it can be used by the other APIs (D3D9 and D3D12)
as well

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4161>
This commit is contained in:
Seungha Yang 2021-11-28 19:29:23 +09:00
parent 7f99fe243e
commit 729c11cb0c
21 changed files with 4714 additions and 0 deletions

View file

@ -1,6 +1,7 @@
subprojects/gst-plugins-bad/ext/qt6d3d11
subprojects/gst-plugins-bad/gst-libs/gst/cuda
subprojects/gst-plugins-bad/gst-libs/gst/d3d11
subprojects/gst-plugins-bad/gst-libs/gst/dxva
subprojects/gst-plugins-bad/gst-libs/gst/va
subprojects/gst-plugins-bad/gst-libs/gst/winrt
subprojects/gst-plugins-bad/sys/amfcodec

View file

@ -0,0 +1,31 @@
/* GStreamer
* Copyright (C) 2023 GStreamer developers
*
* 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 <gst/gst.h>
#ifndef GST_DXVA_API
# ifdef BUILDING_GST_DXVA
# define GST_DXVA_API GST_API_EXPORT /* from config.h */
# else
# define GST_DXVA_API GST_API_IMPORT
# endif
#endif

View file

@ -0,0 +1,30 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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
#ifndef GST_USE_UNSTABLE_API
#pragma message ("The dxva library from gst-plugins-bad is unstable API and may change in future.")
#pragma message ("You can define GST_USE_UNSTABLE_API to avoid this warning.")
#endif
#include <gst/gst.h>
#include <gst/dxva/dxva-prelude.h>
#include <gst/dxva/gstdxvatypes.h>
#include <gst/dxva/gstdxvautils.h>

View file

@ -0,0 +1,339 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <windows.h>
#include <gst/gst.h>
G_BEGIN_DECLS
/* 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)
/* AV1 picture entry data structure */
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 GlobalMotionFlags;
} DUMMYUNIONNAME;
UCHAR Index;
UINT16 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;
/* AV1 tile structure */
typedef struct _GST_DXVA_Tile_AV1
{
UINT DataOffset;
UINT DataSize;
USHORT row;
USHORT column;
UINT16 Reserved16Bits;
UCHAR anchor_frame;
UCHAR Reserved8Bits;
} GST_DXVA_Tile_AV1;
/* AV1 status reporting data structure */
typedef struct _GST_DXVA_Status_AV1
{
UINT StatusReportFeedbackNumber;
GST_DXVA_PicEntry_AV1 CurrPic;
UCHAR BufType;
UCHAR Status;
UCHAR Reserved8Bits;
USHORT NumMbsAffected;
} GST_DXVA_Status_AV1;
#pragma pack(pop)
G_END_DECLS

View file

@ -0,0 +1,691 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdxvaav1decoder.h"
#include "gstdxvaav1.h"
#include <string.h>
#include <vector>
GST_DEBUG_CATEGORY_STATIC (gst_dxva_av1_decoder_debug);
#define GST_CAT_DEFAULT gst_dxva_av1_decoder_debug
/* *INDENT-OFF* */
struct _GstDxvaAV1DecoderPrivate
{
GstAV1SequenceHeaderOBU seq_hdr;
GST_DXVA_PicParams_AV1 pic_params;
std::vector<GST_DXVA_Tile_AV1> tile_list;
std::vector<guint8> bitstream_buffer;
GPtrArray *ref_pics = nullptr;
guint max_width = 0;
guint max_height = 0;
guint bitdepth = 0;
gboolean configured = FALSE;
};
/* *INDENT-ON* */
static void gst_dxva_av1_decoder_finalize (GObject * object);
static gboolean gst_dxva_av1_decoder_start (GstVideoDecoder * decoder);
static GstFlowReturn gst_dxva_av1_decoder_new_sequence (GstAV1Decoder * decoder,
const GstAV1SequenceHeaderOBU * seq_hdr, gint max_dpb_size);
static GstFlowReturn gst_dxva_av1_decoder_new_picture (GstAV1Decoder * decoder,
GstVideoCodecFrame * frame, GstAV1Picture * picture);
static GstAV1Picture *gst_dxva_av1_decoder_duplicate_picture (GstAV1Decoder *
decoder, GstVideoCodecFrame * frame, GstAV1Picture * picture);
static GstFlowReturn
gst_dxva_av1_decoder_start_picture (GstAV1Decoder * decoder,
GstAV1Picture * picture, GstAV1Dpb * dpb);
static GstFlowReturn gst_dxva_av1_decoder_decode_tile (GstAV1Decoder * decoder,
GstAV1Picture * picture, GstAV1Tile * tile);
static GstFlowReturn gst_dxva_av1_decoder_end_picture (GstAV1Decoder * decoder,
GstAV1Picture * picture);
static GstFlowReturn gst_dxva_av1_decoder_output_picture (GstAV1Decoder *
decoder, GstVideoCodecFrame * frame, GstAV1Picture * picture);
#define gst_dxva_av1_decoder_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstDxvaAV1Decoder,
gst_dxva_av1_decoder, GST_TYPE_AV1_DECODER,
GST_DEBUG_CATEGORY_INIT (gst_dxva_av1_decoder_debug, "dxvaav1decoder",
0, "dxvaav1decoder"));
static void
gst_dxva_av1_decoder_class_init (GstDxvaAV1DecoderClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
GstAV1DecoderClass *av1decoder_class = GST_AV1_DECODER_CLASS (klass);
gobject_class->finalize = gst_dxva_av1_decoder_finalize;
decoder_class->start = GST_DEBUG_FUNCPTR (gst_dxva_av1_decoder_start);
av1decoder_class->new_sequence =
GST_DEBUG_FUNCPTR (gst_dxva_av1_decoder_new_sequence);
av1decoder_class->new_picture =
GST_DEBUG_FUNCPTR (gst_dxva_av1_decoder_new_picture);
av1decoder_class->duplicate_picture =
GST_DEBUG_FUNCPTR (gst_dxva_av1_decoder_duplicate_picture);
av1decoder_class->start_picture =
GST_DEBUG_FUNCPTR (gst_dxva_av1_decoder_start_picture);
av1decoder_class->decode_tile =
GST_DEBUG_FUNCPTR (gst_dxva_av1_decoder_decode_tile);
av1decoder_class->end_picture =
GST_DEBUG_FUNCPTR (gst_dxva_av1_decoder_end_picture);
av1decoder_class->output_picture =
GST_DEBUG_FUNCPTR (gst_dxva_av1_decoder_output_picture);
}
static void
gst_dxva_av1_decoder_init (GstDxvaAV1Decoder * self)
{
self->priv = new GstDxvaAV1DecoderPrivate ();
self->priv->ref_pics = g_ptr_array_new ();
}
static void
gst_dxva_av1_decoder_finalize (GObject * object)
{
GstDxvaAV1Decoder *self = GST_DXVA_AV1_DECODER (object);
GstDxvaAV1DecoderPrivate *priv = self->priv;
g_ptr_array_unref (priv->ref_pics);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_dxva_av1_decoder_reset (GstDxvaAV1Decoder * self)
{
GstDxvaAV1DecoderPrivate *priv = self->priv;
priv->max_width = 0;
priv->max_height = 0;
priv->bitdepth = 0;
priv->configured = FALSE;
}
static gboolean
gst_dxva_av1_decoder_start (GstVideoDecoder * decoder)
{
GstDxvaAV1Decoder *self = GST_DXVA_AV1_DECODER (decoder);
gst_dxva_av1_decoder_reset (self);
return GST_VIDEO_DECODER_CLASS (parent_class)->start (decoder);
}
static GstFlowReturn
gst_dxva_av1_decoder_new_sequence (GstAV1Decoder * decoder,
const GstAV1SequenceHeaderOBU * seq_hdr, gint max_dpb_size)
{
GstDxvaAV1Decoder *self = GST_DXVA_AV1_DECODER (decoder);
GstDxvaAV1DecoderPrivate *priv = self->priv;
GstDxvaAV1DecoderClass *klass = GST_DXVA_AV1_DECODER_GET_CLASS (self);
gboolean modified = FALSE;
guint max_width, max_height;
GstVideoInfo info;
GstVideoFormat out_format = GST_VIDEO_FORMAT_UNKNOWN;
GstFlowReturn ret;
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 GST_FLOW_NOT_NEGOTIATED;
}
if (seq_hdr->num_planes != 3) {
GST_WARNING_OBJECT (self, "Monochrome is not supported");
return GST_FLOW_NOT_NEGOTIATED;
}
priv->seq_hdr = *seq_hdr;
if (priv->bitdepth != seq_hdr->bit_depth) {
GST_INFO_OBJECT (self, "Bitdepth changed %d -> %d", priv->bitdepth,
seq_hdr->bit_depth);
priv->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 (priv->max_width != max_width || priv->max_height != max_height) {
GST_INFO_OBJECT (self, "Resolution changed %dx%d -> %dx%d",
priv->max_width, priv->max_height, max_width, max_height);
priv->max_width = max_width;
priv->max_height = max_height;
modified = TRUE;
}
if (!modified && priv->configured)
return GST_FLOW_OK;
if (priv->bitdepth == 8) {
out_format = GST_VIDEO_FORMAT_NV12;
} else if (priv->bitdepth == 10) {
out_format = GST_VIDEO_FORMAT_P010_10LE;
} else {
GST_WARNING_OBJECT (self, "Invalid bit-depth %d", seq_hdr->bit_depth);
priv->configured = FALSE;
return GST_FLOW_NOT_NEGOTIATED;
}
gst_video_info_set_format (&info,
out_format, priv->max_width, priv->max_height);
g_assert (klass->configure);
ret = klass->configure (self, decoder->input_state, &info, 0, 0,
priv->max_width, priv->max_height, max_dpb_size);
if (ret == GST_FLOW_OK) {
priv->configured = TRUE;
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
GST_WARNING_OBJECT (self, "Couldn't negotiate with new sequence");
ret = GST_FLOW_NOT_NEGOTIATED;
}
} else {
priv->configured = FALSE;
}
return ret;
}
static GstFlowReturn
gst_dxva_av1_decoder_new_picture (GstAV1Decoder * decoder,
GstVideoCodecFrame * frame, GstAV1Picture * picture)
{
GstDxvaAV1Decoder *self = GST_DXVA_AV1_DECODER (decoder);
GstDxvaAV1DecoderClass *klass = GST_DXVA_AV1_DECODER_GET_CLASS (self);
g_assert (klass->new_picture);
return klass->new_picture (self, GST_CODEC_PICTURE (picture));
}
static GstAV1Picture *
gst_dxva_av1_decoder_duplicate_picture (GstAV1Decoder * decoder,
GstVideoCodecFrame * frame, GstAV1Picture * picture)
{
GstDxvaAV1Decoder *self = GST_DXVA_AV1_DECODER (decoder);
GstDxvaAV1DecoderClass *klass = GST_DXVA_AV1_DECODER_GET_CLASS (self);
GstAV1Picture *new_picture;
g_assert (klass->duplicate_picture);
new_picture = gst_av1_picture_new ();
if (klass->duplicate_picture (self, GST_CODEC_PICTURE (picture),
GST_CODEC_PICTURE (new_picture)) != GST_FLOW_OK) {
gst_av1_picture_unref (new_picture);
return nullptr;
}
return new_picture;
}
static GstFlowReturn
gst_dxva_av1_decoder_start_picture (GstAV1Decoder * decoder,
GstAV1Picture * picture, GstAV1Dpb * dpb)
{
GstDxvaAV1Decoder *self = GST_DXVA_AV1_DECODER (decoder);
GstDxvaAV1DecoderPrivate *priv = self->priv;
GstDxvaAV1DecoderClass *klass = GST_DXVA_AV1_DECODER_GET_CLASS (self);
const GstAV1SequenceHeaderOBU *seq_hdr = &priv->seq_hdr;
const GstAV1FrameHeaderOBU *frame_hdr = &picture->frame_hdr;
GST_DXVA_PicParams_AV1 *pic_params = &priv->pic_params;
GstCodecPicture *codec_picture = GST_CODEC_PICTURE (picture);
guint i, j;
GstFlowReturn ret;
guint8 picture_id;
g_assert (klass->start_picture);
g_assert (klass->get_picture_id);
ret = klass->start_picture (self, codec_picture, &picture_id);
if (ret != GST_FLOW_OK)
return ret;
priv->bitstream_buffer.resize (0);
priv->tile_list.resize (0);
g_ptr_array_set_size (priv->ref_pics, 0);
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 = picture_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++) {
pic_params->RefFrameMapTextureIndex[i] = 0xff;
if (dpb->pic_list[i]) {
GstAV1Picture *other_pic = dpb->pic_list[i];
guint8 id;
id = klass->get_picture_id (self, GST_CODEC_PICTURE (other_pic));
if (id != 0xff) {
pic_params->RefFrameMapTextureIndex[i] = id;
g_ptr_array_add (priv->ref_pics, other_pic);
}
}
}
/* 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++) {
guint8 secondary;
pic_params->cdef.y_strengths[i].primary =
frame_hdr->cdef_params.cdef_y_pri_strength[i];
secondary = frame_hdr->cdef_params.cdef_y_sec_strength[i];
if (secondary == 4)
secondary--;
pic_params->cdef.y_strengths[i].secondary = secondary;
pic_params->cdef.uv_strengths[i].primary =
frame_hdr->cdef_params.cdef_uv_pri_strength[i];
secondary = frame_hdr->cdef_params.cdef_uv_sec_strength[i];
if (secondary == 4)
secondary--;
pic_params->cdef.uv_strengths[i].secondary = secondary;
}
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_FLOW_OK;
}
static GstFlowReturn
gst_dxva_av1_decoder_decode_tile (GstAV1Decoder * decoder,
GstAV1Picture * picture, GstAV1Tile * tile)
{
GstDxvaAV1Decoder *self = GST_DXVA_AV1_DECODER (decoder);
GstDxvaAV1DecoderPrivate *priv = self->priv;
GstAV1TileGroupOBU *tile_group = &tile->tile_group;
if (tile_group->num_tiles > priv->tile_list.size ())
priv->tile_list.resize (tile_group->num_tiles);
g_assert (tile_group->tg_end < priv->tile_list.size ());
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 (guint i = tile_group->tg_start; i <= tile_group->tg_end; i++) {
GST_DXVA_Tile_AV1 *dxva_tile = &priv->tile_list[i];
GST_TRACE_OBJECT (self,
"Tile offset %d, size %d, row %d, col %d",
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 = priv->bitstream_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);
size_t pos = priv->bitstream_buffer.size ();
priv->bitstream_buffer.resize (pos + tile->obu.obu_size);
memcpy (&priv->bitstream_buffer[0] + pos, tile->obu.data, tile->obu.obu_size);
return GST_FLOW_OK;
}
static GstFlowReturn
gst_dxva_av1_decoder_end_picture (GstAV1Decoder * decoder,
GstAV1Picture * picture)
{
GstDxvaAV1Decoder *self = GST_DXVA_AV1_DECODER (decoder);
GstDxvaAV1DecoderPrivate *priv = self->priv;
GstDxvaAV1DecoderClass *klass = GST_DXVA_AV1_DECODER_GET_CLASS (self);
size_t bitstream_buffer_size;
size_t bitstream_pos;
GstDxvaDecodingArgs args;
if (priv->bitstream_buffer.empty () || priv->tile_list.empty ()) {
GST_ERROR_OBJECT (self, "No bitstream buffer to submit");
return GST_FLOW_ERROR;
}
memset (&args, 0, sizeof (GstDxvaDecodingArgs));
bitstream_pos = priv->bitstream_buffer.size ();
bitstream_buffer_size = GST_ROUND_UP_128 (bitstream_pos);
if (bitstream_buffer_size > bitstream_pos) {
size_t padding = bitstream_buffer_size - bitstream_pos;
/* As per DXVA spec, total amount of bitstream buffer size should be
* 128 bytes aligned. If actual data is not multiple of 128 bytes,
* the last slice data needs to be zero-padded */
priv->bitstream_buffer.resize (bitstream_buffer_size, 0);
GST_DXVA_Tile_AV1 & tile = priv->tile_list.back ();
tile.DataSize += padding;
}
args.picture_params = &priv->pic_params;
args.picture_params_size = sizeof (GST_DXVA_PicParams_AV1);
args.slice_control = &priv->tile_list[0];
args.slice_control_size =
sizeof (GST_DXVA_Tile_AV1) * priv->tile_list.size ();
args.bitstream = &priv->bitstream_buffer[0];
args.bitstream_size = priv->bitstream_buffer.size ();
g_assert (klass->end_picture);
return klass->end_picture (self, GST_CODEC_PICTURE (picture),
priv->ref_pics, &args);
}
static GstFlowReturn
gst_dxva_av1_decoder_output_picture (GstAV1Decoder * decoder,
GstVideoCodecFrame * frame, GstAV1Picture * picture)
{
GstDxvaAV1Decoder *self = GST_DXVA_AV1_DECODER (decoder);
GstDxvaAV1DecoderClass *klass = GST_DXVA_AV1_DECODER_GET_CLASS (self);
g_assert (klass->output_picture);
GST_LOG_OBJECT (self, "Outputting picture %p, %dx%d", picture,
picture->frame_hdr.render_width, picture->frame_hdr.render_height);
return klass->output_picture (self, frame, GST_CODEC_PICTURE (picture),
(GstVideoBufferFlags) 0, picture->frame_hdr.render_width,
picture->frame_hdr.render_height);
}

View file

@ -0,0 +1,94 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/dxva/dxva-prelude.h>
#include <gst/dxva/gstdxvatypes.h>
#include <gst/codecs/gstav1decoder.h>
G_BEGIN_DECLS
#define GST_TYPE_DXVA_AV1_DECODER (gst_dxva_av1_decoder_get_type())
#define GST_DXVA_AV1_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DXVA_AV1_DECODER,GstDxvaAV1Decoder))
#define GST_DXVA_AV1_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DXVA_AV1_DECODER,GstDxvaAV1DecoderClass))
#define GST_DXVA_AV1_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_DXVA_AV1_DECODER,GstDxvaAV1DecoderClass))
#define GST_IS_DXVA_AV1_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DXVA_AV1_DECODER))
#define GST_IS_DXVA_AV1_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DXVA_AV1_DECODER))
#define GST_DXVA_AV1_DECODER_CAST(obj) ((GstDxvaAV1Decoder*)obj)
typedef struct _GstDxvaAV1Decoder GstDxvaAV1Decoder;
typedef struct _GstDxvaAV1DecoderClass GstDxvaAV1DecoderClass;
typedef struct _GstDxvaAV1DecoderPrivate GstDxvaAV1DecoderPrivate;
struct _GstDxvaAV1Decoder
{
GstAV1Decoder parent;
/*< private >*/
GstDxvaAV1DecoderPrivate *priv;
};
struct _GstDxvaAV1DecoderClass
{
GstAV1DecoderClass parent_class;
GstFlowReturn (*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);
GstFlowReturn (*new_picture) (GstDxvaAV1Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*duplicate_picture) (GstDxvaAV1Decoder * decoder,
GstCodecPicture * src,
GstCodecPicture * dst);
guint8 (*get_picture_id) (GstDxvaAV1Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*start_picture) (GstDxvaAV1Decoder * decoder,
GstCodecPicture * picture,
guint8 * picture_id);
GstFlowReturn (*end_picture) (GstDxvaAV1Decoder * decoder,
GstCodecPicture * picture,
GPtrArray * ref_pics,
const GstDxvaDecodingArgs * args);
GstFlowReturn (*output_picture) (GstDxvaAV1Decoder * decoder,
GstVideoCodecFrame * frame,
GstCodecPicture * picture,
GstVideoBufferFlags buffer_flags,
gint display_width,
gint display_height);
};
GST_DXVA_API
GType gst_dxva_av1_decoder_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDxvaAV1Decoder, gst_object_unref)
G_END_DECLS

View file

@ -0,0 +1,642 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdxvah264decoder.h"
#include <string.h>
#include <vector>
/* HACK: to expose dxva data structure on UWP */
#ifdef WINAPI_PARTITION_DESKTOP
#undef WINAPI_PARTITION_DESKTOP
#endif
#define WINAPI_PARTITION_DESKTOP 1
#include <d3d9.h>
#include <dxva.h>
GST_DEBUG_CATEGORY_STATIC (gst_dxva_h264_decoder_debug);
#define GST_CAT_DEFAULT gst_dxva_h264_decoder_debug
/* *INDENT-OFF* */
struct _GstDxvaH264DecoderPrivate
{
DXVA_PicParams_H264 pic_params;
DXVA_Qmatrix_H264 iq_matrix;
std::vector<DXVA_Slice_H264_Short> slice_list;
std::vector<guint8> bitstream_buffer;
GPtrArray *ref_pics = nullptr;
gint crop_x = 0;
gint crop_y = 0;
gint width = 0;
gint height = 0;
gint coded_width = 0;
gint coded_height = 0;
gint bitdepth = 0;
guint8 chroma_format_idc = 0;
GstVideoFormat out_format = GST_VIDEO_FORMAT_UNKNOWN;
gboolean interlaced = FALSE;
gint max_dpb_size = 0;
gboolean configured = FALSE;
};
/* *INDENT-ON* */
static void gst_dxva_h264_decoder_finalize (GObject * object);
static gboolean gst_dxva_h264_decoder_start (GstVideoDecoder * decoder);
static GstFlowReturn
gst_dxva_h264_decoder_new_sequence (GstH264Decoder * decoder,
const GstH264SPS * sps, gint max_dpb_size);
static GstFlowReturn
gst_dxva_h264_decoder_new_picture (GstH264Decoder * decoder,
GstVideoCodecFrame * frame, GstH264Picture * picture);
static GstFlowReturn
gst_dxva_h264_decoder_new_field_picture (GstH264Decoder * decoder,
GstH264Picture * first_field, GstH264Picture * second_field);
static GstFlowReturn
gst_dxva_h264_decoder_start_picture (GstH264Decoder * decoder,
GstH264Picture * picture, GstH264Slice * slice, GstH264Dpb * dpb);
static GstFlowReturn
gst_dxva_h264_decoder_decode_slice (GstH264Decoder * decoder,
GstH264Picture * picture, GstH264Slice * slice, GArray * ref_pic_list0,
GArray * ref_pic_list1);
static GstFlowReturn
gst_dxva_h264_decoder_end_picture (GstH264Decoder * decoder,
GstH264Picture * picture);
static GstFlowReturn
gst_dxva_h264_decoder_output_picture (GstH264Decoder * decoder,
GstVideoCodecFrame * frame, GstH264Picture * picture);
#define gst_dxva_h264_decoder_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstDxvaH264Decoder,
gst_dxva_h264_decoder, GST_TYPE_H264_DECODER,
GST_DEBUG_CATEGORY_INIT (gst_dxva_h264_decoder_debug, "dxvah264decoder",
0, "dxvah264decoder"));
static void
gst_dxva_h264_decoder_class_init (GstDxvaH264DecoderClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
GstH264DecoderClass *h264decoder_class = GST_H264_DECODER_CLASS (klass);
object_class->finalize = gst_dxva_h264_decoder_finalize;
decoder_class->start = GST_DEBUG_FUNCPTR (gst_dxva_h264_decoder_start);
h264decoder_class->new_sequence =
GST_DEBUG_FUNCPTR (gst_dxva_h264_decoder_new_sequence);
h264decoder_class->new_picture =
GST_DEBUG_FUNCPTR (gst_dxva_h264_decoder_new_picture);
h264decoder_class->new_field_picture =
GST_DEBUG_FUNCPTR (gst_dxva_h264_decoder_new_field_picture);
h264decoder_class->start_picture =
GST_DEBUG_FUNCPTR (gst_dxva_h264_decoder_start_picture);
h264decoder_class->decode_slice =
GST_DEBUG_FUNCPTR (gst_dxva_h264_decoder_decode_slice);
h264decoder_class->end_picture =
GST_DEBUG_FUNCPTR (gst_dxva_h264_decoder_end_picture);
h264decoder_class->output_picture =
GST_DEBUG_FUNCPTR (gst_dxva_h264_decoder_output_picture);
}
static void
gst_dxva_h264_decoder_init (GstDxvaH264Decoder * self)
{
self->priv = new GstDxvaH264DecoderPrivate ();
self->priv->ref_pics = g_ptr_array_new ();
}
static void
gst_dxva_h264_decoder_finalize (GObject * object)
{
GstDxvaH264Decoder *self = GST_DXVA_H264_DECODER (object);
GstDxvaH264DecoderPrivate *priv = self->priv;
g_ptr_array_unref (priv->ref_pics);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_dxva_h264_decoder_reset (GstDxvaH264Decoder * self)
{
GstDxvaH264DecoderPrivate *priv = self->priv;
priv->crop_x = 0;
priv->crop_y = 0;
priv->width = 0;
priv->height = 0;
priv->coded_width = 0;
priv->coded_height = 0;
priv->bitdepth = 0;
priv->chroma_format_idc = 0;
priv->out_format = GST_VIDEO_FORMAT_UNKNOWN;
priv->interlaced = FALSE;
priv->max_dpb_size = 0;
priv->configured = FALSE;
}
static gboolean
gst_dxva_h264_decoder_start (GstVideoDecoder * decoder)
{
GstDxvaH264Decoder *self = GST_DXVA_H264_DECODER (decoder);
gst_dxva_h264_decoder_reset (self);
return GST_VIDEO_DECODER_CLASS (parent_class)->start (decoder);
}
static GstFlowReturn
gst_dxva_h264_decoder_new_sequence (GstH264Decoder * decoder,
const GstH264SPS * sps, gint max_dpb_size)
{
GstDxvaH264Decoder *self = GST_DXVA_H264_DECODER (decoder);
GstDxvaH264DecoderPrivate *priv = self->priv;
GstDxvaH264DecoderClass *klass = GST_DXVA_H264_DECODER_GET_CLASS (self);
gint crop_width, crop_height;
gboolean interlaced;
gboolean modified = FALSE;
GstVideoInfo info;
GstFlowReturn ret;
GST_LOG_OBJECT (self, "new sequence");
if (sps->frame_cropping_flag) {
crop_width = sps->crop_rect_width;
crop_height = sps->crop_rect_height;
} else {
crop_width = sps->width;
crop_height = sps->height;
}
if (priv->width != crop_width || priv->height != crop_height ||
priv->coded_width != sps->width || priv->coded_height != sps->height ||
priv->crop_x != sps->crop_rect_x || priv->crop_y != sps->crop_rect_y) {
GST_INFO_OBJECT (self,
"resolution change, %dx%d (%dx%d) -> %dx%d (%dx%d)",
priv->width, priv->height, priv->coded_width, priv->coded_height,
crop_width, crop_height, sps->width, sps->height);
priv->crop_x = sps->crop_rect_x;
priv->crop_y = sps->crop_rect_y;
priv->width = crop_width;
priv->height = crop_height;
priv->coded_width = sps->width;
priv->coded_height = sps->height;
modified = TRUE;
}
if (priv->bitdepth != sps->bit_depth_luma_minus8 + 8) {
gint bitdepth = sps->bit_depth_luma_minus8 + 8;
GST_INFO_OBJECT (self,
"bitdepth change, %d -> %d", priv->bitdepth, bitdepth);
priv->bitdepth = bitdepth;
modified = TRUE;
}
if (priv->chroma_format_idc != sps->chroma_format_idc) {
GST_INFO_OBJECT (self, "chroma format change, %d -> %d",
priv->chroma_format_idc, sps->chroma_format_idc);
priv->chroma_format_idc = sps->chroma_format_idc;
modified = TRUE;
}
interlaced = !sps->frame_mbs_only_flag;
if (priv->interlaced != interlaced) {
GST_INFO_OBJECT (self, "interlaced sequence change, %d -> %d",
priv->interlaced, interlaced);
priv->interlaced = interlaced;
modified = TRUE;
}
if (priv->max_dpb_size < max_dpb_size) {
GST_INFO_OBJECT (self, "Requires larger DPB size (%d -> %d)",
priv->max_dpb_size, max_dpb_size);
modified = TRUE;
}
if (!modified && priv->configured)
return GST_FLOW_OK;
priv->out_format = GST_VIDEO_FORMAT_UNKNOWN;
if (priv->bitdepth == 8) {
if (priv->chroma_format_idc == 1) {
priv->out_format = GST_VIDEO_FORMAT_NV12;
} else {
GST_FIXME_OBJECT (self, "Could not support 8bits non-4:2:0 format");
}
}
if (priv->out_format == GST_VIDEO_FORMAT_UNKNOWN) {
GST_ERROR_OBJECT (self, "Could not support bitdepth/chroma format");
priv->configured = FALSE;
return GST_FLOW_NOT_NEGOTIATED;
}
gst_video_info_set_interlaced_format (&info, priv->out_format,
priv->interlaced ? GST_VIDEO_INTERLACE_MODE_MIXED :
GST_VIDEO_INTERLACE_MODE_PROGRESSIVE, priv->width, priv->height);
priv->max_dpb_size = max_dpb_size;
g_assert (klass->configure);
ret = klass->configure (self, decoder->input_state, &info, priv->crop_x,
priv->crop_y, priv->coded_width, priv->coded_height, max_dpb_size);
if (ret == GST_FLOW_OK) {
priv->configured = TRUE;
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
GST_WARNING_OBJECT (self, "Couldn't negotiate with new sequence");
ret = GST_FLOW_NOT_NEGOTIATED;
}
} else {
priv->configured = FALSE;
}
return ret;
}
static GstFlowReturn
gst_dxva_h264_decoder_new_picture (GstH264Decoder * decoder,
GstVideoCodecFrame * frame, GstH264Picture * picture)
{
GstDxvaH264Decoder *self = GST_DXVA_H264_DECODER (decoder);
GstDxvaH264DecoderClass *klass = GST_DXVA_H264_DECODER_GET_CLASS (self);
g_assert (klass->new_picture);
return klass->new_picture (self, GST_CODEC_PICTURE (picture));
}
static GstFlowReturn
gst_dxva_h264_decoder_new_field_picture (GstH264Decoder * decoder,
GstH264Picture * first_field, GstH264Picture * second_field)
{
GstDxvaH264Decoder *self = GST_DXVA_H264_DECODER (decoder);
GstDxvaH264DecoderClass *klass = GST_DXVA_H264_DECODER_GET_CLASS (self);
g_assert (klass->duplicate_picture);
return klass->duplicate_picture (self, GST_CODEC_PICTURE (first_field),
GST_CODEC_PICTURE (second_field));
}
static void
gst_dxva_h264_decoder_picture_params_from_sps (GstDxvaH264Decoder * self,
const GstH264SPS * sps, gboolean field_pic, DXVA_PicParams_H264 * params)
{
#define COPY_FIELD(f) \
(params)->f = (sps)->f
params->wFrameWidthInMbsMinus1 = sps->pic_width_in_mbs_minus1;
if (!sps->frame_mbs_only_flag) {
params->wFrameHeightInMbsMinus1 =
((sps->pic_height_in_map_units_minus1 + 1) << 1) - 1;
} else {
params->wFrameHeightInMbsMinus1 = sps->pic_height_in_map_units_minus1;
}
params->residual_colour_transform_flag = sps->separate_colour_plane_flag;
params->MbaffFrameFlag = (sps->mb_adaptive_frame_field_flag && !field_pic);
params->field_pic_flag = field_pic;
params->MinLumaBipredSize8x8Flag = sps->level_idc >= 31;
COPY_FIELD (num_ref_frames);
COPY_FIELD (chroma_format_idc);
COPY_FIELD (frame_mbs_only_flag);
COPY_FIELD (bit_depth_luma_minus8);
COPY_FIELD (bit_depth_chroma_minus8);
COPY_FIELD (log2_max_frame_num_minus4);
COPY_FIELD (pic_order_cnt_type);
COPY_FIELD (log2_max_pic_order_cnt_lsb_minus4);
COPY_FIELD (delta_pic_order_always_zero_flag);
COPY_FIELD (direct_8x8_inference_flag);
#undef COPY_FIELD
}
static void
gst_dxva_h264_decoder_picture_params_from_pps (GstDxvaH264Decoder * self,
const GstH264PPS * pps, DXVA_PicParams_H264 * params)
{
#define COPY_FIELD(f) \
(params)->f = (pps)->f
COPY_FIELD (constrained_intra_pred_flag);
COPY_FIELD (weighted_pred_flag);
COPY_FIELD (weighted_bipred_idc);
COPY_FIELD (transform_8x8_mode_flag);
COPY_FIELD (pic_init_qs_minus26);
COPY_FIELD (chroma_qp_index_offset);
COPY_FIELD (second_chroma_qp_index_offset);
COPY_FIELD (pic_init_qp_minus26);
COPY_FIELD (num_ref_idx_l0_active_minus1);
COPY_FIELD (num_ref_idx_l1_active_minus1);
COPY_FIELD (entropy_coding_mode_flag);
COPY_FIELD (pic_order_present_flag);
COPY_FIELD (deblocking_filter_control_present_flag);
COPY_FIELD (redundant_pic_cnt_present_flag);
COPY_FIELD (num_slice_groups_minus1);
COPY_FIELD (slice_group_map_type);
#undef COPY_FIELD
}
static void
gst_dxva_h264_decoder_picture_params_from_slice_header (GstDxvaH264Decoder *
self, const GstH264SliceHdr * slice_header, DXVA_PicParams_H264 * params)
{
params->sp_for_switch_flag = slice_header->sp_for_switch_flag;
params->field_pic_flag = slice_header->field_pic_flag;
params->CurrPic.AssociatedFlag = slice_header->bottom_field_flag;
params->IntraPicFlag =
GST_H264_IS_I_SLICE (slice_header) || GST_H264_IS_SI_SLICE (slice_header);
}
static gboolean
gst_dxva_h264_decoder_fill_picture_params (GstDxvaH264Decoder * self,
const GstH264SliceHdr * slice_header, DXVA_PicParams_H264 * params)
{
const GstH264SPS *sps;
const GstH264PPS *pps;
g_return_val_if_fail (slice_header->pps != nullptr, FALSE);
g_return_val_if_fail (slice_header->pps->sequence != nullptr, FALSE);
pps = slice_header->pps;
sps = pps->sequence;
params->MbsConsecutiveFlag = 1;
params->Reserved16Bits = 3;
params->ContinuationFlag = 1;
params->Reserved8BitsA = 0;
params->Reserved8BitsB = 0;
params->StatusReportFeedbackNumber = 1;
gst_dxva_h264_decoder_picture_params_from_sps (self,
sps, slice_header->field_pic_flag, params);
gst_dxva_h264_decoder_picture_params_from_pps (self, pps, params);
gst_dxva_h264_decoder_picture_params_from_slice_header (self,
slice_header, params);
return TRUE;
}
static inline void
init_pic_params (DXVA_PicParams_H264 * params)
{
memset (params, 0, sizeof (DXVA_PicParams_H264));
for (guint i = 0; i < G_N_ELEMENTS (params->RefFrameList); i++)
params->RefFrameList[i].bPicEntry = 0xff;
}
static GstFlowReturn
gst_dxva_h264_decoder_start_picture (GstH264Decoder * decoder,
GstH264Picture * picture, GstH264Slice * slice, GstH264Dpb * dpb)
{
GstDxvaH264Decoder *self = GST_DXVA_H264_DECODER (decoder);
GstDxvaH264DecoderClass *klass = GST_DXVA_H264_DECODER_GET_CLASS (self);
GstDxvaH264DecoderPrivate *priv = self->priv;
DXVA_PicParams_H264 *pic_params = &priv->pic_params;
DXVA_Qmatrix_H264 *iq_matrix = &priv->iq_matrix;
GstCodecPicture *codec_picture = GST_CODEC_PICTURE (picture);
GArray *dpb_array;
GstH264PPS *pps;
guint i, j;
GstFlowReturn ret;
guint8 picture_id;
g_assert (klass->start_picture);
g_assert (klass->get_picture_id);
ret = klass->start_picture (self, codec_picture, &picture_id);
if (ret != GST_FLOW_OK)
return ret;
pps = slice->header.pps;
priv->slice_list.resize (0);
priv->bitstream_buffer.resize (0);
g_ptr_array_set_size (priv->ref_pics, 0);
init_pic_params (pic_params);
gst_dxva_h264_decoder_fill_picture_params (self, &slice->header, pic_params);
pic_params->CurrPic.Index7Bits = picture_id;
pic_params->RefPicFlag = GST_H264_PICTURE_IS_REF (picture);
pic_params->frame_num = picture->frame_num;
if (picture->field == GST_H264_PICTURE_FIELD_TOP_FIELD) {
pic_params->CurrFieldOrderCnt[0] = picture->top_field_order_cnt;
pic_params->CurrFieldOrderCnt[1] = 0;
} else if (picture->field == GST_H264_PICTURE_FIELD_BOTTOM_FIELD) {
pic_params->CurrFieldOrderCnt[0] = 0;
pic_params->CurrFieldOrderCnt[1] = picture->bottom_field_order_cnt;
} else {
pic_params->CurrFieldOrderCnt[0] = picture->top_field_order_cnt;
pic_params->CurrFieldOrderCnt[1] = picture->bottom_field_order_cnt;
}
dpb_array = gst_h264_dpb_get_pictures_all (dpb);
for (i = 0, j = 0; i < dpb_array->len && j < 16; i++) {
GstH264Picture *other = g_array_index (dpb_array, GstH264Picture *, i);
if (!GST_H264_PICTURE_IS_REF (other))
continue;
/* Ignore nonexisting picture */
if (other->nonexisting)
continue;
/* The second field picture will be handled differently */
if (other->second_field)
continue;
pic_params->RefFrameList[j].Index7Bits =
klass->get_picture_id (self, GST_CODEC_PICTURE (other));
if (GST_H264_PICTURE_IS_LONG_TERM_REF (other)) {
pic_params->RefFrameList[j].AssociatedFlag = 1;
pic_params->FrameNumList[j] = other->long_term_frame_idx;
} else {
pic_params->RefFrameList[j].AssociatedFlag = 0;
pic_params->FrameNumList[j] = other->frame_num;
}
switch (other->field) {
case GST_H264_PICTURE_FIELD_TOP_FIELD:
pic_params->FieldOrderCntList[j][0] = other->top_field_order_cnt;
pic_params->UsedForReferenceFlags |= 0x1 << (2 * j);
break;
case GST_H264_PICTURE_FIELD_BOTTOM_FIELD:
pic_params->FieldOrderCntList[j][1] = other->bottom_field_order_cnt;
pic_params->UsedForReferenceFlags |= 0x1 << (2 * j + 1);
break;
default:
pic_params->FieldOrderCntList[j][0] = other->top_field_order_cnt;
pic_params->FieldOrderCntList[j][1] = other->bottom_field_order_cnt;
pic_params->UsedForReferenceFlags |= 0x3 << (2 * j);
break;
}
if (other->other_field) {
GstH264Picture *other_field = other->other_field;
switch (other_field->field) {
case GST_H264_PICTURE_FIELD_TOP_FIELD:
pic_params->FieldOrderCntList[j][0] =
other_field->top_field_order_cnt;
pic_params->UsedForReferenceFlags |= 0x1 << (2 * j);
break;
case GST_H264_PICTURE_FIELD_BOTTOM_FIELD:
pic_params->FieldOrderCntList[j][1] =
other_field->bottom_field_order_cnt;
pic_params->UsedForReferenceFlags |= 0x1 << (2 * j + 1);
break;
default:
break;
}
}
g_ptr_array_add (priv->ref_pics, other);
j++;
}
g_array_unref (dpb_array);
G_STATIC_ASSERT (sizeof (iq_matrix->bScalingLists4x4) ==
sizeof (pps->scaling_lists_4x4));
memcpy (iq_matrix->bScalingLists4x4, pps->scaling_lists_4x4,
sizeof (pps->scaling_lists_4x4));
G_STATIC_ASSERT (sizeof (iq_matrix->bScalingLists8x8[0]) ==
sizeof (pps->scaling_lists_8x8[0]));
memcpy (iq_matrix->bScalingLists8x8[0], pps->scaling_lists_8x8[0],
sizeof (pps->scaling_lists_8x8[0]));
memcpy (iq_matrix->bScalingLists8x8[1], pps->scaling_lists_8x8[1],
sizeof (pps->scaling_lists_8x8[1]));
return GST_FLOW_OK;
}
static GstFlowReturn
gst_dxva_h264_decoder_decode_slice (GstH264Decoder * decoder,
GstH264Picture * picture, GstH264Slice * slice, GArray * ref_pic_list0,
GArray * ref_pic_list1)
{
GstDxvaH264Decoder *self = GST_DXVA_H264_DECODER (decoder);
GstDxvaH264DecoderPrivate *priv = self->priv;
DXVA_Slice_H264_Short dxva_slice;
static const guint8 start_code[] = { 0, 0, 1 };
const size_t start_code_size = sizeof (start_code);
dxva_slice.BSNALunitDataLocation = priv->bitstream_buffer.size ();
/* Includes 3 bytes start code prefix */
dxva_slice.SliceBytesInBuffer = slice->nalu.size + start_code_size;
dxva_slice.wBadSliceChopping = 0;
priv->slice_list.push_back (dxva_slice);
size_t pos = priv->bitstream_buffer.size ();
priv->bitstream_buffer.resize (pos + start_code_size + slice->nalu.size);
/* Fill start code prefix */
memcpy (&priv->bitstream_buffer[0] + pos, start_code, start_code_size);
/* Copy bitstream */
memcpy (&priv->bitstream_buffer[0] + pos + start_code_size,
slice->nalu.data + slice->nalu.offset, slice->nalu.size);
return GST_FLOW_OK;
}
static GstFlowReturn
gst_dxva_h264_decoder_end_picture (GstH264Decoder * decoder,
GstH264Picture * picture)
{
GstDxvaH264Decoder *self = GST_DXVA_H264_DECODER (decoder);
GstDxvaH264DecoderPrivate *priv = self->priv;
GstDxvaH264DecoderClass *klass = GST_DXVA_H264_DECODER_GET_CLASS (self);
size_t bitstream_buffer_size;
size_t bitstream_pos;
GstDxvaDecodingArgs args;
GST_LOG_OBJECT (self, "end picture %p, (poc %d)",
picture, picture->pic_order_cnt);
if (priv->bitstream_buffer.empty () || priv->slice_list.empty ()) {
GST_ERROR_OBJECT (self, "No bitstream buffer to submit");
return GST_FLOW_ERROR;
}
memset (&args, 0, sizeof (GstDxvaDecodingArgs));
bitstream_pos = priv->bitstream_buffer.size ();
bitstream_buffer_size = GST_ROUND_UP_128 (bitstream_pos);
if (bitstream_buffer_size > bitstream_pos) {
size_t padding = bitstream_buffer_size - bitstream_pos;
/* As per DXVA spec, total amount of bitstream buffer size should be
* 128 bytes aligned. If actual data is not multiple of 128 bytes,
* the last slice data needs to be zero-padded */
priv->bitstream_buffer.resize (bitstream_buffer_size, 0);
DXVA_Slice_H264_Short & slice = priv->slice_list.back ();
slice.SliceBytesInBuffer += padding;
}
args.picture_params = &priv->pic_params;
args.picture_params_size = sizeof (DXVA_PicParams_H264);
args.slice_control = &priv->slice_list[0];
args.slice_control_size =
sizeof (DXVA_Slice_H264_Short) * priv->slice_list.size ();
args.bitstream = &priv->bitstream_buffer[0];
args.bitstream_size = priv->bitstream_buffer.size ();
args.inverse_quantization_matrix = &priv->iq_matrix;
args.inverse_quantization_matrix_size = sizeof (DXVA_Qmatrix_H264);
g_assert (klass->end_picture);
return klass->end_picture (self, GST_CODEC_PICTURE (picture),
priv->ref_pics, &args);
}
static GstFlowReturn
gst_dxva_h264_decoder_output_picture (GstH264Decoder * decoder,
GstVideoCodecFrame * frame, GstH264Picture * picture)
{
GstDxvaH264Decoder *self = GST_DXVA_H264_DECODER (decoder);
GstDxvaH264DecoderClass *klass = GST_DXVA_H264_DECODER_GET_CLASS (self);
GstDxvaH264DecoderPrivate *priv = self->priv;
g_assert (klass->output_picture);
GST_LOG_OBJECT (self,
"Outputting picture %p (poc %d)", picture, picture->pic_order_cnt);
return klass->output_picture (self, frame, GST_CODEC_PICTURE (picture),
picture->buffer_flags, priv->width, priv->height);
}

View file

@ -0,0 +1,94 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/dxva/dxva-prelude.h>
#include <gst/dxva/gstdxvatypes.h>
#include <gst/codecs/gsth264decoder.h>
G_BEGIN_DECLS
#define GST_TYPE_DXVA_H264_DECODER (gst_dxva_h264_decoder_get_type())
#define GST_DXVA_H264_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DXVA_H264_DECODER,GstDxvaH264Decoder))
#define GST_DXVA_H264_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DXVA_H264_DECODER,GstDxvaH264DecoderClass))
#define GST_DXVA_H264_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_DXVA_H264_DECODER,GstDxvaH264DecoderClass))
#define GST_IS_DXVA_H264_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DXVA_H264_DECODER))
#define GST_IS_DXVA_H264_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DXVA_H264_DECODER))
#define GST_DXVA_H264_DECODER_CAST(obj) ((GstDxvaH264Decoder*)obj)
typedef struct _GstDxvaH264Decoder GstDxvaH264Decoder;
typedef struct _GstDxvaH264DecoderClass GstDxvaH264DecoderClass;
typedef struct _GstDxvaH264DecoderPrivate GstDxvaH264DecoderPrivate;
struct _GstDxvaH264Decoder
{
GstH264Decoder parent;
/*< private >*/
GstDxvaH264DecoderPrivate *priv;
};
struct _GstDxvaH264DecoderClass
{
GstH264DecoderClass parent_class;
GstFlowReturn (*configure) (GstDxvaH264Decoder * decoder,
GstVideoCodecState * input_state,
const GstVideoInfo * info,
gint crop_x,
gint crop_y,
gint coded_width,
gint coded_height,
gint max_dpb_size);
GstFlowReturn (*new_picture) (GstDxvaH264Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*duplicate_picture) (GstDxvaH264Decoder * decoder,
GstCodecPicture * src,
GstCodecPicture * dst);
guint8 (*get_picture_id) (GstDxvaH264Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*start_picture) (GstDxvaH264Decoder * decoder,
GstCodecPicture * picture,
guint8 * picture_id);
GstFlowReturn (*end_picture) (GstDxvaH264Decoder * decoder,
GstCodecPicture * picture,
GPtrArray * ref_pics,
const GstDxvaDecodingArgs * args);
GstFlowReturn (*output_picture) (GstDxvaH264Decoder * decoder,
GstVideoCodecFrame * frame,
GstCodecPicture * picture,
GstVideoBufferFlags buffer_flags,
gint display_width,
gint display_height);
};
GST_DXVA_API
GType gst_dxva_h264_decoder_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDxvaH264Decoder, gst_object_unref)
G_END_DECLS

View file

@ -0,0 +1,740 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdxvah265decoder.h"
#include <string.h>
#include <vector>
/* HACK: to expose dxva data structure on UWP */
#ifdef WINAPI_PARTITION_DESKTOP
#undef WINAPI_PARTITION_DESKTOP
#endif
#define WINAPI_PARTITION_DESKTOP 1
#include <d3d9.h>
#include <dxva.h>
GST_DEBUG_CATEGORY_STATIC (gst_dxva_h265_decoder_debug);
#define GST_CAT_DEFAULT gst_dxva_h265_decoder_debug
/* *INDENT-OFF* */
struct _GstDxvaH265DecoderPrivate
{
DXVA_PicParams_HEVC pic_params;
DXVA_Qmatrix_HEVC iq_matrix;
std::vector<DXVA_Slice_HEVC_Short> slice_list;
std::vector<guint8> bitstream_buffer;
GPtrArray *ref_pics = nullptr;
gboolean submit_iq_data;
gint crop_x = 0;
gint crop_y = 0;
gint width = 0;
gint height = 0;
gint coded_width = 0;
gint coded_height = 0;
gint bitdepth = 0;
guint8 chroma_format_idc = 0;
GstVideoFormat out_format = GST_VIDEO_FORMAT_UNKNOWN;
GstVideoInterlaceMode interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
gint max_dpb_size = 0;
gboolean configured;
};
/* *INDENT-ON* */
static void gst_dxva_h265_decoder_finalize (GObject * object);
static gboolean gst_dxva_h265_decoder_start (GstVideoDecoder * decoder);
static GstFlowReturn
gst_dxva_h265_decoder_new_sequence (GstH265Decoder * decoder,
const GstH265SPS * sps, gint max_dpb_size);
static GstFlowReturn
gst_dxva_h265_decoder_new_picture (GstH265Decoder * decoder,
GstVideoCodecFrame * frame, GstH265Picture * picture);
static GstFlowReturn
gst_dxva_h265_decoder_start_picture (GstH265Decoder * decoder,
GstH265Picture * picture, GstH265Slice * slice, GstH265Dpb * dpb);
static GstFlowReturn
gst_dxva_h265_decoder_decode_slice (GstH265Decoder * decoder,
GstH265Picture * picture, GstH265Slice * slice,
GArray * ref_pic_list0, GArray * ref_pic_list1);
static GstFlowReturn
gst_dxva_h265_decoder_end_picture (GstH265Decoder * decoder,
GstH265Picture * picture);
static GstFlowReturn
gst_dxva_h265_decoder_output_picture (GstH265Decoder *
decoder, GstVideoCodecFrame * frame, GstH265Picture * picture);
#define gst_dxva_h265_decoder_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstDxvaH265Decoder,
gst_dxva_h265_decoder, GST_TYPE_H265_DECODER,
GST_DEBUG_CATEGORY_INIT (gst_dxva_h265_decoder_debug, "dxvah265decoder",
0, "dxvah265decoder"));
static void
gst_dxva_h265_decoder_class_init (GstDxvaH265DecoderClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
GstH265DecoderClass *h265decoder_class = GST_H265_DECODER_CLASS (klass);
gobject_class->finalize = gst_dxva_h265_decoder_finalize;
decoder_class->start = GST_DEBUG_FUNCPTR (gst_dxva_h265_decoder_start);
h265decoder_class->new_sequence =
GST_DEBUG_FUNCPTR (gst_dxva_h265_decoder_new_sequence);
h265decoder_class->new_picture =
GST_DEBUG_FUNCPTR (gst_dxva_h265_decoder_new_picture);
h265decoder_class->start_picture =
GST_DEBUG_FUNCPTR (gst_dxva_h265_decoder_start_picture);
h265decoder_class->decode_slice =
GST_DEBUG_FUNCPTR (gst_dxva_h265_decoder_decode_slice);
h265decoder_class->end_picture =
GST_DEBUG_FUNCPTR (gst_dxva_h265_decoder_end_picture);
h265decoder_class->output_picture =
GST_DEBUG_FUNCPTR (gst_dxva_h265_decoder_output_picture);
}
static void
gst_dxva_h265_decoder_init (GstDxvaH265Decoder * self)
{
self->priv = new GstDxvaH265DecoderPrivate ();
self->priv->ref_pics = g_ptr_array_new ();
}
static void
gst_dxva_h265_decoder_finalize (GObject * object)
{
GstDxvaH265Decoder *self = GST_DXVA_H265_DECODER (object);
GstDxvaH265DecoderPrivate *priv = self->priv;
g_ptr_array_unref (priv->ref_pics);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_dxva_h265_decoder_reset (GstDxvaH265Decoder * self)
{
GstDxvaH265DecoderPrivate *priv = self->priv;
priv->crop_x = 0;
priv->crop_y = 0;
priv->width = 0;
priv->height = 0;
priv->coded_width = 0;
priv->coded_height = 0;
priv->bitdepth = 0;
priv->chroma_format_idc = 0;
priv->out_format = GST_VIDEO_FORMAT_UNKNOWN;
priv->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
priv->max_dpb_size = 0;
priv->configured = FALSE;
}
static gboolean
gst_dxva_h265_decoder_start (GstVideoDecoder * decoder)
{
GstDxvaH265Decoder *self = GST_DXVA_H265_DECODER (decoder);
gst_dxva_h265_decoder_reset (self);
return GST_VIDEO_DECODER_CLASS (parent_class)->start (decoder);
}
static GstFlowReturn
gst_dxva_h265_decoder_new_sequence (GstH265Decoder * decoder,
const GstH265SPS * sps, gint max_dpb_size)
{
GstDxvaH265Decoder *self = GST_DXVA_H265_DECODER (decoder);
GstDxvaH265DecoderPrivate *priv = self->priv;
GstDxvaH265DecoderClass *klass = GST_DXVA_H265_DECODER_GET_CLASS (self);
gint crop_width, crop_height;
gboolean modified = FALSE;
GstVideoInterlaceMode interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
GstVideoInfo info;
GstFlowReturn ret;
GST_LOG_OBJECT (self, "new sequence");
if (sps->conformance_window_flag) {
crop_width = sps->crop_rect_width;
crop_height = sps->crop_rect_height;
} else {
crop_width = sps->width;
crop_height = sps->height;
}
if (priv->width != crop_width || priv->height != crop_height ||
priv->coded_width != sps->width || priv->coded_height != sps->height ||
priv->crop_x != sps->crop_rect_x || priv->crop_y != sps->crop_rect_y) {
GST_INFO_OBJECT (self, "resolution changed %dx%d (%dx%d) -> %dx%d (%dx%d)",
priv->width, priv->height, priv->coded_width, priv->coded_height,
crop_width, crop_height, sps->width, sps->height);
priv->crop_x = sps->crop_rect_x;
priv->crop_y = sps->crop_rect_y;
priv->width = crop_width;
priv->height = crop_height;
priv->coded_width = sps->width;
priv->coded_height = sps->height;
modified = TRUE;
}
if (priv->bitdepth != sps->bit_depth_luma_minus8 + 8) {
gint bitdepth = sps->bit_depth_luma_minus8 + 8;
GST_INFO_OBJECT (self,
"bitdepth change, %d -> %d", priv->bitdepth, bitdepth);
priv->bitdepth = bitdepth;
modified = TRUE;
}
if (sps->vui_parameters_present_flag && sps->vui_params.field_seq_flag) {
interlace_mode = GST_VIDEO_INTERLACE_MODE_ALTERNATE;
} else {
/* 7.4.4 Profile, tier and level sementics */
if (sps->profile_tier_level.progressive_source_flag &&
!sps->profile_tier_level.interlaced_source_flag) {
interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
} else {
interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED;
}
}
if (priv->interlace_mode != interlace_mode) {
GST_INFO_OBJECT (self, "Interlace mode change %d -> %d",
priv->interlace_mode, interlace_mode);
priv->interlace_mode = interlace_mode;
modified = TRUE;
}
if (priv->chroma_format_idc != sps->chroma_format_idc) {
GST_INFO_OBJECT (self, "chroma format changed");
priv->chroma_format_idc = sps->chroma_format_idc;
modified = TRUE;
}
if (priv->max_dpb_size < max_dpb_size) {
GST_INFO_OBJECT (self, "Requires larger DPB size (%d -> %d)",
priv->max_dpb_size, max_dpb_size);
modified = TRUE;
}
if (!modified && priv->configured)
return GST_FLOW_OK;
priv->out_format = GST_VIDEO_FORMAT_UNKNOWN;
if (priv->bitdepth == 8) {
if (priv->chroma_format_idc == 1) {
priv->out_format = GST_VIDEO_FORMAT_NV12;
} else {
GST_FIXME_OBJECT (self, "Could not support 8bits non-4:2:0 format");
}
} else if (priv->bitdepth == 10) {
if (priv->chroma_format_idc == 1) {
priv->out_format = GST_VIDEO_FORMAT_P010_10LE;
} else {
GST_FIXME_OBJECT (self, "Could not support 10bits non-4:2:0 format");
}
}
if (priv->out_format == GST_VIDEO_FORMAT_UNKNOWN) {
GST_ERROR_OBJECT (self, "Could not support bitdepth/chroma format");
priv->configured = FALSE;
return GST_FLOW_NOT_NEGOTIATED;
}
gst_video_info_set_interlaced_format (&info, priv->out_format,
priv->interlace_mode, priv->width, priv->height);
priv->max_dpb_size = max_dpb_size;
g_assert (klass->configure);
ret = klass->configure (self, decoder->input_state, &info, priv->crop_x,
priv->crop_y, priv->coded_width, priv->coded_height, max_dpb_size);
if (ret == GST_FLOW_OK) {
priv->configured = TRUE;
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
GST_WARNING_OBJECT (self, "Couldn't negotiate with new sequence");
ret = GST_FLOW_NOT_NEGOTIATED;
}
} else {
priv->configured = FALSE;
}
return ret;
}
static GstFlowReturn
gst_dxva_h265_decoder_new_picture (GstH265Decoder * decoder,
GstVideoCodecFrame * frame, GstH265Picture * picture)
{
GstDxvaH265Decoder *self = GST_DXVA_H265_DECODER (decoder);
GstDxvaH265DecoderClass *klass = GST_DXVA_H265_DECODER_GET_CLASS (self);
g_assert (klass->new_picture);
return klass->new_picture (self, GST_CODEC_PICTURE (picture));
}
static void
gst_dxva_h265_decoder_picture_params_from_sps (GstDxvaH265Decoder * self,
const GstH265SPS * sps, DXVA_PicParams_HEVC * params)
{
#define COPY_FIELD(f) \
(params)->f = (sps)->f
#define COPY_FIELD_WITH_PREFIX(f) \
(params)->G_PASTE(sps_,f) = (sps)->f
params->PicWidthInMinCbsY =
sps->width >> (sps->log2_min_luma_coding_block_size_minus3 + 3);
params->PicHeightInMinCbsY =
sps->height >> (sps->log2_min_luma_coding_block_size_minus3 + 3);
params->sps_max_dec_pic_buffering_minus1 =
sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1];
COPY_FIELD (chroma_format_idc);
COPY_FIELD (separate_colour_plane_flag);
COPY_FIELD (bit_depth_luma_minus8);
COPY_FIELD (bit_depth_chroma_minus8);
COPY_FIELD (log2_max_pic_order_cnt_lsb_minus4);
COPY_FIELD (log2_min_luma_coding_block_size_minus3);
COPY_FIELD (log2_diff_max_min_luma_coding_block_size);
COPY_FIELD (log2_min_transform_block_size_minus2);
COPY_FIELD (log2_diff_max_min_transform_block_size);
COPY_FIELD (max_transform_hierarchy_depth_inter);
COPY_FIELD (max_transform_hierarchy_depth_intra);
COPY_FIELD (num_short_term_ref_pic_sets);
COPY_FIELD (num_long_term_ref_pics_sps);
COPY_FIELD (scaling_list_enabled_flag);
COPY_FIELD (amp_enabled_flag);
COPY_FIELD (sample_adaptive_offset_enabled_flag);
COPY_FIELD (pcm_enabled_flag);
if (sps->pcm_enabled_flag) {
COPY_FIELD (pcm_sample_bit_depth_luma_minus1);
COPY_FIELD (pcm_sample_bit_depth_chroma_minus1);
COPY_FIELD (log2_min_pcm_luma_coding_block_size_minus3);
COPY_FIELD (log2_diff_max_min_pcm_luma_coding_block_size);
}
COPY_FIELD (pcm_loop_filter_disabled_flag);
COPY_FIELD (long_term_ref_pics_present_flag);
COPY_FIELD_WITH_PREFIX (temporal_mvp_enabled_flag);
COPY_FIELD (strong_intra_smoothing_enabled_flag);
#undef COPY_FIELD
#undef COPY_FIELD_WITH_PREFIX
}
static void
gst_dxva_h265_decoder_picture_params_from_pps (GstDxvaH265Decoder * self,
const GstH265PPS * pps, DXVA_PicParams_HEVC * params)
{
guint i;
#define COPY_FIELD(f) \
(params)->f = (pps)->f
#define COPY_FIELD_WITH_PREFIX(f) \
(params)->G_PASTE(pps_,f) = (pps)->f
COPY_FIELD (num_ref_idx_l0_default_active_minus1);
COPY_FIELD (num_ref_idx_l1_default_active_minus1);
COPY_FIELD (init_qp_minus26);
COPY_FIELD (dependent_slice_segments_enabled_flag);
COPY_FIELD (output_flag_present_flag);
COPY_FIELD (num_extra_slice_header_bits);
COPY_FIELD (sign_data_hiding_enabled_flag);
COPY_FIELD (cabac_init_present_flag);
COPY_FIELD (constrained_intra_pred_flag);
COPY_FIELD (transform_skip_enabled_flag);
COPY_FIELD (cu_qp_delta_enabled_flag);
COPY_FIELD_WITH_PREFIX (slice_chroma_qp_offsets_present_flag);
COPY_FIELD (weighted_pred_flag);
COPY_FIELD (weighted_bipred_flag);
COPY_FIELD (transquant_bypass_enabled_flag);
COPY_FIELD (tiles_enabled_flag);
COPY_FIELD (entropy_coding_sync_enabled_flag);
COPY_FIELD (uniform_spacing_flag);
if (pps->tiles_enabled_flag)
COPY_FIELD (loop_filter_across_tiles_enabled_flag);
COPY_FIELD_WITH_PREFIX (loop_filter_across_slices_enabled_flag);
COPY_FIELD (deblocking_filter_override_enabled_flag);
COPY_FIELD_WITH_PREFIX (deblocking_filter_disabled_flag);
COPY_FIELD (lists_modification_present_flag);
COPY_FIELD (slice_segment_header_extension_present_flag);
COPY_FIELD_WITH_PREFIX (cb_qp_offset);
COPY_FIELD_WITH_PREFIX (cr_qp_offset);
if (pps->tiles_enabled_flag) {
COPY_FIELD (num_tile_columns_minus1);
COPY_FIELD (num_tile_rows_minus1);
if (!pps->uniform_spacing_flag) {
for (i = 0; i < pps->num_tile_columns_minus1 &&
i < G_N_ELEMENTS (params->column_width_minus1); i++)
COPY_FIELD (column_width_minus1[i]);
for (i = 0; i < pps->num_tile_rows_minus1 &&
i < G_N_ELEMENTS (params->row_height_minus1); i++)
COPY_FIELD (row_height_minus1[i]);
}
}
COPY_FIELD (diff_cu_qp_delta_depth);
COPY_FIELD_WITH_PREFIX (beta_offset_div2);
COPY_FIELD_WITH_PREFIX (tc_offset_div2);
COPY_FIELD (log2_parallel_merge_level_minus2);
#undef COPY_FIELD
#undef COPY_FIELD_WITH_PREFIX
}
static void
gst_dxva_h265_decoder_picture_params_from_slice_header (GstDxvaH265Decoder *
self, const GstH265SliceHdr * slice_header, DXVA_PicParams_HEVC * params)
{
if (slice_header->short_term_ref_pic_set_sps_flag == 0) {
params->ucNumDeltaPocsOfRefRpsIdx =
slice_header->short_term_ref_pic_sets.NumDeltaPocsOfRefRpsIdx;
params->wNumBitsForShortTermRPSInSlice =
slice_header->short_term_ref_pic_set_size;
}
}
static gboolean
gst_dxva_h265_decoder_fill_picture_params (GstDxvaH265Decoder * self,
const GstH265SliceHdr * slice_header, DXVA_PicParams_HEVC * params)
{
const GstH265SPS *sps;
const GstH265PPS *pps;
pps = slice_header->pps;
sps = pps->sps;
/* not related to hevc syntax */
params->NoPicReorderingFlag = 0;
params->NoBiPredFlag = 0;
params->ReservedBits1 = 0;
params->StatusReportFeedbackNumber = 1;
gst_dxva_h265_decoder_picture_params_from_sps (self, sps, params);
gst_dxva_h265_decoder_picture_params_from_pps (self, pps, params);
gst_dxva_h265_decoder_picture_params_from_slice_header (self,
slice_header, params);
return TRUE;
}
static UCHAR
gst_dxva_h265_decoder_get_ref_index (const DXVA_PicParams_HEVC * pic_params,
guint8 picture_id)
{
if (picture_id == 0xff)
return 0xff;
for (UCHAR i = 0; i < G_N_ELEMENTS (pic_params->RefPicList); i++) {
if (pic_params->RefPicList[i].Index7Bits == picture_id)
return i;
}
return 0xff;
}
static inline void
init_pic_params (DXVA_PicParams_HEVC * params)
{
memset (params, 0, sizeof (DXVA_PicParams_HEVC));
for (guint i = 0; i < G_N_ELEMENTS (params->RefPicList); i++)
params->RefPicList[i].bPicEntry = 0xff;
for (guint i = 0; i < G_N_ELEMENTS (params->RefPicSetStCurrBefore); i++) {
params->RefPicSetStCurrBefore[i] = 0xff;
params->RefPicSetStCurrAfter[i] = 0xff;
params->RefPicSetLtCurr[i] = 0xff;
}
}
static GstFlowReturn
gst_dxva_h265_decoder_start_picture (GstH265Decoder * decoder,
GstH265Picture * picture, GstH265Slice * slice, GstH265Dpb * dpb)
{
GstDxvaH265Decoder *self = GST_DXVA_H265_DECODER (decoder);
GstDxvaH265DecoderPrivate *priv = self->priv;
GstDxvaH265DecoderClass *klass = GST_DXVA_H265_DECODER_GET_CLASS (self);
DXVA_PicParams_HEVC *pic_params = &priv->pic_params;
DXVA_Qmatrix_HEVC *iq_matrix = &priv->iq_matrix;
GstCodecPicture *codec_picture = GST_CODEC_PICTURE (picture);
guint i, j;
GArray *dpb_array;
GstH265SPS *sps;
GstH265PPS *pps;
GstH265ScalingList *scaling_list = nullptr;
GstFlowReturn ret;
guint8 picture_id;
g_assert (klass->start_picture);
g_assert (klass->get_picture_id);
ret = klass->start_picture (self, codec_picture, &picture_id);
if (ret != GST_FLOW_OK)
return ret;
pps = slice->header.pps;
g_assert (pps);
sps = pps->sps;
g_assert (sps);
priv->slice_list.resize (0);
priv->bitstream_buffer.resize (0);
g_ptr_array_set_size (priv->ref_pics, 0);
init_pic_params (pic_params);
gst_dxva_h265_decoder_fill_picture_params (self, &slice->header, pic_params);
pic_params->CurrPic.Index7Bits = picture_id;
pic_params->IrapPicFlag = GST_H265_IS_NAL_TYPE_IRAP (slice->nalu.type);
pic_params->IdrPicFlag = GST_H265_IS_NAL_TYPE_IDR (slice->nalu.type);
pic_params->IntraPicFlag = GST_H265_IS_NAL_TYPE_IRAP (slice->nalu.type);
pic_params->CurrPicOrderCntVal = picture->pic_order_cnt;
dpb_array = gst_h265_dpb_get_pictures_all (dpb);
for (i = 0, j = 0;
i < dpb_array->len && j < G_N_ELEMENTS (pic_params->RefPicList); i++) {
GstH265Picture *other = g_array_index (dpb_array, GstH265Picture *, i);
guint8 id;
if (!other->ref)
continue;
id = klass->get_picture_id (self, GST_CODEC_PICTURE (other));
if (id != 0xff) {
pic_params->RefPicList[j].Index7Bits = id;
pic_params->RefPicList[j].AssociatedFlag = other->long_term;
pic_params->PicOrderCntValList[j] = other->pic_order_cnt;
g_ptr_array_add (priv->ref_pics, other);
}
j++;
}
g_array_unref (dpb_array);
for (i = 0, j = 0; i < G_N_ELEMENTS (pic_params->RefPicSetStCurrBefore); i++) {
GstH265Picture *other = nullptr;
guint8 id = 0xff;
while (!other && j < decoder->NumPocStCurrBefore)
other = decoder->RefPicSetStCurrBefore[j++];
if (other)
id = klass->get_picture_id (self, GST_CODEC_PICTURE (other));
pic_params->RefPicSetStCurrBefore[i] =
gst_dxva_h265_decoder_get_ref_index (pic_params, id);
}
for (i = 0, j = 0; i < G_N_ELEMENTS (pic_params->RefPicSetStCurrAfter); i++) {
GstH265Picture *other = nullptr;
guint8 id = 0xff;
while (!other && j < decoder->NumPocStCurrAfter)
other = decoder->RefPicSetStCurrAfter[j++];
if (other)
id = klass->get_picture_id (self, GST_CODEC_PICTURE (other));
pic_params->RefPicSetStCurrAfter[i] =
gst_dxva_h265_decoder_get_ref_index (pic_params, id);
}
for (i = 0, j = 0; i < G_N_ELEMENTS (pic_params->RefPicSetLtCurr); i++) {
GstH265Picture *other = nullptr;
guint8 id = 0xff;
while (!other && j < decoder->NumPocLtCurr)
other = decoder->RefPicSetLtCurr[j++];
if (other)
id = klass->get_picture_id (self, GST_CODEC_PICTURE (other));
pic_params->RefPicSetLtCurr[i] =
gst_dxva_h265_decoder_get_ref_index (pic_params, id);
}
if (pps->scaling_list_data_present_flag ||
(sps->scaling_list_enabled_flag
&& !sps->scaling_list_data_present_flag)) {
scaling_list = &pps->scaling_list;
} else if (sps->scaling_list_enabled_flag &&
sps->scaling_list_data_present_flag) {
scaling_list = &sps->scaling_list;
}
if (scaling_list) {
G_STATIC_ASSERT (sizeof (iq_matrix->ucScalingLists0) ==
sizeof (scaling_list->scaling_lists_4x4));
G_STATIC_ASSERT (sizeof (iq_matrix->ucScalingLists1) ==
sizeof (scaling_list->scaling_lists_8x8));
G_STATIC_ASSERT (sizeof (iq_matrix->ucScalingLists2) ==
sizeof (scaling_list->scaling_lists_16x16));
G_STATIC_ASSERT (sizeof (iq_matrix->ucScalingLists3) ==
sizeof (scaling_list->scaling_lists_32x32));
memcpy (iq_matrix->ucScalingLists0, scaling_list->scaling_lists_4x4,
sizeof (iq_matrix->ucScalingLists0));
memcpy (iq_matrix->ucScalingLists1, scaling_list->scaling_lists_8x8,
sizeof (iq_matrix->ucScalingLists1));
memcpy (iq_matrix->ucScalingLists2, scaling_list->scaling_lists_16x16,
sizeof (iq_matrix->ucScalingLists2));
memcpy (iq_matrix->ucScalingLists3, scaling_list->scaling_lists_32x32,
sizeof (iq_matrix->ucScalingLists3));
for (i = 0; i < G_N_ELEMENTS (iq_matrix->ucScalingListDCCoefSizeID2); i++) {
iq_matrix->ucScalingListDCCoefSizeID2[i] =
scaling_list->scaling_list_dc_coef_minus8_16x16[i] + 8;
}
for (i = 0; i < G_N_ELEMENTS (iq_matrix->ucScalingListDCCoefSizeID3); i++) {
iq_matrix->ucScalingListDCCoefSizeID3[i] =
scaling_list->scaling_list_dc_coef_minus8_32x32[i] + 8;
}
priv->submit_iq_data = TRUE;
} else {
priv->submit_iq_data = FALSE;
}
return GST_FLOW_OK;
}
static GstFlowReturn
gst_dxva_h265_decoder_decode_slice (GstH265Decoder * decoder,
GstH265Picture * picture, GstH265Slice * slice,
GArray * ref_pic_list0, GArray * ref_pic_list1)
{
GstDxvaH265Decoder *self = GST_DXVA_H265_DECODER (decoder);
GstDxvaH265DecoderPrivate *priv = self->priv;
DXVA_Slice_HEVC_Short dxva_slice;
static const guint8 start_code[] = { 0, 0, 1 };
const size_t start_code_size = sizeof (start_code);
dxva_slice.BSNALunitDataLocation = priv->bitstream_buffer.size ();
/* Includes 3 bytes start code prefix */
dxva_slice.SliceBytesInBuffer = slice->nalu.size + start_code_size;
dxva_slice.wBadSliceChopping = 0;
priv->slice_list.push_back (dxva_slice);
size_t pos = priv->bitstream_buffer.size ();
priv->bitstream_buffer.resize (pos + start_code_size + slice->nalu.size);
/* Fill start code prefix */
memcpy (&priv->bitstream_buffer[0] + pos, start_code, start_code_size);
/* Copy bitstream */
memcpy (&priv->bitstream_buffer[0] + pos + start_code_size,
slice->nalu.data + slice->nalu.offset, slice->nalu.size);
return GST_FLOW_OK;
}
static GstFlowReturn
gst_dxva_h265_decoder_end_picture (GstH265Decoder * decoder,
GstH265Picture * picture)
{
GstDxvaH265Decoder *self = GST_DXVA_H265_DECODER (decoder);
GstDxvaH265DecoderPrivate *priv = self->priv;
GstDxvaH265DecoderClass *klass = GST_DXVA_H265_DECODER_GET_CLASS (self);
size_t bitstream_buffer_size;
size_t bitstream_pos;
GstDxvaDecodingArgs args;
GST_LOG_OBJECT (self, "end picture %p, (poc %d)",
picture, picture->pic_order_cnt);
if (priv->bitstream_buffer.empty () || priv->slice_list.empty ()) {
GST_ERROR_OBJECT (self, "No bitstream buffer to submit");
return GST_FLOW_ERROR;
}
memset (&args, 0, sizeof (GstDxvaDecodingArgs));
bitstream_pos = priv->bitstream_buffer.size ();
bitstream_buffer_size = GST_ROUND_UP_128 (bitstream_pos);
if (bitstream_buffer_size > bitstream_pos) {
size_t padding = bitstream_buffer_size - bitstream_pos;
/* As per DXVA spec, total amount of bitstream buffer size should be
* 128 bytes aligned. If actual data is not multiple of 128 bytes,
* the last slice data needs to be zero-padded */
priv->bitstream_buffer.resize (bitstream_buffer_size, 0);
DXVA_Slice_HEVC_Short & slice = priv->slice_list.back ();
slice.SliceBytesInBuffer += padding;
}
args.picture_params = &priv->pic_params;
args.picture_params_size = sizeof (DXVA_PicParams_HEVC);
args.slice_control = &priv->slice_list[0];
args.slice_control_size =
sizeof (DXVA_Slice_HEVC_Short) * priv->slice_list.size ();
args.bitstream = &priv->bitstream_buffer[0];
args.bitstream_size = priv->bitstream_buffer.size ();
if (priv->submit_iq_data) {
args.inverse_quantization_matrix = &priv->iq_matrix;
args.inverse_quantization_matrix_size = sizeof (DXVA_Qmatrix_HEVC);
}
g_assert (klass->end_picture);
return klass->end_picture (self, GST_CODEC_PICTURE (picture),
priv->ref_pics, &args);
}
static GstFlowReturn
gst_dxva_h265_decoder_output_picture (GstH265Decoder * decoder,
GstVideoCodecFrame * frame, GstH265Picture * picture)
{
GstDxvaH265Decoder *self = GST_DXVA_H265_DECODER (decoder);
GstDxvaH265DecoderPrivate *priv = self->priv;
GstDxvaH265DecoderClass *klass = GST_DXVA_H265_DECODER_GET_CLASS (self);
g_assert (klass->output_picture);
GST_LOG_OBJECT (self, "Outputting picture %p, poc %d, picture_struct %d, "
"buffer flags 0x%x", picture, picture->pic_order_cnt, picture->pic_struct,
picture->buffer_flags);
return klass->output_picture (self, frame, GST_CODEC_PICTURE (picture),
picture->buffer_flags, priv->width, priv->height);
}

View file

@ -0,0 +1,90 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/dxva/dxva-prelude.h>
#include <gst/dxva/gstdxvatypes.h>
#include <gst/codecs/gsth265decoder.h>
G_BEGIN_DECLS
#define GST_TYPE_DXVA_H265_DECODER (gst_dxva_h265_decoder_get_type())
#define GST_DXVA_H265_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DXVA_H265_DECODER,GstDxvaH265Decoder))
#define GST_DXVA_H265_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DXVA_H265_DECODER,GstDxvaH265DecoderClass))
#define GST_DXVA_H265_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_DXVA_H265_DECODER,GstDxvaH265DecoderClass))
#define GST_IS_DXVA_H265_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DXVA_H265_DECODER))
#define GST_IS_DXVA_H265_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DXVA_H265_DECODER))
#define GST_DXVA_H265_DECODER_CAST(obj) ((GstDxvaH265Decoder*)obj)
typedef struct _GstDxvaH265Decoder GstDxvaH265Decoder;
typedef struct _GstDxvaH265DecoderClass GstDxvaH265DecoderClass;
typedef struct _GstDxvaH265DecoderPrivate GstDxvaH265DecoderPrivate;
struct _GstDxvaH265Decoder
{
GstH265Decoder parent;
/*< private >*/
GstDxvaH265DecoderPrivate *priv;
};
struct _GstDxvaH265DecoderClass
{
GstH265DecoderClass parent_class;
GstFlowReturn (*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);
GstFlowReturn (*new_picture) (GstDxvaH265Decoder * decoder,
GstCodecPicture * picture);
guint8 (*get_picture_id) (GstDxvaH265Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*start_picture) (GstDxvaH265Decoder * decoder,
GstCodecPicture * picture,
guint8 * picture_id);
GstFlowReturn (*end_picture) (GstDxvaH265Decoder * decoder,
GstCodecPicture * picture,
GPtrArray * ref_pics,
const GstDxvaDecodingArgs * args);
GstFlowReturn (*output_picture) (GstDxvaH265Decoder * decoder,
GstVideoCodecFrame * frame,
GstCodecPicture * picture,
GstVideoBufferFlags buffer_flags,
gint display_width,
gint display_height);
};
GST_DXVA_API
GType gst_dxva_h265_decoder_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDxvaH265Decoder, gst_object_unref)
G_END_DECLS

View file

@ -0,0 +1,537 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdxvampeg2decoder.h"
#include <string.h>
#include <vector>
/* HACK: to expose dxva data structure on UWP */
#ifdef WINAPI_PARTITION_DESKTOP
#undef WINAPI_PARTITION_DESKTOP
#endif
#define WINAPI_PARTITION_DESKTOP 1
#include <d3d9.h>
#include <dxva.h>
GST_DEBUG_CATEGORY_STATIC (gst_dxva_mpeg2_decoder_debug);
#define GST_CAT_DEFAULT gst_dxva_mpeg2_decoder_debug
/* *INDENT-OFF* */
struct _GstDxvaMpeg2DecoderPrivate
{
DXVA_PictureParameters pic_params;
DXVA_QmatrixData iq_matrix;
std::vector<DXVA_SliceInfo> slice_list;
std::vector<guint8> bitstream_buffer;
GPtrArray *ref_pics = nullptr;
gboolean submit_iq_data;
gint width = 0;
gint height = 0;
guint width_in_mb = 0;
guint height_in_mb = 0;
GstVideoFormat out_format = GST_VIDEO_FORMAT_UNKNOWN;
GstMpegVideoSequenceHdr seq;
GstMpegVideoProfile profile = GST_MPEG_VIDEO_PROFILE_MAIN;
gboolean interlaced = FALSE;
gboolean configured = FALSE;
};
/* *INDENT-ON* */
static void gst_dxva_mpeg2_decoder_finalize (GObject * object);
static gboolean gst_dxva_mpeg2_decoder_start (GstVideoDecoder * decoder);
static GstFlowReturn gst_dxva_mpeg2_decoder_new_sequence (GstMpeg2Decoder *
decoder, const GstMpegVideoSequenceHdr * seq,
const GstMpegVideoSequenceExt * seq_ext,
const GstMpegVideoSequenceDisplayExt * seq_display_ext,
const GstMpegVideoSequenceScalableExt * seq_scalable_ext,
gint max_dpb_size);
static GstFlowReturn
gst_dxva_mpeg2_decoder_new_picture (GstMpeg2Decoder * decoder,
GstVideoCodecFrame * frame, GstMpeg2Picture * picture);
static GstFlowReturn
gst_dxva_mpeg2_decoder_new_field_picture (GstMpeg2Decoder * decoder,
GstMpeg2Picture * first_field, GstMpeg2Picture * second_field);
static GstFlowReturn
gst_dxva_mpeg2_decoder_start_picture (GstMpeg2Decoder * decoder,
GstMpeg2Picture * picture, GstMpeg2Slice * slice,
GstMpeg2Picture * prev_picture, GstMpeg2Picture * next_picture);
static GstFlowReturn
gst_dxva_mpeg2_decoder_decode_slice (GstMpeg2Decoder * decoder,
GstMpeg2Picture * picture, GstMpeg2Slice * slice);
static GstFlowReturn
gst_dxva_mpeg2_decoder_end_picture (GstMpeg2Decoder * decoder,
GstMpeg2Picture * picture);
static GstFlowReturn
gst_dxva_mpeg2_decoder_output_picture (GstMpeg2Decoder * decoder,
GstVideoCodecFrame * frame, GstMpeg2Picture * picture);
#define gst_dxva_mpeg2_decoder_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstDxvaMpeg2Decoder,
gst_dxva_mpeg2_decoder, GST_TYPE_MPEG2_DECODER,
GST_DEBUG_CATEGORY_INIT (gst_dxva_mpeg2_decoder_debug, "dxvampeg2decoder",
0, "dxvampeg2decoder"));
static void
gst_dxva_mpeg2_decoder_class_init (GstDxvaMpeg2DecoderClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
GstMpeg2DecoderClass *mpeg2decoder_class = GST_MPEG2_DECODER_CLASS (klass);
gobject_class->finalize = gst_dxva_mpeg2_decoder_finalize;
decoder_class->start = GST_DEBUG_FUNCPTR (gst_dxva_mpeg2_decoder_start);
mpeg2decoder_class->new_sequence =
GST_DEBUG_FUNCPTR (gst_dxva_mpeg2_decoder_new_sequence);
mpeg2decoder_class->new_picture =
GST_DEBUG_FUNCPTR (gst_dxva_mpeg2_decoder_new_picture);
mpeg2decoder_class->new_field_picture =
GST_DEBUG_FUNCPTR (gst_dxva_mpeg2_decoder_new_field_picture);
mpeg2decoder_class->start_picture =
GST_DEBUG_FUNCPTR (gst_dxva_mpeg2_decoder_start_picture);
mpeg2decoder_class->decode_slice =
GST_DEBUG_FUNCPTR (gst_dxva_mpeg2_decoder_decode_slice);
mpeg2decoder_class->end_picture =
GST_DEBUG_FUNCPTR (gst_dxva_mpeg2_decoder_end_picture);
mpeg2decoder_class->output_picture =
GST_DEBUG_FUNCPTR (gst_dxva_mpeg2_decoder_output_picture);
}
static void
gst_dxva_mpeg2_decoder_init (GstDxvaMpeg2Decoder * self)
{
self->priv = new GstDxvaMpeg2DecoderPrivate ();
self->priv->ref_pics = g_ptr_array_new ();
}
static void
gst_dxva_mpeg2_decoder_finalize (GObject * object)
{
GstDxvaMpeg2Decoder *self = GST_DXVA_MPEG2_DECODER (object);
GstDxvaMpeg2DecoderPrivate *priv = self->priv;
g_ptr_array_unref (priv->ref_pics);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_dxva_mpeg2_decoder_reset (GstDxvaMpeg2Decoder * self)
{
GstDxvaMpeg2DecoderPrivate *priv = self->priv;
priv->width = 0;
priv->height = 0;
priv->width_in_mb = 0;
priv->height_in_mb = 0;
priv->out_format = GST_VIDEO_FORMAT_UNKNOWN;
priv->profile = GST_MPEG_VIDEO_PROFILE_MAIN;
priv->interlaced = FALSE;
priv->configured = FALSE;
}
static gboolean
gst_dxva_mpeg2_decoder_start (GstVideoDecoder * decoder)
{
GstDxvaMpeg2Decoder *self = GST_DXVA_MPEG2_DECODER (decoder);
gst_dxva_mpeg2_decoder_reset (self);
return GST_VIDEO_DECODER_CLASS (parent_class)->start (decoder);
}
static GstFlowReturn
gst_dxva_mpeg2_decoder_new_sequence (GstMpeg2Decoder * decoder,
const GstMpegVideoSequenceHdr * seq,
const GstMpegVideoSequenceExt * seq_ext,
const GstMpegVideoSequenceDisplayExt * seq_display_ext,
const GstMpegVideoSequenceScalableExt * seq_scalable_ext, gint max_dpb_size)
{
GstDxvaMpeg2Decoder *self = GST_DXVA_MPEG2_DECODER (decoder);
GstDxvaMpeg2DecoderPrivate *priv = self->priv;
GstDxvaMpeg2DecoderClass *klass = GST_DXVA_MPEG2_DECODER_GET_CLASS (self);
gboolean interlaced;
gboolean modified = FALSE;
gint width, height;
GstMpegVideoProfile mpeg_profile;
GstVideoInfo info;
GstFlowReturn ret;
GST_LOG_OBJECT (self, "new sequence");
interlaced = seq_ext ? !seq_ext->progressive : FALSE;
if (priv->interlaced != interlaced) {
GST_INFO_OBJECT (self, "interlaced sequence change, %d -> %d",
priv->interlaced, interlaced);
priv->interlaced = interlaced;
modified = TRUE;
}
width = seq->width;
height = seq->height;
if (seq_ext) {
width = (width & 0x0fff) | ((guint32) seq_ext->horiz_size_ext << 12);
height = (height & 0x0fff) | ((guint32) seq_ext->vert_size_ext << 12);
}
if (priv->width != width || priv->height != height) {
GST_INFO_OBJECT (self, "resolution change %dx%d -> %dx%d",
priv->width, priv->height, width, height);
priv->width = width;
priv->height = height;
priv->width_in_mb = GST_ROUND_UP_16 (width) >> 4;
priv->height_in_mb = GST_ROUND_UP_16 (height) >> 4;
modified = TRUE;
}
mpeg_profile = GST_MPEG_VIDEO_PROFILE_MAIN;
if (seq_ext)
mpeg_profile = (GstMpegVideoProfile) seq_ext->profile;
if (mpeg_profile != GST_MPEG_VIDEO_PROFILE_MAIN &&
mpeg_profile != GST_MPEG_VIDEO_PROFILE_SIMPLE) {
GST_ERROR_OBJECT (self, "Cannot support profile %d", mpeg_profile);
return GST_FLOW_NOT_NEGOTIATED;
}
if (priv->profile != mpeg_profile) {
GST_INFO_OBJECT (self, "Profile change %d -> %d",
priv->profile, mpeg_profile);
priv->profile = mpeg_profile;
modified = TRUE;
}
if (!modified && priv->configured)
return GST_FLOW_OK;
priv->out_format = GST_VIDEO_FORMAT_NV12;
gst_video_info_set_interlaced_format (&info,
priv->out_format, priv->interlaced ? GST_VIDEO_INTERLACE_MODE_MIXED :
GST_VIDEO_INTERLACE_MODE_PROGRESSIVE, priv->width, priv->height);
g_assert (klass->configure);
ret = klass->configure (self, decoder->input_state, &info, 0, 0, priv->width,
priv->height, max_dpb_size);
if (ret == GST_FLOW_OK) {
priv->configured = TRUE;
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
GST_WARNING_OBJECT (self, "Couldn't negotiate with new sequence");
ret = GST_FLOW_NOT_NEGOTIATED;
}
} else {
priv->configured = FALSE;
}
return ret;
}
static GstFlowReturn
gst_dxva_mpeg2_decoder_new_picture (GstMpeg2Decoder * decoder,
GstVideoCodecFrame * frame, GstMpeg2Picture * picture)
{
GstDxvaMpeg2Decoder *self = GST_DXVA_MPEG2_DECODER (decoder);
GstDxvaMpeg2DecoderClass *klass = GST_DXVA_MPEG2_DECODER_GET_CLASS (self);
g_assert (klass->new_picture);
return klass->new_picture (self, GST_CODEC_PICTURE (picture));
}
static GstFlowReturn
gst_dxva_mpeg2_decoder_new_field_picture (GstMpeg2Decoder * decoder,
GstMpeg2Picture * first_field, GstMpeg2Picture * second_field)
{
GstDxvaMpeg2Decoder *self = GST_DXVA_MPEG2_DECODER (decoder);
GstDxvaMpeg2DecoderClass *klass = GST_DXVA_MPEG2_DECODER_GET_CLASS (self);
g_assert (klass->duplicate_picture);
return klass->duplicate_picture (self, GST_CODEC_PICTURE (first_field),
GST_CODEC_PICTURE (second_field));
}
static inline WORD
_pack_f_codes (guint8 f_code[2][2])
{
return (((WORD) f_code[0][0] << 12)
| ((WORD) f_code[0][1] << 8)
| ((WORD) f_code[1][0] << 4)
| (f_code[1][1]));
}
static inline WORD
_pack_pce_elements (GstMpeg2Slice * slice)
{
return (((WORD) slice->pic_ext->intra_dc_precision << 14)
| ((WORD) slice->pic_ext->picture_structure << 12)
| ((WORD) slice->pic_ext->top_field_first << 11)
| ((WORD) slice->pic_ext->frame_pred_frame_dct << 10)
| ((WORD) slice->pic_ext->concealment_motion_vectors << 9)
| ((WORD) slice->pic_ext->q_scale_type << 8)
| ((WORD) slice->pic_ext->intra_vlc_format << 7)
| ((WORD) slice->pic_ext->alternate_scan << 6)
| ((WORD) slice->pic_ext->repeat_first_field << 5)
| ((WORD) slice->pic_ext->chroma_420_type << 4)
| ((WORD) slice->pic_ext->progressive_frame << 3));
}
static GstFlowReturn
gst_dxva_mpeg2_decoder_start_picture (GstMpeg2Decoder * decoder,
GstMpeg2Picture * picture, GstMpeg2Slice * slice,
GstMpeg2Picture * prev_picture, GstMpeg2Picture * next_picture)
{
GstDxvaMpeg2Decoder *self = GST_DXVA_MPEG2_DECODER (decoder);
GstDxvaMpeg2DecoderPrivate *priv = self->priv;
GstDxvaMpeg2DecoderClass *klass = GST_DXVA_MPEG2_DECODER_GET_CLASS (self);
DXVA_PictureParameters *pic_params = &priv->pic_params;
DXVA_QmatrixData *iq_matrix = &priv->iq_matrix;
GstCodecPicture *codec_picture = GST_CODEC_PICTURE (picture);
gboolean is_field =
picture->structure != GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME;
GstFlowReturn ret;
guint8 picture_id;
g_assert (klass->start_picture);
g_assert (klass->get_picture_id);
ret = klass->start_picture (self, codec_picture, &picture_id);
if (ret != GST_FLOW_OK)
return ret;
priv->slice_list.resize (0);
priv->bitstream_buffer.resize (0);
g_ptr_array_set_size (priv->ref_pics, 0);
memset (pic_params, 0, sizeof (DXVA_PictureParameters));
memset (iq_matrix, 0, sizeof (DXVA_QmatrixData));
/* Fill DXVA_PictureParameters */
pic_params->wDecodedPictureIndex = picture_id;
pic_params->wForwardRefPictureIndex = 0xffff;
pic_params->wBackwardRefPictureIndex = 0xffff;
switch (picture->type) {
case GST_MPEG_VIDEO_PICTURE_TYPE_B:
{
if (next_picture) {
picture_id = klass->get_picture_id (self,
GST_CODEC_PICTURE (next_picture));
if (picture_id != 0xff) {
pic_params->wBackwardRefPictureIndex = picture_id;
g_ptr_array_add (priv->ref_pics, next_picture);
}
}
}
/* fall-through */
case GST_MPEG_VIDEO_PICTURE_TYPE_P:
{
if (prev_picture) {
picture_id = klass->get_picture_id (self,
GST_CODEC_PICTURE (prev_picture));
if (picture_id != 0xff) {
pic_params->wForwardRefPictureIndex = picture_id;
g_ptr_array_add (priv->ref_pics, prev_picture);
}
}
}
default:
break;
}
pic_params->wPicWidthInMBminus1 = priv->width_in_mb - 1;
pic_params->wPicHeightInMBminus1 = (priv->height_in_mb >> is_field) - 1;
pic_params->bMacroblockWidthMinus1 = 15;
pic_params->bMacroblockHeightMinus1 = 15;
pic_params->bBlockWidthMinus1 = 7;
pic_params->bBlockHeightMinus1 = 7;
pic_params->bBPPminus1 = 7;
pic_params->bPicStructure = (BYTE) picture->structure;
if (picture->first_field && is_field) {
pic_params->bSecondField = TRUE;
}
pic_params->bPicIntra = picture->type == GST_MPEG_VIDEO_PICTURE_TYPE_I;
pic_params->bPicBackwardPrediction =
picture->type == GST_MPEG_VIDEO_PICTURE_TYPE_B;
/* FIXME: 1 -> 4:2:0, 2 -> 4:2:2, 3 -> 4:4:4 */
pic_params->bChromaFormat = 1;
pic_params->bPicScanFixed = 1;
pic_params->bPicScanMethod = slice->pic_ext->alternate_scan;
pic_params->wBitstreamFcodes = _pack_f_codes (slice->pic_ext->f_code);
pic_params->wBitstreamPCEelements = _pack_pce_elements (slice);
/* Fill DXVA_QmatrixData */
if (slice->quant_matrix &&
/* The value in bNewQmatrix[0] and bNewQmatrix[1] must not both be zero.
* https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/dxva/ns-dxva-_dxva_qmatrixdata
*/
(slice->quant_matrix->load_intra_quantiser_matrix ||
slice->quant_matrix->load_non_intra_quantiser_matrix)) {
GstMpegVideoQuantMatrixExt *quant_matrix = slice->quant_matrix;
if (quant_matrix->load_intra_quantiser_matrix) {
iq_matrix->bNewQmatrix[0] = 1;
for (guint i = 0; i < 64; i++) {
iq_matrix->Qmatrix[0][i] = quant_matrix->intra_quantiser_matrix[i];
}
}
if (quant_matrix->load_non_intra_quantiser_matrix) {
iq_matrix->bNewQmatrix[1] = 1;
for (guint i = 0; i < 64; i++) {
iq_matrix->Qmatrix[1][i] = quant_matrix->non_intra_quantiser_matrix[i];
}
}
if (quant_matrix->load_chroma_intra_quantiser_matrix) {
iq_matrix->bNewQmatrix[2] = 1;
for (guint i = 0; i < 64; i++) {
iq_matrix->Qmatrix[2][i] =
quant_matrix->chroma_intra_quantiser_matrix[i];
}
}
if (quant_matrix->load_chroma_non_intra_quantiser_matrix) {
iq_matrix->bNewQmatrix[3] = 1;
for (guint i = 0; i < 64; i++) {
iq_matrix->Qmatrix[3][i] =
quant_matrix->chroma_non_intra_quantiser_matrix[i];
}
}
priv->submit_iq_data = TRUE;
} else {
priv->submit_iq_data = FALSE;
}
return GST_FLOW_OK;
}
static GstFlowReturn
gst_dxva_mpeg2_decoder_decode_slice (GstMpeg2Decoder * decoder,
GstMpeg2Picture * picture, GstMpeg2Slice * slice)
{
GstDxvaMpeg2Decoder *self = GST_DXVA_MPEG2_DECODER (decoder);
GstDxvaMpeg2DecoderPrivate *priv = self->priv;
GstMpegVideoSliceHdr *header = &slice->header;
GstMpegVideoPacket *packet = &slice->packet;
DXVA_SliceInfo slice_info = { 0, };
g_assert (packet->offset >= 4);
slice_info.wHorizontalPosition = header->mb_column;
slice_info.wVerticalPosition = header->mb_row;
/* including start code 4 bytes */
slice_info.dwSliceBitsInBuffer = 8 * (packet->size + 4);
slice_info.dwSliceDataLocation = priv->bitstream_buffer.size ();
/* XXX: We don't have information about the number of MBs in this slice.
* Just store offset here, and actual number will be calculated later */
slice_info.wNumberMBsInSlice =
(header->mb_row * priv->width_in_mb) + header->mb_column;
slice_info.wQuantizerScaleCode = header->quantiser_scale_code;
slice_info.wMBbitOffset = header->header_size + 32;
priv->slice_list.push_back (slice_info);
size_t pos = priv->bitstream_buffer.size ();
priv->bitstream_buffer.resize (pos + packet->size + 4);
memcpy (&priv->bitstream_buffer[0] + pos, packet->data + packet->offset - 4,
packet->size + 4);
return GST_FLOW_OK;
}
static GstFlowReturn
gst_dxva_mpeg2_decoder_end_picture (GstMpeg2Decoder * decoder,
GstMpeg2Picture * picture)
{
GstDxvaMpeg2Decoder *self = GST_DXVA_MPEG2_DECODER (decoder);
GstDxvaMpeg2DecoderPrivate *priv = self->priv;
GstDxvaMpeg2DecoderClass *klass = GST_DXVA_MPEG2_DECODER_GET_CLASS (self);
GstDxvaDecodingArgs args;
gboolean is_field =
picture->structure != GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME;
guint mb_count = priv->width_in_mb * (priv->height_in_mb >> is_field);
if (priv->bitstream_buffer.empty ()) {
GST_ERROR_OBJECT (self, "No bitstream buffer to submit");
return GST_FLOW_ERROR;
}
memset (&args, 0, sizeof (GstDxvaDecodingArgs));
DXVA_SliceInfo *first = &priv->slice_list[0];
for (size_t i = 0; i < priv->slice_list.size (); i++) {
DXVA_SliceInfo *slice = first + i;
/* Update the number of MBs per slice */
if (i == priv->slice_list.size () - 1) {
slice->wNumberMBsInSlice = mb_count - slice->wNumberMBsInSlice;
} else {
DXVA_SliceInfo *next = first + i + 1;
slice->wNumberMBsInSlice =
next->wNumberMBsInSlice - slice->wNumberMBsInSlice;
}
}
args.picture_params = &priv->pic_params;
args.picture_params_size = sizeof (DXVA_PictureParameters);
args.slice_control = &priv->slice_list[0];
args.slice_control_size = sizeof (DXVA_SliceInfo) * priv->slice_list.size ();
args.bitstream = &priv->bitstream_buffer[0];
args.bitstream_size = priv->bitstream_buffer.size ();
if (priv->submit_iq_data) {
args.inverse_quantization_matrix = &priv->iq_matrix;
args.inverse_quantization_matrix_size = sizeof (DXVA_QmatrixData);
}
g_assert (klass->end_picture);
return klass->end_picture (self, GST_CODEC_PICTURE (picture),
priv->ref_pics, &args);
}
static GstFlowReturn
gst_dxva_mpeg2_decoder_output_picture (GstMpeg2Decoder * decoder,
GstVideoCodecFrame * frame, GstMpeg2Picture * picture)
{
GstDxvaMpeg2Decoder *self = GST_DXVA_MPEG2_DECODER (decoder);
GstDxvaMpeg2DecoderPrivate *priv = self->priv;
GstDxvaMpeg2DecoderClass *klass = GST_DXVA_MPEG2_DECODER_GET_CLASS (self);
g_assert (klass->output_picture);
GST_LOG_OBJECT (self, "Outputting picture %p", picture);
return klass->output_picture (self, frame, GST_CODEC_PICTURE (picture),
picture->buffer_flags, priv->width, priv->height);
}

View file

@ -0,0 +1,94 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/dxva/dxva-prelude.h>
#include <gst/dxva/gstdxvatypes.h>
#include <gst/codecs/gstmpeg2decoder.h>
G_BEGIN_DECLS
#define GST_TYPE_DXVA_MPEG2_DECODER (gst_dxva_mpeg2_decoder_get_type())
#define GST_DXVA_MPEG2_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DXVA_MPEG2_DECODER,GstDxvaMpeg2Decoder))
#define GST_DXVA_MPEG2_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DXVA_MPEG2_DECODER,GstDxvaMpeg2DecoderClass))
#define GST_DXVA_MPEG2_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_DXVA_MPEG2_DECODER,GstDxvaMpeg2DecoderClass))
#define GST_IS_DXVA_MPEG2_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DXVA_MPEG2_DECODER))
#define GST_IS_DXVA_MPEG2_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DXVA_MPEG2_DECODER))
#define GST_DXVA_MPEG2_DECODER_CAST(obj) ((GstDxvaMpeg2Decoder*)obj)
typedef struct _GstDxvaMpeg2Decoder GstDxvaMpeg2Decoder;
typedef struct _GstDxvaMpeg2DecoderClass GstDxvaMpeg2DecoderClass;
typedef struct _GstDxvaMpeg2DecoderPrivate GstDxvaMpeg2DecoderPrivate;
struct _GstDxvaMpeg2Decoder
{
GstMpeg2Decoder parent;
/*< private >*/
GstDxvaMpeg2DecoderPrivate *priv;
};
struct _GstDxvaMpeg2DecoderClass
{
GstMpeg2DecoderClass parent_class;
GstFlowReturn (*configure) (GstDxvaMpeg2Decoder * decoder,
GstVideoCodecState * input_state,
const GstVideoInfo * info,
gint crop_x,
gint crop_y,
gint coded_width,
gint coded_height,
gint max_dpb_size);
GstFlowReturn (*new_picture) (GstDxvaMpeg2Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*duplicate_picture) (GstDxvaMpeg2Decoder * decoder,
GstCodecPicture * src,
GstCodecPicture * dst);
guint8 (*get_picture_id) (GstDxvaMpeg2Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*start_picture) (GstDxvaMpeg2Decoder * decoder,
GstCodecPicture * picture,
guint8 * picture_id);
GstFlowReturn (*end_picture) (GstDxvaMpeg2Decoder * decoder,
GstCodecPicture * picture,
GPtrArray * ref_pics,
const GstDxvaDecodingArgs * args);
GstFlowReturn (*output_picture) (GstDxvaMpeg2Decoder * decoder,
GstVideoCodecFrame * frame,
GstCodecPicture * picture,
GstVideoBufferFlags buffer_flags,
gint display_width,
gint display_height);
};
GST_DXVA_API
GType gst_dxva_mpeg2_decoder_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDxvaMpeg2Decoder, gst_object_unref)
G_END_DECLS

View file

@ -0,0 +1,86 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/gst.h>
#include <gst/dxva/dxva-prelude.h>
G_BEGIN_DECLS
typedef struct _GstDxvaDecodingArgs GstDxvaDecodingArgs;
typedef struct _GstDxvaResolution GstDxvaResolution;
/**
* GstDxvaDecodingArgs:
*
* Since: 1.24
*/
struct _GstDxvaDecodingArgs
{
gpointer picture_params;
gsize picture_params_size;
gpointer slice_control;
gsize slice_control_size;
gpointer bitstream;
gsize bitstream_size;
gpointer inverse_quantization_matrix;
gsize inverse_quantization_matrix_size;
};
/**
* GstDxvaCodec:
*
* Since: 1.24
*/
typedef enum
{
GST_DXVA_CODEC_NONE,
GST_DXVA_CODEC_MPEG2,
GST_DXVA_CODEC_H264,
GST_DXVA_CODEC_H265,
GST_DXVA_CODEC_VP8,
GST_DXVA_CODEC_VP9,
GST_DXVA_CODEC_AV1,
/* the last of supported codec */
GST_DXVA_CODEC_LAST
} GstDxvaCodec;
/**
* GstDxvaResolution:
*
* Since: 1.24
*/
struct _GstDxvaResolution
{
guint width;
guint height;
};
static const GstDxvaResolution gst_dxva_resolutions[] = {
{1920, 1088}, {2560, 1440}, {3840, 2160}, {4096, 2160},
{7680, 4320}, {8192, 4320}, {15360, 8640}, {16384, 8640}
};
G_END_DECLS

View file

@ -0,0 +1,58 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdxvautils.h"
/**
* gst_dxva_codec_to_string:
* @codec: a #GstDxvaCodec
*
* Returns: the string representation of @codec
*
* Since: 1.24
*/
const gchar *
gst_dxva_codec_to_string (GstDxvaCodec codec)
{
switch (codec) {
case GST_DXVA_CODEC_NONE:
return "none";
case GST_DXVA_CODEC_H264:
return "H.264";
case GST_DXVA_CODEC_VP9:
return "VP9";
case GST_DXVA_CODEC_H265:
return "H.265";
case GST_DXVA_CODEC_VP8:
return "VP8";
case GST_DXVA_CODEC_MPEG2:
return "MPEG2";
case GST_DXVA_CODEC_AV1:
return "AV1";
default:
g_assert_not_reached ();
break;
}
return "Unknown";
}

View file

@ -0,0 +1,31 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/dxva/dxva-prelude.h>
#include <gst/dxva/gstdxvatypes.h>
G_BEGIN_DECLS
GST_DXVA_API
const gchar * gst_dxva_codec_to_string (GstDxvaCodec codec);
G_END_DECLS

View file

@ -0,0 +1,413 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdxvavp8decoder.h"
#include <string.h>
#include <vector>
/* HACK: to expose dxva data structure on UWP */
#ifdef WINAPI_PARTITION_DESKTOP
#undef WINAPI_PARTITION_DESKTOP
#endif
#define WINAPI_PARTITION_DESKTOP 1
#include <d3d9.h>
#include <dxva.h>
GST_DEBUG_CATEGORY_STATIC (gst_dxva_vp8_decoder_debug);
#define GST_CAT_DEFAULT gst_dxva_vp8_decoder_debug
/* *INDENT-OFF* */
struct _GstDxvaVp8DecoderPrivate
{
DXVA_PicParams_VP8 pic_params;
DXVA_Slice_VPx_Short slice;
std::vector<guint8> bitstream_buffer;
GPtrArray *ref_pics = nullptr;
gint width = 0;
gint height = 0;
};
/* *INDENT-ON* */
static void gst_dxva_vp8_decoder_finalize (GObject * object);
static gboolean gst_dxva_vp8_decoder_start (GstVideoDecoder * decoder);
static GstFlowReturn gst_dxva_vp8_decoder_new_sequence (GstVp8Decoder * decoder,
const GstVp8FrameHdr * frame_hdr, gint max_dpb_size);
static GstFlowReturn gst_dxva_vp8_decoder_new_picture (GstVp8Decoder * decoder,
GstVideoCodecFrame * frame, GstVp8Picture * picture);
static GstFlowReturn
gst_dxva_vp8_decoder_decode_picture (GstVp8Decoder * decoder,
GstVp8Picture * picture, GstVp8Parser * parser);
static GstFlowReturn gst_dxva_vp8_decoder_end_picture (GstVp8Decoder * decoder,
GstVp8Picture * picture);
static GstFlowReturn gst_dxva_vp8_decoder_output_picture (GstVp8Decoder *
decoder, GstVideoCodecFrame * frame, GstVp8Picture * picture);
#define gst_dxva_vp8_decoder_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstDxvaVp8Decoder,
gst_dxva_vp8_decoder, GST_TYPE_VP8_DECODER,
GST_DEBUG_CATEGORY_INIT (gst_dxva_vp8_decoder_debug, "dxvavp8decoder",
0, "dxvavp8decoder"));
static void
gst_dxva_vp8_decoder_class_init (GstDxvaVp8DecoderClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
GstVp8DecoderClass *vp8decoder_class = GST_VP8_DECODER_CLASS (klass);
object_class->finalize = gst_dxva_vp8_decoder_finalize;
decoder_class->start = GST_DEBUG_FUNCPTR (gst_dxva_vp8_decoder_start);
vp8decoder_class->new_sequence =
GST_DEBUG_FUNCPTR (gst_dxva_vp8_decoder_new_sequence);
vp8decoder_class->new_picture =
GST_DEBUG_FUNCPTR (gst_dxva_vp8_decoder_new_picture);
vp8decoder_class->decode_picture =
GST_DEBUG_FUNCPTR (gst_dxva_vp8_decoder_decode_picture);
vp8decoder_class->end_picture =
GST_DEBUG_FUNCPTR (gst_dxva_vp8_decoder_end_picture);
vp8decoder_class->output_picture =
GST_DEBUG_FUNCPTR (gst_dxva_vp8_decoder_output_picture);
}
static void
gst_dxva_vp8_decoder_init (GstDxvaVp8Decoder * self)
{
self->priv = new GstDxvaVp8DecoderPrivate ();
self->priv->ref_pics = g_ptr_array_new ();
}
static void
gst_dxva_vp8_decoder_finalize (GObject * object)
{
GstDxvaVp8Decoder *self = GST_DXVA_VP8_DECODER (object);
GstDxvaVp8DecoderPrivate *priv = self->priv;
g_ptr_array_unref (priv->ref_pics);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_dxva_vp8_decoder_reset (GstDxvaVp8Decoder * self)
{
GstDxvaVp8DecoderPrivate *priv = self->priv;
priv->width = 0;
priv->height = 0;
}
static gboolean
gst_dxva_vp8_decoder_start (GstVideoDecoder * decoder)
{
GstDxvaVp8Decoder *self = GST_DXVA_VP8_DECODER (decoder);
gst_dxva_vp8_decoder_reset (self);
return GST_VIDEO_DECODER_CLASS (parent_class)->start (decoder);
}
static GstFlowReturn
gst_dxva_vp8_decoder_new_sequence (GstVp8Decoder * decoder,
const GstVp8FrameHdr * frame_hdr, gint max_dpb_size)
{
GstDxvaVp8Decoder *self = GST_DXVA_VP8_DECODER (decoder);
GstDxvaVp8DecoderPrivate *priv = self->priv;
GstDxvaVp8DecoderClass *klass = GST_DXVA_VP8_DECODER_GET_CLASS (self);
GstVideoInfo info;
GstFlowReturn ret;
GST_LOG_OBJECT (self, "new sequence");
priv->width = frame_hdr->width;
priv->height = frame_hdr->height;
gst_video_info_set_format (&info,
GST_VIDEO_FORMAT_NV12, frame_hdr->width, frame_hdr->height);
g_assert (klass->configure);
ret = klass->configure (self, decoder->input_state, &info, 0, 0,
frame_hdr->width, frame_hdr->height, max_dpb_size);
if (ret == GST_FLOW_OK &&
!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
GST_WARNING_OBJECT (self, "Couldn't negotiate with new sequence");
ret = GST_FLOW_NOT_NEGOTIATED;
}
return ret;
}
static GstFlowReturn
gst_dxva_vp8_decoder_new_picture (GstVp8Decoder * decoder,
GstVideoCodecFrame * frame, GstVp8Picture * picture)
{
GstDxvaVp8Decoder *self = GST_DXVA_VP8_DECODER (decoder);
GstDxvaVp8DecoderClass *klass = GST_DXVA_VP8_DECODER_GET_CLASS (self);
g_assert (klass->new_picture);
return klass->new_picture (self, GST_CODEC_PICTURE (picture));
}
static void
gst_dxva_vp8_decoder_copy_frame_params (GstDxvaVp8Decoder * self,
GstVp8Picture * picture, GstVp8Parser * parser, DXVA_PicParams_VP8 * params)
{
const GstVp8FrameHdr *frame_hdr = &picture->frame_hdr;
gint i;
/* 0: keyframe, 1: inter */
params->frame_type = !frame_hdr->key_frame;
params->version = frame_hdr->version;
params->show_frame = frame_hdr->show_frame;
params->clamp_type = frame_hdr->clamping_type;
params->filter_type = frame_hdr->filter_type;
params->filter_level = frame_hdr->loop_filter_level;
params->sharpness_level = frame_hdr->sharpness_level;
params->mode_ref_lf_delta_enabled =
parser->mb_lf_adjust.loop_filter_adj_enable;
params->mode_ref_lf_delta_update =
parser->mb_lf_adjust.mode_ref_lf_delta_update;
for (i = 0; i < 4; i++) {
params->ref_lf_deltas[i] = parser->mb_lf_adjust.ref_frame_delta[i];
params->mode_lf_deltas[i] = parser->mb_lf_adjust.mb_mode_delta[i];
}
params->log2_nbr_of_dct_partitions = frame_hdr->log2_nbr_of_dct_partitions;
params->base_qindex = frame_hdr->quant_indices.y_ac_qi;
params->y1dc_delta_q = frame_hdr->quant_indices.y_dc_delta;
params->y2dc_delta_q = frame_hdr->quant_indices.y2_dc_delta;
params->y2ac_delta_q = frame_hdr->quant_indices.y2_ac_delta;
params->uvdc_delta_q = frame_hdr->quant_indices.uv_dc_delta;
params->uvac_delta_q = frame_hdr->quant_indices.uv_ac_delta;
params->ref_frame_sign_bias_golden = frame_hdr->sign_bias_golden;
params->ref_frame_sign_bias_altref = frame_hdr->sign_bias_alternate;
params->refresh_entropy_probs = frame_hdr->refresh_entropy_probs;
memcpy (params->vp8_coef_update_probs, frame_hdr->token_probs.prob,
sizeof (frame_hdr->token_probs.prob));
params->mb_no_coeff_skip = frame_hdr->mb_no_skip_coeff;
params->prob_skip_false = frame_hdr->prob_skip_false;
params->prob_intra = frame_hdr->prob_intra;
params->prob_last = frame_hdr->prob_last;
params->prob_golden = frame_hdr->prob_gf;
memcpy (params->intra_16x16_prob, frame_hdr->mode_probs.y_prob,
sizeof (frame_hdr->mode_probs.y_prob));
memcpy (params->intra_chroma_prob, frame_hdr->mode_probs.uv_prob,
sizeof (frame_hdr->mode_probs.uv_prob));
memcpy (params->vp8_mv_update_probs, frame_hdr->mv_probs.prob,
sizeof (frame_hdr->mv_probs.prob));
}
static gboolean
gst_dxva_vp8_decoder_copy_reference_frames (GstDxvaVp8Decoder * self,
DXVA_PicParams_VP8 * params)
{
GstVp8Decoder *decoder = GST_VP8_DECODER (self);
GstDxvaVp8DecoderPrivate *priv = self->priv;
GstDxvaVp8DecoderClass *klass = GST_DXVA_VP8_DECODER_GET_CLASS (self);
guint8 id;
params->alt_fb_idx.bPicEntry = 0xff;
if (decoder->alt_ref_picture) {
id = klass->get_picture_id (self,
GST_CODEC_PICTURE (decoder->alt_ref_picture));
if (id != 0xff) {
params->alt_fb_idx.Index7Bits = id;
g_ptr_array_add (priv->ref_pics, decoder->alt_ref_picture);
}
}
params->gld_fb_idx.bPicEntry = 0xff;
if (decoder->golden_ref_picture) {
id = klass->get_picture_id (self,
GST_CODEC_PICTURE (decoder->golden_ref_picture));
if (id != 0xff) {
params->gld_fb_idx.Index7Bits = id;
g_ptr_array_add (priv->ref_pics, decoder->golden_ref_picture);
}
}
params->lst_fb_idx.bPicEntry = 0xff;
if (decoder->last_picture) {
id = klass->get_picture_id (self,
GST_CODEC_PICTURE (decoder->last_picture));
if (id != 0xff) {
params->gld_fb_idx.Index7Bits = id;
g_ptr_array_add (priv->ref_pics, decoder->last_picture);
}
}
return TRUE;
}
static void
gst_dxva_vp8_decoder_copy_segmentation_params (GstDxvaVp8Decoder * self,
GstVp8Parser * parser, DXVA_PicParams_VP8 * params)
{
const GstVp8Segmentation *seg = &parser->segmentation;
gint i;
params->stVP8Segments.segmentation_enabled = seg->segmentation_enabled;
params->stVP8Segments.update_mb_segmentation_map =
seg->update_mb_segmentation_map;
params->stVP8Segments.update_mb_segmentation_data =
seg->update_segment_feature_data;
params->stVP8Segments.mb_segement_abs_delta = seg->segment_feature_mode;
for (i = 0; i < 4; i++) {
params->stVP8Segments.segment_feature_data[0][i] =
seg->quantizer_update_value[i];
}
for (i = 0; i < 4; i++) {
params->stVP8Segments.segment_feature_data[1][i] = seg->lf_update_value[i];
}
for (i = 0; i < 3; i++) {
params->stVP8Segments.mb_segment_tree_probs[i] = seg->segment_prob[i];
}
}
static GstFlowReturn
gst_dxva_vp8_decoder_decode_picture (GstVp8Decoder * decoder,
GstVp8Picture * picture, GstVp8Parser * parser)
{
GstDxvaVp8Decoder *self = GST_DXVA_VP8_DECODER (decoder);
GstDxvaVp8DecoderPrivate *priv = self->priv;
GstDxvaVp8DecoderClass *klass = GST_DXVA_VP8_DECODER_GET_CLASS (self);
DXVA_PicParams_VP8 *pic_params = &priv->pic_params;
DXVA_Slice_VPx_Short *slice = &priv->slice;
const GstVp8FrameHdr *frame_hdr = &picture->frame_hdr;
GstCodecPicture *codec_picture = GST_CODEC_PICTURE (picture);
GstFlowReturn ret;
guint8 picture_id;
g_assert (klass->start_picture);
g_assert (klass->get_picture_id);
ret = klass->start_picture (self, codec_picture, &picture_id);
if (ret != GST_FLOW_OK)
return ret;
priv->bitstream_buffer.resize (0);
g_ptr_array_set_size (priv->ref_pics, 0);
memset (pic_params, 0, sizeof (DXVA_PicParams_VP8));
pic_params->first_part_size = frame_hdr->first_part_size;
pic_params->width = priv->width;
pic_params->height = priv->height;
pic_params->CurrPic.Index7Bits = picture_id;
pic_params->StatusReportFeedbackNumber = 1;
if (!gst_dxva_vp8_decoder_copy_reference_frames (self, pic_params))
return GST_FLOW_ERROR;
gst_dxva_vp8_decoder_copy_frame_params (self, picture, parser, pic_params);
gst_dxva_vp8_decoder_copy_segmentation_params (self, parser, pic_params);
priv->bitstream_buffer.resize (picture->size);
memcpy (&priv->bitstream_buffer[0], picture->data, picture->size);
slice->BSNALunitDataLocation = 0;
slice->SliceBytesInBuffer = priv->bitstream_buffer.size ();
slice->wBadSliceChopping = 0;
return GST_FLOW_OK;
}
static GstFlowReturn
gst_dxva_vp8_decoder_end_picture (GstVp8Decoder * decoder,
GstVp8Picture * picture)
{
GstDxvaVp8Decoder *self = GST_DXVA_VP8_DECODER (decoder);
GstDxvaVp8DecoderPrivate *priv = self->priv;
GstDxvaVp8DecoderClass *klass = GST_DXVA_VP8_DECODER_GET_CLASS (self);
size_t bitstream_buffer_size;
size_t bitstream_pos;
GstDxvaDecodingArgs args;
if (priv->bitstream_buffer.empty ()) {
GST_ERROR_OBJECT (self, "No bitstream buffer to submit");
return GST_FLOW_ERROR;
}
memset (&args, 0, sizeof (GstDxvaDecodingArgs));
bitstream_pos = priv->bitstream_buffer.size ();
bitstream_buffer_size = GST_ROUND_UP_128 (bitstream_pos);
if (bitstream_buffer_size > bitstream_pos) {
size_t padding = bitstream_buffer_size - bitstream_pos;
/* As per DXVA spec, total amount of bitstream buffer size should be
* 128 bytes aligned. If actual data is not multiple of 128 bytes,
* the last slice data needs to be zero-padded */
priv->bitstream_buffer.resize (bitstream_buffer_size, 0);
priv->slice.SliceBytesInBuffer += padding;
}
args.picture_params = &priv->pic_params;
args.picture_params_size = sizeof (DXVA_PicParams_VP8);
args.slice_control = &priv->slice;
args.slice_control_size = sizeof (DXVA_Slice_VPx_Short);
args.bitstream = &priv->bitstream_buffer[0];
args.bitstream_size = priv->bitstream_buffer.size ();
g_assert (klass->end_picture);
return klass->end_picture (self, GST_CODEC_PICTURE (picture),
priv->ref_pics, &args);
}
static GstFlowReturn
gst_dxva_vp8_decoder_output_picture (GstVp8Decoder * decoder,
GstVideoCodecFrame * frame, GstVp8Picture * picture)
{
GstDxvaVp8Decoder *self = GST_DXVA_VP8_DECODER (decoder);
GstDxvaVp8DecoderPrivate *priv = self->priv;
GstDxvaVp8DecoderClass *klass = GST_DXVA_VP8_DECODER_GET_CLASS (self);
g_assert (klass->output_picture);
GST_LOG_OBJECT (self, "Outputting picture %p", picture);
return klass->output_picture (self, frame, GST_CODEC_PICTURE (picture),
(GstVideoBufferFlags) 0, priv->width, priv->height);
}

View file

@ -0,0 +1,90 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/dxva/dxva-prelude.h>
#include <gst/dxva/gstdxvatypes.h>
#include <gst/codecs/gstvp8decoder.h>
G_BEGIN_DECLS
#define GST_TYPE_DXVA_VP8_DECODER (gst_dxva_vp8_decoder_get_type())
#define GST_DXVA_VP8_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DXVA_VP8_DECODER,GstDxvaVp8Decoder))
#define GST_DXVA_VP8_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DXVA_VP8_DECODER,GstDxvaVp8DecoderClass))
#define GST_DXVA_VP8_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_DXVA_VP8_DECODER,GstDxvaVp8DecoderClass))
#define GST_IS_DXVA_VP8_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DXVA_VP8_DECODER))
#define GST_IS_DXVA_VP8_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DXVA_VP8_DECODER))
#define GST_DXVA_VP8_DECODER_CAST(obj) ((GstDxvaVp8Decoder*)obj)
typedef struct _GstDxvaVp8Decoder GstDxvaVp8Decoder;
typedef struct _GstDxvaVp8DecoderClass GstDxvaVp8DecoderClass;
typedef struct _GstDxvaVp8DecoderPrivate GstDxvaVp8DecoderPrivate;
struct _GstDxvaVp8Decoder
{
GstVp8Decoder parent;
/*< private >*/
GstDxvaVp8DecoderPrivate *priv;
};
struct _GstDxvaVp8DecoderClass
{
GstVp8DecoderClass parent_class;
GstFlowReturn (*configure) (GstDxvaVp8Decoder * decoder,
GstVideoCodecState * input_state,
const GstVideoInfo * info,
gint crop_x,
gint crop_y,
gint coded_width,
gint coded_height,
gint max_dpb_size);
GstFlowReturn (*new_picture) (GstDxvaVp8Decoder * decoder,
GstCodecPicture * picture);
guint8 (*get_picture_id) (GstDxvaVp8Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*start_picture) (GstDxvaVp8Decoder * decoder,
GstCodecPicture * picture,
guint8 * picture_id);
GstFlowReturn (*end_picture) (GstDxvaVp8Decoder * decoder,
GstCodecPicture * picture,
GPtrArray * ref_pics,
const GstDxvaDecodingArgs * args);
GstFlowReturn (*output_picture) (GstDxvaVp8Decoder * decoder,
GstVideoCodecFrame * frame,
GstCodecPicture * picture,
GstVideoBufferFlags buffer_flags,
gint display_width,
gint display_height);
};
GST_DXVA_API
GType gst_dxva_vp8_decoder_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDxvaVp8Decoder, gst_object_unref)
G_END_DECLS

View file

@ -0,0 +1,503 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdxvavp9decoder.h"
#include <string.h>
#include <vector>
/* HACK: to expose dxva data structure on UWP */
#ifdef WINAPI_PARTITION_DESKTOP
#undef WINAPI_PARTITION_DESKTOP
#endif
#define WINAPI_PARTITION_DESKTOP 1
#include <d3d9.h>
#include <dxva.h>
GST_DEBUG_CATEGORY_STATIC (gst_dxva_vp9_decoder_debug);
#define GST_CAT_DEFAULT gst_dxva_vp9_decoder_debug
/* *INDENT-OFF* */
struct _GstDxvaVp9DecoderPrivate
{
DXVA_PicParams_VP9 pic_params;
DXVA_Slice_VPx_Short slice;
std::vector<guint8> bitstream_buffer;
GPtrArray *ref_pics = nullptr;
/* To calculate use_prev_in_find_mv_refs */
guint last_frame_width = 0;
guint last_frame_height = 0;
gboolean last_show_frame = FALSE;
};
/* *INDENT-ON* */
static void gst_dxva_vp9_decoder_finalize (GObject * object);
static gboolean gst_dxva_vp9_decoder_start (GstVideoDecoder * decoder);
static GstFlowReturn gst_dxva_vp9_decoder_new_sequence (GstVp9Decoder * decoder,
const GstVp9FrameHeader * frame_hdr, gint max_dpb_size);
static GstFlowReturn gst_dxva_vp9_decoder_new_picture (GstVp9Decoder * decoder,
GstVideoCodecFrame * frame, GstVp9Picture * picture);
static GstVp9Picture *gst_dxva_vp9_decoder_duplicate_picture (GstVp9Decoder *
decoder, GstVideoCodecFrame * frame, GstVp9Picture * picture);
static GstFlowReturn
gst_dxva_vp9_decoder_decode_picture (GstVp9Decoder * decoder,
GstVp9Picture * picture, GstVp9Dpb * dpb);
static GstFlowReturn gst_dxva_vp9_decoder_end_picture (GstVp9Decoder * decoder,
GstVp9Picture * picture);
static GstFlowReturn gst_dxva_vp9_decoder_output_picture (GstVp9Decoder *
decoder, GstVideoCodecFrame * frame, GstVp9Picture * picture);
#define gst_dxva_vp9_decoder_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstDxvaVp9Decoder,
gst_dxva_vp9_decoder, GST_TYPE_VP9_DECODER,
GST_DEBUG_CATEGORY_INIT (gst_dxva_vp9_decoder_debug, "dxvavp9decoder",
0, "dxvavp9decoder"));
static void
gst_dxva_vp9_decoder_class_init (GstDxvaVp9DecoderClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
GstVp9DecoderClass *vp9decoder_class = GST_VP9_DECODER_CLASS (klass);
object_class->finalize = gst_dxva_vp9_decoder_finalize;
decoder_class->start = GST_DEBUG_FUNCPTR (gst_dxva_vp9_decoder_start);
vp9decoder_class->new_sequence =
GST_DEBUG_FUNCPTR (gst_dxva_vp9_decoder_new_sequence);
vp9decoder_class->new_picture =
GST_DEBUG_FUNCPTR (gst_dxva_vp9_decoder_new_picture);
vp9decoder_class->duplicate_picture =
GST_DEBUG_FUNCPTR (gst_dxva_vp9_decoder_duplicate_picture);
vp9decoder_class->decode_picture =
GST_DEBUG_FUNCPTR (gst_dxva_vp9_decoder_decode_picture);
vp9decoder_class->end_picture =
GST_DEBUG_FUNCPTR (gst_dxva_vp9_decoder_end_picture);
vp9decoder_class->output_picture =
GST_DEBUG_FUNCPTR (gst_dxva_vp9_decoder_output_picture);
}
static void
gst_dxva_vp9_decoder_init (GstDxvaVp9Decoder * self)
{
self->priv = new GstDxvaVp9DecoderPrivate ();
self->priv->ref_pics = g_ptr_array_new ();
}
static void
gst_dxva_vp9_decoder_finalize (GObject * object)
{
GstDxvaVp9Decoder *self = GST_DXVA_VP9_DECODER (object);
GstDxvaVp9DecoderPrivate *priv = self->priv;
g_ptr_array_unref (priv->ref_pics);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_dxva_vp9_decoder_reset (GstDxvaVp9Decoder * self)
{
GstDxvaVp9DecoderPrivate *priv = self->priv;
priv->last_frame_width = 0;
priv->last_frame_height = 0;
priv->last_show_frame = FALSE;
}
static gboolean
gst_dxva_vp9_decoder_start (GstVideoDecoder * decoder)
{
GstDxvaVp9Decoder *self = GST_DXVA_VP9_DECODER (decoder);
gst_dxva_vp9_decoder_reset (self);
return GST_VIDEO_DECODER_CLASS (parent_class)->start (decoder);
}
static GstFlowReturn
gst_dxva_vp9_decoder_new_sequence (GstVp9Decoder * decoder,
const GstVp9FrameHeader * frame_hdr, gint max_dpb_size)
{
GstDxvaVp9Decoder *self = GST_DXVA_VP9_DECODER (decoder);
GstDxvaVp9DecoderPrivate *priv = self->priv;
GstDxvaVp9DecoderClass *klass = GST_DXVA_VP9_DECODER_GET_CLASS (self);
GstVideoInfo info;
GstVideoFormat out_format = GST_VIDEO_FORMAT_UNKNOWN;
GstFlowReturn ret;
GST_LOG_OBJECT (self, "new sequence");
if (frame_hdr->profile == GST_VP9_PROFILE_0)
out_format = GST_VIDEO_FORMAT_NV12;
else if (frame_hdr->profile == GST_VP9_PROFILE_2)
out_format = GST_VIDEO_FORMAT_P010_10LE;
if (out_format == GST_VIDEO_FORMAT_UNKNOWN) {
GST_ERROR_OBJECT (self, "Could not support profile %d", frame_hdr->profile);
return GST_FLOW_NOT_NEGOTIATED;
}
/* Will be updated per decode_picture */
priv->last_frame_width = priv->last_frame_height = 0;
priv->last_show_frame = FALSE;
gst_video_info_set_format (&info,
out_format, frame_hdr->width, frame_hdr->height);
g_assert (klass->configure);
ret = klass->configure (self, decoder->input_state, &info, 0, 0,
frame_hdr->width, frame_hdr->height, max_dpb_size);
if (ret == GST_FLOW_OK &&
!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) {
GST_WARNING_OBJECT (self, "Couldn't negotiate with new sequence");
ret = GST_FLOW_NOT_NEGOTIATED;
}
return ret;
}
static GstFlowReturn
gst_dxva_vp9_decoder_new_picture (GstVp9Decoder * decoder,
GstVideoCodecFrame * frame, GstVp9Picture * picture)
{
GstDxvaVp9Decoder *self = GST_DXVA_VP9_DECODER (decoder);
GstDxvaVp9DecoderClass *klass = GST_DXVA_VP9_DECODER_GET_CLASS (self);
g_assert (klass->new_picture);
return klass->new_picture (self, GST_CODEC_PICTURE (picture));
}
static GstVp9Picture *
gst_dxva_vp9_decoder_duplicate_picture (GstVp9Decoder * decoder,
GstVideoCodecFrame * frame, GstVp9Picture * picture)
{
GstDxvaVp9Decoder *self = GST_DXVA_VP9_DECODER (decoder);
GstDxvaVp9DecoderClass *klass = GST_DXVA_VP9_DECODER_GET_CLASS (self);
GstVp9Picture *new_picture;
g_assert (klass->duplicate_picture);
new_picture = gst_vp9_picture_new ();
new_picture->frame_hdr = picture->frame_hdr;
if (klass->duplicate_picture (self, GST_CODEC_PICTURE (picture),
GST_CODEC_PICTURE (new_picture)) != GST_FLOW_OK) {
gst_vp9_picture_unref (new_picture);
return nullptr;
}
return new_picture;
}
static void
gst_dxva_vp9_decoder_copy_frame_params (GstDxvaVp9Decoder * self,
GstVp9Picture * picture, DXVA_PicParams_VP9 * params)
{
const GstVp9FrameHeader *frame_hdr = &picture->frame_hdr;
params->profile = frame_hdr->profile;
params->frame_type = frame_hdr->frame_type;
params->show_frame = frame_hdr->show_frame;
params->error_resilient_mode = frame_hdr->error_resilient_mode;
params->subsampling_x = frame_hdr->subsampling_x;
params->subsampling_y = frame_hdr->subsampling_y;
params->refresh_frame_context = frame_hdr->refresh_frame_context;
params->frame_parallel_decoding_mode =
frame_hdr->frame_parallel_decoding_mode;
params->intra_only = frame_hdr->intra_only;
params->frame_context_idx = frame_hdr->frame_context_idx;
params->reset_frame_context = frame_hdr->reset_frame_context;
if (frame_hdr->frame_type == GST_VP9_KEY_FRAME)
params->allow_high_precision_mv = 0;
else
params->allow_high_precision_mv = frame_hdr->allow_high_precision_mv;
params->width = frame_hdr->width;
params->height = frame_hdr->height;
params->BitDepthMinus8Luma = frame_hdr->bit_depth - 8;
params->BitDepthMinus8Chroma = frame_hdr->bit_depth - 8;
params->interp_filter = frame_hdr->interpolation_filter;
params->log2_tile_cols = frame_hdr->tile_cols_log2;
params->log2_tile_rows = frame_hdr->tile_rows_log2;
}
static void
gst_dxva_vp9_decoder_copy_reference_frames (GstDxvaVp9Decoder * self,
GstVp9Picture * picture, GstVp9Dpb * dpb, DXVA_PicParams_VP9 * params)
{
GstDxvaVp9DecoderPrivate *priv = self->priv;
GstDxvaVp9DecoderClass *klass = GST_DXVA_VP9_DECODER_GET_CLASS (self);
for (guint i = 0; i < GST_VP9_REF_FRAMES; i++) {
params->ref_frame_map[i].bPicEntry = 0xff;
params->ref_frame_coded_width[i] = 0;
params->ref_frame_coded_height[i] = 0;
if (dpb->pic_list[i]) {
GstVp9Picture *other = dpb->pic_list[i];
guint8 id = klass->get_picture_id (self, GST_CODEC_PICTURE (other));
if (id != 0xff) {
params->ref_frame_map[i].Index7Bits = id;
params->ref_frame_coded_width[i] = other->frame_hdr.width;
params->ref_frame_coded_height[i] = other->frame_hdr.height;
g_ptr_array_add (priv->ref_pics, other);
}
}
}
}
static void
gst_dxva_vp9_decoder_copy_frame_refs (GstDxvaVp9Decoder * self,
GstVp9Picture * picture, DXVA_PicParams_VP9 * params)
{
const GstVp9FrameHeader *frame_hdr = &picture->frame_hdr;
gint i;
for (i = 0; i < GST_VP9_REFS_PER_FRAME; i++)
params->frame_refs[i] = params->ref_frame_map[frame_hdr->ref_frame_idx[i]];
G_STATIC_ASSERT (G_N_ELEMENTS (params->ref_frame_sign_bias) ==
G_N_ELEMENTS (frame_hdr->ref_frame_sign_bias));
G_STATIC_ASSERT (sizeof (params->ref_frame_sign_bias) ==
sizeof (frame_hdr->ref_frame_sign_bias));
memcpy (params->ref_frame_sign_bias,
frame_hdr->ref_frame_sign_bias, sizeof (frame_hdr->ref_frame_sign_bias));
}
static void
gst_dxva_vp9_decoder_copy_loop_filter_params (GstDxvaVp9Decoder * self,
GstVp9Picture * picture, DXVA_PicParams_VP9 * params)
{
GstDxvaVp9DecoderPrivate *priv = self->priv;
const GstVp9FrameHeader *frame_hdr = &picture->frame_hdr;
const GstVp9LoopFilterParams *lfp = &frame_hdr->loop_filter_params;
params->filter_level = lfp->loop_filter_level;
params->sharpness_level = lfp->loop_filter_sharpness;
params->mode_ref_delta_enabled = lfp->loop_filter_delta_enabled;
params->mode_ref_delta_update = lfp->loop_filter_delta_update;
params->use_prev_in_find_mv_refs =
priv->last_show_frame && !frame_hdr->error_resilient_mode;
if (frame_hdr->frame_type != GST_VP9_KEY_FRAME && !frame_hdr->intra_only) {
params->use_prev_in_find_mv_refs &=
(frame_hdr->width == priv->last_frame_width &&
frame_hdr->height == priv->last_frame_height);
}
G_STATIC_ASSERT (G_N_ELEMENTS (params->ref_deltas) ==
G_N_ELEMENTS (lfp->loop_filter_ref_deltas));
G_STATIC_ASSERT (sizeof (params->ref_deltas) ==
sizeof (lfp->loop_filter_ref_deltas));
memcpy (params->ref_deltas, lfp->loop_filter_ref_deltas,
sizeof (lfp->loop_filter_ref_deltas));
G_STATIC_ASSERT (G_N_ELEMENTS (params->mode_deltas) ==
G_N_ELEMENTS (lfp->loop_filter_mode_deltas));
G_STATIC_ASSERT (sizeof (params->mode_deltas) ==
sizeof (lfp->loop_filter_mode_deltas));
memcpy (params->mode_deltas, lfp->loop_filter_mode_deltas,
sizeof (lfp->loop_filter_mode_deltas));
}
static void
gst_dxva_vp9_decoder_copy_quant_params (GstDxvaVp9Decoder * self,
GstVp9Picture * picture, DXVA_PicParams_VP9 * params)
{
const GstVp9FrameHeader *frame_hdr = &picture->frame_hdr;
const GstVp9QuantizationParams *qp = &frame_hdr->quantization_params;
params->base_qindex = qp->base_q_idx;
params->y_dc_delta_q = qp->delta_q_y_dc;
params->uv_dc_delta_q = qp->delta_q_uv_dc;
params->uv_ac_delta_q = qp->delta_q_uv_ac;
}
static void
gst_dxva_vp9_decoder_copy_segmentation_params (GstDxvaVp9Decoder * self,
GstVp9Picture * picture, DXVA_PicParams_VP9 * params)
{
const GstVp9FrameHeader *frame_hdr = &picture->frame_hdr;
const GstVp9SegmentationParams *sp = &frame_hdr->segmentation_params;
params->stVP9Segments.enabled = sp->segmentation_enabled;
params->stVP9Segments.update_map = sp->segmentation_update_map;
params->stVP9Segments.temporal_update = sp->segmentation_temporal_update;
params->stVP9Segments.abs_delta = sp->segmentation_abs_or_delta_update;
G_STATIC_ASSERT (G_N_ELEMENTS (params->stVP9Segments.tree_probs) ==
G_N_ELEMENTS (sp->segmentation_tree_probs));
G_STATIC_ASSERT (sizeof (params->stVP9Segments.tree_probs) ==
sizeof (sp->segmentation_tree_probs));
memcpy (params->stVP9Segments.tree_probs, sp->segmentation_tree_probs,
sizeof (sp->segmentation_tree_probs));
G_STATIC_ASSERT (G_N_ELEMENTS (params->stVP9Segments.pred_probs) ==
G_N_ELEMENTS (sp->segmentation_pred_prob));
G_STATIC_ASSERT (sizeof (params->stVP9Segments.pred_probs) ==
sizeof (sp->segmentation_pred_prob));
if (sp->segmentation_temporal_update) {
memcpy (params->stVP9Segments.pred_probs, sp->segmentation_pred_prob,
sizeof (params->stVP9Segments.pred_probs));
} else {
memset (params->stVP9Segments.pred_probs, 255,
sizeof (params->stVP9Segments.pred_probs));
}
for (guint i = 0; i < GST_VP9_MAX_SEGMENTS; i++) {
params->stVP9Segments.feature_mask[i] =
(sp->feature_enabled[i][GST_VP9_SEG_LVL_ALT_Q] << 0) |
(sp->feature_enabled[i][GST_VP9_SEG_LVL_ALT_L] << 1) |
(sp->feature_enabled[i][GST_VP9_SEG_LVL_REF_FRAME] << 2) |
(sp->feature_enabled[i][GST_VP9_SEG_SEG_LVL_SKIP] << 3);
for (guint j = 0; j < 3; j++)
params->stVP9Segments.feature_data[i][j] = sp->feature_data[i][j];
params->stVP9Segments.feature_data[i][3] = 0;
}
}
static GstFlowReturn
gst_dxva_vp9_decoder_decode_picture (GstVp9Decoder * decoder,
GstVp9Picture * picture, GstVp9Dpb * dpb)
{
GstDxvaVp9Decoder *self = GST_DXVA_VP9_DECODER (decoder);
GstDxvaVp9DecoderPrivate *priv = self->priv;
GstDxvaVp9DecoderClass *klass = GST_DXVA_VP9_DECODER_GET_CLASS (self);
DXVA_PicParams_VP9 *pic_params = &priv->pic_params;
DXVA_Slice_VPx_Short *slice = &priv->slice;
GstCodecPicture *codec_picture = GST_CODEC_PICTURE (picture);
GstFlowReturn ret;
guint8 picture_id;
g_assert (klass->start_picture);
g_assert (klass->get_picture_id);
ret = klass->start_picture (self, codec_picture, &picture_id);
if (ret != GST_FLOW_OK)
return ret;
priv->bitstream_buffer.resize (0);
g_ptr_array_set_size (priv->ref_pics, 0);
memset (pic_params, 0, sizeof (DXVA_PicParams_VP9));
pic_params->CurrPic.Index7Bits = picture_id;
pic_params->uncompressed_header_size_byte_aligned =
picture->frame_hdr.frame_header_length_in_bytes;
pic_params->first_partition_size = picture->frame_hdr.header_size_in_bytes;
pic_params->StatusReportFeedbackNumber = 1;
gst_dxva_vp9_decoder_copy_reference_frames (self, picture, dpb, pic_params);
gst_dxva_vp9_decoder_copy_frame_params (self, picture, pic_params);
gst_dxva_vp9_decoder_copy_frame_refs (self, picture, pic_params);
gst_dxva_vp9_decoder_copy_loop_filter_params (self, picture, pic_params);
gst_dxva_vp9_decoder_copy_quant_params (self, picture, pic_params);
gst_dxva_vp9_decoder_copy_segmentation_params (self, picture, pic_params);
priv->bitstream_buffer.resize (picture->size);
memcpy (&priv->bitstream_buffer[0], picture->data, picture->size);
slice->BSNALunitDataLocation = 0;
slice->SliceBytesInBuffer = priv->bitstream_buffer.size ();
slice->wBadSliceChopping = 0;
priv->last_frame_width = pic_params->width;
priv->last_frame_height = pic_params->height;
priv->last_show_frame = pic_params->show_frame;
return GST_FLOW_OK;
}
static GstFlowReturn
gst_dxva_vp9_decoder_end_picture (GstVp9Decoder * decoder,
GstVp9Picture * picture)
{
GstDxvaVp9Decoder *self = GST_DXVA_VP9_DECODER (decoder);
GstDxvaVp9DecoderPrivate *priv = self->priv;
GstDxvaVp9DecoderClass *klass = GST_DXVA_VP9_DECODER_GET_CLASS (self);
size_t bitstream_buffer_size;
size_t bitstream_pos;
GstDxvaDecodingArgs args;
if (priv->bitstream_buffer.empty ()) {
GST_ERROR_OBJECT (self, "No bitstream buffer to submit");
return GST_FLOW_ERROR;
}
memset (&args, 0, sizeof (GstDxvaDecodingArgs));
bitstream_pos = priv->bitstream_buffer.size ();
bitstream_buffer_size = GST_ROUND_UP_128 (bitstream_pos);
if (bitstream_buffer_size > bitstream_pos) {
size_t padding = bitstream_buffer_size - bitstream_pos;
/* As per DXVA spec, total amount of bitstream buffer size should be
* 128 bytes aligned. If actual data is not multiple of 128 bytes,
* the last slice data needs to be zero-padded */
priv->bitstream_buffer.resize (bitstream_buffer_size, 0);
priv->slice.SliceBytesInBuffer += padding;
}
args.picture_params = &priv->pic_params;
args.picture_params_size = sizeof (DXVA_PicParams_VP9);
args.slice_control = &priv->slice;
args.slice_control_size = sizeof (DXVA_Slice_VPx_Short);
args.bitstream = &priv->bitstream_buffer[0];
args.bitstream_size = priv->bitstream_buffer.size ();
g_assert (klass->end_picture);
return klass->end_picture (self, GST_CODEC_PICTURE (picture),
priv->ref_pics, &args);
}
static GstFlowReturn
gst_dxva_vp9_decoder_output_picture (GstVp9Decoder * decoder,
GstVideoCodecFrame * frame, GstVp9Picture * picture)
{
GstDxvaVp9Decoder *self = GST_DXVA_VP9_DECODER (decoder);
GstDxvaVp9DecoderClass *klass = GST_DXVA_VP9_DECODER_GET_CLASS (self);
g_assert (klass->output_picture);
GST_LOG_OBJECT (self, "Outputting picture %p", picture);
return klass->output_picture (self, frame, GST_CODEC_PICTURE (picture),
(GstVideoBufferFlags) 0, picture->frame_hdr.width,
picture->frame_hdr.height);
}

View file

@ -0,0 +1,94 @@
/* GStreamer
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
*
* 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 <gst/dxva/dxva-prelude.h>
#include <gst/dxva/gstdxvatypes.h>
#include <gst/codecs/gstvp9decoder.h>
G_BEGIN_DECLS
#define GST_TYPE_DXVA_VP9_DECODER (gst_dxva_vp9_decoder_get_type())
#define GST_DXVA_VP9_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DXVA_VP9_DECODER,GstDxvaVp9Decoder))
#define GST_DXVA_VP9_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DXVA_VP9_DECODER,GstDxvaVp9DecoderClass))
#define GST_DXVA_VP9_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_DXVA_VP9_DECODER,GstDxvaVp9DecoderClass))
#define GST_IS_DXVA_VP9_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DXVA_VP9_DECODER))
#define GST_IS_DXVA_VP9_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DXVA_VP9_DECODER))
#define GST_DXVA_VP9_DECODER_CAST(obj) ((GstDxvaVp9Decoder*)obj)
typedef struct _GstDxvaVp9Decoder GstDxvaVp9Decoder;
typedef struct _GstDxvaVp9DecoderClass GstDxvaVp9DecoderClass;
typedef struct _GstDxvaVp9DecoderPrivate GstDxvaVp9DecoderPrivate;
struct _GstDxvaVp9Decoder
{
GstVp9Decoder parent;
/*< private >*/
GstDxvaVp9DecoderPrivate *priv;
};
struct _GstDxvaVp9DecoderClass
{
GstVp9DecoderClass parent_class;
GstFlowReturn (*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);
GstFlowReturn (*new_picture) (GstDxvaVp9Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*duplicate_picture) (GstDxvaVp9Decoder * decoder,
GstCodecPicture * src,
GstCodecPicture * dst);
guint8 (*get_picture_id) (GstDxvaVp9Decoder * decoder,
GstCodecPicture * picture);
GstFlowReturn (*start_picture) (GstDxvaVp9Decoder * decoder,
GstCodecPicture * picture,
guint8 * picture_id);
GstFlowReturn (*end_picture) (GstDxvaVp9Decoder * decoder,
GstCodecPicture * picture,
GPtrArray * ref_pics,
const GstDxvaDecodingArgs * args);
GstFlowReturn (*output_picture) (GstDxvaVp9Decoder * decoder,
GstVideoCodecFrame * frame,
GstCodecPicture * picture,
GstVideoBufferFlags buffer_flags,
gint display_width,
gint display_height);
};
GST_DXVA_API
GType gst_dxva_vp9_decoder_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDxvaVp9Decoder, gst_object_unref)
G_END_DECLS

View file

@ -0,0 +1,55 @@
dxva_sources = [
'gstdxvaav1decoder.cpp',
'gstdxvah264decoder.cpp',
'gstdxvah265decoder.cpp',
'gstdxvampeg2decoder.cpp',
'gstdxvautils.cpp',
'gstdxvavp8decoder.cpp',
'gstdxvavp9decoder.cpp',
]
extra_args = [
'-DGST_USE_UNSTABLE_API',
'-DBUILDING_GST_DXVA',
'-DG_LOG_DOMAIN="GStreamer-Dxva"',
]
if host_system != 'windows'
subdir_done()
endif
if not cc.has_header('dxva.h') or not cc.has_header('d3d9.h')
subdir_done()
endif
# MinGW 32bits compiler seems to be complaining about redundant-decls
# when ComPtr is in use. Let's just disable the warning
if cc.get_id() != 'msvc'
extra_mingw_args = cc.get_supported_arguments([
'-Wno-redundant-decls',
])
extra_args += extra_mingw_args
endif
pkg_name = 'gstreamer-dxva-' + api_version
gstdxva = library('gstdxva-' + api_version,
dxva_sources,
c_args : gst_plugins_bad_args + extra_args,
cpp_args : gst_plugins_bad_args + extra_args,
include_directories : [configinc, libsinc],
version : libversion,
soversion : soversion,
install : true,
dependencies : [gstvideo_dep, gstcodecs_dep]
)
library_def = {'lib': gstdxva}
gst_libraries += [[pkg_name, library_def]]
# Still non-public api, should not install headers
gstdxva_dep = declare_dependency(link_with : gstdxva,
include_directories : [libsinc],
dependencies : [gstvideo_dep, gstcodecs_dep])
meson.override_dependency(pkg_name, gstdxva_dep)

View file

@ -8,6 +8,7 @@ subdir('codecs')
subdir('d3d11')
# cuda can depend on d3d11
subdir('cuda')
subdir('dxva')
subdir('insertbin')
subdir('interfaces')
subdir('isoff')