v4l2codecs: h264: Fix split field handling
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Thu, 19 Aug 2021 20:45:18 +0000 (16:45 -0400)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Fri, 20 Aug 2021 19:29:53 +0000 (19:29 +0000)
Split fields ends up on multiple picture and requires accessing the
other_field to complete the information (POC).

This also cleanup the DPB from non-reference (was not useful) and skips
properly merge field instead of keeping them duplicated. This fixes most
of interlace decoding seen in fluster.

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

sys/v4l2codecs/gstv4l2codech264dec.c

index bd482b5..8517325 100644 (file)
@@ -530,13 +530,11 @@ gst_v4l2_codec_h264_dec_fill_decoder_params (GstV4l2CodecH264Dec * self,
     GstH264SliceHdr * slice_hdr, GstH264Picture * picture, GstH264Dpb * dpb)
 {
   GArray *refs = gst_h264_dpb_get_pictures_all (dpb);
-  gint i;
+  gint i, entry_id = 0;
 
   /* *INDENT-OFF* */
   self->decode_params = (struct v4l2_ctrl_h264_decode_params) {
     .nal_ref_idc = picture->nal_ref_idc,
-    .top_field_order_cnt = picture->top_field_order_cnt,
-    .bottom_field_order_cnt = picture->bottom_field_order_cnt,
     .frame_num = slice_hdr->frame_num,
     .idr_pic_id = slice_hdr->idr_pic_id,
     .pic_order_cnt_lsb = slice_hdr->pic_order_cnt_lsb,
@@ -551,28 +549,38 @@ gst_v4l2_codec_h264_dec_fill_decoder_params (GstV4l2CodecH264Dec * self,
              (slice_hdr->bottom_field_flag ? V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD : 0),
   };
 
-  for (i = 0; i < refs->len; i++) {
-    GstH264Picture *ref_pic = g_array_index (refs, GstH264Picture *, i);
-    gint pic_num = ref_pic->pic_num;
-    guchar dpb_ref = 0;
-
-    /* Unwrap pic_num */
-    if (pic_num < 0)
-      pic_num += slice_hdr->max_pic_num;
-
-    switch (ref_pic->field) {
+  switch (picture->field) {
     case GST_H264_PICTURE_FIELD_FRAME:
-      dpb_ref = V4L2_H264_FRAME_REF;
+      self->decode_params.top_field_order_cnt = picture->top_field_order_cnt;
+      self->decode_params.bottom_field_order_cnt =
+        picture->bottom_field_order_cnt;
       break;
     case GST_H264_PICTURE_FIELD_TOP_FIELD:
-      dpb_ref = V4L2_H264_TOP_FIELD_REF;
+      self->decode_params.top_field_order_cnt = picture->top_field_order_cnt;
+      self->decode_params.bottom_field_order_cnt = 0;
       break;
     case GST_H264_PICTURE_FIELD_BOTTOM_FIELD:
-      dpb_ref = V4L2_H264_BOTTOM_FIELD_REF;
+      self->decode_params.top_field_order_cnt = 0;
+      self->decode_params.bottom_field_order_cnt =
+          picture->bottom_field_order_cnt;
       break;
-    }
+  }
 
-    self->decode_params.dpb[i] = (struct v4l2_h264_dpb_entry) {
+  for (i = 0; i < refs->len; i++) {
+    GstH264Picture *ref_pic = g_array_index (refs, GstH264Picture *, i);
+    gint pic_num = ref_pic->pic_num;
+    struct v4l2_h264_dpb_entry *entry;
+
+    /* Skip non-reference as they are not useful to decoding */
+    if (!GST_H264_PICTURE_IS_REF (ref_pic))
+      continue;
+
+    /* The second field picture will be handled differently */
+    if (ref_pic->second_field)
+      continue;
+
+    entry = &self->decode_params.dpb[entry_id++];
+    *entry = (struct v4l2_h264_dpb_entry) {
       /*
        * The reference is multiplied by 1000 because it's wassed as micro
        * seconds and this TS is nanosecond.
@@ -580,15 +588,42 @@ gst_v4l2_codec_h264_dec_fill_decoder_params (GstV4l2CodecH264Dec * self,
       .reference_ts = (guint64) ref_pic->system_frame_number * 1000,
       .frame_num = ref_pic->frame_num,
       .pic_num = pic_num,
-      .top_field_order_cnt = ref_pic->pic_order_cnt,
-      .bottom_field_order_cnt = ref_pic->bottom_field_order_cnt,
       .flags = V4L2_H264_DPB_ENTRY_FLAG_VALID
-          | (GST_H264_PICTURE_IS_REF (ref_pic) ?
-              V4L2_H264_DPB_ENTRY_FLAG_ACTIVE : 0)
-          | (GST_H264_PICTURE_IS_LONG_TERM_REF (ref_pic) ?
-              V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM : 0),
-      .fields = dpb_ref,
+          | (GST_H264_PICTURE_IS_REF (ref_pic) ? V4L2_H264_DPB_ENTRY_FLAG_ACTIVE : 0)
+          | (GST_H264_PICTURE_IS_LONG_TERM_REF (ref_pic) ? V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM : 0),
     };
+
+    switch (ref_pic->field) {
+      case GST_H264_PICTURE_FIELD_FRAME:
+        entry->top_field_order_cnt = ref_pic->top_field_order_cnt;
+        entry->bottom_field_order_cnt = ref_pic->bottom_field_order_cnt;
+        entry->fields = V4L2_H264_FRAME_REF;
+        break;
+      case GST_H264_PICTURE_FIELD_TOP_FIELD:
+        entry->top_field_order_cnt = ref_pic->top_field_order_cnt;
+        entry->fields = V4L2_H264_TOP_FIELD_REF;
+
+        if (ref_pic->other_field) {
+          entry->bottom_field_order_cnt =
+              ref_pic->other_field->bottom_field_order_cnt;
+          entry->fields |= V4L2_H264_BOTTOM_FIELD_REF;
+        } else {
+          entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_FIELD;
+        }
+        break;
+      case GST_H264_PICTURE_FIELD_BOTTOM_FIELD:
+        entry->bottom_field_order_cnt = ref_pic->bottom_field_order_cnt;
+        entry->fields = V4L2_H264_BOTTOM_FIELD_REF;
+
+        if (ref_pic->other_field) {
+          entry->top_field_order_cnt =
+            ref_pic->other_field->top_field_order_cnt;
+          entry->fields |= V4L2_H264_TOP_FIELD_REF;
+        } else {
+          entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_FIELD;
+        }
+        break;
+    }
   }
   /* *INDENT-ON* */
 
@@ -704,6 +739,10 @@ lookup_dpb_index (struct v4l2_h264_dpb_entry dpb[16], GstH264Picture * ref_pic)
   if (!ref_pic)
     return 0xff;
 
+  /* DPB entries only stores first field in a merged fashion */
+  if (ref_pic->second_field && ref_pic->other_field)
+    ref_pic = ref_pic->other_field;
+
   ref_ts = (guint64) ref_pic->system_frame_number * 1000;
   for (i = 0; i < 16; i++) {
     if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE
@@ -714,15 +753,33 @@ lookup_dpb_index (struct v4l2_h264_dpb_entry dpb[16], GstH264Picture * ref_pic)
   return 0xff;
 }
 
+static guint
+_get_v4l2_fields_ref (GstH264Picture * ref_pic, gboolean merge)
+{
+  if (merge && ref_pic->other_field)
+    return V4L2_H264_FRAME_REF;
+
+  switch (ref_pic->field) {
+    case GST_H264_PICTURE_FIELD_FRAME:
+      return V4L2_H264_FRAME_REF;
+      break;
+    case GST_H264_PICTURE_FIELD_TOP_FIELD:
+      return V4L2_H264_TOP_FIELD_REF;
+      break;
+    case GST_H264_PICTURE_FIELD_BOTTOM_FIELD:
+      return V4L2_H264_BOTTOM_FIELD_REF;
+      break;
+  }
+
+  return V4L2_H264_FRAME_REF;
+}
+
 static void
 gst_v4l2_codec_h264_dec_fill_references (GstV4l2CodecH264Dec * self,
-    GArray * ref_pic_list0, GArray * ref_pic_list1)
+    gboolean cur_is_frame, GArray * ref_pic_list0, GArray * ref_pic_list1)
 {
   struct v4l2_ctrl_h264_slice_params *slice_params;
-  gint i, fields = V4L2_H264_FRAME_REF;
-
-  if (self->interlaced)
-    fields = 0;
+  gint i;
 
   slice_params = &g_array_index (self->slice_params,
       struct v4l2_ctrl_h264_slice_params, 0);
@@ -737,7 +794,8 @@ gst_v4l2_codec_h264_dec_fill_references (GstV4l2CodecH264Dec * self,
         g_array_index (ref_pic_list0, GstH264Picture *, i);
     slice_params->ref_pic_list0[i].index =
         lookup_dpb_index (self->decode_params.dpb, ref_pic);
-    slice_params->ref_pic_list0[i].fields = fields;
+    slice_params->ref_pic_list0[i].fields =
+        _get_v4l2_fields_ref (ref_pic, cur_is_frame);
   }
 
   for (i = 0; i < ref_pic_list1->len; i++) {
@@ -745,7 +803,8 @@ gst_v4l2_codec_h264_dec_fill_references (GstV4l2CodecH264Dec * self,
         g_array_index (ref_pic_list1, GstH264Picture *, i);
     slice_params->ref_pic_list1[i].index =
         lookup_dpb_index (self->decode_params.dpb, ref_pic);
-    slice_params->ref_pic_list1[i].fields = fields;
+    slice_params->ref_pic_list1[i].fields =
+        _get_v4l2_fields_ref (ref_pic, cur_is_frame);
   }
 }
 
@@ -1182,8 +1241,8 @@ gst_v4l2_codec_h264_dec_decode_slice (GstH264Decoder * decoder,
 
     gst_v4l2_codec_h264_dec_fill_slice_params (self, slice);
     gst_v4l2_codec_h264_dec_fill_pred_weight (self, &slice->header);
-    gst_v4l2_codec_h264_dec_fill_references (self, ref_pic_list0,
-        ref_pic_list1);
+    gst_v4l2_codec_h264_dec_fill_references (self,
+        GST_H264_PICTURE_IS_FRAME (picture), ref_pic_list0, ref_pic_list1);
   }
 
   bitstream_data = self->bitstream_map.data + self->bitstream_map.size;