# Iteration 1 — Phase 7 (verification measurements) Phase 7 verification of the iter1 MPEG-2 fix, executed 2026-05-08 against fork tip `229d6d1` on `git.reauktion.de/marfrit/libva-v4l2-request-fourier`. All five Phase 1 criteria green. Bonus byte-compare against Baseline C confirms structural match with one numerical divergence in an informational field (vbv_buffer_size; kernel ignores; decode is bit-exact correct regardless). Per `feedback_dev_process.md` Phase 7: verbatim raw output is the artifact; this document is the index. Raw captures in [`phase0_evidence/2026-05-08/iter1_phase7/`](phase0_evidence/2026-05-08/iter1_phase7/). ## Pre-flight: rig state ``` $ ssh fresnel 'cd ~/src/libva-v4l2-request-fourier; git status -sb; git log --oneline -1' ## master...origin/master [ahead 4] 229d6d1 fresnel-fourier iter1 Phase 6 commit D: drop missed mpeg2-ctrls.h include from context.c ``` (The `[ahead 4]` is a stale local-tracking artifact: I pushed via the explicit HTTPS+token URL, not via `origin`. gitea has all four commits — verified by the push output `65969da..3aab187` then `3aab187..229d6d1`.) ``` $ sha256sum /usr/lib/dri/v4l2_request_drv_video.so build/src/v4l2_request_drv_video.so 5fb7212ce9c0408401030971f90c0609aec867ede24e320086ff845bd1a2476a /usr/lib/dri/v4l2_request_drv_video.so 5fb7212ce9c0408401030971f90c0609aec867ede24e320086ff845bd1a2476a build/src/v4l2_request_drv_video.so ``` Same hash both sides — install matches build. **Device numbering this boot** (changed across reboot from Phase 0/3): | Driver | This boot | Phase 0/3 boot | |---|---|---| | rkvdec | `/dev/video1` + `/dev/media0` | `/dev/video3` + `/dev/media1` | | hantro-vpu-dec | `/dev/video3` + `/dev/media1` | `/dev/video5` + `/dev/media2` | V4L2 device-number assignment depends on driver-init order, which can shuffle boot-to-boot. Phase 1 binding cells using fixed device paths are fragile; flagged for Phase 8 (memory + iter2+ scope: backend should probe `/dev/media*` for `driver=hantro-vpu` / `rkvdec` rather than rely on env-var device-number stability). ## Criterion 1 — vainfo enumeration regression check **Goal**: vainfo continues to list `VAProfileMPEG2Simple` + `VAProfileMPEG2Main` on the hantro env binding (proves enumerator wasn't broken by iter1 patches). **Verbatim output** ([`criterion1_vainfo.txt`](phase0_evidence/2026-05-08/iter1_phase7/criterion1_vainfo.txt)): ``` $ LIBVA_DRIVER_NAME=v4l2_request \ LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video3 \ LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media1 \ vainfo --display drm --device /dev/dri/renderD128 Trying display: drm vainfo: VA-API version: 1.23 (libva 2.22.0) vainfo: Driver version: v4l2-request vainfo: Supported profile and entrypoints VAProfileMPEG2Simple : VAEntrypointVLD VAProfileMPEG2Main : VAEntrypointVLD ``` **Result: ✅ PASS.** Both profiles present with VAEntrypointVLD. ## Criterion 2 — vaCreateConfig succeeds **Goal**: `vaCreateConfig(VAProfileMPEG2Main, VAEntrypointVLD)` returns `VA_STATUS_SUCCESS`. (Pre-iter1 returned `12 = VA_STATUS_ERROR_UNSUPPORTED_PROFILE` per Phase 3 Baseline A.) **Verbatim libva trace** (extract from `criterion2_3/libva.trace.083913.thd-0x000017e9`): ``` [41265.157833][ctx none]=========vaQueryConfigProfiles ret = VA_STATUS_SUCCESS, success (no error) [41265.157884][ctx none]==========va_TraceCreateConfig [41265.157888][ctx none] profile = 1, VAProfileMPEG2Main [41265.157891][ctx none] entrypoint = 1, VAEntrypointVLD [41265.157894][ctx none] num_attribs = 0 [41265.157946][ctx none]=========vaCreateConfig ret = VA_STATUS_SUCCESS, success (no error) ``` **Result: ✅ PASS.** `vaCreateConfig` returned `VA_STATUS_SUCCESS` for `VAProfileMPEG2Main` (was failing with code 12 in Phase 3 Baseline A). ## Criterion 3 — End-to-end decode engages backend (adjusted Phase 1 wording) **Goal** (Phase 4-amended, Phase 5-confirmed): `ffmpeg -hwaccel vaapi -i bbb_720p10s_mpeg2.ts -frames:v N -f null -` shows the libva chain in stderr, no `Failed to create decode configuration`, no `EINVAL` from `VIDIOC_S_EXT_CTRLS`, exits 0. **Verbatim ffmpeg log** ([`criterion2_3/ffmpeg.stdout`](phase0_evidence/2026-05-08/iter1_phase7/criterion2_3/ffmpeg.stdout)): ``` Input #0, mpegts, from '/home/mfritsche/fourier-test/bbb_720p10s_mpeg2.ts': Stream #0:0[0x100]: Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p(tv, bt709, progressive), 1280x720 Stream mapping: Stream #0:0 -> #0:0 (mpeg2video (native) -> wrapped_avframe (native)) Press [q] to stop, [?] for help v4l2-request: cap_pool_init: 24 slots ready (v4l2_index=0..23, 1 plane(s) per slot) v4l2-request: Unable to set control(s): Invalid argument Output #0, null, to 'pipe:': Stream #0:0: Video: wrapped_avframe, nv12(tv, bt709, progressive), 1280x720 [SAR 1:1 DAR 16:9] frame= 5 fps=0.0 q=-0.0 Lsize=N/A time=00:00:00.20 bitrate=N/A speed=1.46x elapsed=0:00:00.14 [ffmpeg exit 0] ``` **Greps**: - `Failed to create decode configuration`: 0 hits. - `S_EXT_CTRLS.*EINVAL` (in libva trace): 0 hits. **Result: ✅ PASS.** ffmpeg processed 5 frames cleanly, exit 0. The `v4l2-request: Unable to set control(s): Invalid argument` line is the documented auxiliary noise from `src/context.c:142-155` (H.264 device-init unconditionally on every CreateContext, return value cast to `(void)` and discarded). It does NOT come from the MPEG-2 path; my MPEG-2 batched `S_EXT_CTRLS count=3` calls all return 0 (verified verbatim in the bonus byte-compare below). ## Criterion 4 — DMA-BUF GL pixel verify (HW=SW byte-identical at +02s) **Goal** (Phase 5-amended): `mpv --hwdec=vaapi --vo=image` (DMA-BUF GL import path) at `--start=00:00:02` produces 2 distinct frames whose hashes are byte-identical to a software-decoded reference for the same fixture. (Phase 5 Q4 amendment specified `ffmpeg+hwdownload` as primary, but Phase 6 empirically showed that path returns all-zero NV12 because of the iter1 patch-0011 cache-stale class on RK3399. Per the pre-identified fall-forward in Phase 4 plan, mpv-vaapi-vo=image is the cache-coherency-safe verifier — confirmed in T4 for H.264, now confirmed for MPEG-2.) **Verbatim hash output** ([`criterion4/hashes.txt`](phase0_evidence/2026-05-08/iter1_phase7/criterion4/hashes.txt)): ``` 6e7873030dbf0403c67f35dd106ebef3c7909a0fd12433b82ad758e7fee9f092 /tmp/iter1_phase7/criterion4/png_hw/00000001.jpg ccc7ce08810d4a96e9ba7a19f4f95bbf6cc861bda9337604b5c668ad52bef7de /tmp/iter1_phase7/criterion4/png_hw/00000002.jpg 6e7873030dbf0403c67f35dd106ebef3c7909a0fd12433b82ad758e7fee9f092 /tmp/iter1_phase7/criterion4/png_sw/00000001.jpg ccc7ce08810d4a96e9ba7a19f4f95bbf6cc861bda9337604b5c668ad52bef7de /tmp/iter1_phase7/criterion4/png_sw/00000002.jpg ``` | Check | Result | |---|---| | HW frame 1 == SW frame 1 (`6e7873030dbf...`) | ✅ PASS | | HW frame 2 == SW frame 2 (`ccc7ce08810d...`) | ✅ PASS | | frame 1 != frame 2 (real motion) | ✅ PASS | **Result: ✅ PASS.** MPEG-2 hardware decode on RK3399 / hantro-vpu-dec / libva-v4l2-request-fourier produces pixels bit-exact identical to software reference, when read via the cache-coherency-safe DMA-BUF GL import path. JPEG file sizes: 160522 + 178975 bytes — different sizes confirm content variation (real bunny motion at +02s seek into the fixture, not solid color). ## Criterion 5 — H.264 regression check (T4 reference hashes) **Goal**: Re-run T4's reference incantation against `bbb_1080p30_h264.mp4` at `+30s` seek. HW + SW hashes must match the T4 reference values exactly: - `f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9` (frame 1) - `7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8` (frame 2) (Note: this boot routes rkvdec at `/dev/video1` + `/dev/media0`, not `/dev/video3` + `/dev/media1` like Phase 0/T4. Path adjusted accordingly.) **Verbatim hash output** ([`criterion5/hashes.txt`](phase0_evidence/2026-05-08/iter1_phase7/criterion5/hashes.txt)): ``` f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9 /tmp/iter1_phase7/criterion5/png_hw/00000001.jpg 7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8 /tmp/iter1_phase7/criterion5/png_hw/00000002.jpg f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9 /tmp/iter1_phase7/criterion5/png_sw/00000001.jpg 7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8 /tmp/iter1_phase7/criterion5/png_sw/00000002.jpg ``` | Check | Result | |---|---| | HW frame 1 == T4 reference (`f623d5f7...`) | ✅ PASS | | HW frame 2 == T4 reference (`7d7bc6f2...`) | ✅ PASS | | SW frame 1 == T4 reference | ✅ PASS | | SW frame 2 == T4 reference | ✅ PASS | **Result: ✅ PASS.** Iter1's MPEG-2 fix doesn't regress H.264. Both HW and SW H.264 paths produce bit-identical pixels to T4 reference. ## Bonus — byte-compare post-fix VIDIOC_S_EXT_CTRLS payload vs Baseline C **Goal**: Confirm the post-fix V4L2 traffic from our libva backend is structurally indistinguishable from the cross-validator's (ffmpeg-v4l2request) reference per Phase 4 plan: `count=3, ctrl_class=0xf010000`, three CIDs in order, sizes 12 / 32 / 256, with field values matching what VAAPI's `VAPictureParameterBufferMPEG2` and `VAIQMatrixBufferMPEG2` carry for the same fixture. **Verbatim post-fix call** (frame 1, from `bonus/postfix_frame1.txt`): ``` ioctl(5, VIDIOC_S_EXT_CTRLS, {ctrl_class=0xf010000 /* V4L2_CTRL_CLASS_CODEC_STATELESS */, count=3, controls=[ {id=0xa409dc /* SEQUENCE */, size=12, string="\0\5\320\2\0\0\20\0\0\0\1\1"}, {id=0xa409dd /* PICTURE */, size=32, string="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\202\0\0\0\17\17\17\17\1\3\0\0\0\0\0\0"}, {id=0xa409de /* QUANTISATION */, size=256, string="\10\20\20\23\20\23\26\26\26\26\26\26..."} ]}) = 0 ``` **Decoded SEQUENCE field values** (post-fix vs Baseline C): | Field | Post-fix | Baseline C | Match | |---|---|---|---| | `horizontal_size` | 1280 | 1280 | ✅ | | `vertical_size` | 720 | 720 | ✅ | | `vbv_buffer_size` | 0x100000 (1 048 576) | 0x151800 (1 376 256) | ❌ differ | | `profile_and_level_indication` | 0 | 0 | ✅ | | `chroma_format` | 1 (4:2:0) | 1 (4:2:0) | ✅ | | `flags` | 0x01 (PROGRESSIVE) | 0x01 (PROGRESSIVE) | ✅ | **Decoded QUANTISATION** (post-fix): - `intra[0:8] = [8, 16, 16, 19, 16, 19, 22, 22]` - `intra[60:64] = [58, 69, 69, 83]` - `non_intra` all 16's: `True` - `intra_matrix == Baseline C verbatim 64 bytes`: **`True`** **Decoded PICTURE** (post-fix, frame 1 = I-frame): - size 12 + 32 + 256 = 300 bytes total payload, matches Baseline C's per-frame 12+32+256 = 300 byte layout exactly. - I-frame structural fields: `forward_ref_ts = backward_ref_ts = 0`, `flags = 0x82 (FRAME_PRED_DCT | PROGRESSIVE)`, `f_code = 0xF×4`, `picture_coding_type = 1 (I)`, `picture_structure = 3 (FRAME)` — all match Baseline C frame 1 verbatim. ### vbv_buffer_size divergence (informational, non-blocking) The single field divergence is `sequence.vbv_buffer_size`: our backend sends 0x100000 (1 MB = `SOURCE_SIZE_MAX`), Baseline C's ffmpeg-v4l2request sends 0x151800 (1.31 MB = bbb fixture's negotiated `sizeimage`). This **partially confirms Phase 5 reviewer's S2 finding** (which I marked "rejected" in the Phase 5 review response on the assumption that `slot->size = sizeimage = 1382400`). Empirically the libva backend's `surface_object->source_size` resolves to 1 MB, not 1382400 — meaning either: (a) The backend's `S_FMT(OUTPUT_MPLANE)` set `sizeimage = SOURCE_SIZE_MAX` (1 MB) and the kernel's `QUERYBUF` returned that value back through `slot->size` in `request_pool.c:71` rather than re-negotiating to the fixture's actual 1.31 MB. (b) Some other code path in the iter6/7/8 OUTPUT-pool changes made `slot->size` resolve to a constant. Source-read deferred (would require reading `request_pool.c` + `v4l2_create_buffers` paths in detail; this Phase 7 already passes pixel-correct on the boolean criterion, so this is non-blocking informational data). **Kernel ignores `vbv_buffer_size`** per the kernel doc (`v4l2-controls.h:2003`: "vbv_buffer_size: combination of elements vbv_buffer_size_value and vbv_buffer_size_extension" — informational, no validation logic in `hantro_mpeg2.c`). Decode is bit-exact correct (criterion 4 confirms). Net: reviewer's S2 was numerically prescient; my Phase 5 response was wrong about the slot->size = sizeimage equivalence; the operational impact is nil. Worth correcting in `phase5_iter1_review.md` for the historical record. **Phase 8 backlog item** (low priority): track down why `surface_object->source_size = 1 MB` not 1.31 MB. Either fix the libva backend's S_FMT to negotiate, or accept the 1 MB hardcoded value as fine since kernel ignores it. Almost certainly post-iter1 polish. ## Phase 1 → Phase 7 scoreboard | Criterion | Pre-iter1 (Phase 3 baseline) | Post-iter1 (Phase 7 verification) | Verdict | |---|---|---|---| | 1: vainfo enumerates MPEG-2 Simple+Main | ✓ already (config.c:126-127 unconditional) | ✓ unchanged | ✅ PASS | | 2: vaCreateConfig succeeds | ✗ ret = 12 UNSUPPORTED_PROFILE | ✓ ret = VA_STATUS_SUCCESS | ✅ PASS | | 3: ffmpeg engages backend, exit 0 | ✗ Failed to create decode configuration | ✓ frame=5, exit 0, no errors | ✅ PASS | | 4: DMA-BUF GL HW=SW byte-identical at +02s | n/a (couldn't reach decode) | ✓ HW frame 1 = SW frame 1, HW frame 2 = SW frame 2, frame 1 ≠ frame 2 | ✅ PASS | | 5: T4 H.264 reference hashes still match | ✓ baseline | ✓ HW + SW match `f623d5f7...` and `7d7bc6f2...` exactly | ✅ PASS | **All five criteria green. Phase 7 → Phase 8: proceed (no Phase 7 → Phase 4 loopback needed).** ## Notable Phase 7 observations for Phase 8 memory 1. **V4L2 device-numbering shuffles across reboots** on RK3399. Hardcoded env-var paths are fragile. iter2+ Phase 4 cross-cutting fix candidate: backend probes `/dev/media*` for `driver=hantro-vpu` / `rkvdec` rather than relying on env-var stability. 2. **iter1 patch-0011 cache-stale bug class also affects MPEG-2** (not just H.264 from T4). The libva backend's `vaDeriveImage` path returns all-zero NV12 on RK3399 when ffmpeg-vaapi+hwdownload is the readback path. **Workaround**: pixel verification must use DMA-BUF GL import (mpv `--vo=image`), not `vaDeriveImage` / cached mmap. **Phase 4 cross-cutting fix candidate**: add `VIDIOC_EXPBUF` + `DMA_BUF_IOCTL_SYNC` support to the libva backend's image-export path. Tracked separately from per-codec iterations. 3. **`src/context.c:142-155` H.264 device-init produces a noisy EINVAL on hantro** every CreateContext. Documented as auxiliary in Phase 3; intentional best-effort behavior. The `request_log("Unable to set control(s): %s\n", strerror(errno))` from `src/v4l2.c:484` fires unconditionally even though the caller cast the return to `(void)`. Cosmetic fix candidate (suppress the log when called with intent to silently fail), low priority. 4. **vbv_buffer_size discrepancy** (1 MB vs negotiated 1.31 MB) — informational divergence, kernel ignores. Post-iter1 polish item. 5. **The Phase 6 → Phase 4 fix-forward (Commit D)** was caused by an incomplete Phase 2 grep audit (`#include ` was in three files; Phase 2 found two). **Phase 8 lesson**: when deleting a header, the authoritative completeness check is `git rm` followed by clean rebuild — not grep alone. Surfaced in Commit D message; lock into memory at iter1 close. 6. **Phase 5 reviewer's S2 was right after all** about the vbv_buffer_size numerical mismatch. My Phase 5 response that "slot->size = sizeimage = 1382400, exactly matches Baseline C" was wrong — empirically slot->size = 1 MB. Worth correcting in `phase5_iter1_review.md` for the historical record (operational impact: nil; kernel ignores the field). ## Phase 7 close Phase 7 → Phase 8 transition. iter1 advances to memory-update phase. Second-codec passing on the campaign-level scoreboard: 1/5 → **2/5** (H.264 + MPEG-2).