From a12d29937cb28c8ae1773935963d84c6f6276d18 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Tue, 5 May 2026 13:25:40 +0000 Subject: [PATCH] =?UTF-8?q?iter4=20DEBUG:=20Y2=20v3=20=E2=80=94=20retry=20?= =?UTF-8?q?with=20TRY=5FEXT=5FCTRLS=20on=20S=5FEXT=5FCTRLS=20EINVAL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per kernel comment in v4l2-ctrls-api.c:222-224, S_EXT_CTRLS deliberately obfuscates by setting error_idx = count, while TRY_EXT_CTRLS reports the actual failing index. Adds TRY retry inside the EINVAL diagnostic path. Empirical finding (iter4 Phase 4): TRY also returned error_idx == count on the frame-11 EINVAL on bbb_1080p30. Conclusion: failure is in the post-validate cluster commit (hantro driver's try_ctrl op or similar state-coherence check), NOT in any individual control's std_validate. The kernel comment may be outdated for compound controls, or the H.264 stateless cluster is committed atomically post-validate where error_idx is intentionally not updated for either S or TRY. Path forward (Phase 4 next): switch from "read kernel source" to "diff our DECODE_PARAMS construction vs FFmpeg's libavcodec/v4l2_request_h264.c" to identify field-by-field divergence at frame 11. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/v4l2.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/v4l2.c b/src/v4l2.c index 5267072..d881cbe 100644 --- a/src/v4l2.c +++ b/src/v4l2.c @@ -447,12 +447,42 @@ static int v4l2_ioctl_controls(int video_fd, int request_fd, unsigned long ioc, rc = ioctl(video_fd, ioc, &controls); if (rc < 0 && errno == EINVAL && ioc == VIDIOC_S_EXT_CTRLS) { - request_log("S_EXT_CTRLS EINVAL: num_controls=%u error_idx=%u\n", + int saved_errno = errno; + request_log("S_EXT_CTRLS EINVAL: num_controls=%u error_idx=%u (count = obfuscated)\n", num_controls, controls.error_idx); for (unsigned int i = 0; i < num_controls; i++) { request_log(" ctrl[%u]: id=0x%08x size=%u\n", i, control_array[i].id, control_array[i].size); } + /* + * Retry with VIDIOC_TRY_EXT_CTRLS — kernel comment in + * v4l2-ctrls-api.c:222-224 says TRY_EXT_CTRLS "never modifies + * controls [so] error_idx is just set to whatever control has + * an invalid value." Unlike S_EXT_CTRLS which deliberately + * obfuscates by setting error_idx = count. + */ + struct v4l2_ext_controls try_controls; + memset(&try_controls, 0, sizeof(try_controls)); + try_controls.controls = control_array; + try_controls.count = num_controls; + if (request_fd >= 0) { + try_controls.which = V4L2_CTRL_WHICH_REQUEST_VAL; + try_controls.request_fd = request_fd; + } + int try_rc = ioctl(video_fd, VIDIOC_TRY_EXT_CTRLS, &try_controls); + if (try_rc < 0) { + request_log(" TRY_EXT_CTRLS retry: errno=%d (%s) error_idx=%u\n", + errno, strerror(errno), try_controls.error_idx); + if (try_controls.error_idx < num_controls) { + request_log(" --> failing control is ctrl[%u]: id=0x%08x size=%u\n", + try_controls.error_idx, + control_array[try_controls.error_idx].id, + control_array[try_controls.error_idx].size); + } + } else { + request_log(" TRY_EXT_CTRLS retry: passed (S vs TRY semantics differ — likely cluster commit)\n"); + } + errno = saved_errno; } return rc; }