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;
}
}
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_;
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;
}
}
+// 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),
// 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;
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.
// 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
} 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;
}
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.
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