picture, request_pool: transparent OUTPUT-pool resize on bitstream overrun (#15) #16
Reference in New Issue
Block a user
Delete Branch "claude-noether/libva-v4l2-request-fourier:noether/output-pool-resize-issue-15"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #15 — the proper fix track for #13.
PR #14 left the OUTPUT-pool slot's
sizeimagefixed for the life of the pool, returningVA_STATUS_ERROR_ALLOCATION_FAILEDon overrun and forcing the libva consumer to drop the frame. This patch absorbs a mid-session resolution upshift transparently: on overrun, snapshot the in-flight surface's accumulated bytes, STREAMOFF the OUTPUT queue, REQBUFS(0), re-S_FMTwith a biggersizeimagehint, CREATE_BUFS, re-mmap, re-allocper-slotmedia_requestfds, STREAMON — then restore the bytes and continue. The triggering memcpy succeeds, the frame survives.What changes
src/request_pool.h/src/request_pool.c—request_pool_initgains threeS_FMT-param args (pixelformat,picture_width,picture_height) which it stashes on the pool. Newrequest_pool_resize(pool, new_sizeimage_min)runs the STREAMOFF → REQBUFS(0) → S_FMT(sizeimage-override) → CREATE_BUFS → mmap → media_request_alloc → STREAMON cycle.src/v4l2.h/src/v4l2.c— newv4l2_set_format_sizeimage()helper.v4l2_set_format()keeps theSOURCE_SIZE_MAX(1 MiB) default forCreateContext-timeS_FMT; the resize path passes its own hint.src/picture.c— new staticcodec_store_buffer_ensure_capacity(). The three append sites inVASliceDataBufferTypecall it before each memcpy/memset. On overflow it does the save / release / resize / re-acquire / re-mirror / restore dance.src/context.c— pass the cached S_FMT params intorequest_pool_init.Why the resize is safe at
codec_store_buffertimeThe inline-Sync-in-EndPicture pattern guarantees that when
codec_store_bufferruns, exactly ONE OUTPUT pool slot is borrowed (the currentrender_surface_id's) and it has NOT yet beenVIDIOC_QBUF'd to the kernel.ensure_capacitytemporarily releases that slot (cleanbusy=falseflip, no kernel state touched), runs the resize (which the pool guards with a busy-scan precondition), then re-acquires.CAPTURE side is intentionally untouched. V4L2 stateless treats per-queue streaming independently; STREAMOFF on OUTPUT does not disturb the still-STREAMON CAPTURE queue. A genuine resolution-upshift CAPTURE-side budget mismatch then surfaces as a clean
V4L2_BUF_FLAG_ERRORon the next DQBUF, handled by the existing surface error path.Geometric growth
New
sizeimage = max(need × 2, source_size × 2), capped at 1 GiB, rounded up to a 4 KiB page. Compute insize_tso the doubling cannot wrap at 2 GiB on 32-bitunsigned intbefore the clamp. The post-resizeneed > source_sizere-check returns hardERROR_ALLOCATION_FAILEDrather than retrying — no infinite-loop possibility if the kernel rejects the hint.On resize failure
The surface's slot has already been released by
ensure_capacitywhen the resize fails. We re-acquire it (it's a cleanbusy=falseslot the inline-Sync invariant guarantees no one else grabbed) so the surface'ssource_data/source_size/request_fdmirror stays internally consistent. Then surfaceVA_STATUS_ERROR_ALLOCATION_FAILEDto libva — fallback behaviour matches pre-patch (consumer recreates the surface).Independent review
Ran code-review pass (independent of this author): traced Begin/Render/End/Sync invariants in
picture.c+surface.c, traced the OUTPUT-only V4L2 cycle's interaction with CAPTURE for rkvdec / hantro / daedalus_v4l2, audited the geometric-growth math for overflow + infinite-loop. Verdict: sound. One minor observation about the resize-failure surface state, addressed by the explicit re-acquire (see commit message + inline comment incodec_store_buffer_ensure_capacity).Test plan
libva-v4l2-request-fourier-aarch64+-debianGitea Actions jobs build clean.mpv --hwdec=vaapi-copy bbb_1080p30_h264.mp4against a 720p-default daedalus session) decodes through the upshift instead of crashing;request_logshows thecodec_store_buffer: OUTPUT-pool resizeline at the first 1080p slice.vainfoenumerates all 8 codec profiles, no perf regression.sizeimageslices remain on the happy path, larger slices trigger the resize cleanly).