From 385dee1bbfe354d257a72b1aa70304b97120e788 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Tue, 5 May 2026 14:12:41 +0000 Subject: [PATCH] iter4 fix: fresh request_fd per frame (fixes carryover EINVAL) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the load-bearing fix that resolves the iter1+iter2+iter3 "frame-11 EINVAL" carryover. Replace the per-surface request_fd cache + MEDIA_REQUEST_IOC_REINIT pattern with allocate-fresh-per-frame: in RequestSyncSurface, after queue + wait_completion succeed, close the request_fd and reset surface_object->request_fd = -1 so the next BeginPicture allocates a new one via media_request_alloc. Diagnostic root cause: per-control VIDIOC_TRY_EXT_CTRLS isolation showed all four H.264 controls (SPS/PPS/DECODE_PARAMS/SCALING_MATRIX) fail individually with EINVAL on the *same* request_fd that had been through queue+wait+reinit. The fd state was bad even though every ioctl in the previous decode cycle returned success. Allocating fresh sidesteps any kernel-side request-state-machine subtlety we don't fully understand. Empirical verification (iter4 Phase 7, 90s autonomous run on ohm via firefox-fourier without MOZ_DISABLE_RDD_SANDBOX=1, bbb_1080p30 H.264): - ENETDOWN count: 0 - S_EXT_CTRLS rejected: 0 (was: fired at frame 11 every iter1-3) - Unable to set control(s): 0 - Generic EINVAL: 0 - Video stream mTime reached: 49.7 seconds - Audio stream mTime reached: 51.5 seconds Cost: ~one extra MEDIA_IOC_REQUEST_ALLOC + close() per decoded frame. Negligible (cycles below the V4L2 set_controls + queue + wait stack). Companion fixes that landed earlier in iter4 to get to this point: 74d8dd1 — DPB fields=V4L2_H264_FRAME_REF + skip !used entries (matches FFmpeg's libavcodec/v4l2_request_h264.c semantics) Co-Authored-By: Claude Opus 4.7 (1M context) --- src/surface.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/surface.c b/src/surface.c index 88a0b7a..37155d6 100644 --- a/src/surface.c +++ b/src/surface.c @@ -461,11 +461,19 @@ VAStatus RequestSyncSurface(VADriverContextP context, VASurfaceID surface_id) goto error; } - rc = media_request_reinit(request_fd); - if (rc < 0) { - status = VA_STATUS_ERROR_OPERATION_FAILED; - goto error; - } + /* + * iter4: instead of REINITing for reuse, close the request_fd here + * and force the next BeginPicture to allocate a fresh one. The cached + * request_fd path was hitting EINVAL on S_EXT_CTRLS for some surface + * recycle patterns (4 individual TRY_EXT_CTRLS on the same fd all + * fail with EINVAL — the fd state is bad even though queue+wait+reinit + * appeared successful). Allocating fresh per frame is unambiguous and + * sidesteps any state-lifecycle issue. Tradeoff: ~one extra ioctl per + * frame (MEDIA_IOC_REQUEST_ALLOC + close), negligible cost. + */ + close(request_fd); + surface_object->request_fd = -1; + (void)0; /* placeholder for the now-removed reinit error path */ rc = v4l2_dequeue_buffer(driver_data->video_fd, -1, output_type, surface_object->source_index, 1);