Files
fresnel-fourier/phase7_iter1_verification.md
T
claude-noether ec9133a5e4 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>
2026-05-08 09:00:14 +00:00

16 KiB
Raw Blame History

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/.

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):

$ 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):

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):

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):

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).