codecs: h264decoder: Add support for field-pair input frame
authorSeungha Yang <seungha@centricular.com>
Sun, 10 Jan 2021 14:11:01 +0000 (23:11 +0900)
committerSeungha Yang <seungha@centricular.com>
Sun, 10 Jan 2021 14:11:01 +0000 (23:11 +0900)
In case that upstream pushed buffer as a frame unit, not picture
unit for interlaced stream, baseclass should be able to detect
AU boundary (i.e., complementary field pair).

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1947>

gst-libs/gst/codecs/gsth264decoder.c

index 588d494efdbfdc728ec36e3122adbf04fb4909a5..aba2a97d2c5ade4eb69fef0f99dbf8863513c129 100644 (file)
@@ -668,6 +668,7 @@ gst_h264_decoder_split_frame (GstH264Decoder * self, GstH264Picture * picture)
   other_field->frame_num = picture->frame_num;
   other_field->ref = picture->ref;
   other_field->nonexisting = picture->nonexisting;
+  other_field->system_frame_number = picture->system_frame_number;
 
   return other_field;
 }
@@ -965,6 +966,29 @@ gst_h264_decoder_parse_slice (GstH264Decoder * self, GstH264NalUnit * nalu)
   priv->active_pps = priv->current_slice.header.pps;
   priv->active_sps = priv->active_pps->sequence;
 
+  /* Check whether field picture boundary within given codec frame.
+   * This might happen in case that upstream sent buffer per frame unit,
+   * not picture unit (i.e., AU unit).
+   * If AU boundary is detected, then finish first field picture we decoded
+   * in this chain, we should finish the current picture and
+   * start new field picture decoding */
+  if (gst_h264_dpb_get_interlaced (priv->dpb) && priv->current_picture &&
+      !GST_H264_PICTURE_IS_FRAME (priv->current_picture) &&
+      !priv->current_picture->second_field) {
+    GstH264PictureField prev_field = priv->current_picture->field;
+    GstH264PictureField cur_field = GST_H264_PICTURE_FIELD_FRAME;
+    if (priv->current_slice.header.field_pic_flag)
+      cur_field = priv->current_slice.header.bottom_field_flag ?
+          GST_H264_PICTURE_FIELD_BOTTOM_FIELD :
+          GST_H264_PICTURE_FIELD_TOP_FIELD;
+
+    if (cur_field != prev_field) {
+      GST_LOG_OBJECT (self,
+          "Found new field picture, finishing the first field picture");
+      gst_h264_decoder_finish_current_picture (self);
+    }
+  }
+
   if (!priv->current_picture) {
     GstH264DecoderClass *klass = GST_H264_DECODER_GET_CLASS (self);
     GstH264Picture *picture = NULL;
@@ -1742,8 +1766,12 @@ gst_h264_decoder_finish_picture (GstH264Decoder * self,
    * them as such */
   gst_h264_dpb_delete_unused (priv->dpb);
 
-  /* If this is the second field, drop corresponding frame */
-  if (picture->second_field) {
+  /* If field pictures belong to different codec frame,
+   * drop codec frame of the second field because we are consuming
+   * only the first codec frame via GstH264Decoder::output_picture() method */
+  if (picture->second_field && picture->other_field &&
+      picture->system_frame_number !=
+      picture->other_field->system_frame_number) {
     GstVideoCodecFrame *frame = gst_video_decoder_get_frame (decoder,
         picture->system_frame_number);