meta-digi/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0064-codecs-Introduce-a-PID...

356 lines
9.7 KiB
Diff

From ab51f9d3d91b8210a4e8d5796eeaba21ccbda6d8 Mon Sep 17 00:00:00 2001
From: Denis Shimizu <denis.shimizu@collabora.com>
Date: Mon, 27 Nov 2023 11:25:54 -0500
Subject: [PATCH 64/68] codecs: Introduce a PID based rate controller
This is a simplistic rate controller using a PID and some boundaries.
This has only been tested for H.264 for now and may need some more work
to support various quantizer scale.
Upstream-Status: Pending
---
gst-libs/gst/codecs/gstratecontroller.c | 241 ++++++++++++++++++++++++
gst-libs/gst/codecs/gstratecontroller.h | 70 +++++++
gst-libs/gst/codecs/meson.build | 1 +
3 files changed, 312 insertions(+)
create mode 100644 gst-libs/gst/codecs/gstratecontroller.c
create mode 100644 gst-libs/gst/codecs/gstratecontroller.h
diff --git a/gst-libs/gst/codecs/gstratecontroller.c b/gst-libs/gst/codecs/gstratecontroller.c
new file mode 100644
index 0000000..42f7d0c
--- /dev/null
+++ b/gst-libs/gst/codecs/gstratecontroller.c
@@ -0,0 +1,241 @@
+/* GStreamer
+ * Copyright (C) 2023 Denis Yuji 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "gstratecontroller.h"
+
+#include <math.h>
+
+#define KP_CONST 0.0001
+#define KI_CONST 0.0000000
+#define KD_CONST 0.0000000
+
+typedef struct
+{
+ gdouble prev_error;
+ gdouble integral;
+} PIDController;
+
+struct _GstRateController
+{
+ GstObject parent;
+
+ PIDController pid;
+ gdouble qp;
+ gsize total_macroblocks;
+ GstClockTime frame_duration;
+
+ /* configuration */
+ gint max_qp;
+ gint min_qp;
+ gint init_qp;
+ gint qp_step;
+ GstRateControlMode mode;
+ gint bitrate;
+};
+
+GType
+gst_rate_control_mode_get_type (void)
+{
+ const static GEnumValue rate_control_modes[] = {
+ {GST_RC_CONSTANT_QP, "cqp", "Constant QP"},
+ {GST_RC_CONSTANT_BITRATE, "cbr", "Constant Bitrate"},
+ {0, NULL, NULL},
+ };
+ static GType rc_mode_type = 0;
+
+ if (g_once_init_enter (&rc_mode_type)) {
+ GType _type = g_enum_register_static ("GstRateControlMode",
+ rate_control_modes);
+ g_once_init_leave (&rc_mode_type, _type);
+ }
+
+ return rc_mode_type;
+}
+
+G_DEFINE_TYPE (GstRateController, gst_rc, G_TYPE_OBJECT);
+
+static void
+gst_rc_init (GstRateController * self)
+{
+}
+
+static void
+gst_rc_class_init (GstRateControllerClass * klass)
+{
+}
+
+GstRateController *
+gst_rc_new (void)
+{
+ return g_object_new (GST_RATE_CONTROLLER_TYPE, NULL);
+}
+
+static gdouble
+pid_control (GstRateController * self, gdouble measured_value,
+ gdouble target_bitrate)
+{
+ PIDController *pid = &self->pid;
+ gdouble error = target_bitrate - measured_value;
+ gdouble derivative = error - pid->prev_error;
+ pid->integral += error;
+ pid->prev_error = error;
+
+ /* Output is inverted, since QP and bitrate are inversely related (higher QP
+ * means lower bitrate) */
+ gdouble output =
+ 0.0 - (KP_CONST * error +
+ KI_CONST * pid->integral + KD_CONST * derivative);
+
+ return output;
+}
+
+void
+gst_rc_record (GstRateController * self, GstRcFrameType frame_type,
+ gsize coded_size, GstClockTime duration)
+{
+ /* If there is no duration, use frame rate */
+ if (!GST_CLOCK_TIME_IS_VALID (duration) || duration == 0)
+ duration = self->frame_duration;
+
+ /* We simply ignore keyframe for now */
+ if (frame_type != GST_RC_KEY_FRAME) {
+ GstClockTime bps =
+ gst_util_uint64_scale (coded_size * 8, GST_SECOND, duration);
+
+ double target_bps_per_macroblock =
+ (gdouble) self->bitrate / self->total_macroblocks;
+ double bps_per_macroblock =
+ gst_guint64_to_gdouble (bps) / self->total_macroblocks;
+
+ gdouble pid_output =
+ pid_control (self, bps_per_macroblock, target_bps_per_macroblock);
+ gdouble qp_increase = CLAMP (pid_output,
+ 0.0 - (gdouble) self->qp_step, (gdouble) self->qp_step);
+ self->qp += qp_increase;
+ }
+}
+
+gint
+gst_rc_get_max_qp (GstRateController * self)
+{
+ return self->max_qp;
+}
+
+gint
+gst_rc_get_min_qp (GstRateController * self)
+{
+ return self->min_qp;
+}
+
+gint
+gst_rc_get_qp_step (GstRateController * self)
+{
+ return self->qp_step;
+}
+
+gint
+gst_rc_get_init_qp (GstRateController * self)
+{
+ return self->init_qp;
+}
+
+GstRateControlMode
+gst_rc_get_mode (GstRateController * self)
+{
+ return self->mode;
+}
+
+gint
+gst_rc_get_bitrate (GstRateController * self)
+{
+ return self->bitrate;
+}
+
+void
+gst_rc_set_format (GstRateController * self, const GstVideoInfo * vinfo)
+{
+ gint width = GST_VIDEO_INFO_WIDTH (vinfo);
+ gint height = GST_VIDEO_INFO_HEIGHT (vinfo);
+ gint fps_n = GST_VIDEO_INFO_FPS_N (vinfo);
+ gint fps_d = GST_VIDEO_INFO_FPS_D (vinfo);
+
+ gint width_in_macroblocks = (width + 15) / 16;
+ gint height_in_macroblocks = (height + 15) / 16;
+
+ self->total_macroblocks = width_in_macroblocks * height_in_macroblocks;
+ self->pid.prev_error = 0;
+ self->pid.integral = 0;
+ self->qp = self->init_qp;
+
+ if (!fps_n || !fps_d) {
+ fps_n = 30;
+ fps_d = 1;
+ }
+
+ self->frame_duration = gst_util_uint64_scale (fps_d, GST_SECOND, fps_n);
+}
+
+void
+gst_rc_set_max_qp (GstRateController * self, gint max_qp)
+{
+ self->max_qp = max_qp;
+}
+
+void
+gst_rc_set_min_qp (GstRateController * self, gint min_qp)
+{
+ self->min_qp = min_qp;
+}
+
+void
+gst_rc_set_qp_step (GstRateController * self, gint qp_step)
+{
+ self->qp_step = qp_step;
+}
+
+void
+gst_rc_set_init_qp (GstRateController * self, gint init_qp)
+{
+ self->init_qp = self->qp = init_qp;
+}
+
+void
+gst_rc_set_mode (GstRateController * self, GstRateControlMode mode)
+{
+ self->mode = mode;
+}
+
+void
+gst_rc_set_bitrate (GstRateController * self, gint bitrate)
+{
+ self->bitrate = bitrate;
+}
+
+gint
+gst_rc_get_qp (GstRateController * self)
+{
+ switch (self->mode) {
+ case GST_RC_CONSTANT_BITRATE:
+ return CLAMP (round (self->qp), self->min_qp, self->max_qp);
+ case GST_RC_CONSTANT_QP:
+ return self->init_qp;
+ }
+
+ g_assert_not_reached ();
+}
diff --git a/gst-libs/gst/codecs/gstratecontroller.h b/gst-libs/gst/codecs/gstratecontroller.h
new file mode 100644
index 0000000..f71cb8e
--- /dev/null
+++ b/gst-libs/gst/codecs/gstratecontroller.h
@@ -0,0 +1,70 @@
+/* GStreamer
+ * Copyright (C) 2023 Michael Grzeschik <m.grzeschik@pengutronix.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/codecs/codecs-prelude.h>
+#include <gst/video/video.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ GST_RC_CONSTANT_QP,
+ GST_RC_CONSTANT_BITRATE,
+} GstRateControlMode;
+
+typedef enum
+{
+ GST_RC_KEY_FRAME,
+ GST_RC_INTER_FRAME,
+} GstRcFrameType;
+
+GType gst_rate_control_mode_get_type (void);
+
+#define GST_RATE_CONTROLLER_TYPE (gst_rc_get_type())
+G_DECLARE_FINAL_TYPE(GstRateController, gst_rc,
+ GST, RC, GObject);
+
+GstRateController* gst_rc_new (void);
+
+void gst_rc_record (GstRateController * self,
+ GstRcFrameType frame_type,
+ gsize coded_size,
+ GstClockTime duration);
+
+gint gst_rc_get_max_qp (GstRateController *self);
+gint gst_rc_get_min_qp (GstRateController *self);
+gint gst_rc_get_qp_step (GstRateController *self);
+gint gst_rc_get_init_qp (GstRateController *self);
+GstRateControlMode gst_rc_get_mode (GstRateController *self);
+gint gst_rc_get_bitrate (GstRateController *self);
+
+void gst_rc_set_format (GstRateController *self,
+ const GstVideoInfo * vinfo);
+void gst_rc_set_max_qp (GstRateController *self, gint max_qp);
+void gst_rc_set_min_qp (GstRateController *self, gint min_qp);
+void gst_rc_set_qp_step (GstRateController *self, gint qp_step);
+void gst_rc_set_init_qp (GstRateController *self, gint init_qp);
+void gst_rc_set_mode (GstRateController *self, GstRateControlMode mode);
+void gst_rc_set_bitrate (GstRateController *self, gint bitrate);
+
+gint gst_rc_get_qp(GstRateController *self);
+
+G_END_DECLS
diff --git a/gst-libs/gst/codecs/meson.build b/gst-libs/gst/codecs/meson.build
index 3ea3bdc..c04600e 100644
--- a/gst-libs/gst/codecs/meson.build
+++ b/gst-libs/gst/codecs/meson.build
@@ -16,6 +16,7 @@ codecs_sources = files(
'gstvp8frame.c',
'gsth264encoder.c',
'gsth264frame.c',
+ 'gstratecontroller.c',
)
codecs_headers = files(
--
2.25.1