356 lines
9.7 KiB
Diff
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
|
|
|