va: Add the low-delay-b frame support for H265.
authorHe Junyan <junyan.he@intel.com>
Mon, 18 Apr 2022 14:36:09 +0000 (22:36 +0800)
committerHe Junyan <junyan.he@intel.com>
Thu, 1 Dec 2022 01:44:41 +0000 (09:44 +0800)
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2036>

subprojects/gst-plugins-bad/sys/va/gstvah265enc.c

index d493229..a86c6e0 100644 (file)
@@ -277,6 +277,7 @@ struct _GstVaH265Enc
     guint32 max_l1_num;
     guint32 forward_ref_num;
     guint32 backward_ref_num;
+    gboolean low_delay_b_mode;
 
     guint num_reorder_frames;
     guint max_dpb_size;
@@ -1265,7 +1266,12 @@ _h265_to_va_coding_type (GstVaH265Enc * self, GstVaH265EncFrame * frame)
       coding_type = 1;
       break;
     case GST_H265_P_SLICE:
-      coding_type = 2;
+      if (self->gop.low_delay_b_mode) {
+        /* Convert P into forward ref B */
+        coding_type = 3;
+      } else {
+        coding_type = 2;
+      }
       break;
     case GST_H265_B_SLICE:
       /* We use hierarchical_level_plus1, so same for all B frames */
