Reduce overshoot in 1 pass rate control
authorJohn Koleszar <jkoleszar@google.com>
Tue, 3 May 2011 15:17:05 +0000 (11:17 -0400)
committerJohn Koleszar <jkoleszar@google.com>
Fri, 3 Jun 2011 20:38:11 +0000 (16:38 -0400)
This patch attempts to reduce the peak bitrate hit by the encoder
when using small buffer windows.

Tested on the CIF set over 200-500kbps using these settings:

  --buf-sz=500 --buf-initial-sz=250 --buf-optimal-sz=250 \
  --undershoot-pct=100

Two pass encodes were tested at best quality. One pass encodes were
tested only at realtime speed 4:

  --rt --cpu-used=-4

The peak datarate (over the specified 500ms window) was measured
for each encode, and averaged together to get metric for
"average peak," computed as SUM(peak)/SUM(target). This patch
reduces the average peak datarate as follows:

  One pass:
    baseline:   1.29715
    this patch: 1.23664

  Two pass:
    baseline:   1.32702
    this patch: 1.37824

This change had a positive effect on our quality metrics as well:

  One pass CBR:
                    Min  / Mean / Max (pct)
    Average PSNR    -0.42 / 2.86 / 27.32
    Overall PSNR    -0.90 / 2.00 / 17.27
    SSIM            -0.05 / 3.95 / 37.46

  Two pass CBR:
                    Min  / Mean / Max (pct)
    Average PSNR    -4.47 / 4.35 / 35.99
    Overall PSNR    -3.40 / 4.18 / 36.46
    SSIM            -4.56 / 6.98 / 53.67

  One pass VBR:
                    Min  / Mean / Max (pct)
    Average PSNR    -5.21 /  0.01 / 3.30
    Overall PSNR    -8.10 / -0.38 / 1.21
    SSIM            -7.38 / -0.11 / 3.17
    (note: most values here were close to the mean, there were a few
     outliers on files that were very sensitive to golden frame size)

  Two pass VBR:
                    Min  / Mean / Max (pct)
    Average PSNR    0.00 / 0.00 / 0.00
    Overall PSNR    0.00 / 0.00 / 0.00
    SSIM            0.00 / 0.00 / 0.00

Neither one pass or two pass CBR mode adheres particularly strictly
to the short term buffer constraints, and two pass is less
consistent, even in the baseline commit. This should be addressed
in a later commit. This likely will hurt the quality numbers, as it
will have to reduce the burstiness of golden frames.

Aside: My work on this commit makes it clear that we need to make
rate control modes "pluggable", where you can easily write a new
one or work on one in isolation.

Change-Id: I1ea9a48f2beedd59891f1288aabf7064956b4716

vp8/encoder/firstpass.c
vp8/encoder/onyx_if.c
vp8/encoder/onyx_int.h
vp8/encoder/ratectrl.c

index 7cf5041..b563968 100644 (file)
@@ -243,33 +243,58 @@ static int frame_max_bits(VP8_COMP *cpi)
     int max_bits;
 
     // For CBR we need to also consider buffer fullness.
-    // If we are running below the optimal level then we need to gradually tighten up on max_bits.
     if (cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER)
     {
-        double buffer_fullness_ratio = (double)cpi->buffer_level / DOUBLE_DIVIDE_CHECK((double)cpi->oxcf.optimal_buffer_level);
+        max_bits = 2 * cpi->av_per_frame_bandwidth;
+        max_bits -= cpi->buffered_av_per_frame_bandwidth;
+        max_bits *= ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0);
+    }
+    // VBR
+    else
+    {
+        // For VBR base this on the bits and frames left plus the two_pass_vbrmax_section rate passed in by the user
+        max_bits = (int)(((double)cpi->twopass.bits_left / (cpi->twopass.total_stats->count - (double)cpi->common.current_video_frame)) * ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0));
+    }
+
+    // Trap case where we are out of bits
+    if (max_bits < 0)
+        max_bits = 0;
 
-        // For CBR base this on the target average bits per frame plus the maximum sedction rate passed in by the user
-        max_bits = (int)(cpi->av_per_frame_bandwidth * ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0));
+    return max_bits;
+}
 
