Commit 212f6183 authored by John Koleszar's avatar John Koleszar

Reduce overshoot in 1 pass rate control

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
parent fbea3728
......@@ -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;
......
......@@ -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)
......
......@@ -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;
......
......@@ -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;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment