Files
fresnel-fourier/phase3_iter1_baseline.md
T
claude-noether b9625af278 iter1 Phase 3: baseline measurements — Phase 2 confirmed empirically
Four Phase 3 baselines captured on fresnel post-reboot 2026-05-08
00:39 CEST. SDDM watchpoint condition stayed green (greeter passed
cleanly on the new boot). All four baselines confirm Phase 2's
situation analysis empirically; one Phase 1 criterion needs minor
adjustment (Phase 3 → Phase 1 loopback per feedback_dev_process.md).

Baseline A — pre-patch failure mode (master tip 65969da):

  ffmpeg -hwaccel vaapi -i bbb_720p10s_mpeg2.ts ... under strace +
  LIBVA_TRACE captures the chain:

  vaInitialize ret = SUCCESS
  vaQueryConfigProfiles ret = SUCCESS
  vaCreateConfig(profile=VAProfileMPEG2Main, entrypoint=VLD)
    ret = VA_STATUS_ERROR_UNSUPPORTED_PROFILE

  No V4L2 ioctls beyond ENUM_FMT probes from RequestQueryConfigProfiles.
  Confirms Phase 2 Bug 1 (config.c:55-69 fall-through to default).

Baseline B — post Bug 1 scratch patch (the missing break added):

  vaCreateConfig now returns SUCCESS. V4L2 setup proceeds:
  CREATE_BUFS, QUERYBUF (40), REQBUFS, STREAMON, S_FMT, etc.
  Then VIDIOC_S_EXT_CTRLS fails:

    ioctl(/dev/video5, VIDIOC_S_EXT_CTRLS,
          {ctrl_class=0xf010000,
           count=1,
           controls=[
             {id=V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS,
              size=56, ...}
           ]})
        = -1 EINVAL

  CID 0x9909fa (V4L2_CID_MPEG_BASE+250) doesn't exist on this kernel —
  mainline removed it in favor of the split V4L2_CID_STATELESS_MPEG2_*
  CIDs. Size 56 = sizeof(combined v4l2_ctrl_mpeg2_slice_params) from
  the fork's local include/mpeg2-ctrls.h. Confirms Phase 2 Bug 2.

  Auxiliary EINVAL: src/context.c:142-155 unconditionally sets H.264
  device-wide controls (H264_DECODE_MODE, H264_START_CODE) on every
  CreateContext, regardless of profile. EINVALs on hantro-vpu-dec
  (no H.264 controls there). Intentional best-effort behavior —
  return value is cast to (void) and discarded. Auxiliary, not iter1
  scope.

Baseline C — cross-validator verbatim contract anchor:

  ffmpeg -hwaccel v4l2request strace shows ONE batched call per frame:

    ioctl(/dev/video5, VIDIOC_S_EXT_CTRLS,
          {ctrl_class=0xf010000,    // V4L2_CTRL_CLASS_CODEC_STATELESS
           count=3,
           controls=[
             {id=0xa409dc, size=12,  ...},   // SEQUENCE
             {id=0xa409dd, size=32,  ...},   // PICTURE
             {id=0xa409de, size=256, ...}    // QUANTISATION
           ]}) = 0

  Field-by-field decode of frame 1 (I-picture):
    SEQUENCE: 1280×720, vbv=0x151800, profile_level=0,
              chroma_format=1, flags=PROGRESSIVE
    PICTURE: back/fwd_ref_ts=0/0, flags=0x82
             (FRAME_PRED_DCT|PROGRESSIVE), f_code=0xF×4 (I-frame
             default), P_C_T=1 (I), structure=3 (FRAME),
             intra_dc_precision=0
    QUANTISATION: starts [8, 16, 16, 19, 16, 19, 22, 22, ...] —
                  canonical MPEG-2 default intra matrix in zigzag
                  scanning order.

  Frame 2 (P-picture) shows real f_code values {{1,1},{15,15}}
  and forward_ref_ts pointing to frame 1's timestamp. Confirms
  Phase 2's claim that matrices arrive in zigzag order;
  no permutation needed in the libva backend (kernel's
  hantro_mpeg2_dec_copy_qtable handles zigzag-to-raster).

  This is the iter1 contract anchor: every Phase 4 implementation
  diff must produce a structurally indistinguishable
  VIDIOC_S_EXT_CTRLS call.

