From e997b20a8f4ee596ed533eeaa68a310dffc68914 Mon Sep 17 00:00:00 2001 From: Wind Yuan Date: Mon, 5 Nov 2012 15:22:28 +0800 Subject: [PATCH] h264encoder: implement bitrate control for CBR and VBR --- gst-libs/gst/vaapi/gstvaapiencoder_h264.c | 177 ++++++++++++++++++++++++++++-- gst-libs/gst/vaapi/gstvaapiencoder_h264.h | 4 +- gst/vaapi/gstvaapiencode_h264.c | 6 +- 3 files changed, 171 insertions(+), 16 deletions(-) diff --git a/gst-libs/gst/vaapi/gstvaapiencoder_h264.c b/gst-libs/gst/vaapi/gstvaapiencoder_h264.c index 93779e7..2686bbd 100644 --- a/gst-libs/gst/vaapi/gstvaapiencoder_h264.c +++ b/gst-libs/gst/vaapi/gstvaapiencoder_h264.c @@ -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); diff --git a/gst-libs/gst/vaapi/gstvaapiencoder_h264.h b/gst-libs/gst/vaapi/gstvaapiencoder_h264.h index 2235700..2de9769 100644 --- a/gst-libs/gst/vaapi/gstvaapiencoder_h264.h +++ b/gst-libs/gst/vaapi/gstvaapiencoder_h264.h @@ -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*/ diff --git a/gst/vaapi/gstvaapiencode_h264.c b/gst/vaapi/gstvaapiencode_h264.c index 9df2143..6ebfad1 100644 --- a/gst/vaapi/gstvaapiencode_h264.c +++ b/gst/vaapi/gstvaapiencode_h264.c @@ -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)); -- 2.7.4