Files
marfrit-packages/arch/libva-v4l2-request-ohm-gl-fix/0018-h264-level-idc-from-annex-a3.patch
T
test0r 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
Add libva-v4l2-request-ohm-gl-fix package
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)
2026-05-02 15:17:10 +00:00

160 lines
6.9 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: