mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Arun Iyengar via B4 Relay <devnull+aiyengar1990.gmail.com@kernel.org>
To: Mauro Carvalho Chehab <mchehab@kernel.org>,
	 Paul Kocialkowski <paulk@sys-base.io>
Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org,
	 Nicolas Dufresne <nicolas.dufresne@collabora.com>,
	 Hans Verkuil <hverkuil@kernel.org>,
	Marco Felsch <m.felsch@pengutronix.de>,
	 Michael Tretter <m.tretter@pengutronix.de>,
	 Arun Iyengar <aiyengar1990@gmail.com>
Subject: [PATCH RFC 04/11] media: v4l2-h264-enc-rc: Rework the constant bitrate mode
Date: Tue, 10 Nov 2026 10:05:18 +0000	[thread overview]
Message-ID: <20261110-h264-encode-fixes-v1-4-7a64486df499@gmail.com> (raw)
In-Reply-To: <20261110-h264-encode-fixes-v1-0-7a64486df499@gmail.com>

From: Arun Iyengar <aiyengar1990@gmail.com>

The CBR mode of the H.264 encode core misses its bitrate and has a large
size spike after each IDR picture:

- the QP only moves when a picture misses its size target by more than
  25% (the ratio table has a dead band from 0.8 to 1.25), so a steady
  error below that is never corrected and the QP stays near its start
  value;
- P pictures get 0.8 times the average size left in a 50 picture budget,
  which settles at about 0.89 times the nominal picture size: the
  bitrate stays 5-9% low;
- I and P pictures keep separate QP histories, so an IDR picture is coded
  with the I QP of the previous GOP, seconds old, and the first P
  picture after it uses the P history against a reference of a very
  different quality, and overshoots its target several times;
- the start QP comes from a fixed bits per macroblock table and a P
  target 6 times below the I table.

Replace it with a model like the frame-level rate control of the Rockchip
MPP library: one QP state for the stream (the QP of the P pictures), I
pictures 2 QP below it, a start QP from the bits per luma sample of the
first I picture, picture size targets from a GOP plan with the I/P size
ratio and the IDR period measured on the stream, and the deviation of
the coded size from that plan spread over the next 8 pictures. After
each picture the QP moves by 1/8 of its QP error, 6 * log2(size /
target), and by half of the part beyond 3 QP steps.

The constant QP and constant quality modes are not changed.

Board results (RK3588S Hantro H1, 1080p60 clips of 300 pictures, GOP 120,
2-16 Mb/s, GStreamer v4l2slh264enc): the bitrate error goes from
-8.3..+2.3% to +0.3..+2.3% (-0.0..+0.2% over whole GOPs), PSNR-Y
improves by 0.2-0.6 dB, the first P picture after an IDR goes from up to
20 times to at most 1.4 times the median P size, and the encode speed is
unchanged.

Signed-off-by: Arun Iyengar <aiyengar1990@gmail.com>
---
 drivers/media/v4l2-core/v4l2-h264-enc-rc.c | 426 +++++++++++++++--------------
 include/media/v4l2-h264-enc-rc.h           |  34 +++
 2 files changed, 248 insertions(+), 212 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-h264-enc-rc.c b/drivers/media/v4l2-core/v4l2-h264-enc-rc.c
index 8f9622395eb6..bbfe03febac8 100644
--- a/drivers/media/v4l2-core/v4l2-h264-enc-rc.c
+++ b/drivers/media/v4l2-core/v4l2-h264-enc-rc.c
@@ -5,6 +5,9 @@
  * Copyright (C) 2025-2026 Paul Kocialkowski <paulk@sys-base.io>
  */
 
+#include <linux/int_log.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
 #include <linux/module.h>
 #include <media/v4l2-h264-enc.h>
 
@@ -158,275 +161,274 @@ static const struct v4l2_h264_enc_rc_mode cq_mode = {
 	.estimate	= cq_estimate,
 };
 