@@ -1431,6 +1437,7 @@ _h265_fill_slice_parameter (GstVaH265Enc * self, GstVaH265EncFrame * frame,
     VAEncSliceParameterBufferHEVC * slice)
 {
   int8_t slice_qp_delta = 0;
+  GstH265SliceType frame_type;
   gint i;
 
   /* *INDENT-OFF* */
@@ -1443,10 +1450,25 @@ _h265_fill_slice_parameter (GstVaH265Enc * self, GstVaH265EncFrame * frame,
     g_assert (slice_qp_delta <= 51 && slice_qp_delta >= -51);
   }
 
+  frame_type = frame->type;
+  /* If low_delay_b_mode, we convert P to low delay b, which has 2
+     ref lists and clone L1 from L0. */
+  if (self->gop.low_delay_b_mode && frame->type == GST_H265_P_SLICE) {
+    g_assert (self->gop.max_l1_num > 0);
+    g_assert (list1_num == 0);
+
+    frame_type = GST_H265_B_SLICE;
+    list1_num = (list0_num <= self->gop.max_l1_num ?
+        list0_num : self->gop.max_l1_num);
+
+    for (i = 0; i < list1_num; i++)
+      list1[i] = list0[i];
+  }
+
   *slice = (VAEncSliceParameterBufferHEVC) {
     .slice_segment_address = start_address,
     .num_ctu_in_slice = ctu_num,
-    .slice_type = frame->type,
+    .slice_type = frame_type,
     /* Only one parameter set supported now. */
     .slice_pic_parameter_set_id = 0,
     /* Set the reference list later
@@ -1490,7 +1512,7 @@ _h265_fill_slice_parameter (GstVaH265Enc * self, GstVaH265EncFrame * frame,
       /* deblocking_filter_control_present_flag not set now */
       .slice_deblocking_filter_disabled_flag = 0,
       .slice_loop_filter_across_slices_enabled_flag = 1,
-      .collocated_from_l0_flag = (frame->type == GST_H265_I_SLICE ?
+      .collocated_from_l0_flag = (frame_type == GST_H265_I_SLICE ?
           0 : self->features.collocated_from_l0_flag),
     },
 #if VA_CHECK_VERSION(1, 10, 0)
@@ -1500,16 +1522,17 @@ _h265_fill_slice_parameter (GstVaH265Enc * self, GstVaH265EncFrame * frame,
   };
   /* *INDENT-ON* */
 
-  if (frame->type == GST_H265_B_SLICE || frame->type == GST_H265_P_SLICE) {
+  if (frame_type == GST_H265_B_SLICE || frame_type == GST_H265_P_SLICE) {
     slice->slice_fields.bits.num_ref_idx_active_override_flag =
         (list0_num > 0 || list1_num > 0);
     slice->num_ref_idx_l0_active_minus1 = list0_num > 0 ? list0_num - 1 : 0;
-    if (frame->type == GST_H265_B_SLICE)
+
+    if (frame_type == GST_H265_B_SLICE)
       slice->num_ref_idx_l1_active_minus1 = list1_num > 0 ? list1_num - 1 : 0;
   }
 
   i = 0;
-  if (frame->type != GST_H265_I_SLICE) {
+  if (frame_type != GST_H265_I_SLICE) {
     for (; i < list0_num; i++) {
       slice->ref_pic_list0[i].picture_id =
           gst_va_encode_picture_get_reconstruct_surface (list0[i]->picture);
@@ -1522,7 +1545,7 @@ _h265_fill_slice_parameter (GstVaH265Enc * self, GstVaH265EncFrame * frame,
   }
 
   i = 0;
-  if (frame->type == GST_H265_B_SLICE) {
+  if (frame_type == GST_H265_B_SLICE) {
     for (; i < list1_num; i++) {
       slice->ref_pic_list1[i].picture_id =
           gst_va_encode_picture_get_reconstruct_surface (list1[i]->picture);
@@ -2311,6 +2334,7 @@ gst_va_h265_enc_reset_state (GstVaBaseEnc * base)
   self->gop.i_period = 0;
   self->gop.total_idr_count = 0;
   self->gop.ip_period = 0;
+  self->gop.low_delay_b_mode = FALSE;
   self->gop.highest_pyramid_level = 0;
   memset (self->gop.frame_types, 0, sizeof (self->gop.frame_types));
   self->gop.cur_frame_index = 0;
@@ -2942,8 +2966,13 @@ _h265_print_gop_structure (GstVaH265Enc * self)
       g_string_append_printf (str, ", ");
     }
 
-    g_string_append_printf (str, "%s",
-        _h265_slice_type_name (self->gop.frame_types[i].slice_type));
+    if (self->gop.low_delay_b_mode &&
+        self->gop.frame_types[i].slice_type == GST_H265_P_SLICE) {
+      g_string_append_printf (str, "%s", "LDB");
+    } else {
+      g_string_append_printf (str, "%s",
+          _h265_slice_type_name (self->gop.frame_types[i].slice_type));
+    }
 
     if (self->gop.b_pyramid
         && self->gop.frame_types[i].slice_type == GST_H265_B_SLICE) {
@@ -3028,13 +3057,14 @@ _get_log2_max_num (guint num)
 /* Consider the idr_period, num_bframes, L0/L1 reference number.
  * TODO: Load some preset fixed GOP structure.
  * TODO: Skip this if in lookahead mode. */
-static void
+static gboolean
 _h265_generate_gop_structure (GstVaH265Enc * self)
 {
   GstVaBaseEnc *base = GST_VA_BASE_ENC (self);
   guint32 log2_max_frame_num;
   guint32 list0, list1, forward_num, backward_num, gop_ref_num;
   gint32 p_frames;
+  guint32 prediction_direction;
 
   /* If not set, generate a idr every second */
   if (self->gop.idr_period == 0) {
@@ -3085,12 +3115,40 @@ _h265_generate_gop_structure (GstVaH265Enc * self)
 
   forward_num = list0;
   backward_num = list1;
+
+  prediction_direction = gst_va_encoder_get_prediction_direction (base->encoder,
+      base->profile, GST_VA_BASE_ENC_ENTRYPOINT (base));
+  if (prediction_direction) {
+#if VA_CHECK_VERSION(1,9,0)
+    if (!(prediction_direction & VA_PREDICTION_DIRECTION_PREVIOUS)) {
+      GST_INFO_OBJECT (self, "No forward prediction support");
+      forward_num = 0;
+      /* Only backward ref is insane. */
+      backward_num = 0;
+    }
+
+    if (!(prediction_direction & VA_PREDICTION_DIRECTION_FUTURE)) {
+      GST_INFO_OBJECT (self, "No backward prediction support");
+      backward_num = 0;
+    }
+
+    if (prediction_direction & VA_PREDICTION_DIRECTION_BI_NOT_EMPTY) {
+      if (self->gop.max_l1_num == 0) {
+        GST_INFO_OBJECT (self, "Not possible to support "
+            "VA_PREDICTION_DIRECTION_BI_NOT_EMPTY while list1 is 0");
+        return FALSE;
+      }
+      GST_INFO_OBJECT (self, "Enable low-delay-b mode");
+      self->gop.low_delay_b_mode = TRUE;
+    }
+#endif
+  }
+
   if (forward_num > self->gop.num_ref_frames)
     forward_num = self->gop.num_ref_frames;
   if (backward_num > self->gop.num_ref_frames)
     backward_num = self->gop.num_ref_frames;
 
-
   if (forward_num == 0) {
     GST_INFO_OBJECT (self,
         "No reference support, fallback to intra only stream");
@@ -3261,6 +3319,8 @@ create_poc:
       self->gop.num_bframes, PROP_BFRAMES);
   update_property_bool (base, &self->prop.b_pyramid,
       self->gop.b_pyramid, PROP_B_PYRAMID);
+
+  return TRUE;
 }
 
 static gboolean
@@ -3770,7 +3830,8 @@ gst_va_h265_enc_reconfig (GstVaBaseEnc * base)
   if (!_h265_calculate_tier_level (self))
     return FALSE;
 
-  _h265_generate_gop_structure (self);
+  if (!_h265_generate_gop_structure (self))
+    return FALSE;
 
   _h265_setup_encoding_features (self);