iter4 DEBUG: Y2 v3 — retry with TRY_EXT_CTRLS on S_EXT_CTRLS EINVAL

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) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 13:25:40 +00:00
parent 086b7ce8cb
commit a12d29937c
+31 -1
View File
@@ -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;
}