summaryrefslogtreecommitdiff
path: root/gst-libs
diff options
context:
space:
mode:
authorHe Junyan <junyan.he@intel.com>2021-07-27 10:51:03 +0800
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>2021-07-28 10:54:21 +0000
commit055ded53e9f1087146582a95ef52d9e140653602 (patch)
tree8b0dfb9dfeed8a443b29e5c9b71986cf6bb6e373 /gst-libs
parent04974015ec158985ca75298d8e1a78e5025a573c (diff)
downloadgstreamer-plugins-bad-055ded53e9f1087146582a95ef52d9e140653602.tar.gz
codecs: h264dec: Consider the field case when directly output.
For interlaced streams, it is also possible that the last frame is not able to be inserted into DPB when the DPB is full and the last frame is a non ref. For this case, we need to hold a extra ref for the first field of the last frame and wait for the complete frame with both top and bottom fields. For the progressive stream, the behaviour is unchanged. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2430>
Diffstat (limited to 'gst-libs')
-rw-r--r--gst-libs/gst/codecs/gsth264decoder.c78
1 files changed, 73 insertions, 5 deletions
diff --git a/gst-libs/gst/codecs/gsth264decoder.c b/gst-libs/gst/codecs/gsth264decoder.c
index f98778754..f0ce654c7 100644
--- a/gst-libs/gst/codecs/gsth264decoder.c
+++ b/gst-libs/gst/codecs/gsth264decoder.c
@@ -91,6 +91,9 @@ struct _GstH264DecoderPrivate
GstH264DecoderAlign align;
GstH264NalParser *parser;
GstH264Dpb *dpb;
+ /* Cache last field which can not enter the DPB, should be a non ref */
+ GstH264Picture *last_field;
+
GstFlowReturn last_ret;
/* used for low-latency vs. high throughput mode decision */
gboolean is_live;
@@ -305,6 +308,7 @@ gst_h264_decoder_reset (GstH264Decoder * self)
g_clear_pointer (&self->input_state, gst_video_codec_state_unref);
g_clear_pointer (&priv->parser, gst_h264_nal_parser_free);
g_clear_pointer (&priv->dpb, gst_h264_dpb_free);
+ gst_h264_picture_clear (&priv->last_field);
priv->width = 0;
priv->height = 0;
@@ -372,6 +376,7 @@ gst_h264_decoder_clear_dpb (GstH264Decoder * self, gboolean flush)
gst_queue_array_clear (priv->output_queue);
gst_h264_decoder_clear_ref_pic_lists (self);
+ gst_h264_picture_clear (&priv->last_field);
gst_h264_dpb_clear (priv->dpb);
priv->last_output_poc = G_MININT32;
}
@@ -713,6 +718,67 @@ gst_h264_decoder_split_frame (GstH264Decoder * self, GstH264Picture * picture)
}
static gboolean
+output_picture_directly (GstH264Decoder * self, GstH264Picture * picture)
+{
+ GstH264DecoderPrivate *priv = self->priv;
+ GstH264Picture *out_pic = NULL;
+ gboolean ret = TRUE;
+
+ if (!gst_h264_dpb_get_interlaced (priv->dpb)) {
+ g_assert (priv->last_field == NULL);
+ out_pic = g_steal_pointer (&picture);
+ ret = TRUE;
+ goto output;
+ }
+
+ if (priv->last_field == NULL) {
+ if (picture->second_field) {
+ GST_WARNING ("Set the last output %p poc:%d, without first field",
+ picture, picture->pic_order_cnt);
+
+ ret = FALSE;
+ goto output;
+ }
+
+ /* Just cache the first field. */
+ priv->last_field = g_steal_pointer (&picture);
+ ret = TRUE;
+ } else {
+ if (!picture->second_field || !picture->other_field
+ || picture->other_field != priv->last_field) {
+ GST_WARNING ("The last field %p poc:%d is not the pair of the "
+ "current field %p poc:%d",
+ priv->last_field, priv->last_field->pic_order_cnt,
+ picture, picture->pic_order_cnt);
+
+ gst_h264_picture_clear (&priv->last_field);
+ ret = FALSE;
+ goto output;
+ }
+
+ GST_TRACE ("Pair the last field %p poc:%d and the current"
+ " field %p poc:%d",
+ priv->last_field, priv->last_field->pic_order_cnt,
+ picture, picture->pic_order_cnt);
+
+ out_pic = priv->last_field;
+ priv->last_field = NULL;
+ /* Link each field. */
+ out_pic->other_field = picture;
+ }
+
+output:
+ if (out_pic) {
+ gst_h264_dpb_set_last_output (priv->dpb, out_pic);
+ gst_h264_decoder_do_output_picture (self, out_pic);
+ }
+
+ gst_h264_picture_clear (&picture);
+
+ return ret;
+}
+
+static gboolean
gst_h264_decoder_handle_frame_num_gap (GstH264Decoder * self, gint frame_num)
{
GstH264DecoderPrivate *priv = self->priv;
@@ -1645,6 +1711,7 @@ gst_h264_decoder_drain_internal (GstH264Decoder * self)
gst_h264_decoder_drain_output_queue (self, 0);
+ gst_h264_picture_clear (&priv->last_field);
gst_h264_dpb_clear (priv->dpb);
priv->last_output_poc = G_MININT32;
@@ -1807,6 +1874,7 @@ gst_h264_decoder_finish_picture (GstH264Decoder * self,
{
GstVideoDecoder *decoder = GST_VIDEO_DECODER (self);
GstH264DecoderPrivate *priv = self->priv;
+ gboolean ret = TRUE;
/* Finish processing the picture.
* Start by storing previous picture data for later use */
@@ -1872,8 +1940,9 @@ gst_h264_decoder_finish_picture (GstH264Decoder * self,
For a non-reference decoded picture, if there is empty frame buffer
after bumping the smaller POC, add to DPB.
Otherwise, output directly. */
- if (picture->second_field || picture->ref
- || gst_h264_dpb_has_empty_frame_buffer (priv->dpb)) {
+ if ((picture->second_field && picture->other_field
+ && picture->other_field->ref)
+ || picture->ref || gst_h264_dpb_has_empty_frame_buffer (priv->dpb)) {
/* Split frame into top/bottom field pictures for reference picture marking
* process. Even if current picture has field_pic_flag equal to zero,
* if next picture is a field picture, complementary field pair of reference
@@ -1896,8 +1965,7 @@ gst_h264_decoder_finish_picture (GstH264Decoder * self,
gst_h264_dpb_add (priv->dpb, picture);
}
} else {
- gst_h264_decoder_do_output_picture (self, picture);
- gst_h264_dpb_set_last_output (priv->dpb, picture);
+ ret = output_picture_directly (self, picture);
}
GST_LOG_OBJECT (self,
@@ -1907,7 +1975,7 @@ gst_h264_decoder_finish_picture (GstH264Decoder * self,
gst_h264_picture_unref (picture);
- return TRUE;
+ return ret;
}
static gboolean