From 1b8b8b0d0df9ac6f0a50db73d20231ecb7529666 Mon Sep 17 00:00:00 2001 From: Marco Paniconi Date: Tue, 17 Dec 2013 15:45:30 -0800 Subject: [PATCH] Updates for 1-pass CBR rate control. Adjustments based on buffer level, frame dropper. Change-Id: Iaa85b570493526a60c4b9fb7ded4c0226b1b3a33 --- test/datarate_test.cc | 79 ++++++++++++++- vp9/common/vp9_onyx.h | 3 + vp9/encoder/vp9_onyx_if.c | 19 ++++ vp9/encoder/vp9_onyx_int.h | 6 +- vp9/encoder/vp9_ratectrl.c | 202 +++++++++++++++++++++++++++++++------ vp9/encoder/vp9_ratectrl.h | 6 ++ vp9/vp9_cx_iface.c | 2 + 7 files changed, 280 insertions(+), 37 deletions(-) diff --git a/test/datarate_test.cc b/test/datarate_test.cc index 2d4652271..40a968c81 100644 --- a/test/datarate_test.cc +++ b/test/datarate_test.cc @@ -193,7 +193,10 @@ class DatarateTestVP9 : public ::libvpx_test::EncoderTest, virtual void ResetModel() { last_pts_ = 0; + bits_in_buffer_model_ = cfg_.rc_target_bitrate * cfg_.rc_buf_initial_sz; frame_number_ = 0; + first_drop_ = 0; + num_drops_ = 0; bits_total_ = 0; duration_ = 0.0; } @@ -209,8 +212,29 @@ class DatarateTestVP9 : public ::libvpx_test::EncoderTest, } virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) { + // Time since last timestamp = duration. + vpx_codec_pts_t duration = pkt->data.frame.pts - last_pts_; + + // Add to the buffer the bits we'd expect from a constant bitrate server. + bits_in_buffer_model_ += duration * timebase_ * cfg_.rc_target_bitrate + * 1000; + + // Buffer should not go negative. + ASSERT_GE(bits_in_buffer_model_, 0) << "Buffer Underrun at frame " + << pkt->data.frame.pts; + const int frame_size_in_bits = pkt->data.frame.sz * 8; bits_total_ += frame_size_in_bits; + + // If first drop not set and we have a drop set it to this time. + if (!first_drop_ && duration > 1) + first_drop_ = last_pts_ + 1; + + // Update the number of frame drops. + if (duration > 1) { + num_drops_+= (duration - 1); + } + // Update the most recent pts. last_pts_ = pkt->data.frame.pts; ++frame_number_; @@ -231,13 +255,17 @@ class DatarateTestVP9 : public ::libvpx_test::EncoderTest, double duration_; double effective_datarate_; int set_cpu_used_; + int bits_in_buffer_model_; + int first_drop_; + int num_drops_; }; -// There is no buffer model/frame dropper in VP9 currently, so for now we -// have separate test for VP9 rate targeting for 1-pass CBR. We only check -// that effective datarate is within some range of target bitrate. -// No frame dropper, so we can't go to low bitrates. +// Check basic rate targeting, TEST_P(DatarateTestVP9, BasicRateTargeting) { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_dropframe_thresh = 1; cfg_.rc_min_quantizer = 0; cfg_.rc_max_quantizer = 63; cfg_.rc_end_usage = VPX_CBR; @@ -257,6 +285,49 @@ TEST_P(DatarateTestVP9, BasicRateTargeting) { } } +// Check that (1) the first dropped frame gets earlier and earlier +// as the drop frame threshold is increased, and (2) that the total number of +// frame drops does not decrease as we increase frame drop threshold. +// Use a lower qp-max to force some frame drops. +TEST_P(DatarateTestVP9, ChangingDropFrameThresh) { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_undershoot_pct = 20; + cfg_.rc_undershoot_pct = 20; + cfg_.rc_dropframe_thresh = 10; + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 50; + cfg_.rc_end_usage = VPX_CBR; + cfg_.rc_target_bitrate = 200; + + ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, + 30, 1, 0, 140); + + const int kDropFrameThreshTestStep = 30; + int last_drop = 140; + int last_num_drops = 0; + for (int i = 10; i < 100; i += kDropFrameThreshTestStep) { + cfg_.rc_dropframe_thresh = i; + ResetModel(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_GE(effective_datarate_, cfg_.rc_target_bitrate * 0.85) + << " The datarate for the file is lower than target by too much!"; + ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * 1.15) + << " The datarate for the file is greater than target by too much!"; + ASSERT_LE(first_drop_, last_drop) + << " The first dropped frame for drop_thresh " << i + << " > first dropped frame for drop_thresh " + << i - kDropFrameThreshTestStep; + ASSERT_GE(num_drops_, last_num_drops) + << " The number of dropped frames for drop_thresh " << i + << " < number of dropped frames for drop_thresh " + << i - kDropFrameThreshTestStep; + last_drop = first_drop_; + last_num_drops = num_drops_; + } +} + VP8_INSTANTIATE_TEST_CASE(DatarateTest, ALL_TEST_MODES); VP9_INSTANTIATE_TEST_CASE(DatarateTestVP9, ::testing::Values(::libvpx_test::kOnePassGood), diff --git a/vp9/common/vp9_onyx.h b/vp9/common/vp9_onyx.h index 65a2a5ed1..be8fedc47 100644 --- a/vp9/common/vp9_onyx.h +++ b/vp9/common/vp9_onyx.h @@ -129,6 +129,9 @@ extern "C" int64_t optimal_buffer_level; int64_t maximum_buffer_size; + // Frame drop threshold. + int drop_frames_water_mark; + // controlling quality int fixed_q; int worst_allowed_q; diff --git a/vp9/encoder/vp9_onyx_if.c b/vp9/encoder/vp9_onyx_if.c index 5bbd42bea..daf44658d 100644 --- a/vp9/encoder/vp9_onyx_if.c +++ b/vp9/encoder/vp9_onyx_if.c @@ -2987,6 +2987,20 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi, configure_static_seg_features(cpi); } + // For 1 pass CBR, check if we are dropping this frame. + // Never drop on key frame. + if (cpi->pass == 0 && + cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER && + cm->frame_type != KEY_FRAME) { + if (vp9_drop_frame(cpi)) { + // Update buffer level with zero size, update frame counters, and return. + vp9_update_buffer_level(cpi, 0); + cm->current_video_frame++; + cpi->rc.frames_since_key++; + return; + } + } + vp9_clear_system_state(); vp9_zero(cpi->rd_tx_select_threshes); @@ -3587,6 +3601,11 @@ int vp9_get_compressed_data(VP9_PTR ptr, unsigned int *frame_flags, if (cm->refresh_frame_context) cm->frame_contexts[cm->frame_context_idx] = cm->fc; + // Frame was dropped, release scaled references. + if (*size == 0) { + release_scaled_references(cpi); + } + if (*size > 0) { // if its a dropped frame honor the requests on subsequent frames cpi->droppable = !frame_is_reference(cpi); diff --git a/vp9/encoder/vp9_onyx_int.h b/vp9/encoder/vp9_onyx_int.h index 2d7cd01f4..13ce1cc24 100644 --- a/vp9/encoder/vp9_onyx_int.h +++ b/vp9/encoder/vp9_onyx_int.h @@ -331,6 +331,9 @@ typedef struct { int buffer_level; int bits_off_target; + int decimation_factor; + int decimation_count; + int rolling_target_bits; int rolling_actual_bits; @@ -480,9 +483,6 @@ typedef struct VP9_COMP { int static_mb_pct; // % forced skip mbs by segmentation int seg0_progress, seg0_idx, seg0_cnt; - int decimation_factor; - int decimation_count; - // for real time encoding int speed; int compressor_speed; diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c index 9ea0c3ddf..ca7d6c64c 100644 --- a/vp9/encoder/vp9_ratectrl.c +++ b/vp9/encoder/vp9_ratectrl.c @@ -222,6 +222,31 @@ static void calc_iframe_target_size(VP9_COMP *cpi) { // New Two pass RC target = cpi->rc.per_frame_bandwidth; + // For 1-pass. + if (cpi->pass == 0) { + if (cpi->common.current_video_frame == 0) { + target = cpi->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 (cpi->rc.frames_since_key < cpi->output_framerate / 2) { + kf_boost = (int)(kf_boost * cpi->rc.frames_since_key / + (cpi->output_framerate / 2)); + } + kf_boost = (kf_boost < 16) ? 16 : kf_boost; + target = ((16 + kf_boost) * cpi->rc.per_frame_bandwidth) >> 4; + } + cpi->rc.active_worst_quality = cpi->rc.worst_quality; + } + if (cpi->oxcf.rc_max_intra_bitrate_pct) { int max_rate = cpi->rc.per_frame_bandwidth * cpi->oxcf.rc_max_intra_bitrate_pct / 100; @@ -242,18 +267,154 @@ static void calc_gf_params(VP9_COMP *cpi) { cpi->rc.frames_till_gf_update_due = cpi->rc.baseline_gf_interval; } +// Update the buffer level: leaky bucket model. +void vp9_update_buffer_level(VP9_COMP *const cpi, int encoded_frame_size) { + VP9_COMMON *const cm = &cpi->common; + // Non-viewable frames are a special case and are treated as pure overhead. + if (!cm->show_frame) { + cpi->rc.bits_off_target -= encoded_frame_size; + } else { + cpi->rc.bits_off_target += cpi->rc.av_per_frame_bandwidth - + encoded_frame_size; + } + // Clip the buffer level to the maximum specified buffer size. + if (cpi->rc.bits_off_target > cpi->oxcf.maximum_buffer_size) { + cpi->rc.bits_off_target = cpi->oxcf.maximum_buffer_size; + } + cpi->rc.buffer_level = cpi->rc.bits_off_target; +} + +int vp9_drop_frame(VP9_COMP *const cpi) { + if (!cpi->oxcf.drop_frames_water_mark) { + return 0; + } else { + if (cpi->rc.buffer_level < 0) { + // Always drop if buffer is below 0. + return 1; + } else { + // If buffer is below drop_mark, for now just drop every other frame + // (starting with the next frame) until it increases back over drop_mark. + int drop_mark = (int)(cpi->oxcf.drop_frames_water_mark * + cpi->oxcf.optimal_buffer_level / 100); + if ((cpi->rc.buffer_level > drop_mark) && + (cpi->rc.decimation_factor > 0)) { + --cpi->rc.decimation_factor; + } else if (cpi->rc.buffer_level <= drop_mark && + cpi->rc.decimation_factor == 0) { + cpi->rc.decimation_factor = 1; + } + if (cpi->rc.decimation_factor > 0) { + if (cpi->rc.decimation_count > 0) { + --cpi->rc.decimation_count; + return 1; + } else { + cpi->rc.decimation_count = cpi->rc.decimation_factor; + return 0; + } + } else { + cpi->rc.decimation_count = 0; + return 0; + } + } + } +} + +// Adjust active_worst_quality level based on buffer level. +static int adjust_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 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 = cpi->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 = cpi->oxcf.optimal_buffer_level >> 2; + int adjustment = 0; + int buff_lvl_step = 0; + if (cpi->rc.buffer_level > cpi->oxcf.optimal_buffer_level) { + // Adjust down. + if (max_adjustment_down) { + buff_lvl_step = (int)((cpi->oxcf.maximum_buffer_size - + cpi->oxcf.optimal_buffer_level) / max_adjustment_down); + if (buff_lvl_step) { + adjustment = (int)((cpi->rc.buffer_level - + cpi->oxcf.optimal_buffer_level) / buff_lvl_step); + } + active_worst_quality -= adjustment; + } + } else if (cpi->rc.buffer_level > critical_level) { + // Adjust up from ambient Q. + if (critical_level) { + buff_lvl_step = (cpi->oxcf.optimal_buffer_level - critical_level); + if (buff_lvl_step) { + adjustment = + (cpi->rc.worst_quality - cpi->rc.avg_frame_qindex[INTER_FRAME]) * + (cpi->oxcf.optimal_buffer_level - cpi->rc.buffer_level) / + buff_lvl_step; + } + active_worst_quality = cpi->rc.avg_frame_qindex[INTER_FRAME] + adjustment; + } + } else { + // Set to worst_quality if buffer is below critical level. + active_worst_quality = cpi->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_COMP *cpi) { + int this_frame_target = cpi->rc.this_frame_target; + int percent_low = 0; + int percent_high = 0; + int one_percent_bits = (int)(1 + cpi->oxcf.optimal_buffer_level / 100); + if (cpi->rc.buffer_level < cpi->oxcf.optimal_buffer_level) { + percent_low = (int)((cpi->oxcf.optimal_buffer_level - cpi->rc.buffer_level) + / one_percent_bits); + if (percent_low > cpi->oxcf.under_shoot_pct) { + percent_low = cpi->oxcf.under_shoot_pct; + } else if (percent_low < 0) { + percent_low = 0; + } + // Lower the target bandwidth for this frame. + this_frame_target -= (this_frame_target * percent_low) / 200; + } else if (cpi->rc.buffer_level > cpi->oxcf.optimal_buffer_level) { + percent_high = (int)((cpi->rc.buffer_level - cpi->oxcf.optimal_buffer_level) + / one_percent_bits); + if (percent_high > cpi->oxcf.over_shoot_pct) { + percent_high = cpi->oxcf.over_shoot_pct; + } else if (percent_high < 0) { + percent_high = 0; + } + // Increase the target bandwidth for this frame. + this_frame_target += (this_frame_target * percent_high) / 200; + } + return this_frame_target; +} -static void calc_pframe_target_size(VP9_COMP *cpi) { - const int min_frame_target = MAX(cpi->rc.min_frame_bandwidth, - cpi->rc.av_per_frame_bandwidth >> 5); +static void calc_pframe_target_size(VP9_COMP *const cpi) { + int min_frame_target = MAX(cpi->rc.min_frame_bandwidth, + cpi->rc.av_per_frame_bandwidth >> 5); if (cpi->refresh_alt_ref_frame) { // Special alt reference frame case // Per frame bit target for the alt ref frame cpi->rc.per_frame_bandwidth = cpi->twopass.gf_bits; cpi->rc.this_frame_target = cpi->rc.per_frame_bandwidth; } else { - // Normal frames (gf,and inter) + // Normal frames (gf and inter). cpi->rc.this_frame_target = cpi->rc.per_frame_bandwidth; + // Set target frame size based on buffer level, for 1 pass CBR. + if (cpi->pass == 0 && cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) { + // Need to decide how low min_frame_target should be for 1-pass CBR. + // For now, use: cpi->rc.av_per_frame_bandwidth / 16: + min_frame_target = MAX(cpi->rc.av_per_frame_bandwidth >> 4, + FRAME_OVERHEAD_BITS); + cpi->rc.this_frame_target = target_size_from_buffer_level(cpi); + // Adjust qp-max based on buffer level. + cpi->rc.active_worst_quality = + adjust_active_worst_quality_from_buffer_level(cpi); + } } // Check that the total sum of adjustments is not above the maximum allowed. @@ -262,11 +423,13 @@ static void calc_pframe_target_size(VP9_COMP *cpi) { // not capable of recovering all the extra bits we have spent in the KF or GF, // then the remainder will have to be recovered over a longer time span via // other buffer / rate control mechanisms. - if (cpi->rc.this_frame_target < min_frame_target) + if (cpi->rc.this_frame_target < min_frame_target) { cpi->rc.this_frame_target = min_frame_target; + } // Adjust target frame size for Golden Frames: - if (cpi->rc.frames_till_gf_update_due == 0) { + if (cpi->rc.frames_till_gf_update_due == 0 && + !(cpi->pass == 0 && cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER)) { cpi->refresh_golden_frame = 1; calc_gf_params(cpi); // If we are using alternate ref instead of gf then do not apply the boost @@ -608,17 +771,8 @@ int vp9_rc_pick_q_and_adjust_q_bounds(const VP9_COMP *cpi, } else if ((cm->frame_type == KEY_FRAME) && cpi->rc.this_key_frame_forced) { q = cpi->rc.last_boosted_qindex; } else { - // Determine initial Q to try. - if (cpi->pass == 0) { - // 1-pass: for now, use per-frame-bw for target size of frame, scaled - // by |x| for key frame. - int scale = (cm->frame_type == KEY_FRAME) ? 5 : 1; - q = vp9_rc_regulate_q(cpi, scale * cpi->rc.av_per_frame_bandwidth, - active_best_quality, active_worst_quality); - } else { - q = vp9_rc_regulate_q(cpi, cpi->rc.this_frame_target, - active_best_quality, active_worst_quality); - } + q = vp9_rc_regulate_q(cpi, cpi->rc.this_frame_target, + active_best_quality, active_worst_quality); if (q > *top_index) q = *top_index; } @@ -741,17 +895,7 @@ void vp9_rc_postencode_update(VP9_COMP *cpi, uint64_t bytes_used) { cpi->rc.last_boosted_qindex = cm->base_qindex; } - // Update the buffer level variable. - // Non-viewable frames are a special case and are treated as pure overhead. - if (!cm->show_frame) - cpi->rc.bits_off_target -= cpi->rc.projected_frame_size; - else - cpi->rc.bits_off_target += cpi->rc.av_per_frame_bandwidth - - cpi->rc.projected_frame_size; - - // Clip the buffer level at the maximum buffer size - if (cpi->rc.bits_off_target > cpi->oxcf.maximum_buffer_size) - cpi->rc.bits_off_target = cpi->oxcf.maximum_buffer_size; + vp9_update_buffer_level(cpi, cpi->rc.projected_frame_size); // Rolling monitors of whether we are over or underspending used to help // regulate min and Max Q in two pass. @@ -777,8 +921,6 @@ void vp9_rc_postencode_update(VP9_COMP *cpi, uint64_t bytes_used) { cpi->rc.total_target_vs_actual += (cpi->rc.this_frame_target - cpi->rc.projected_frame_size); - cpi->rc.buffer_level = cpi->rc.bits_off_target; - #ifndef DISABLE_RC_LONG_TERM_MEM // Update bits left to the kf and gf groups to account for overshoot or // undershoot on these frames diff --git a/vp9/encoder/vp9_ratectrl.h b/vp9/encoder/vp9_ratectrl.h index 8113a056c..82935578c 100644 --- a/vp9/encoder/vp9_ratectrl.h +++ b/vp9/encoder/vp9_ratectrl.h @@ -61,4 +61,10 @@ int vp9_rc_bits_per_mb(FRAME_TYPE frame_type, int qindex, void vp9_twopass_postencode_update(VP9_COMP *cpi, uint64_t bytes_used); +// Decide if we should drop this frame: For 1-pass CBR. +int vp9_drop_frame(VP9_COMP *cpi); + +// Update the buffer level. +void vp9_update_buffer_level(VP9_COMP *cpi, int encoded_frame_size); + #endif // VP9_ENCODER_VP9_RATECTRL_H_ diff --git a/vp9/vp9_cx_iface.c b/vp9/vp9_cx_iface.c index 6bfca8d80..5d63dc95e 100644 --- a/vp9/vp9_cx_iface.c +++ b/vp9/vp9_cx_iface.c @@ -307,6 +307,8 @@ static vpx_codec_err_t set_vp9e_config(VP9_CONFIG *oxcf, oxcf->starting_buffer_level = cfg.rc_buf_initial_sz; oxcf->optimal_buffer_level = cfg.rc_buf_optimal_sz; + oxcf->drop_frames_water_mark = cfg.rc_dropframe_thresh; + oxcf->two_pass_vbrbias = cfg.rc_2pass_vbr_bias_pct; oxcf->two_pass_vbrmin_section = cfg.rc_2pass_vbr_minsection_pct; oxcf->two_pass_vbrmax_section = cfg.rc_2pass_vbr_maxsection_pct; -- 2.40.0