Skip to content
10 changes: 10 additions & 0 deletions apps/avmenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ const arg_def_t *kf_args[] = { &g_av2_codec_arg_defs.fwd_kf_enabled,
&g_av2_codec_arg_defs.sframe_dist,
&g_av2_codec_arg_defs.sframe_mode,
&g_av2_codec_arg_defs.sframe_type,
&g_av2_codec_arg_defs.sframe_refresh_all,
&g_av2_codec_arg_defs.sframe_conformance_option,
NULL };

// TODO(bohanli): Currently all options are supported by the key & value API.
Expand Down Expand Up @@ -1185,6 +1187,14 @@ static int parse_stream_params(struct AvxEncoderConfig *global,
config->cfg.sframe_mode = arg_parse_uint(&arg);
} else if (arg_match(&arg, &g_av2_codec_arg_defs.sframe_type, argi)) {
config->cfg.sframe_type = arg_parse_uint(&arg);
} else if (arg_match(&arg, &g_av2_codec_arg_defs.sframe_refresh_all,
argi)) {
config->cfg.sframe_refresh_all = arg_parse_uint(&arg);
} else if (arg_match(&arg, &g_av2_codec_arg_defs.sframe_replace_kf, argi)) {
config->cfg.sframe_replace_kf = arg_parse_uint(&arg);
} else if (arg_match(&arg, &g_av2_codec_arg_defs.sframe_conformance_option,
argi)) {
config->cfg.sframe_conformance_option = arg_parse_uint(&arg);
} else if (arg_match(&arg, &g_av2_codec_arg_defs.enable_lcr, argi)) {
config->cfg.enable_lcr = arg_parse_uint(&arg);
} else if (arg_match(&arg, &g_av2_codec_arg_defs.enable_ops, argi)) {
Expand Down
12 changes: 12 additions & 0 deletions av2/arg_defs.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,18 @@ const av2_codec_arg_definitions_t g_av2_codec_arg_defs = {
ARG_DEF(NULL, "sframe-mode", 1, "S-Frame insertion mode (0..2)"),
.sframe_type = ARG_DEF(NULL, "sframe-type", 1,
"(0: Regular S-Frame (default), 1: RAS frame)"),
.sframe_refresh_all =
ARG_DEF(NULL, "sframe-refresh-all", 1,
"S-Frame refreshes all ref slots (0: off (default), 1: on)"),
.sframe_replace_kf =
ARG_DEF(NULL, "sframe-replace-kf", 1,
"Replace N consecutive keyframes with S-frames (0: off)"),
.sframe_conformance_option = ARG_DEF(
NULL, "sframe-conformance-option", 1,
"How to satisfy the BAWP/IBC/OPFL/RefineMV bitstream-conformance "
"requirement around switch frames (0: disable refmvbank (default), "
"1: keep refmvbank, disable BAWP/IntraBC after switch, "
"2: disable OPFL refinement and RefineMV)"),
.enable_lcr =
ARG_DEF(NULL, "enable-lcr", 1,
"Enable layer config record (LCR) OBU (0: off (default), 1: on)"),
Expand Down
3 changes: 3 additions & 0 deletions av2/arg_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ typedef struct av2_codec_arg_definitions {
arg_def_t sframe_dist;
arg_def_t sframe_mode;
arg_def_t sframe_type;
arg_def_t sframe_refresh_all;
arg_def_t sframe_replace_kf;
arg_def_t sframe_conformance_option;
arg_def_t enable_lcr;
arg_def_t enable_ops;
arg_def_t num_ops;
Expand Down
54 changes: 54 additions & 0 deletions av2/av2_cx_iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,13 @@ static avm_codec_err_t set_encoder_config(AV2EncoderConfig *oxcf,

tool_cfg->enable_joint_mvd = extra_cfg->enable_joint_mvd;
tool_cfg->enable_refinemv = extra_cfg->enable_refinemv;
if (cfg->enable_sframe && cfg->sframe_conformance_option == 2) {
// Option 3: disable RefineMV sequence-wide, applied at the point
// tool_cfg->enable_refinemv is derived (matching the enable_opfl_refine
// override below).
tool_cfg->enable_refinemv = 0;
extra_cfg->enable_refinemv = 0;
}
tool_cfg->enable_mvd_sign_derive = extra_cfg->enable_mvd_sign_derive;
// Turn off BRU if LA, AI or resize mode
tool_cfg->enable_bru = extra_cfg->enable_bru;
Expand Down Expand Up @@ -1463,6 +1470,24 @@ static avm_codec_err_t set_encoder_config(AV2EncoderConfig *oxcf,
tool_cfg->max_drl_refmvs = extra_cfg->max_drl_refmvs;
tool_cfg->max_drl_refbvs = extra_cfg->max_drl_refbvs;
tool_cfg->enable_refmvbank = extra_cfg->enable_refmvbank;
if (cfg->enable_sframe) {
switch (cfg->sframe_conformance_option) {
case 1:
// Option 2: keep the reference-MV bank on sequence-wide.
tool_cfg->enable_refmvbank = 1;
extra_cfg->enable_refmvbank = 1;
break;
case 2:
// Option 3 doesn't touch enable_refmvbank.
break;
case 0:
default:
// Option 1 (default): disable the reference-MV bank sequence-wide.
tool_cfg->enable_refmvbank = 0;
extra_cfg->enable_refmvbank = 0;
break;
}
}
tool_cfg->enable_cropping_window = extra_cfg->enable_cropping_window;
tool_cfg->crop_win_left_offset = extra_cfg->crop_win_left_offset;
tool_cfg->crop_win_right_offset = extra_cfg->crop_win_right_offset;
Expand Down Expand Up @@ -1505,6 +1530,13 @@ static avm_codec_err_t set_encoder_config(AV2EncoderConfig *oxcf,
tool_cfg->enable_high_motion = extra_cfg->enable_high_motion;

tool_cfg->enable_opfl_refine = extra_cfg->enable_opfl_refine;
if (cfg->enable_sframe && cfg->sframe_conformance_option == 2) {
// Option 3: disable OPFL refinement sequence-wide, applied at the point
// tool_cfg->enable_opfl_refine is derived (matching the enable_refinemv
// override above).
tool_cfg->enable_opfl_refine = 0;
extra_cfg->enable_opfl_refine = 0;
}
if (tool_cfg->enable_opfl_refine) {
if (cfg->g_lag_in_frames == 0) {
tool_cfg->enable_opfl_refine = 0;
Expand Down Expand Up @@ -1594,6 +1626,9 @@ static avm_codec_err_t set_encoder_config(AV2EncoderConfig *oxcf,
kf_cfg->sframe_dist = cfg->sframe_dist;
kf_cfg->sframe_mode = cfg->sframe_mode;
kf_cfg->sframe_type = cfg->sframe_type;
kf_cfg->sframe_refresh_all = cfg->sframe_refresh_all;
kf_cfg->sframe_replace_kf = cfg->sframe_replace_kf;
kf_cfg->sframe_conformance_option = cfg->sframe_conformance_option;
oxcf->unit_test_cfg.insert_sframe = extra_cfg->enable_sframe;

kf_cfg->enable_keyframe_filtering = extra_cfg->enable_keyframe_filtering;
Expand Down Expand Up @@ -2528,6 +2563,18 @@ static avm_codec_err_t ctrl_set_enable_sframe(avm_codec_alg_priv_t *ctx,
return update_extra_cfg(ctx, &extra_cfg);
}

static avm_codec_err_t ctrl_set_sframe_conformance_option(
avm_codec_alg_priv_t *ctx, va_list args) {
struct av2_extracfg extra_cfg = ctx->extra_cfg;
// sframe_conformance_option lives in avm_codec_enc_cfg_t, not in
// av2_extracfg, so set it directly on ctx->cfg; set_encoder_config()
// reads it from there into kf_cfg. update_extra_cfg() still runs to
// re-validate and re-apply the full configuration.
ctx->cfg.sframe_conformance_option =
CAST(AV2E_SET_SFRAME_CONFORMANCE_OPTION, args);
return update_extra_cfg(ctx, &extra_cfg);
}

static avm_codec_err_t ctrl_set_frame_parallel_decoding_mode(
avm_codec_alg_priv_t *ctx, va_list args) {
struct av2_extracfg extra_cfg = ctx->extra_cfg;
Expand Down Expand Up @@ -4734,6 +4781,7 @@ static avm_codec_ctrl_fn_map_t encoder_ctrl_maps[] = {
{ AV2E_SET_FRAME_PARALLEL_DECODING, ctrl_set_frame_parallel_decoding_mode },
{ AV2E_SET_ENABLE_CDF_AVERAGING, ctrl_set_enable_cdf_averaging },
{ AV2E_SET_ENABLE_SFRAME, ctrl_set_enable_sframe },
{ AV2E_SET_SFRAME_CONFORMANCE_OPTION, ctrl_set_sframe_conformance_option },
{ AV2E_SET_ENABLE_RECT_PARTITIONS, ctrl_set_enable_rect_partitions },
{ AV2E_SET_ENABLE_1TO4_PARTITIONS, ctrl_set_enable_uneven_4way_partitions },
{ AV2E_SET_MIN_PARTITION_SIZE, ctrl_set_min_partition_size },
Expand Down Expand Up @@ -4883,6 +4931,9 @@ static const avm_codec_enc_cfg_t encoder_usage_cfg[] = {
0, // sframe_dist
1, // sframe_mode
0, // sframe_type
0, // sframe_refresh_all
0, // sframe_replace_kf
0, // sframe_conformance_option
0, // monochrome
0, // full_still_picture_hdr
1, // enable_tcq
Expand Down Expand Up @@ -5019,6 +5070,9 @@ static const avm_codec_enc_cfg_t encoder_usage_cfg[] = {
0, // sframe_dist
1, // sframe_mode
0, // sframe_type
0, // sframe_refresh_all
0, // sframe_replace_kf
0, // sframe_conformance_option
0, // monochrome
0, // full_still_picture_hdr
0, // enable_tcq
Expand Down
19 changes: 19 additions & 0 deletions av2/common/av2_common_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -3026,6 +3026,25 @@ typedef struct AV2Common {
*/
bool restricted_prediction_switch;

/*!
* Set to 1 for a frame that is within the "risk window" opened by a coded
* frame with obu_type equal to OBU_SWITCH (the switch frame itself, and
* every following frame up to the next random access point). Used to
* enforce the bitstream conformance requirement constraining BAWP/IBC
* around switch frames.
*/
bool in_switch_risk_window;
/*!
* The mlayer_id of the OBU_SWITCH frame that opened the current
* in_switch_risk_window. Only meaningful when in_switch_risk_window is 1.
*/
int switch_risk_window_mlayer_id;
/*!
* The tlayer_id of the OBU_SWITCH frame that opened the current
* in_switch_risk_window. Only meaningful when in_switch_risk_window is 1.
*/
int switch_risk_window_tlayer_id;

} AV2_COMMON;

/*!\cond */
Expand Down
63 changes: 63 additions & 0 deletions av2/decoder/decodeframe.c
Original file line number Diff line number Diff line change
Expand Up @@ -6778,6 +6778,31 @@ static INLINE void read_intrabc_params(AV2_COMMON *const cm,
FeatureFlags *const features = &cm->features;
features->allow_intrabc = avm_rb_read_bit(rb);

// Bitstream conformance: for a coded video sequence containing an
// OBU_SWITCH frame, at least one of the BAWP/IBC/OPFL/RefineMV
// constraints must hold for the switch frame and every dependent inter
// frame that follows it (the switch frame itself, frame_type == S_FRAME,
// is excluded from this particular constraint). When enable_refmvbank is
// 1 and OPFL refinement/RefineMV are not both off sequence-wide,
// allow_intrabc must be 0 here.
if (features->allow_intrabc && cm->in_switch_risk_window &&
current_frame->frame_type == INTER_FRAME &&
cm->seq_params.enable_refmvbank &&
!(cm->seq_params.enable_opfl_refine == AVM_OPFL_REFINE_NONE &&
cm->seq_params.enable_refinemv == 0) &&
is_mlayer_transitively_dependent(&cm->seq_params, cm->mlayer_id,
cm->switch_risk_window_mlayer_id) &&
(cm->tlayer_id == cm->switch_risk_window_tlayer_id ||
cm->seq_params
.tlayer_dependency_map[cm->mlayer_id][cm->tlayer_id]
[cm->switch_risk_window_tlayer_id])) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"allow_intrabc must be 0 for an inter frame depending on the layer "
"of a preceding OBU_SWITCH frame when enable_refmvbank is 1, unless "
"enable_opfl_refine is REFINE_NONE and enable_refinemv is 0");
}

if (features->allow_intrabc) {
if (current_frame->frame_type == KEY_FRAME ||
current_frame->frame_type == INTRA_ONLY_FRAME) {
Expand Down Expand Up @@ -8074,6 +8099,22 @@ static int read_uncompressed_header(AV2Decoder *pbi, OBU_TYPE obu_type,
current_frame->frame_type =
avm_rb_read_bit(rb) ? INTER_FRAME : INTRA_ONLY_FRAME;
}

// Track the "risk window" opened by an OBU_SWITCH frame, used below to
// enforce the bitstream conformance requirement constraining BAWP/IBC
// around switch frames (decoder state derived from reconstructed
// samples can diverge across bitstreams spliced at the switch frame
// even though parsing stays deterministic). A CLK/OLK or an
// OBU_RAS_FRAME is a random access point, which closes any risk window
// opened by a prior OBU_SWITCH frame.
if (current_frame->frame_type == KEY_FRAME || obu_type == OBU_RAS_FRAME) {
cm->in_switch_risk_window = false;
} else if (obu_type == OBU_SWITCH) {
cm->in_switch_risk_window = true;
cm->switch_risk_window_mlayer_id = cm->mlayer_id;
cm->switch_risk_window_tlayer_id = cm->tlayer_id;
}

current_frame->long_term_id = -1;
if (current_frame->frame_type == KEY_FRAME) {
const int long_term_id_plus_1 =
Expand Down Expand Up @@ -9303,6 +9344,28 @@ static int read_uncompressed_header(AV2Decoder *pbi, OBU_TYPE obu_type,
else
features->enable_bawp = 0;

// Bitstream conformance: for a coded video sequence containing an
// OBU_SWITCH frame, at least one of the BAWP/IBC/OPFL/RefineMV
// constraints must hold for the switch frame and every dependent inter
// frame that follows it (the switch frame itself, frame_type == S_FRAME,
// is excluded from this particular constraint). If OPFL refinement and
// RefineMV are not both off sequence-wide, then BAWP must be off here.
if (features->enable_bawp && cm->in_switch_risk_window &&
current_frame->frame_type == INTER_FRAME &&
!(seq_params->enable_opfl_refine == AVM_OPFL_REFINE_NONE &&
seq_params->enable_refinemv == 0) &&
is_mlayer_transitively_dependent(seq_params, cm->mlayer_id,
cm->switch_risk_window_mlayer_id) &&
(cm->tlayer_id == cm->switch_risk_window_tlayer_id ||
seq_params->tlayer_dependency_map[cm->mlayer_id][cm->tlayer_id]
[cm->switch_risk_window_tlayer_id])) {
avm_internal_error(
&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
"enable_bawp must be 0 for an inter frame depending on the layer of "
"a preceding OBU_SWITCH frame, unless enable_opfl_refine is "
"REFINE_NONE and enable_refinemv is 0");
}

features->enable_intra_bawp = seq_params->enable_bawp;

features->enable_cwp = seq_params->enable_cwp;
Expand Down
18 changes: 16 additions & 2 deletions av2/encoder/encode_strategy.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,18 @@ int av2_get_refresh_frame_flags(
return (1 << cpi->common.seq_params.ref_frames) - 1;
}

if (frame_params->frame_type == S_FRAME ||
// sframe_refresh_all only gates real, restricted OBU_SWITCH frames
// (restricted_prediction_switch, i.e. sframe_mode == 0). RAS frames
// (sframe_type == RAS_FRAME) and non-restricted switch frames always
// refresh all RPL slots: RAS frames are random access points, and a
// non-restricted switch frame retains no is_restricted old references to
// make partial refresh meaningful. cpi->is_ras_frame is not used here
// because it is only valid for the frame currently being encoded, not
// reliably for every call site reachable from this function.
if ((frame_params->frame_type == S_FRAME &&
(cpi->oxcf.kf_cfg.sframe_refresh_all ||
cpi->oxcf.kf_cfg.sframe_type == RAS_FRAME ||
!cpi->common.restricted_prediction_switch)) ||
frame_params->frame_type == KEY_FRAME) {
AV2_COMMON *const cm = &cpi->common;
int refresh_frame_flags = (1 << cpi->common.seq_params.ref_frames) - 1;
Expand Down Expand Up @@ -794,6 +805,7 @@ int av2_get_refresh_frame_flags(
// been output yet and whose DOH is at least the current
// frame's DOH. (DOH requirement)
if (cm->ref_frame_map[i] != NULL &&
!cm->ref_frame_map[i]->is_restricted &&
cm->ref_frame_map[i]->implicit_output_picture &&
!cm->ref_frame_map[i]->frame_output_done &&
(int)cm->ref_frame_map[i]->display_order_hint >= cur_disp_order) {
Expand Down Expand Up @@ -847,7 +859,7 @@ int av2_get_refresh_frame_flags(
// at least the current frame's DOH. (DOH requirement)
for (int i = 0; i < cpi->common.seq_params.ref_frames; i++) {
const RefCntBuffer *const buf = cpi->common.ref_frame_map[i];
if (buf != NULL && buf->implicit_output_picture &&
if (buf != NULL && !buf->is_restricted && buf->implicit_output_picture &&
!buf->frame_output_done &&
(int)buf->display_order_hint >= cur_disp_order) {
olk_flags_to_keep |= (1 << i);
Expand Down Expand Up @@ -1278,6 +1290,8 @@ int av2_encode_strategy(AV2_COMP *const cpi, size_t *const size,

cm->restricted_prediction_switch =
(cpi->oxcf.kf_cfg.enable_sframe && cpi->oxcf.kf_cfg.sframe_mode == 0) ||
(cpi->oxcf.kf_cfg.sframe_replace_kf > 0 &&
cpi->oxcf.kf_cfg.sframe_mode == 0) ||
cpi->oxcf.tool_cfg.g_error_resilient_mode;

av2_configure_buffer_updates(cpi, frame_update_type);
Expand Down
Loading
Loading