From d77f51ba9ed10f4c26ee694445f9b0ec53c2500c Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 8 Jun 2015 10:03:51 -0700 Subject: [PATCH] Add dynamic resize logic for 1 pass CBR. Decision to scale down/up is based on buffer state and average QP over previous time window. Limit the total amount of down-scaling to be at most one scale down for now. Reset certain quantities after resize (buffer level, cyclic refresh, rate correction factor). Feature is enable via the setting rc_resize_allowed = 1. Change-Id: I9b1a53024e1e1e953fb8a1e1f75d21d160280dc7 --- examples/vpx_temporal_svc_encoder.c | 4 +- vp9/encoder/vp9_aq_cyclicrefresh.c | 7 +++ vp9/encoder/vp9_aq_cyclicrefresh.h | 2 + vp9/encoder/vp9_encoder.c | 28 +++++++++ vp9/encoder/vp9_encoder.h | 6 ++ vp9/encoder/vp9_ratectrl.c | 90 +++++++++++++++++++++++++++++ vp9/encoder/vp9_ratectrl.h | 2 + 7 files changed, 138 insertions(+), 1 deletion(-) diff --git a/examples/vpx_temporal_svc_encoder.c b/examples/vpx_temporal_svc_encoder.c index da2a7cfcf..484deb5b3 100644 --- a/examples/vpx_temporal_svc_encoder.c +++ b/examples/vpx_temporal_svc_encoder.c @@ -608,7 +608,6 @@ int main(int argc, char **argv) { // Real time parameters. cfg.rc_dropframe_thresh = strtol(argv[9], NULL, 0); cfg.rc_end_usage = VPX_CBR; - cfg.rc_resize_allowed = 0; cfg.rc_min_quantizer = 2; cfg.rc_max_quantizer = 56; if (strncmp(encoder->name, "vp9", 3) == 0) @@ -619,6 +618,9 @@ int main(int argc, char **argv) { cfg.rc_buf_optimal_sz = 600; cfg.rc_buf_sz = 1000; + // Disable dynamic resizing by default. + cfg.rc_resize_allowed = 0; + // Use 1 thread as default. cfg.g_threads = 1; diff --git a/vp9/encoder/vp9_aq_cyclicrefresh.c b/vp9/encoder/vp9_aq_cyclicrefresh.c index df70d48f8..78dced26b 100644 --- a/vp9/encoder/vp9_aq_cyclicrefresh.c +++ b/vp9/encoder/vp9_aq_cyclicrefresh.c @@ -529,3 +529,10 @@ void vp9_cyclic_refresh_setup(VP9_COMP *const cpi) { int vp9_cyclic_refresh_get_rdmult(const CYCLIC_REFRESH *cr) { return cr->rdmult; } + +void vp9_cyclic_refresh_reset_resize(VP9_COMP *const cpi) { + const VP9_COMMON *const cm = &cpi->common; + CYCLIC_REFRESH *const cr = cpi->cyclic_refresh; + memset(cr->map, 0, cm->mi_rows * cm->mi_cols); + cr->sb_index = 0; +} diff --git a/vp9/encoder/vp9_aq_cyclicrefresh.h b/vp9/encoder/vp9_aq_cyclicrefresh.h index 99bb98ec8..29d2a91bc 100644 --- a/vp9/encoder/vp9_aq_cyclicrefresh.h +++ b/vp9/encoder/vp9_aq_cyclicrefresh.h @@ -75,6 +75,8 @@ void vp9_cyclic_refresh_setup(struct VP9_COMP *const cpi); int vp9_cyclic_refresh_get_rdmult(const CYCLIC_REFRESH *cr); +void vp9_cyclic_refresh_reset_resize(struct VP9_COMP *const cpi); + static INLINE int cyclic_refresh_segment_id_boosted(int segment_id) { return segment_id == CR_SEGMENT_ID_BOOST1 || segment_id == CR_SEGMENT_ID_BOOST2; diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c index ecd60d772..4bdd09fb8 100644 --- a/vp9/encoder/vp9_encoder.c +++ b/vp9/encoder/vp9_encoder.c @@ -1596,6 +1596,9 @@ VP9_COMP *vp9_create_compressor(VP9EncoderConfig *oxcf, sizeof(*cm->frame_contexts))); cpi->use_svc = 0; + cpi->resize_state = 0; + cpi->resize_avg_qp = 0; + cpi->resize_buffer_underflow = 0; cpi->common.buffer_pool = pool; init_config(cpi, oxcf); @@ -3032,6 +3035,31 @@ static void set_frame_size(VP9_COMP *cpi) { oxcf->scaled_frame_height); } + if (oxcf->pass == 0 && + oxcf->rc_mode == VPX_CBR && + !cpi->use_svc && + oxcf->resize_mode == RESIZE_DYNAMIC) { + if (cpi->resize_state == 1) { + oxcf->scaled_frame_width = + (cm->width * cpi->resize_scale_num) / cpi->resize_scale_den; + oxcf->scaled_frame_height = + (cm->height * cpi->resize_scale_num) /cpi->resize_scale_den; + } else if (cpi->resize_state == -1) { + // Go back up to original size. + oxcf->scaled_frame_width = oxcf->width; + oxcf->scaled_frame_height = oxcf->height; + } + if (cpi->resize_state != 0) { + // There has been a change in frame size. + vp9_set_size_literal(cpi, + oxcf->scaled_frame_width, + oxcf->scaled_frame_height); + + // TODO(agrange) Scale cpi->max_mv_magnitude if frame-size has changed. + set_mv_search_params(cpi); + } + } + if ((oxcf->pass == 2) && (!cpi->use_svc || (is_two_pass_svc(cpi) && diff --git a/vp9/encoder/vp9_encoder.h b/vp9/encoder/vp9_encoder.h index 4d2a186e8..2b0da103f 100644 --- a/vp9/encoder/vp9_encoder.h +++ b/vp9/encoder/vp9_encoder.h @@ -479,6 +479,12 @@ typedef struct VP9_COMP { #endif int resize_pending; + int resize_state; + int resize_scale_num; + int resize_scale_den; + int resize_avg_qp; + int resize_buffer_underflow; + int resize_count; // VAR_BASED_PARTITION thresholds // 0 - threshold_64x64; 1 - threshold_32x32; diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c index 32682fe74..158581b81 100644 --- a/vp9/encoder/vp9_ratectrl.c +++ b/vp9/encoder/vp9_ratectrl.c @@ -1596,6 +1596,7 @@ void vp9_rc_get_one_pass_cbr_params(VP9_COMP *cpi) { target = calc_pframe_target_size_one_pass_cbr(cpi); vp9_rc_set_frame_target(cpi, target); + cpi->resize_state = vp9_resize_one_pass_cbr(cpi); } int vp9_compute_qdelta(const RATE_CONTROL *rc, double qstart, double qtarget, @@ -1756,3 +1757,92 @@ void vp9_set_target_rate(VP9_COMP *cpi) { vbr_rate_correction(cpi, &target_rate); vp9_rc_set_frame_target(cpi, target_rate); } + +// Check if we should resize, based on average QP from past x frames. +// Only allow for resize at most one scale down for now, scaling factor is 2. +int vp9_resize_one_pass_cbr(VP9_COMP *cpi) { + const VP9_COMMON *const cm = &cpi->common; + RATE_CONTROL *const rc = &cpi->rc; + int resize_now = 0; + cpi->resize_scale_num = 1; + cpi->resize_scale_den = 1; + // Don't resize on key frame; reset the counters on key frame. + if (cm->frame_type == KEY_FRAME) { + cpi->resize_avg_qp = 0; + cpi->resize_count = 0; + return 0; + } + // Resize based on average QP over some window. + // Ignore samples close to key frame, since QP is usually high after key. + if (cpi->rc.frames_since_key > 2 * cpi->framerate) { + const int window = 5 * cpi->framerate; + cpi->resize_avg_qp += cm->base_qindex; + if (cpi->rc.buffer_level < 0) + ++cpi->resize_buffer_underflow; + ++cpi->resize_count; + // Check for resize action every "window" frames. + if (cpi->resize_count == window) { + int avg_qp = cpi->resize_avg_qp / cpi->resize_count; + // Resize down if buffer level has underflowed sufficent amount in past + // window, and we are at original resolution. + // Resize back up if average QP is low, and we are currently in a resized + // down state. + if (cpi->resize_state == 0 && + cpi->resize_buffer_underflow > (cpi->resize_count >> 3)) { + resize_now = 1; + } else if (cpi->resize_state == 1 && + avg_qp < 40 * cpi->rc.worst_quality / 100) { + resize_now = -1; + } + // Reset for next window measurement. + cpi->resize_avg_qp = 0; + cpi->resize_count = 0; + cpi->resize_buffer_underflow = 0; + } + } + // If decision is to resize, reset some quantities, and check is we should + // reduce rate correction factor, + if (resize_now != 0) { + int target_bits_per_frame; + int active_worst_quality; + int qindex; + int tot_scale_change; + // For now, resize is by 1/2 x 1/2. + cpi->resize_scale_num = 1; + cpi->resize_scale_den = 2; + tot_scale_change = (cpi->resize_scale_den * cpi->resize_scale_den) / + (cpi->resize_scale_num * cpi->resize_scale_num); + // Reset buffer level to optimal, update target size. + rc->buffer_level = rc->optimal_buffer_level; + rc->bits_off_target = rc->optimal_buffer_level; + rc->this_frame_target = calc_pframe_target_size_one_pass_cbr(cpi); + // Reset cyclic refresh parameters. + if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ && cm->seg.enabled) + vp9_cyclic_refresh_reset_resize(cpi); + // Get the projected qindex, based on the scaled target frame size (scaled + // so target_bits_per_mb in vp9_rc_regulate_q will be correct target). + target_bits_per_frame = (resize_now == 1) ? + rc->this_frame_target * tot_scale_change : + rc->this_frame_target / tot_scale_change; + active_worst_quality = calc_active_worst_quality_one_pass_cbr(cpi); + qindex = vp9_rc_regulate_q(cpi, + target_bits_per_frame, + rc->best_quality, + active_worst_quality); + // If resize is down, check if projected q index is close to worst_quality, + // and if so, reduce the rate correction factor (since likely can afford + // lower q for resized frame). + if (resize_now == 1 && + qindex > 90 * cpi->rc.worst_quality / 100) { + rc->rate_correction_factors[INTER_NORMAL] *= 0.85; + } + // If resize is back up, check if projected q index is too much above the + // current base_qindex, and if so, reduce the rate correction factor + // (since prefer to keep q for resized frame at least close to previous q). + if (resize_now == -1 && + qindex > 130 * cm->base_qindex / 100) { + rc->rate_correction_factors[INTER_NORMAL] *= 0.9; + } + } + return resize_now; +} diff --git a/vp9/encoder/vp9_ratectrl.h b/vp9/encoder/vp9_ratectrl.h index e12d200be..a10836c74 100644 --- a/vp9/encoder/vp9_ratectrl.h +++ b/vp9/encoder/vp9_ratectrl.h @@ -245,6 +245,8 @@ void vp9_rc_set_gf_interval_range(const struct VP9_COMP *const cpi, void vp9_set_target_rate(struct VP9_COMP *cpi); +int vp9_resize_one_pass_cbr(struct VP9_COMP *cpi); + #ifdef __cplusplus } // extern "C" #endif -- 2.40.0