-        // If our buffer is below the optimum level
-        if (buffer_fullness_ratio < 1.0)
-        {
-            // The lower of max_bits / 4 or cpi->av_per_frame_bandwidth / 4.
-            int min_max_bits = ((cpi->av_per_frame_bandwidth >> 2) < (max_bits >> 2)) ? cpi->av_per_frame_bandwidth >> 2 : max_bits >> 2;
 
-            max_bits = (int)(max_bits * buffer_fullness_ratio);
+static int gf_group_max_bits(VP8_COMP *cpi)
+{
+    // Max allocation for a golden frame group
+    int max_bits;
 
-            if (max_bits < min_max_bits)
-                max_bits = min_max_bits;       // Lowest value we will set ... which should allow the buffer to refil.
+    // For CBR we need to also consider buffer fullness.
+    if (cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER)
+    {
+        max_bits = cpi->av_per_frame_bandwidth * cpi->baseline_gf_interval;
+        if (max_bits > cpi->oxcf.optimal_buffer_level)
+        {
+            max_bits -= cpi->oxcf.optimal_buffer_level;
+            max_bits += cpi->buffer_level;
         }
+        else
+        {
+            max_bits -= (cpi->buffered_av_per_frame_bandwidth
+                         - cpi->av_per_frame_bandwidth)
+                        * cpi->baseline_gf_interval;
+        }
+
+        max_bits *= ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0);
     }
-    // VBR
     else
     {
         // For VBR base this on the bits and frames left plus the two_pass_vbrmax_section rate passed in by the user
         max_bits = (int)(((double)cpi->twopass.bits_left / (cpi->twopass.total_stats->count - (double)cpi->common.current_video_frame)) * ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0));
+        max_bits *=  cpi->baseline_gf_interval;
     }
 
+
     // Trap case where we are out of bits
     if (max_bits < 0)
         max_bits = 0;
@@ -1357,7 +1382,7 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
     double abs_mv_in_out_accumulator = 0.0;
     double mod_err_per_mb_accumulator = 0.0;
 
-    int max_bits = frame_max_bits(cpi);     // Max for a single frame
+    int max_group_bits;
 
     unsigned int allow_alt_ref =
                     cpi->oxcf.play_alternate && cpi->oxcf.lag_in_frames;
@@ -1711,8 +1736,9 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
     cpi->twopass.gf_group_bits = (cpi->twopass.gf_group_bits < 0) ? 0 : (cpi->twopass.gf_group_bits > cpi->twopass.kf_group_bits) ? cpi->twopass.kf_group_bits : cpi->twopass.gf_group_bits;
 
     // Clip cpi->twopass.gf_group_bits based on user supplied data rate variability limit (cpi->oxcf.two_pass_vbrmax_section)
-    if (cpi->twopass.gf_group_bits > max_bits * cpi->baseline_gf_interval)
-        cpi->twopass.gf_group_bits = max_bits * cpi->baseline_gf_interval;
+    max_group_bits = gf_group_max_bits(cpi);
+    if (cpi->twopass.gf_group_bits > max_group_bits)
+        cpi->twopass.gf_group_bits = max_group_bits;
 
     // Reset the file position
     reset_fpf_position(cpi, start_pos);
@@ -1806,13 +1832,6 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
             }
         }
 
-        // Apply an additional limit for CBR
-        if (cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER)
-        {
-            if (cpi->twopass.gf_bits > (cpi->buffer_level >> 1))
-                cpi->twopass.gf_bits = cpi->buffer_level >> 1;
-        }
-
         // Dont allow a negative value for gf_bits
         if (cpi->twopass.gf_bits < 0)
             cpi->twopass.gf_bits = 0;
index 15d9289..023384e 100644 (file)
@@ -1473,6 +1473,7 @@ static void init_config(VP8_PTR ptr, VP8_CONFIG *oxcf)
     cpi->rolling_actual_bits          = cpi->av_per_frame_bandwidth;
     cpi->long_rolling_target_bits     = cpi->av_per_frame_bandwidth;
     cpi->long_rolling_actual_bits     = cpi->av_per_frame_bandwidth;
