meta-digi/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0065-codecs-h264encoder-Use...

579 lines
19 KiB
Diff

From 8455dc4d016d32559a5da33e6b03d860e33c4800 Mon Sep 17 00:00:00 2001
From: Denis Shimizu <denis.shimizu@collabora.com>
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 <m.grzeschik@pengutronix.de>
+ * Copyright (C) 2023 Denis Shimizu <denis.shimizu@collabora.com>
*
* 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 <gst/codecparsers/gsth264bitwriter.h>
-#include <gst/video/video.h>
#include <gst/video/gstvideometa.h>
-#include <gst/base/base.h>
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 <m.grzeschik@pengutronix.de>
+ * Copyright (C) 2023 Denis Shimizu <denis.shimizu@collabora.com>
*
* 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 <config.h>
#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