encoder: h264: add support for packed slice headers.
authorSreerenj Balachandran <sreerenj.balachandran@intel.com>
Tue, 6 May 2014 21:09:45 +0000 (00:09 +0300)
committerGwenole Beauchesne <gwenole.beauchesne@intel.com>
Mon, 2 Jun 2014 16:25:13 +0000 (18:25 +0200)
https://bugzilla.gnome.org/show_bug.cgi?id=722905

gst-libs/gst/vaapi/gstvaapiencoder_h264.c

index 9140c75..be56bd0 100644 (file)
@@ -69,7 +69,8 @@
 /* Supported set of VA packed headers, within this implementation */
 #define SUPPORTED_PACKED_HEADERS                \
   (VA_ENC_PACKED_HEADER_SEQUENCE |              \
-   VA_ENC_PACKED_HEADER_PICTURE)
+   VA_ENC_PACKED_HEADER_PICTURE  |              \
+   VA_ENC_PACKED_HEADER_SLICE)
 
 #define GST_VAAPI_ENCODER_H264_NAL_REF_IDC_NONE        0
 #define GST_VAAPI_ENCODER_H264_NAL_REF_IDC_LOW         1
@@ -711,6 +712,8 @@ struct _GstVaapiEncoderH264
   guint32 max_pic_order_cnt;
   guint32 log2_max_pic_order_cnt;
   guint32 idr_num;
+  guint8 pic_order_cnt_type;
+  guint8 delta_pic_order_always_zero_flag;
 
   GstBuffer *sps_data;
   GstBuffer *subset_sps_data;
@@ -728,6 +731,124 @@ struct _GstVaapiEncoderH264
   GstVaapiH264ViewReorderPool reorder_pools[MAX_NUM_VIEWS];
 };
 
