9865416ed2
Review by sonnet-architect with cold-context source reads of fork +
kernel UAPI + VAAPI + FFmpeg references + kernel rkvdec source.
Reviewer applied Direction 2 (empirical-over-theoretical) by
test-compiling struct sizes, gcc-c-checking VAAPI field accesses,
and source-tracing FFmpeg's filter-mode XOR provenance.
Critical findings (all empirically validated by author before
incorporation per feedback_review_empirical_over_theoretical.md):
C1 - interpolation_filter double-XOR: vaapi_vp9.c:62 ALREADY applies
`filtermode ^ (filtermode <= 1)` when filling VAAPI's
mcomp_filter_type. Plan's second XOR was incorrect; would swap
EIGHTTAP and EIGHTTAP_SMOOTH for inter frames -> wrong
loop-filter strength. Fix: direct assignment, no XOR.
C2 - LF deltas not persistent: kernel UAPI explicitly says
"users should pass its last value" when delta_update=0. Plan
memset-zeroed each frame; would send {0,0,0,0,0,0} on BBB inter
frames instead of {1,0,-1,-1,0,0}. Fix: add persistent vp9_lf
state to object_context, init to VP9 spec defaults, update only
when parser sees delta_update=1, always copy to kernel control.
C3 - reference_mode out-parameter missing: reference_mode lives in
FRAME struct, not COMPRESSED_HDR. Plan referenced
`compressed_hdr_reference_mode` placeholder which would be an
undefined identifier -> compile failure. Fix: add
`uint8_t *out_reference_mode` param to vp9_fill_compressed_hdr;
derive `allowcompinter` at call site from the 3 sign biases.
C4 - Mitigation B scope claim overstated: walk-and-pick-first always
selects rkvdec on 7.0 (since video1 enumerates first). Hantro
codecs (MPEG-2, VP8) at video3 still require env override.
Fix: qualify criterion-5 trace; add LIBVA_V4L2_REQUEST_NO_
AUTODETECT=1 escape hatch for legacy callers.
6 Suggested (S1-S6): all confirm plan correctness OR are scope-
aligned non-issues. S4 (uv_mode memcpy omission safe for rkvdec)
baked into Clause 9 amended text.
Without this review, iter4 Phase 6 would have failed first compile
(C3) + produced wrong inter-frame output (C1+C2) + caused user
confusion (C4). Estimated saving: 1 compile failure + 1 Phase 7 ->
Phase 4 loopback + 1 doc correction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
267 lines
20 KiB
Markdown
267 lines
20 KiB
Markdown
# Iteration 4 — Phase 5 (review)
|
||
|
||
Second-model review of `phase4_iter4_plan.md` by sonnet-architect on 2026-05-10. Reviewer performed cold-context source reads of: fork files at iter3 tip (`e1aca9c`), kernel UAPI at `/usr/include/linux/v4l2-controls.h`, VAAPI header `va_dec_vp9.h`, FFmpeg V4L2-request reference `v4l2_request_vp9.c`, FFmpeg VAAPI reference `vaapi_vp9.c`, FFmpeg internal VP9 decoder `vp9.c` + `vp9shared.h`, and kernel sources `v4l2-vp9.c` + `rkvdec-vp9.c`. Struct sizes verified by compile (`gcc`). VAAPI field accesses verified by compile. All Critical findings were verified empirically before inclusion per memory `feedback_review_empirical_over_theoretical.md` Direction 2.
|
||
|
||
**Overall plan health**: The mapping logic for Clauses 1–5 and Clause 8–9's VPX rac port strategy is sound. Three correctness bugs were found: a double-XOR on interpolation_filter (C1, critical for inter frames), a missing persistent-state mechanism for loop-filter deltas across frames (C2, critical for criterion-4 on BBB frames 2–5), and an underspecified function signature for reference_mode extraction from the compressed-header parser (C3). All three will produce Phase 7 byte-mismatches without amendment. Commit Z has a documented but partially-incorrect scope claim (C4).
|
||
|
||
---
|
||
|
||
## Critical findings (BLOCKING Phase 6)
|
||
|
||
### C1 — `interpolation_filter` double-XOR: VAAPI delivers post-remap value; plan re-remaps it
|
||
|
||
**Site**: Clause 10, plan lines ~279–280.
|
||
|
||
**Issue**: The plan applies `mcomp_filter_type ^ (mcomp_filter_type <= 1)` to convert VAAPI's `mcomp_filter_type` to V4L2's `interpolation_filter`. But the FFmpeg VAAPI consumer (`vaapi_vp9.c:62`) already applies this exact XOR when filling `mcomp_filter_type`:
|
||
|
||
```c
|
||
// vaapi_vp9.c:62:
|
||
.mcomp_filter_type = h->h.filtermode ^ (h->h.filtermode <= 1),
|
||
```
|
||
|
||
The XOR swaps FFmpeg's `FILTER_8TAP_SMOOTH` (0) and `FILTER_8TAP_REGULAR` (1) to align with V4L2 enum order (`V4L2_VP9_INTERP_FILTER_EIGHTTAP=0` = regular, `EIGHTTAP_SMOOTH=1`). VAAPI's `mcomp_filter_type` is therefore already in V4L2 order. The plan applying a second XOR undoes the first, reverting to FFmpeg's internal enum — the opposite of what V4L2 expects.
|
||
|
||
**Evidence** (empirically verified by tracing all values):
|
||
|
||
| FFmpeg FilterMode | VAAPI `mcomp_filter_type` (post-XOR) | Plan applies XOR again → | V4L2 expected |
|
||
|---|---|---|---|
|
||
| SMOOTH=0 | 1 (EIGHTTAP_SMOOTH) | 1^1=0 (EIGHTTAP) | **1 WRONG** |
|
||
| REGULAR=1 | 0 (EIGHTTAP) | 0^1=1 (EIGHTTAP_SMOOTH) | **0 WRONG** |
|
||
| SHARP=2 | 2 | 2^0=2 | 2 OK |
|
||
| BILINEAR=3 | 3 | 3^0=3 | 3 OK |
|
||
| SWITCHABLE=4 | 4 | 4^0=4 | 4 OK |
|
||
|
||
`vaapi_vp9.c` verified at `/home/mfritsche/src/libva-multiplanar/references/ffmpeg-kwiboo/libavcodec/vaapi_vp9.c:62`. `v4l2_request_vp9.c` applies XOR to FFmpeg's raw `filtermode` at `v4l2_request_vp9.c:292` — this is the operation that VAAPI already did.
|
||
|
||
The plan's own footnote ("XOR with (0 <= 1)=1 → 1 hmm") flags this discrepancy but leaves it unresolved.
|
||
|
||
The bug affects only SMOOTH and REGULAR — values 2, 3, 4 are unaffected. For keyframes, the interpolation filter is irrelevant (all-intra). The bug surfaces on BBB inter frames (frames 2–5 in the criterion-4 test), where EIGHTTAP_REGULAR is swapped for EIGHTTAP_SMOOTH → wrong deblocking filter selection → criterion-4 byte mismatch.
|
||
|
||
**Proposed fix**:
|
||
|
||
```c
|
||
/* VAAPI mcomp_filter_type is already in V4L2 enum order.
|
||
* vaapi_vp9.c:62 applied the XOR when filling VAAPI's mcomp_filter_type.
|
||
* Direct assignment, no re-remap needed. */
|
||
frame.interpolation_filter = picture->pic_fields.bits.mcomp_filter_type;
|
||
```
|
||
|
||
**Empirical validation**: Author should strace a BBB frame-2 submission under FFmpeg `v4l2request` and read the `interpolation_filter` byte from the FRAME payload (offset 157 in the 168-byte struct). Then LIBVA_TRACE-log the same frame under mpv/ffmpeg-vaapi to extract `mcomp_filter_type`. They should be EQUAL. If the plan's XOR were applied, the byte would differ by 1 for SMOOTH/REGULAR content.
|
||
|
||
---
|
||
|
||
### C2 — Loop-filter `ref_deltas`/`mode_deltas` not persistent across frames
|
||
|
||
**Site**: Clause 6; `vp9_parse_uncompressed_header_lf_quant` design.
|
||
|
||
**Issue**: The kernel UAPI comment for `v4l2_vp9_loop_filter.ref_deltas` explicitly states:
|
||
|
||
> "If this syntax element is not present in the bitstream, users should pass its last value."
|
||
|
||
(`/usr/include/linux/v4l2-controls.h:2578`)
|
||
|
||
The plan zero-inits both structs with `memset` each frame. For frames where `loop_filter_delta_update=0` (deltas not updated in the bitstream, but `loop_filter_delta_enabled=1`), the plan's parser correctly sets the DELTA_ENABLED flag but leaves `ref_deltas` and `mode_deltas` at zero from the memset. The kernel needs the persisted values from the most recent update.
|
||
|
||
FFmpeg handles this correctly by reading from `s->s.h.lf_delta.ref[i]` (persistent internal state, initialized to `{1,0,-1,-1}` on keyframe per `vp9.c:666–669` and updated when `lf_delta.updated=1`):
|
||
|
||
```c
|
||
// v4l2_request_vp9.c:298-302:
|
||
for (i = 0; i < 4; i++)
|
||
ctrl->lf.ref_deltas[i] = s->s.h.lf_delta.ref[i]; // always, even if not updated
|
||
```
|
||
|
||
**Evidence**:
|
||
- FFmpeg `vp9.c:666–669` sets defaults `{1,0,-1,-1,0,0}` on keyframe (empirically confirmed these match Phase 3 anchor values).
|
||
- FFmpeg `vp9.c:693-700` updates from bitstream only when `lf_delta.updated=1`.
|
||
- FFmpeg `v4l2_request_vp9.c:298–302` always copies the persistent state (not conditional on `updated`).
|
||
- Phase 3 anchor frame 1 (keyframe): `lf.flags=3` (DELTA_ENABLED|DELTA_UPDATE). Frames 2–5 very likely have `DELTA_ENABLED=1` but `DELTA_UPDATE=0` (stable content — no change). The plan would send `{0,0,0,0,0,0}` instead of `{1,0,-1,-1,0,0}`.
|
||
- VP9 spec default values `{1,0,-1,-1,0,0}` confirmed at `vp9.c:664–671`: reset occurs on `keyframe || errorres || intraonly`.
|
||
|
||
**Proposed fix**: Add persistent LF delta state to `object_context` (not `object_surface` — context is the right scope for VP9 session state):
|
||
|
||
```c
|
||
// In context.h, inside object_context:
|
||
/* VP9 loop-filter delta state — persisted across frames per VP9 spec */
|
||
struct {
|
||
int8_t ref_deltas[4]; /* init to {1,0,-1,-1} per VP9 spec 8.5.1 */
|
||
int8_t mode_deltas[2]; /* init to {0,0} */
|
||
} vp9_lf;
|
||
```
|
||
|
||
Initialize in `RequestCreateContext` (or at first VP9 frame) to `{1,0,-1,-1,0,0}`. In Clause 6: after parsing the uncompressed header, if `delta_enabled=1 && delta_update=1`, write the new deltas into `context->vp9_lf`. Then always copy `context->vp9_lf` into `frame.lf.ref_deltas`/`frame.lf.mode_deltas` (replacing the parser's local writes).
|
||
|
||
**Empirical validation**: After Phase 6 build, strace BBB frame-2 submission. The `ref_deltas` bytes in the FRAME payload (offset 0–3 in `v4l2_vp9_loop_filter`) should be `{1,0,-1,-1}` matching frame-1's values. If the fix is missing, frame-2 shows `{0,0,0,0}` — detectable by strace.
|
||
|
||
---
|
||
|
||
### C3 — `reference_mode` extraction from compressed-header parser is underspecified
|
||
|
||
**Site**: Clause 10, plan line ~284: `frame.reference_mode = compressed_hdr_reference_mode; (state from Clause 9)`.
|
||
|
||
**Issue**: `comppredmode` (reference_mode) is read from the **compressed** header by FFmpeg's VP9 decoder (`vp9.c:1022–1024`, using the VPX range coder `s->c`). The plan correctly identifies Clause 9 as the source. However, Clause 9 only specifies filling `struct v4l2_ctrl_vp9_compressed_hdr`. The `reference_mode` field is in `struct v4l2_ctrl_vp9_frame`, not `compressed_hdr`. The plan's function signature for `vp9_fill_compressed_hdr` (Clause 9) does not show any mechanism to return this value to Clause 10.
|
||
|
||
**Evidence**: Verified that `struct v4l2_ctrl_vp9_compressed_hdr` (kernel UAPI at `v4l2-controls.h:2833–2851`) has no `reference_mode` or `comppredmode` field. The frame struct (`v4l2_ctrl_vp9_frame:2741–2765`) has `__u8 reference_mode`. FFmpeg's `fill_compressed_hdr` (`v4l2_request_vp9.c:104`) uses a local `comppredmode` variable for internal parsing only; `fill_frame` reads `s->s.h.comppredmode` (persistent decoder state) separately.
|
||
|
||
The placeholder `compressed_hdr_reference_mode` in the plan is a C variable name that doesn't exist in any defined scope. If the author ports `fill_compressed_hdr` without adding an out-parameter, `compressed_hdr_reference_mode` will be an undefined identifier — compile failure.
|
||
|
||
For the **BBB keyframe**: `allowcompinter=0` (all refs have same sign bias on keyframe), so the `if (allowcompinter)` block is not entered and `comppredmode` defaults to `PRED_SINGLEREF (0)` from memset. No bug on keyframe. For BBB inter frames 2–5, `allowcompinter` may be true, and the correct `comppredmode` must be extracted.
|
||
|
||
**Proposed fix**: Add an out-parameter to `vp9_fill_compressed_hdr`:
|
||
|
||
```c
|
||
static void vp9_fill_compressed_hdr(
|
||
struct v4l2_ctrl_vp9_compressed_hdr *compressed_hdr,
|
||
const uint8_t *buf, uint32_t size,
|
||
uint8_t lossless_flag,
|
||
bool keyframe_or_intraonly,
|
||
bool allowcompinter,
|
||
uint8_t *out_reference_mode) /* NEW out-param */
|
||
{
|
||
...
|
||
if (!keyframe_or_intraonly) {
|
||
if (allowcompinter) {
|
||
comppredmode = vp89_rac_get(&c);
|
||
if (comppredmode)
|
||
comppredmode += vp89_rac_get(&c);
|
||
...
|
||
} else {
|
||
comppredmode = 0; /* PRED_SINGLEREF */
|
||
}
|
||
*out_reference_mode = comppredmode;
|
||
} else {
|
||
*out_reference_mode = 0; /* PRED_SINGLEREF for keyframe */
|
||
}
|
||
...
|
||
}
|
||
```
|
||
|
||
`allowcompinter` (VP9 spec: true when sign biases of the 3 active refs are not all identical) must be determined before calling the compressed-header parser. It can be derived from `picture->pic_fields.bits.{last,golden,alt}_ref_frame_sign_bias` values.
|
||
|
||
**Empirical validation**: Before Phase 6 build, confirm that the plan's `vp9_fill_compressed_hdr` function signature includes `out_reference_mode` output. Compile test without it — expect undefined variable. With it — expect clean compile. Phase 7 byte-compare frame-2 `reference_mode` byte (offset 160 in FRAME struct) vs strace of ffmpeg-v4l2request must match.
|
||
|
||
---
|
||
|
||
### C4 — Commit Z 4-codec regression claim is overstated; hantro codecs remain broken without env override
|
||
|
||
**Site**: Commit Z description + Phase 1 criteria table (Criterion 5 row).
|
||
|
||
**Issue**: The plan states: "Commit Z restores baseline functionality on 7.0" and the criterion-5 trace says "Commit Z restores baseline (mitigation B)." This is **partially incorrect**. Commit Z's `find_codec_device` walks `/dev/video0..15` and picks the **first** device whose driver name matches `{rkvdec, hantro-vpu, cedrus, sun4i_csi}`. On 7.0:
|
||
|
||
```
|
||
video0 = rockchip-rga (skip)
|
||
video1 = rkvdec (MATCH → returns rkvdec)
|
||
video2 = hantro-enc (never reached)
|
||
video3 = hantro-dec (never reached)
|
||
```
|
||
|
||
Result: `find_codec_device` always returns rkvdec (`video1`). H.264, HEVC, VP9 (all rkvdec) benefit. MPEG-2 and VP8 (hantro at `video3`) still get the wrong device — they will see 0 supported profiles because rkvdec doesn't advertise MPEG-2/VP8 pixel formats.
|
||
|
||
This is actually documented in the plan's "End-user UX gap" paragraph: "If user wants the OTHER decoder (e.g., hantro for MPEG-2/VP8), they still need env override." But the criterion-5 trace says Commit Z "restores baseline" — contradicting the UX gap note. The 4-codec regression is NOT fully fixed by Commit Z alone.
|
||
|
||
**Evidence**: Phase 3 baseline table shows all 4 codecs needed env override on 7.0. Commit Z only eliminates the override for rkvdec codecs. Hantro codecs still require `LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video3 LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media1`.
|
||
|
||
**Proposed fix**: Amend the criterion-5 trace to accurately state: "Commit Z restores rkvdec-codec baseline (H.264, HEVC) and enables VP9 enumeration. MPEG-2 and VP8 (hantro) still require explicit env override." This is a documentation correctness fix, not a code fix. The code behavior is acceptable for iter4 scope (iter4-B1 already tracks full multi-decoder dispatch).
|
||
|
||
Additionally, the plan should add `LIBVA_V4L2_REQUEST_NO_AUTODETECT=1` as a safety knob so existing test automation that relied on `/dev/video0` can opt out of the new walk:
|
||
|
||
```c
|
||
if (getenv("LIBVA_V4L2_REQUEST_NO_AUTODETECT"))
|
||
goto old_fallback; /* skip walk, use /dev/video0 as before */
|
||
```
|
||
|
||
**Empirical validation**: Before merging Commit Z, verify `vainfo` without env override enumerates H.264/HEVC profiles (rkvdec) but NOT MPEG-2/VP8. Verify `vainfo` WITH `LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video3` enumerates MPEG-2/VP8. This confirms the scope is correct.
|
||
|
||
---
|
||
|
||
## Suggested findings (NON-BLOCKING)
|
||
|
||
### S1 — Compile-time assertions: struct sizes are stable on 7.0 UAPI but volatile across kernel major versions
|
||
|
||
**Site**: Clause 3.
|
||
|
||
The `_Static_assert` values 168 and 2040 were verified empirically by test-compile against `/usr/include/linux/v4l2-controls.h` on this system:
|
||
|
||
```
|
||
$ gcc -c /tmp/vp9_size_check.c && ./vp9_size_check
|
||
v4l2_ctrl_vp9_frame: 168
|
||
v4l2_ctrl_vp9_compressed_hdr: 2040
|
||
```
|
||
|
||
These match the Phase 3 strace exactly. The plan's assertion strategy is sound.
|
||
|
||
Historical stability note: Phase 2 estimated 144/1947 vs Phase 3 empirical 168/2040 — a delta of +24/+93 bytes between the Phase 2 reading and the live 7.0 kernel. This confirms that struct sizes CAN change between kernel versions. The `_Static_assert` approach is the right response: it turns any future growth into a loud build failure, as designed. No change needed.
|
||
|
||
### S2 — `seg.pred_probs` 255-fill: plan's direct copy is already correct
|
||
|
||
**Site**: Clause 7.
|
||
|
||
FFmpeg `vaapi_vp9.c:96–100` fills `segment_pred_probs` with 255 for the non-temporal case before handing the VAAPI buffer to the backend. The plan's direct `for (i=0;i<3;i++) frame.seg.pred_probs[i] = picture->segment_pred_probs[i]` is correct — it receives already-padded values. FFmpeg `v4l2_request_vp9.c:336–341` does the same 255-fill but independently (for the V4L2 path). Both produce identical output. No fix needed.
|
||
|
||
### S3 — `reset_frame_context` FFmpeg remap is correct as applied to VAAPI
|
||
|
||
**Site**: Clause 10.
|
||
|
||
Verified: `vaapi_vp9.c:64` sets `.reset_frame_context = h->h.resetctx` — raw VP9 spec value (0..3), NO pre-remap. The plan's `(resetctx > 0 ? resetctx - 1 : 0)` correctly maps: 0→0(NONE), 1→0(NONE, spec says both 0 and 1 mean no-reset), 2→1(SPEC), 3→2(ALL). This matches V4L2 enum constants. Correct.
|
||
|
||
### S4 — `uv_mode` memcpy omission is safe for rkvdec
|
||
|
||
**Site**: Clause 9 (compressed header port).
|
||
|
||
FFmpeg `fill_compressed_hdr` ends with `memcpy(ctrl->uv_mode, s->prob.p.uv_mode, sizeof(ctrl->uv_mode))`. This sets `compressed_hdr.uv_mode` to current probability values. The plan's port does not mention this memcpy.
|
||
|
||
Verified: `v4l2_vp9_fw_update_probs` (`v4l2-vp9.c:1415–1444`) does NOT call any `update_uv_mode_probs` function — there is no `uv_mode` update path from the compressed_hdr struct. `rkvdec-vp9.c:280–287` reads `uv_mode` from `vp9_ctx->probability_tables` (the frame_context table), not from `prob_updates` (the compressed_hdr ctrl). The memcpy in FFmpeg's `fill_compressed_hdr` fills a field that rkvdec never reads from the compressed_hdr control. Omission is safe.
|
||
|
||
### S5 — `lossless_flag` semantics align exactly
|
||
|
||
**Site**: Clause 9.
|
||
|
||
VAAPI (`va_dec_vp9.h:131–140`): `LosslessFlag = base_qindex==0 && y_dc_delta_q==0 && uv_dc_delta_q==0 && uv_ac_delta_q==0`. FFmpeg VP9 decoder (`vp9shared.h:136`): `uint8_t lossless` set by the same condition. FFmpeg `vaapi_vp9.c:78`: `.lossless_flag = h->h.lossless` — direct assignment, no remap. Semantics are identical. Plan's `if (picture->pic_fields.bits.lossless_flag) ctrl->tx_mode = V4L2_VP9_TX_MODE_ONLY_4X4` is correct.
|
||
|
||
### S6 — Per-segment ALT_Q/ALT_L mapping gap is correctly identified but needs explicit V4L2 `UPDATE_DATA` flag handling
|
||
|
||
**Site**: Clause 7.
|
||
|
||
The plan leaves `V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA` and `V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE` zero for all frames (since VAAPI doesn't expose `segmentation_update_data` or `absolute_vals`). For BBB (segmentation disabled), these flags are irrelevant. The plan documents this as a fidelity gap. This is correct scope-management for iter4.
|
||
|
||
Minor clarification worth adding to the plan comment: the kernel only reads `feature_data[]` and `feature_enabled[]` when `SEGMENTATION_FLAG_ENABLED` is set. Since BBB has `ENABLED=0`, the zero'd feature_data arrays are never examined. Safe for iter4 scope.
|
||
|
||
---
|
||
|
||
## Direct answers to the 7 queued questions
|
||
|
||
**Q1 — Uncompressed-header parser correctness (Clause 6)**
|
||
|
||
The spec parse path for BBB's keyframe to reach `loop_filter_params()` requires correctly parsing: frame_marker (2 bits) → profile (2 bits) → show_existing_frame (1 bit) → frame_type (1 bit) → show_frame (1 bit) → error_resilient_mode (1 bit) → frame_sync_code (3 bytes) → color_config() [variable length, profile-dependent] → frame_size() [2×16 bits] → render_and_frame_size_different (1 bit) → refresh_frame_flags (8 bits) → loop_filter_params(). The `color_config()` and inter-frame reference frame stuff make the path non-trivial but well-defined for profile=0 keyframe. The plan's "~50 LOC" estimate is conservative — a faithful profile-0 keyframe + profile-0 inter-frame parse is closer to 80–100 LOC. The Phase 3 anchor (`lf.ref_deltas={1,0,-1,-1}, lf.flags=3, quant.base_q_idx=46`) is the correct empirical reference. See **C2** for the critical persistence issue.
|
||
|
||
**Q2 — `reset_frame_context` and `interpolation_filter` remap**
|
||
|
||
`reset_frame_context`: correct, no issue (see S3). `interpolation_filter`: WRONG — double-XOR. See **C1**. Fix: direct assignment `frame.interpolation_filter = picture->pic_fields.bits.mcomp_filter_type` with no XOR.
|
||
|
||
**Q3 — Compile-time size assertions stability**
|
||
|
||
168/2040 are correct on 7.0 UAPI. Verified by test-compile. Assertions are appropriate and will fire loudly on kernel UAPI bump. If assertions fire, re-run Phase 3 baseline capture against the new kernel — do not just update the numbers. See **S1**.
|
||
|
||
**Q4 — Per-segment ALT_Q mapping for non-BBB segmentation-enabled streams**
|
||
|
||
VAAPI's `seg_param[s].luma_ac_quant_scale` is the dequantization scale `y_dequant[qindex][1]` (confirmed by `vaapi_vp9.c:153`: `.luma_ac_quant_scale = h->h.segmentation.feat[i].qmul[0][1]`). The kernel needs the raw Q-index delta (ALT_Q), NOT the dequant scale. The inverse mapping would require the VP9 quantization table lookup (spec table in section 8.3.1) which is non-trivial. The plan correctly identifies this as an iter4-Q6 fidelity gap. For BBB it is dead code. For segmentation-enabled streams, the mapping is fundamentally broken without the Q-table inverse. Recommend flagging in Commit B header comment. The flag `V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA` also cannot be set without knowing `segmentation_update_data` from the uncompressed header — another VAAPI gap. All correctly documented.
|
||
|
||
**Q5 — Test compile field availability**
|
||
|
||
All VAAPI field accesses in Clauses 4, 5, 7, and 10 compile cleanly. Verified by `gcc -c` test against inline-definition of the VAAPI structs matching `va_dec_vp9.h`. Fields tested: `frame_width`, `frame_height`, `profile`, `bit_depth`, `log2_tile_columns`, `log2_tile_rows`, `pic_fields.bits.frame_context_idx`, `filter_level`, `sharpness_level`, `frame_header_length_in_bytes`, `first_partition_size`, all `pic_fields.bits.*` in Clauses 5 and 10, `mb_segment_tree_probs[]`, `segment_pred_probs[]`, `seg_param[].segment_flags.fields.*`. All present. Note: `pic_fields.bits.frame_context_idx` is 2 bits (not 8) — assignment to `__u8 frame_context_idx` in the V4L2 struct is safe (value fits: 0..3).
|
||
|
||
**Q6 — Mitigation B regression risk**
|
||
|
||
See **C4** for the detailed finding. Short answer: Commit Z's walk-and-pick-first selects rkvdec for ALL codecs, not hantro. MPEG-2 and VP8 remain broken without env override. The criterion-5 regression claim is overstated. The `LIBVA_V4L2_REQUEST_NO_AUTODETECT=1` safety knob is recommended as a Suggested addition (see C4).
|
||
|
||
**Q7 — `lossless_flag` semantic alignment**
|
||
|
||
Semantics are identical between VAAPI and FFmpeg. See **S5**. Plan's usage is correct.
|
||
|
||
---
|
||
|
||
## Verdict
|
||
|
||
Plan needs two code amendments (C1 + C2) before Phase 6 implementation, plus one function-signature clarification (C3) and one documentation correction (C4). C1 is a one-line fix. C2 requires adding 6 bytes of persistent state to `object_context` and threading it through the Clause 6 parser call. C3 requires adding an `out_reference_mode` parameter to `vp9_fill_compressed_hdr`. C4 is a comment/documentation fix plus an optional safety env var. None require re-baselining or a Phase 3 re-capture.
|
||
|
||
**Plan ready for Phase 6 after C1 + C2 + C3 amendments. C4 is strongly recommended but may be accepted as a known scope limitation note.**
|