AV1 decode through daedalus daemon needs a sequence-header OBU synthesiser #11
Reference in New Issue
Block a user
Delete Branch "%!s()"
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?
Summary
With libva PR #10 (the AV1 no-op codec_set_controls case) landed, AV1 frames now reach the daedalus_v4l2 daemon — but libdav1d in the daemon can't parse them. Same architectural shape as the H.264 SPS/PPS problem that DAEMON-PPS solved (daedalus-v4l2 PR #1 + PR #2), just for AV1.
Concrete failure on higgs
With
picture.cAV1 no-op + the test stream from H.264 verification re-encoded to AV1 vialibsvtav1:Daemon journal:
Result: 0 successful decodes across all 60 frames, 40+ libdav1d errors.
Why this is the AV1 analogue of the H.264 SPS/PPS gap
ffmpeg-vaapi (libavcodec/vaapi_av1.c) parses the AV1 bitstream client-side, splits it across multiple VAAPI buffers:
VAPictureParameterBufferAV1— carries the decoded sequence header + per-frame infoVASliceParameterBufferAV1— per-tile-group offsetslibva-v4l2-request-fourier currently puts only the slice data in the V4L2 OUTPUT buffer (which is correct for the V4L2 stateless AV1 API — the sequence header is supposed to go into V4L2_CID_STATELESS_AV1_SEQUENCE).
The daedalus daemon hands the OUTPUT buffer directly to
libavcodec(Option γ — dlopen). libavcodec for AV1 calls libdav1d, which expects a full OBU stream including the sequence header OBU. Without the sequence header OBU prepended, libdav1d sees the tile group OBU's leading bits, mis-interprets the obu_type field, and bails.Same as H.264 (libavcodec wants SPS+PPS NALs in the stream; libva-v4l2-request only puts the slice NAL in the OUTPUT buffer). DAEMON-PPS solved that by:
For AV1, the same pattern applies — with the AV1 OBU encoding instead of H.264 NAL encoding.
Scope of the fix
Mechanically similar to DAEMON-PPS, with these specifics:
libva-v4l2-request-fourier side (~150 LoC)
av1_set_controls(in a newsrc/av1.c) that readsVAPictureParameterBufferAV1and writesV4L2_CID_STATELESS_AV1_SEQUENCE(struct v4l2_ctrl_av1_sequence). The kernel ctrl framework is already there — the daedalus kernel module registersV4L2_CID_STATELESS_AV1_SEQUENCEatdaedalus_stateless_ctrls[]line 188.picture.c::codec_set_controls(from PR #10) withav1_set_controls(...).daedalus-v4l2 kernel side (~30 LoC)
struct daedalus_h264_metawith anav1_sequencefield, OR add a newstruct daedalus_av1_metablock +DAEDALUS_REQ_FLAG_AV1_METAbit. Probably better to be symmetric: separate meta block per codec.daedalus_collect_av1_meta()mirrorsdaedalus_collect_h264_meta()— readsctrl->p_cur.p_av1_sequence(withv4l2_ctrl_request_setupalready in place from PR #2).daedalus daemon side (~400 LoC)
daemon/src/av1_obu_synth.{c,h}— AV1 OBU encoder. Smaller than H.264 NAL synth because AV1 OBUs use a leb128 size prefix instead of NAL emulation prevention. Specifically encodesOBU_SEQUENCE_HEADER(type 1) fromv4l2_ctrl_av1_sequenceper AV1 spec 5.5.f(n)(fixed-width unsigned) anduvlc()(unsigned variable-length coding similar to Exp-Golomb's ue(v)) plus its ownleb128()for size fields. Extendingdaemon/src/bitstream_writer.hto addbsw_put_uvlc()+bsw_put_leb128()covers it.decoder.c::daedalus_decoder_run_request— when codec_id == AV1 && av1_meta != NULL, synthesise OBU_SEQUENCE_HEADER + temporal_delimiter, prepend to the slice bitstream beforeavcodec_send_packet.Wire protocol decision
Two shapes possible:
DAEDALUS_REQ_FLAG_H264_META(existing),DAEDALUS_REQ_FLAG_AV1_META(new), each pointing at codec-specific struct. Cleaner separation.daedalus_codec_metawith codec-tagged contents. Smaller wire size for codecs that need short metadata, slightly more glue.I'd ship option 1 — matches the existing H.264 layout, easier to read.
Out of scope / non-goals
V4L2_CID_STATELESS_AV1_FILM_GRAIN). libdav1d handles film grain internally if the OBU stream describes it — we just need the sequence header right, the rest comes via OBUs in the slice buffer.V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY). Same — the tile group OBUs are already in the slice data, no struct→OBU synthesis required.V4L2_CID_STATELESS_AV1_FRAME. The slice data ffmpeg-vaapi puts in the OUTPUT buffer already contains the OBU_FRAME_HEADER + OBU_TILE_GROUP — we don't need to re-synthesise these.Only the sequence header is missing, and it's a per-stream constant (re-encoded once at session start, prepended to every frame). Single-purpose synth.
Sequencing
Depends on:
case VAProfileAV1Profile0) — landed/in-review.Ready to implement after PR #10 merges.