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:
+571
-1
@@ -39,7 +39,20 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define OBU_SEQUENCE_HEADER 1
|
||||
#define OBU_SEQUENCE_HEADER 1
|
||||
#define OBU_TEMPORAL_DELIMITER 2
|
||||
#define OBU_FRAME_HEADER 3
|
||||
#define OBU_TILE_GROUP 4
|
||||
#define OBU_FRAME 6
|
||||
|
||||
/* AV1 §3 ref-frame symbolic constants — values per the spec table.
|
||||
* INTRA_FRAME is index 0 (used for intra-only); LAST_FRAME..ALTREF_FRAME
|
||||
* are 1..7. TOTAL_REFS_PER_FRAME = 8 (V4L2 mirrors this). */
|
||||
#define AV1_INTRA_FRAME 0
|
||||
#define AV1_LAST_FRAME 1
|
||||
#define AV1_NUM_REF_FRAMES 8 /* the DPB size */
|
||||
#define AV1_REFS_PER_FRAME 7 /* refs available to an inter frame */
|
||||
#define AV1_PRIMARY_REF_NONE 7
|
||||
|
||||
/* Default operating-point level: 5.1 — supports any frame size up to
|
||||
* 4K@60fps. Well past anything the V4L2 path is realistically driven
|
||||
@@ -325,3 +338,560 @@ size_t av1_synth_sequence_header_obu(const struct v4l2_ctrl_av1_sequence *seq,
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Shared OBU wrap helper (header byte + leb128 size + payload). Used
|
||||
* by frame_header_obu and the temporal_delimiter helper; the sequence
|
||||
* header above predates this factor-out and keeps its inline
|
||||
* assembly so its memory footprint stays predictable.
|
||||
* ----------------------------------------------------------------- */
|
||||
static size_t wrap_obu(uint8_t obu_type, const uint8_t *payload,
|
||||
size_t payload_len, uint8_t *out, size_t out_cap)
|
||||
{
|
||||
size_t w = 0;
|
||||
|
||||
if (out_cap < 2)
|
||||
return 0;
|
||||
out[w++] = (uint8_t)(
|
||||
(0u << 7) |
|
||||
((obu_type & 0xfu) << 3) |
|
||||
(0u << 2) |
|
||||
(1u << 1) |
|
||||
(0u << 0));
|
||||
|
||||
{
|
||||
size_t leb_n = leb128_put((uint32_t) payload_len,
|
||||
out + w, out_cap - w);
|
||||
if (leb_n == 0)
|
||||
return 0;
|
||||
w += leb_n;
|
||||
}
|
||||
|
||||
if (out_cap - w < payload_len)
|
||||
return 0;
|
||||
if (payload_len)
|
||||
memcpy(out + w, payload, payload_len);
|
||||
w += payload_len;
|
||||
return w;
|
||||
}
|
||||
|
||||
size_t av1_synth_temporal_delimiter_obu(uint8_t *out, size_t out_cap)
|
||||
{
|
||||
return wrap_obu(OBU_TEMPORAL_DELIMITER, NULL, 0, out, out_cap);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Frame Header OBU — §5.9
|
||||
*
|
||||
* The encoder is sectioned to mirror the spec. Each subsection
|
||||
* helper writes into the shared bs_writer and signals "out of
|
||||
* scope" by setting a sticky `*unsupported` flag that the top-level
|
||||
* checks before returning. This keeps the spec-mirror linear and
|
||||
* the failure modes diagnosable.
|
||||
* ----------------------------------------------------------------- */
|
||||
|
||||
/* MiCols / MiRows per spec §3 — 4x4-unit count, rounded up to the
|
||||
* 8x8 alignment the spec uses for tiling math. Returns AlignPow2
|
||||
* of ((dim + 7) >> 3) at miSize=2 (8x8 mi-block). */
|
||||
static uint32_t mi_cols_for(uint32_t frame_width)
|
||||
{
|
||||
uint32_t mi = (frame_width + 7u) >> 3;
|
||||
return mi << 1; /* 4x4 mi units == miCols */
|
||||
}
|
||||
static uint32_t mi_rows_for(uint32_t frame_height)
|
||||
{
|
||||
uint32_t mi = (frame_height + 7u) >> 3;
|
||||
return mi << 1;
|
||||
}
|
||||
|
||||
/* tile_log2(blkSize, target) per AV1 §5.9.15 — smallest k such that
|
||||
* (blkSize << k) >= target. */
|
||||
static int tile_log2_ge(int blk, int target)
|
||||
{
|
||||
int k = 0;
|
||||
while ((blk << k) < target) k++;
|
||||
return k;
|
||||
}
|
||||
|
||||
/* §5.9.12 quantization_params */
|
||||
static void write_quantization_params(struct bs_writer *bs,
|
||||
const struct v4l2_av1_quantization *q,
|
||||
bool num_planes_gt_1,
|
||||
bool separate_uv_delta_q)
|
||||
{
|
||||
bsw_put_u(bs, q->base_q_idx, 8);
|
||||
|
||||
/* read_delta_q: 1 bit "delta_coded" + (s(7)?) — we always emit
|
||||
* the full delta if non-zero, zero-encoded as delta_coded=0
|
||||
* (single bit). */
|
||||
#define EMIT_DELTA_Q(val) do { \
|
||||
int _v = (int8_t)(val); \
|
||||
if (_v != 0) { \
|
||||
bsw_put_u(bs, 1u, 1); \
|
||||
/* su(1+6): sign + 6-bit magnitude */ \
|
||||
if (_v < 0) { \
|
||||
bsw_put_u(bs, (uint32_t)(_v + 128) & 0x7fu, 7); \
|
||||
} else { \
|
||||
bsw_put_u(bs, (uint32_t)_v & 0x7fu, 7); \
|
||||
} \
|
||||
} else { \
|
||||
bsw_put_u(bs, 0u, 1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
EMIT_DELTA_Q(q->delta_q_y_dc);
|
||||
|
||||
if (num_planes_gt_1) {
|
||||
if (separate_uv_delta_q)
|
||||
bsw_put_u(bs,
|
||||
(q->flags & V4L2_AV1_QUANTIZATION_FLAG_DIFF_UV_DELTA) ? 1u : 0u,
|
||||
1);
|
||||
EMIT_DELTA_Q(q->delta_q_u_dc);
|
||||
EMIT_DELTA_Q(q->delta_q_u_ac);
|
||||
if (separate_uv_delta_q &&
|
||||
(q->flags & V4L2_AV1_QUANTIZATION_FLAG_DIFF_UV_DELTA)) {
|
||||
EMIT_DELTA_Q(q->delta_q_v_dc);
|
||||
EMIT_DELTA_Q(q->delta_q_v_ac);
|
||||
}
|
||||
}
|
||||
#undef EMIT_DELTA_Q
|
||||
|
||||
bsw_put_u(bs,
|
||||
(q->flags & V4L2_AV1_QUANTIZATION_FLAG_USING_QMATRIX) ? 1u : 0u,
|
||||
1);
|
||||
if (q->flags & V4L2_AV1_QUANTIZATION_FLAG_USING_QMATRIX) {
|
||||
bsw_put_u(bs, q->qm_y, 4);
|
||||
bsw_put_u(bs, q->qm_u, 4);
|
||||
if (num_planes_gt_1 && separate_uv_delta_q)
|
||||
bsw_put_u(bs, q->qm_v, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/* §5.9.11 loop_filter_params */
|
||||
static void write_loop_filter_params(struct bs_writer *bs,
|
||||
const struct v4l2_av1_loop_filter *lf,
|
||||
bool num_planes_gt_1,
|
||||
bool coded_lossless_or_allow_intrabc)
|
||||
{
|
||||
if (coded_lossless_or_allow_intrabc) {
|
||||
/* spec §6.8.10: when CodedLossless or allow_intrabc is set,
|
||||
* loop filter levels are inferred and not coded. */
|
||||
return;
|
||||
}
|
||||
bsw_put_u(bs, lf->level[0], 6);
|
||||
bsw_put_u(bs, lf->level[1], 6);
|
||||
if (num_planes_gt_1) {
|
||||
if (lf->level[0] || lf->level[1]) {
|
||||
bsw_put_u(bs, lf->level[2], 6);
|
||||
bsw_put_u(bs, lf->level[3], 6);
|
||||
}
|
||||
}
|
||||
bsw_put_u(bs, lf->sharpness, 3);
|
||||
|
||||
/* loop_filter_delta_enabled */
|
||||
bool delta_en = !!(lf->flags & V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED);
|
||||
bsw_put_u(bs, delta_en ? 1u : 0u, 1);
|
||||
if (delta_en) {
|
||||
bool delta_upd = !!(lf->flags & V4L2_AV1_LOOP_FILTER_FLAG_DELTA_UPDATE);
|
||||
bsw_put_u(bs, delta_upd ? 1u : 0u, 1);
|
||||
if (delta_upd) {
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
/* update_ref_delta: emit 0 (no update) — V4L2 carries the
|
||||
* cumulative state; trying to differentially encode here
|
||||
* would need previous-frame state we don't track. */
|
||||
bsw_put_u(bs, 0u, 1);
|
||||
}
|
||||
for (i = 0; i < 2; i++)
|
||||
bsw_put_u(bs, 0u, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* §5.9.19 cdef_params */
|
||||
static void write_cdef_params(struct bs_writer *bs,
|
||||
const struct v4l2_av1_cdef *cdef,
|
||||
bool num_planes_gt_1,
|
||||
bool enable_cdef,
|
||||
bool coded_lossless_or_intrabc)
|
||||
{
|
||||
int i, n;
|
||||
if (!enable_cdef || coded_lossless_or_intrabc)
|
||||
return;
|
||||
bsw_put_u(bs, cdef->damping_minus_3, 2);
|
||||
bsw_put_u(bs, cdef->bits, 2);
|
||||
n = 1 << cdef->bits;
|
||||
for (i = 0; i < n; i++) {
|
||||
bsw_put_u(bs, cdef->y_pri_strength[i] & 0xfu, 4);
|
||||
bsw_put_u(bs, cdef->y_sec_strength[i] & 0x3u, 2);
|
||||
if (num_planes_gt_1) {
|
||||
bsw_put_u(bs, cdef->uv_pri_strength[i] & 0xfu, 4);
|
||||
bsw_put_u(bs, cdef->uv_sec_strength[i] & 0x3u, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* §5.9.20 lr_params — only RESTORE_NONE supported here */
|
||||
static int write_lr_params(struct bs_writer *bs,
|
||||
const struct v4l2_av1_loop_restoration *lr,
|
||||
int num_planes,
|
||||
bool enable_restoration,
|
||||
bool coded_lossless_or_intrabc)
|
||||
{
|
||||
int p;
|
||||
if (!enable_restoration || coded_lossless_or_intrabc)
|
||||
return 1;
|
||||
|
||||
/* Out-of-scope if ANY plane uses restoration */
|
||||
if (lr->frame_restoration_type[0] != V4L2_AV1_FRAME_RESTORE_NONE)
|
||||
return 0;
|
||||
if (num_planes > 1) {
|
||||
if (lr->frame_restoration_type[1] != V4L2_AV1_FRAME_RESTORE_NONE)
|
||||
return 0;
|
||||
if (lr->frame_restoration_type[2] != V4L2_AV1_FRAME_RESTORE_NONE)
|
||||
return 0;
|
||||
}
|
||||
/* Emit 2-bit RESTORE_NONE per plane */
|
||||
for (p = 0; p < num_planes; p++)
|
||||
bsw_put_u(bs, 0u, 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* §5.9.15 tile_info — single-tile uniform-spacing path only */
|
||||
static int write_tile_info_single_tile(struct bs_writer *bs,
|
||||
uint32_t frame_width,
|
||||
uint32_t frame_height,
|
||||
bool use_128_sb)
|
||||
{
|
||||
uint32_t mi_cols = mi_cols_for(frame_width);
|
||||
uint32_t mi_rows = mi_rows_for(frame_height);
|
||||
int sb_log2 = use_128_sb ? 5 : 4; /* mi units */
|
||||
uint32_t sb_cols = (mi_cols + ((1u << sb_log2) - 1u)) >> sb_log2;
|
||||
uint32_t sb_rows = (mi_rows + ((1u << sb_log2) - 1u)) >> sb_log2;
|
||||
int min_log2_cols = tile_log2_ge(use_128_sb ? 4096 : 4096 / 1,
|
||||
(int)(sb_cols * (use_128_sb ? 128 : 64)));
|
||||
(void) min_log2_cols;
|
||||
|
||||
/* uniform_tile_spacing_flag = 1, both increment loops = 0 →
|
||||
* tile_cols_log2 = tile_rows_log2 = 0 (single tile). This
|
||||
* matches "uniform spacing with no width/height halving" which
|
||||
* is the simplest valid encoding. */
|
||||
bsw_put_u(bs, 1u, 1); /* uniform_tile_spacing_flag */
|
||||
|
||||
/* increment_tile_cols_log2: 0 zeros + the next non-increment
|
||||
* bit terminates the loop. In single-tile mode we encode the
|
||||
* terminator immediately. */
|
||||
(void) sb_cols;
|
||||
(void) sb_rows;
|
||||
/* The increment loops in the spec run while
|
||||
* tile_cols_log2 < max_log2_tile_cols, reading bits until a 0
|
||||
* appears. For our forced single-tile, we emit a single 0 bit
|
||||
* to terminate the cols loop and another for the rows loop. */
|
||||
bsw_put_u(bs, 0u, 1); /* terminate cols */
|
||||
bsw_put_u(bs, 0u, 1); /* terminate rows */
|
||||
|
||||
/* tile_size_bytes_minus_1: 0 (1 byte) — only meaningful when
|
||||
* NumTiles > 1, but spec emits it unconditionally when
|
||||
* NumTiles > 1. Single tile → not emitted. We're single tile,
|
||||
* skip. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint8_t rbsp[256];
|
||||
struct bs_writer bs;
|
||||
uint32_t sf, ff;
|
||||
bool show_existing_frame = false;
|
||||
bool reduced_still_picture_header;
|
||||
bool show_frame, showable_frame, error_resilient_mode;
|
||||
bool disable_cdf_update, allow_screen_content_tools;
|
||||
bool force_integer_mv, allow_intrabc, frame_size_override;
|
||||
bool allow_high_precision_mv, is_motion_mode_switchable;
|
||||
bool use_ref_frame_mvs, disable_frame_end_update_cdf;
|
||||
bool reference_select, allow_warped_motion, reduced_tx_set;
|
||||
bool skip_mode_present, monochrome;
|
||||
uint8_t frame_type, primary_ref_frame;
|
||||
uint32_t frame_width, frame_height;
|
||||
int num_planes;
|
||||
int width_bits, height_bits;
|
||||
uint8_t order_hint_bits;
|
||||
bool enable_order_hint, enable_ref_frame_mvs, enable_warped_motion_seq;
|
||||
bool enable_cdef_seq, enable_restoration_seq;
|
||||
int i;
|
||||
|
||||
if (!seq || !fr || !out || out_cap < 16)
|
||||
return 0;
|
||||
|
||||
sf = seq->flags;
|
||||
ff = fr->flags;
|
||||
|
||||
/* sanity */
|
||||
monochrome = !!(sf & V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME);
|
||||
num_planes = monochrome ? 1 : 3;
|
||||
enable_order_hint = !!(sf & V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT);
|
||||
enable_ref_frame_mvs = !!(sf & V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS);
|
||||
enable_warped_motion_seq = !!(sf & V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION);
|
||||
enable_cdef_seq = !!(sf & V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF);
|
||||
enable_restoration_seq = !!(sf & V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION);
|
||||
order_hint_bits = enable_order_hint ? seq->order_hint_bits : 0;
|
||||
if (order_hint_bits > 8) order_hint_bits = 8;
|
||||
reduced_still_picture_header = false; /* matches sequence-header default */
|
||||
|
||||
frame_type = fr->frame_type;
|
||||
if (frame_type == V4L2_AV1_SWITCH_FRAME)
|
||||
return 0; /* out of scope */
|
||||
|
||||
show_frame = !!(ff & V4L2_AV1_FRAME_FLAG_SHOW_FRAME);
|
||||
showable_frame = !!(ff & V4L2_AV1_FRAME_FLAG_SHOWABLE_FRAME);
|
||||
error_resilient_mode = !!(ff & V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE);
|
||||
disable_cdf_update = !!(ff & V4L2_AV1_FRAME_FLAG_DISABLE_CDF_UPDATE);
|
||||
allow_screen_content_tools = !!(ff & V4L2_AV1_FRAME_FLAG_ALLOW_SCREEN_CONTENT_TOOLS);
|
||||
force_integer_mv = !!(ff & V4L2_AV1_FRAME_FLAG_FORCE_INTEGER_MV);
|
||||
allow_intrabc = !!(ff & V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC);
|
||||
frame_size_override = !!(ff & V4L2_AV1_FRAME_FLAG_FRAME_SIZE_OVERRIDE);
|
||||
allow_high_precision_mv = !!(ff & V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV);
|
||||
is_motion_mode_switchable = !!(ff & V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE);
|
||||
use_ref_frame_mvs = !!(ff & V4L2_AV1_FRAME_FLAG_USE_REF_FRAME_MVS);
|
||||
disable_frame_end_update_cdf = !!(ff & V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF);
|
||||
reference_select = !!(ff & V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT);
|
||||
allow_warped_motion = !!(ff & V4L2_AV1_FRAME_FLAG_ALLOW_WARPED_MOTION);
|
||||
reduced_tx_set = !!(ff & V4L2_AV1_FRAME_FLAG_REDUCED_TX_SET);
|
||||
skip_mode_present = !!(ff & V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT);
|
||||
primary_ref_frame = fr->primary_ref_frame;
|
||||
|
||||
frame_width = fr->frame_width_minus_1 + 1;
|
||||
frame_height = fr->frame_height_minus_1 + 1;
|
||||
|
||||
width_bits = min_bits_for((uint32_t) seq->max_frame_width_minus_1);
|
||||
height_bits = min_bits_for((uint32_t) seq->max_frame_height_minus_1);
|
||||
|
||||
bsw_init(&bs, rbsp, sizeof(rbsp));
|
||||
|
||||
/* show_existing_frame: 0 (V4L2 doesn't surface the show-only path
|
||||
* — every fr ctrl describes a real decoded frame). */
|
||||
bsw_put_u(&bs, show_existing_frame ? 1u : 0u, 1);
|
||||
|
||||
bsw_put_u(&bs, (uint32_t) frame_type, 2);
|
||||
bsw_put_u(&bs, show_frame ? 1u : 0u, 1);
|
||||
if (show_frame) {
|
||||
/* No decoder_model_info_present_flag emitted in seq header,
|
||||
* so no buffer-removal-time bits here either. */
|
||||
} else {
|
||||
bsw_put_u(&bs, showable_frame ? 1u : 0u, 1);
|
||||
}
|
||||
if (frame_type == V4L2_AV1_SWITCH_FRAME ||
|
||||
(frame_type == V4L2_AV1_KEY_FRAME && show_frame)) {
|
||||
/* error_resilient_mode = 1 inferred — not coded */
|
||||
} else {
|
||||
bsw_put_u(&bs, error_resilient_mode ? 1u : 0u, 1);
|
||||
}
|
||||
|
||||
bsw_put_u(&bs, disable_cdf_update ? 1u : 0u, 1);
|
||||
/* allow_screen_content_tools coded as 1 bit when sequence
|
||||
* forces NOT-SELECT; SELECT mode means we always emit a 1 bit
|
||||
* for the SELECT_SCREEN_CONTENT_TOOLS path. Our sequence
|
||||
* header always emits SELECT, so emit a single bit equal to
|
||||
* the V4L2 flag. */
|
||||
bsw_put_u(&bs, allow_screen_content_tools ? 1u : 0u, 1);
|
||||
if (allow_screen_content_tools) {
|
||||
/* seq_force_integer_mv = SELECT (2) so:
|
||||
* force_integer_mv coded as 1 bit */
|
||||
bsw_put_u(&bs, force_integer_mv ? 1u : 0u, 1);
|
||||
}
|
||||
|
||||
/* frame_id_numbers_present_flag = 0 in seq → no current_frame_id */
|
||||
|
||||
if (frame_type != V4L2_AV1_SWITCH_FRAME && !reduced_still_picture_header)
|
||||
bsw_put_u(&bs, frame_size_override ? 1u : 0u, 1);
|
||||
|
||||
if (enable_order_hint)
|
||||
bsw_put_u(&bs, fr->order_hint, order_hint_bits);
|
||||
|
||||
if (frame_type != V4L2_AV1_KEY_FRAME && frame_type != V4L2_AV1_INTRA_ONLY_FRAME &&
|
||||
!error_resilient_mode)
|
||||
bsw_put_u(&bs, primary_ref_frame, 3);
|
||||
|
||||
/* frame_size + render_size (§5.9.5, §5.9.6) */
|
||||
if (frame_size_override) {
|
||||
bsw_put_u(&bs, fr->frame_width_minus_1, width_bits);
|
||||
bsw_put_u(&bs, fr->frame_height_minus_1, height_bits);
|
||||
}
|
||||
/* superres_params: §5.9.8 */
|
||||
{
|
||||
bool use_superres = !!(ff & V4L2_AV1_FRAME_FLAG_USE_SUPERRES);
|
||||
if (sf & V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES)
|
||||
bsw_put_u(&bs, use_superres ? 1u : 0u, 1);
|
||||
if (use_superres) {
|
||||
/* coded_denom = superres_denom - SUPERRES_DENOM_MIN(9) */
|
||||
int denom = fr->superres_denom;
|
||||
if (denom < 9) denom = 9;
|
||||
bsw_put_u(&bs, (uint32_t)(denom - 9) & 0x7u, 3);
|
||||
}
|
||||
}
|
||||
/* render_size present flag: 1 if render dims given */
|
||||
{
|
||||
bool render_and_frame_match =
|
||||
(fr->render_width_minus_1 == fr->frame_width_minus_1) &&
|
||||
(fr->render_height_minus_1 == fr->frame_height_minus_1);
|
||||
bsw_put_u(&bs, render_and_frame_match ? 0u : 1u, 1);
|
||||
if (!render_and_frame_match) {
|
||||
bsw_put_u(&bs, fr->render_width_minus_1, 16);
|
||||
bsw_put_u(&bs, fr->render_height_minus_1, 16);
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_type != V4L2_AV1_KEY_FRAME && frame_type != V4L2_AV1_INTRA_ONLY_FRAME) {
|
||||
/* allow_intrabc only on key/intra-only — skip for inter */
|
||||
(void) allow_intrabc;
|
||||
if (!error_resilient_mode && enable_order_hint)
|
||||
bsw_put_u(&bs, 0u, 1); /* frame_refs_short_signaling */
|
||||
/* read ref_frame_idx for each of REFS_PER_FRAME */
|
||||
for (i = 0; i < AV1_REFS_PER_FRAME; i++) {
|
||||
int8_t idx = fr->ref_frame_idx[i];
|
||||
if (idx < 0) idx = 0;
|
||||
bsw_put_u(&bs, (uint32_t)(idx & 0x7), 3);
|
||||
}
|
||||
if (frame_size_override && !error_resilient_mode) {
|
||||
/* found_ref loop — emit "no" for each, so frame_size
|
||||
* fields above already populated. */
|
||||
for (i = 0; i < AV1_REFS_PER_FRAME; i++)
|
||||
bsw_put_u(&bs, 0u, 1);
|
||||
}
|
||||
bsw_put_u(&bs, allow_high_precision_mv ? 1u : 0u, 1);
|
||||
/* read_interpolation_filter: is_filter_switchable + value */
|
||||
{
|
||||
int interp = fr->interpolation_filter;
|
||||
bool switchable = (interp == V4L2_AV1_INTERPOLATION_FILTER_SWITCHABLE);
|
||||
bsw_put_u(&bs, switchable ? 1u : 0u, 1);
|
||||
if (!switchable)
|
||||
bsw_put_u(&bs, (uint32_t)interp & 0x3u, 2);
|
||||
}
|
||||
bsw_put_u(&bs, is_motion_mode_switchable ? 1u : 0u, 1);
|
||||
if (!error_resilient_mode && enable_ref_frame_mvs)
|
||||
bsw_put_u(&bs, use_ref_frame_mvs ? 1u : 0u, 1);
|
||||
} else {
|
||||
if (frame_type == V4L2_AV1_INTRA_ONLY_FRAME && allow_screen_content_tools)
|
||||
bsw_put_u(&bs, allow_intrabc ? 1u : 0u, 1);
|
||||
else if (frame_type == V4L2_AV1_KEY_FRAME && allow_screen_content_tools)
|
||||
bsw_put_u(&bs, allow_intrabc ? 1u : 0u, 1);
|
||||
}
|
||||
|
||||
/* disable_frame_end_update_cdf */
|
||||
if (!disable_cdf_update)
|
||||
bsw_put_u(&bs, disable_frame_end_update_cdf ? 1u : 0u, 1);
|
||||
|
||||
/* tile_info: single-tile path */
|
||||
{
|
||||
bool use_128 = !!(sf & V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK);
|
||||
if (!write_tile_info_single_tile(&bs, frame_width, frame_height,
|
||||
use_128))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* quantization_params */
|
||||
write_quantization_params(&bs, &fr->quantization,
|
||||
num_planes > 1,
|
||||
!!(sf & V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q));
|
||||
|
||||
/* segmentation_params: only enabled=0 supported */
|
||||
{
|
||||
bool seg_en = !!(fr->segmentation.flags & V4L2_AV1_SEGMENTATION_FLAG_ENABLED);
|
||||
if (seg_en)
|
||||
return 0;
|
||||
bsw_put_u(&bs, 0u, 1); /* segmentation_enabled */
|
||||
}
|
||||
|
||||
/* delta_q_params + delta_lf_params */
|
||||
{
|
||||
bool delta_q_present = !!(fr->quantization.flags &
|
||||
V4L2_AV1_QUANTIZATION_FLAG_DELTA_Q_PRESENT);
|
||||
if (fr->quantization.base_q_idx > 0) {
|
||||
bsw_put_u(&bs, delta_q_present ? 1u : 0u, 1);
|
||||
if (delta_q_present)
|
||||
bsw_put_u(&bs, fr->quantization.delta_q_res & 0x3u, 2);
|
||||
}
|
||||
if (delta_q_present && !allow_intrabc) {
|
||||
bool delta_lf_present =
|
||||
!!(fr->loop_filter.flags & V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT);
|
||||
bsw_put_u(&bs, delta_lf_present ? 1u : 0u, 1);
|
||||
if (delta_lf_present) {
|
||||
bsw_put_u(&bs, fr->loop_filter.delta_lf_res & 0x3u, 2);
|
||||
bsw_put_u(&bs,
|
||||
(fr->loop_filter.flags & V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_MULTI)
|
||||
? 1u : 0u, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* coded_lossless heuristic: when base_q_idx==0 and all deltas==0
|
||||
* and qm not in use, AV1 treats the frame as lossless. We
|
||||
* approximate with the base_q_idx check; the lf/cdef writers
|
||||
* gate on the same value. */
|
||||
{
|
||||
bool coded_lossless = (fr->quantization.base_q_idx == 0);
|
||||
|
||||
write_loop_filter_params(&bs, &fr->loop_filter,
|
||||
num_planes > 1,
|
||||
coded_lossless || allow_intrabc);
|
||||
write_cdef_params(&bs, &fr->cdef, num_planes > 1,
|
||||
enable_cdef_seq,
|
||||
coded_lossless || allow_intrabc);
|
||||
if (!write_lr_params(&bs, &fr->loop_restoration, num_planes,
|
||||
enable_restoration_seq,
|
||||
coded_lossless || allow_intrabc))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read_tx_mode (§5.9.21) */
|
||||
{
|
||||
bool coded_lossless = (fr->quantization.base_q_idx == 0);
|
||||
if (coded_lossless) {
|
||||
/* tx_mode = ONLY_4X4 (inferred) */
|
||||
} else {
|
||||
int tx_mode = fr->tx_mode;
|
||||
bsw_put_u(&bs, (tx_mode == V4L2_AV1_TX_MODE_SELECT) ? 1u : 0u, 1);
|
||||
if (tx_mode != V4L2_AV1_TX_MODE_SELECT)
|
||||
bsw_put_u(&bs, (tx_mode == V4L2_AV1_TX_MODE_LARGEST) ? 1u : 0u, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* frame_reference_mode (§5.9.23) */
|
||||
if (frame_type != V4L2_AV1_KEY_FRAME && frame_type != V4L2_AV1_INTRA_ONLY_FRAME)
|
||||
bsw_put_u(&bs, reference_select ? 1u : 0u, 1);
|
||||
|
||||
/* skip_mode_params (§5.9.22) */
|
||||
{
|
||||
bool skip_allowed = !!(ff & V4L2_AV1_FRAME_FLAG_SKIP_MODE_ALLOWED);
|
||||
if (skip_allowed)
|
||||
bsw_put_u(&bs, skip_mode_present ? 1u : 0u, 1);
|
||||
}
|
||||
|
||||
/* reduced_tx_set */
|
||||
bsw_put_u(&bs, reduced_tx_set ? 1u : 0u, 1);
|
||||
|
||||
/* global_motion_params: §5.9.24 — emit IDENTITY for each ref */
|
||||
if (frame_type != V4L2_AV1_KEY_FRAME && frame_type != V4L2_AV1_INTRA_ONLY_FRAME) {
|
||||
int r;
|
||||
(void) enable_warped_motion_seq;
|
||||
(void) allow_warped_motion;
|
||||
for (r = 1; r < AV1_NUM_REF_FRAMES; r++) {
|
||||
uint8_t wm_type = fr->global_motion.type[r];
|
||||
if (wm_type != V4L2_AV1_WARP_MODEL_IDENTITY)
|
||||
return 0; /* out of scope */
|
||||
bsw_put_u(&bs, 0u, 1); /* is_global = 0 → identity */
|
||||
}
|
||||
}
|
||||
|
||||
/* film_grain_params: §6.8.20 — only "not present" path supported */
|
||||
if (sf & V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT)
|
||||
return 0; /* out of scope: film grain coding deferred */
|
||||
|
||||
bsw_align_rbsp(&bs);
|
||||
if (bsw_overflowed(&bs))
|
||||
return 0;
|
||||
|
||||
return wrap_obu(OBU_FRAME_HEADER, rbsp, bsw_bytes(&bs), out, out_cap);
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -306,6 +306,150 @@ static int test_overflow(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Case 5: Temporal Delimiter is exactly 2 bytes 0x12 0x00. */
|
||||
static int test_temporal_delimiter(void)
|
||||
{
|
||||
uint8_t out[4];
|
||||
size_t n;
|
||||
|
||||
memset(out, 0xff, sizeof(out));
|
||||
n = av1_synth_temporal_delimiter_obu(out, sizeof(out));
|
||||
CHECK_EQ(n, 2, "TD length");
|
||||
CHECK_EQ(out[0], 0x12, "TD obu header byte (obu_type=2, has_size=1)");
|
||||
CHECK_EQ(out[1], 0x00, "TD leb128 size = 0");
|
||||
printf(" temporal delimiter: OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test fixtures for Frame Header cases. */
|
||||
static void mk_seq_1080p_p0(struct v4l2_ctrl_av1_sequence *seq)
|
||||
{
|
||||
memset(seq, 0, sizeof(*seq));
|
||||
seq->seq_profile = 0;
|
||||
seq->order_hint_bits = 7;
|
||||
seq->bit_depth = 8;
|
||||
seq->max_frame_width_minus_1 = 1919;
|
||||
seq->max_frame_height_minus_1 = 1079;
|
||||
seq->flags = V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK |
|
||||
V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT |
|
||||
V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF |
|
||||
V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION;
|
||||
}
|
||||
|
||||
static void mk_frame_key_1080p(struct v4l2_ctrl_av1_frame *fr)
|
||||
{
|
||||
memset(fr, 0, sizeof(*fr));
|
||||
fr->frame_type = V4L2_AV1_KEY_FRAME;
|
||||
fr->frame_width_minus_1 = 1919;
|
||||
fr->frame_height_minus_1 = 1079;
|
||||
fr->render_width_minus_1 = 1919;
|
||||
fr->render_height_minus_1 = 1079;
|
||||
fr->primary_ref_frame = 7; /* PRIMARY_REF_NONE */
|
||||
fr->quantization.base_q_idx = 60;
|
||||
fr->loop_filter.level[0] = 16;
|
||||
fr->loop_filter.level[1] = 16;
|
||||
fr->loop_filter.level[2] = 16;
|
||||
fr->loop_filter.level[3] = 16;
|
||||
fr->cdef.bits = 0;
|
||||
fr->loop_restoration.frame_restoration_type[0] = V4L2_AV1_FRAME_RESTORE_NONE;
|
||||
fr->loop_restoration.frame_restoration_type[1] = V4L2_AV1_FRAME_RESTORE_NONE;
|
||||
fr->loop_restoration.frame_restoration_type[2] = V4L2_AV1_FRAME_RESTORE_NONE;
|
||||
fr->interpolation_filter = 0;
|
||||
fr->tx_mode = V4L2_AV1_TX_MODE_SELECT;
|
||||
fr->flags = V4L2_AV1_FRAME_FLAG_SHOW_FRAME;
|
||||
}
|
||||
|
||||
/* Case 6: KEY frame at 1080p — happy path, structural smoke. */
|
||||
static int test_frame_header_key_1080p(void)
|
||||
{
|
||||
struct v4l2_ctrl_av1_sequence seq;
|
||||
struct v4l2_ctrl_av1_frame fr;
|
||||
uint8_t out[256];
|
||||
size_t n;
|
||||
struct br br;
|
||||
uint32_t bit;
|
||||
|
||||
mk_seq_1080p_p0(&seq);
|
||||
mk_frame_key_1080p(&fr);
|
||||
|
||||
n = av1_synth_frame_header_obu(&seq, &fr, out, sizeof(out));
|
||||
CHECK(n > 0 && n <= sizeof(out), "FH synth returned %zu", n);
|
||||
|
||||
/* OBU header byte: obu_type=3 (FRAME_HEADER), has_size_field=1
|
||||
* → 0_0011_0_1_0 = 0x1A. */
|
||||
CHECK_EQ(out[0], 0x1A, "FH obu header byte");
|
||||
CHECK((out[1] & 0x80) == 0, "leb128 single byte");
|
||||
CHECK_EQ(n, 2 + (size_t)(out[1] & 0x7f), "total = header+leb+payload");
|
||||
|
||||
br_init(&br, out + 2, n - 2);
|
||||
bit = br_get(&br, 1); CHECK_EQ(bit, 0, "show_existing_frame");
|
||||
bit = br_get(&br, 2); CHECK_EQ(bit, 0, "frame_type=KEY");
|
||||
bit = br_get(&br, 1); CHECK_EQ(bit, 1, "show_frame");
|
||||
bit = br_get(&br, 1); CHECK_EQ(bit, 0, "disable_cdf_update");
|
||||
bit = br_get(&br, 1); CHECK_EQ(bit, 0, "allow_screen_content_tools");
|
||||
|
||||
printf(" KEY frame 1080p: OK (%zu bytes)\n", n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Case 7: INTER frame — coverage smoke. */
|
||||
static int test_frame_header_inter(void)
|
||||
{
|
||||
struct v4l2_ctrl_av1_sequence seq;
|
||||
struct v4l2_ctrl_av1_frame fr;
|
||||
uint8_t out[256];
|
||||
size_t n;
|
||||
int i;
|
||||
|
||||
mk_seq_1080p_p0(&seq);
|
||||
mk_frame_key_1080p(&fr);
|
||||
fr.frame_type = V4L2_AV1_INTER_FRAME;
|
||||
fr.primary_ref_frame = 0;
|
||||
for (i = 0; i < V4L2_AV1_REFS_PER_FRAME; i++)
|
||||
fr.ref_frame_idx[i] = (int8_t)(i & 7);
|
||||
fr.flags |= V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT;
|
||||
|
||||
n = av1_synth_frame_header_obu(&seq, &fr, out, sizeof(out));
|
||||
CHECK(n > 0, "INTER FH synth returned %zu", n);
|
||||
CHECK_EQ(out[0], 0x1A, "FH obu header");
|
||||
printf(" INTER frame: OK (%zu bytes)\n", n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Case 8: SWITCH frame should be rejected. */
|
||||
static int test_frame_header_switch_rejected(void)
|
||||
{
|
||||
struct v4l2_ctrl_av1_sequence seq;
|
||||
struct v4l2_ctrl_av1_frame fr;
|
||||
uint8_t out[256];
|
||||
size_t n;
|
||||
|
||||
mk_seq_1080p_p0(&seq);
|
||||
mk_frame_key_1080p(&fr);
|
||||
fr.frame_type = V4L2_AV1_SWITCH_FRAME;
|
||||
n = av1_synth_frame_header_obu(&seq, &fr, out, sizeof(out));
|
||||
CHECK_EQ(n, 0, "SWITCH frame should be out of scope");
|
||||
printf(" SWITCH frame rejected: OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Case 9: segmentation enabled should be rejected. */
|
||||
static int test_frame_header_segmentation_rejected(void)
|
||||
{
|
||||
struct v4l2_ctrl_av1_sequence seq;
|
||||
struct v4l2_ctrl_av1_frame fr;
|
||||
uint8_t out[256];
|
||||
size_t n;
|
||||
|
||||
mk_seq_1080p_p0(&seq);
|
||||
mk_frame_key_1080p(&fr);
|
||||
fr.segmentation.flags = V4L2_AV1_SEGMENTATION_FLAG_ENABLED;
|
||||
n = av1_synth_frame_header_obu(&seq, &fr, out, sizeof(out));
|
||||
CHECK_EQ(n, 0, "segmentation-enabled should be out of scope");
|
||||
printf(" segmentation enabled rejected: OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fail = 0;
|
||||
@@ -317,6 +461,15 @@ int main(void)
|
||||
fail |= test_reject_invalid_profile_bitdepth();
|
||||
fail |= test_overflow();
|
||||
|
||||
printf("=== av1_synth_temporal_delimiter_obu ===\n");
|
||||
fail |= test_temporal_delimiter();
|
||||
|
||||
printf("=== av1_synth_frame_header_obu ===\n");
|
||||
fail |= test_frame_header_key_1080p();
|
||||
fail |= test_frame_header_inter();
|
||||
fail |= test_frame_header_switch_rejected();
|
||||
fail |= test_frame_header_segmentation_rejected();
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "AV1 OBU synth tests FAILED\n");
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user