Updates t resolution adaptation (cama):

-set image type when QM is reset.
  -fix for undoing two stages of spatial downsampling.
  -some adjustments and code clean-up.
  -updates to control parameters and unittest.
Review URL: https://webrtc-codereview.appspot.com/641010

git-svn-id: http://webrtc.googlecode.com/svn/trunk@2473 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
marpan@webrtc.org 2012-06-29 21:44:55 +00:00
parent ea5b8b5903
commit c5b392e9d6
4 changed files with 409 additions and 317 deletions

View File

@ -40,7 +40,6 @@ VCMQmMethod::~VCMQmMethod() {
void VCMQmMethod::ResetQM() {
aspect_ratio_ = 1.0f;
image_type_ = kVGA;
motion_.Reset();
spatial_.Reset();
content_class_ = 0;
@ -137,11 +136,11 @@ ImageType VCMQmMethod::FindClosestImageType(uint16_t width, uint16_t height) {
}
FrameRateLevelClass VCMQmMethod::FrameRateLevel(float avg_framerate) {
if (avg_framerate < kLowFrameRate) {
if (avg_framerate <= kLowFrameRate) {
return kFrameRateLow;
} else if (avg_framerate < kMiddleFrameRate) {
} else if (avg_framerate <= kMiddleFrameRate) {
return kFrameRateMiddle1;
} else if (avg_framerate < kHighFrameRate) {
} else if (avg_framerate <= kHighFrameRate) {
return kFrameRateMiddle2;
} else {
return kFrameRateHigh;
@ -165,7 +164,7 @@ void VCMQmResolution::ResetRates() {
sum_rate_MM_ = 0.0f;
sum_rate_MM_sgn_ = 0.0f;
sum_packet_loss_ = 0.0f;
buffer_level_ = kOptBufferLevel * target_bitrate_;
buffer_level_ = kInitBufferLevel * target_bitrate_;
frame_cnt_ = 0;
frame_cnt_delta_ = 0;
low_buffer_cnt_ = 0;
@ -185,7 +184,7 @@ void VCMQmResolution::Reset() {
target_bitrate_ = 0.0f;
incoming_framerate_ = 0.0f;
buffer_level_ = 0.0f;
per_frame_bandwidth_ =0.0f;
per_frame_bandwidth_ = 0.0f;
avg_target_rate_ = 0.0f;
avg_incoming_framerate_ = 0.0f;
avg_ratio_buffer_low_ = 0.0f;
@ -222,7 +221,7 @@ int VCMQmResolution::Initialize(float bitrate,
native_frame_rate_ = user_framerate;
num_layers_ = num_layers;
// Initial buffer level.
buffer_level_ = kOptBufferLevel * target_bitrate_;
buffer_level_ = kInitBufferLevel * target_bitrate_;
// Per-frame bandwidth.
per_frame_bandwidth_ = target_bitrate_ / user_framerate;
init_ = true;
@ -247,12 +246,13 @@ void VCMQmResolution::UpdateEncodedSize(int encoded_size,
// Update the buffer level:
// Note this is not the actual encoder buffer level.
// |buffer_level_| is reset to average value every time SelectResolution is
// |buffer_level_| is reset to an initial value after SelectResolution is
// called, and does not account for frame dropping by encoder or VCM.
buffer_level_ += per_frame_bandwidth_ - encoded_size_kbits;
// Counter for occurrences of low buffer level:
// low/negative values means encoder is likely dropping frames.
if (buffer_level_ <= kPercBufferThr * kOptBufferLevel * target_bitrate_) {
if (buffer_level_ <= kPercBufferThr * kInitBufferLevel * target_bitrate_) {
low_buffer_cnt_++;
}
}
@ -282,13 +282,12 @@ void VCMQmResolution::UpdateRates(float target_bitrate,
sum_rate_MM_sgn_ += sgnDiff;
// Update with the current new target and frame rate:
// these values are ones the encoder will use for the current/next ~1sec
// these values are ones the encoder will use for the current/next ~1sec.
target_bitrate_ = target_bitrate;
incoming_framerate_ = incoming_framerate;
sum_incoming_framerate_ += incoming_framerate_;
// Update the per_frame_bandwidth:
// this is the per_frame_bw for the current/next ~1sec
// this is the per_frame_bw for the current/next ~1sec.
per_frame_bandwidth_ = 0.0f;
if (incoming_framerate_ > 0.0f) {
per_frame_bandwidth_ = target_bitrate_ / incoming_framerate_;
@ -318,9 +317,16 @@ int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) {
return VCM_OK;
}
// Check conditions on down-sampling state.
assert(state_dec_factor_spatial_ >= 1.0f);
assert(state_dec_factor_temporal_ >= 1.0f);
assert(state_dec_factor_spatial_ <= kMaxSpatialDown);
assert(state_dec_factor_temporal_ <= kMaxTempDown);
assert(state_dec_factor_temporal_ * state_dec_factor_spatial_ <=
kMaxTotalDown);
// Compute content class for selection.
content_class_ = ComputeContentClass();
// Compute various rate quantities for selection.
ComputeRatesForSelection();
@ -341,13 +347,10 @@ int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) {
}
}
// Check for going down in resolution, only if current total amount of
// down-sampling state is below threshold.
if (state_dec_factor_temporal_ * state_dec_factor_spatial_ < kMaxDownSample) {
if (GoingDownResolution()) {
*qm = qm_;
return VCM_OK;
}
// Check for going down in resolution.
if (GoingDownResolution()) {
*qm = qm_;
return VCM_OK;
}
return VCM_OK;
}
@ -423,9 +426,19 @@ void VCMQmResolution::ComputeEncoderState() {
bool VCMQmResolution::GoingUpResolution() {
// For going up, we check for undoing the previous down-sampling action.
float fac_width = kFactorWidthSpatial[down_action_history_[0].spatial];
float fac_height = kFactorHeightSpatial[down_action_history_[0].spatial];
float fac_temp = kFactorTemporal[down_action_history_[0].temporal];
// For going up spatially, we allow for going up by 3/4x3/4 at each stage.
// So if the last spatial action was 1/2x1/2 it would be undone in 2 stages.
// Modify the fac_width/height for this case.
if (down_action_history_[0].spatial == kOneQuarterSpatialUniform) {
fac_width = kFactorWidthSpatial[kOneQuarterSpatialUniform] /
kFactorWidthSpatial[kOneHalfSpatialUniform];
fac_height = kFactorHeightSpatial[kOneQuarterSpatialUniform] /
kFactorHeightSpatial[kOneHalfSpatialUniform];
}
// Check if we should go up both spatially and temporally.
if (down_action_history_[0].spatial != kNoChangeSpatial &&
@ -473,7 +486,6 @@ bool VCMQmResolution::ConditionForGoingUp(float fac_width,
float scale_fac) {
float estimated_transition_rate_up = GetTransitionRate(fac_width, fac_height,
fac_temp, scale_fac);
// Go back up if:
// 1) target rate is above threshold and current encoder state is stable, or
// 2) encoder state is easy (encoder is significantly under-shooting target).
@ -490,7 +502,6 @@ bool VCMQmResolution::GoingDownResolution() {
float estimated_transition_rate_down =
GetTransitionRate(1.0f, 1.0f, 1.0f, 1.0f);
float max_rate = kFrameRateFac[framerate_level_] * kMaxRateQm[image_type_];
// Resolution reduction if:
// (1) target rate is below transition rate, or
// (2) encoder is in stressed state and target rate below a max threshold.
@ -539,18 +550,14 @@ bool VCMQmResolution::GoingDownResolution() {
assert(false);
}
}
// Only allow for one action (spatial or temporal) at a given time.
assert(action_.temporal == kNoChangeTemporal ||
action_.spatial == kNoChangeSpatial);
// Adjust cases not captured in tables, mainly based on frame rate.
// Adjust cases not captured in tables, mainly based on frame rate, and
// also check for odd frame sizes.
AdjustAction();
ConvertSpatialFractionalToWhole();
CheckForEvenFrameSize();
// Update down-sampling state.
if (action_.spatial != kNoChangeSpatial ||
action_.temporal != kNoChangeTemporal) {
@ -587,7 +594,6 @@ float VCMQmResolution::GetTransitionRate(float fac_width,
// Scale factor for down-sampling transition threshold:
// factor based on the content class and the image size.
float scaleTransRate = kScaleTransRateQm[table_index];
// Threshold bitrate for resolution action.
return static_cast<float> (scale_fac * scaleTransRate * max_rate);
}
@ -596,13 +602,24 @@ void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) {
if (up_down == kUpResolution) {
qm_->spatial_width_fact = 1.0f / kFactorWidthSpatial[action_.spatial];
qm_->spatial_height_fact = 1.0f / kFactorHeightSpatial[action_.spatial];
// If last spatial action was 1/2x1/2, we undo it in two steps, so the
// spatial scale factor in this first step is modified as (4.0/3.0 / 2.0).
if (action_.spatial == kOneQuarterSpatialUniform) {
qm_->spatial_width_fact =
1.0f * kFactorWidthSpatial[kOneHalfSpatialUniform] /
kFactorWidthSpatial[kOneQuarterSpatialUniform];
qm_->spatial_height_fact =
1.0f * kFactorHeightSpatial[kOneHalfSpatialUniform] /
kFactorHeightSpatial[kOneQuarterSpatialUniform];
}
qm_->temporal_fact = 1.0f / kFactorTemporal[action_.temporal];
RemoveLastDownAction();
} else if (up_down == kDownResolution) {
ConstrainAmountOfDownSampling();
ConvertSpatialFractionalToWhole();
qm_->spatial_width_fact = kFactorWidthSpatial[action_.spatial];
qm_->spatial_height_fact = kFactorHeightSpatial[action_.spatial];
qm_->temporal_fact = kFactorTemporal[action_.temporal];
ConstrainAmountOfDownSampling();
InsertLatestDownAction();
} else {
// This function should only be called if either the Up or Down action
@ -613,10 +630,6 @@ void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) {
state_dec_factor_spatial_ = state_dec_factor_spatial_ *
qm_->spatial_width_fact * qm_->spatial_height_fact;
state_dec_factor_temporal_ = state_dec_factor_temporal_ * qm_->temporal_fact;
assert(state_dec_factor_spatial_ >= 1.0f);
assert(state_dec_factor_spatial_ <= kMaxSpatialDown);
assert(state_dec_factor_temporal_ >= 1.0f);
assert(state_dec_factor_temporal_ <= kMaxTempDown);
}
void VCMQmResolution::UpdateCodecResolution() {
@ -626,10 +639,11 @@ void VCMQmResolution::UpdateCodecResolution() {
qm_->spatial_width_fact + 0.5f);
qm_->codec_height = static_cast<uint16_t>(height_ /
qm_->spatial_height_fact + 0.5f);
// Size can never exceed native sizes.
// Size should not exceed native sizes.
assert(qm_->codec_width <= native_width_);
assert(qm_->codec_height <= native_height_);
// Size should be multiple of 2.
// New sizes should be multiple of 2, otherwise spatial should not have
// been selected.
assert(qm_->codec_width % 2 == 0);
assert(qm_->codec_height % 2 == 0);
}
@ -652,34 +666,36 @@ uint8_t VCMQmResolution::RateClass(float transition_rate) {
(avg_target_rate_ >= transition_rate ? 2 : 1);
}
// TODO(marpan): Would be better to capture these frame rate adjustments by
// extending the table data (qm_select_data.h).
void VCMQmResolution::AdjustAction() {
// If the spatial level is default state (neither low or high) and motion
// is not high, then safer to take frame rate reduction if the
// average incoming frame rate is high.
// If the spatial level is default state (neither low or high), motion level
// is not high, and spatial action was selected, switch to 2/3 frame rate
// reduction if the average incoming frame rate is high.
if (spatial_.level == kDefault && motion_.level != kHigh &&
action_.spatial != kNoChangeSpatial &&
framerate_level_ == kFrameRateHigh) {
action_.spatial = kNoChangeSpatial;
action_.temporal = kOneHalfTemporal;
action_.temporal = kTwoThirdsTemporal;
}
// If both motion and spatial level are low, and temporal down action
// was selected, switch to spatial 3/4x3/4 if the frame rate is low.
// If both motion and spatial level are low, and temporal down action was
// selected, switch to spatial 3/4x3/4 if the frame rate is not above the
// lower middle level (|kFrameRateMiddle1|).
if (motion_.level == kLow && spatial_.level == kLow &&
framerate_level_ == kFrameRateLow &&
framerate_level_ <= kFrameRateMiddle1 &&
action_.temporal != kNoChangeTemporal) {
action_.spatial = kOneHalfSpatialUniform;
action_.temporal = kNoChangeTemporal;
}
// If too much spatial action, and temporal action has not yet been chosen,
// then change to temporal action if the average frame rate is not low.
if (action_.spatial == kOneQuarterSpatialUniform &&
// If spatial action is selected, and there has been too much spatial
// reduction already (i.e., 1/4), then switch to temporal action if the
// average frame rate is not low.
if (action_.spatial != kNoChangeSpatial &&
down_action_history_[0].spatial == kOneQuarterSpatialUniform &&
down_action_history_[0].temporal == kNoChangeTemporal &&
framerate_level_ != kFrameRateLow) {
action_.spatial = kNoChangeSpatial;
action_.temporal = kOneHalfTemporal;
action_.temporal = kTwoThirdsTemporal;
}
// Never use temporal action if number of temporal layers is above 2.
if (num_layers_ > 2) {
if (action_.temporal != kNoChangeTemporal) {
@ -687,6 +703,15 @@ void VCMQmResolution::AdjustAction() {
}
action_.temporal = kNoChangeTemporal;
}
// If spatial action was selected, we need to make sure the frame sizes
// are multiples of two. Otherwise switch to 2/3 temporal.
if (action_.spatial != kNoChangeSpatial &&
!EvenFrameSize()) {
action_.spatial = kNoChangeSpatial;
// Only one action (spatial or temporal) is allowed at a given time, so need
// to check whether temporal action is currently selected.
action_.temporal = kTwoThirdsTemporal;
}
}
void VCMQmResolution::ConvertSpatialFractionalToWhole() {
@ -704,42 +729,57 @@ void VCMQmResolution::ConvertSpatialFractionalToWhole() {
}
}
if (found) {
// Update state for removing 3/4 spatial.
state_dec_factor_spatial_ = state_dec_factor_spatial_ /
(kFactorWidthSpatial[kOneHalfSpatialUniform] *
kFactorHeightSpatial[kOneHalfSpatialUniform]);
width_ = width_ * kFactorWidthSpatial[kOneHalfSpatialUniform];
height_ = height_ * kFactorHeightSpatial[kOneHalfSpatialUniform];
// Remove 3/4 from the history.
for (int i = isel; i < kDownActionHistorySize - 1; ++i) {
down_action_history_[i].spatial = down_action_history_[i + 1].spatial;
}
// Update current selection action to be 1/2x1/2 (=1/4) spatial.
action_.spatial = kOneQuarterSpatialUniform;
action_.spatial = kOneQuarterSpatialUniform;
state_dec_factor_spatial_ = state_dec_factor_spatial_ /
(kFactorWidthSpatial[kOneHalfSpatialUniform] *
kFactorHeightSpatial[kOneHalfSpatialUniform]);
// Check if switching to 1/2x1/2 (=1/4) spatial is allowed.
ConstrainAmountOfDownSampling();
if (action_.spatial == kNoChangeSpatial) {
// Not allowed. Go back to 3/4x3/4 spatial.
action_.spatial = kOneHalfSpatialUniform;
state_dec_factor_spatial_ = state_dec_factor_spatial_ *
kFactorWidthSpatial[kOneHalfSpatialUniform] *
kFactorHeightSpatial[kOneHalfSpatialUniform];
} else {
// Switching is allowed. Remove 3/4x3/4 from the history, and update
// the frame size.
for (int i = isel; i < kDownActionHistorySize - 1; ++i) {
down_action_history_[i].spatial =
down_action_history_[i + 1].spatial;
}
width_ = width_ * kFactorWidthSpatial[kOneHalfSpatialUniform];
height_ = height_ * kFactorHeightSpatial[kOneHalfSpatialUniform];
}
}
}
}
void VCMQmResolution::CheckForEvenFrameSize() {
// If 3/4 is selected, check that the new frame size is still multiple of 2,
// otherwise take 1/2.
// Returns false if the new frame sizes, under the current spatial action,
// are not multiples of two.
bool VCMQmResolution::EvenFrameSize() {
if (action_.spatial == kOneHalfSpatialUniform) {
if ((width_ * 3 / 4)%2 != 0 || (height_ * 3 / 4)%2 != 0) {
action_.spatial = kOneQuarterSpatialUniform;
if ((width_ * 3 / 4) % 2 != 0 || (height_ * 3 / 4) % 2 != 0) {
return false;
}
} else if (action_.spatial == kOneQuarterSpatialUniform) {
if ((width_ * 1 / 2) % 2 != 0 || (height_ * 1 / 2) % 2 != 0) {
return false;
}
}
return true;
}
void VCMQmResolution::InsertLatestDownAction() {
if (action_.spatial != kNoChangeSpatial) {
for (int i = kDownActionHistorySize - 1; i > 0; --i) {
down_action_history_[i].spatial = down_action_history_[i - 1].spatial;
down_action_history_[i].spatial = down_action_history_[i - 1].spatial;
}
down_action_history_[0].spatial = action_.spatial;
}
if (action_.temporal != kNoChangeTemporal) {
for (int i = kDownActionHistorySize - 1; i > 0; --i) {
down_action_history_[i].temporal = down_action_history_[i - 1].temporal;
down_action_history_[i].temporal = down_action_history_[i - 1].temporal;
}
down_action_history_[0].temporal = action_.temporal;
}
@ -747,14 +787,20 @@ void VCMQmResolution::InsertLatestDownAction() {
void VCMQmResolution::RemoveLastDownAction() {
if (action_.spatial != kNoChangeSpatial) {
for (int i = 0; i< kDownActionHistorySize - 1; ++i) {
down_action_history_[i].spatial = down_action_history_[i + 1].spatial;
// If the last spatial action was 1/2x1/2 we replace it with 3/4x3/4.
if (action_.spatial == kOneQuarterSpatialUniform) {
down_action_history_[0].spatial = kOneHalfSpatialUniform;
} else {
for (int i = 0; i < kDownActionHistorySize - 1; ++i) {
down_action_history_[i].spatial = down_action_history_[i + 1].spatial;
}
down_action_history_[kDownActionHistorySize - 1].spatial =
kNoChangeSpatial;
}
down_action_history_[kDownActionHistorySize - 1].spatial = kNoChangeSpatial;
}
if (action_.temporal != kNoChangeTemporal) {
for (int i = 0; i< kDownActionHistorySize - 1; ++i) {
down_action_history_[i].temporal = down_action_history_[i + 1].temporal;
for (int i = 0; i < kDownActionHistorySize - 1; ++i) {
down_action_history_[i].temporal = down_action_history_[i + 1].temporal;
}
down_action_history_[kDownActionHistorySize - 1].temporal =
kNoChangeTemporal;
@ -766,25 +812,42 @@ void VCMQmResolution::ConstrainAmountOfDownSampling() {
// override the settings for too small image size and/or frame rate.
// Also check the limit on current down-sampling states.
// No spatial sampling if current frame size is too small (QCIF),
// or if the amount of spatial down-sampling will be too much.
float spatial_width_fact = kFactorWidthSpatial[action_.spatial];
float spatial_height_fact = kFactorHeightSpatial[action_.spatial];
float temporal_fact = kFactorTemporal[action_.temporal];
float new_dec_factor_spatial = state_dec_factor_spatial_ *
qm_->spatial_width_fact * qm_->spatial_height_fact;
spatial_width_fact * spatial_height_fact;
float new_dec_factor_temp = state_dec_factor_temporal_ * temporal_fact;
// No spatial sampling if current frame size is too small, or if the
// amount of spatial down-sampling is above maximum spatial down-action.
if ((width_ * height_) <= kMinImageSize ||
new_dec_factor_spatial > kMaxSpatialDown) {
action_.spatial = kNoChangeSpatial;
qm_->change_resolution_spatial = false;
qm_->spatial_width_fact = 1.0f;
qm_->spatial_height_fact = 1.0f;
new_dec_factor_spatial = state_dec_factor_spatial_;
}
// No frame rate reduction if average frame rate is below some point,
// or if the amount of temporal down-sampling will be too much.
float new_dec_factor_temp = state_dec_factor_temporal_ * qm_->temporal_fact;
// No frame rate reduction if average frame rate is below some point, or if
// the amount of temporal down-sampling is above maximum temporal down-action.
if (avg_incoming_framerate_ <= kMinFrameRate ||
new_dec_factor_temp >= kMaxTempDown) {
new_dec_factor_temp > kMaxTempDown) {
action_.temporal = kNoChangeTemporal;
qm_->change_resolution_temporal = false;
qm_->temporal_fact = 1.0f;
new_dec_factor_temp = state_dec_factor_temporal_;
}
// Check if the total (spatial-temporal) down-action is above maximum allowed,
// if so, disallow the current selected down-action.
if (new_dec_factor_spatial * new_dec_factor_temp > kMaxTotalDown) {
if (action_.spatial != kNoChangeSpatial) {
action_.spatial = kNoChangeSpatial;
} else if (action_.temporal != kNoChangeTemporal) {
action_.temporal = kNoChangeTemporal;
} else {
// We only allow for one action (spatial or temporal) at a given time, so
// either spatial or temporal action is selected when this function is
// called. If the selected action is disallowed from one of the above
// 2 prior conditions (on spatial & temporal max down-action), then this
// condition "total down-action > |kMaxTotalDown|" would not be entered.
assert(false);
}
}
}

View File

@ -278,8 +278,9 @@ class VCMQmResolution : public VCMQmMethod {
// Covert 2 stages of 3/4 (=9/16) spatial decimation to 1/2.
void ConvertSpatialFractionalToWhole();
// Check if the new frame sizes are still divisible by 2.
void CheckForEvenFrameSize();
// Returns true if the new frame sizes, under the selected spatial action,
// are of even size.
bool EvenFrameSize();
// Insert latest down-sampling action into the history list.
void InsertLatestDownAction();

View File

@ -23,16 +23,16 @@ namespace webrtc {
// PARAMETERS FOR RESOLUTION ADAPTATION
//
// Optimal level of buffer in secs: should corresponds to wrapper settings.
const float kOptBufferLevel = 0.5f;
// Initial level of buffer in secs.
const float kInitBufferLevel = 0.5f;
// Threshold of (max) buffer size below which we consider too low (underflow).
const float kPercBufferThr = 0.10f;
// Threshold on the occurrences of low buffer levels.
const float kMaxBufferLow = 0.5f;
const float kMaxBufferLow = 0.30f;
// Threshold on rate mismatch
// Threshold on rate mismatch.
const float kMaxRateMisMatch = 0.5f;
// Threshold on amount of under/over encoder shooting.
@ -50,21 +50,21 @@ const float kTransRateScaleUpSpatialTemp = 1.25f;
// Threshold on packet loss rate, above which favor resolution reduction.
const float kPacketLossThr = 0.1f;
// Factor for reducing transitonal bitrate under packet loss.
// Factor for reducing transitional bitrate under packet loss.
const float kPacketLossRateFac = 1.0f;
// Maximum possible transitional rate for down-sampling:
// (units in kbps), for 30fps.
const uint16_t kMaxRateQm[9] = {
50, // QCIF
100, // kHCIF
175, // kQVGA
250, // CIF
350, // HVGA
500, // VGA
1000, // QFULLHD
1500, // WHD
2000 // FULLHD
0, // QCIF
50, // kHCIF
125, // kQVGA
200, // CIF
280, // HVGA
400, // VGA
700, // QFULLHD
1000, // WHD
1500 // FULLHD
};
// Frame rate scale for maximum transition rate.
@ -79,30 +79,30 @@ const float kFrameRateFac[4] = {
// motion=L/H/D,spatial==L/H/D: for low, high, middle levels
const float kScaleTransRateQm[18] = {
// VGA and lower
0.50f, // L, L
0.40f, // L, L
0.50f, // L, H
0.50f, // L, D
0.50f, // H ,L
0.35f, // H, H
0.35f, // H, D
0.40f, // L, D
0.60f, // H ,L
0.60f, // H, H
0.60f, // H, D
0.50f, // D, L
0.50f, // D, D
0.35f, // D, H
0.50f, // D, H
// over VGA
0.50f, // L, L
0.40f, // L, L
0.50f, // L, H
0.50f, // L, D
0.50f, // H ,L
0.35f, // H, H
0.35f, // H, D
0.40f, // L, D
0.60f, // H ,L
0.60f, // H, H
0.60f, // H, D
0.50f, // D, L
0.50f, // D, D
0.35f, // D, H
0.50f, // D, H
};
// Threshold on the target rate relative to transitional rate.
const float kFacLowRate = 0.75f;
const float kFacLowRate = 0.5f;
// Action for down-sampling:
// motion=L/H/D,spatial==L/H/D, for low, high, middle levels;
@ -123,7 +123,7 @@ const uint8_t kSpatialAction[27] = {
1, // L, L
1, // L, H
1, // L, D
4, // H ,L
2, // H ,L
1, // H, H
2, // H, D
2, // D, L
@ -156,7 +156,7 @@ const uint8_t kTemporalAction[27] = {
// rateClass = 1:
3, // L, L
2, // L, H
3, // L, H
3, // L, D
1, // H ,L
3, // H, H
@ -179,14 +179,14 @@ const uint8_t kTemporalAction[27] = {
// Control the total amount of down-sampling allowed.
const float kMaxSpatialDown = 8.0f;
const float kMaxTempDown = 4.0f;
const float kMaxDownSample = 12.0f;
const float kMaxTempDown = 3.0f;
const float kMaxTotalDown = 9.0f;
// Minimum image size for a spatial down-sampling.
const int kMinImageSize= 176 * 144;
const int kMinImageSize = 176 * 144;
// Minimum frame rate for temporal down-sampling:
// no frame rate reduction if incomingFrameRate <= MIN_FRAME_RATE
// no frame rate reduction if incomingFrameRate <= MIN_FRAME_RATE.
const int kMinFrameRate = 8;
//
@ -202,20 +202,20 @@ const int kLowFrameRate = 10;
const int kMiddleFrameRate = 15;
const int kHighFrameRate = 25;
// Thresholds for motion: motion level is from NFD
// Thresholds for motion: motion level is from NFD.
const float kHighMotionNfd = 0.075f;
const float kLowMotionNfd = 0.04f;
const float kLowMotionNfd = 0.03f;
// Thresholds for spatial prediction error:
// this is applied on the min(2x2,1x2,2x1)
// this is applied on the average of (2x2,1x2,2x1).
const float kHighTexture = 0.035f;
const float kLowTexture = 0.025f;
const float kLowTexture = 0.020f;
// Used to reduce thresholds for larger/HD scenes: correction factor since
// higher correlation in HD scenes means lower spatial prediction error.
const float kScaleTexture = 0.9f;
// percentage reduction in transitional bitrate for 2x2 selected over 1x2/2x1
// Percentage reduction in transitional bitrate for 2x2 selected over 1x2/2x1.
const float kRateRedSpatial2X2 = 0.6f;
const float kSpatialErr2x2VsHoriz = 0.1f; // percentage to favor 2x2 over H

View File

@ -91,9 +91,12 @@ TEST_F(QmSelectTest, HandleInputs) {
30.0f));
}
// TODO(marpan): Add a test for number of temporal layers > 1.
// No down-sampling action at high rates.
TEST_F(QmSelectTest, NoActionHighRate) {
// Initialize with bitrate, frame rate, and native system width/height.
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(800, 30, 640, 480, 1);
// Update with encoder frame size.
@ -122,8 +125,9 @@ TEST_F(QmSelectTest, NoActionHighRate) {
// Rate is well below transition, down-sampling action is taken,
// depending on the content state.
TEST_F(QmSelectTest, DownActionLowRate) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(100, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(50, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -132,8 +136,8 @@ TEST_F(QmSelectTest, DownActionLowRate) {
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {100, 100, 100};
int encoder_sent_rate[] = {100, 100, 100};
int target_rate[] = {50, 50, 50};
int encoder_sent_rate[] = {50, 50, 50};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -209,20 +213,21 @@ TEST_F(QmSelectTest, DownActionLowRate) {
15.5f));
qm_resolution_->ResetDownSamplingState();
// Medium motion, medium spatial: high frame rate, so 1/2 temporal expected.
// Medium motion, medium spatial: high frame rate, so 2/3 temporal expected.
UpdateQmContentData(kTemporalMedium, kSpatialMedium, kSpatialMedium,
kSpatialMedium);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(8, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480,
15.5f));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 640, 480,
20.5f));
}
// Rate mis-match is high, and we have over-shooting.
// since target rate is below max for down-sampling, down-sampling is selected.
TEST_F(QmSelectTest, DownActionHighRateMMOvershoot) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(450, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(300, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -231,7 +236,7 @@ TEST_F(QmSelectTest, DownActionHighRateMMOvershoot) {
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {450, 450, 450};
int target_rate[] = {300, 300, 300};
int encoder_sent_rate[] = {900, 900, 900};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
@ -259,8 +264,9 @@ TEST_F(QmSelectTest, DownActionHighRateMMOvershoot) {
// Rate mis-match is high, target rate is below max for down-sampling,
// but since we have consistent under-shooting, no down-sampling action.
TEST_F(QmSelectTest, NoActionHighRateMMUndershoot) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(450, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(300, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -269,7 +275,7 @@ TEST_F(QmSelectTest, NoActionHighRateMMUndershoot) {
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {450, 450, 450};
int target_rate[] = {300, 300, 300};
int encoder_sent_rate[] = {100, 100, 100};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
@ -297,8 +303,9 @@ TEST_F(QmSelectTest, NoActionHighRateMMUndershoot) {
// Buffer is underflowing, and target rate is below max for down-sampling,
// so action is taken.
TEST_F(QmSelectTest, DownActionBufferUnderflow) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(450, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(300, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -312,7 +319,7 @@ TEST_F(QmSelectTest, DownActionBufferUnderflow) {
UpdateQmEncodedFrame(encoded_size, 10);
// Update rates for a sequence of intervals.
int target_rate[] = {450, 450, 450};
int target_rate[] = {300, 300, 300};
int encoder_sent_rate[] = {450, 450, 450};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
@ -340,8 +347,9 @@ TEST_F(QmSelectTest, DownActionBufferUnderflow) {
// Target rate is below max for down-sampling, but buffer level is stable,
// so no action is taken.
TEST_F(QmSelectTest, NoActionBufferStable) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(450, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(350, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -355,8 +363,8 @@ TEST_F(QmSelectTest, NoActionBufferStable) {
UpdateQmEncodedFrame(encoded_size, 10);
// Update rates for a sequence of intervals.
int target_rate[] = {450, 450, 450};
int encoder_sent_rate[] = {450, 450, 450};
int target_rate[] = {350, 350, 350};
int encoder_sent_rate[] = {350, 450, 450};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -382,7 +390,8 @@ TEST_F(QmSelectTest, NoActionBufferStable) {
// Very low rate, but no spatial down-sampling below some size (QCIF).
TEST_F(QmSelectTest, LimitDownSpatialAction) {
// Initialize with bitrate, frame rate, and native system width/height.
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(10, 30, 176, 144, 1);
// Update with encoder frame size.
@ -411,7 +420,8 @@ TEST_F(QmSelectTest, LimitDownSpatialAction) {
// Very low rate, but no frame reduction below some frame_rate (8fps).
TEST_F(QmSelectTest, LimitDownTemporalAction) {
// Initialize with bitrate, frame rate, and native system width/height.
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(10, 8, 640, 480, 1);
// Update with encoder frame size.
@ -442,8 +452,9 @@ TEST_F(QmSelectTest, LimitDownTemporalAction) {
// Two stages: spatial down-sample and then back up spatially,
// as rate as increased.
TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(100, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(50, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -452,8 +463,8 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) {
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {100, 100, 100};
int encoder_sent_rate[] = {100, 100, 100};
int target_rate[] = {50, 50, 50};
int encoder_sent_rate[] = {50, 50, 50};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -468,7 +479,7 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) {
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
30.0f));
// Reset and go up in rate: expected to go back up.
// Reset and go up in rate: expected to go back up, in 2 stages of 3/4.
qm_resolution_->ResetRates();
qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
@ -481,15 +492,23 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) {
fraction_lost2, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f, 640, 480,
float scale = (4.0f / 3.0f) / 2.0f;
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 1.0f, 480, 360,
30.0f));
qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f,
640, 480, 30.0f));
}
// Two stages: spatial down-sample and then back up spatially, since encoder
// is under-shooting target even though rate has not increased much.
TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(100, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(50, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -498,8 +517,8 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) {
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {100, 100, 100};
int encoder_sent_rate[] = {100, 100, 100};
int target_rate[] = {50, 50, 50};
int encoder_sent_rate[] = {50, 50, 50};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -515,6 +534,7 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) {
30.0f));
// Reset rates and simulate under-shooting scenario.: expect to go back up.
// Goes up spatially in two stages for 1/2x1/2 down-sampling.
qm_resolution_->ResetRates();
qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
@ -527,15 +547,23 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) {
fraction_lost2, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f, 640, 480,
float scale = (4.0f / 3.0f) / 2.0f;
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 1.0f, 480, 360,
30.0f));
qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f,
640, 480, 30.0f));
}
// Two stages: spatial down-sample and then no action to go up,
// as encoding rate mis-match is too high.
TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(100, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(50, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -544,8 +572,8 @@ TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) {
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {100, 100, 100};
int encoder_sent_rate[] = {100, 100, 100};
int target_rate[] = {50, 50, 50};
int encoder_sent_rate[] = {50, 50, 50};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -580,8 +608,9 @@ TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) {
// Two stages: temporally down-sample and then back up temporally,
// as rate as increased.
TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(100, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(50, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -590,8 +619,8 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) {
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {100, 100, 100};
int encoder_sent_rate[] = {100, 100, 100};
int target_rate[] = {50, 50, 50};
int encoder_sent_rate[] = {50, 50, 50};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -624,8 +653,9 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) {
// Two stages: temporal down-sample and then back up temporally, since encoder
// is under-shooting target even though rate has not increased much.
TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporalUndershoot) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(100, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(50, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -634,8 +664,8 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporalUndershoot) {
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {100, 100, 100};
int encoder_sent_rate[] = {100, 100, 100};
int target_rate[] = {50, 50, 50};
int encoder_sent_rate[] = {50, 50, 50};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -653,7 +683,7 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporalUndershoot) {
// Reset rates and simulate under-shooting scenario.: expect to go back up.
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate2[] = {200, 200, 200, 200, 200};
int target_rate2[] = {150, 150, 150, 150, 150};
int encoder_sent_rate2[] = {50, 50, 50, 50, 50};
int incoming_frame_rate2[] = {15, 15, 15, 15, 15};
uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
@ -668,8 +698,9 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporalUndershoot) {
// Two stages: temporal down-sample and then no action to go up,
// as encoding rate mis-match is too high.
TEST_F(QmSelectTest, 2StageDownTemporalNoActionUp) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(100, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(50, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -678,8 +709,8 @@ TEST_F(QmSelectTest, 2StageDownTemporalNoActionUp) {
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {100, 100, 100};
int encoder_sent_rate[] = {100, 100, 100};
int target_rate[] = {50, 50, 50};
int encoder_sent_rate[] = {50, 50, 50};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -711,8 +742,9 @@ TEST_F(QmSelectTest, 2StageDownTemporalNoActionUp) {
// 3 stages: spatial down-sample, followed by temporal down-sample,
// and then go up to full state, as encoding rate has increased.
TEST_F(QmSelectTest, 3StageDownSpatialTemporlaUpSpatialTemporal) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(100, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(80, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -741,37 +773,54 @@ TEST_F(QmSelectTest, 3StageDownSpatialTemporlaUpSpatialTemporal) {
qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
// Reset rates and go lower in rate.
qm_resolution_->ResetRates();
int target_rate2[] = {40, 40, 40, 40, 40};
int encoder_sent_rate2[] = {40, 40, 40, 40, 40};
int incoming_frame_rate2[] = {30, 30, 30, 30, 30};
uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
fraction_lost2, 5);
// Update content: motion level, and 3 spatial prediction errors.
// Low motion, high spatial.
UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 320, 240,
15.5f));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 320, 240,
20.5f));
// Reset rates and go high up in rate: expect to go back up both spatial
// and temporally.
// and temporally. The 1/2x1/2 spatial is undone in two stages.
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate2[] = {1000, 1000, 1000, 1000, 1000};
int encoder_sent_rate2[] = {1000, 1000, 1000, 1000, 1000};
int incoming_frame_rate2[] = {15, 15, 15, 15, 15};
uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
fraction_lost2, 5);
int target_rate3[] = {1000, 1000, 1000, 1000, 1000};
int encoder_sent_rate3[] = {1000, 1000, 1000, 1000, 1000};
int incoming_frame_rate3[] = {20, 20, 20, 20, 20};
uint8_t fraction_lost3[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3,
fraction_lost3, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 0.5f, 640, 480,
30.0f));
float scale = (4.0f / 3.0f) / 2.0f;
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 2.0f / 3.0f,
480, 360, 30.0f));
qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f,
640, 480, 30.0f));
}
// No down-sampling below some total amount.
TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(400, 30, 1280, 720, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(150, 30, 1280, 720, 1);
// Update with encoder frame size.
uint16_t codec_width = 1280;
@ -780,8 +829,8 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
EXPECT_EQ(7, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals.
int target_rate[] = {200, 200, 200};
int encoder_sent_rate[] = {200, 200, 200};
int target_rate[] = {150, 150, 150};
int encoder_sent_rate[] = {150, 150, 150};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -796,14 +845,15 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 640, 360,
30.0f));
// Reset and lower rates to get another spatial action (3/4x3/4)
// Reset and lower rates to get another spatial action (3/4x3/4).
// Lower the frame rate for spatial to be selected again.
qm_resolution_->ResetRates();
qm_resolution_->UpdateCodecParameters(30.0f, 640, 360);
qm_resolution_->UpdateCodecParameters(10.0f, 640, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(640, 360));
// Update rates for a sequence of intervals.
int target_rate2[] = {80, 80, 80, 80, 80};
int encoder_sent_rate2[] = {80, 80, 80, 80, 80};
int incoming_frame_rate2[] = {30, 30, 30, 30, 30};
int target_rate2[] = {70, 70, 70, 70, 70};
int encoder_sent_rate2[] = {70, 70, 70, 70, 70};
int incoming_frame_rate2[] = {10, 10, 10, 10, 10};
uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
fraction_lost2, 5);
@ -816,17 +866,17 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
EXPECT_EQ(5, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f,
1.0f, 480, 270, 30.0f));
1.0f, 480, 270, 10.0f));
// Reset and go to very low rate: no action should be taken,
// we went down too much already.
qm_resolution_->ResetRates();
qm_resolution_->UpdateCodecParameters(30.0f, 480, 270);
qm_resolution_->UpdateCodecParameters(10.0f, 480, 270);
EXPECT_EQ(3, qm_resolution_->GetImageType(480, 270));
// Update rates for a sequence of intervals.
int target_rate3[] = {10, 10, 10, 10, 10};
int encoder_sent_rate3[] = {10, 10, 10, 10, 10};
int incoming_frame_rate3[] = {30, 30, 30, 30, 30};
int incoming_frame_rate3[] = {10, 10, 10, 10, 10};
uint8_t fraction_lost3[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3,
fraction_lost3, 5);
@ -834,16 +884,17 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
EXPECT_EQ(5, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 480, 270,
30.0f));
10.0f));
}
// Multiple down-sampling stages and then undo all of them.
// Spatial down-sample 3/4x3/4, followed by temporal down-sample 2/3,
// followed by spatial 1/2x1/2. Then go up to full state,
// followed by spatial 3/4x3/4. Then go up to full state,
// as encoding rate has increased.
TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(200, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(150, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -853,8 +904,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
// Go down spatial 3/4x3/4.
// Update rates for a sequence of intervals.
int target_rate[] = {200, 200, 200};
int encoder_sent_rate[] = {200, 200, 200};
int target_rate[] = {150, 150, 150};
int encoder_sent_rate[] = {150, 150, 150};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -868,7 +919,7 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f,
1.0f, 480, 360, 30.0f));
// Go down 1/2 temporal.
// Go down 2/3 temporal.
qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
qm_resolution_->ResetRates();
@ -885,15 +936,15 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 480, 360,
15.5f));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 480, 360,
20.5f));
// Go down 1/2x1/2 spatial.
qm_resolution_->UpdateCodecParameters(15.0f, 480, 360);
// Go down 3/4x3/4 spatial:
qm_resolution_->UpdateCodecParameters(20.0f, 480, 360);
qm_resolution_->ResetRates();
int target_rate3[] = {50, 50, 50, 50, 50};
int encoder_sent_rate3[] = {50, 50, 50, 50, 50};
int incoming_frame_rate3[] = {15, 15, 15, 15, 15};
int target_rate3[] = {80, 80, 80, 80, 80};
int encoder_sent_rate3[] = {80, 80, 80, 80, 80};
int incoming_frame_rate3[] = {20, 20, 20, 20, 20};
uint8_t fraction_lost3[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3,
fraction_lost3, 5);
@ -904,16 +955,17 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 240, 180,
15.0f));
// The two spatial actions of 3/4x3/4 are converted to 1/2x1/2,
// so scale factor is 2.0.
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
20.0f));
// Reset rates and go high up in rate: expect to go up:
// should go up first: 1/2x1x2 and 1/2 temporally,
// and second: 3/4x3/4 spatial.
// 1/2x1x2 spatial and 1/2 temporally.
// Go up 1/2x1/2 spatially and 1/2 temporally
qm_resolution_->UpdateCodecParameters(15.0f, 240, 180);
EXPECT_EQ(1, qm_resolution_->GetImageType(240, 180));
// Go up 1/2x1/2 spatially and 1/2 temporally. Spatial is done in 2 stages.
qm_resolution_->UpdateCodecParameters(15.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate4[] = {1000, 1000, 1000, 1000, 1000};
@ -926,35 +978,24 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 0.5f, 480, 360,
30.0f));
float scale = (4.0f / 3.0f) / 2.0f;
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 2.0f / 3.0f, 480,
360, 30.0f));
// Go up 3/4x3/4 spatially.
qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate5[] = {1000, 1000, 1000, 1000, 1000};
int encoder_sent_rate5[] = {1000, 1000, 1000, 1000, 1000};
int incoming_frame_rate5[] = {30, 30, 30, 30, 30};
uint8_t fraction_lost5[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate5, encoder_sent_rate5, incoming_frame_rate5,
fraction_lost5, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f,
1.0f, 640, 480, 30.0f));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f,
640, 480, 30.0f));
}
// Multiple down-sampling and up-sample stages, with partial undoing.
// Spatial down-sample 1/2x1/2, followed by temporal down-sample 2/3,
// undo the spatial 1/2x1/2, then another temporal 1/2, and undo
// the 1/2 temporal.
// Spatial down-sample 1/2x1/2, followed by temporal down-sample 2/3, undo the
// temporal, then another temporal, and then undo both spatial and temporal.
TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(100, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(80, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -964,8 +1005,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
// Go down 1/2x1/2 spatial.
// Update rates for a sequence of intervals.
int target_rate[] = {100, 100, 100};
int encoder_sent_rate[] = {100, 100, 100};
int target_rate[] = {80, 80, 80};
int encoder_sent_rate[] = {80, 80, 80};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -984,8 +1025,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
qm_resolution_->ResetRates();
int target_rate2[] = {80, 80, 80, 80, 80};
int encoder_sent_rate2[] = {80, 80, 80, 80, 80};
int target_rate2[] = {40, 40, 40, 40, 40};
int encoder_sent_rate2[] = {40, 40, 40, 40, 40};
int incoming_frame_rate2[] = {30, 30, 30, 30, 30};
uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
@ -1001,12 +1042,12 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 320, 240,
20.5f));
// Go up 1/2x1/2 spatially.
// Go up 2/3 temporally.
qm_resolution_->UpdateCodecParameters(20.0f, 320, 240);
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate3[] = {300, 300, 300, 300, 300};
int encoder_sent_rate3[] = {300, 300, 300, 300, 300};
int target_rate3[] = {150, 150, 150, 150, 150};
int encoder_sent_rate3[] = {150, 150, 150, 150, 150};
int incoming_frame_rate3[] = {20, 20, 20, 20, 20};
uint8_t fraction_lost3[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3,
@ -1015,16 +1056,16 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(7, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f, 640, 480,
20.0f));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f / 3.0f, 320,
240, 30.0f));
// Go down 1/2 temporal.
qm_resolution_->UpdateCodecParameters(20.0f, 640, 480);
EXPECT_EQ(5, qm_resolution_->GetImageType(640, 480));
// Go down 2/3 temporal.
qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
qm_resolution_->ResetRates();
int target_rate4[] = {100, 100, 100, 100, 100};
int encoder_sent_rate4[] = {100, 100, 100, 100, 100};
int incoming_frame_rate4[] = {20, 20, 20, 20, 20};
int target_rate4[] = {40, 40, 40, 40, 40};
int encoder_sent_rate4[] = {40, 40, 40, 40, 40};
int incoming_frame_rate4[] = {30, 30, 30, 30, 30};
uint8_t fraction_lost4[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate4, encoder_sent_rate4, incoming_frame_rate4,
fraction_lost4, 5);
@ -1035,33 +1076,39 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480,
10.5f));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 320, 240,
20.5f));
// Go up 1/2 temporal.
// Go up spatial and temporal. Spatial undoing is done in 2 stages.
qm_resolution_->UpdateCodecParameters(20.5f, 320, 240);
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate5[] = {1000, 1000, 1000, 1000, 1000};
int encoder_sent_rate5[] = {1000, 1000, 1000, 1000, 1000};
int incoming_frame_rate5[] = {10, 10, 10, 10, 10};
int incoming_frame_rate5[] = {20, 20, 20, 20, 20};
uint8_t fraction_lost5[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate5, encoder_sent_rate5, incoming_frame_rate5,
fraction_lost5, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f, 640, 480,
20.5f));
float scale = (4.0f / 3.0f) / 2.0f;
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 2.0f / 3.0f,
480, 360, 30.0f));
qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f,
640, 480, 30.0f));
}
// Multiple down-sampling and up-sample stages, with partial undoing.
// Spatial down-sample 3/4x3/4, followed by temporal down-sample 1/2,
// undo the temporal 1/2, then another temporal 2/3 down, and undo
// the 2/3 temporal.
// Spatial down-sample 3/4x3/4, followed by temporal down-sample 2/3,
// undo the temporal 2/3, and then undo the spatial.
TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(200, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(100, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -1071,8 +1118,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
// Go down 3/4x3/4 spatial.
// Update rates for a sequence of intervals.
int target_rate[] = {200, 200, 200};
int encoder_sent_rate[] = {200, 200, 200};
int target_rate[] = {100, 100, 100};
int encoder_sent_rate[] = {100, 100, 100};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -1087,7 +1134,7 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f,
1.0f, 480, 360, 30.0f));
// Go down 1/2 temporal.
// Go down 2/3 temporal.
qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
qm_resolution_->ResetRates();
@ -1104,15 +1151,16 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 480, 360,
15.5f));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 480, 360,
20.5f));
// Go up 1/2 temporal.
// Go up 2/3 temporal.
qm_resolution_->UpdateCodecParameters(20.5f, 480, 360);
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate3[] = {300, 300, 300, 300, 300};
int encoder_sent_rate3[] = {300, 300, 300, 300, 300};
int incoming_frame_rate3[] = {15, 15, 15, 15, 15};
int target_rate3[] = {250, 250, 250, 250, 250};
int encoder_sent_rate3[] = {250, 250, 250, 250, 250};
int incoming_frame_rate3[] = {20, 20, 20, 20, 120};
uint8_t fraction_lost3[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3,
fraction_lost3, 5);
@ -1120,51 +1168,31 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f, 480, 360,
30.0f));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f / 3.0f, 480,
360, 30.0f));
// Go down 2/3 temporal.
qm_resolution_->UpdateCodecParameters(30.0f, 640, 480);
EXPECT_EQ(5, qm_resolution_->GetImageType(640, 480));
// Go up spatial.
qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
qm_resolution_->ResetRates();
int target_rate4[] = {200, 200, 200, 200, 200};
int encoder_sent_rate4[] = {200, 200, 200, 200, 200};
int target_rate4[] = {500, 500, 500, 500, 500};
int encoder_sent_rate4[] = {500, 500, 500, 500, 500};
int incoming_frame_rate4[] = {30, 30, 30, 30, 30};
uint8_t fraction_lost4[] = {30, 30, 30, 30, 30};
UpdateQmRateData(target_rate4, encoder_sent_rate4, incoming_frame_rate4,
fraction_lost4, 5);
// Update content: motion level, and 3 spatial prediction errors.
// Medium motion, high spatial.
UpdateQmContentData(kTemporalMedium, kSpatialHigh, kSpatialHigh,
kSpatialHigh);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(7, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 640, 480,
20.5f));
// Go up 2/3 temporal.
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate5[] = {500, 500, 500, 500, 500};
int encoder_sent_rate5[] = {500, 500, 500, 500, 500};
int incoming_frame_rate5[] = {20, 20, 20, 20, 20};
uint8_t fraction_lost5[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate5, encoder_sent_rate5, incoming_frame_rate5,
fraction_lost5, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(7, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f / 3.0f, 640,
480, 30.0f));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f,
1.0f, 640, 480, 30.0f));
}
// Two stages of 3/4x3/4 converted to one stage of 1/2x1/2.
TEST_F(QmSelectTest, ConvertThreeQuartersToOneHalf) {
// Initialize with bitrate, frame rate, and native system width/height.
InitQmNativeData(200, 30, 640, 480, 1);
// Initialize with bitrate, frame rate, native system width/height, and
// number of temporal layers.
InitQmNativeData(150, 30, 640, 480, 1);
// Update with encoder frame size.
uint16_t codec_width = 640;
@ -1174,8 +1202,8 @@ TEST_F(QmSelectTest, ConvertThreeQuartersToOneHalf) {
// Go down 3/4x3/4 spatial.
// Update rates for a sequence of intervals.
int target_rate[] = {200, 200, 200};
int encoder_sent_rate[] = {200, 200, 200};
int target_rate[] = {150, 150, 150};
int encoder_sent_rate[] = {150, 150, 150};
int incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
@ -1194,8 +1222,8 @@ TEST_F(QmSelectTest, ConvertThreeQuartersToOneHalf) {
qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
qm_resolution_->ResetRates();
int target_rate2[] = {150, 150, 150, 150, 150};
int encoder_sent_rate2[] = {150, 150, 150, 150, 150};
int target_rate2[] = {100, 100, 100, 100, 100};
int encoder_sent_rate2[] = {100, 100, 100, 100, 100};
int incoming_frame_rate2[] = {30, 30, 30, 30, 30};
uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,