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).
This commit is contained in:
@@ -12,9 +12,9 @@
|
||||
* to the tile-group bytes before handing the assembled stream to
|
||||
* libavcodec.
|
||||
*
|
||||
* This header covers the Sequence Header OBU (AV1 spec §5.5.1).
|
||||
* The Frame Header / Frame OBU synthesisers are separate modules
|
||||
* (follow-on tasks); they all share the same wire conventions:
|
||||
* This header covers Sequence Header (§5.5.1), Temporal Delimiter
|
||||
* (§5.6), and Frame Header (§5.9) OBUs. All share the same wire
|
||||
* conventions:
|
||||
* - No emulation prevention (AV1 uses leb128 sized fields instead).
|
||||
* - obu_has_size_field = 1 in the OBU header byte.
|
||||
* - obu_extension_flag = 0 (no temporal_id / spatial_id encoding).
|
||||
@@ -47,4 +47,73 @@
|
||||
size_t av1_synth_sequence_header_obu(const struct v4l2_ctrl_av1_sequence *seq,
|
||||
uint8_t *out, size_t out_cap);
|
||||
|
||||
/*
|
||||
* Encode an AV1 Temporal Delimiter OBU into @out. Always exactly 2
|
||||
* bytes: 0x12 (obu_type=TEMPORAL_DELIMITER, has_size_field=1) followed
|
||||
* by 0x00 (leb128(0) — zero-payload). Returns 2 on success, 0 if
|
||||
* @out_cap < 2.
|
||||
*
|
||||
* Per AV1 spec §5.6 every temporal unit MUST start with a Temporal
|
||||
* Delimiter OBU when temporal_delimiter_obus_present is implied — the
|
||||
* libavcodec AV1 parser uses TD OBUs as access-unit boundaries when
|
||||
* fed full-bitstream input.
|
||||
*/
|
||||
size_t av1_synth_temporal_delimiter_obu(uint8_t *out, size_t out_cap);
|
||||
|
||||
/*
|
||||
* Integration status (2026-05-23):
|
||||
*
|
||||
* The Sequence / Frame Header / Temporal Delimiter encoders below are
|
||||
* standalone primitives. They are NOT yet called from decoder.c — the
|
||||
* AV1 decode hot path still passes the OUTPUT buffer straight to
|
||||
* libavcodec, which only works if the V4L2 consumer happens to be
|
||||
* sending a fully-OBU'd access unit (i.e. is not strictly following
|
||||
* the V4L2 stateless AV1 "tile-group bytes only" contract).
|
||||
*
|
||||
* Wiring these primitives in requires a separate kernel-side change:
|
||||
*
|
||||
* - extend daedalus_v4l2_proto.h with a `struct daedalus_av1_meta`
|
||||
* mirroring v4l2_ctrl_av1_sequence + v4l2_ctrl_av1_frame
|
||||
* - update kernel/daedalus_v4l2_main.c to capture
|
||||
* V4L2_CID_STATELESS_AV1_{SEQUENCE,FRAME} at device_run time and
|
||||
* ship the meta alongside the bitstream over the chardev
|
||||
* - update daemon/src/chardev_client.c to receive the meta
|
||||
* - update daemon/src/decoder.c to: synth TD + SH + FH OBUs, wrap
|
||||
* the OUTPUT bytes as an OBU_TILE_GROUP, concat in that order,
|
||||
* hand the assembled bitstream to libavcodec
|
||||
*
|
||||
* Tracked as a follow-on; see daedalus-v4l2 task notes.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Encode an AV1 Frame Header OBU from the V4L2 stateless frame control
|
||||
* (and the matching sequence control, which provides fields the
|
||||
* frame-header encoder branches on per §5.9.1).
|
||||
*
|
||||
* Scope (this revision — libva-v4l2-request common-case path):
|
||||
* - Frame types KEY / INTER / INTRA_ONLY. SWITCH frames return 0
|
||||
* (caller should fall back to libavcodec native parsing).
|
||||
* - segmentation_params() emits the "segmentation disabled" path
|
||||
* when V4L2_AV1_SEGMENTATION_FLAG_ENABLED is 0. Enabled
|
||||
* segmentation returns 0.
|
||||
* - loop_restoration_params(): only RESTORE_NONE on all planes
|
||||
* supported. Other restoration types return 0.
|
||||
* - global_motion: only IDENTITY warp model emitted. Non-IDENTITY
|
||||
* entries return 0.
|
||||
* - film_grain_params(): treated as "not present" — only valid when
|
||||
* the sequence header has film_grain_params_present = 0. If the
|
||||
* sequence claims film grain is present this revision returns 0
|
||||
* (the per-frame film-grain coding is a separate follow-on).
|
||||
*
|
||||
* Out-of-scope branches return 0 so the caller can surface a coverage
|
||||
* warning and fall back to direct libavcodec parsing of the original
|
||||
* bitstream where possible.
|
||||
*
|
||||
* @out_cap must be at least 128 bytes for any reasonable frame header;
|
||||
* 256 bytes is a safe upper bound for the supported subset.
|
||||
*/
|
||||
size_t av1_synth_frame_header_obu(const struct v4l2_ctrl_av1_sequence *seq,
|
||||
const struct v4l2_ctrl_av1_frame *fr,
|
||||
uint8_t *out, size_t out_cap);
|
||||
|
||||
#endif /* DAEDALUS_AV1_OBU_SYNTH_H */
|
||||
|
||||
Reference in New Issue
Block a user