From 35ee7f5f4bf488f8cb5e931eeb0618f3cb2c7477 Mon Sep 17 00:00:00 2001 From: Deb Mukherjee Date: Wed, 22 Jan 2014 18:00:08 -0800 Subject: [PATCH] One-pass rate control fixes and cleanups Fixes rate control partially in one-pass non-cbr case to achieve a bitrate close to the one desired. Previous version was way off at the high bitrate end. Also includes several one-pass rate control cleanups and refactoring. On derfraw300, one-pass encoding is now 19% off from two-pass speed 0 encoding, down from 35%. Change-Id: I6f0dcdb7f8aa85a7e7cd3a3155d4f9d2a4d2f4f4 --- vp9/encoder/vp9_firstpass.c | 105 +++++++++++- vp9/encoder/vp9_ratectrl.c | 323 +++++++++++++++++++++++------------- 2 files changed, 313 insertions(+), 115 deletions(-) diff --git a/vp9/encoder/vp9_firstpass.c b/vp9/encoder/vp9_firstpass.c index 153046440..3c38c369d 100644 --- a/vp9/encoder/vp9_firstpass.c +++ b/vp9/encoder/vp9_firstpass.c @@ -2336,8 +2336,26 @@ void vp9_get_one_pass_params(VP9_COMP *cpi) { cpi->rc.frames_to_key = cpi->key_frame_frequency; cpi->rc.kf_boost = KEY_FRAME_BOOST; cpi->rc.source_alt_ref_active = 0; + cpi->rc.per_frame_bandwidth = cpi->rc.av_per_frame_bandwidth * 8; + if (cm->current_video_frame == 0) { + cpi->rc.active_worst_quality = cpi->rc.worst_quality; + } else { + // Choose active worst quality twice as large as the last q. + cpi->rc.active_worst_quality = cpi->rc.last_q[KEY_FRAME] * 2; + if (cpi->rc.active_worst_quality > cpi->rc.worst_quality) + cpi->rc.active_worst_quality = cpi->rc.worst_quality; + } } else { cm->frame_type = INTER_FRAME; + cpi->rc.per_frame_bandwidth = cpi->rc.av_per_frame_bandwidth; + if (cm->current_video_frame == 1) { + cpi->rc.active_worst_quality = cpi->rc.worst_quality; + } else { + // Choose active worst quality twice as large as the last q. + cpi->rc.active_worst_quality = cpi->rc.last_q[INTER_FRAME] * 2; + if (cpi->rc.active_worst_quality > cpi->rc.worst_quality) + cpi->rc.active_worst_quality = cpi->rc.worst_quality; + } } if (cpi->rc.frames_till_gf_update_due == 0) { cpi->rc.baseline_gf_interval = DEFAULT_GF_INTERVAL; @@ -2347,8 +2365,88 @@ void vp9_get_one_pass_params(VP9_COMP *cpi) { cpi->rc.frames_till_gf_update_due = cpi->rc.frames_to_key; cpi->refresh_golden_frame = 1; cpi->rc.source_alt_ref_pending = USE_ALTREF_FOR_ONE_PASS; - cpi->rc.gfu_boost = 1000; + cpi->rc.gfu_boost = 2000; + } +} + +// Adjust active_worst_quality level based on buffer level. +static int calc_active_worst_quality_from_buffer_level(const VP9_COMP *cpi) { + // Adjust active_worst_quality: If buffer is above the optimal/target level, + // bring active_worst_quality down depending on fullness of buffer. + // If buffer is below the optimal level, let the active_worst_quality go from + // ambient Q (at buffer = optimal level) to worst_quality level + // (at buffer = critical level). + const VP9_CONFIG *oxcf = &cpi->oxcf; + const RATE_CONTROL *rc = &cpi->rc; + int active_worst_quality = rc->active_worst_quality; + // Maximum limit for down adjustment, ~20%. + int max_adjustment_down = active_worst_quality / 5; + // Buffer level below which we push active_worst to worst_quality. + int critical_level = oxcf->optimal_buffer_level >> 2; + int adjustment = 0; + int buff_lvl_step = 0; + if (rc->buffer_level > oxcf->optimal_buffer_level) { + // Adjust down. + if (max_adjustment_down) { + buff_lvl_step = (int)((oxcf->maximum_buffer_size - + oxcf->optimal_buffer_level) / max_adjustment_down); + if (buff_lvl_step) + adjustment = (int)((rc->buffer_level - oxcf->optimal_buffer_level) / + buff_lvl_step); + active_worst_quality -= adjustment; + } + } else if (rc->buffer_level > critical_level) { + // Adjust up from ambient Q. + if (critical_level) { + buff_lvl_step = (oxcf->optimal_buffer_level - critical_level); + if (buff_lvl_step) { + adjustment = (rc->worst_quality - rc->avg_frame_qindex[INTER_FRAME]) * + (oxcf->optimal_buffer_level - rc->buffer_level) / + buff_lvl_step; + } + active_worst_quality = rc->avg_frame_qindex[INTER_FRAME] + adjustment; + } + } else { + // Set to worst_quality if buffer is below critical level. + active_worst_quality = rc->worst_quality; + } + return active_worst_quality; +} + +static int calc_pframe_target_size_one_pass_cbr(const VP9_COMP *cpi) { + const VP9_CONFIG *oxcf = &cpi->oxcf; + const RATE_CONTROL *rc = &cpi->rc; + int target = rc->av_per_frame_bandwidth; + const int64_t diff = oxcf->optimal_buffer_level - rc->buffer_level; + const int one_pct_bits = 1 + oxcf->optimal_buffer_level / 100; + if (diff > 0) { + // Lower the target bandwidth for this frame. + const int pct_low = MIN(diff / one_pct_bits, oxcf->under_shoot_pct); + target -= (target * pct_low) / 200; + } else if (diff < 0) { + // Increase the target bandwidth for this frame. + const int pct_high = MIN(-diff / one_pct_bits, oxcf->over_shoot_pct); + target += (target * pct_high) / 200; + } + return target; +} + +static int calc_iframe_target_size_one_pass_cbr(const VP9_COMP *cpi) { + int per_frame_bandwidth; + const RATE_CONTROL *rc = &cpi->rc; + if (cpi->common.current_video_frame == 0) { + per_frame_bandwidth = cpi->oxcf.starting_buffer_level / 2; + } else { + int initial_boost = 32; + int kf_boost = MAX(initial_boost, (int)(2 * cpi->output_framerate - 16)); + if (rc->frames_since_key < cpi->output_framerate / 2) { + kf_boost = (int)(kf_boost * rc->frames_since_key / + (cpi->output_framerate / 2)); + } + per_frame_bandwidth = + ((16 + kf_boost) * rc->av_per_frame_bandwidth) >> 4; } + return per_frame_bandwidth; } void vp9_get_one_pass_cbr_params(VP9_COMP *cpi) { @@ -2363,8 +2461,13 @@ void vp9_get_one_pass_cbr_params(VP9_COMP *cpi) { cpi->rc.frames_to_key = cpi->key_frame_frequency; cpi->rc.kf_boost = KEY_FRAME_BOOST; cpi->rc.source_alt_ref_active = 0; + cpi->rc.per_frame_bandwidth = calc_iframe_target_size_one_pass_cbr(cpi); + cpi->rc.active_worst_quality = cpi->rc.worst_quality; } else { cm->frame_type = INTER_FRAME; + cpi->rc.per_frame_bandwidth = calc_pframe_target_size_one_pass_cbr(cpi); + cpi->rc.active_worst_quality = + calc_active_worst_quality_from_buffer_level(cpi); } // Don't use gf_update by default in CBR mode. cpi->rc.frames_till_gf_update_due = INT_MAX; diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c index 701557238..f8cfe4929 100644 --- a/vp9/encoder/vp9_ratectrl.c +++ b/vp9/encoder/vp9_ratectrl.c @@ -213,37 +213,10 @@ static int estimate_bits_at_q(int frame_kind, int q, int mbs, static void calc_iframe_target_size(VP9_COMP *cpi) { const VP9_CONFIG *oxcf = &cpi->oxcf; RATE_CONTROL *const rc = &cpi->rc; - int target; + int target = rc->per_frame_bandwidth; vp9_clear_system_state(); // __asm emms; - // For 1-pass. - if (cpi->pass == 0 && oxcf->end_usage == USAGE_STREAM_FROM_SERVER) { - if (cpi->common.current_video_frame == 0) { - target = oxcf->starting_buffer_level / 2; - } else { - // TODO(marpan): Add in adjustment based on Q. - // If this keyframe was forced, use a more recent Q estimate. - // int Q = (cpi->common.frame_flags & FRAMEFLAGS_KEY) ? - // cpi->rc.avg_frame_qindex : cpi->rc.ni_av_qi; - int initial_boost = 32; - // Boost depends somewhat on frame rate. - int kf_boost = MAX(initial_boost, (int)(2 * cpi->output_framerate - 16)); - // Adjustment up based on q: need to fix. - // kf_boost = kf_boost * kfboost_qadjust(Q) / 100; - // Frame separation adjustment (down). - if (rc->frames_since_key < cpi->output_framerate / 2) { - kf_boost = (int)(kf_boost * rc->frames_since_key / - (cpi->output_framerate / 2)); - } - kf_boost = (kf_boost < 16) ? 16 : kf_boost; - target = ((16 + kf_boost) * rc->per_frame_bandwidth) >> 4; - } - rc->active_worst_quality = rc->worst_quality; - } else { - target = rc->per_frame_bandwidth; - } - if (oxcf->rc_max_intra_bitrate_pct) { const int max_rate = rc->per_frame_bandwidth * oxcf->rc_max_intra_bitrate_pct / 100; @@ -308,70 +281,6 @@ int vp9_drop_frame(VP9_COMP *cpi) { } } -// Adjust active_worst_quality level based on buffer level. -static int adjust_active_worst_quality_from_buffer_level(const VP9_CONFIG *oxcf, - const RATE_CONTROL *rc) { - // Adjust active_worst_quality: If buffer is above the optimal/target level, - // bring active_worst_quality down depending on fullness over buffer. - // If buffer is below the optimal level, let the active_worst_quality go from - // ambient Q (at buffer = optimal level) to worst_quality level - // (at buffer = critical level). - - int active_worst_quality = rc->active_worst_quality; - // Maximum limit for down adjustment, ~20%. - int max_adjustment_down = active_worst_quality / 5; - // Buffer level below which we push active_worst to worst_quality. - int critical_level = oxcf->optimal_buffer_level >> 2; - int adjustment = 0; - int buff_lvl_step = 0; - if (rc->buffer_level > oxcf->optimal_buffer_level) { - // Adjust down. - if (max_adjustment_down) { - buff_lvl_step = (int)((oxcf->maximum_buffer_size - - oxcf->optimal_buffer_level) / max_adjustment_down); - if (buff_lvl_step) - adjustment = (int)((rc->buffer_level - oxcf->optimal_buffer_level) / - buff_lvl_step); - active_worst_quality -= adjustment; - } - } else if (rc->buffer_level > critical_level) { - // Adjust up from ambient Q. - if (critical_level) { - buff_lvl_step = (oxcf->optimal_buffer_level - critical_level); - if (buff_lvl_step) { - adjustment = (rc->worst_quality - rc->avg_frame_qindex[INTER_FRAME]) * - (oxcf->optimal_buffer_level - rc->buffer_level) / - buff_lvl_step; - } - active_worst_quality = rc->avg_frame_qindex[INTER_FRAME] + adjustment; - } - } else { - // Set to worst_quality if buffer is below critical level. - active_worst_quality = rc->worst_quality; - } - return active_worst_quality; -} - -// Adjust target frame size with respect to the buffering constraints: -static int target_size_from_buffer_level(const VP9_CONFIG *oxcf, - const RATE_CONTROL *rc) { - int target = rc->this_frame_target; - const int64_t diff = oxcf->optimal_buffer_level - rc->buffer_level; - const int one_pct_bits = 1 + oxcf->optimal_buffer_level / 100; - - if (diff > 0) { - // Lower the target bandwidth for this frame. - const int pct_low = MIN(diff / one_pct_bits, oxcf->under_shoot_pct); - target -= (target * pct_low) / 200; - } else if (diff < 0) { - // Increase the target bandwidth for this frame. - const int pct_high = MIN(-diff / one_pct_bits, oxcf->over_shoot_pct); - target += (target * pct_high) / 200; - } - - return target; -} - static void calc_pframe_target_size(VP9_COMP *const cpi) { RATE_CONTROL *const rc = &cpi->rc; const VP9_CONFIG *const oxcf = &cpi->oxcf; @@ -383,11 +292,6 @@ static void calc_pframe_target_size(VP9_COMP *const cpi) { // For now, use: cpi->rc.av_per_frame_bandwidth / 16: min_frame_target = MAX(rc->av_per_frame_bandwidth >> 4, FRAME_OVERHEAD_BITS); - rc->this_frame_target = target_size_from_buffer_level(oxcf, rc); - // Adjust qp-max based on buffer level. - rc->active_worst_quality = - adjust_active_worst_quality_from_buffer_level(oxcf, rc); - if (rc->this_frame_target < min_frame_target) rc->this_frame_target = min_frame_target; return; @@ -463,7 +367,6 @@ void vp9_rc_update_rate_correction_factors(VP9_COMP *cpi, int damp_var) { projected_size_based_on_q = estimate_bits_at_q(cpi->common.frame_type, q, cpi->common.MBs, rate_correction_factor); - // Work out a size correction factor. if (projected_size_based_on_q > 0) correction_factor = (100 * cpi->rc.projected_frame_size) / @@ -562,8 +465,9 @@ static int get_active_quality(int q, int gfu_boost, int low, int high, } } -int vp9_rc_pick_q_and_adjust_q_bounds(const VP9_COMP *cpi, - int *bottom_index, int *top_index) { +static int rc_pick_q_and_adjust_q_bounds_one_pass(const VP9_COMP *cpi, + int *bottom_index, + int *top_index) { const VP9_COMMON *const cm = &cpi->common; const RATE_CONTROL *const rc = &cpi->rc; const VP9_CONFIG *const oxcf = &cpi->oxcf; @@ -583,7 +487,7 @@ int vp9_rc_pick_q_and_adjust_q_bounds(const VP9_COMP *cpi, int delta_qindex = vp9_compute_qdelta(cpi, last_boosted_q, (last_boosted_q * 0.75)); active_best_quality = MAX(qindex + delta_qindex, rc->best_quality); - } else if (!(cpi->pass == 0 && cm->current_video_frame == 0)) { + } else if (cm->current_video_frame > 0) { // not first frame of one pass and kf_boost is set double q_adj_factor = 1.0; double q_val; @@ -600,9 +504,6 @@ int vp9_rc_pick_q_and_adjust_q_bounds(const VP9_COMP *cpi, q_adj_factor -= 0.25; } - // Make a further adjustment based on the kf zero motion measure. - q_adj_factor += 0.05 - (0.001 * (double)cpi->twopass.kf_zeromotion_pct); - // Convert the adjustment factor to a qindex delta // on active_best_quality. q_val = vp9_convert_qindex_to_q(active_best_quality); @@ -618,7 +519,6 @@ int vp9_rc_pick_q_and_adjust_q_bounds(const VP9_COMP *cpi, #endif } else if (!rc->is_src_frame_alt_ref && (cpi->refresh_golden_frame || cpi->refresh_alt_ref_frame)) { - // Use the lower of active_worst_quality and recent // average Q as basis for GF/ARF best Q limit unless last frame was // a key frame. @@ -669,14 +569,7 @@ int vp9_rc_pick_q_and_adjust_q_bounds(const VP9_COMP *cpi, if (oxcf->end_usage == USAGE_CONSTANT_QUALITY) { active_best_quality = cpi->cq_target_quality; } else { - if (cpi->pass == 0 && - rc->avg_frame_qindex[INTER_FRAME] < active_worst_quality) - // 1-pass: for now, use the average Q for the active_best, if its lower - // than active_worst. - active_best_quality = inter_minq[rc->avg_frame_qindex[INTER_FRAME]]; - else - active_best_quality = inter_minq[active_worst_quality]; - + active_best_quality = inter_minq[rc->avg_frame_qindex[INTER_FRAME]]; // For the constrained quality mode we don't want // q to fall below the cq level. if ((oxcf->end_usage == USAGE_CONSTRAINED_QUALITY) && @@ -711,7 +604,7 @@ int vp9_rc_pick_q_and_adjust_q_bounds(const VP9_COMP *cpi, #if LIMIT_QRANGE_FOR_ALTREF_AND_KEY // Limit Q range for the adaptive loop. if (cm->frame_type == KEY_FRAME && !rc->this_key_frame_forced) { - if (!(cpi->pass == 0 && cm->current_video_frame == 0)) + if (!(cm->current_video_frame == 0)) *top_index = (active_worst_quality + active_best_quality * 3) / 4; } else if (!rc->is_src_frame_alt_ref && (oxcf->end_usage != USAGE_STREAM_FROM_SERVER) && @@ -761,6 +654,208 @@ int vp9_rc_pick_q_and_adjust_q_bounds(const VP9_COMP *cpi, return q; } +static int rc_pick_q_and_adjust_q_bounds_two_pass(const VP9_COMP *cpi, + int *bottom_index, + int *top_index) { + const VP9_COMMON *const cm = &cpi->common; + const RATE_CONTROL *const rc = &cpi->rc; + const VP9_CONFIG *const oxcf = &cpi->oxcf; + int active_best_quality; + int active_worst_quality = rc->active_worst_quality; + int q; + + if (frame_is_intra_only(cm)) { + active_best_quality = rc->best_quality; +#if !CONFIG_MULTIPLE_ARF + // Handle the special case for key frames forced when we have75 reached + // the maximum key frame interval. Here force the Q to a range + // based on the ambient Q to reduce the risk of popping. + if (rc->this_key_frame_forced) { + int qindex = rc->last_boosted_qindex; + double last_boosted_q = vp9_convert_qindex_to_q(qindex); + int delta_qindex = vp9_compute_qdelta(cpi, last_boosted_q, + (last_boosted_q * 0.75)); + active_best_quality = MAX(qindex + delta_qindex, rc->best_quality); + } else { + // Not forced keyframe. + double q_adj_factor = 1.0; + double q_val; + // Baseline value derived from cpi->active_worst_quality and kf boost. + active_best_quality = get_active_quality(active_worst_quality, + rc->kf_boost, + kf_low, kf_high, + kf_low_motion_minq, + kf_high_motion_minq); + + // Allow somewhat lower kf minq with small image formats. + if ((cm->width * cm->height) <= (352 * 288)) { + q_adj_factor -= 0.25; + } + + // Make a further adjustment based on the kf zero motion measure. + q_adj_factor += 0.05 - (0.001 * (double)cpi->twopass.kf_zeromotion_pct); + + // Convert the adjustment factor to a qindex delta + // on active_best_quality. + q_val = vp9_convert_qindex_to_q(active_best_quality); + active_best_quality += vp9_compute_qdelta(cpi, q_val, q_val * + q_adj_factor); + } +#else + double current_q; + // Force the KF quantizer to be 30% of the active_worst_quality. + current_q = vp9_convert_qindex_to_q(active_worst_quality); + active_best_quality = active_worst_quality + + vp9_compute_qdelta(cpi, current_q, current_q * 0.3); +#endif + } else if (!rc->is_src_frame_alt_ref && + (cpi->refresh_golden_frame || cpi->refresh_alt_ref_frame)) { + // Use the lower of active_worst_quality and recent + // average Q as basis for GF/ARF best Q limit unless last frame was + // a key frame. + if (rc->frames_since_key > 1 && + rc->avg_frame_qindex[INTER_FRAME] < active_worst_quality) { + q = rc->avg_frame_qindex[INTER_FRAME]; + } else { + q = active_worst_quality; + } + // For constrained quality dont allow Q less than the cq level + if (oxcf->end_usage == USAGE_CONSTRAINED_QUALITY) { + if (q < cpi->cq_target_quality) + q = cpi->cq_target_quality; + if (rc->frames_since_key > 1) { + active_best_quality = get_active_quality(q, rc->gfu_boost, + gf_low, gf_high, + afq_low_motion_minq, + afq_high_motion_minq); + } else { + active_best_quality = get_active_quality(q, rc->gfu_boost, + gf_low, gf_high, + gf_low_motion_minq, + gf_high_motion_minq); + } + // Constrained quality use slightly lower active best. + active_best_quality = active_best_quality * 15 / 16; + + } else if (oxcf->end_usage == USAGE_CONSTANT_QUALITY) { + if (!cpi->refresh_alt_ref_frame) { + active_best_quality = cpi->cq_target_quality; + } else { + if (rc->frames_since_key > 1) { + active_best_quality = get_active_quality( + q, rc->gfu_boost, gf_low, gf_high, + afq_low_motion_minq, afq_high_motion_minq); + } else { + active_best_quality = get_active_quality( + q, rc->gfu_boost, gf_low, gf_high, + gf_low_motion_minq, gf_high_motion_minq); + } + } + } else { + active_best_quality = get_active_quality( + q, rc->gfu_boost, gf_low, gf_high, + gf_low_motion_minq, gf_high_motion_minq); + } + } else { + if (oxcf->end_usage == USAGE_CONSTANT_QUALITY) { + active_best_quality = cpi->cq_target_quality; + } else { + active_best_quality = inter_minq[active_worst_quality]; + + // For the constrained quality mode we don't want + // q to fall below the cq level. + if ((oxcf->end_usage == USAGE_CONSTRAINED_QUALITY) && + (active_best_quality < cpi->cq_target_quality)) { + // If we are strongly undershooting the target rate in the last + // frames then use the user passed in cq value not the auto + // cq value. + if (rc->rolling_actual_bits < rc->min_frame_bandwidth) + active_best_quality = oxcf->cq_level; + else + active_best_quality = cpi->cq_target_quality; + } + } + } + + // Clip the active best and worst quality values to limits. + if (active_worst_quality > rc->worst_quality) + active_worst_quality = rc->worst_quality; + + if (active_best_quality < rc->best_quality) + active_best_quality = rc->best_quality; + + if (active_best_quality > rc->worst_quality) + active_best_quality = rc->worst_quality; + + if (active_worst_quality < active_best_quality) + active_worst_quality = active_best_quality; + + *top_index = active_worst_quality; + *bottom_index = active_best_quality; + +#if LIMIT_QRANGE_FOR_ALTREF_AND_KEY + // Limit Q range for the adaptive loop. + if (cm->frame_type == KEY_FRAME && !rc->this_key_frame_forced) { + *top_index = (active_worst_quality + active_best_quality * 3) / 4; + } else if (!rc->is_src_frame_alt_ref && + (oxcf->end_usage != USAGE_STREAM_FROM_SERVER) && + (cpi->refresh_golden_frame || cpi->refresh_alt_ref_frame)) { + *top_index = (active_worst_quality + active_best_quality) / 2; + } +#endif + + if (oxcf->end_usage == USAGE_CONSTANT_QUALITY) { + q = active_best_quality; + // Special case code to try and match quality with forced key frames. + } else if ((cm->frame_type == KEY_FRAME) && rc->this_key_frame_forced) { + q = rc->last_boosted_qindex; + } else { + q = vp9_rc_regulate_q(cpi, rc->this_frame_target, + active_best_quality, active_worst_quality); + if (q > *top_index) { + // Special case when we are targeting the max allowed rate. + if (cpi->rc.this_frame_target >= cpi->rc.max_frame_bandwidth) + *top_index = q; + else + q = *top_index; + } + } +#if CONFIG_MULTIPLE_ARF + // Force the quantizer determined by the coding order pattern. + if (cpi->multi_arf_enabled && (cm->frame_type != KEY_FRAME) && + cpi->oxcf.end_usage != USAGE_CONSTANT_QUALITY) { + double new_q; + double current_q = vp9_convert_qindex_to_q(active_worst_quality); + int level = cpi->this_frame_weight; + assert(level >= 0); + new_q = current_q * (1.0 - (0.2 * (cpi->max_arf_level - level))); + q = active_worst_quality + + vp9_compute_qdelta(cpi, current_q, new_q); + + *bottom_index = q; + *top_index = q; + printf("frame:%d q:%d\n", cm->current_video_frame, q); + } +#endif + assert(*top_index <= rc->worst_quality && + *top_index >= rc->best_quality); + assert(*bottom_index <= rc->worst_quality && + *bottom_index >= rc->best_quality); + assert(q <= rc->worst_quality && q >= rc->best_quality); + return q; +} + +int vp9_rc_pick_q_and_adjust_q_bounds(const VP9_COMP *cpi, + int *bottom_index, + int *top_index) { + if (cpi->pass == 0) + return rc_pick_q_and_adjust_q_bounds_one_pass( + cpi, bottom_index, top_index); + else + return rc_pick_q_and_adjust_q_bounds_two_pass( + cpi, bottom_index, top_index); +} + void vp9_rc_compute_frame_size_bounds(const VP9_COMP *cpi, int this_frame_target, int *frame_under_shoot_limit, -- 2.40.0