Daemon never recovers from AVERROR_INVALIDDATA on tiny (≤3-byte) pause-time bitstreams; Firefox falls off to SW after resume #17

Closed
opened 2026-05-22 15:25:14 +00:00 by marfrit · 0 comments
Owner

Symptom

On higgs (Pi CM5, Debian trixie, daedalus-v4l2 0.1.0+r41+g6e6dfa1 + ffmpeg-v4l2-request-fourier 2:8.1+rfourier+gb57fbbe-9), Firefox YouTube playback through libva → daedalus is real-time HW (~46 fps at 720p H.264 / avc1). As soon as the user pauses and resumes playback, the pipeline does NOT re-engage:

  • daemon stops receiving REQ_DECODE
  • /dev/video0 drops out of every Firefox process's /proc/<pid>/fd/ listing
  • Firefox silently falls back to libmozavcodec SW for the rest of the session
  • Stats for nerds keeps reporting avc1 (the stream codec is still H.264; only the decode path switched)

Root cause

The last entry the daemon logs before going quiet is a 3-byte bitstream:

[17:17:39.505] REQ_DECODE cookie=25036 codec=3 bitstream=3 bytes meta=h264 capture=1280x720 1 planes
[h264 @ 0x55558375c780] no frame!
[ERR] decoder: avcodec_send_packet failed: -1094995529  (AVERROR_INVALIDDATA)

3 bytes is below the structural minimum for any valid H.264 NAL (3-byte start code + at least 1-byte NAL header = 4 bytes minimum). This is almost certainly libva-v4l2-request-fourier flushing a pause-time sentinel into the V4L2 OUTPUT_MPLANE queue.

daedalus_decoder_run_request returns the negative rc straight up to chardev_client.c::handle_req_decode, which propagates it without sending a RESP_FRAME. The kernel-side V4L2 client (libva) sees the request fail; libva returns failure to Firefox; Firefox marks H.264-via-VAAPI as broken for this content-process session and routes all subsequent H.264 to libmozavcodec.

Fix (proposed)

Filter degenerate bitstreams at the REQ_DECODE entry in daemon/src/chardev_client.c:

  • If req.bitstream_len < 4 (less than minimum-possible H.264 NAL), log INFO, skip the daedalus_decoder_run_request call, and return RESP_FRAME with status=OK and zero pixel output.
  • The bitstream is structurally not parseable as H.264; treating it as a no-op decode keeps libva's V4L2 state machine happy without polluting the libavcodec context with an AVERROR_INVALIDDATA error.

Follow-up that we intentionally skip in this issue:

  • A more general "tolerate AVERROR_INVALIDDATA mid-stream" path (drop bad packet, keep going). Worth doing later but masks unrelated bugs.
  • Investigating WHY libva sends the 3-byte sentinel on pause. Likely upstream in libva-v4l2-request-fourier; tracked separately if reproduced after this fix.

Reproduce

  1. Pi CM5 / trixie / daedalus-v4l2-dkms 0.1.0+r41 / daedalus-v4l2 0.1.0+r41 / ffmpeg-v4l2-request-fourier 2:8.1+rfourier+gb57fbbe-9
  2. Firefox, open a YouTube avc1 stream (any 720p+ H.264 video; codec confirmed via Stats for nerds)
  3. sudo journalctl -u daedalus-v4l2 --since "10 seconds ago" | grep -c 'decoder: OK' while playing → >0 (HW decode through daemon)
  4. Pause for ≥1 sec
  5. Resume
  6. Re-run step 3 → 0 (SW fallback, daemon silent)
  7. Check daemon journal — last entry before silence is the bitstream=3 bytes + AVERROR_INVALIDDATA pair shown above.

Refs

  • daemon/src/chardev_client.c::handle_req_decode (the entry point that needs the length check)
  • daedalus-v4l2 #11 / #12 (LOW_DELAY half-measure on daemon side, predecessor work)
  • marfrit-packages PR #87 (libavcodec LOW_DELAY restore — fixes the 2-1-4-3 display-order regression but does NOT address this pause-resume issue)
