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:
parent
ea5b8b5903
commit
c5b392e9d6
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user