Baseline D — H.264 regression check (Phase 1 criterion #5):

  T4 reference hashes match exactly with scratch Bug 1 fix installed:
    HW frame 1: f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9
    SW frame 1: f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9
    HW frame 2: 7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8
    SW frame 2: 7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8

  Bug 1 fix in isolation does not regress H.264.

Phase 1 criterion #3 needs adjustment (Phase 3 → Phase 1 loopback):

  Original wording: "mpv --hwdec=vaapi-copy ... engages the backend"
  Reality: mpv-vaapi-copy never loads libva for MPEG-2. mpv's hwdec
  policy filters MPEG-2 out before libva is touched. Zero V4L2
  ioctls, zero libva trace, silent SW fallback. Independent of
  Bug 1 fix state.

  Adjusted criterion #3 (proposed; locks alongside Phase 4 plan):
    "ffmpeg -hwaccel vaapi -i bbb_720p10s_mpeg2.ts -frames:v 2
     -f null - shows vaCreateConfig SUCCESS, no Failed to create
     decode configuration lines, no EINVAL from VIDIOC_S_EXT_CTRLS,
     exits 0 cleanly."

  mpv-driven testing moves to a follow-up task (mpv hwdec-codecs
  filter override), separate from iter1.

Other 4 Phase 1 criteria (vainfo regression, vaCreateConfig SUCCESS,
DMA-BUF GL pixel verify HW=SW, T4 H.264 regression) hold as locked.

Scratch state cleanup: scratch patch reverted, master backend
reinstalled, MPEG-2 fails again with vaCreateConfig=12 — back to
Baseline A state, no leak.

Phase 4 plan inputs:

  - Diff scope: src/config.c (1 break), src/mpeg2.c (rewrite to
    new API), include/mpeg2-ctrls.h (delete or empty). picture.c
    + context.c unchanged.
  - Contract anchor: cite verbatim from
    linux/v4l2-controls.h:1985-2105, FFmpeg
    libavcodec/v4l2_request_mpeg2.c:130-155, kernel
    drivers/media/platform/verisilicon/hantro_mpeg2.c, AND this
    document's Baseline C verbatim payload.
  - Phase 7 verification: re-run all 5 Phase 1 criteria
    (with #3 adjusted), byte-by-byte compare post-fix
    VIDIOC_S_EXT_CTRLS payload against Baseline C.

Evidence files:

  Tracked (text):
    phase3_iter1_baseline.md (writeup with verbatim raw output)
    phase0_evidence/2026-05-07/iter1_phase3/baseline_A_ffmpeg/ffmpeg.stdout
    phase0_evidence/2026-05-07/iter1_phase3/baseline_B_postbug1/ffmpeg.stdout
    phase0_evidence/2026-05-07/iter1_phase3/baseline_C_xvalidator/ffmpeg.stdout

  Gitignored (regenerable from re-run incantations in the writeup):
    *.strace.*  *.txt (ftrace) libva.trace.* (added the latter pattern)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 04:04:25 +00:00

368 lines
22 KiB
Markdown
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.
# Iteration 1 — Phase 3 (baseline measurements)
Phase 3 baselines for the iter1 MPEG-2 boolean-correctness work, captured 2026-05-08 00:39 CEST after fresnel reboot. All four baseline runs and the verbatim output anchors are in [`phase0_evidence/2026-05-07/iter1_phase3/`](phase0_evidence/2026-05-07/iter1_phase3/).
Phase 3 discipline (per `feedback_dev_process.md`): raw data leads, derived numbers are anchored to the visible tool invocation that produced them. Every "this matches Phase 2 prediction" claim cites a verbatim line from the captured trace, not a paraphrase.
## Reset context
```
$ ssh fresnel 'hostname; uname -r; uptime'
fresnel
6.19.9-99-eos-arm
00:31:35 up 4 min, 2 users, load average: 1.01, 0.70, 0.34
$ loginctl list-sessions --no-pager | head
SESSION UID USER SEAT LEADER CLASS TTY IDLE SINCE
2 1000 mfritsche seat0 2244 user tty1 no -
```
fresnel rebooted between Phase 2 and Phase 3 (uptime 4 min explains the `No route to host` Phase 2 noted). SDDM watchpoint condition not triggered: `journalctl -u sddm -b | grep -E "(qFatal|crash|Error|fatal|SIGABRT)"` returns empty for this boot. Plasma Wayland session active on tty1, mfritsche logged in.
## Baseline A — pre-patch failure mode
**Goal**: capture the exact path that produces `vaCreateConfig: 12 (UNSUPPORTED_PROFILE)` on master tip `65969da`, before any iter1 work touches the code.
**Invocation**:
```bash
strace -ff -tt -y -e trace=ioctl,openat,close \
-o /tmp/iter1_phase3/baseline_A_ffmpeg/ffmpeg.strace \
env LIBVA_DRIVER_NAME=v4l2_request \
LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video5 \
LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media2 \
LIBVA_TRACE=/tmp/iter1_phase3/baseline_A_ffmpeg/libva.trace \
ffmpeg -hide_banner -loglevel debug -hwaccel vaapi \
-i ~/fourier-test/bbb_720p10s_mpeg2.ts -frames:v 2 -f null -
```
**Verbatim libva trace** ([`baseline_A_ffmpeg/libva.trace.*`](phase0_evidence/2026-05-07/iter1_phase3/baseline_A_ffmpeg/)):
```
[5276.234210][ctx none] VA-API vendor string: v4l2-request
[5276.234352][ctx none] vaInitialize ret = VA_STATUS_SUCCESS, success (no error)
[5276.264142][ctx none] vaQueryConfigProfiles ret = VA_STATUS_SUCCESS, success (no error)
[5276.264347][ctx none] va_TraceCreateConfig
[5276.264351][ctx none] profile = 1, VAProfileMPEG2Main
[5276.264354][ctx none] entrypoint = 1, VAEntrypointVLD
[5276.264357][ctx none] num_attribs = 0
[5276.264606][ctx none] vaCreateConfig ret = VA_STATUS_ERROR_UNSUPPORTED_PROFILE, the requested VAProfile is not supported
[5276.425879][ctx none] va_TraceTerminate
[5276.426283][ctx none] vaTerminate ret = VA_STATUS_SUCCESS, success (no error)
```
**Verbatim ffmpeg log** ([`baseline_A_ffmpeg/ffmpeg.stdout`](phase0_evidence/2026-05-07/iter1_phase3/baseline_A_ffmpeg/ffmpeg.stdout)):
```
[VAAPI @ 0xaaaaef94e010] libva: User environment variable requested driver 'v4l2_request'
[VAAPI @ 0xaaaaef94e010] libva: Trying to open /usr/lib/dri/v4l2_request_drv_video.so
[VAAPI @ 0xaaaaef94e010] libva: Found init function __vaDriverInit_1_23
[VAAPI @ 0xaaaaef94e010] Initialised VAAPI connection: version 1.23
[VAAPI @ 0xaaaaef94e010] VAAPI driver: v4l2-request.
[mpeg2video @ 0xaaaaef99ee10] Format vaapi chosen by get_format().
[mpeg2video @ 0xaaaaef99ee10] Format vaapi requires hwaccel mpeg2_vaapi initialisation.
[mpeg2video @ 0xaaaaef99ee10] Failed to create decode configuration: 12 (the requested VAProfile is not supported).
[mpeg2video @ 0xaaaaef99ee10] Failed setup for format vaapi: hwaccel initialisation returned error.
[mpeg2video @ 0xaaaaef99ee10] Format vaapi not usable, retrying get_format() without it.
```
**Verbatim ioctl summary**:
```
10 ioctl(6</dev/video5>, VIDIOC_ENUM_FMT
4 ioctl(4</dev/dri/renderD128>, DRM_IOCTL_VERSION
1 ioctl(6</dev/video5>, VIDIOC_QUERYCAP
```
12 ftrace v4l2 lines (the ENUM_FMT probe events). Zero VIDIOC_S_FMT, zero REQBUFS, zero S_EXT_CTRLS, zero MEDIA_IOC_REQUEST_ALLOC. Decode never starts.
**Confirms Phase 2 Bug 1 directly**: `vaCreateConfig` returns `VA_STATUS_ERROR_UNSUPPORTED_PROFILE` for `VAProfileMPEG2Main` — the missing `break;` at `src/config.c:65-66` makes execution fall through to `default:` (line 67-68) which returns the error code we observe (`12`).
**mpv-as-driver does not reach this path**: a separate baseline run with `mpv --hwdec=vaapi-copy` instead of `ffmpeg -hwaccel vaapi` showed mpv silently fall back to SW decode without ever loading libva for MPEG-2 (no `[vaapi]` log lines, zero V4L2 ioctls, zero libva trace files). mpv's hwdec policy filters MPEG-2 out of vaapi-copy candidates before libva is touched. **Phase 1 success criterion #3 (mpv MPEG-2 engages backend) requires either an mpv config override or a different invocation path** — to be addressed in Phase 4 plan. ffmpeg-direct invocations exercise the full path.
## Baseline B — post Bug 1 patch failure mode
**Goal**: with the missing `break;` added as a Phase 3 scratch test, see what fails downstream. Predicted by Phase 2: `VIDIOC_S_EXT_CTRLS` returns `EINVAL` because the staging-era CID `V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS` (= `0x9909fa`) doesn't exist on this kernel.
**Patch applied** (scratch only, on throwaway branch `iter1-phase3-scratch`, reverted at end of Phase 3):
```diff
@@ -63,6 +63,8 @@ VAStatus RequestCreateConfig(VADriverContextP context, VAProfile profile,
break;
case VAProfileMPEG2Simple:
case VAProfileMPEG2Main:
+ // Phase 3 scratch — would break here in real fix
+ break;
case VAProfileHEVCMain:
default:
return VA_STATUS_ERROR_UNSUPPORTED_PROFILE;
```
Rebuilt + installed (`ninja -C build && sudo ninja -C build install`).
**Verbatim libva trace** showing config-create now succeeds:
```
[5329.689037][ctx none] vaQueryConfigProfiles ret = VA_STATUS_SUCCESS
[5329.689228][ctx none] va_TraceCreateConfig
[5329.689233][ctx none] profile = 1, VAProfileMPEG2Main
[5329.689237][ctx none] entrypoint = 1, VAEntrypointVLD
[5329.689239][ctx none] num_attribs = 0
[5329.689471][ctx none] vaCreateConfig ret = VA_STATUS_SUCCESS, success (no error)
[5329.689703][ctx none] vaQuerySurfaceAttributes ret = VA_STATUS_SUCCESS
... (9 more vaQuerySurfaceAttributes calls; surface format negotiation works)
```
**Verbatim failing ioctl** (extracted from `baseline_B_postbug1/ffmpeg.strace.*`, 6 occurrences across 5 frames):
```
ioctl(6</dev/video5>, VIDIOC_S_EXT_CTRLS,
{ctrl_class=0xf010000 /* V4L2_CTRL_CLASS_??? */,
count=1,
controls=[
{id=V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS,
size=56,
string="h\1\17\0\0\0\0\0H\307Z\226\254i\255\30..."}
]}
=> {controls=[...], error_idx=1}
) = -1 EINVAL (Invalid argument)
```
- **CID** `V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS` = `V4L2_CID_MPEG_BASE+250` = `0x9909fa` — the fork's `include/mpeg2-ctrls.h:13` symbol. **The kernel doesn't recognize this CID** (mainline removed it; replaced by the split `V4L2_CID_STATELESS_MPEG2_{SEQUENCE,PICTURE,QUANTISATION}` at `0xa409dc/dd/de`).
- **size=56** matches `sizeof(struct v4l2_ctrl_mpeg2_slice_params)` from the fork's local header (combined sequence + picture + slice fields).
- **`error_idx=1`** = first (and only) control in the array failed validation.
- 6 calls visible across the 5-frame attempt — one per frame, plus one buffer-pool bring-up artefact.
**Confirms Phase 2 Bug 2 directly**: the kernel rejects the staging-era CID. The fork's UAPI is stale; rewriting `mpeg2.c` against the new split API is non-optional.
**Auxiliary EINVAL — H.264 device-init runs unconditionally on hantro**:
```
ioctl(6</dev/video5>, VIDIOC_S_EXT_CTRLS,
{ctrl_class=0,
count=2,
controls=[
{id=0xa40900 /* V4L2_CID_STATELESS_H264_DECODE_MODE */, size=0, value=1, value64=1},
{id=0xa40901 /* V4L2_CID_STATELESS_H264_START_CODE */, size=0, value=1, value64=1}
]}
=> {error_idx=2}
) = -1 EINVAL (Invalid argument)
```
Source: `src/context.c:142-155` unconditionally sets the H.264 device-wide controls (`V4L2_CID_STATELESS_H264_DECODE_MODE = FRAME_BASED`, `V4L2_CID_STATELESS_H264_START_CODE = ANNEX_B`) on every CreateContext, regardless of profile. **This is intentional** per the existing comment at `src/context.c:138-141`:
> Errors here are not fatal: not every backing driver supports both controls (e.g. cedrus may default to SLICE_BASED without exposing DECODE_MODE).
The error is silently swallowed via `(void)v4l2_set_controls(...)` (line 153 — explicit cast discards the return). For MPEG-2 on hantro-vpu-dec the call's a no-op; the EINVAL is noise in the trace, not a bug. **Out of scope for iter1**; documented as auxiliary finding.
**Verbatim ioctl summary** (post-Bug-1 path engages full V4L2 lifecycle):
```
40 ioctl(6</dev/video5>, VIDIOC_QUERYBUF
22 ioctl(6</dev/video5>, VIDIOC_ENUM_FMT
16 ioctl(7</dev/media2>, MEDIA_IOC_REQUEST_ALLOC
10 ioctl(6</dev/video5>, VIDIOC_QBUF
10 ioctl(6</dev/video5>, VIDIOC_G_FMT
10 ioctl(6</dev/video5>, VIDIOC_DQBUF
6 ioctl(6</dev/video5>, VIDIOC_S_EXT_CTRLS <-- 6 fail with EINVAL
4 ioctl(4</dev/dri/renderD128>, DRM_IOCTL_VERSION
2 ioctl(6</dev/video5>, VIDIOC_STREAMON
2 ioctl(6</dev/video5>, VIDIOC_STREAMOFF
2 ioctl(6</dev/video5>, VIDIOC_REQBUFS
2 ioctl(6</dev/video5>, VIDIOC_CREATE_BUFS
1 ioctl(9<anon_inode:request>, MEDIA_REQUEST_IOC_REINIT
1 ioctl(9<anon_inode:request>, MEDIA_REQUEST_IOC_QUEUE
1 ioctl(6</dev/video5>, VIDIOC_S_FMT
1 ioctl(6</dev/video5>, VIDIOC_QUERYCAP
```
The buffer-pool bring-up + per-frame plumbing (CREATE_BUFS, QUERYBUF, REQBUFS, STREAMON, REQUEST_ALLOC, QBUF, DQBUF, REQUEST_IOC_QUEUE) all succeeds. **Only the codec-specific control submission is broken**. This bounds the iter1 fix scope sharply.
## Baseline C — cross-validator verbatim contract anchor
**Goal**: extract the exact byte-level `VIDIOC_S_EXT_CTRLS` payload that the working independent V4L2 client (ffmpeg's `-hwaccel v4l2request`) submits per MPEG-2 frame. This is the contract Phase 4 implementation must match.
**Invocation** (verbose strace decoding compound `v4l2_ext_control` payloads):
```bash
strace -ff -tt -y -v -e trace=ioctl \
-o /tmp/iter1_phase3/baseline_C_xvalidator/ffmpeg.strace \
ffmpeg -hide_banner -loglevel error -hwaccel v4l2request \
-i ~/fourier-test/bbb_720p10s_mpeg2.ts -frames:v 2 -f null -
```
**Verbatim per-frame submission** (5 frames captured, frame 1 shown verbatim):
```
ioctl(5</dev/video5>, VIDIOC_S_EXT_CTRLS,
{ctrl_class=0xf010000 /* V4L2_CTRL_CLASS_CODEC_STATELESS */,
count=3,
controls=[
{id=0xa409dc /* V4L2_CID_STATELESS_MPEG2_SEQUENCE */,
size=12,
string="\0\5\320\2\0\30\25\0\0\0\1\1"},
{id=0xa409dd /* V4L2_CID_STATELESS_MPEG2_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 /* V4L2_CID_STATELESS_MPEG2_QUANTISATION */,
size=256,
string="\10\20\20\23\20\23\26\26\26\26\26\26\32\30\32\33\33\33\32\32\32\32\33\33\33\35\35\35\"\"\"\35..."}
]}) = 0
```
**Field-by-field decode** (frame 1, BBB +30s seek into MPEG-2 fixture):
`SEQUENCE` (12 bytes, `struct v4l2_ctrl_mpeg2_sequence` per `linux/v4l2-controls.h:2009`):
| Bytes (LE) | Value | Field |
|---|---|---|
| `\0\5` | `0x0500` = 1280 | `horizontal_size` |
| `\320\2` | `0x02d0` = 720 | `vertical_size` |
| `\0\30\25\0` | `0x00151800` | `vbv_buffer_size` |
| `\0\0` | `0x0000` | `profile_and_level_indication` |
| `\1` | 1 | `chroma_format` (4:2:0) |
| `\1` | `V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE` (0x01) | `flags` |
`PICTURE` (32 bytes, `struct v4l2_ctrl_mpeg2_picture` per `linux/v4l2-controls.h:2056`):
| Bytes (LE) | Value | Field |
|---|---|---|
| `\0\0\0\0\0\0\0\0` | 0 | `backward_ref_ts` (I-frame, no ref) |
| `\0\0\0\0\0\0\0\0` | 0 | `forward_ref_ts` (I-frame, no ref) |
| `\202\0\0\0` | `0x82` = `FRAME_PRED_DCT(0x02) \| PROGRESSIVE(0x80)` | `flags` |
| `\17\17\17\17` | `f_code = {{15,15},{15,15}}` (default for I) | `f_code[2][2]` |
| `\1` | 1 | `picture_coding_type` (I) |
| `\3` | 3 | `picture_structure` (FRAME) |
| `\0` | 0 | `intra_dc_precision` (8-bit) |
| `\0\0\0\0\0` | 0 | `reserved[5]` |
Frame 2 (P-picture per the MPEG-2 GOP) shows different values, verbatim from the next strace line:
```
PICTURE: "\0\0\0\0\0\0\0\0p\27\0\0\0\0\0\0\202\0\0\0\1\1\17\17\2\3\0\0\0\0\0\0"
```
| Bytes (LE) | Value | Field |
|---|---|---|
| `\0\0\0\0\0\0\0\0` | 0 | `backward_ref_ts` (P, no backward) |
| `p\27\0\0\0\0\0\0` | `0x1770` ns = 6000 (matches the I-frame's timestamp) | `forward_ref_ts` (refers to frame 1) |
| `\202\0\0\0` | 0x82 | `flags` (same FRAME_PRED_DCT \| PROGRESSIVE) |
| `\1\1\17\17` | `f_code = {{1,1},{15,15}}` (real motion vectors for P) | `f_code` |
| `\2` | 2 | `picture_coding_type` (P) |
| `\3` | 3 | `picture_structure` (FRAME) |
| `\0` | 0 | `intra_dc_precision` |
| `\0\0\0\0\0` | 0 | `reserved[5]` |
`QUANTISATION` (256 bytes = 4 × 64 bytes):
```
intra_quantiser_matrix[64] = [8, 16, 16, 19, 16, 19, 22, 22, 22, 22, 22, 22, 26, 24, 26, 27, 27, 27, 26, 26, 26, 26, 27, 27, 27, 29, 29, 29, 34, 34, 34, 29, ...]
```
This is the canonical MPEG-2 default intra matrix in zigzag scanning order — `8` is at zigzag-position 0 (DC coefficient), `16,16` at positions 1-2 (first AC pair). Confirms the kernel-doc claim that matrices arrive **in zigzag scanning order** (`linux/v4l2-controls.h:2076-2084`).
The remaining 192 bytes (non-intra, chroma_intra, chroma_non_intra) all start with `\20\20...` = 16,16,... — MPEG-2 default non-intra is a flat 16 matrix; chroma matrices duplicate luma when chroma_format=4:2:0.
**This is the iter1 contract anchor**: every Phase 4 implementation diff and Phase 7 verification must produce a `VIDIOC_S_EXT_CTRLS` call that's structurally indistinguishable from this — `count=3`, `ctrl_class=V4L2_CTRL_CLASS_CODEC_STATELESS=0xf010000`, the three CIDs in this order, with sizes 12 / 32 / 256 and the per-frame field values matching what VAAPI's `VAPictureParameterBufferMPEG2` and `VAIQMatrixBufferMPEG2` carry for the same fixture.
## Baseline D — H.264 regression check (Phase 1 success criterion #5)
**Goal**: prove Bug 1 fix in isolation doesn't break H.264. Re-runs T4's reference incantation against `bbb_1080p30_h264.mp4` with the scratch backend installed; expects byte-identical SHA-256 hashes against T4's reference values (`f623d5f7…` for frame 1, `7d7bc6f2…` for frame 2 at +30s seek).
**Verbatim hash output**:
```
f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9 /tmp/iter1_phase3/baseline_D_h264_regr/png_seek_hw/00000001.jpg
7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8 /tmp/iter1_phase3/baseline_D_h264_regr/png_seek_hw/00000002.jpg
f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9 /tmp/iter1_phase3/baseline_D_h264_regr/png_seek_sw/00000001.jpg
7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8 /tmp/iter1_phase3/baseline_D_h264_regr/png_seek_sw/00000002.jpg
```
Pass: HW frame 1 == SW frame 1 == T4 reference; HW frame 2 == SW frame 2 == T4 reference. Bug 1 fix in isolation does not regress H.264.
## Scratch state cleanup
After Baseline D, the scratch patch was reverted to keep master clean for Phase 4 work:
```
$ git checkout -- src/config.c
$ ninja -C build && sudo ninja -C build install
$ git branch -D iter1-phase3-scratch # branch was empty — patch was uncommitted
```
**Sanity check** (master backend reinstalled, MPEG-2 should fail again):
```
$ ffmpeg -hwaccel vaapi -i bbb_720p10s_mpeg2.ts -frames:v 2 -f null -
[mpeg2video] Failed to create decode configuration: 12 (the requested VAProfile is not supported).
[mpeg2video] Failed setup for format vaapi: hwaccel initialisation returned error.
```
Back to Baseline A state — scratch state successfully cleaned.
## What Phase 3 confirms / refutes from Phase 2
| Phase 2 claim | Phase 3 evidence | Status |
|---|---|---|
| Bug 1: `RequestCreateConfig` rejects MPEG-2 via fall-through to `default:` returning `VA_STATUS_ERROR_UNSUPPORTED_PROFILE` | Baseline A libva trace shows `vaCreateConfig ret = VA_STATUS_ERROR_UNSUPPORTED_PROFILE` for `profile=VAProfileMPEG2Main` | ✅ confirmed |
| Bug 1 fix is sufficient to advance past `vaCreateConfig` | Baseline B libva trace shows `vaCreateConfig ret = VA_STATUS_SUCCESS` post-patch | ✅ confirmed |
| Bug 2: `mpeg2.c` uses staging-era CID `V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS` (`0x9909fa`) which mainline kernel removed | Baseline B strace shows `VIDIOC_S_EXT_CTRLS id=V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS size=56 = -1 EINVAL` | ✅ confirmed |
| Bug 2: kernel exposes the new split API (`0xa409dc/dd/de`) | Baseline C strace shows ffmpeg-v4l2request submits these three CIDs successfully | ✅ confirmed |
| Cross-validator submits 1 batched `VIDIOC_S_EXT_CTRLS, count=3` per frame | Baseline C verbatim payload shows exactly that | ✅ confirmed |
| Quantisation matrices arrive in zigzag order | Baseline C QUANTISATION first 4 bytes `[8, 16, 16, 19]` matches MPEG-2 default intra matrix in zigzag | ✅ confirmed |
| H.264 device-init in context.c is best-effort and silently swallows EINVAL on hantro | Baseline B strace shows the `0xa40900/0xa40901` EINVAL (silently); `src/context.c:153` has `(void)v4l2_set_controls(...)` cast | ✅ confirmed (auxiliary, not iter1 scope) |
| Phase 1 criterion #5 (H.264 regression check) holds with scratch Bug 1 fix in isolation | Baseline D hashes match T4 reference exactly | ✅ confirmed |
| mpv-as-driver engages the libva backend for MPEG-2 | Baseline A's mpv variant: zero V4L2 ioctls, zero libva trace, silent SW fallback | ❌ **refuted** — mpv silently bypasses vaapi for MPEG-2; Phase 1 criterion #3 needs adjustment |
## Phase 1 criterion #3 needs adjustment (Phase 3 → Phase 1 loopback)
Per `feedback_dev_process.md`'s Phase 3 → Phase 1 loopback edge:
> If baseline reveals the Phase 1 metric was tracking the wrong thing → loop back to Phase 1 with the corrected target.
The Phase 1 lock specified:
> 3. End-to-end decode engages the backend. `mpv --hwdec=vaapi-copy --frames=2 --vo=null --no-audio …` logs the `[vaapi] libva: Trying to open …` chain, the `Using hardware decoding (vaapi-copy)` confirmation, and exits 0 with no `Failed to create decode configuration` lines.
Phase 3 baseline shows mpv-vaapi-copy **never engages libva for MPEG-2** in the current configuration — neither pre-patch nor post-Bug-1-patch produces `[vaapi]` log lines or VA-API ioctls when invoked via mpv on the MPEG-2 fixture. The mpv hwdec policy (or ffmpeg-mpv glue layer) filters MPEG-2 out before libva is touched. So the Phase 1 criterion #3 wording can never be satisfied via the original incantation, regardless of whether iter1 fixes the libva backend.
**Adjusted Phase 1 criterion #3** (proposed; locks alongside Phase 4 plan):
> 3. End-to-end decode engages the backend. `ffmpeg -hwaccel vaapi -i bbb_720p10s_mpeg2.ts -frames:v 2 -f null - 2>&1` (with the v4l2_request env vars set) shows the `[VAAPI] libva: Trying to open /usr/lib/dri/v4l2_request_drv_video.so` chain, `vaCreateConfig ret = VA_STATUS_SUCCESS`, no `Failed to create decode configuration` lines, no `EINVAL` from `VIDIOC_S_EXT_CTRLS`, and exits 0 cleanly.
The `mpv` invocation moves from criterion to optional follow-up (potentially needs an `--vd` override or a fix to mpv's hwdec-codecs filter; a separate, smaller iteration after iter1 lands).
The other four Phase 1 criteria (vainfo enumeration regression, vaCreateConfig success, DMA-BUF GL pixel verify HW=SW, T4 H.264 regression) hold as locked — no adjustment needed.
## Phase 4 plan inputs
Phase 4 plan should specify:
1. **Diff scope** (per Phase 2 `phase2_iter1_situation.md`):
- `src/config.c:55-69` — add `break;` for `VAProfileMPEG2Simple/Main` cases (3 lines).
- `src/mpeg2.c` — full rewrite against the new split API.
- `include/mpeg2-ctrls.h` — delete or empty (drop the staging-era header that masks the kernel's modern definitions).
- `src/picture.c` — no changes (verified wired correctly in Phase 2).
- `src/context.c` — no changes (the H.264 device-init EINVAL is auxiliary, by-design swallowed).
2. **Contract anchor** (per Phase 6 contract-before-code per `feedback_dev_process.md`):
- Cite verbatim from `linux/v4l2-controls.h:1985-2105` — the three control struct definitions and flag constants.
- Cite verbatim from FFmpeg `libavcodec/v4l2_request_mpeg2.c:130-155` — the batched submission shape (3 controls, `ctrl_class=V4L2_CTRL_CLASS_CODEC_STATELESS`).
- Cite verbatim from kernel `drivers/media/platform/verisilicon/hantro_mpeg2.c::hantro_mpeg2_dec_copy_qtable` — the kernel-side zigzag-to-raster permutation (so the libva backend doesn't double-permute).
- Cite Baseline C verbatim payload from this document (`SEQUENCE` 12 bytes, `PICTURE` 32 bytes, `QUANTISATION` 256 bytes per frame).
3. **Field mapping table** (per Phase 2 `phase2_iter1_situation.md` Bug 2 detail):
- 6 structural changes from old to new API documented.
- Source-data extraction in current `src/mpeg2.c` is sound; only destination-control-struct shape and CIDs change.
4. **Phase 7 verification harness**:
- Re-run all 5 Phase 1 boolean checks (with criterion #3 adjusted as above).
- Compare the post-fix `VIDIOC_S_EXT_CTRLS` payload byte-by-byte against Baseline C's verbatim payload — exact match expected for SEQUENCE field values; PICTURE will differ on `forward_ref_ts/backward_ref_ts` (different timestamp source) and `f_code` (real values not defaults); QUANTISATION should match exactly for the same fixture.
5. **Mpv-criterion-3 follow-up** (deferred):
- File a follow-up task to investigate why mpv-vaapi-copy filters MPEG-2 out and how to override. Out of iter1 scope.
## Phase 3 → Phase 4 close
Phase 3 baselines all four green. Phase 2 Bug 1 + Bug 2 + Bug 3 confirmed empirically. Phase 1 criterion #3 needs a minor wording adjustment (mpv → ffmpeg-direct anchor) — proposed text above; locks with Phase 4 plan if approved. Phase 4 can proceed with the contract-anchored diff scope.