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 09/11] media: v4l2-core: Fix all-intra streams in the encode CBR rate control
Date: Tue, 10 Nov 2026 10:05:23 +0000	[thread overview]
Message-ID: <20261110-h264-encode-fixes-v1-9-7a64486df499@gmail.com> (raw)
In-Reply-To: <20261110-h264-encode-fixes-v1-0-7a64486df499@gmail.com>

From: Arun Iyengar <aiyengar1990@gmail.com>

All-intra streams (an IDR period of 1) have two problems in the constant
bitrate rate control:

- the QP state, which is the QP of the P pictures, is limited to the QP
  range, and the I pictures are coded the I QP delta below it, so they
  never reach the highest QP: at targets that need it the bitrate stays
  far above the target (2.3 times at 1080p30 and 1 Mbit/s with QP 49
  instead of 51);
- the start QP is estimated for an I picture of a GOP of two seconds,
  which gets about nine times the nominal size, so the first pictures of
  an all-intra stream come out several times their size until the state
  has climbed: the largest one second window of a 1080p30 stream at
  4 Mbit/s is 1.55 times the bitrate.

Shift the range of the state by the I QP delta in all-intra streams, and
when the second picture is an I picture again, set the state from the
size of the first one at its QP. In a host simulation against a model
encoder (sizes halving every 6 QP, 720p and 1080p, 256 kbit/s to
20 Mbit/s), the all-intra bitrate is then within 0.1% of the target
wherever QP 51 can reach it, and the largest one second window drops to
1.02-1.11 times the bitrate (was up to 1.88). Streams with P pictures are
not changed.

Signed-off-by: Arun Iyengar <aiyengar1990@gmail.com>
---
 drivers/media/v4l2-core/v4l2-enc-cbr.c | 27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-enc-cbr.c b/drivers/media/v4l2-core/v4l2-enc-cbr.c
index 28826e32c418..87db60469182 100644
--- a/drivers/media/v4l2-core/v4l2-enc-cbr.c
+++ b/drivers/media/v4l2-core/v4l2-enc-cbr.c
@@ -239,8 +239,22 @@ int v4l2_enc_cbr_begin(struct v4l2_enc_cbr *cbr, u32 bitrate,
 
 	if (intra) {
 		/* An I picture closes the GOP: its length is the IDR period. */
-		if (cbr->seen_i)
+		if (cbr->seen_i) {
+			/*
+			 * The second picture of an all-intra stream: the size
+			 * of the first one at its QP tells the QP of the
+			 * nominal size better than the start estimate, which
+			 * assumed a GOP with P pictures.
+			 */
+			if (!cbr->gop && !cbr->since_i)
+				cbr->qp_q8 = clamp_t(int, (cbr->qp_pic +
+						 cbr->qp_delta_intra) * 256 +
+						 6 * (cbr_log2_q8(cbr->i_bits) -
+						      cbr_log2_q8(nominal)),
+						 (qp_min + cbr->qp_delta_intra) * 256,
+						 (qp_max + cbr->qp_delta_intra) * 256);
 			cbr->gop = cbr->since_i + 1;
+		}
 		cbr->since_i = 0;
 		cbr->seen_i = true;
 	}
@@ -381,7 +395,7 @@ void v4l2_enc_cbr_end(struct v4l2_enc_cbr *cbr, u64 bits,
 	unsigned int fps = cbr_fps(timeperframe);
 	s64 limit = cbr->nominal * CBR_BUFFER_SECONDS * fps;
 	unsigned int ratio;
-	int err, step;
+	int err, step, shift;
 	s64 cap, add;
 
 	if (!cbr->bitrate || !cbr->target_bits)
@@ -452,7 +466,14 @@ void v4l2_enc_cbr_end(struct v4l2_enc_cbr *cbr, u64 bits,
 		cbr->qp_q8 += cbr->gop == 1 ? step : step / 4;
 	}
 
-	cbr->qp_q8 = clamp_t(int, cbr->qp_q8, qp_min * 256, qp_max * 256);
+	/*
+	 * The state is the QP of the P pictures. All-intra streams code their
+	 * pictures at the state less the I QP delta, so they need the QP range
+	 * shifted up by it to reach the highest QP.
+	 */
+	shift = cbr->gop == 1 ? cbr->qp_delta_intra : 0;
+	cbr->qp_q8 = clamp_t(int, cbr->qp_q8, (qp_min + shift) * 256,
+			     (qp_max + shift) * 256);
 
 	pr_debug("+ v4l2-enc-cbr: end");
 	pr_debug("  size: %llu bits, target: %llu bits, qp error: %d/256, qp: %d/256",

-- 
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 ` [PATCH RFC 04/11] media: v4l2-h264-enc-rc: Rework the constant bitrate mode Arun Iyengar via B4 Relay
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 ` Arun Iyengar via B4 Relay [this message]
2026-11-10 10:05 ` [PATCH RFC 10/11] media: v4l2-core: Add a peak bitrate to the encode rate control 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-9-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®