Improved 1-pass CBR rate control
This patch attempts to improve the handling of CBR streams with respect to the short term buffering requirements. The "buffer level" is changed to be an average over the rc buffer, rather than a long running average. Overshoot is also tracked over the same interval and the golden frame targets suppressed accordingly to correct for overly aggressive boosting. Testing shows that this is fairly consistently positive in one metric or another -- some clips that show significant decreases in quality have better buffering characteristics, others show improvenents in both. Change-Id: I924c89aa9bdb210271f2e03311e63de3f1f8f920
This commit is contained in:
@@ -1455,6 +1455,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;
|
||||
@@ -1550,7 +1551,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];
|
||||
@@ -3187,6 +3188,116 @@ void loopfilter_frame(VP8_COMP *cpi, VP8_COMMON *cm)
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void update_buffer_level(VP8_COMP *cpi)
|
||||
{
|
||||
long long tmp;
|
||||
|
||||
/* Update the buffered average bitrate.
|
||||
*
|
||||
* The buffered average bitrate tracks the bitrate over the buffer
|
||||
* window. Here we simulate taking a frame of average size out
|
||||
* of the buffer, and putting in the new frame just encoded.
|
||||
* It is calculated accordingly:
|
||||
*
|
||||
* A = Average Bits Per Frame In The Buffer
|
||||
* P = New Frame Size
|
||||
* N = Number of bits in the buffer
|
||||
*
|
||||
* We recalculate the average as so:
|
||||
* (N-A)*A + A*P A * (N - A + P)
|
||||
* A' = ------------- = ---------------
|
||||
* N N
|
||||
*
|
||||
* This is modeled after a the standard algorithm for a moving
|
||||
* average with fixed weighting (eg A' = ((N-1)*A + 1*P) / N). This makes
|
||||
* the step response nonlinear but consistent with expected behavior --
|
||||
* when A is large, the model adapts more quickly, since there are
|
||||
* fewer frames in the buffer and conversely when A is small there
|
||||
* will be more frames in the buffer so the average will adapt
|
||||
* slowly.
|
||||
*
|
||||
* TODO(jkoleszar): This may give poor step response in some situations,
|
||||
* for example motion following a long static section. It might be
|
||||
* worth experimenting more with weighting by av_per_frame_bandwidth
|
||||
* rather than buffered_av_per_frame_bandwidth or using a more accurate
|
||||
* algorithm to get faster response. Current testing showed worse results
|
||||
* with that setting though.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Guard against buffered_av_per_frame_bandwidth falling to 0. Should
|
||||
* never happen, but without this check, it would be irrecoverable.
|
||||
*/
|
||||
if(cpi->buffered_av_per_frame_bandwidth == 0)
|
||||
cpi->buffered_av_per_frame_bandwidth = 1;
|
||||
|
||||
tmp = cpi->oxcf.maximum_buffer_size
|
||||
- cpi->buffered_av_per_frame_bandwidth
|
||||
+ cpi->projected_frame_size;
|
||||
tmp *= cpi->buffered_av_per_frame_bandwidth;
|
||||
cpi->buffered_av_per_frame_bandwidth = tmp
|
||||
/ cpi->oxcf.maximum_buffer_size;
|
||||
|
||||
if(cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER)
|
||||
{
|
||||
/* In CBR mode, buffer level is synthesized from the buffered
|
||||
* average per-frame bandwidth to get the response characteristics
|
||||
* of that model, rather than using the unbounded (wrt buffer size)
|
||||
* bits_off_target. ie, the long term average bitrate doesn't
|
||||
* matter in CBR mode. If the clip is consistently undershooting
|
||||
* because it is very static, for example, you don't want to blow
|
||||
* your short term bitrate budget trying to the the long term spend
|
||||
* up to the target when you hit a motion section.
|
||||
*
|
||||
* Instead, the ratio of buffered_av_per_frame_bandwidth to the
|
||||
* target av_per_frame_bandwidth is taken, scaled by
|
||||
* maximum_buffer_size and centered around optimal_buffer_level,
|
||||
* which presents the expected behavior of buffer_level for the other
|
||||
* parts of the rate control code which handle the targeting.
|
||||
*
|
||||
* Note that this only happens after the starting_buffer_level
|
||||
* has passed, to give the model a chance to stabilize.
|
||||
*/
|
||||
if(cpi->total_actual_bits > cpi->oxcf.starting_buffer_level)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
cpi->buffer_level = cpi->oxcf.optimal_buffer_level;
|
||||
|
||||
/* Accumulate recent overshoot error.
|
||||
*
|
||||
* If this frame is larger than the target, then accumulate
|
||||
* that error to apply as a damping factor later. Only care about
|
||||
* recent overshoot, so this value decays by (N-P)/N
|
||||
*/
|
||||
if(cpi->total_actual_bits > cpi->oxcf.starting_buffer_level)
|
||||
{
|
||||
long long decayed_overshoot;
|
||||
|
||||
decayed_overshoot = cpi->accumulated_overshoot;
|
||||
decayed_overshoot *= (cpi->oxcf.maximum_buffer_size
|
||||
- cpi->projected_frame_size);
|
||||
decayed_overshoot /= cpi->oxcf.maximum_buffer_size;
|
||||
cpi->accumulated_overshoot = decayed_overshoot;
|
||||
|
||||
cpi->accumulated_overshoot +=
|
||||
(cpi->projected_frame_size > cpi->av_per_frame_bandwidth)
|
||||
? cpi->projected_frame_size - cpi->av_per_frame_bandwidth
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
cpi->buffer_level = cpi->bits_off_target;
|
||||
}
|
||||
|
||||
|
||||
static void encode_frame_to_data_rate
|
||||
(
|
||||
VP8_COMP *cpi,
|
||||
@@ -3432,7 +3543,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
|
||||
@@ -3523,6 +3635,10 @@ static void encode_frame_to_data_rate
|
||||
}
|
||||
else
|
||||
{
|
||||
if(cpi->pass != 2)
|
||||
Q = cpi->auto_worst_q?
|
||||
cpi->active_worst_quality:cpi->avg_frame_qindex;
|
||||
|
||||
cpi->active_best_quality = inter_minq[Q];
|
||||
|
||||
// For the constant/constrained quality mode we dont want
|
||||
@@ -3824,15 +3940,17 @@ 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. Once the limit on active_best_quality
|
||||
* is reached, active_worst_quality will equal worst_quality.
|
||||
*/
|
||||
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;
|
||||
@@ -4220,10 +4338,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;
|
||||
@@ -4237,7 +4354,7 @@ 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_buffer_level(cpi);
|
||||
|
||||
// Update bits left to the kf and gf groups to account for overshoot or undershoot on these frames
|
||||
if (cm->frame_type == KEY_FRAME)
|
||||
|
||||
Reference in New Issue
Block a user