+/* Write a Slice NAL unit */
+static gboolean
+bs_write_slice (GstBitWriter * bs,
+    const VAEncSliceParameterBufferH264 * slice_param,
+    GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
+{
+  const VAEncPictureParameterBufferH264 *const pic_param = picture->param;
+  guint32 field_pic_flag = 0;
+  guint32 ref_pic_list_modification_flag_l0 = 0;
+  guint32 ref_pic_list_modification_flag_l1 = 0;
+  guint32 no_output_of_prior_pics_flag = 0;
+  guint32 long_term_reference_flag = 0;
+  guint32 adaptive_ref_pic_marking_mode_flag = 0;
+
+  /* first_mb_in_slice */
+  WRITE_UE (bs, slice_param->macroblock_address);
+  /* slice_type */
+  WRITE_UE (bs, slice_param->slice_type);
+  /* pic_parameter_set_id */
+  WRITE_UE (bs, slice_param->pic_parameter_set_id);
+  /* frame_num */
+  WRITE_UINT32 (bs, picture->frame_num, encoder->log2_max_frame_num);
+
+  /* XXX: only frames (i.e. non-interlaced) are supported for now */
+  /* frame_mbs_only_flag == 0 */
+
+  /* idr_pic_id */
+  if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
+    WRITE_UE (bs, slice_param->idr_pic_id);
+
+  /* XXX: only POC type 0 is supported */
+  if (!encoder->pic_order_cnt_type) {
+    WRITE_UINT32 (bs, slice_param->pic_order_cnt_lsb,
+        encoder->log2_max_pic_order_cnt);
+    /* bottom_field_pic_order_in_frame_present_flag is FALSE */
+    if (pic_param->pic_fields.bits.pic_order_present_flag && !field_pic_flag)
+      WRITE_SE (bs, slice_param->delta_pic_order_cnt_bottom);
+  } else if (encoder->pic_order_cnt_type == 1 &&
+      !encoder->delta_pic_order_always_zero_flag) {
+    WRITE_SE (bs, slice_param->delta_pic_order_cnt[0]);
+    if (pic_param->pic_fields.bits.pic_order_present_flag && !field_pic_flag)
+      WRITE_SE (bs, slice_param->delta_pic_order_cnt[1]);
+  }
+  /* redundant_pic_cnt_present_flag is FALSE, no redundant coded pictures */
+
+  /* only works for B-frames */
+  if (slice_param->slice_type == 1)
+    WRITE_UINT32 (bs, slice_param->direct_spatial_mv_pred_flag, 1);
+
+  /* not supporting SP slices */
+  if (slice_param->slice_type == 0 || slice_param->slice_type == 1) {
+    WRITE_UINT32 (bs, slice_param->num_ref_idx_active_override_flag, 1);
+    if (slice_param->num_ref_idx_active_override_flag) {
+      WRITE_UE (bs, slice_param->num_ref_idx_l0_active_minus1);
+      if (slice_param->slice_type == 1)
+        WRITE_UE (bs, slice_param->num_ref_idx_l1_active_minus1);
+    }
+  }
+  /* XXX: not supporting custom reference picture list modifications */
+  if ((slice_param->slice_type != 2) && (slice_param->slice_type != 4))
+    WRITE_UINT32 (bs, ref_pic_list_modification_flag_l0, 1);
+  if (slice_param->slice_type == 1)
+    WRITE_UINT32 (bs, ref_pic_list_modification_flag_l1, 1);
+
+  /* we have: weighted_pred_flag == FALSE and */
+  /*        : weighted_bipred_idc == FALSE */
+  if ((pic_param->pic_fields.bits.weighted_pred_flag &&
+          (slice_param->slice_type == 0)) ||
+      ((pic_param->pic_fields.bits.weighted_bipred_idc == 1) &&
+          (slice_param->slice_type == 1))) {
+    /* XXXX: add pred_weight_table() */
+  }
+
+  /* dec_ref_pic_marking() */
+  if (slice_param->slice_type == 0 || slice_param->slice_type == 2) {
+    if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture)) {
+      /* no_output_of_prior_pics_flag = 0 */
+      WRITE_UINT32 (bs, no_output_of_prior_pics_flag, 1);
+      /* long_term_reference_flag = 0 */
+      WRITE_UINT32 (bs, long_term_reference_flag, 1);
+    } else {
+      /* only sliding_window reference picture marking mode is supported */
+      /* adpative_ref_pic_marking_mode_flag = 0 */
+      WRITE_UINT32 (bs, adaptive_ref_pic_marking_mode_flag, 1);
+    }
+  }
+
+  /* cabac_init_idc */
+  if (pic_param->pic_fields.bits.entropy_coding_mode_flag &&
+      slice_param->slice_type != 2)
+    WRITE_UE (bs, slice_param->cabac_init_idc);
+  /*slice_qp_delta */
+  WRITE_SE (bs, slice_param->slice_qp_delta);
+
+  /* XXX: only supporting I, P and B type slices */
+  /* no sp_for_switch_flag and no slice_qs_delta */
+
+  if (pic_param->pic_fields.bits.deblocking_filter_control_present_flag) {
+    /* disable_deblocking_filter_idc */
+    WRITE_UE (bs, slice_param->disable_deblocking_filter_idc);
+    if (slice_param->disable_deblocking_filter_idc != 1) {
+      WRITE_SE (bs, slice_param->slice_alpha_c0_offset_div2);
+      WRITE_SE (bs, slice_param->slice_beta_offset_div2);
+    }
+  }
+
+  /* XXX: unsupported arbitrary slice ordering (ASO) */
+  /* num_slic_groups_minus1 should be zero */
+  return TRUE;
+
+  /* ERRORS */
+bs_error:
+  {
+    GST_WARNING ("failed to write Slice NAL unit");
+    return FALSE;
+  }
+}
+
 static inline void
 _check_sps_pps_status (GstVaapiEncoderH264 * encoder,
     const guint8 * nal, guint32 size)
@@ -1235,6 +1356,81 @@ bs_error:
   }
 }
 
