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
This commit is contained in:
Marco 2015-06-08 10:03:51 -07:00
parent d1398e9f13
commit d77f51ba9e
7 changed files with 138 additions and 1 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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) &&

View File

@ -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;

View File

@ -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;
}

View File

@ -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