From 3e4e6e8eae615b6b6033bf3a71ac4df02cbb6913 Mon Sep 17 00:00:00 2001 From: claude-noether Date: Fri, 22 May 2026 17:26:25 +0200 Subject: [PATCH] daemon: filter tiny pause-time bitstreams (closes #17) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libva-v4l2-request-fourier flushes a stub packet into the V4L2 OUTPUT_MPLANE queue at playback-pause boundaries. The payload is shorter than any parseable H.264 NAL (3-byte start code + 1-byte NAL header = 4 bytes minimum); avcodec_send_packet returns AVERROR_INVALIDDATA (-1094995529), which propagated to the kernel as a decode failure. Firefox then marked H.264-via-VAAPI as broken for the session and routed every subsequent frame to libmozavcodec SW — pause never recovered to HW. At the REQ_DECODE entry in chardev_client.c::handle_req_decode, short-circuit any bitstream below the minimum-parseable threshold: log INFO, skip daedalus_decoder_run_request, and reply RESP_FRAME with status=DAEDALUS_DECODE_NO_FRAME so libva's V4L2 surface pool stays healthy and Firefox doesn't see a failure. Repro: Pi CM5 trixie, daedalus-v4l2 0.1.0+r41 + ffmpeg-v4l2- request-fourier 2:8.1+rfourier+gb57fbbe-9, Firefox YouTube avc1. Play → daemon decodes at ~46 fps. Pause ≥ 1s. Resume → daemon silent; sudo journalctl -u daedalus-v4l2 --since '10s' | grep -c 'decoder: OK' = 0. Last entry before silence: REQ_DECODE cookie=N codec=3 bitstream=3 bytes ... [h264 @ ...] no frame! [ERR] decoder: avcodec_send_packet failed: -1094995529 After this fix the 3-byte sentinel logs as 'tiny bitstream 3 bytes — dropping as no-op' and the libavcodec context is untouched; the next real REQ_DECODE proceeds normally. Scope NOT covered (intentionally deferred): - A more general "tolerate AVERROR_INVALIDDATA mid-stream" path. Worth doing later but masks unrelated bugs. - Investigating WHY libva sends the 3-byte sentinel on pause. Likely an upstream libva-v4l2-request-fourier issue; tracked separately if this filter is not enough. Wire protocol unchanged. No DAEDALUS_PROTO_VERSION bump. --- daemon/src/chardev_client.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/daemon/src/chardev_client.c b/daemon/src/chardev_client.c index 10621cc..d3082d7 100644 --- a/daemon/src/chardev_client.c +++ b/daemon/src/chardev_client.c @@ -181,6 +181,29 @@ static int handle_req_decode(struct chardev_client *cli, req.capture_width, req.capture_height, req.capture_num_planes); + /* + * Degenerate-bitstream filter (issue #17): libva-v4l2-request- + * fourier flushes a stub packet into the OUTPUT_MPLANE queue at + * playback-pause boundaries. The payload is shorter than any + * parseable H.264 NAL (3-byte start code + 1-byte NAL header = + * 4 bytes minimum); avcodec_send_packet returns + * AVERROR_INVALIDDATA, which we used to propagate to the kernel + * as a decode failure. Firefox then marks H.264-via-VAAPI as + * broken for the session and routes every subsequent frame to + * libmozavcodec SW — pause never recovers to HW. + * + * Drop the request as a no-op decode and reply RESP_FRAME OK so + * libva's V4L2 state machine keeps its surface pool alive. + */ + if (req.bitstream_len < 4) { + log_info("REQ_DECODE cookie=%u: tiny bitstream %u bytes — dropping as no-op (pause-time sentinel)", + hdr->cookie, req.bitstream_len); + memset(&resp, 0, sizeof(resp)); + resp.status = DAEDALUS_DECODE_NO_FRAME; + return send_response(cli, DAEDALUS_MSG_RESP_FRAME, + hdr->cookie, &resp, sizeof(resp)); + } + /* * Open dmabuf-fds for every CAPTURE plane and mmap them. * If this fails we still attempt the decode (so the kernel