+static gboolean
+get_nal_hdr_attributes (GstVaapiEncPicture * picture,
+    guint8 * nal_ref_idc, guint8 * nal_unit_type)
+{
+  switch (picture->type) {
+    case GST_VAAPI_PICTURE_TYPE_I:
+      *nal_ref_idc = GST_VAAPI_ENCODER_H264_NAL_REF_IDC_HIGH;
+      if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
+        *nal_unit_type = GST_VAAPI_ENCODER_H264_NAL_IDR;
+      else
+        *nal_unit_type = GST_VAAPI_ENCODER_H264_NAL_NON_IDR;
+      break;
+    case GST_VAAPI_PICTURE_TYPE_P:
+      *nal_ref_idc = GST_VAAPI_ENCODER_H264_NAL_REF_IDC_MEDIUM;
+      *nal_unit_type = GST_VAAPI_ENCODER_H264_NAL_NON_IDR;
+      break;
+    case GST_VAAPI_PICTURE_TYPE_B:
+      *nal_ref_idc = GST_VAAPI_ENCODER_H264_NAL_REF_IDC_NONE;
+      *nal_unit_type = GST_VAAPI_ENCODER_H264_NAL_NON_IDR;
+      break;
+    default:
+      return FALSE;
+  }
+  return TRUE;
+}
+
+/* Adds the supplied slice header to the list of packed
+   headers to pass down as-is to the encoder */
+static gboolean
+add_packed_slice_header (GstVaapiEncoderH264 * encoder,
+    GstVaapiEncPicture * picture, GstVaapiEncSlice * slice)
+{
+  GstVaapiEncPackedHeader *packed_slice;
+  GstBitWriter bs;
+  VAEncPackedHeaderParameterBuffer packed_slice_param = { 0 };
+  const VAEncSliceParameterBufferH264 *const slice_param = slice->param;
+  guint32 data_bit_size;
+  guint8 *data;
+  guint8 nal_ref_idc, nal_unit_type;
+
+  gst_bit_writer_init (&bs, 128 * 8);
+  WRITE_UINT32 (&bs, 0x00000001, 32);   /* start code */
+
+  if (!get_nal_hdr_attributes (picture, &nal_ref_idc, &nal_unit_type))
+    goto bs_error;
+  bs_write_nal_header (&bs, nal_ref_idc, nal_unit_type);
+
+  bs_write_slice (&bs, slice_param, encoder, picture);
+  data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
+  data = GST_BIT_WRITER_DATA (&bs);
+
+  packed_slice_param.type = VAEncPackedHeaderSlice;
+  packed_slice_param.bit_length = data_bit_size;
+  packed_slice_param.has_emulation_bytes = 0;
+
+  packed_slice = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
+      &packed_slice_param, sizeof (packed_slice_param),
+      data, (data_bit_size + 7) / 8);
+  g_assert (packed_slice);
+
+  gst_vaapi_enc_picture_add_packed_header (picture, packed_slice);
+  gst_vaapi_codec_object_replace (&packed_slice, NULL);
+
+  gst_bit_writer_clear (&bs, TRUE);
+  return TRUE;
+
+  /* ERRORS */
+bs_error:
+  {
+    GST_WARNING ("failed to write Slice NAL unit header");
+    gst_bit_writer_clear (&bs, TRUE);
+    return FALSE;
+  }
+}
+
 /* Reference picture management */
 static void
 reference_pic_free (GstVaapiEncoderH264 * encoder, GstVaapiEncoderH264Ref * ref)
@@ -1369,7 +1565,8 @@ fill_sequence (GstVaapiEncoderH264 * encoder, GstVaapiEncSequence * sequence)
   seq_param->seq_fields.bits.log2_max_frame_num_minus4 =
       encoder->log2_max_frame_num - 4;
   /* picture order count */
-  seq_param->seq_fields.bits.pic_order_cnt_type = 0;
+  encoder->pic_order_cnt_type = seq_param->seq_fields.bits.pic_order_cnt_type =
+      0;
   g_assert (encoder->log2_max_pic_order_cnt >= 4);
   seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 =
       encoder->log2_max_pic_order_cnt - 4;
@@ -1379,7 +1576,8 @@ fill_sequence (GstVaapiEncoderH264 * encoder, GstVaapiEncSequence * sequence)
 
   /* not used if pic_order_cnt_type == 0 */
   if (seq_param->seq_fields.bits.pic_order_cnt_type == 1) {
-    seq_param->seq_fields.bits.delta_pic_order_always_zero_flag = TRUE;
+    encoder->delta_pic_order_always_zero_flag =
+        seq_param->seq_fields.bits.delta_pic_order_always_zero_flag = TRUE;
     seq_param->num_ref_frames_in_pic_order_cnt_cycle = 0;
     seq_param->offset_for_non_ref_pic = 0;
     seq_param->offset_for_top_to_bottom_field = 0;
@@ -1750,11 +1948,23 @@ add_slice_headers (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture,
     /* set calculation for next slice */
     last_mb_index += cur_slice_mbs;
 
+    if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
+            VAEncPackedHeaderH264_Slice)
+        && !add_packed_slice_header (encoder, picture, slice))
+      goto error_create_packed_slice_hdr;
+
     gst_vaapi_enc_picture_add_slice (picture, slice);
     gst_vaapi_codec_object_replace (&slice, NULL);
   }
   g_assert (last_mb_index == mb_size);
   return TRUE;
+
+error_create_packed_slice_hdr:
+  {
+    GST_ERROR ("failed to create packed slice header buffer");
+    gst_vaapi_codec_object_replace (&slice, NULL);
+    return FALSE;
+  }
 }
 
 /* Generates and submits SPS header accordingly into the bitstream */