shithub: libvpx

Download patch

ref: 1b8b8b0d0df9ac6f0a50db73d20231ecb7529666
parent: d2f49407830de9dbc466f03fcdc90d379a3c0269
author: Marco Paniconi <marpan@google.com>
date: Tue Dec 17 10:45:30 EST 2013

Updates for 1-pass CBR rate control.

Adjustments based on buffer level, frame dropper.

Change-Id: Iaa85b570493526a60c4b9fb7ded4c0226b1b3a33

--- a/test/datarate_test.cc
+++ b/test/datarate_test.cc
@@ -193,7 +193,10 @@
 
   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 @@
   }
 
   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 @@
   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;
@@ -254,6 +282,49 @@
     ASSERT_LE(static_cast<double>(cfg_.rc_target_bitrate),
               effective_datarate_ * 1.15)
         << " The datarate for the file missed the target!";
+  }
+}
+
+// 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_;
   }
 }
 
--- a/vp9/common/vp9_onyx.h
+++ b/vp9/common/vp9_onyx.h
@@ -129,6 +129,9 @@
     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;
--- a/vp9/encoder/vp9_onyx_if.c
+++ b/vp9/encoder/vp9_onyx_if.c
@@ -2987,6 +2987,20 @@
     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);
@@ -3586,6 +3600,11 @@
 
   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
--- a/vp9/encoder/vp9_onyx_int.h
+++ b/vp9/encoder/vp9_onyx_int.h
@@ -331,6 +331,9 @@
   int buffer_level;
   int bits_off_target;
 
+  int decimation_factor;
+  int decimation_count;
+
   int rolling_target_bits;
   int rolling_actual_bits;
 
@@ -479,9 +482,6 @@
   int mbgraph_n_frames;             // number of frames filled in the above
   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;
--- a/vp9/encoder/vp9_ratectrl.c
+++ b/vp9/encoder/vp9_ratectrl.c
@@ -222,6 +222,31 @@
   // 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,10 +267,135 @@
   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;
+}
 
-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);
+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 *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
@@ -252,8 +402,19 @@
     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 @@
   // 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 @@
   } 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,18 +895,8 @@
     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;
+  vp9_update_buffer_level(cpi, 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;
-
   // Rolling monitors of whether we are over or underspending used to help
   // regulate min and Max Q in two pass.
   if (cm->frame_type != KEY_FRAME) {
@@ -776,8 +920,6 @@
   // Debug stats
   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
--- a/vp9/encoder/vp9_ratectrl.h
+++ b/vp9/encoder/vp9_ratectrl.h
@@ -61,4 +61,10 @@
 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_
--- a/vp9/vp9_cx_iface.c
+++ b/vp9/vp9_cx_iface.c
@@ -307,6 +307,8 @@
   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;