## Symptom On higgs (Pi CM5, Debian trixie, daedalus-v4l2 0.1.0+r41+g6e6dfa1 + ffmpeg-v4l2-request-fourier 2:8.1+rfourier+gb57fbbe-9), Firefox YouTube playback through libva → daedalus is real-time HW (~46 fps at 720p H.264 / avc1). As soon as the user pauses and resumes playback, the pipeline does NOT re-engage: - daemon stops receiving REQ_DECODE - `/dev/video0` drops out of every Firefox process's `/proc/<pid>/fd/` listing - Firefox silently falls back to libmozavcodec SW for the rest of the session - Stats for nerds keeps reporting `avc1` (the stream codec is still H.264; only the decode path switched) ## Root cause The last entry the daemon logs before going quiet is a 3-byte bitstream: ``` [17:17:39.505] REQ_DECODE cookie=25036 codec=3 bitstream=3 bytes meta=h264 capture=1280x720 1 planes [h264 @ 0x55558375c780] no frame! [ERR] decoder: avcodec_send_packet failed: -1094995529 (AVERROR_INVALIDDATA) ``` 3 bytes is below the structural minimum for any valid H.264 NAL (3-byte start code + at least 1-byte NAL header = 4 bytes minimum). This is almost certainly libva-v4l2-request-fourier flushing a pause-time sentinel into the V4L2 OUTPUT_MPLANE queue. `daedalus_decoder_run_request` returns the negative rc straight up to `chardev_client.c::handle_req_decode`, which propagates it without sending a RESP_FRAME. The kernel-side V4L2 client (libva) sees the request fail; libva returns failure to Firefox; Firefox marks H.264-via-VAAPI as broken for this content-process session and routes all subsequent H.264 to libmozavcodec. ## Fix (proposed) Filter degenerate bitstreams at the REQ_DECODE entry in `daemon/src/chardev_client.c`: - If `req.bitstream_len < 4` (less than minimum-possible H.264 NAL), log INFO, skip the `daedalus_decoder_run_request` call, and return RESP_FRAME with status=OK and zero pixel output. - The bitstream is structurally not parseable as H.264; treating it as a no-op decode keeps libva's V4L2 state machine happy without polluting the libavcodec context with an AVERROR_INVALIDDATA error. Follow-up that we intentionally skip in this issue: - A more general "tolerate AVERROR_INVALIDDATA mid-stream" path (drop bad packet, keep going). Worth doing later but masks unrelated bugs. - Investigating WHY libva sends the 3-byte sentinel on pause. Likely upstream in `libva-v4l2-request-fourier`; tracked separately if reproduced after this fix. ## Reproduce 1. Pi CM5 / trixie / daedalus-v4l2-dkms 0.1.0+r41 / daedalus-v4l2 0.1.0+r41 / ffmpeg-v4l2-request-fourier 2:8.1+rfourier+gb57fbbe-9 2. Firefox, open a YouTube avc1 stream (any 720p+ H.264 video; codec confirmed via Stats for nerds) 3. `sudo journalctl -u daedalus-v4l2 --since "10 seconds ago" | grep -c 'decoder: OK'` while playing → >0 (HW decode through daemon) 4. Pause for ≥1 sec 5. Resume 6. Re-run step 3 → 0 (SW fallback, daemon silent) 7. Check daemon journal — last entry before silence is the `bitstream=3 bytes` + AVERROR_INVALIDDATA pair shown above. ## Refs - `daemon/src/chardev_client.c::handle_req_decode` (the entry point that needs the length check) - daedalus-v4l2 #11 / #12 (LOW_DELAY half-measure on daemon side, predecessor work) - marfrit-packages PR #87 (libavcodec LOW_DELAY restore — fixes the 2-1-4-3 display-order regression but does NOT address this pause-resume issue)
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: reauktion/daedalus-v4l2#17