h264encoder: implement bitrate control for CBR and VBR
authorWind Yuan <feng.yuan@intel.com>
Mon, 5 Nov 2012 07:22:28 +0000 (15:22 +0800)
committerZhong Cong <congx.zhong@intel.com>
Tue, 5 Feb 2013 07:37:12 +0000 (15:37 +0800)
gst-libs/gst/vaapi/gstvaapiencoder_h264.c
gst-libs/gst/vaapi/gstvaapiencoder_h264.h
gst/vaapi/gstvaapiencode_h264.c

index 93779e7..2686bbd 100644 (file)
@@ -102,6 +102,7 @@ struct _GstVaapiEncoderH264Private {
   VABufferID        seq_param_id;
   VABufferID        pic_param_id;
   VABufferID        slice_param_id;
+  VABufferID        misc_param_hdr_id;
   VABufferID        packed_seq_param_id;
   VABufferID        packed_seq_data_id;
   VABufferID        packed_pic_param_id;
@@ -284,7 +285,10 @@ gst_vaapi_encoder_h264_validate_attributes(GstVaapiBaseEncoder *base)
     encoder->init_qp = H264_DEFAULT_INIT_QP;
   }
   if (-1 == encoder->min_qp) {
-    encoder->min_qp = H264_DEFAULT_MIN_QP;
+    if (GST_VAAPI_RATECONTROL_CQP == ENCODER_RATE_CONTROL(encoder))
+      encoder->min_qp = encoder->init_qp;
+    else
+      encoder->min_qp = H264_DEFAULT_MIN_QP;
   }
 
   if (encoder->min_qp > encoder->init_qp) {
@@ -292,9 +296,16 @@ gst_vaapi_encoder_h264_validate_attributes(GstVaapiBaseEncoder *base)
   }
 
   /* default compress ratio 1: (4*8*1.5) */
-  if (!encoder->bitrate) {
-    encoder->bitrate = 0; //ENCODER_WIDTH(encoder)*ENCODER_HEIGHT(encoder)*ENCODER_FPS(encoder)/4;
-  }
+  if (GST_VAAPI_RATECONTROL_CBR == ENCODER_RATE_CONTROL(encoder) ||
+      GST_VAAPI_RATECONTROL_VBR == ENCODER_RATE_CONTROL(encoder) ||
+      GST_VAAPI_RATECONTROL_VBR_CONSTRAINED == ENCODER_RATE_CONTROL(encoder))
+  {
+    if (!encoder->bitrate)
+      encoder->bitrate = ENCODER_WIDTH(encoder) *
+                         ENCODER_HEIGHT(encoder) *
+                         ENCODER_FPS(encoder) / 4 / 1024;
+  } else
+    encoder->bitrate = 0;
 
   if (!encoder->slice_num) {
     encoder->slice_num = H264_DEFAULT_SLICE_NUM;
@@ -357,6 +368,10 @@ h264_encoder_release_parameters(GstVaapiEncoderH264 *encoder)
     va_status = vaDestroyBuffer(va_dpy, priv->slice_param_id);
     priv->slice_param_id = VA_INVALID_ID;
   }
