iter1 Phase 7: verification — all 5 criteria GREEN, second codec PASS

Phase 7 verification of iter1 MPEG-2 fix executed against fork tip
229d6d1 (libva-v4l2-request-fourier master = post-Commit-D).
Verbatim raw output captured to phase0_evidence/2026-05-08/
iter1_phase7/. All five Phase 1 criteria green; bonus byte-compare
confirms structural match against Baseline C with one numerical
divergence (vbv_buffer_size, kernel-ignored, non-blocking).

Phase 1 → Phase 7 scoreboard:

  Criterion 1 (vainfo MPEG-2 Simple+Main enum):           PASS
  Criterion 2 (vaCreateConfig SUCCESS for MPEG2Main):     PASS
    Pre-iter1: VA_STATUS_ERROR_UNSUPPORTED_PROFILE (12)
    Post-iter1: VA_STATUS_SUCCESS (verified verbatim libva trace)
  Criterion 3 (ffmpeg-hwaccel-vaapi engages backend):     PASS
    5 frames decoded, exit 0, no Failed-to-create lines,
    no S_EXT_CTRLS EINVAL on the MPEG-2 path
  Criterion 4 (DMA-BUF GL HW=SW byte-identical at +02s):  PASS
    HW frame 1: 6e7873030dbf0403c67f35dd106ebef3c7909a0fd12433b82ad758e7fee9f092
    SW frame 1: 6e7873030dbf0403c67f35dd106ebef3c7909a0fd12433b82ad758e7fee9f092
    HW frame 2: ccc7ce08810d4a96e9ba7a19f4f95bbf6cc861bda9337604b5c668ad52bef7de
    SW frame 2: ccc7ce08810d4a96e9ba7a19f4f95bbf6cc861bda9337604b5c668ad52bef7de
    Frames 1 vs 2 differ in size (real motion).
  Criterion 5 (T4 H.264 reference hashes match):          PASS
    HW + SW frames at +30s into bbb_1080p30_h264.mp4 match
    f623d5f7... and 7d7bc6f2... exactly. No H.264 regression.

Bonus byte-compare against Phase 3 Baseline C verbatim:

  count=3, ctrl_class=V4L2_CTRL_CLASS_CODEC_STATELESS=0xf010000:
    SEQUENCE     id=0xa409dc size=12   (matches)
    PICTURE      id=0xa409dd size=32   (matches structurally)
    QUANTISATION id=0xa409de size=256  (intra matrix bytes
                                        IDENTICAL to Baseline C
                                        verbatim 64 bytes;
                                        non_intra all 16's)
    All return = 0 (kernel accepts every batched call).

  One numerical divergence: sequence.vbv_buffer_size
    post-fix:    0x100000 = 1 048 576 (= SOURCE_SIZE_MAX)
    Baseline C:  0x151800 = 1 376 256 (= negotiated sizeimage)
    Kernel ignores per v4l2-controls.h:2003 (informational).
    Decode is bit-exact correct regardless. Phase 5 reviewer S2
    was numerically prescient; my Phase 5 response (rejected with
    "slot->size = sizeimage") was wrong empirically; operational
    impact nil. Tracked as low-priority post-iter1 polish.

Phase 7 → Phase 8: clean transition, no loopback to Phase 4.

Notable observations for Phase 8 memory update:

  1. V4L2 /dev/videoN numbering shuffles across reboots on RK3399.
     Phase 0/3 had rkvdec=video3+media1, hantro=video5+media2; this
     boot has rkvdec=video1+media0, hantro=video3+media1. Phase 1
     binding cells using fixed paths fragile across reboots. Phase
     4 cross-cutting fix candidate: backend probes /dev/media* for
     driver=hantro-vpu/rkvdec rather than env-var stability.

  2. iter1 patch-0011 cache-stale bug class also affects MPEG-2
     (verified empirically; same as H.264 in T4). vaDeriveImage
     readback returns all-zero NV12 via ffmpeg-vaapi+hwdownload.
     Workaround: DMA-BUF GL import (mpv --vo=image) is cache-
     coherency-safe. Phase 4 cross-cutting fix candidate: add
     VIDIOC_EXPBUF + DMA_BUF_IOCTL_SYNC support to libva backend
     image-export path.

  3. src/context.c:142-155 H.264 device-init logs noisy EINVAL on
     hantro every CreateContext (return value cast to (void) but
     v4l2.c:484 still calls request_log). Cosmetic suppression
     candidate; low priority.

  4. Phase 6 commit D (fix-forward for missed mpeg2-ctrls.h
     include in context.c) — Phase 2 grep audit was incomplete.
     Phase 8 lesson: when deleting a header, completeness check
     is git rm + clean rebuild, not grep alone.

Campaign scoreboard: 1/5 → 2/5 codecs passing
(H.264 in T4, MPEG-2 in iter1). Iter1 advances to Phase 8.

Refs:
  ../libva-v4l2-request-fourier@229d6d1 (the fork tip verified)
  phase4_iter1_plan.md (criteria as locked, including Phase 5
                        amendments to criterion 3 + criterion 4)
  phase5_iter1_review.md (S2 partial-correct; S3, Q4, Q5
                          confirmed empirically)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 09:00:14 +00:00
parent 0e2e1c2293
commit ec9133a5e4
2 changed files with 254 additions and 0 deletions
@@ -0,0 +1,22 @@
Input #0, mpegts, from '/home/mfritsche/fourier-test/bbb_720p10s_mpeg2.ts':
Duration: 00:00:10.00, start: 1.441667, bitrate: 4202 kb/s
Program 1
Metadata:
service_name : Service01
service_provider: FFmpeg
Stream #0:0[0x100]: Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p(tv, bt709, progressive), 1280x720 [SAR 1:1 DAR 16:9], 24 fps, 24 tbr, 90k tbn, start 1.441667
Side data:
CPB properties: bitrate max/min/avg: 0/0/0 buffer size: 1130496 vbv_delay: N/A
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:':
Metadata:
encoder : Lavf62.12.100
Stream #0:0: Video: wrapped_avframe, nv12(tv, bt709, progressive), 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 24 fps, 24 tbn
Metadata:
encoder : Lavc62.28.100 wrapped_avframe
[out#0/null @ 0xaaaabf90c500] video:2KiB audio:0KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: unknown
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
+232
View File
@@ -0,0 +1,232 @@
# 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).