# 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</dev/video3>, 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 <mpeg2-ctrls.h>` 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).
