b47938e0bc
build and publish packages / distcc-avahi-aarch64 (push) Successful in 1m3s
build and publish packages / lmcp-any (push) Successful in 9s
build and publish packages / lmcp-debian (push) Successful in 4s
build and publish packages / claude-his-any (push) Failing after 4s
build and publish packages / ffmpeg-v4l2-request-aarch64 (push) Has been skipped
build and publish packages / claude-his-debian (push) Has been skipped
Mirrors phase6/step1/ from the ohm_gl_fix campaign. Contract-correct
hantro multi-planar / chromium-149-era stateless H.264 port of
bootlin's libva-v4l2-request, patches 0001..0018 + fourier-local.
Honest characterisation in README:
- Builds cleanly on chromium-builder LXC (boltzmann)
- vainfo enumerates H.264 profiles cleanly with LIBVA_DRIVER_NAME=v4l2_request
- NOT on Brave's decode path on ohm_gl_fix stack — Brave uses
Chromium's own V4L2VideoDecoder in media/gpu/v4l2/.
- Most likely useful for a future Firefox-via-libavcodec-vaapi
campaign, modulo a separate Mesa-panfrost WSI pitch issue.
- DEBUG patches (0010, 0011, 0014) intentionally kept in series
for development; remove for cleaner production runs.
Audit trail in the source repo at ohm_gl_fix:
phase6/step1/audit_0008_decode_params_2026-05-01.md
phase6/step1/api_contract_findings_2026-05-01.md
phase3_remeasure_2026-05-02/B3_decoder_discovery.md (why this
isn't on Brave's path)
160 lines
6.9 KiB
Diff
160 lines
6.9 KiB
Diff
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||
Date: 2026-05-02
|
||
Subject: [PATCH] h264: derive sps.level_idc from H.264 Annex A.3 MaxFS
|
||
|
||
Replaces patch 0013's hardcoded level_idc = 51 with a small lookup
|
||
that picks the smallest level whose MaxFS contains the encoded
|
||
frame size. Patch 0013's TODO is resolved by this change.
|
||
|
||
VAAPI does not expose level_idc on the decode side
|
||
(VAPictureParameterBufferH264 has no such field; only
|
||
VAEncSequenceParameterBufferH264 carries it). The H.264 SPS NAL is
|
||
parsed client-side by ffmpeg-vaapi and only slice data forwards in
|
||
VASliceDataBuffer, so a SPS-NAL byte parser is not viable from the
|
||
bitstream the libva-v4l2-request layer receives. We therefore
|
||
derive level_idc from picture dimensions, which VAAPI does provide
|
||
in VAPictureParameterBufferH264.picture_{width,height}_in_mbs_minus1.
|
||
|
||
Annex A.3 (Table A-1) MaxFS thresholds:
|
||
Level 1.0: 99 MBs ( 176×144 = 11×9 = 99 )
|
||
Level 1.1: 396 ( 352×288 = 22×18 = 396 )
|
||
Level 2.0: 396
|
||
Level 2.1: 792 ( 352×576 / 720×288 )
|
||
Level 2.2: 1620 ( 720×480 ≈ 1350; 720×576 = 1620 )
|
||
Level 3.0: 1620
|
||
Level 3.1: 3600 (1280×720 ≈ 3600 )
|
||
Level 3.2: 5120
|
||
Level 4.0: 8192 (1920×1088 = 8160 fits )
|
||
Level 4.1: 8192
|
||
Level 4.2: 8704
|
||
Level 5.0: 22080
|
||
Level 5.1: 36864 (3840×2176 = 32640 fits; 4K@8K-edge )
|
||
Level 5.2: 36864
|
||
Level 6.0: 139264 (8K )
|
||
|
||
V4L2 control encoding: level_idc = (level major × 10) + (level minor).
|
||
Level 4.1 → 41, Level 5.1 → 51, Level 6.0 → 60.
|
||
|
||
Picks for typical content:
|
||
1080p (1920×1088 = 8160 MBs) → Level 4.1 (level_idc = 41)
|
||
4K (3840×2176 = 32640 MBs) → Level 5.1 (level_idc = 51)
|
||
8K (7680×4352 = 130560 MBs) → Level 6.0 (level_idc = 60)
|
||
|
||
The previous hardcode of 51 was over-allocating for 1080p; with
|
||
this patch hantro can pre-allocate based on the actual frame size.
|
||
For our ohm corpus (1080p) this drops the requested DPB / MV
|
||
buffer sizing from level-5.1 generosity to level-4.1 right-sized.
|
||
|
||
Without VAAPI exposing framerate we cannot also check MaxMBPS /
|
||
MaxBR / MaxCPB. The frame-size-based pick is acceptable in
|
||
practice: temporally-dense streams almost always also push
|
||
spatially-large frames, so MaxFS captures the dominant
|
||
resource-sizing signal.
|
||
|
||
Cross-reference: H.264 spec Annex A, Table A-1 ("Level limits").
|
||
ext-ctrls-codec-stateless.rst V4L2_CID_STATELESS_H264_SPS lists
|
||
level_idc as required-userspace-input, no kernel-derives annotation.
|
||
|
||
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||
---
|
||
--- a/src/h264.c
|
||
+++ b/src/h264.c
|
||
@@ -638,6 +638,55 @@
|
||
}
|
||
}
|
||
|
||
+/*
|
||
+ * Derive sps.level_idc from the encoded frame size in macroblocks per
|
||
+ * H.264 Annex A.3 (Table A-1) MaxFS thresholds. Each level's MaxFS is
|
||
+ * the maximum encoded frame size in MBs the level supports; we pick
|
||
+ * the smallest level whose MaxFS contains the actual frame size.
|
||
+ *
|
||
+ * Level decoding for the V4L2 control: level_idc = level * 10
|
||
+ * Level 1.0 → 10, Level 4.1 → 41, Level 5.1 → 51, Level 6.0 → 60.
|
||
+ *
|
||
+ * VAAPI does not expose the bitstream's actual level_idc on the
|
||
+ * decode side (VAPictureParameterBufferH264 has no such field) — see
|
||
+ * va.h. The H.264 SPS NAL is parsed client-side by ffmpeg-vaapi /
|
||
+ * mpv and only slice data is forwarded in VASliceDataBuffer, so a
|
||
+ * SPS-NAL byte parser is not viable at this layer.
|
||
+ *
|
||
+ * Without framerate we cannot also check MaxMBPS / MaxBR / MaxCPB.
|
||
+ * That gap is acceptable in practice: consumers that push
|
||
+ * temporally-dense streams (high MBPS) almost always also push
|
||
+ * spatially-large frames (high MaxFS), so frame-size-based level
|
||
+ * selection over-allocates on the temporal axis but never
|
||
+ * under-allocates a level the consumer relies on for correct
|
||
+ * decode-resource sizing.
|
||
+ *
|
||
+ * Picks for typical content:
|
||
+ * 1080p (8160 MBs) → Level 4.1 (level_idc = 41)
|
||
+ * 4K (32400 MBs) → Level 5.1 (level_idc = 51)
|
||
+ * 8K (138240 MBs) → Level 6.0 (level_idc = 60)
|
||
+ *
|
||
+ * Replaces the hardcoded level_idc=51 from patch 0013.
|
||
+ */
|
||
+static inline __u8 h264_derive_level_idc(unsigned int width_in_mbs,
|
||
+ unsigned int height_in_mbs)
|
||
+{
|
||
+ const unsigned int frame_size_mbs = width_in_mbs * height_in_mbs;
|
||
+
|
||
+ if (frame_size_mbs <= 99) return 10; /* Level 1.0 */
|
||
+ if (frame_size_mbs <= 396) return 11; /* Level 1.1 - 2.0 */
|
||
+ if (frame_size_mbs <= 792) return 21; /* Level 2.1 */
|
||
+ if (frame_size_mbs <= 1620) return 22; /* Level 2.2 - 3.0 */
|
||
+ if (frame_size_mbs <= 3600) return 31; /* Level 3.1 */
|
||
+ if (frame_size_mbs <= 5120) return 32; /* Level 3.2 */
|
||
+ if (frame_size_mbs <= 8192) return 41; /* Level 4.0 - 4.1 */
|
||
+ if (frame_size_mbs <= 8704) return 42; /* Level 4.2 */
|
||
+ if (frame_size_mbs <= 22080) return 50; /* Level 5.0 */
|
||
+ if (frame_size_mbs <= 36864) return 51; /* Level 5.1 - 5.2 */
|
||
+ if (frame_size_mbs <= 139264) return 60; /* Level 6.0 - 6.2 */
|
||
+ return 62; /* > Level 6 ceiling */
|
||
+}
|
||
+
|
||
int h264_set_controls(struct request_data *driver_data,
|
||
struct object_context *context,
|
||
VAProfile profile,
|
||
@@ -705,33 +754,15 @@
|
||
sps.profile_idc = h264_profile_to_idc(profile);
|
||
|
||
/*
|
||
- * VAAPI's decode-side VAPictureParameterBufferH264 does not carry
|
||
- * level_idc — see va.h, the field exists only in
|
||
- * VAEncSequenceParameterBufferH264 on the encode path. The H.264
|
||
- * SPS NAL is also not included in VASliceDataBuffer (ffmpeg-vaapi
|
||
- * parses it client-side and forwards only slice data), so a
|
||
- * SPS-NAL byte extractor is not viable from the bitstream we
|
||
- * receive.
|
||
- *
|
||
- * Hantro and other stateless H.264 decoders use level_idc to
|
||
- * pre-allocate decoder resources (DPB, motion-vector buffers); a
|
||
- * zero-init level_idc=0 is invalid (lowest legal is 10 = Level
|
||
- * 1.0) and causes hantro to silently skip the decode hardware
|
||
- * dispatch.
|
||
- *
|
||
- * Hardcode level_idc = 51 (Level 5.1, max for 1080p/4K@30) as a
|
||
- * known-incomplete intermediate. This INTENTIONALLY OVER-ALLOCATES
|
||
- * decoder resources and is sufficient for any stream up to 4K@30.
|
||
- * It is corpus-correct, not contract-correct.
|
||
- *
|
||
- * TODO: derive level_idc from (VAProfile, picture_width_in_mbs,
|
||
- * picture_height_in_mbs) per H.264 Annex A.3 max-MB-per-second
|
||
- * thresholds. That is a small lookup table but requires also
|
||
- * mapping the consumer's framerate, which VAAPI doesn't provide
|
||
- * directly. For now the over-allocation is the upstreamable
|
||
- * compromise.
|
||
+ * Derive level_idc from encoded frame size per H.264 Annex A.3.
|
||
+ * VAAPI doesn't expose level_idc on the decode side (see
|
||
+ * h264_derive_level_idc()'s docblock for the rationale); we pick
|
||
+ * the smallest level whose MaxFS contains the picture dimensions.
|
||
+ * Replaces patch 0013's intermediate hardcode of 51.
|
||
*/
|
||
- sps.level_idc = 51;
|
||
+ sps.level_idc = h264_derive_level_idc(
|
||
+ (unsigned int)surface->params.h264.picture.picture_width_in_mbs_minus1 + 1u,
|
||
+ (unsigned int)surface->params.h264.picture.picture_height_in_mbs_minus1 + 1u);
|
||
|
||
/*
|
||
* Build the per-request control list incrementally:
|