Commit Graph

2 Commits

Author SHA1 Message Date
claude-noether 9797a0daa6 daemon: AV1 Frame Header OBU synthesiser + Temporal Delimiter
Extends the AV1 OBU encoder pack (PR #22 landed the Sequence Header
half) with the two remaining pieces of the per-frame OBU assembly:

  - av1_synth_temporal_delimiter_obu() — trivial 2-byte OBU (0x12,
    0x00) that AV1 temporal units must start with so libavcodec's
    parser can detect access-unit boundaries.

  - av1_synth_frame_header_obu() — encodes a Frame Header OBU (AV1
    §5.9) from V4L2_CID_STATELESS_AV1_SEQUENCE + V4L2_CID_STATELESS_
    AV1_FRAME controls.

## Frame Header scope

The encoder covers the libva-v4l2-request common-case path:

  - frame_type: KEY / INTER / INTRA_ONLY supported.  SWITCH returns 0.
  - tile_info: single-tile uniform-spacing only (forced
    tile_cols_log2 = tile_rows_log2 = 0).
  - quantization_params: full coverage (base_q_idx, delta_q_*, qmatrix).
  - loop_filter_params: full coverage (levels, sharpness, ref/mode deltas).
  - cdef_params: full coverage.
  - segmentation: only enabled=0 path supported (returns 0 if enabled).
  - loop_restoration: only RESTORE_NONE supported (returns 0 if
    any plane uses Wiener / SGRPROJ / SWITCHABLE).
  - global_motion: only IDENTITY warp model emitted (returns 0 if
    any ref uses ROTZOOM / AFFINE / TRANSLATION).
  - film_grain_params: only "not present" path — returns 0 if the
    sequence header has FILM_GRAIN_PARAMS_PRESENT set.

Out-of-scope branches return 0 so a future decoder.c integration can
surface a coverage warning and fall back to direct libavcodec
parsing of the original bitstream where the consumer happens to
ship a fully-OBU'd access unit.

## Integration status

The new primitives are NOT yet wired into decoder.c.  The AV1 decode
hot path still passes the OUTPUT buffer straight to libavcodec,
which works only when the V4L2 consumer is sending a fully-OBU'd
access unit (not strictly the V4L2 stateless contract).

A real wiring needs a separate kernel-side change:
  - daedalus_v4l2_proto.h: add struct daedalus_av1_meta mirroring
    v4l2_ctrl_av1_sequence + v4l2_ctrl_av1_frame
  - kernel/daedalus_v4l2_main.c: capture V4L2_CID_STATELESS_AV1_{SEQUENCE,
    FRAME} at device_run, ship over the chardev
  - daemon/src/chardev_client.c: receive meta
  - daemon/src/decoder.c: assemble TD + SH + FH + OBU_TILE_GROUP-wrapped
    OUTPUT bytes, send to libavcodec

Tracked as a follow-on.

## Tests

test_av1_obu_synth.c grows 5 new cases (9 total, all green on hertz):

  === av1_synth_temporal_delimiter_obu ===
    temporal delimiter: OK
  === av1_synth_frame_header_obu ===
    KEY frame 1080p: OK (13 bytes)
    INTER frame: OK (18 bytes)
    SWITCH frame rejected: OK
    segmentation enabled rejected: OK
  AV1 OBU synth tests PASSED

Bit-walk of the KEY-frame happy path confirms the OBU envelope
(obu_type=3 = FRAME_HEADER, has_size_field=1, leb128 size byte),
then steps through show_existing_frame, frame_type, show_frame,
disable_cdf_update, allow_screen_content_tools.  Fuller bit-walks
would tie the test to encoder details that are spec-driven and
already linear in the source; structural smoke + spec-driven
linearity is the right gate.

Build clean on hertz (Pi 5, Debian trixie, 6.18.29+rpt-rpi-2712,
gcc -Wall -Wextra -Wpedantic).  No new warnings.

Closes daedalus backlog task #159 (AV1 Frame Header OBU synthesiser;
decoder.c integration deferred per task notes above).
2026-05-23 18:31:41 +02:00
claude-noether 1e9619afe8 daemon: AV1 Sequence Header OBU synthesiser + unit test
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.
2026-05-23 15:41:07 +02:00