diff --git a/apps/avmenc.c b/apps/avmenc.c index 100bdba18e..a97ba08400 100644 --- a/apps/avmenc.c +++ b/apps/avmenc.c @@ -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. @@ -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)) { diff --git a/av2/arg_defs.c b/av2/arg_defs.c index 14b58370d8..56c0d7e4f3 100644 --- a/av2/arg_defs.c +++ b/av2/arg_defs.c @@ -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)"), diff --git a/av2/arg_defs.h b/av2/arg_defs.h index 896c51fa4a..0de85c8f40 100644 --- a/av2/arg_defs.h +++ b/av2/arg_defs.h @@ -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; diff --git a/av2/av2_cx_iface.c b/av2/av2_cx_iface.c index ee9c25210c..5e8b97f948 100644 --- a/av2/av2_cx_iface.c +++ b/av2/av2_cx_iface.c @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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 }, @@ -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 @@ -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 diff --git a/av2/common/av2_common_int.h b/av2/common/av2_common_int.h index 6e39442efe..e35423283c 100644 --- a/av2/common/av2_common_int.h +++ b/av2/common/av2_common_int.h @@ -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 */ diff --git a/av2/decoder/decodeframe.c b/av2/decoder/decodeframe.c index e6cb3b9bb7..e62a727336 100644 --- a/av2/decoder/decodeframe.c +++ b/av2/decoder/decodeframe.c @@ -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) { @@ -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 = @@ -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; diff --git a/av2/encoder/encode_strategy.c b/av2/encoder/encode_strategy.c index 33054e25de..f2df6249f7 100644 --- a/av2/encoder/encode_strategy.c +++ b/av2/encoder/encode_strategy.c @@ -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; @@ -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) { @@ -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); @@ -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); diff --git a/av2/encoder/encoder.c b/av2/encoder/encoder.c index e6574c4c80..f618016e3e 100644 --- a/av2/encoder/encoder.c +++ b/av2/encoder/encoder.c @@ -668,6 +668,9 @@ void av2_init_seq_coding_tools(AV2_COMP *cpi, SequenceHeader *seq, seq->base_uv_dc_delta_q == seq->base_uv_ac_delta_q)); seq->enable_refmvbank = tool_cfg->enable_refmvbank; + // Bitstream conformance: a coded video sequence containing an OBU_SWITCH + // frame must satisfy one of the BAWP/IBC/OPFL/RefineMV constraints in the + // spec seq->enable_drl_reorder = tool_cfg->enable_drl_reorder; seq->conf.conf_win_enabled_flag = oxcf->tool_cfg.enable_cropping_window; @@ -4712,6 +4715,36 @@ static int encode_frame_to_data_rate(AV2_COMP *cpi, size_t *size, uint8_t *dest, features->allow_warpmv_mode = features->enabled_motion_modes; // temporal set of frame level enable_bawp flag. features->enable_bawp = seq_params->enable_bawp; + // Bitstream conformance around OBU_SWITCH frames: force BAWP (and, when + // the reference-MV bank is on, IntraBC for inter frames) off for the + // switch frame and for every following frame, up to the next random + // access point, whose embedded/temporal layer depends on the switch + // frame's own layer. Skipped when OPFL refinement and RefineMV are + // already off sequence-wide, since that alone satisfies conformance + // In a single-layer encode the layer scoping always evaluates true within the + // risk window, since mlayer_id/tlayer_id are both 0 and trivially match the + // switch frame's own layer. + const bool switch_frame_layer_dependent = + cm->in_switch_risk_window && + !(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]); + if (switch_frame_layer_dependent) { + // The switch frame itself (frame_type == S_FRAME) is excluded from the + // BAWP and IntraBC constraints, matching the spec text ("every frame + // ... that follows that frame"); only later dependent inter frames are + // covered. + if (current_frame->frame_type == INTER_FRAME) { + features->enable_bawp = 0; + if (seq_params->enable_refmvbank) { + features->allow_intrabc = 0; + } + } + } features->enable_intra_bawp = seq_params->enable_bawp; features->enable_cwp = seq_params->enable_cwp; @@ -5227,7 +5260,35 @@ int av2_encode(AV2_COMP *const cpi, uint8_t *const dest, current_frame->mlayer_id = cm->mlayer_id; 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; + + // A CLK, or an OBU_RAS_FRAME (frame_type == S_FRAME with is_ras_frame == + // 1), is a random access point that closes any risk window opened by a + // prior OBU_SWITCH frame immediately. An OLK is weaker: being open-loop, + // it does not by itself guarantee the same drift-free reconstruction a + // CLK does, and its leading pictures (and any immediately following + // forward-KF overlay/successor frame) can still reference content from + // before the OLK's own refresh. So the window only closes once we are + // clearly past that span, mirroring the codebase's own olk_encountered + // lifecycle (see bitstream.c). A OBU_SWITCH(frame_type == S_FRAME + !OBU_RAS) + // (re)opens the risk window for its own layer. + const bool past_olk_leading_span = + current_frame->display_order_hint >= cm->last_olk_disp_order_hint && + cpi->gf_group.update_type[cpi->gf_group.index] != FWD_KF_OVERLAY_UPDATE && + cpi->gf_group.update_type[cpi->gf_group.index] != FWD_KF_SUCCESSOR_UPDATE; + if (current_frame->cm_obu_type == OBU_CLOSED_LOOP_KEY || + (cpi->olk_encountered && current_frame->frame_type != KEY_FRAME && + past_olk_leading_span) || + (current_frame->frame_type == S_FRAME && cpi->is_ras_frame == 1)) { + cm->in_switch_risk_window = false; + } else if (current_frame->frame_type == S_FRAME) { + cm->in_switch_risk_window = true; + cm->switch_risk_window_mlayer_id = cm->mlayer_id; + cm->switch_risk_window_tlayer_id = cm->tlayer_id; + } + if (current_frame->frame_type == KEY_FRAME) { for (int i = 0; i < cm->seq_params.ref_frames; i++) { if (cm->ref_frame_map[i] != NULL) @@ -5243,6 +5304,7 @@ int av2_encode(AV2_COMP *const cpi, uint8_t *const dest, cm->ref_frame_map[i]->mlayer_id, cm->mlayer_id)) { cm->ref_frame_map[i]->is_restricted = true; + cm->ref_frame_map[i]->display_order_hint = REF_RESTRICTED_DOH; } } } diff --git a/av2/encoder/encoder.h b/av2/encoder/encoder.h index aa90b45b5c..7d8968b7ce 100644 --- a/av2/encoder/encoder.h +++ b/av2/encoder/encoder.h @@ -425,6 +425,13 @@ typedef struct { */ int sframe_type; + /*! + * Selects how the encoder satisfies the AV2 bitstream-conformance + * requirement constraining BAWP/IBC/OPFL/RefineMV around OBU_SWITCH + * frames. Only has an effect when enable_sframe is on. + */ + int sframe_conformance_option; + /*! * Indicates if encoder should autodetect cut scenes and set the keyframes. */ @@ -440,6 +447,17 @@ typedef struct { */ bool enable_sframe; + /*! + * Indicates if S-Frames should refresh all reference slots. + */ + bool sframe_refresh_all; + + /*! + * Number of consecutive keyframes to replace with S-frames between actual + * keyframes. + */ + int sframe_replace_kf; + /*! * Indicates if intra block copy prediction mode should be enabled or not. */ diff --git a/av2/encoder/pass2_strategy.c b/av2/encoder/pass2_strategy.c index c5994861bb..c679d58fbe 100644 --- a/av2/encoder/pass2_strategy.c +++ b/av2/encoder/pass2_strategy.c @@ -2931,6 +2931,17 @@ void av2_get_second_pass_params(AV2_COMP *cpi, frame_params->frame_type = KEY_FRAME; if (frame_params->frame_params_obu_type == NUM_OBU_TYPES) frame_params->frame_params_obu_type = OBU_CLOSED_LOOP_KEY; + const int replace_kf = oxcf->kf_cfg.sframe_replace_kf; + if (replace_kf > 0 && cpi->common.current_frame.frame_number > 0) { + rc->sframe_replace_kf_count++; + if (rc->sframe_replace_kf_count <= replace_kf) { + frame_params->frame_type = S_FRAME; + frame_params->frame_params_obu_type = NUM_OBU_TYPES; + cpi->is_ras_frame = (oxcf->kf_cfg.sframe_type == RAS_FRAME); + } else { + rc->sframe_replace_kf_count = 0; + } + } // Define next KF group and assign bits to it. find_next_key_frame(cpi, &this_frame); } diff --git a/av2/encoder/ratectrl.h b/av2/encoder/ratectrl.h index 8a5e8b69b1..311bcb3607 100644 --- a/av2/encoder/ratectrl.h +++ b/av2/encoder/ratectrl.h @@ -186,6 +186,7 @@ typedef struct { int next_key_frame_forced; int is_src_frame_alt_ref; int sframe_due; + int sframe_replace_kf_count; int high_source_sad; uint64_t avg_source_sad; diff --git a/av2/encoder/rdopt.h b/av2/encoder/rdopt.h index bf650d910d..a1d06dbd29 100644 --- a/av2/encoder/rdopt.h +++ b/av2/encoder/rdopt.h @@ -215,13 +215,6 @@ static INLINE int prune_ref_by_selective_ref_frame( assert(ref_frame[0] != NONE_FRAME); if (ref_frame[0] == INTRA_FRAME) return 0; - RefCntBuffer *ref0_buf = get_ref_frame_buf(cm, ref_frame[0]); - if (ref0_buf) - if (ref0_buf->is_restricted) return 1; - RefCntBuffer *ref1_buf = get_ref_frame_buf(cm, ref_frame[1]); - if (ref1_buf) - if (ref1_buf->is_restricted) return 1; - const int comp_pred = is_inter_ref_frame(ref_frame[1]); if (comp_pred && ref_frame[0] >= RANKED_REF0_TO_PRUNE) return 1; @@ -259,6 +252,13 @@ static INLINE int prune_ref_by_selective_ref_frame( } } + RefCntBuffer *ref0_buf = get_ref_frame_buf(cm, ref_frame[0]); + if (ref0_buf) + if (ref0_buf->is_restricted) return 0; + RefCntBuffer *ref1_buf = get_ref_frame_buf(cm, ref_frame[1]); + if (ref1_buf) + if (ref1_buf->is_restricted) return 0; + int dir_refrank0[2] = { -1, -1 }; int dir_refrank1[2] = { -1, -1 }; int d0 = get_dir_rank(cm, ref_frame[0], dir_refrank0); diff --git a/avm/avm_encoder.h b/avm/avm_encoder.h index da30358261..cbb64f0bc5 100644 --- a/avm/avm_encoder.h +++ b/avm/avm_encoder.h @@ -1071,6 +1071,35 @@ typedef struct avm_codec_enc_cfg { */ unsigned int sframe_type; + /*!\brief sframe_refresh_all + * + * If nonzero, S-Frames refresh all reference slots. + */ + unsigned int sframe_refresh_all; + + /*!\brief sframe_replace_kf + * + * Number of consecutive keyframes to replace with S-frames between actual + * keyframes. 0: disabled, 1: K-S-K-S, 2: K-S-S-K-S-S, etc. + */ + unsigned int sframe_replace_kf; + + /*!\brief sframe_conformance_option + * + * Selects how the encoder satisfies the AV2 bitstream-conformance + * requirement constraining BAWP/IBC/OPFL/RefineMV around OBU_SWITCH + * frames. Only has an effect when enable_sframe is on. Values: + * - 0 (default): force enable_refmvbank = 0 sequence-wide, and disable + * BAWP for the switch frame and every dependent frame that follows + * it. + * - 1: force enable_refmvbank = 1 sequence-wide, and disable BAWP and + * IntraBC (for inter frames) for the switch frame and every + * dependent frame that follows it. + * - 2: force enable_opfl_refine = REFINE_NONE and enable_refinemv = 0 + * sequence-wide. + */ + unsigned int sframe_conformance_option; + /*!\brief Monochrome mode * * If this is nonzero, the encoder will generate a monochrome stream diff --git a/avm/avmcx.h b/avm/avmcx.h index 40c9def402..4b9e170243 100644 --- a/avm/avmcx.h +++ b/avm/avmcx.h @@ -1255,6 +1255,13 @@ enum avme_enc_control_id { * (g_usage == AVM_USAGE_GOOD_QUALITY) enables this mode. */ AV2E_SET_ENABLE_LOW_COMPLEXITY_DECODE = 186, + + /*!\brief Codec control function to select how the encoder satisfies the + * bitstream-conformance requirement constraining BAWP/IBC/OPFL/RefineMV + * around OBU_SWITCH frames, unsigned int parameter. Only has an effect + * when S-Frame encoding is enabled. + */ + AV2E_SET_SFRAME_CONFORMANCE_OPTION = 187, }; /*!\brief avm 1-D scaling mode @@ -1607,6 +1614,9 @@ AVM_CTRL_USE_TYPE(AV2E_SET_ENABLE_CDF_AVERAGING, int) AVM_CTRL_USE_TYPE(AV2E_SET_ENABLE_SFRAME, int) #define AVM_CTRL_AV2E_SET_ENABLE_SFRAME +AVM_CTRL_USE_TYPE(AV2E_SET_SFRAME_CONFORMANCE_OPTION, unsigned int) +#define AVM_CTRL_AV2E_SET_SFRAME_CONFORMANCE_OPTION + AVM_CTRL_USE_TYPE(AV2E_SET_AQ_MODE, unsigned int) #define AVM_CTRL_AV2E_SET_AQ_MODE diff --git a/test/multi_layer_test.cc b/test/multi_layer_test.cc index 0d1d2bdb03..f2d5c6c960 100644 --- a/test/multi_layer_test.cc +++ b/test/multi_layer_test.cc @@ -574,6 +574,7 @@ TEST_P(MultiLayerTest, MultiLayerTest2Embedded2TemporaSframe) { enable_buffer_refresh_test_ = true; enable_s_frame_ = true; cfg_.enable_sframe = 1; + cfg_.sframe_mode = 0; start_decoding_tl1_ = 11; ASSERT_NO_FATAL_FAILURE(RunLoop(&video_nonsc)); EXPECT_EQ(num_mismatch_, 0);