+  if (VA_INVALID_ID != priv->misc_param_hdr_id) {
+    va_status = vaDestroyBuffer(va_dpy, priv->misc_param_hdr_id);
+    priv->misc_param_hdr_id = VA_INVALID_ID;
+  }
 
   if (VA_INVALID_ID != priv->packed_seq_param_id) {
     va_status = vaDestroyBuffer(va_dpy, priv->packed_seq_param_id);
@@ -611,7 +626,7 @@ set_sequence_parameters(
   seq_param->picture_width_in_mbs = (ENCODER_WIDTH(encoder)+15)/16;
   seq_param->picture_height_in_mbs = (ENCODER_HEIGHT(encoder)+15)/16;
 
-  seq_param->bits_per_second = encoder->bitrate;
+  seq_param->bits_per_second = encoder->bitrate * 1024;
   seq_param->frame_rate = ENCODER_FPS(encoder);
   seq_param->initial_qp = encoder->init_qp; /*qp_value; 15, 24, 26?*/
   seq_param->min_qp = encoder->min_qp;     /*1, 6, 10*/
@@ -693,7 +708,7 @@ set_sequence_parameters(
   seq_param->intra_period = encoder->intra_period;
   seq_param->ip_period = 0;           // ?
   if (encoder->bitrate> 0)
-      seq_param->bits_per_second = encoder->bitrate; /* use kbps as input */
+      seq_param->bits_per_second = encoder->bitrate * 1024;
   else
       seq_param->bits_per_second = 0;
 
@@ -754,7 +769,16 @@ set_sequence_parameters(
 #endif
 
   /*vui not set*/
-  seq_param->vui_parameters_present_flag = FALSE;
+  seq_param->vui_parameters_present_flag = (encoder->bitrate> 0 ? TRUE : FALSE);
+  if (seq_param->vui_parameters_present_flag) {
+    seq_param->vui_fields.bits.aspect_ratio_info_present_flag = FALSE;
+    seq_param->vui_fields.bits.bitstream_restriction_flag = FALSE;
+    seq_param->vui_fields.bits.timing_info_present_flag = (encoder->bitrate> 0 ? TRUE : FALSE);
+    if (seq_param->vui_fields.bits.timing_info_present_flag) {
+      seq_param->num_units_in_tick = 100;
+      seq_param->time_scale = ENCODER_FPS(encoder)*2*100;
+    }
+  }
   return TRUE;
 }
 
@@ -834,7 +858,7 @@ set_picture_parameters(
   pic_param->frame_num = (priv->cur_slice_type == SLICE_TYPE_B ?
                        (priv->cur_decode_num + 1) : priv->cur_decode_num);
   //pic_param.coding_type = 0;
-  pic_param->pic_init_qp = (encoder->init_qp >= 0 ? encoder->init_qp : 26);
+  pic_param->pic_init_qp = encoder->init_qp;
   pic_param->num_ref_idx_l0_active_minus1 = 0; /* only 1 reference */
   pic_param->num_ref_idx_l1_active_minus1 = 0; /* B frames only have 1 backward and 1 forward reference*/
   pic_param->chroma_qp_index_offset = 0;
@@ -1015,7 +1039,9 @@ set_slices_parameters(
     memset(slice_param->chroma_offset_l1, 0, sizeof(slice_param->chroma_offset_l1));
 
     slice_param->cabac_init_idc = 0;
-    slice_param->slice_qp_delta = 0;
+    slice_param->slice_qp_delta = encoder->init_qp - encoder->min_qp;
+    if (slice_param->slice_qp_delta > 4)
+      slice_param->slice_qp_delta = 4;
     slice_param->disable_deblocking_filter_idc = 0;
     slice_param->slice_alpha_c0_offset_div2 = 2;
     slice_param->slice_beta_offset_div2 = 2;
@@ -1025,6 +1051,57 @@ set_slices_parameters(
   return TRUE;
 }
 
+static gboolean
+h264_fill_hdr_buffer(GstVaapiEncoderH264 *encoder)
+{
+  GstVaapiEncoderH264Private *priv = encoder->priv;
+  VAEncMiscParameterBuffer *misc_param;
+  VAEncMiscParameterHRD *misc_hrd_param;
+  VADisplay va_dpy       = ENCODER_VA_DISPLAY(encoder);
+  VAContextID context_id = ENCODER_VA_CONTEXT(encoder);
+  gboolean ret = TRUE;
+  VAStatus va_status = VA_STATUS_SUCCESS;
+
+  if (VA_INVALID_ID != priv->misc_param_hdr_id) {
+    vaDestroyBuffer(va_dpy, priv->misc_param_hdr_id);
+    priv->misc_param_hdr_id = VA_INVALID_ID;
+  }
+
+  /*  hrd parameter */
+  va_status = vaCreateBuffer(
+      va_dpy,
+      context_id,
+      VAEncMiscParameterBufferType,
+      sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterRateControl),
+      1,
+      NULL,
+      &priv->misc_param_hdr_id);
+  ENCODER_CHECK_STATUS(VA_STATUS_SUCCESS == va_status,
+                       FALSE,
+                       "vaCreateEncMiscParameterBuffer failed");
+
+  va_status = vaMapBuffer(va_dpy,
+                          priv->misc_param_hdr_id,
+                          (void **)&misc_param);
+  ENCODER_CHECK_STATUS(VA_STATUS_SUCCESS == va_status,
+                       FALSE,
+                       "H264 HDR buffer map failed");
+  misc_param->type = VAEncMiscParameterTypeHRD;
+  misc_hrd_param = (VAEncMiscParameterHRD *)misc_param->data;
+
+  if (encoder->bitrate > 0) {
+      misc_hrd_param->initial_buffer_fullness = encoder->bitrate * 1024 * 4;
+      misc_hrd_param->buffer_size = encoder->bitrate * 1024 * 8;
+  } else {
+      misc_hrd_param->initial_buffer_fullness = 0;
+      misc_hrd_param->buffer_size = 0;
+  }
+
+  vaUnmapBuffer(va_dpy, priv->misc_param_hdr_id);
+
+end:
+  return ret;
+}
 #endif
 
 static gboolean
@@ -1196,6 +1273,15 @@ gst_vaapi_encoder_h264_rendering(
   ENCODER_CHECK_STATUS(is_params_ok,
                        ENCODER_PARAMETER_ERR,
                        "h264_recreate_pic_param failed");
+
+#if !HAVE_OLD_H264_ENCODER
+  /* set misc_hdr_parameters*/
+  is_params_ok = h264_fill_hdr_buffer(encoder);
+  ENCODER_CHECK_STATUS(is_params_ok,
+                       ENCODER_PARAMETER_ERR,
+                       "h264_fill_hdr__param failed");
+#endif
+
   /* set slice parameters, support multiple slices */
   is_params_ok = h264_fill_slice_buffers(encoder);
   ENCODER_CHECK_STATUS(is_params_ok,
@@ -1219,6 +1305,9 @@ gst_vaapi_encoder_h264_rendering(
   if (VA_INVALID_ID != priv->pic_param_id) {
     va_buffers[va_buffers_count++] = priv->pic_param_id;
   }
+  if (VA_INVALID_ID != priv->misc_param_hdr_id) {
+    va_buffers[va_buffers_count++] = priv->misc_param_hdr_id;
+  }
   if (VA_INVALID_ID != priv->slice_param_id) {
     va_buffers[va_buffers_count++] = priv->slice_param_id;
   }
@@ -1563,6 +1652,7 @@ gst_vaapi_encoder_h264_init(GstVaapiEncoderH264 *encoder)
   priv->seq_param_id = VA_INVALID_ID;
   priv->pic_param_id = VA_INVALID_ID;
   priv->slice_param_id = VA_INVALID_ID;
+  priv->misc_param_hdr_id = VA_INVALID_ID;
   priv->packed_seq_param_id = VA_INVALID_ID;
   priv->packed_seq_data_id = VA_INVALID_ID;
   priv->packed_pic_param_id = VA_INVALID_ID;
@@ -1858,6 +1948,7 @@ h264_bitstream_write_sps(
   guint32 constraint_set0_flag, constraint_set1_flag;
   guint32 constraint_set2_flag, constraint_set3_flag;
   guint32 gaps_in_frame_num_value_allowed_flag = 0; // ??
+  gboolean nal_hrd_parameters_present_flag;
 
   guint32 b_qpprime_y_zero_transform_bypass = 0;
   guint32 residual_color_transform_flag = 0;
@@ -1984,12 +2075,76 @@ h264_bitstream_write_sps(
       /* frame_crop_bottom_offset */
       h264_bitstream_write_ue(bitstream, seq->frame_crop_bottom_offset);
   }
-  ENCODER_ASSERT(seq->vui_parameters_present_flag == FALSE);
 
   /* vui_parameters_present_flag */
   h264_bitstream_write_uint(bitstream, seq->vui_parameters_present_flag, 1);
   if (seq->vui_parameters_present_flag) {
-    /*FIXME, to write vui parameters*/
+    /* aspect_ratio_info_present_flag */
+    h264_bitstream_write_uint(bitstream,
+                              seq->vui_fields.bits.aspect_ratio_info_present_flag,
+                              1);
+    if (seq->vui_fields.bits.aspect_ratio_info_present_flag) {
+      h264_bitstream_write_uint(bitstream, seq->aspect_ratio_idc, 8);
+      if (seq->aspect_ratio_idc == 0xFF) {
+        h264_bitstream_write_uint(bitstream, seq->sar_width, 16);
+        h264_bitstream_write_uint(bitstream, seq->sar_height, 16);
+      }
+    }
+
+    /* overscan_info_present_flag */
+    h264_bitstream_write_uint(bitstream, 0, 1);
+    /* video_signal_type_present_flag */
+    h264_bitstream_write_uint(bitstream, 0, 1);
+    /* chroma_loc_info_present_flag */
+    h264_bitstream_write_uint(bitstream, 0, 1);
+
+    /* timing_info_present_flag */
+    h264_bitstream_write_uint(bitstream,
+                              seq->vui_fields.bits.timing_info_present_flag,
+                              1);
+    if (seq->vui_fields.bits.timing_info_present_flag) {
+      h264_bitstream_write_uint(bitstream, seq->num_units_in_tick, 32);
+      h264_bitstream_write_uint(bitstream, seq->time_scale, 32);
+      h264_bitstream_write_uint(bitstream, 1, 1); /* fixed_frame_rate_flag */
+    }
+
+    nal_hrd_parameters_present_flag = (seq->bits_per_second > 0 ? TRUE : FALSE);
+    /* nal_hrd_parameters_present_flag */
+    h264_bitstream_write_uint(bitstream, nal_hrd_parameters_present_flag, 1);
+    if (nal_hrd_parameters_present_flag) {
+      /* hrd_parameters */
+      /* cpb_cnt_minus1 */
+      h264_bitstream_write_ue(bitstream, 0);
+      h264_bitstream_write_uint(bitstream, 4, 4); /* bit_rate_scale */
+      h264_bitstream_write_uint(bitstream, 6, 4); /* cpb_size_scale */
+
+      for (i = 0; i < 1; ++i) {
+        /* bit_rate_value_minus1[0] */
+        h264_bitstream_write_ue(bitstream, seq->bits_per_second/1024- 1);
+        /* cpb_size_value_minus1[0] */
+        h264_bitstream_write_ue(bitstream, seq->bits_per_second/1024*8 - 1);
+        /* cbr_flag[0] */
+        h264_bitstream_write_uint(bitstream, 1, 1);
+      }
+      /* initial_cpb_removal_delay_length_minus1 */
+      h264_bitstream_write_uint(bitstream, 23, 5);
+      /* cpb_removal_delay_length_minus1 */
+      h264_bitstream_write_uint(bitstream, 23, 5);
+      /* dpb_output_delay_length_minus1 */
+      h264_bitstream_write_uint(bitstream, 23, 5);
+      /* time_offset_length  */
+      h264_bitstream_write_uint(bitstream, 23, 5);
+    }
+    /* vcl_hrd_parameters_present_flag */
+    h264_bitstream_write_uint(bitstream, 0, 1);
+    if (nal_hrd_parameters_present_flag || 0/*vcl_hrd_parameters_present_flag*/) {
+      /* low_delay_hrd_flag */
+      h264_bitstream_write_uint(bitstream, 0, 1);
+    }
+    /* pic_struct_present_flag */
+    h264_bitstream_write_uint(bitstream, 0, 1);
+    /* bitstream_restriction_flag */
+    h264_bitstream_write_uint(bitstream, 0, 1);
   }
   /* rbsp_trailing_bits */
   h264_bitstream_write_trailing_bits(bitstream);
index 2235700..2de9769 100644 (file)
@@ -91,7 +91,7 @@ typedef enum {
 
 #define H264_DEFAULT_PROFILE      H264_PROFILE_BASELINE
 #define H264_DEFAULT_LEVEL        H264_LEVEL_30
-#define H264_DEFAULT_INIT_QP      24
+#define H264_DEFAULT_INIT_QP      26
 #define H264_DEFAULT_MIN_QP       1
 #define H264_DEFAULT_INTRA_PERIOD 30
 #define H264_DEFAULT_FPS          30
@@ -104,7 +104,7 @@ struct _GstVaapiEncoderH264 {
 
   guint32         profile;
   guint32         level;
-  guint32         bitrate;
+  guint32         bitrate;  /*kbps*/
   guint32         intra_period;
   guint32         init_qp;  /*default 24*/
   guint32         min_qp;   /*default 1*/
index 9df2143..6ebfad1 100644 (file)
@@ -389,10 +389,10 @@ gst_vaapi_encode_h264_class_init(GstVaapiEncodeH264Class *klass)
       H264_PROP_BITRATE,
       g_param_spec_uint (
           "bitrate",
-          "H264 encoding bitrate",
-          "H264 encoding bitrate, 10k~100M, (0, auto-calculate)",
+          "H264 encoding bitrate(kbps)",
+          "H264 encoding bitrate(kbps), (0, auto-calculate)",
           0,
-          100*1000*1000,
+          100*1024,
           0,
           G_PARAM_READWRITE));