diff --git a/apps/avmenc.c b/apps/avmenc.c index 05f2441dc0..440fab2229 100644 --- a/apps/avmenc.c +++ b/apps/avmenc.c @@ -1180,6 +1180,8 @@ 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_replace_kf, argi)) { + config->cfg.sframe_replace_kf = 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 9df0368685..851e0672ad 100644 --- a/av2/arg_defs.c +++ b/av2/arg_defs.c @@ -289,6 +289,9 @@ 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_replace_kf = + ARG_DEF(NULL, "sframe-replace-kf", 1, + "Replace N consecutive keyframes with S-frames (0: off)"), .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 7fe25641bc..c77d9d2b6f 100644 --- a/av2/arg_defs.h +++ b/av2/arg_defs.h @@ -108,6 +108,7 @@ 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_replace_kf; 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 089fc9e154..41101dd231 100644 --- a/av2/av2_cx_iface.c +++ b/av2/av2_cx_iface.c @@ -1523,6 +1523,7 @@ 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_replace_kf = cfg->sframe_replace_kf; oxcf->unit_test_cfg.insert_sframe = extra_cfg->enable_sframe; kf_cfg->enable_keyframe_filtering = extra_cfg->enable_keyframe_filtering; @@ -4782,6 +4783,7 @@ static const avm_codec_enc_cfg_t encoder_usage_cfg[] = { { 0, // sframe_dist 1, // sframe_mode 0, // sframe_type + 1, // sframe_replace_kf 0, // monochrome 0, // full_still_picture_hdr 1, // enable_tcq diff --git a/av2/encoder/encode_strategy.c b/av2/encoder/encode_strategy.c index 6e6eb63d39..f94fd04564 100644 --- a/av2/encoder/encode_strategy.c +++ b/av2/encoder/encode_strategy.c @@ -1277,6 +1277,7 @@ 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 aa9c53fb8e..e91a44d1c1 100644 --- a/av2/encoder/encoder.c +++ b/av2/encoder/encoder.c @@ -5102,6 +5102,7 @@ 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; if (current_frame->frame_type == KEY_FRAME) { for (int i = 0; i < cm->seq_params.ref_frames; i++) { diff --git a/av2/encoder/encoder.h b/av2/encoder/encoder.h index 15df43f57f..c014e940c8 100644 --- a/av2/encoder/encoder.h +++ b/av2/encoder/encoder.h @@ -438,6 +438,12 @@ typedef struct { */ bool enable_sframe; + /*! + * 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 8418bffc07..13aba6e1c4 100644 --- a/av2/encoder/pass2_strategy.c +++ b/av2/encoder/pass2_strategy.c @@ -2930,6 +2930,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/avm/avm_encoder.h b/avm/avm_encoder.h index 9a82ee009d..ed03bdda8a 100644 --- a/avm/avm_encoder.h +++ b/avm/avm_encoder.h @@ -1064,6 +1064,13 @@ typedef struct avm_codec_enc_cfg { */ unsigned int sframe_type; + /*!\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 Monochrome mode * * If this is nonzero, the encoder will generate a monochrome stream