V4L2 stateless AV1 passes the sequence header information as a
structured control (V4L2_CID_STATELESS_AV1_SEQUENCE) and ships
only tile-group bytes in the OUTPUT buffer. libavcodec's AV1
decoder is full-bitstream, so the daemon needs to reconstruct
the OBU bytes the consumer parsed out before feeding the
assembled stream to libavcodec.
This commit lands the Sequence Header OBU half of that
reconstruction — av1_synth_sequence_header_obu(). Frame
Header / Frame OBU synthesisers + the integration that wires
the assembled OBUs into the decode hot path are separate
follow-on modules.
Module shape mirrors the H.264 NAL synthesiser (PR #1):
- Public API: single function returning byte count or 0
on overflow/invalid input.
- Wire encoder uses the existing bitstream_writer (bsw_put_u
is AV1's f(n); bsw_put_ue is bit-identical to AV1's uvlc;
bsw_align_rbsp matches AV1's trailing_bits()).
- AV1-specific helpers (leb128 size, min_bits_for, subsampling
resolution per §5.5.2) are file-local statics.
- No emulation prevention — AV1 uses leb128-sized OBUs for
bitstream boundaries, not byte-pattern escapes.
Synthesis decisions for fields V4L2 doesn't carry are documented
verbatim in the file header (reduced_still_picture_header = 0;
single operating point at seq_level_idx = 13 / level 5.1;
color_description_present_flag = 0; chroma_sample_position = 0;
seq_choose_screen_detection_tools = 1; seq_choose_integer_mv = 1).
Rejection cases:
- seq_profile > 2
- bit_depth not in {8, 10, 12}
- seq_profile = 1 + monochrome (4:4:4 forced colour)
- seq_profile = 1 + bit_depth = 12 (only profile 2 allows it)
- max_frame_{width,height}_minus_1 requiring > 16 length bits
- out_cap too small to hold header + leb128 + payload
Each returns 0 to surface the mismatch loudly rather than emit
nonsense the libavcodec parser would reject downstream.
Unit test (test_av1_obu_synth.c, opt-in via DAEDALUS_BUILD_TESTS=ON)
exercises four cases bit-by-bit against a hand-computed reference:
1. profile 0, 1080p, 8-bit, 4:2:0, order_hint on (7 bits),
CDEF+restoration on — the common Pi 5 path.
2. profile 0, 720p, 10-bit, monochrome — exercises high_bitdepth
and the monochrome short-form color_config.
3. profile 1 + bit_depth 12 → expects 0 (rejected).
4. tiny out_cap → expects 0 (overflow).
All four green on hertz (aarch64 Arch, gcc Wall+Wextra+Wpedantic
clean).
This commit does not change daemon behaviour — av1_obu_synth.c is
built into the daemon binary so the symbols are reachable, but
no call site is wired yet. Integration goes in the follow-on
DAEMON-AV1 patches that also synthesise the Frame Header OBU
and bracket the assembled OBUs with a Temporal Delimiter.
Refs reauktion/daedalus-v4l2#11 daemon-half; closes daedalus
backlog task #144.