From e55edf4c917348bb15e8e2accef523d7244f5867 Mon Sep 17 00:00:00 2001 From: Jerome Jiang Date: Fri, 28 Aug 2026 09:54:14 -0400 Subject: [PATCH] Add 1-pass real-time CBR rate control support --- av2/encoder/encode_strategy.c | 13 ++++++-- av2/encoder/ratectrl.c | 58 ++++++++++++++++++++++++++++++++++- av2/encoder/ratectrl.h | 14 +++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/av2/encoder/encode_strategy.c b/av2/encoder/encode_strategy.c index 3dab3f0708..3e28fef4d4 100644 --- a/av2/encoder/encode_strategy.c +++ b/av2/encoder/encode_strategy.c @@ -170,6 +170,10 @@ static INLINE void update_frames_till_gf_update(AV2_COMP *cpi) { } static INLINE void update_gf_group_index(AV2_COMP *cpi) { + if (cpi->oxcf.mode == REALTIME && cpi->oxcf.gf_cfg.lag_in_frames == 0) { + cpi->gf_group.index = 0; + return; + } // Increment the gf group index ready for the next frame. If this is // a show_existing_frame with a source other than altref, or if it is not // a displayed forward keyframe, the index was incremented when it was @@ -1223,8 +1227,13 @@ int av2_encode_strategy(AV2_COMP *const cpi, size_t *const size, // Work out some encoding parameters specific to the pass: if (has_no_stats_stage(cpi)) { - if (*frame_flags & FRAMEFLAGS_KEY) { - frame_params.frame_type = KEY_FRAME; + if (cpi->oxcf.gf_cfg.lag_in_frames == 0 && cpi->oxcf.mode == REALTIME) { + av2_get_one_pass_rt_params(cpi, &frame_params.frame_type, *frame_flags); + frame_update_type = get_frame_update_type(gf_group); + } else { + if (*frame_flags & FRAMEFLAGS_KEY) { + frame_params.frame_type = KEY_FRAME; + } } if (oxcf->q_cfg.aq_mode == CYCLIC_REFRESH_AQ) { av2_cyclic_refresh_update_parameters(cpi, frame_params.frame_type); diff --git a/av2/encoder/ratectrl.c b/av2/encoder/ratectrl.c index bf6814e51c..0f0098d5ca 100644 --- a/av2/encoder/ratectrl.c +++ b/av2/encoder/ratectrl.c @@ -1771,7 +1771,9 @@ void av2_rc_postencode_update(AV2_COMP *cpi, uint64_t bytes_used) { rc->avg_frame_qindex[KEY_FRAME] = ROUND_POWER_OF_TWO(3 * rc->avg_frame_qindex[KEY_FRAME] + qindex, 2); } else { - if ((!rc->is_src_frame_alt_ref && !(level <= 1 || is_intrnl_arf))) { + if (!rc->is_src_frame_alt_ref && + (((cpi->oxcf.mode == REALTIME && cpi->oxcf.gf_cfg.lag_in_frames == 0) || + !(level <= 1 || is_intrnl_arf)))) { rc->last_q[INTER_FRAME] = qindex; rc->avg_frame_qindex[INTER_FRAME] = ROUND_POWER_OF_TWO(3 * rc->avg_frame_qindex[INTER_FRAME] + qindex, 2); @@ -2125,3 +2127,57 @@ int av2_calc_iframe_target_size_one_pass_cbr(const AV2_COMP *cpi) { } return av2_rc_clamp_iframe_target_size(cpi, target); } + +#define DEFAULT_KF_BOOST_RT 2000 + +static inline int set_key_frame(AV2_COMP *cpi, unsigned int frame_flags) { + RATE_CONTROL *const rc = &cpi->rc; + AV2_COMMON *const cm = &cpi->common; + // Very first frame has to be key frame. + if (cm->current_frame.frame_number == 0) return 1; + // Set key frame if forced by frame flags. + if (frame_flags & FRAMEFLAGS_KEY) return 1; + if (cpi->oxcf.kf_cfg.auto_key && rc->frames_to_key == 0) return 1; + return 0; +} + +void av2_get_one_pass_rt_params(AV2_COMP *cpi, FRAME_TYPE *const frame_type, + unsigned int frame_flags) { + RATE_CONTROL *const rc = &cpi->rc; + AV2_COMMON *const cm = &cpi->common; + cpi->gf_group.index = 0; + cpi->gf_group.size = 1; + GF_GROUP *const gf_group = &cpi->gf_group; + int target; + // Set frame type. + if (set_key_frame(cpi, frame_flags)) { + *frame_type = KEY_FRAME; + rc->this_key_frame_forced = + cm->current_frame.frame_number != 0 && rc->frames_to_key == 0; + rc->frames_to_key = cpi->oxcf.kf_cfg.key_freq_max; + rc->kf_boost = DEFAULT_KF_BOOST_RT; + gf_group->update_type[gf_group->index] = KF_UPDATE; + } else { + *frame_type = INTER_FRAME; + gf_group->update_type[gf_group->index] = LF_UPDATE; + } + // Set target size. + if (cpi->oxcf.rc_cfg.mode == AVM_CBR) { + if (*frame_type == KEY_FRAME || *frame_type == INTRA_ONLY_FRAME) { + target = av2_calc_iframe_target_size_one_pass_cbr(cpi); + } else { + target = av2_calc_pframe_target_size_one_pass_cbr( + cpi, gf_group->update_type[gf_group->index]); + } + } else { + if (*frame_type == KEY_FRAME || *frame_type == INTRA_ONLY_FRAME) { + target = av2_calc_iframe_target_size_one_pass_vbr(cpi); + } else { + target = av2_calc_pframe_target_size_one_pass_vbr( + cpi, gf_group->update_type[gf_group->index]); + } + } + av2_rc_set_frame_target(cpi, target, cm->width, cm->height); + rc->base_frame_target = target; + cm->current_frame.frame_type = *frame_type; +} diff --git a/av2/encoder/ratectrl.h b/av2/encoder/ratectrl.h index 8a5e8b69b1..52f971a2fe 100644 --- a/av2/encoder/ratectrl.h +++ b/av2/encoder/ratectrl.h @@ -498,6 +498,20 @@ int av2_calc_pframe_target_size_one_pass_cbr( */ int av2_calc_iframe_target_size_one_pass_cbr(const struct AV2_COMP *cpi); +/*!\brief Setup the rate control parameters for 1 pass real-time mode. + * + * \ingroup rate_control + * \param[in,out] cpi Top level encoder structure + * \param[out] frame_type Encoder frame type + * \param[in] frame_flags Encoder frame flags + * + * \remark Nothing is returned. Instead the settings computed in this + * function are set in: \c frame_type, \c cpi->common, \c cpi->rc. + */ +void av2_get_one_pass_rt_params(struct AV2_COMP *cpi, + FRAME_TYPE *const frame_type, + unsigned int frame_flags); + #ifdef __cplusplus } // extern "C" #endif