mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-07 18:14:35 +00:00
codecs: h265decocer: Rework for DPB management
* Move all DPB bumping process into GstH265Dpb internal * Handle DPB add process in GstH265Dpb struct * Make implementation to be 1:1 mappable with hevc specification * Fix wrong DPB bumping implementation especially when no_output_of_prior_pics_flag was specified. With fixes from Nicolas Dufresne <nicolas.dufresne@collabora.com> Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1748>
This commit is contained in:
parent
0bb73ec800
commit
eeffd91109
3 changed files with 240 additions and 267 deletions
|
@ -107,9 +107,6 @@ struct _GstH265DecoderPrivate
|
||||||
GArray *ref_pic_list_tmp;
|
GArray *ref_pic_list_tmp;
|
||||||
GArray *ref_pic_list0;
|
GArray *ref_pic_list0;
|
||||||
GArray *ref_pic_list1;
|
GArray *ref_pic_list1;
|
||||||
|
|
||||||
/* Cached array to handle pictures to be outputted */
|
|
||||||
GArray *to_output;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define parent_class gst_h265_decoder_parent_class
|
#define parent_class gst_h265_decoder_parent_class
|
||||||
|
@ -170,11 +167,6 @@ gst_h265_decoder_init (GstH265Decoder * self)
|
||||||
sizeof (GstH265Picture *), 32);
|
sizeof (GstH265Picture *), 32);
|
||||||
priv->ref_pic_list1 = g_array_sized_new (FALSE, TRUE,
|
priv->ref_pic_list1 = g_array_sized_new (FALSE, TRUE,
|
||||||
sizeof (GstH265Picture *), 32);
|
sizeof (GstH265Picture *), 32);
|
||||||
|
|
||||||
priv->to_output = g_array_sized_new (FALSE, TRUE,
|
|
||||||
sizeof (GstH265Picture *), 16);
|
|
||||||
g_array_set_clear_func (priv->to_output,
|
|
||||||
(GDestroyNotify) gst_h265_picture_clear);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -183,7 +175,6 @@ gst_h265_decoder_finalize (GObject * object)
|
||||||
GstH265Decoder *self = GST_H265_DECODER (object);
|
GstH265Decoder *self = GST_H265_DECODER (object);
|
||||||
GstH265DecoderPrivate *priv = self->priv;
|
GstH265DecoderPrivate *priv = self->priv;
|
||||||
|
|
||||||
g_array_unref (priv->to_output);
|
|
||||||
g_array_unref (priv->ref_pic_list_tmp);
|
g_array_unref (priv->ref_pic_list_tmp);
|
||||||
g_array_unref (priv->ref_pic_list0);
|
g_array_unref (priv->ref_pic_list0);
|
||||||
g_array_unref (priv->ref_pic_list1);
|
g_array_unref (priv->ref_pic_list1);
|
||||||
|
@ -527,7 +518,7 @@ gst_h265_decoder_preprocess_slice (GstH265Decoder * self, GstH265Slice * slice)
|
||||||
|
|
||||||
if (GST_H265_IS_NAL_TYPE_IDR (nalu->type)) {
|
if (GST_H265_IS_NAL_TYPE_IDR (nalu->type)) {
|
||||||
GST_DEBUG_OBJECT (self, "IDR nalu, clear dpb");
|
GST_DEBUG_OBJECT (self, "IDR nalu, clear dpb");
|
||||||
gst_h265_decoder_drain (GST_VIDEO_DECODER (self));
|
gst_h265_decoder_drain_internal (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -1289,27 +1280,16 @@ gst_h265_decoder_prepare_rps (GstH265Decoder * self, const GstH265Slice * slice,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
gst_h265_decoder_clear_dpb (GstH265Decoder * self)
|
|
||||||
{
|
|
||||||
GstH265DecoderPrivate *priv = self->priv;
|
|
||||||
|
|
||||||
gst_h265_dpb_clear (priv->dpb);
|
|
||||||
priv->last_output_poc = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_h265_decoder_do_output_picture (GstH265Decoder * self,
|
gst_h265_decoder_do_output_picture (GstH265Decoder * self,
|
||||||
GstH265Picture * picture, gboolean clear_dpb)
|
GstH265Picture * picture)
|
||||||
{
|
{
|
||||||
GstH265DecoderPrivate *priv = self->priv;
|
GstH265DecoderPrivate *priv = self->priv;
|
||||||
GstH265DecoderClass *klass;
|
GstH265DecoderClass *klass;
|
||||||
GstVideoCodecFrame *frame = NULL;
|
GstVideoCodecFrame *frame = NULL;
|
||||||
|
|
||||||
picture->outputted = TRUE;
|
GST_LOG_OBJECT (self, "Output picture %p (poc %d)", picture,
|
||||||
|
picture->pic_order_cnt);
|
||||||
if (clear_dpb && !picture->ref)
|
|
||||||
gst_h265_dpb_delete_by_poc (priv->dpb, picture->pic_order_cnt);
|
|
||||||
|
|
||||||
if (picture->pic_order_cnt < priv->last_output_poc) {
|
if (picture->pic_order_cnt < priv->last_output_poc) {
|
||||||
GST_WARNING_OBJECT (self,
|
GST_WARNING_OBJECT (self,
|
||||||
|
@ -1327,8 +1307,8 @@ gst_h265_decoder_do_output_picture (GstH265Decoder * self,
|
||||||
"No available codec frame with frame number %d",
|
"No available codec frame with frame number %d",
|
||||||
picture->system_frame_number);
|
picture->system_frame_number);
|
||||||
priv->last_ret = GST_FLOW_ERROR;
|
priv->last_ret = GST_FLOW_ERROR;
|
||||||
gst_h265_picture_unref (picture);
|
|
||||||
|
|
||||||
|
gst_h265_picture_unref (picture);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1338,57 +1318,30 @@ gst_h265_decoder_do_output_picture (GstH265Decoder * self,
|
||||||
priv->last_ret = klass->output_picture (self, frame, picture);
|
priv->last_ret = klass->output_picture (self, frame, picture);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static void
|
||||||
poc_asc_compare (const GstH265Picture ** a, const GstH265Picture ** b)
|
gst_h265_decoder_clear_dpb (GstH265Decoder * self)
|
||||||
{
|
{
|
||||||
return (*a)->pic_order_cnt > (*b)->pic_order_cnt;
|
GstH265DecoderPrivate *priv = self->priv;
|
||||||
|
|
||||||
|
gst_h265_dpb_clear (priv->dpb);
|
||||||
|
priv->last_output_poc = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_h265_decoder_drain_internal (GstH265Decoder * self)
|
gst_h265_decoder_drain_internal (GstH265Decoder * self)
|
||||||
{
|
{
|
||||||
GstH265DecoderPrivate *priv = self->priv;
|
GstH265DecoderPrivate *priv = self->priv;
|
||||||
GArray *to_output = priv->to_output;
|
GstH265Picture *picture;
|
||||||
|
|
||||||
gst_h265_dpb_delete_outputted (priv->dpb);
|
while ((picture = gst_h265_dpb_bump (priv->dpb)) != NULL)
|
||||||
gst_h265_dpb_get_pictures_not_outputted (priv->dpb, to_output);
|
gst_h265_decoder_do_output_picture (self, picture);
|
||||||
g_array_sort (to_output, (GCompareFunc) poc_asc_compare);
|
|
||||||
|
|
||||||
while (to_output->len) {
|
|
||||||
GstH265Picture *picture = g_array_index (to_output, GstH265Picture *, 0);
|
|
||||||
|
|
||||||
/* We want the last reference when outputing so take a ref and then remove
|
|
||||||
* from both arrays. */
|
|
||||||
gst_h265_picture_ref (picture);
|
|
||||||
g_array_remove_index (to_output, 0);
|
|
||||||
gst_h265_dpb_delete_by_poc (priv->dpb, picture->pic_order_cnt);
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (self, "Output picture %p (poc %d)", picture,
|
|
||||||
picture->pic_order_cnt);
|
|
||||||
gst_h265_decoder_do_output_picture (self, picture, FALSE);
|
|
||||||
picture = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_array_set_size (to_output, 0);
|
|
||||||
gst_h265_dpb_clear (priv->dpb);
|
gst_h265_dpb_clear (priv->dpb);
|
||||||
priv->last_output_poc = 0;
|
priv->last_output_poc = 0;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_h265_decoder_check_latency_count (GArray * array, guint32 max_latency)
|
|
||||||
{
|
|
||||||
gint i;
|
|
||||||
|
|
||||||
for (i = 0; i < array->len; i++) {
|
|
||||||
GstH265Picture *pic = g_array_index (array, GstH265Picture *, i);
|
|
||||||
if (!pic->outputted && pic->pic_latency_cnt >= max_latency)
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* C.5.2.2 */
|
/* C.5.2.2 */
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_h265_decoder_dpb_init (GstH265Decoder * self, const GstH265Slice * slice,
|
gst_h265_decoder_dpb_init (GstH265Decoder * self, const GstH265Slice * slice,
|
||||||
|
@ -1397,7 +1350,10 @@ gst_h265_decoder_dpb_init (GstH265Decoder * self, const GstH265Slice * slice,
|
||||||
GstH265DecoderPrivate *priv = self->priv;
|
GstH265DecoderPrivate *priv = self->priv;
|
||||||
const GstH265SliceHdr *slice_hdr = &slice->header;
|
const GstH265SliceHdr *slice_hdr = &slice->header;
|
||||||
const GstH265NalUnit *nalu = &slice->nalu;
|
const GstH265NalUnit *nalu = &slice->nalu;
|
||||||
|
const GstH265SPS *sps = priv->active_sps;
|
||||||
|
GstH265Picture *to_output;
|
||||||
|
|
||||||
|
/* C 3.2 */
|
||||||
if (GST_H265_IS_NAL_TYPE_IRAP (nalu->type) && picture->NoRaslOutputFlag
|
if (GST_H265_IS_NAL_TYPE_IRAP (nalu->type) && picture->NoRaslOutputFlag
|
||||||
&& !priv->new_bitstream) {
|
&& !priv->new_bitstream) {
|
||||||
if (nalu->type == GST_H265_NAL_SLICE_CRA_NUT)
|
if (nalu->type == GST_H265_NAL_SLICE_CRA_NUT)
|
||||||
|
@ -1408,11 +1364,29 @@ gst_h265_decoder_dpb_init (GstH265Decoder * self, const GstH265Slice * slice,
|
||||||
|
|
||||||
if (picture->NoOutputOfPriorPicsFlag) {
|
if (picture->NoOutputOfPriorPicsFlag) {
|
||||||
GST_DEBUG_OBJECT (self, "Clear dpb");
|
GST_DEBUG_OBJECT (self, "Clear dpb");
|
||||||
gst_h265_decoder_drain (GST_VIDEO_DECODER (self));
|
gst_h265_decoder_clear_dpb (self);
|
||||||
|
} else {
|
||||||
|
gst_h265_dpb_delete_unused (priv->dpb);
|
||||||
|
while ((to_output = gst_h265_dpb_bump (priv->dpb)) != NULL)
|
||||||
|
gst_h265_decoder_do_output_picture (self, to_output);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* C 3.2 */
|
|
||||||
gst_h265_dpb_delete_unused (priv->dpb);
|
gst_h265_dpb_delete_unused (priv->dpb);
|
||||||
|
while (gst_h265_dpb_needs_bump (priv->dpb,
|
||||||
|
sps->max_num_reorder_pics[sps->max_sub_layers_minus1],
|
||||||
|
priv->SpsMaxLatencyPictures,
|
||||||
|
sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1] +
|
||||||
|
1)) {
|
||||||
|
to_output = gst_h265_dpb_bump (priv->dpb);
|
||||||
|
|
||||||
|
/* Something wrong... */
|
||||||
|
if (!to_output) {
|
||||||
|
GST_WARNING_OBJECT (self, "Bumping is needed but no picture to output");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_h265_decoder_do_output_picture (self, to_output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -1463,126 +1437,51 @@ static gboolean
|
||||||
gst_h265_decoder_finish_picture (GstH265Decoder * self,
|
gst_h265_decoder_finish_picture (GstH265Decoder * self,
|
||||||
GstH265Picture * picture)
|
GstH265Picture * picture)
|
||||||
{
|
{
|
||||||
|
GstVideoDecoder *decoder = GST_VIDEO_DECODER (self);
|
||||||
GstH265DecoderPrivate *priv = self->priv;
|
GstH265DecoderPrivate *priv = self->priv;
|
||||||
const GstH265SPS *sps = priv->active_sps;
|
const GstH265SPS *sps = priv->active_sps;
|
||||||
GArray *not_outputted = priv->to_output;
|
|
||||||
guint num_remaining;
|
|
||||||
gint i;
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (self,
|
GST_LOG_OBJECT (self,
|
||||||
"Finishing picture %p (poc %d), entries in DPB %d",
|
"Finishing picture %p (poc %d), entries in DPB %d",
|
||||||
picture, picture->pic_order_cnt, gst_h265_dpb_get_size (priv->dpb));
|
picture, picture->pic_order_cnt, gst_h265_dpb_get_size (priv->dpb));
|
||||||
|
|
||||||
/* Get all pictures that haven't been outputted yet */
|
gst_h265_dpb_delete_unused (priv->dpb);
|
||||||
gst_h265_dpb_get_pictures_not_outputted (priv->dpb, not_outputted);
|
|
||||||
|
|
||||||
/* C.5.2.3 */
|
/* This picture is decode only, drop corresponding frame */
|
||||||
if (picture->output_flag) {
|
if (!picture->output_flag) {
|
||||||
for (i = 0; i < not_outputted->len; i++) {
|
GstVideoCodecFrame *frame = gst_video_decoder_get_frame (decoder,
|
||||||
GstH265Picture *other =
|
picture->system_frame_number);
|
||||||
g_array_index (not_outputted, GstH265Picture *, i);
|
|
||||||
|
|
||||||
if (!other->outputted)
|
gst_video_decoder_release_frame (decoder, frame);
|
||||||
other->pic_latency_cnt++;
|
}
|
||||||
|
|
||||||
|
/* gst_h265_dpb_add() will take care of pic_latency_cnt increment and
|
||||||
|
* reference picture marking for this picture */
|
||||||
|
gst_h265_dpb_add (priv->dpb, picture);
|
||||||
|
|
||||||
|
/* NOTE: As per C.5.2.2, bumping by sps_max_dec_pic_buffering_minus1 is
|
||||||
|
* applied only for the output and removal of pictures from the DPB before
|
||||||
|
* the decoding of the current picture. So pass zero here */
|
||||||
|
while (gst_h265_dpb_needs_bump (priv->dpb,
|
||||||
|
sps->max_num_reorder_pics[sps->max_sub_layers_minus1],
|
||||||
|
priv->SpsMaxLatencyPictures, 0)) {
|
||||||
|
GstH265Picture *to_output = gst_h265_dpb_bump (priv->dpb);
|
||||||
|
|
||||||
|
/* Something wrong... */
|
||||||
|
if (!to_output) {
|
||||||
|
GST_WARNING_OBJECT (self, "Bumping is needed but no picture to output");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
picture->outputted = FALSE;
|
gst_h265_decoder_do_output_picture (self, to_output);
|
||||||
picture->pic_latency_cnt = 0;
|
|
||||||
} else {
|
|
||||||
picture->outputted = TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set pic as short_term_ref */
|
if (gst_h265_dpb_is_full (priv->dpb)) {
|
||||||
picture->ref = TRUE;
|
/* If we haven't managed to output anything to free up space in DPB
|
||||||
picture->long_term = FALSE;
|
* to store this picture, it's an error in the stream */
|
||||||
|
GST_WARNING_OBJECT (self, "Could not free up space in DPB");
|
||||||
/* Include the one we've just decoded */
|
return FALSE;
|
||||||
if (picture->output_flag)
|
|
||||||
g_array_append_val (not_outputted, picture);
|
|
||||||
|
|
||||||
/* for debugging */
|
|
||||||
#ifndef GST_DISABLE_GST_DEBUG
|
|
||||||
GST_TRACE_OBJECT (self, "Before sorting not outputted list");
|
|
||||||
for (i = 0; i < not_outputted->len; i++) {
|
|
||||||
GstH265Picture *tmp = g_array_index (not_outputted, GstH265Picture *, i);
|
|
||||||
GST_TRACE_OBJECT (self,
|
|
||||||
"\t%dth picture %p (poc %d)", i, tmp, tmp->pic_order_cnt);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Sort in output order */
|
|
||||||
g_array_sort (not_outputted, (GCompareFunc) poc_asc_compare);
|
|
||||||
|
|
||||||
#ifndef GST_DISABLE_GST_DEBUG
|
|
||||||
GST_TRACE_OBJECT (self,
|
|
||||||
"After sorting not outputted list in poc ascending order");
|
|
||||||
for (i = 0; i < not_outputted->len; i++) {
|
|
||||||
GstH265Picture *tmp = g_array_index (not_outputted, GstH265Picture *, i);
|
|
||||||
GST_TRACE_OBJECT (self,
|
|
||||||
"\t%dth picture %p (poc %d)", i, tmp, tmp->pic_order_cnt);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Try to output as many pictures as we can. A picture can be output,
|
|
||||||
* if the number of decoded and not yet outputted pictures that would remain
|
|
||||||
* in DPB afterwards would at least be equal to max_num_reorder_frames.
|
|
||||||
* If the outputted picture is not a reference picture, it doesn't have
|
|
||||||
* to remain in the DPB and can be removed */
|
|
||||||
num_remaining = not_outputted->len;
|
|
||||||
|
|
||||||
while (num_remaining > sps->max_num_reorder_pics[sps->max_sub_layers_minus1]
|
|
||||||
|| (num_remaining &&
|
|
||||||
sps->max_latency_increase_plus1[sps->max_sub_layers_minus1] &&
|
|
||||||
gst_h265_decoder_check_latency_count (not_outputted,
|
|
||||||
priv->SpsMaxLatencyPictures))) {
|
|
||||||
gboolean clear_dpb = TRUE;
|
|
||||||
GstH265Picture *to_output =
|
|
||||||
g_array_index (not_outputted, GstH265Picture *, 0);
|
|
||||||
|
|
||||||
gst_h265_picture_ref (to_output);
|
|
||||||
g_array_remove_index (not_outputted, 0);
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (self,
|
|
||||||
"Output picture %p (poc %d)", to_output, to_output->pic_order_cnt);
|
|
||||||
|
|
||||||
/* Current picture hasn't been inserted into DPB yet, so don't remove it
|
|
||||||
* if we managed to output it immediately */
|
|
||||||
if (picture && to_output == picture) {
|
|
||||||
clear_dpb = FALSE;
|
|
||||||
|
|
||||||
if (picture->ref) {
|
|
||||||
GST_TRACE_OBJECT (self,
|
|
||||||
"Put current picture %p (poc %d) to dpb",
|
|
||||||
picture, picture->pic_order_cnt);
|
|
||||||
gst_h265_dpb_add (priv->dpb, gst_h265_picture_ref (picture));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* and mark current picture as handled */
|
|
||||||
picture = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_h265_decoder_do_output_picture (self, to_output, clear_dpb);
|
|
||||||
|
|
||||||
num_remaining--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we haven't managed to output the picture that we just decoded, or if
|
|
||||||
* it's a reference picture, we have to store it in DPB */
|
|
||||||
if (picture && (!picture->outputted || picture->ref)) {
|
|
||||||
if (gst_h265_dpb_is_full (priv->dpb)) {
|
|
||||||
/* If we haven't managed to output anything to free up space in DPB
|
|
||||||
* to store this picture, it's an error in the stream */
|
|
||||||
GST_WARNING_OBJECT (self, "Could not free up space in DPB");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_TRACE_OBJECT (self,
|
|
||||||
"Put picture %p (outputted %d, ref %d, poc %d) to dpb",
|
|
||||||
picture, picture->outputted, picture->ref, picture->pic_order_cnt);
|
|
||||||
gst_h265_dpb_add (priv->dpb, gst_h265_picture_ref (picture));
|
|
||||||
}
|
|
||||||
|
|
||||||
g_array_set_size (not_outputted, 0);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@ struct _GstH265Dpb
|
||||||
{
|
{
|
||||||
GArray *pic_list;
|
GArray *pic_list;
|
||||||
gint max_num_pics;
|
gint max_num_pics;
|
||||||
|
gint num_output_needed;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,6 +188,7 @@ gst_h265_dpb_clear (GstH265Dpb * dpb)
|
||||||
g_return_if_fail (dpb != NULL);
|
g_return_if_fail (dpb != NULL);
|
||||||
|
|
||||||
g_array_set_size (dpb->pic_list, 0);
|
g_array_set_size (dpb->pic_list, 0);
|
||||||
|
dpb->num_output_needed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -194,7 +196,8 @@ gst_h265_dpb_clear (GstH265Dpb * dpb)
|
||||||
* @dpb: a #GstH265Dpb
|
* @dpb: a #GstH265Dpb
|
||||||
* @picture: (transfer full): a #GstH265Picture
|
* @picture: (transfer full): a #GstH265Picture
|
||||||
*
|
*
|
||||||
* Store the @picture
|
* Store the @picture and perform increase pic_latency_cnt as defined in
|
||||||
|
* "C.5.2.3 Additional bumping" process
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
gst_h265_dpb_add (GstH265Dpb * dpb, GstH265Picture * picture)
|
gst_h265_dpb_add (GstH265Dpb * dpb, GstH265Picture * picture)
|
||||||
|
@ -202,6 +205,27 @@ gst_h265_dpb_add (GstH265Dpb * dpb, GstH265Picture * picture)
|
||||||
g_return_if_fail (dpb != NULL);
|
g_return_if_fail (dpb != NULL);
|
||||||
g_return_if_fail (GST_IS_H265_PICTURE (picture));
|
g_return_if_fail (GST_IS_H265_PICTURE (picture));
|
||||||
|
|
||||||
|
if (picture->output_flag) {
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
for (i = 0; i < dpb->pic_list->len; i++) {
|
||||||
|
GstH265Picture *other =
|
||||||
|
g_array_index (dpb->pic_list, GstH265Picture *, i);
|
||||||
|
|
||||||
|
if (other->needed_for_output)
|
||||||
|
other->pic_latency_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dpb->num_output_needed++;
|
||||||
|
picture->needed_for_output = TRUE;
|
||||||
|
} else {
|
||||||
|
picture->needed_for_output = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* C.3.4 */
|
||||||
|
picture->ref = TRUE;
|
||||||
|
picture->long_term = FALSE;
|
||||||
|
|
||||||
g_array_append_val (dpb->pic_list, picture);
|
g_array_append_val (dpb->pic_list, picture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +233,7 @@ gst_h265_dpb_add (GstH265Dpb * dpb, GstH265Picture * picture)
|
||||||
* gst_h265_dpb_delete_unused:
|
* gst_h265_dpb_delete_unused:
|
||||||
* @dpb: a #GstH265Dpb
|
* @dpb: a #GstH265Dpb
|
||||||
*
|
*
|
||||||
* Delete already outputted and not referenced all pictures from dpb
|
* Delete not needed for output and not referenced all pictures from dpb
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
gst_h265_dpb_delete_unused (GstH265Dpb * dpb)
|
gst_h265_dpb_delete_unused (GstH265Dpb * dpb)
|
||||||
|
@ -222,7 +246,7 @@ gst_h265_dpb_delete_unused (GstH265Dpb * dpb)
|
||||||
GstH265Picture *picture =
|
GstH265Picture *picture =
|
||||||
g_array_index (dpb->pic_list, GstH265Picture *, i);
|
g_array_index (dpb->pic_list, GstH265Picture *, i);
|
||||||
|
|
||||||
if (picture->outputted && !picture->ref) {
|
if (!picture->needed_for_output && !picture->ref) {
|
||||||
GST_TRACE ("remove picture %p (poc %d) from dpb",
|
GST_TRACE ("remove picture %p (poc %d) from dpb",
|
||||||
picture, picture->pic_order_cnt);
|
picture, picture->pic_order_cnt);
|
||||||
g_array_remove_index (dpb->pic_list, i);
|
g_array_remove_index (dpb->pic_list, i);
|
||||||
|
@ -231,61 +255,6 @@ gst_h265_dpb_delete_unused (GstH265Dpb * dpb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* gst_h265_dpb_delete_outputted:
|
|
||||||
* @dpb: a #GstH265Dpb
|
|
||||||
*
|
|
||||||
* Delete already outputted picture, even if they are referenced.
|
|
||||||
*
|
|
||||||
* Since: 1.20
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
gst_h265_dpb_delete_outputted (GstH265Dpb * dpb)
|
|
||||||
{
|
|
||||||
gint i;
|
|
||||||
|
|
||||||
g_return_if_fail (dpb != NULL);
|
|
||||||
|
|
||||||
for (i = 0; i < dpb->pic_list->len; i++) {
|
|
||||||
GstH265Picture *picture =
|
|
||||||
g_array_index (dpb->pic_list, GstH265Picture *, i);
|
|
||||||
|
|
||||||
if (picture->outputted) {
|
|
||||||
GST_TRACE ("remove picture %p (poc %d) from dpb",
|
|
||||||
picture, picture->pic_order_cnt);
|
|
||||||
g_array_remove_index_fast (dpb->pic_list, i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* gst_h265_dpb_delete_by_poc:
|
|
||||||
* @dpb: a #GstH265Dpb
|
|
||||||
* @poc: a poc of #GstH265Picture to remove
|
|
||||||
*
|
|
||||||
* Delete a #GstH265Dpb by @poc
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
gst_h265_dpb_delete_by_poc (GstH265Dpb * dpb, gint poc)
|
|
||||||
{
|
|
||||||
gint i;
|
|
||||||
|
|
||||||
g_return_if_fail (dpb != NULL);
|
|
||||||
|
|
||||||
for (i = 0; i < dpb->pic_list->len; i++) {
|
|
||||||
GstH265Picture *picture =
|
|
||||||
g_array_index (dpb->pic_list, GstH265Picture *, i);
|
|
||||||
|
|
||||||
if (picture->pic_order_cnt == poc) {
|
|
||||||
g_array_remove_index (dpb->pic_list, i);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_WARNING ("Couldn't find picture with poc %d", poc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_h265_dpb_num_ref_pictures:
|
* gst_h265_dpb_num_ref_pictures:
|
||||||
* @dpb: a #GstH265Dpb
|
* @dpb: a #GstH265Dpb
|
||||||
|
@ -448,33 +417,6 @@ gst_h265_dpb_get_long_ref_by_poc (GstH265Dpb * dpb, gint poc)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* gst_h265_dpb_get_pictures_not_outputted:
|
|
||||||
* @dpb: a #GstH265Dpb
|
|
||||||
* @out: (out) (element-type GstH265Picture) (transfer full): an array
|
|
||||||
* of #GstH265Picture pointer
|
|
||||||
*
|
|
||||||
* Retrieve all not-outputted pictures from @dpb
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
gst_h265_dpb_get_pictures_not_outputted (GstH265Dpb * dpb, GArray * out)
|
|
||||||
{
|
|
||||||
gint i;
|
|
||||||
|
|
||||||
g_return_if_fail (dpb != NULL);
|
|
||||||
g_return_if_fail (out != NULL);
|
|
||||||
|
|
||||||
for (i = 0; i < dpb->pic_list->len; i++) {
|
|
||||||
GstH265Picture *picture =
|
|
||||||
g_array_index (dpb->pic_list, GstH265Picture *, i);
|
|
||||||
|
|
||||||
if (!picture->outputted) {
|
|
||||||
gst_h265_picture_ref (picture);
|
|
||||||
g_array_append_val (out, picture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_h265_dpb_get_pictures_all:
|
* gst_h265_dpb_get_pictures_all:
|
||||||
* @dpb: a #GstH265Dpb
|
* @dpb: a #GstH265Dpb
|
||||||
|
@ -548,3 +490,136 @@ gst_h265_dpb_get_picture (GstH265Dpb * dpb, guint32 system_frame_number)
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_h265_decoder_check_latency_count (GstH265Dpb * dpb, guint32 max_latency)
|
||||||
|
{
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
for (i = 0; i < dpb->pic_list->len; i++) {
|
||||||
|
GstH265Picture *picture =
|
||||||
|
g_array_index (dpb->pic_list, GstH265Picture *, i);
|
||||||
|
|
||||||
|
if (!picture->needed_for_output)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (picture->pic_latency_cnt >= max_latency)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_h265_dpb_needs_bump:
|
||||||
|
* @dpb: a #GstH265Dpb
|
||||||
|
* @max_num_reorder_pics: sps_max_num_reorder_pics[HighestTid]
|
||||||
|
* @max_latency_increase: SpsMaxLatencyPictures[HighestTid]
|
||||||
|
* @max_dec_pic_buffering: sps_max_dec_pic_buffering_minus1[HighestTid ] + 1
|
||||||
|
* or zero if this shouldn't be used for bumping decision
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if bumping is required
|
||||||
|
*
|
||||||
|
* Since: 1.20
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_h265_dpb_needs_bump (GstH265Dpb * dpb, guint max_num_reorder_pics,
|
||||||
|
guint max_latency_increase, guint max_dec_pic_buffering)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (dpb != NULL, FALSE);
|
||||||
|
g_assert (dpb->num_output_needed >= 0);
|
||||||
|
|
||||||
|
/* C.5.2.3 */
|
||||||
|
if (dpb->num_output_needed > max_num_reorder_pics) {
|
||||||
|
GST_TRACE ("num_output_needed (%d) > max_num_reorder_pics (%d)",
|
||||||
|
dpb->num_output_needed, max_num_reorder_pics);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dpb->num_output_needed && max_latency_increase &&
|
||||||
|
gst_h265_decoder_check_latency_count (dpb, max_latency_increase)) {
|
||||||
|
GST_TRACE ("has late picture, max_latency_increase: %d",
|
||||||
|
max_latency_increase);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* C.5.2.2 */
|
||||||
|
if (max_dec_pic_buffering && dpb->pic_list->len >= max_dec_pic_buffering) {
|
||||||
|
GST_TRACE ("dpb size (%d) >= max_dec_pic_buffering (%d)",
|
||||||
|
dpb->pic_list->len, max_dec_pic_buffering);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
gst_h265_dpb_get_lowest_output_needed_picture (GstH265Dpb * dpb,
|
||||||
|
GstH265Picture ** picture)
|
||||||
|
{
|
||||||
|
gint i;
|
||||||
|
GstH265Picture *lowest = NULL;
|
||||||
|
gint index = -1;
|
||||||
|
|
||||||
|
*picture = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < dpb->pic_list->len; i++) {
|
||||||
|
GstH265Picture *picture =
|
||||||
|
g_array_index (dpb->pic_list, GstH265Picture *, i);
|
||||||
|
|
||||||
|
if (!picture->needed_for_output)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!lowest) {
|
||||||
|
lowest = picture;
|
||||||
|
index = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (picture->pic_order_cnt < lowest->pic_order_cnt) {
|
||||||
|
lowest = picture;
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lowest)
|
||||||
|
*picture = gst_h265_picture_ref (lowest);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_h265_dpb_bump:
|
||||||
|
* @dpb: a #GstH265Dpb
|
||||||
|
*
|
||||||
|
* Perform bumping process as defined in C.5.2.4 "Bumping" process.
|
||||||
|
*
|
||||||
|
* Returns: (nullable) (transfer full): a #GstH265Picture which is needed to be
|
||||||
|
* outputted
|
||||||
|
*
|
||||||
|
* Since: 1.20
|
||||||
|
*/
|
||||||
|
GstH265Picture *
|
||||||
|
gst_h265_dpb_bump (GstH265Dpb * dpb)
|
||||||
|
{
|
||||||
|
GstH265Picture *picture;
|
||||||
|
gint index;
|
||||||
|
|
||||||
|
g_return_val_if_fail (dpb != NULL, NULL);
|
||||||
|
|
||||||
|
/* C.5.2.4 "Bumping" process */
|
||||||
|
index = gst_h265_dpb_get_lowest_output_needed_picture (dpb, &picture);
|
||||||
|
|
||||||
|
if (!picture || index < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
picture->needed_for_output = FALSE;
|
||||||
|
|
||||||
|
dpb->num_output_needed--;
|
||||||
|
g_assert (dpb->num_output_needed >= 0);
|
||||||
|
|
||||||
|
if (!picture->ref)
|
||||||
|
g_array_remove_index_fast (dpb->pic_list, index);
|
||||||
|
|
||||||
|
return picture;
|
||||||
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ typedef enum
|
||||||
|
|
||||||
struct _GstH265Picture
|
struct _GstH265Picture
|
||||||
{
|
{
|
||||||
|
/*< private >*/
|
||||||
GstMiniObject parent;
|
GstMiniObject parent;
|
||||||
|
|
||||||
GstH265SliceType type;
|
GstH265SliceType type;
|
||||||
|
@ -81,7 +82,7 @@ struct _GstH265Picture
|
||||||
|
|
||||||
gboolean ref;
|
gboolean ref;
|
||||||
gboolean long_term;
|
gboolean long_term;
|
||||||
gboolean outputted;
|
gboolean needed_for_output;
|
||||||
|
|
||||||
GstH265PictureField field;
|
GstH265PictureField field;
|
||||||
|
|
||||||
|
@ -160,13 +161,6 @@ void gst_h265_dpb_add (GstH265Dpb * dpb,
|
||||||
GST_CODECS_API
|
GST_CODECS_API
|
||||||
void gst_h265_dpb_delete_unused (GstH265Dpb * dpb);
|
void gst_h265_dpb_delete_unused (GstH265Dpb * dpb);
|
||||||
|
|
||||||
GST_CODECS_API
|
|
||||||
void gst_h265_dpb_delete_outputted (GstH265Dpb * dpb);
|
|
||||||
|
|
||||||
GST_CODECS_API
|
|
||||||
void gst_h265_dpb_delete_by_poc (GstH265Dpb * dpb,
|
|
||||||
gint poc);
|
|
||||||
|
|
||||||
GST_CODECS_API
|
GST_CODECS_API
|
||||||
gint gst_h265_dpb_num_ref_pictures (GstH265Dpb * dpb);
|
gint gst_h265_dpb_num_ref_pictures (GstH265Dpb * dpb);
|
||||||
|
|
||||||
|
@ -189,10 +183,6 @@ GST_CODECS_API
|
||||||
GstH265Picture * gst_h265_dpb_get_long_ref_by_poc (GstH265Dpb * dpb,
|
GstH265Picture * gst_h265_dpb_get_long_ref_by_poc (GstH265Dpb * dpb,
|
||||||
gint poc);
|
gint poc);
|
||||||
|
|
||||||
GST_CODECS_API
|
|
||||||
void gst_h265_dpb_get_pictures_not_outputted (GstH265Dpb * dpb,
|
|
||||||
GArray * out);
|
|
||||||
|
|
||||||
GST_CODECS_API
|
GST_CODECS_API
|
||||||
GArray * gst_h265_dpb_get_pictures_all (GstH265Dpb * dpb);
|
GArray * gst_h265_dpb_get_pictures_all (GstH265Dpb * dpb);
|
||||||
|
|
||||||
|
@ -206,6 +196,15 @@ gint gst_h265_dpb_get_size (GstH265Dpb * dpb);
|
||||||
GST_CODECS_API
|
GST_CODECS_API
|
||||||
gboolean gst_h265_dpb_is_full (GstH265Dpb * dpb);
|
gboolean gst_h265_dpb_is_full (GstH265Dpb * dpb);
|
||||||
|
|
||||||
|
GST_CODECS_API
|
||||||
|
gboolean gst_h265_dpb_needs_bump (GstH265Dpb * dpb,
|
||||||
|
guint max_num_reorder_pics,
|
||||||
|
guint max_latency_increase,
|
||||||
|
guint max_dec_pic_buffering);
|
||||||
|
|
||||||
|
GST_CODECS_API
|
||||||
|
GstH265Picture * gst_h265_dpb_bump (GstH265Dpb * dpb);
|
||||||
|
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstH265Picture, gst_h265_picture_unref)
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstH265Picture, gst_h265_picture_unref)
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
Loading…
Reference in a new issue