From 8455dc4d016d32559a5da33e6b03d860e33c4800 Mon Sep 17 00:00:00 2001 From: Denis Shimizu Date: Wed, 15 Nov 2023 16:59:14 -0300 Subject: [PATCH 65/68] codecs: h264encoder: Use the PID base rate controller This port the h264encoder to use the generic rate controller and adapt the v4l2codecs accordingly. Upstream-Status: Pending --- gst-libs/gst/codecs/gsth264encoder.c | 256 +++++++++++++-------------- gst-libs/gst/codecs/gsth264frame.h | 2 +- sys/v4l2codecs/gstv4l2codech264enc.c | 27 +-- 3 files changed, 143 insertions(+), 142 deletions(-) diff --git a/gst-libs/gst/codecs/gsth264encoder.c b/gst-libs/gst/codecs/gsth264encoder.c index 032b57a..f1b0200 100644 --- a/gst-libs/gst/codecs/gsth264encoder.c +++ b/gst-libs/gst/codecs/gsth264encoder.c @@ -1,5 +1,6 @@ /* GStreamer * Copyright (C) 2023 Michael Grzeschik + * Copyright (C) 2023 Denis Shimizu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,47 +23,44 @@ #endif #include "gsth264encoder.h" - +#include "gstratecontroller.h" #include -#include #include -#include GST_DEBUG_CATEGORY (gst_h264_encoder_debug); #define GST_CAT_DEFAULT gst_h264_encoder_debug -#define H264ENC_DEFAULT_KEYFRAME_INTERVAL 30 - -#define H264_MAX_QUALITY 51 -#define H264_MIN_QUALITY 0 +#define H264_MAX_QP 51 +#define H264_MIN_QP 0 -#define H264_DEFAULT_BITRATE 100000 - -#define H264ENC_DEFAULT_CABAC_IDC 0 +#define DEFAULT_KEYFRAME_INTERVAL 30 +#define DEFAULT_MAX_QP 51 +#define DEFAULT_MIN_QP 10 +#define DEFAULT_QP_STEP 4 +#define DEFAULT_QUANTIZER 18 +#define DEFAULT_BITRATE G_MAXUINT enum { PROP_0, PROP_KEYFRAME_INTERVAL, - PROP_MAX_QUALITY, - PROP_MIN_QUALITY, + PROP_MAX_QP, + PROP_MIN_QP, + PROP_QP_STEP, + PROP_QUANTIZER, PROP_BITRATE, PROP_CABAC, PROP_CABAC_INIT_IDC, + PROP_RATE_CONTROL, }; struct _GstH264EncoderPrivate { - gint keyframe_interval; - guint32 last_keyframe; + GstRateController *rate_controller; - guint64 targeted_bitrate; - gint max_quality; - gint min_quality; - gint current_quality; - guint64 used_bytes; - guint64 nb_frames; + /* properties */ + gint keyframe_interval; gboolean cabac; guint cabac_init_idc; }; @@ -78,25 +76,22 @@ static void gst_h264_encoder_init (GstH264Encoder * self) { self->priv = gst_h264_encoder_get_instance_private (self); + self->priv->rate_controller = gst_rc_new (); } static void gst_h264_encoder_finalize (GObject * object) { + GstH264Encoder *self = GST_H264_ENCODER (object); + + gst_object_unref (self->priv->rate_controller); + G_OBJECT_CLASS (parent_class)->finalize (object); } static gboolean gst_h264_encoder_start (GstVideoEncoder * encoder) { - GstH264Encoder *self = GST_H264_ENCODER (encoder); - GstH264EncoderPrivate *priv = self->priv; - - priv->last_keyframe = 0; - priv->current_quality = priv->min_quality; - priv->used_bytes = 0; - priv->nb_frames = 0; - return TRUE; } @@ -110,46 +105,12 @@ static gboolean gst_h264_encoder_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state) { - return TRUE; -} - -static GstFlowReturn -gst_h264_encoder_set_quality (GstH264Encoder * self, GstH264Frame * h264_frame) -{ + GstH264Encoder *self = GST_H264_ENCODER (encoder); GstH264EncoderPrivate *priv = self->priv; - GstVideoEncoder *encoder = GST_VIDEO_ENCODER (self); - GstVideoCodecState *output_state = - gst_video_encoder_get_output_state (encoder); - gint qp = priv->current_quality; - guint64 bitrate = 0; - guint fps_n = 30, fps_d = 1; - - if (output_state == NULL) - return qp; - - if (GST_VIDEO_INFO_FPS_N (&output_state->info) != 0) { - fps_n = GST_VIDEO_INFO_FPS_N (&output_state->info); - fps_d = GST_VIDEO_INFO_FPS_D (&output_state->info); - } - gst_video_codec_state_unref (output_state); - - bitrate = (priv->used_bytes * 8 * fps_n) / (priv->nb_frames * fps_d); - if (bitrate > priv->targeted_bitrate) { - qp++; - } - - if (bitrate < priv->targeted_bitrate) { - qp--; - } - if (qp > priv->max_quality) - qp = priv->max_quality; - if (qp < priv->min_quality) - qp = priv->min_quality; + gst_rc_set_format (priv->rate_controller, &state->info); - h264_frame->quality = qp; - - return GST_FLOW_OK; + return TRUE; } static GstFlowReturn @@ -183,16 +144,19 @@ gst_h264_encoder_mark_frame (GstH264Encoder * self, GstH264Frame * h264_frame) { GstVideoCodecFrame *frame = h264_frame->frame; GstH264EncoderPrivate *priv = self->priv; + GstRcFrameType rc_frame_type = GST_RC_INTER_FRAME; switch (h264_frame->type) { case GstH264Keyframe: priv->last_keyframe = frame->system_frame_number; + rc_frame_type = GST_RC_KEY_FRAME; + break; + default: break; } - priv->current_quality = h264_frame->quality; - priv->used_bytes += gst_buffer_get_size (frame->output_buffer); - priv->nb_frames++; + gst_rc_record (priv->rate_controller, rc_frame_type, + gst_buffer_get_size (frame->output_buffer), frame->duration); } static GstFlowReturn @@ -200,6 +164,7 @@ gst_h264_encoder_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame) { GstH264Encoder *self = GST_H264_ENCODER (encoder); + GstH264EncoderPrivate *priv = self->priv; GstH264EncoderClass *klass = GST_H264_ENCODER_GET_CLASS (self); GstFlowReturn ret = GST_FLOW_OK; GstH264Frame *h264_frame = gst_h264_frame_new (frame); @@ -208,12 +173,9 @@ gst_h264_encoder_handle_frame (GstVideoEncoder * encoder, if (ret != GST_FLOW_OK) return ret; - ret = gst_h264_encoder_set_quality (self, h264_frame); - if (ret != GST_FLOW_OK) - return ret; + h264_frame->qp = gst_rc_get_qp (priv->rate_controller); - /* TODO: add encoding parameters management here - * for now just send the frame to encode */ + /* Send the frame to encode */ if (klass->encode_frame) { ret = klass->encode_frame (self, h264_frame); if (ret == GST_FLOW_OK) @@ -232,41 +194,42 @@ gst_h264_encoder_get_property (GObject * object, guint property_id, GstH264Encoder *self = GST_H264_ENCODER (object); GstH264EncoderPrivate *priv = self->priv; + GST_OBJECT_LOCK (self); + switch (property_id) { case PROP_KEYFRAME_INTERVAL: - GST_OBJECT_LOCK (self); g_value_set_int (value, priv->keyframe_interval); - GST_OBJECT_UNLOCK (self); break; - case PROP_MAX_QUALITY: - GST_OBJECT_LOCK (self); - g_value_set_int (value, priv->max_quality); - GST_OBJECT_UNLOCK (self); + case PROP_MAX_QP: + g_value_set_int (value, gst_rc_get_max_qp (priv->rate_controller)); + break; + case PROP_MIN_QP: + g_value_set_int (value, gst_rc_get_min_qp (priv->rate_controller)); break; - case PROP_MIN_QUALITY: - GST_OBJECT_LOCK (self); - g_value_set_int (value, priv->min_quality); - GST_OBJECT_UNLOCK (self); + case PROP_QP_STEP: + g_value_set_int (value, gst_rc_get_qp_step (priv->rate_controller)); + break; + case PROP_QUANTIZER: + g_value_set_int (value, gst_rc_get_init_qp (priv->rate_controller)); break; case PROP_BITRATE: - GST_OBJECT_LOCK (self); - g_value_set_uint64 (value, priv->targeted_bitrate); - GST_OBJECT_UNLOCK (self); + g_value_set_uint (value, gst_rc_get_bitrate (priv->rate_controller)); + break; + case PROP_RATE_CONTROL: + g_value_set_enum (value, gst_rc_get_mode (priv->rate_controller)); break; case PROP_CABAC: - GST_OBJECT_LOCK (self); g_value_set_boolean (value, priv->cabac); - GST_OBJECT_UNLOCK (self); break; case PROP_CABAC_INIT_IDC: - GST_OBJECT_LOCK (self); g_value_set_uint (value, priv->cabac_init_idc); - GST_OBJECT_UNLOCK (self); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } + + GST_OBJECT_UNLOCK (self); } static void @@ -276,41 +239,42 @@ gst_h264_encoder_set_property (GObject * object, guint property_id, GstH264Encoder *self = GST_H264_ENCODER (object); GstH264EncoderPrivate *priv = self->priv; + GST_OBJECT_LOCK (self); + switch (property_id) { case PROP_KEYFRAME_INTERVAL: - GST_OBJECT_LOCK (self); priv->keyframe_interval = g_value_get_int (value); - GST_OBJECT_UNLOCK (self); break; - case PROP_MAX_QUALITY: - GST_OBJECT_LOCK (self); - priv->max_quality = g_value_get_int (value); - GST_OBJECT_UNLOCK (self); + case PROP_MAX_QP: + gst_rc_set_max_qp (priv->rate_controller, g_value_get_int (value)); + break; + case PROP_MIN_QP: + gst_rc_set_min_qp (priv->rate_controller, g_value_get_int (value)); + break; + case PROP_QP_STEP: + gst_rc_set_qp_step (priv->rate_controller, g_value_get_int (value)); break; - case PROP_MIN_QUALITY: - GST_OBJECT_LOCK (self); - priv->min_quality = g_value_get_int (value); - GST_OBJECT_UNLOCK (self); + case PROP_QUANTIZER: + gst_rc_set_init_qp (priv->rate_controller, g_value_get_int (value)); break; case PROP_BITRATE: - GST_OBJECT_LOCK (self); - priv->targeted_bitrate = g_value_get_uint64 (value); - GST_OBJECT_UNLOCK (self); + gst_rc_set_bitrate (priv->rate_controller, g_value_get_uint (value)); + break; + case PROP_RATE_CONTROL: + gst_rc_set_mode (priv->rate_controller, g_value_get_enum (value)); break; case PROP_CABAC: - GST_OBJECT_LOCK (self); priv->cabac = g_value_get_boolean (value); - GST_OBJECT_UNLOCK (self); break; case PROP_CABAC_INIT_IDC: - GST_OBJECT_LOCK (self); priv->cabac_init_idc = g_value_get_uint (value); - GST_OBJECT_UNLOCK (self); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } + + GST_OBJECT_UNLOCK (self); } static void @@ -332,69 +296,99 @@ gst_h264_encoder_class_init (GstH264EncoderClass * klass) /** * GstH264Encoder:keyframe-interval: * - * * Since: 1.2x */ g_object_class_install_property (object_class, PROP_KEYFRAME_INTERVAL, g_param_spec_int ("keyframe-interval", "Keyframe Interval", - "Interval between keyframes", - 0, G_MAXINT, H264ENC_DEFAULT_KEYFRAME_INTERVAL, + "Maximum distance in frames between IDR.", + 0, G_MAXINT, DEFAULT_KEYFRAME_INTERVAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); /** - * GstH264Encoder:max-quality: - * + * GstH264Encoder:qp-max: * * Since: 1.2x */ - g_object_class_install_property (object_class, PROP_MAX_QUALITY, - g_param_spec_int ("max-quality", "Max Quality Level", - "Set upper quality limit (lower number equates to higher quality but more bits)", - H264_MIN_QUALITY, H264_MAX_QUALITY, H264_MAX_QUALITY, + g_object_class_install_property (object_class, PROP_MAX_QP, + g_param_spec_int ("qp-max", "Max Quantizer Level", + "Set upper qp limit (lower number equates to higher quality but more bits)", + H264_MIN_QP, H264_MAX_QP, DEFAULT_MAX_QP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); /** - * GstH264Encoder:min-quality: + * GstH264Encoder:qp-min: * + * Since: 1.2x + */ + g_object_class_install_property (object_class, PROP_MIN_QP, + g_param_spec_int ("qp-min", "Min Quantizer Level", + "Set lower qp limit (lower number equates to higher quality but more bits)", + H264_MIN_QP, H264_MAX_QP, DEFAULT_MIN_QP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + + /** + * GstH264Encoder:qp-step: + * + * Since: 1.2x + */ + g_object_class_install_property (object_class, PROP_QP_STEP, + g_param_spec_int ("qp-step", "Max QP increase/decrease step", + "Set maximum value which qp value can be increase/decrease by the bitrate controller (Valid only with rate-control=cbr)", + H264_MIN_QP, H264_MAX_QP, DEFAULT_QP_STEP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + + /** + * GstH264Encoder:quantizer: * * Since: 1.2x */ - g_object_class_install_property (object_class, PROP_MIN_QUALITY, - g_param_spec_int ("min-quality", "Min Quality Level", - "Set lower quality limit (lower number equates to higher quality but more bits)", - H264_MIN_QUALITY, H264_MAX_QUALITY, H264_MIN_QUALITY, + g_object_class_install_property (object_class, PROP_QUANTIZER, + g_param_spec_int ("quantizer", "Quantizer Level", + "Set the qp value (lower number equates to higher quality but more bits, initial value for rate-control=cbr)", + H264_MIN_QP, H264_MAX_QP, DEFAULT_QUANTIZER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); /** * GstH264Encoder:bitrate: * - * * Since: 1.2x */ g_object_class_install_property (object_class, PROP_BITRATE, - g_param_spec_uint64 ("bitrate", "Targeted bitrate", - "Set bitrate target", - 0, UINT_MAX, H264_DEFAULT_BITRATE, + g_param_spec_uint ("bitrate", "Targeted bitrate", + "Set the targeted bitrate (in bit/s)", + 0, UINT_MAX, DEFAULT_BITRATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); /** * GstH264Encoder:cabac: - * + * Note: Supported only on main profile * * Since: 1.2x */ g_object_class_install_property (object_class, PROP_CABAC, - g_param_spec_boolean ("cabac", "CABAC", "Enable use of CABAC over CAVLC", - FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("cabac", "CABAC", + "Enable Cabac", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GstH264Encoder:cabac-init-idc: - * + * Note: Supported only on main profile * * Since: 1.2x */ g_object_class_install_property (object_class, PROP_CABAC_INIT_IDC, - g_param_spec_uint ("cabac-init-idc", "Initial CABAC table ID", - "Set initial CABAC table idc value", 0, 2, H264ENC_DEFAULT_CABAC_IDC, + g_param_spec_uint ("cabac-init-idc", "PROP_CABAC_INIT_IDC", + "Set Cabac init idc value", + 0, 2, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + + /** + * GstH264Encoder:rate-control: + * + * Since: 1.2x + */ + g_object_class_install_property (object_class, PROP_RATE_CONTROL, + g_param_spec_enum ("rate-control", "Rate Control Mode", + "Select rate control mode", gst_rate_control_mode_get_type (), + GST_RC_CONSTANT_QP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); } diff --git a/gst-libs/gst/codecs/gsth264frame.h b/gst-libs/gst/codecs/gsth264frame.h index 3c90d53..3a5a658 100644 --- a/gst-libs/gst/codecs/gsth264frame.h +++ b/gst-libs/gst/codecs/gsth264frame.h @@ -42,7 +42,7 @@ struct _GstH264Frame { GstMiniObject parent; gint type; - gint quality; + gint qp; GstVideoCodecFrame *frame; }; diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c index 967fd4d..7d52fba 100644 --- a/sys/v4l2codecs/gstv4l2codech264enc.c +++ b/sys/v4l2codecs/gstv4l2codech264enc.c @@ -1,5 +1,6 @@ /* GStreamer * Copyright (C) 2023 Michael Grzeschik + * Copyright (C) 2023 Denis Shimizu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,7 +21,6 @@ #ifdef HAVE_CONFIG_H #include #endif - #include "gstv4l2codecallocator.h" #include "gstv4l2codech264enc.h" #include "gstv4l2codecpool.h" @@ -79,6 +79,7 @@ struct _GstV4l2CodecH264Enc gint height; gint width_in_macroblocks; gint height_in_macroblocks; + gint qp_init, qp_max, qp_min; guint64 targeted_bitrate; gboolean cabac; guint cabac_init_idc; @@ -477,7 +478,7 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, gint n_levels = G_N_ELEMENTS (_h264_level_limits); GValue bitrate = G_VALUE_INIT; g_object_get_property (G_OBJECT (self), "bitrate", &bitrate); - self->targeted_bitrate = g_value_get_uint64 (&bitrate); + self->targeted_bitrate = g_value_get_uint (&bitrate); maximum_bitrate = self->targeted_bitrate; frame_size_in_macroblocks = self->width * self->height / 256; macroblocks_per_second = frame_size_in_macroblocks * state->info.fps_n / state->info.fps_d; // Assuming each macroblock is 16x16 @@ -571,8 +572,11 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, self->pps.weighted_bipred_idc = 0; /* Rate Control */ + GValue qp_init = G_VALUE_INIT; + g_object_get_property (G_OBJECT (self), "quantizer", &qp_init); + self->qp_init = g_value_get_int (&qp_init); self->pps.chroma_qp_index_offset = 4; - self->pps.pic_init_qp_minus26 = -13; + self->pps.pic_init_qp_minus26 = self->qp_init - 26; self->pps.second_chroma_qp_index_offset = self->pps.chroma_qp_index_offset; self->pps.deblocking_filter_control_present_flag = 1; self->pps.entropy_coding_mode_flag = self->cabac; @@ -649,6 +653,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, self->width = state->info.width; self->height = state->info.height; + GST_VIDEO_ENCODER_CLASS (parent_class)->set_format (encoder, state); + gst_v4l2_codec_h264_enc_buffers_allocation (encoder); self->width_in_macroblocks = (self->width + 15) / 16; self->height_in_macroblocks = (self->height + 15) / 16; @@ -684,6 +690,9 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder, return FALSE; } + g_object_get (self, "cabac", &self->cabac, "cabac-init-idc", + &self->cabac_init_idc, NULL); + gst_v4l2_codec_h264_enc_init_sps_pps (self, state); return TRUE; @@ -905,9 +914,9 @@ gst_v4l2_codec_h264_enc_fill_encode_rc (GstH264Encoder * encoder, GstV4l2CodecH264Enc *self = GST_V4L2_CODEC_H264_ENC (encoder); /* Rate Control */ - self->encode_rc.qp = h264_frame->quality; - self->encode_rc.qp_min = 0; - self->encode_rc.qp_max = 51; + self->encode_rc.qp = h264_frame->qp; + self->encode_rc.qp_min = self->qp_min; + self->encode_rc.qp_max = self->qp_max; } static GstFlowReturn @@ -950,8 +959,7 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, /* *INDENT-ON* */ }; - GST_DEBUG_OBJECT (self, "encode h264 frame with quality = %d", - h264_frame->quality); + GST_DEBUG_OBJECT (self, "encode h264 frame with qp = %d", h264_frame->qp); if (!gst_v4l2_codec_h264_enc_ensure_output_bitstream (self, frame)) { GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT, @@ -959,8 +967,7 @@ gst_v4l2_codec_h264_enc_encode_frame (GstH264Encoder * encoder, goto done; } - GST_DEBUG_OBJECT (self, "encode h264 frame with quality = %d", - h264_frame->quality); + GST_DEBUG_OBJECT (self, "encode h264 frame with qp = %d", h264_frame->qp); request = gst_v4l2_encoder_alloc_request (self->encoder, frame->system_frame_number, frame->input_buffer, frame->output_buffer); -- 2.25.1