-static int cbr_measure(struct v4l2_h264_enc_rc *rc, unsigned long *size)
-{
-	struct v4l2_h264_enc_state *state = rc->state;
-	struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
-	struct v4l2_fract *timeperframe = &state->timeperframe;
-	struct v4l2_h264_enc_rc_stats *stats = &rc->stats;
-	unsigned long long budget;
-	unsigned long target;
-	unsigned long target_nominal;
-
-	pr_debug("+ v4l2-h264-enc-rc: cbr measure");
-
-	/* Nominal average size target for exact bitrate. */
-	target_nominal = state->bitrate * timeperframe->numerator /
-			 timeperframe->denominator / 8;
-
-	/* Total size budget for twice the size window. */
-	budget = 2 * V4L2_H264_ENC_RC_STATS_SIZE_COUNT * state->bitrate *
-		 timeperframe->numerator / timeperframe->denominator / 8;
-
-	if (budget < stats->size_total) {
-		target = target_nominal;
-		goto complete;
-	}
+/*
+ * Constant bitrate mode
+ *
+ * One QP state is kept for the stream: it is the QP of the P (and B)
+ * pictures, and I pictures are coded a fixed number of steps below it, so
+ * that the quality of the reference pictures stays even across GOPs. Each
+ * picture gets a size target from a GOP plan, in which an I picture is
+ * "ratio" times the size of a P picture, with the ratio and the IDR period
+ * measured on the stream. The difference between the coded size so far and
+ * the size the plan expects at this point of the GOP is spread over the next
+ * pictures, so that the bitrate converges to the target.
+ *
+ * After each picture the QP state moves by a fraction of the QP error
+ * 6 * log2(size / target) (a QP step of 6 halves the size): slowly for small
+ * errors, so that the QP does not follow the size noise from picture to
+ * picture, and quickly for errors of several QP steps (a scene change, a
+ * poor start).
+ */
 
-	/* Remaining size budget after deducing total size in window. */
-	budget -= stats->size_total;
-	/* Average size target from remaining size budget. */
-	target = budget / (2 * V4L2_H264_ENC_RC_STATS_SIZE_COUNT - stats->count);
+/* The accumulated size error is spread over this many pictures. */
+#define CBR_WINDOW		8
+/* Limit of the accumulated size error, in seconds of the bitrate */
+#define CBR_BUFFER_SECONDS	3
+/* I/P size ratio, 1/16 units: initial value (10x) and limits (1.5x .. 30x) */
+#define CBR_RATIO_INIT		160
+#define CBR_RATIO_MIN		24
+#define CBR_RATIO_MAX		480
+/* P pictures after an I picture when the I/P size ratio is measured */
+#define CBR_RATIO_PICS		4
+/* QP of the I pictures below the QP of the P pictures */
+#define CBR_QP_DELTA_INTRA	2
+/* QP errors up to this are corrected by 1/8 per picture, 1/256 units */
+#define CBR_ERR_SLOW		(3 * 256)
+/*
+ * Largest QP increase and decrease per picture, 1/256 units. Decreases are
+ * slower, so that pictures that are much smaller than their target, such as
+ * repeated pictures, don't pull the QP down far.
+ */
+#define CBR_STEP_UP_MAX		(4 * 256)
+#define CBR_STEP_DOWN_MAX	(2 * 256)
 
-complete:
-	pr_debug("  nominal target: %lu bytes", target_nominal);
-	pr_debug("  uniform target: %lu bytes", target);
+/* log2(x) in 8.8 fixed point */
+static int cbr_log2_q8(u64 x)
+{
+	unsigned int shift = 0;
 
-	/* Bump size for intra frames and reduce for pred frames.
-	 * We are better off with good quality initial references.
-	 * Maintain average assuming 1 intra frame for 24 inter frames.
-	 */
-	if (encode->slice_type == V4L2_H264_SLICE_TYPE_I)
-		target = target * 6;
-	else
-		target = target * 80 / 100;
+	if (!x)
+		return 0;
 
-	/* Reduce target size on bitrate overshoot to calm things down. */
-	if (9 * stats->size_total > 10 * stats->count * target_nominal) {
-		pr_debug("  overshoot, reducing target size");
-		target = 80 * target / 100;
+	while (x > U32_MAX) {
+		x >>= 1;
+		shift++;
 	}
 
-	pr_debug("  final target: %lu bytes", target);
-
-	*size = target;
-
-	return 0;
+	return (int)(intlog2(x) >> 16) + shift * 256;
 }
 
-/* Intra bit size per macroblock estimation for QP starting at 10. */
-static unsigned int cbr_qp_initial_bits_mb[] = {
-	437, 411, 376, 355, 318, 303, 291, 271, 252, 238, 218, 204, 188, 171,
-	158, 149, 133, 124, 115, 105, 98, 92, 83, 77, 70, 63, 57, 52, 45, 39,
-	34, 31, 30, 29, 26, 23, 22, 20, 18, 17, 16, 15
-};
-
-static int cbr_qp_initial(struct v4l2_h264_enc_rc *rc, unsigned int *qp)
+static unsigned int cbr_fps(const struct v4l2_fract *timeperframe)
 {
-	struct v4l2_h264_enc_state *state = rc->state;
-	struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
-	unsigned long size_mb;
-	unsigned int qp_initial;
-	unsigned int threshold;
-	unsigned int i;
+	if (!timeperframe->numerator || !timeperframe->denominator)
+		return 30;
 
-	size_mb = 8 * rc->size / (state->width_mbs * state->height_mbs);
-
-	pr_debug("  initial estimate from %lu bits/mb", size_mb);
+	return max(DIV_ROUND_CLOSEST(timeperframe->denominator,
+				     timeperframe->numerator), 1U);
+}
 
-	for (i = 0; i < ARRAY_SIZE(cbr_qp_initial_bits_mb); i++) {
-		threshold = cbr_qp_initial_bits_mb[i];
+static u64 cbr_nominal(u32 bitrate, const struct v4l2_fract *timeperframe)
+{
+	if (!timeperframe->numerator || !timeperframe->denominator)
+		return max(bitrate / 30, 1U);
 
-		/* Expect inter frames to be 6 times smaller. */
-		if (encode->slice_type != V4L2_H264_SLICE_TYPE_I)
-			threshold = cbr_qp_initial_bits_mb[i] / 6;
+	return max_t(u64, div_u64((u64)bitrate * timeperframe->numerator,
+				  timeperframe->denominator), 1);
+}
 
-		if (size_mb >= threshold) {
-			qp_initial = 10 + i;
-			break;
-		}
-	}
+/* Planned size of an I or P picture of the current GOP */
+static u64 cbr_plan(const struct v4l2_h264_enc_rc_cbr *cbr, unsigned int fps,
+		    bool intra)
+{
+	unsigned int gop = cbr->gop;
+	u64 p;
 
-	/* Fallback to the lowest QP (last in the list). */
-	if (i == ARRAY_SIZE(cbr_qp_initial_bits_mb))
-		qp_initial = 10 + i - 1;
+	/* Until the second I picture, assume at least two seconds. */
+	if (!gop)
+		gop = max(cbr->since_i + 1, 2 * fps);
+	if (gop <= 1)
+		return cbr->nominal;
 
-	*qp = qp_initial;
+	p = div_u64(cbr->nominal * gop * 16, cbr->ratio + 16 * (gop - 1));
+	if (intra)
+		p = div_u64(p * cbr->ratio, 16);
 
-	return 0;
+	return max_t(u64, p, 1);
 }
 
 /*
- * This table gives the QP decrease/increase (coded as index, with QP = 0 in
- * the middle) for each size ratio (in base 1000).
- *
- * Intra is a bit agressive since we generally have few intra slices to adapt
- * to a scene change. The QP diff range goes from -4 to +4.
- */
-static unsigned long cbr_ratio_qp_diff_steps_intra[] = {
-	3000, 2000, 1500, 1250, 800, 666, 500, 333
-};
-
-/*
- * Inter is less agressive since we generally have more inter slices to adapt
- * to a scene change and want to give more stability to QP. The QP diff range
- * goes from -3 to +3.
+ * Start QP of an I picture from its size in bits per luma sample:
+ * 20.3 - 6 * log2(bits per sample), clipped to 15..45. This is the relation
+ * the Rockchip MPP library uses for its first picture at 1080p.
  */
-static unsigned long cbr_ratio_qp_diff_steps_inter[] = {
-	2000, 1500, 1250, 800, 666, 500
-};
-
-static int cbr_ratio_qp_diff(unsigned long ratio, unsigned char slice_type)
+static int cbr_qp_initial(u64 bits, unsigned int samples)
 {
-	unsigned long *steps;
-	unsigned int count;
-	unsigned int i;
-
-	if (slice_type == V4L2_H264_SLICE_TYPE_I) {
-		steps = cbr_ratio_qp_diff_steps_intra;
-		count = ARRAY_SIZE(cbr_ratio_qp_diff_steps_intra);
-	} else {
-		steps = cbr_ratio_qp_diff_steps_inter;
-		count = ARRAY_SIZE(cbr_ratio_qp_diff_steps_inter);
-	}
+	int qp_q8 = 20 * 256 + 77 +
+		    6 * (cbr_log2_q8(samples) - cbr_log2_q8(max_t(u64, bits, 1)));
 
-	WARN_ON(count % 2);
+	return clamp(DIV_ROUND_CLOSEST(qp_q8, 256), 15, 45);
+}
 
-	for (i = 0; i < count; i++)
-		if (ratio > steps[i])
-			return -1 * (int)count / 2 + i;
+static int cbr_init(struct v4l2_h264_enc_rc *rc)
+{
+	memset(&rc->cbr, 0, sizeof(rc->cbr));
 
-	return count / 2;
+	return 0;
 }
 
-static int cbr_estimate(struct v4l2_h264_enc_rc *rc, unsigned int *qp)
+static int cbr_measure(struct v4l2_h264_enc_rc *rc, unsigned long *size)
 {
 	struct v4l2_h264_enc_state *state = rc->state;
-	struct v4l2_ctrl_h264_encode_params *encode = &state->encode;
-	struct v4l2_h264_enc_rc_stats *stats = &rc->stats;
-	struct v4l2_h264_enc_rc_stats_type *type;
-	unsigned int type_index[3];
-	unsigned int ratio_qp[3];
-	unsigned int penalty[3];
-	unsigned int penalty_total;
-	unsigned int weight[3];
-	unsigned int weight_total;
-	int ratio_qp_diff;
-	unsigned long diff;
-	unsigned long diff_closest;
-	unsigned int slot_closest;
-	unsigned int index;
-	unsigned int count;
-	unsigned int total;
-	unsigned long ratio;
-	unsigned int i;
-
-	if (!stats->count)
-		return cbr_qp_initial(rc, qp);
-
-	if (encode->slice_type == V4L2_H264_SLICE_TYPE_I)
-		type = &stats->intra;
-	else if (encode->slice_type == V4L2_H264_SLICE_TYPE_P)
-		type = &stats->pred;
-	else if (encode->slice_type == V4L2_H264_SLICE_TYPE_B)
-		type = &stats->bipred;
-	else
+	struct v4l2_h264_enc_rc_cbr *cbr = &rc->cbr;
+	struct v4l2_fract *timeperframe = &state->timeperframe;
+	bool intra = state->encode.slice_type == V4L2_H264_SLICE_TYPE_I;
+	unsigned int fps = cbr_fps(timeperframe);
+	u64 nominal = cbr_nominal(state->bitrate, timeperframe);
+	unsigned int samples = state->width_mbs * state->height_mbs * 256;
+	s64 target, expected = 0;
+	int qp;
+
+	if (!state->bitrate)
 		return -EINVAL;
 
-	pr_debug("+ v4l2-h264-enc-rc: cbr estimate");
-
-	if (!type->count)
-		return cbr_qp_initial(rc, qp);
-
-	for (i = 0; i < 3; i++) {
-		if (i == type->count)
-			break;
-
-		index = type->index;
-		if (index <= i)
-			index = type->count + index - (i + 1);
-		else
-			index -= i + 1;
-
-		/* Set initial penalty based on frame age. */
-		penalty[i] = 4 * i;
-		type_index[i] = index;
-
-		/* Track frame with size closest to target. */
-		diff = abs((long)rc->size - type->size[index]);
-
-		if (!i || diff < diff_closest) {
-			diff_closest = diff;
-			slot_closest = i;
-		}
+	if (!cbr->bitrate) {
+		cbr->ratio = CBR_RATIO_INIT;
+		cbr->qp_q8 = -1;
 	}
 
-	count = i;
-	penalty_total = 0;
-
-	for (i = 0; i < count; i++) {
-		/* Add penalty for frames with larger size difference. */
-		if (i != slot_closest)
-			penalty[i] += 10;
+	/* A new bitrate keeps the QP state and the measured statistics. */
+	if (cbr->bitrate != state->bitrate || cbr->nominal != nominal) {
+		cbr->bitrate = state->bitrate;
+		cbr->nominal = nominal;
+		cbr->buffer_bits = 0;
+	}
 
-		penalty_total += penalty[i];
+	if (intra) {
+		/* An I picture closes the GOP: its length is the IDR period. */
+		if (cbr->seen_i)
+			cbr->gop = cbr->since_i + 1;
+		cbr->since_i = 0;
+		cbr->seen_i = true;
+	}
 
-		index = type_index[i];
+	cbr->plan_bits = cbr_plan(cbr, fps, intra);
 
-		/* Calculate QP from target to observed size ratio. */
-		ratio = 1000UL * rc->size / type->size[index];
+	if (cbr->qp_q8 < 0) {
+		/* First picture: start QP from the bits per sample. */
+		qp = cbr_qp_initial(cbr_plan(cbr, fps, true), samples) +
+		     CBR_QP_DELTA_INTRA;
+		cbr->qp_q8 = clamp_t(int, qp, state->qp_min, state->qp_max) *
+			     256;
+	}
 
-		ratio_qp_diff = cbr_ratio_qp_diff(ratio, encode->slice_type);
+	/*
+	 * The buffer holds the coded minus the nominal sizes. Within a GOP the
+	 * plan expects it to go up by the excess of the I picture and back
+	 * down to zero by the savings of the P pictures until the next I
+	 * picture. Spread the difference to that over the next pictures.
+	 */
+	if (!intra && cbr->seen_i)
+		expected = (s64)cbr_plan(cbr, fps, true) - (s64)nominal +
+			   (s64)cbr->since_i *
+			   ((s64)cbr->plan_bits - (s64)nominal);
+
+	target = (s64)cbr->plan_bits -
+		 div_s64(cbr->buffer_bits - max_t(s64, expected, 0),
+			 CBR_WINDOW);
+	cbr->target_bits = clamp_t(s64, target,
+				   max_t(u64, cbr->plan_bits / 4, 8),
+				   cbr->plan_bits * 2);
+	cbr->intra = intra;
 
-		if ((int)type->qp[index] + ratio_qp_diff < 0)
-			ratio_qp[i] = 0;
-		else if ((int)type->qp[index] + ratio_qp_diff > 51)
-			ratio_qp[i] = 51;
-		else
-			ratio_qp[i] = type->qp[index] + ratio_qp_diff;
+	pr_debug("+ v4l2-h264-enc-rc: cbr measure");
+	pr_debug("  nominal: %llu bits, plan: %llu bits, target: %llu bits",
+		 cbr->nominal, cbr->plan_bits, cbr->target_bits);
+	pr_debug("  buffer: %lld bits, expected: %lld bits, gop: %u, ratio: %u",
+		 cbr->buffer_bits, expected, cbr->gop, cbr->ratio);
 
-		diff = abs((long)rc->size - type->size[index]);
+	*size = max_t(unsigned long, cbr->target_bits / 8, 1);
 
-		pr_debug("  - backlog %d size: %lu (diff: %lu), qp: %u, penalty %u, ratio qp: %u",
-			 -(i + 1), type->size[index], diff, type->qp[index],
-			 penalty[i], ratio_qp[i]);
-	}
+	return 0;
+}
 
-	total = 0;
-	weight_total = 0;
+static int cbr_estimate(struct v4l2_h264_enc_rc *rc, unsigned int *qp)
+{
+	struct v4l2_h264_enc_rc_cbr *cbr = &rc->cbr;
+	int value;
 
-	for (i = 0; i < count; i++) {
-		/* Weight each calculated QP using the penalty ratio. */
-		if (penalty_total)
-			weight[i] = 1000 * (penalty_total - penalty[i]) /
-				    penalty_total;
-		else
-			weight[i] = 1000;
+	value = DIV_ROUND_CLOSEST(cbr->qp_q8, 256);
+	if (cbr->intra)
+		value -= CBR_QP_DELTA_INTRA;
 
-		/* Give non-zero low weight to full penalty cases. */
-		if (weight[i] < 200)
-			weight[i] = 200;
+	*qp = clamp(value, 0, 51);
 
-		total += ratio_qp[i] * weight[i];
-		weight_total += weight[i];
+	return 0;
+}
 
-		pr_debug("  - backlog %d weight: %u", -(i + 1),
-			 weight[i]);
+static void cbr_update(struct v4l2_h264_enc_rc *rc, u64 bits)
+{
+	struct v4l2_h264_enc_state *state = rc->state;
+	struct v4l2_h264_enc_rc_cbr *cbr = &rc->cbr;
+	s64 limit = cbr->nominal * CBR_BUFFER_SECONDS *
+		    cbr_fps(&state->timeperframe);
+	unsigned int ratio;
+	int err, step;
+
+	if (!cbr->bitrate || !cbr->target_bits)
+		return;
+
+	cbr->buffer_bits = clamp_t(s64, cbr->buffer_bits + (s64)bits -
+				   (s64)cbr->nominal, -limit, limit);
+
+	/* QP error of the picture, 1/256 units */
+	err = 6 * (cbr_log2_q8(bits) - cbr_log2_q8(cbr->target_bits));
+
+	step = err / 8;
+	if (err > CBR_ERR_SLOW)
+		step += (err - CBR_ERR_SLOW) / 2;
+	else if (err < -CBR_ERR_SLOW)
+		step += (err + CBR_ERR_SLOW) / 2;
+	step = clamp(step, -CBR_STEP_DOWN_MAX, CBR_STEP_UP_MAX);
+
+	if (!cbr->intra) {
+		cbr->qp_q8 += step;
+		cbr->p_bits = cbr->p_bits ? (7 * cbr->p_bits + bits) / 8 : bits;
+		cbr->since_i++;
+
+		/* I/P size ratio, once a few P pictures were coded */
+		if (cbr->since_i == CBR_RATIO_PICS && cbr->i_bits) {
+			ratio = div64_u64(cbr->i_bits * 16, cbr->p_bits);
+			ratio = clamp(ratio, CBR_RATIO_MIN, CBR_RATIO_MAX);
+			cbr->ratio = (cbr->ratio + ratio + 1) / 2;
+		}
+	} else {
+		/*
+		 * The size of an I picture mostly reflects the I/P ratio
+		 * estimate: only all-intra streams steer the QP fully with it.
+		 */
+		cbr->i_bits = bits;
+		cbr->qp_q8 += cbr->gop == 1 ? step : step / 4;
 	}
 
-	*qp = total / weight_total;
+	cbr->qp_q8 = clamp_t(int, cbr->qp_q8, state->qp_min * 256,
+			     state->qp_max * 256);
 
-	return 0;
+	pr_debug("+ v4l2-h264-enc-rc: cbr update");
+	pr_debug("  size: %llu bits, target: %llu bits, qp error: %d/256, qp: %d/256",
+		 bits, cbr->target_bits, err, cbr->qp_q8);
 }
 
 static int cbr_complete(struct v4l2_h264_enc_rc *rc, unsigned long bytesused)
 {
+	cbr_update(rc, (u64)bytesused * 8);
+
 	return v4l2_h264_enc_rc_stats_collect(rc, bytesused);
 }
 
 static const struct v4l2_h264_enc_rc_mode cbr_mode = {
 	.name		= "constant bitrate",
 	.type		= V4L2_H264_ENC_RC_TYPE_CBR,
+	.init		= cbr_init,
 	.measure	= cbr_measure,
 	.estimate	= cbr_estimate,
 	.complete	= cbr_complete,
diff --git a/include/media/v4l2-h264-enc-rc.h b/include/media/v4l2-h264-enc-rc.h
index 8b453c3a212e..72d78a594e40 100644
--- a/include/media/v4l2-h264-enc-rc.h
+++ b/include/media/v4l2-h264-enc-rc.h
@@ -8,6 +8,7 @@
 #ifndef _MEDIA_V4L2_H264_ENC_RC_H
 #define _MEDIA_V4L2_H264_ENC_RC_H
 
+#include <linux/types.h>
 #include <linux/v4l2-controls.h>
 #include <media/v4l2-h264.h>
 
@@ -82,6 +83,38 @@ struct v4l2_h264_enc_rc_stats {
 	unsigned int index;
 };
 
+/**
+ * struct v4l2_h264_enc_rc_cbr - constant bitrate mode state
+ * @bitrate: bitrate the state was set up for, 0 before the first picture
+ * @nominal: nominal picture size in bits (bitrate times frame interval)
+ * @qp_q8: QP of the P pictures in 1/256 units, -1 before the first picture
+ * @ratio: I/P picture size ratio in 1/16 units
+ * @gop: measured IDR period in pictures, 0 until two I pictures were seen
+ * @since_i: pictures coded since the last I picture
+ * @seen_i: an I picture was coded
+ * @intra: the current picture is an I picture
+ * @p_bits: running mean of the P picture sizes
+ * @i_bits: size of the last I picture
+ * @buffer_bits: accumulated difference between the coded and nominal sizes
+ * @plan_bits: planned size of the current picture
+ * @target_bits: size target of the current picture
+ */
+struct v4l2_h264_enc_rc_cbr {
+	u32 bitrate;
+	u64 nominal;
+	int qp_q8;
+	unsigned int ratio;
+	unsigned int gop;
+	unsigned int since_i;
+	bool seen_i;
+	bool intra;
+	u64 p_bits;
+	u64 i_bits;
+	s64 buffer_bits;
+	u64 plan_bits;
+	u64 target_bits;
+};
+
 struct v4l2_h264_enc_rc {
 	const struct v4l2_h264_enc_rc_ops *ops;
 	void *private_data;
@@ -91,6 +124,7 @@ struct v4l2_h264_enc_rc {
 	void *mode_data;
 
 	struct v4l2_h264_enc_rc_stats stats;
+	struct v4l2_h264_enc_rc_cbr cbr;
 	unsigned long size;
 	unsigned int qp;
 };

-- 
2.55.0



  parent reply	other threads:[~2026-11-10 10:05 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-10 10:05 [PATCH RFC 00/11] media: h264: Stateless encode core follow-ups: parameter sets, POC type 2, CBR and VBR Arun Iyengar via B4 Relay
2026-11-10 10:05 ` [PATCH RFC 01/11] media: h264: Only write the SPS and PPS of a stateless encode when they change Arun Iyengar via B4 Relay
2026-11-10 10:05 ` [PATCH RFC 02/11] media: h264: Support picture order count type 2 in stateless encode Arun Iyengar via B4 Relay
2026-11-10 10:05 ` [PATCH RFC 03/11] media: h264: Write the stateless encode SPS and PPS with nal_ref_idc 3 Arun Iyengar via B4 Relay
2026-11-10 10:05 ` Arun Iyengar via B4 Relay [this message]
2026-11-10 10:05 ` [PATCH RFC 05/11] media: v4l2-core: Move the H.264 encode CBR rate control to a helper Arun Iyengar via B4 Relay
2026-11-10 10:05 ` [PATCH RFC 06/11] media: v4l2-core: Bound the I picture size in the encode CBR rate control Arun Iyengar via B4 Relay
2026-11-10 10:05 ` [PATCH RFC 07/11] media: v4l2-core: Halve the I picture QP increase of the encode CBR Arun Iyengar via B4 Relay
2026-11-10 10:05 ` [PATCH RFC 08/11] media: v4l2-core: Re-encode oversized pictures in " Arun Iyengar via B4 Relay
2026-11-10 10:05 ` [PATCH RFC 09/11] media: v4l2-core: Fix all-intra streams in the encode CBR rate control Arun Iyengar via B4 Relay
2026-11-10 10:05 ` [PATCH RFC 10/11] media: v4l2-core: Add a peak bitrate to the encode " Arun Iyengar via B4 Relay
2026-11-10 10:05 ` [PATCH RFC 11/11] media: h264: Add a VBR mode to the stateless " Arun Iyengar via B4 Relay

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261110-h264-encode-fixes-v1-4-7a64486df499@gmail.com \
    --to=devnull+aiyengar1990.gmail.com@kernel.org \
    --cc=aiyengar1990@gmail.com \
    --cc=hverkuil@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=m.felsch@pengutronix.de \
    --cc=m.tretter@pengutronix.de \
    --cc=mchehab@kernel.org \
    --cc=nicolas.dufresne@collabora.com \
    --cc=paulk@sys-base.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®