+    cpi->buffered_av_per_frame_bandwidth = cpi->av_per_frame_bandwidth;
 
     cpi->total_actual_bits            = 0;
     cpi->total_target_vs_actual       = 0;
@@ -1568,7 +1569,7 @@ void vp8_change_config(VP8_PTR ptr, VP8_CONFIG *oxcf)
         break;
     }
 
-    if (cpi->pass == 0)
+    if (cpi->pass == 0 && cpi->oxcf.end_usage != USAGE_STREAM_FROM_SERVER)
         cpi->auto_worst_q = 1;
 
     cpi->oxcf.worst_allowed_q = q_trans[oxcf->worst_allowed_q];
@@ -3411,7 +3412,8 @@ static void encode_frame_to_data_rate
     // For CBR if the buffer reaches its maximum level then we can no longer
     // save up bits for later frames so we might as well use them up
     // on the current frame.
-    if ((cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) &&
+    if (cpi->pass == 2
+        && (cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) &&
         (cpi->buffer_level >= cpi->oxcf.optimal_buffer_level) && cpi->buffered_mode)
     {
         int Adjustment = cpi->active_worst_quality / 4;       // Max adjustment is 1/4
@@ -3502,6 +3504,9 @@ static void encode_frame_to_data_rate
         }
         else
         {
+            if(cpi->pass != 2)
+                Q = cpi->avg_frame_qindex;
+
             cpi->active_best_quality = inter_minq[Q];
 
             // For the constant/constrained quality mode we dont want
@@ -3803,15 +3808,16 @@ static void encode_frame_to_data_rate
             (cpi->active_worst_quality < cpi->worst_quality)      &&
             (cpi->projected_frame_size > frame_over_shoot_limit))
         {
-            int over_size_percent = ((cpi->projected_frame_size - frame_over_shoot_limit) * 100) / frame_over_shoot_limit;
+            /* step down active_worst_quality such that the corresponding
+             * active_best_quality will be equal to the current
+             * active_worst_quality + 1
+             */
+            int i;
 
-            // If so is there any scope for relaxing it
-            while ((cpi->active_worst_quality < cpi->worst_quality) && (over_size_percent > 0))
-            {
-                cpi->active_worst_quality++;
-                top_index = cpi->active_worst_quality;
-                over_size_percent = (int)(over_size_percent * 0.96);        // Assume 1 qstep = about 4% on frame size.
-            }
+            for(i=cpi->active_worst_quality; i<cpi->worst_quality; i++)
+                if(inter_minq[i] >= cpi->active_worst_quality + 1)
+                    break;
+            cpi->active_worst_quality = i;
 
             // If we have updated the active max Q do not call vp8_update_rate_correction_factors() this loop.
             active_worst_qchanged = TRUE;
@@ -4203,10 +4209,9 @@ static void encode_frame_to_data_rate
 
     // Update the buffer level variable.
     // Non-viewable frames are a special case and are treated as pure overhead.
-    if ( !cm->show_frame )
-        cpi->bits_off_target -= cpi->projected_frame_size;
-    else
-        cpi->bits_off_target += cpi->av_per_frame_bandwidth - cpi->projected_frame_size;
+    if ( cm->show_frame )
+        cpi->bits_off_target += cpi->av_per_frame_bandwidth;
+    cpi->bits_off_target -= cpi->projected_frame_size;
 
     // Rolling monitors of whether we are over or underspending used to help regulate min and Max Q in two pass.
     cpi->rolling_target_bits = ((cpi->rolling_target_bits * 3) + cpi->this_frame_target + 2) / 4;
@@ -4220,7 +4225,33 @@ static void encode_frame_to_data_rate
     // Debug stats
     cpi->total_target_vs_actual += (cpi->this_frame_target - cpi->projected_frame_size);
 
-    cpi->buffer_level = cpi->bits_off_target;
+    // Update the buffered average bitrate
+    {
+        long long numerator;
+
+        numerator = cpi->oxcf.maximum_buffer_size
+                    - cpi->buffered_av_per_frame_bandwidth
+                    + cpi->projected_frame_size;
+        numerator *= cpi->buffered_av_per_frame_bandwidth;
+        cpi->buffered_av_per_frame_bandwidth = numerator
+                                               / cpi->oxcf.maximum_buffer_size;
+    }
+
+    {
+        long long tmp = (long long)cpi->buffered_av_per_frame_bandwidth
+                        * cpi->oxcf.maximum_buffer_size
+                        / cpi->av_per_frame_bandwidth;
+        cpi->buffer_level = cpi->oxcf.maximum_buffer_size
+                            - tmp
+                            + cpi->oxcf.optimal_buffer_level;
+    }
+
+    // Accumulate overshoot error.
+    cpi->accumulated_overshoot +=
+        (cpi->projected_frame_size > cpi->av_per_frame_bandwidth)
+        ? cpi->projected_frame_size - cpi->av_per_frame_bandwidth
+        : 0;
+
 
     // Update bits left to the kf and gf groups to account for overshoot or undershoot on these frames
     if (cm->frame_type == KEY_FRAME)
index 6637860..84ddeeb 100644 (file)
@@ -351,6 +351,10 @@ typedef struct VP8_COMP
     int per_frame_bandwidth;          // Current section per frame bandwidth target
     int av_per_frame_bandwidth;        // Average frame size target for clip
     int min_frame_bandwidth;          // Minimum allocation that should be used for any frame
+    int buffered_av_per_frame_bandwidth; // Average bitrate over the last buffer
+    int buffered_av_per_frame_bandwidth_rem; // Average bitrate remainder
+    int accumulated_overshoot;           // Accumulated # of bits spent > target
+
     int inter_frame_target;
     double output_frame_rate;
     long long last_time_stamp_seen;
index 54c394d..73e1437 100644 (file)
@@ -605,10 +605,10 @@ static void calc_gf_params(VP8_COMP *cpi)
 
 static void calc_pframe_target_size(VP8_COMP *cpi)
 {
-    int min_frame_target;
+    int min_frame_target, max_frame_target;
     int Adjustment;
 
-    min_frame_target = 0;
+    min_frame_target = 1;
 
     if (cpi->pass == 2)
     {
@@ -616,10 +616,19 @@ static void calc_pframe_target_size(VP8_COMP *cpi)
 
         if (min_frame_target < (cpi->av_per_frame_bandwidth >> 5))
             min_frame_target = cpi->av_per_frame_bandwidth >> 5;
+
+        max_frame_target = INT_MAX;
     }
-    else if (min_frame_target < cpi->per_frame_bandwidth / 4)
-        min_frame_target = cpi->per_frame_bandwidth / 4;
+    else
+    {
+        if (min_frame_target < cpi->per_frame_bandwidth / 4)
+            min_frame_target = cpi->per_frame_bandwidth / 4;
 
+        /* Don't allow the target to completely deplete the buffer. */
+        max_frame_target = cpi->buffer_level + cpi->av_per_frame_bandwidth;
+        if(max_frame_target < min_frame_target)
+            max_frame_target = min_frame_target;
+    }
 
     // Special alt reference frame case
     if (cpi->common.refresh_alt_ref_frame)
@@ -1112,6 +1121,32 @@ static void calc_pframe_target_size(VP8_COMP *cpi)
 
         }
     }
+
+    if (cpi->pass==0 && cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER){
+        /* determine the accumulated error to apply to this frame. Apply
+         * more of the error when we've been undershooting, less when
+         * we've been overshooting
+         */
+        long long adjust;
+        int bitrate_error;
+
+        bitrate_error = cpi->av_per_frame_bandwidth
+                        - cpi->buffered_av_per_frame_bandwidth;
+
+        adjust = cpi->accumulated_overshoot;
+        adjust *= cpi->av_per_frame_bandwidth + bitrate_error;
+        adjust /= cpi->oxcf.maximum_buffer_size;
+        if (adjust > (cpi->this_frame_target - min_frame_target))
+            adjust = (cpi->this_frame_target - min_frame_target);
+        else if (adjust < 0)
+            adjust = 0;
+
+        cpi->this_frame_target -= adjust;
+        cpi->accumulated_overshoot -= adjust;
+    }
+
+    if(cpi->this_frame_target > max_frame_target)
+        cpi->this_frame_target = max_frame_target;
 }