Updates to resolution adpatation:

-moved calculation of selected frame size & frame rate to qm_select class.

-various updates to qm_select class (switch to 1/2 from 2 stages of 3/4, 
include native frame rate for going up temporal, favor spatial action for temporal layers,..).

-updates to unittest.
Review URL: https://webrtc-codereview.appspot.com/450008

git-svn-id: http://webrtc.googlecode.com/svn/trunk@1914 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
marpan@webrtc.org 2012-03-20 18:21:53 +00:00
parent a496b03c78
commit e22d81ce4d
6 changed files with 474 additions and 249 deletions

View File

@ -24,8 +24,6 @@ _maxBitRate(0),
_sendCodecType(kVideoCodecUnknown), _sendCodecType(kVideoCodecUnknown),
_codecWidth(0), _codecWidth(0),
_codecHeight(0), _codecHeight(0),
_initCodecWidth(0),
_initCodecHeight(0),
_userFrameRate(0), _userFrameRate(0),
_packetLossEnc(0), _packetLossEnc(0),
_fractionLost(0), _fractionLost(0),
@ -286,8 +284,6 @@ VCMMediaOptimization::SetEncodingData(VideoCodecType sendCodecType,
_userFrameRate = static_cast<float>(frameRate); _userFrameRate = static_cast<float>(frameRate);
_codecWidth = width; _codecWidth = width;
_codecHeight = height; _codecHeight = height;
_initCodecWidth = width;
_initCodecHeight = height;
_numLayers = (numLayers <= 1) ? 1 : numLayers; // Can also be zero. _numLayers = (numLayers <= 1) ? 1 : numLayers; // Can also be zero.
WebRtc_Word32 ret = VCM_OK; WebRtc_Word32 ret = VCM_OK;
ret = _qmResolution->Initialize((float)_targetBitRate, _userFrameRate, ret = _qmResolution->Initialize((float)_targetBitRate, _userFrameRate,
@ -577,41 +573,39 @@ VCMMediaOptimization::checkStatusForQMchange()
bool VCMMediaOptimization::QMUpdate(VCMResolutionScale* qm) { bool VCMMediaOptimization::QMUpdate(VCMResolutionScale* qm) {
// Check for no change // Check for no change
if (!qm->change_resolution) { if (!qm->change_resolution_spatial && !qm->change_resolution_temporal) {
return false; return false;
} }
// Check for change in frame rate. // Check for change in frame rate.
if (qm->temporal_fact != 1.0f) { if (qm->change_resolution_temporal) {
_incomingFrameRate = _incomingFrameRate / qm->temporal_fact + 0.5f; _incomingFrameRate = qm->frame_rate;
// Reset frame rate estimate.
memset(_incomingFrameTimes, -1, sizeof(_incomingFrameTimes)); memset(_incomingFrameTimes, -1, sizeof(_incomingFrameTimes));
} }
// Check for change in frame size. // Check for change in frame size.
if (qm->spatial_height_fact != 1.0 || qm->spatial_width_fact != 1.0) { if (qm->change_resolution_spatial) {
_codecWidth = static_cast<uint16_t>(_codecWidth / _codecWidth = qm->codec_width;
qm->spatial_width_fact); _codecHeight = qm->codec_height;
_codecHeight = static_cast<uint16_t>(_codecHeight /
qm->spatial_height_fact);
// New frame sizes should not exceed original size from SetEncodingData().
assert(_codecWidth <= _initCodecWidth);
assert(_codecHeight <= _initCodecHeight);
// Check that new frame sizes are multiples of two.
assert(_codecWidth % 2 == 0);
assert(_codecHeight % 2 == 0);
} }
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, _id, WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, _id,
"Quality Mode Update: W = %d, H = %d, FR = %f", "Resolution change from QM select: W = %d, H = %d, FR = %f",
_codecWidth, _codecHeight, _incomingFrameRate); qm->codec_width, qm->codec_height, qm->frame_rate);
// Update VPM with new target frame rate and size // Update VPM with new target frame rate and frame size.
_videoQMSettingsCallback->SetVideoQMSettings(_incomingFrameRate, // Note: use |qm->frame_rate| instead of |_incomingFrameRate| for updating
// target frame rate in VPM frame dropper. The quantity |_incomingFrameRate|
// will vary/fluctuate, and since we don't want to change the state of the
// VPM frame dropper, unless a temporal action was selected, we use the
// quantity |qm->frame_rate| for updating.
_videoQMSettingsCallback->SetVideoQMSettings(qm->frame_rate,
_codecWidth, _codecWidth,
_codecHeight); _codecHeight);
_content->UpdateFrameRate(qm->frame_rate);
_content->UpdateFrameRate(_incomingFrameRate); _qmResolution->UpdateCodecParameters(qm->frame_rate, _codecWidth,
_qmResolution->UpdateCodecFrameSize(_codecWidth, _codecHeight); _codecHeight);
return true; return true;
} }

View File

@ -168,8 +168,6 @@ private:
VideoCodecType _sendCodecType; VideoCodecType _sendCodecType;
WebRtc_UWord16 _codecWidth; WebRtc_UWord16 _codecWidth;
WebRtc_UWord16 _codecHeight; WebRtc_UWord16 _codecHeight;
WebRtc_UWord16 _initCodecWidth;
WebRtc_UWord16 _initCodecHeight;
float _userFrameRate; float _userFrameRate;
VCMFrameDropper* _frameDropper; VCMFrameDropper* _frameDropper;

View File

@ -26,9 +26,11 @@ VCMQmMethod::VCMQmMethod()
: content_metrics_(NULL), : content_metrics_(NULL),
width_(0), width_(0),
height_(0), height_(0),
user_frame_rate_(0.0f),
native_width_(0), native_width_(0),
native_height_(0), native_height_(0),
framerate_level_(kDefault), native_frame_rate_(0.0f),
framerate_level_(kFrameRateHigh),
init_(false) { init_(false) {
ResetQM(); ResetQM();
} }
@ -134,13 +136,15 @@ ImageType VCMQmMethod::FindClosestImageType(uint16_t width, uint16_t height) {
return static_cast<ImageType>(isel); return static_cast<ImageType>(isel);
} }
LevelClass VCMQmMethod::FrameRateLevel(float avg_framerate) { FrameRateLevelClass VCMQmMethod::FrameRateLevel(float avg_framerate) {
if (avg_framerate < kLowFrameRate) { if (avg_framerate < kLowFrameRate) {
return kLow; return kFrameRateLow;
} else if (avg_framerate > kHighFrameRate) { } else if (avg_framerate < kMiddleFrameRate) {
return kHigh; return kFrameRateMiddle1;
} else if (avg_framerate < kHighFrameRate) {
return kFrameRateMiddle2;
} else { } else {
return kDefault; return kFrameRateHigh;
} }
} }
@ -179,7 +183,6 @@ void VCMQmResolution::ResetDownSamplingState() {
void VCMQmResolution::Reset() { void VCMQmResolution::Reset() {
target_bitrate_ = 0.0f; target_bitrate_ = 0.0f;
user_framerate_ = 0.0f;
incoming_framerate_ = 0.0f; incoming_framerate_ = 0.0f;
buffer_level_ = 0.0f; buffer_level_ = 0.0f;
per_frame_bandwidth_ =0.0f; per_frame_bandwidth_ =0.0f;
@ -212,23 +215,26 @@ int VCMQmResolution::Initialize(float bitrate,
} }
Reset(); Reset();
target_bitrate_ = bitrate; target_bitrate_ = bitrate;
user_framerate_ = user_framerate;
incoming_framerate_ = user_framerate; incoming_framerate_ = user_framerate;
UpdateCodecFrameSize(width, height); UpdateCodecParameters(user_framerate, width, height);
native_width_ = width; native_width_ = width;
native_height_ = height; native_height_ = height;
native_frame_rate_ = user_framerate;
num_layers_ = num_layers; num_layers_ = num_layers;
// Initial buffer level. // Initial buffer level.
buffer_level_ = kOptBufferLevel * target_bitrate_; buffer_level_ = kOptBufferLevel * target_bitrate_;
// Per-frame bandwidth. // Per-frame bandwidth.
per_frame_bandwidth_ = target_bitrate_ / user_framerate_; per_frame_bandwidth_ = target_bitrate_ / user_framerate;
init_ = true; init_ = true;
return VCM_OK; return VCM_OK;
} }
void VCMQmResolution::UpdateCodecFrameSize(uint16_t width, uint16_t height) { void VCMQmResolution::UpdateCodecParameters(float frame_rate, uint16_t width,
uint16_t height) {
width_ = width; width_ = width;
height_ = height; height_ = height;
// |user_frame_rate| is the target frame rate for VPM frame dropper.
user_frame_rate_ = frame_rate;
image_type_ = GetImageType(width, height); image_type_ = GetImageType(width, height);
} }
@ -256,11 +262,9 @@ void VCMQmResolution::UpdateRates(float target_bitrate,
float encoder_sent_rate, float encoder_sent_rate,
float incoming_framerate, float incoming_framerate,
uint8_t packet_loss) { uint8_t packet_loss) {
// Sum the target bitrate and incoming frame rate: // Sum the target bitrate: this is the encoder rate from previous update
// these values are the encoder rates (from previous update ~1sec), // (~1sec), i.e, before the update for next ~1sec.
// i.e, before the update for next ~1sec.
sum_target_rate_ += target_bitrate_; sum_target_rate_ += target_bitrate_;
sum_incoming_framerate_ += incoming_framerate_;
update_rate_cnt_++; update_rate_cnt_++;
// Sum the received (from RTCP reports) packet loss rates. // Sum the received (from RTCP reports) packet loss rates.
@ -281,6 +285,7 @@ void VCMQmResolution::UpdateRates(float target_bitrate,
// 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; target_bitrate_ = target_bitrate;
incoming_framerate_ = incoming_framerate; incoming_framerate_ = incoming_framerate;
sum_incoming_framerate_ += incoming_framerate_;
// Update the per_frame_bandwidth: // 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
@ -296,8 +301,8 @@ void VCMQmResolution::UpdateRates(float target_bitrate,
// In the current version the following constraints are imposed: // In the current version the following constraints are imposed:
// 1) We only allow for one action, either down or up, at a given time. // 1) We only allow for one action, either down or up, at a given time.
// 2) The possible down-sampling actions are: spatial 1/2x1/2, 3/4x3/4; // 2) The possible down-sampling actions are: spatial by 1/2x1/2, 3/4x3/4;
// temporal 1/2 and 2/3. // temporal/frame rate reduction by 1/2 and 2/3.
// 3) The action for going back up is the reverse of last (spatial or temporal) // 3) The action for going back up is the reverse of last (spatial or temporal)
// down-sampling action. The list of down-sampling actions from the // down-sampling action. The list of down-sampling actions from the
// Initialize() state are kept in |down_action_history_|. // Initialize() state are kept in |down_action_history_|.
@ -313,10 +318,6 @@ int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) {
return VCM_OK; return VCM_OK;
} }
// Default settings: no action.
SetDefaultAction();
*qm = qm_;
// Compute content class for selection. // Compute content class for selection.
content_class_ = ComputeContentClass(); content_class_ = ComputeContentClass();
@ -326,6 +327,10 @@ int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) {
// Get the encoder state. // Get the encoder state.
ComputeEncoderState(); ComputeEncoderState();
// Default settings: no action.
SetDefaultAction();
*qm = qm_;
// Check for going back up in resolution, if we have had some down-sampling // Check for going back up in resolution, if we have had some down-sampling
// relative to native state in Initialize(). // relative to native state in Initialize().
if (down_action_history_[0].spatial != kNoChangeSpatial || if (down_action_history_[0].spatial != kNoChangeSpatial ||
@ -348,10 +353,14 @@ int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) {
} }
void VCMQmResolution::SetDefaultAction() { void VCMQmResolution::SetDefaultAction() {
qm_->codec_width = width_;
qm_->codec_height = height_;
qm_->frame_rate = user_frame_rate_;
qm_->change_resolution_spatial = false;
qm_->change_resolution_temporal = false;
qm_->spatial_width_fact = 1.0f; qm_->spatial_width_fact = 1.0f;
qm_->spatial_height_fact = 1.0f; qm_->spatial_height_fact = 1.0f;
qm_->temporal_fact = 1.0f; qm_->temporal_fact = 1.0f;
qm_->change_resolution = false;
action_.spatial = kNoChangeSpatial; action_.spatial = kNoChangeSpatial;
action_.temporal = kNoChangeTemporal; action_.temporal = kNoChangeTemporal;
} }
@ -385,7 +394,9 @@ void VCMQmResolution::ComputeRatesForSelection() {
(1.0 - kWeightRate) * target_bitrate_; (1.0 - kWeightRate) * target_bitrate_;
avg_incoming_framerate_ = kWeightRate * avg_incoming_framerate_ + avg_incoming_framerate_ = kWeightRate * avg_incoming_framerate_ +
(1.0 - kWeightRate) * incoming_framerate_; (1.0 - kWeightRate) * incoming_framerate_;
framerate_level_ = FrameRateLevel(avg_incoming_framerate_); // Use base layer frame rate for temporal layers: this will favor spatial.
framerate_level_ = FrameRateLevel(avg_incoming_framerate_ /
pow(2, num_layers_ - 1));
} }
void VCMQmResolution::ComputeEncoderState() { void VCMQmResolution::ComputeEncoderState() {
@ -461,6 +472,7 @@ bool VCMQmResolution::ConditionForGoingUp(float fac_width,
float scale_fac) { float scale_fac) {
float estimated_transition_rate_up = GetTransitionRate(fac_width, fac_height, float estimated_transition_rate_up = GetTransitionRate(fac_width, fac_height,
fac_temp, scale_fac); fac_temp, scale_fac);
// Go back up if: // Go back up if:
// 1) target rate is above threshold and current encoder state is stable, or // 1) target rate is above threshold and current encoder state is stable, or
// 2) encoder state is easy (encoder is significantly under-shooting target). // 2) encoder state is easy (encoder is significantly under-shooting target).
@ -527,11 +539,15 @@ bool VCMQmResolution::GoingDownResolution() {
} }
} }
// TODO(marpan): If num_layers_ > 1, adjust/favor spatial over temporal ? // 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.
AdjustAction(); AdjustAction();
ConvertSpatialFractionalToWhole();
CheckForEvenFrameSize(); CheckForEvenFrameSize();
// Update down-sampling state. // Update down-sampling state.
@ -552,8 +568,14 @@ float VCMQmResolution::GetTransitionRate(float fac_width,
static_cast<uint16_t>(fac_width * width_), static_cast<uint16_t>(fac_width * width_),
static_cast<uint16_t>(fac_height * height_)); static_cast<uint16_t>(fac_height * height_));
LevelClass framerate_level = FrameRateLevelClass framerate_level =
FrameRateLevel(fac_temp * avg_incoming_framerate_); FrameRateLevel(fac_temp * avg_incoming_framerate_);
// If we are checking for going up temporally, and this is the last
// temporal action, then use native frame rate.
if (down_action_history_[1].temporal == kNoChangeTemporal &&
fac_temp > 1.0f) {
framerate_level = FrameRateLevel(native_frame_rate_);
}
// The maximum allowed rate below which down-sampling is allowed: // The maximum allowed rate below which down-sampling is allowed:
// Nominal values based on image format (frame size and frame rate). // Nominal values based on image format (frame size and frame rate).
@ -570,7 +592,6 @@ float VCMQmResolution::GetTransitionRate(float fac_width,
} }
void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) { void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) {
qm_->change_resolution = true;
if (up_down == kUpResolution) { if (up_down == kUpResolution) {
qm_->spatial_width_fact = 1.0f / kFactorWidthSpatial[action_.spatial]; qm_->spatial_width_fact = 1.0f / kFactorWidthSpatial[action_.spatial];
qm_->spatial_height_fact = 1.0f / kFactorHeightSpatial[action_.spatial]; qm_->spatial_height_fact = 1.0f / kFactorHeightSpatial[action_.spatial];
@ -587,9 +608,9 @@ void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) {
// has been selected. // has been selected.
assert(false); assert(false);
} }
UpdateCodecResolution();
state_dec_factor_spatial_ = state_dec_factor_spatial_ * state_dec_factor_spatial_ = state_dec_factor_spatial_ *
qm_->spatial_width_fact * qm_->spatial_width_fact * qm_->spatial_height_fact;
qm_->spatial_height_fact;
state_dec_factor_temporal_ = state_dec_factor_temporal_ * qm_->temporal_fact; state_dec_factor_temporal_ = state_dec_factor_temporal_ * qm_->temporal_fact;
assert(state_dec_factor_spatial_ >= 1.0f); assert(state_dec_factor_spatial_ >= 1.0f);
assert(state_dec_factor_spatial_ <= kMaxSpatialDown); assert(state_dec_factor_spatial_ <= kMaxSpatialDown);
@ -597,6 +618,33 @@ void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) {
assert(state_dec_factor_temporal_ <= kMaxTempDown); assert(state_dec_factor_temporal_ <= kMaxTempDown);
} }
void VCMQmResolution::UpdateCodecResolution() {
if (action_.spatial != kNoChangeSpatial) {
qm_->change_resolution_spatial = true;
qm_->codec_width = static_cast<uint16_t>(width_ / qm_->spatial_width_fact);
qm_->codec_height = static_cast<uint16_t>
(height_ / qm_->spatial_height_fact);
// Size can never exceed native sizes.
assert(qm_->codec_width <= native_width_);
assert(qm_->codec_height <= native_height_);
// Size should be multiple of 2.
assert(qm_->codec_width % 2 == 0);
assert(qm_->codec_height % 2 == 0);
}
if (action_.temporal != kNoChangeTemporal) {
qm_->change_resolution_temporal = true;
// Update the frame rate based on the average incoming frame rate.
qm_->frame_rate = avg_incoming_framerate_ / qm_->temporal_fact + 0.5f;
if (down_action_history_[0].temporal == 0) {
// When we undo the last temporal-down action, make sure we go back up
// to the native frame rate. Since the incoming frame rate may
// fluctuate over time, |avg_incoming_framerate_| scaled back up may
// be smaller than |native_frame rate_|.
qm_->frame_rate = native_frame_rate_;
}
}
}
uint8_t VCMQmResolution::RateClass(float transition_rate) { uint8_t VCMQmResolution::RateClass(float transition_rate) {
return avg_target_rate_ < (kFacLowRate * transition_rate) ? 0: return avg_target_rate_ < (kFacLowRate * transition_rate) ? 0:
(avg_target_rate_ >= transition_rate ? 2 : 1); (avg_target_rate_ >= transition_rate ? 2 : 1);
@ -607,17 +655,67 @@ void VCMQmResolution::AdjustAction() {
// is not high, then safer to take frame rate reduction if the // is not high, then safer to take frame rate reduction if the
// average incoming frame rate is high. // average incoming frame rate is high.
if (spatial_.level == kDefault && motion_.level != kHigh && if (spatial_.level == kDefault && motion_.level != kHigh &&
framerate_level_ == kHigh) { framerate_level_ == kFrameRateHigh) {
action_.spatial = kNoChangeSpatial; action_.spatial = kNoChangeSpatial;
action_.temporal = kOneHalfTemporal; action_.temporal = kOneHalfTemporal;
} }
// If both motion and spatial level are low, and temporal down-action // 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. // was selected, switch to spatial 3/4x3/4 if the frame rate is low.
if (motion_.level == kLow && spatial_.level == kLow && if (motion_.level == kLow && spatial_.level == kLow &&
framerate_level_ == kLow && action_.temporal != kNoChangeTemporal) { framerate_level_ == kFrameRateLow &&
action_.temporal != kNoChangeTemporal) {
action_.spatial = kOneHalfSpatialUniform; action_.spatial = kOneHalfSpatialUniform;
action_.temporal = kNoChangeTemporal; 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 &&
down_action_history_[0].spatial == kOneQuarterSpatialUniform &&
down_action_history_[0].temporal == kNoChangeTemporal &&
framerate_level_ != kFrameRateLow) {
action_.spatial = kNoChangeSpatial;
action_.temporal = kOneHalfTemporal;
}
// Never use temporal action if number of temporal layers is above 2.
if (num_layers_ > 2) {
if (action_.temporal != kNoChangeTemporal) {
action_.spatial = kOneHalfSpatialUniform;
}
action_.temporal = kNoChangeTemporal;
}
}
void VCMQmResolution::ConvertSpatialFractionalToWhole() {
// If 3/4 spatial is selected, check if there has been another 3/4,
// and if so, combine them into 1/2. 1/2 scaling is more efficient than 9/16.
// Note we define 3/4x3/4 spatial as kOneHalfSpatialUniform.
if (action_.spatial == kOneHalfSpatialUniform) {
bool found = false;
int isel = kDownActionHistorySize;
for (int i = 0; i < kDownActionHistorySize; ++i) {
if (down_action_history_[i].spatial == kOneHalfSpatialUniform) {
isel = i;
found = true;
break;
}
}
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;
}
}
} }
void VCMQmResolution::CheckForEvenFrameSize() { void VCMQmResolution::CheckForEvenFrameSize() {
@ -669,11 +767,11 @@ void VCMQmResolution::ConstrainAmountOfDownSampling() {
// No spatial sampling if current frame size is too small (QCIF), // No spatial sampling if current frame size is too small (QCIF),
// or if the amount of spatial down-sampling will be too much. // or if the amount of spatial down-sampling will be too much.
float new_dec_factor_spatial = state_dec_factor_spatial_ * float new_dec_factor_spatial = state_dec_factor_spatial_ *
qm_->spatial_width_fact * qm_->spatial_width_fact * qm_->spatial_height_fact;
qm_->spatial_height_fact;
if ((width_ * height_) <= kMinImageSize || if ((width_ * height_) <= kMinImageSize ||
new_dec_factor_spatial > kMaxSpatialDown) { new_dec_factor_spatial > kMaxSpatialDown) {
action_.spatial = kNoChangeSpatial; action_.spatial = kNoChangeSpatial;
qm_->change_resolution_spatial = false;
qm_->spatial_width_fact = 1.0f; qm_->spatial_width_fact = 1.0f;
qm_->spatial_height_fact = 1.0f; qm_->spatial_height_fact = 1.0f;
} }
@ -683,6 +781,7 @@ void VCMQmResolution::ConstrainAmountOfDownSampling() {
if (avg_incoming_framerate_ <= kMinFrameRate || if (avg_incoming_framerate_ <= kMinFrameRate ||
new_dec_factor_temp >= kMaxTempDown) { new_dec_factor_temp >= kMaxTempDown) {
action_.temporal = kNoChangeTemporal; action_.temporal = kNoChangeTemporal;
qm_->change_resolution_temporal = false;
qm_->temporal_fact = 1.0f; qm_->temporal_fact = 1.0f;
} }
} }
@ -698,7 +797,7 @@ void VCMQmResolution::PickSpatialOrTemporal() {
} }
} }
// TODO(marpan): Update this when we allow for 1/2 spatial down-sampling. // TODO(marpan): Update when we allow for directional spatial down-sampling.
void VCMQmResolution::SelectSpatialDirectionMode(float transition_rate) { void VCMQmResolution::SelectSpatialDirectionMode(float transition_rate) {
// Default is 4/3x4/3 // Default is 4/3x4/3
// For bit rates well below transitional rate, we select 2x2. // For bit rates well below transitional rate, we select 2x2.

View File

@ -23,15 +23,23 @@ struct VideoContentMetrics;
struct VCMResolutionScale { struct VCMResolutionScale {
VCMResolutionScale() VCMResolutionScale()
: spatial_width_fact(1.0f), : codec_width(640),
codec_height(480),
frame_rate(30.0f),
spatial_width_fact(1.0f),
spatial_height_fact(1.0f), spatial_height_fact(1.0f),
temporal_fact(1.0f), temporal_fact(1.0f),
change_resolution(false) { change_resolution_spatial(false),
change_resolution_temporal(false) {
} }
uint16_t codec_width;
uint16_t codec_height;
float frame_rate;
float spatial_width_fact; float spatial_width_fact;
float spatial_height_fact; float spatial_height_fact;
float temporal_fact; float temporal_fact;
bool change_resolution; bool change_resolution_spatial;
bool change_resolution_temporal;
}; };
enum ImageType { enum ImageType {
@ -50,7 +58,14 @@ enum ImageType {
const uint32_t kSizeOfImageType[kNumImageTypes] = const uint32_t kSizeOfImageType[kNumImageTypes] =
{ 25344, 57024, 76800, 101376, 172800, 307200, 518400, 921600, 2073600 }; { 25344, 57024, 76800, 101376, 172800, 307200, 518400, 921600, 2073600 };
enum LevelClass { enum FrameRateLevelClass {
kFrameRateLow,
kFrameRateMiddle1,
kFrameRateMiddle2,
kFrameRateHigh
};
enum ContentLevelClass {
kLow, kLow,
kHigh, kHigh,
kDefault kDefault
@ -66,7 +81,7 @@ struct VCMContFeature {
level = kDefault; level = kDefault;
} }
float value; float value;
LevelClass level; ContentLevelClass level;
}; };
enum UpDownAction { enum UpDownAction {
@ -146,7 +161,7 @@ class VCMQmMethod {
ImageType FindClosestImageType(uint16_t width, uint16_t height); ImageType FindClosestImageType(uint16_t width, uint16_t height);
// Get the frame rate level. // Get the frame rate level.
LevelClass FrameRateLevel(float frame_rate); FrameRateLevelClass FrameRateLevel(float frame_rate);
protected: protected:
// Content Data. // Content Data.
@ -155,12 +170,14 @@ class VCMQmMethod {
// Encoder frame sizes and native frame sizes. // Encoder frame sizes and native frame sizes.
uint16_t width_; uint16_t width_;
uint16_t height_; uint16_t height_;
float user_frame_rate_;
uint16_t native_width_; uint16_t native_width_;
uint16_t native_height_; uint16_t native_height_;
float native_frame_rate_;
float aspect_ratio_; float aspect_ratio_;
// Image type and frame rate leve, for the current encoder resolution. // Image type and frame rate leve, for the current encoder resolution.
ImageType image_type_; ImageType image_type_;
LevelClass framerate_level_; FrameRateLevelClass framerate_level_;
// Content class data. // Content class data.
VCMContFeature motion_; VCMContFeature motion_;
VCMContFeature spatial_; VCMContFeature spatial_;
@ -195,7 +212,7 @@ class VCMQmResolution : public VCMQmMethod {
int num_layers); int num_layers);
// Update the encoder frame size. // Update the encoder frame size.
void UpdateCodecFrameSize(uint16_t width, uint16_t height); void UpdateCodecParameters(float frame_rate, uint16_t width, uint16_t height);
// Update with actual bit rate (size of the latest encoded frame) // Update with actual bit rate (size of the latest encoded frame)
// and frame type, after every encoded frame. // and frame type, after every encoded frame.
@ -214,6 +231,7 @@ class VCMQmResolution : public VCMQmMethod {
// Output: the spatial and/or temporal scale change. // Output: the spatial and/or temporal scale change.
int SelectResolution(VCMResolutionScale** qm); int SelectResolution(VCMResolutionScale** qm);
private:
// Set the default resolution action. // Set the default resolution action.
void SetDefaultAction(); void SetDefaultAction();
@ -248,12 +266,18 @@ class VCMQmResolution : public VCMQmMethod {
// Update the down-sampling state. // Update the down-sampling state.
void UpdateDownsamplingState(UpDownAction up_down); void UpdateDownsamplingState(UpDownAction up_down);
// Update the codec frame size and frame rate.
void UpdateCodecResolution();
// Return a state based on average target rate relative transition rate. // Return a state based on average target rate relative transition rate.
uint8_t RateClass(float transition_rate); uint8_t RateClass(float transition_rate);
// Adjust the action selected from the table. // Adjust the action selected from the table.
void AdjustAction(); void AdjustAction();
// 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. // Check if the new frame sizes are still divisible by 2.
void CheckForEvenFrameSize(); void CheckForEvenFrameSize();
@ -273,13 +297,11 @@ class VCMQmResolution : public VCMQmMethod {
// Select the directional (1x2 or 2x1) spatial down-sampling action. // Select the directional (1x2 or 2x1) spatial down-sampling action.
void SelectSpatialDirectionMode(float transition_rate); void SelectSpatialDirectionMode(float transition_rate);
private:
enum { kDownActionHistorySize = 10}; enum { kDownActionHistorySize = 10};
VCMResolutionScale* qm_; VCMResolutionScale* qm_;
// Encoder rate control parameters. // Encoder rate control parameters.
float target_bitrate_; float target_bitrate_;
float user_framerate_;
float incoming_framerate_; float incoming_framerate_;
float per_frame_bandwidth_; float per_frame_bandwidth_;
float buffer_level_; float buffer_level_;

View File

@ -53,7 +53,6 @@ const float kPacketLossThr = 0.1f;
// Factor for reducing transitonal bitrate under packet loss. // Factor for reducing transitonal bitrate under packet loss.
const float kPacketLossRateFac = 1.0f; const float kPacketLossRateFac = 1.0f;
// Maximum possible transitional rate for down-sampling: // Maximum possible transitional rate for down-sampling:
// (units in kbps), for 30fps. // (units in kbps), for 30fps.
const uint16_t kMaxRateQm[9] = { const uint16_t kMaxRateQm[9] = {
@ -69,10 +68,11 @@ const uint16_t kMaxRateQm[9] = {
}; };
// Frame rate scale for maximum transition rate. // Frame rate scale for maximum transition rate.
const float kFrameRateFac[3] = { const float kFrameRateFac[4] = {
0.7f, // L 0.5f, // Low
1.0f, // H 0.7f, // Middle level 1
0.8f // D 0.85f, // Middle level 2
1.0f, // High
}; };
// Scale for transitional rate: based on content class // Scale for transitional rate: based on content class
@ -180,7 +180,7 @@ const uint8_t kTemporalAction[27] = {
// Control the total amount of down-sampling allowed. // Control the total amount of down-sampling allowed.
const float kMaxSpatialDown = 8.0f; const float kMaxSpatialDown = 8.0f;
const float kMaxTempDown = 4.0f; const float kMaxTempDown = 4.0f;
const float kMaxDownSample = 16.0f; const float kMaxDownSample = 12.0f;
// Minimum image size for a spatial down-sampling. // Minimum image size for a spatial down-sampling.
const int kMinImageSize= 176 * 144; const int kMinImageSize= 176 * 144;
@ -199,6 +199,7 @@ const int kMinFrameRate = 8;
// Thresholds for frame rate: // Thresholds for frame rate:
const int kLowFrameRate = 10; const int kLowFrameRate = 10;
const int kMiddleFrameRate = 15;
const int kHighFrameRate = 25; const int kHighFrameRate = 25;
// Thresholds for motion: motion level is from NFD // Thresholds for motion: motion level is from NFD

View File

@ -62,7 +62,10 @@ class QmSelectTest : public ::testing::Test {
bool IsSelectedActionCorrect(VCMResolutionScale* qm_scale, bool IsSelectedActionCorrect(VCMResolutionScale* qm_scale,
float fac_width, float fac_width,
float fac_height, float fac_height,
float fac_temp); float fac_temp,
uint16_t new_width,
uint16_t new_height,
float new_frame_rate);
void TearDown() { void TearDown() {
delete qm_resolution_; delete qm_resolution_;
@ -84,7 +87,8 @@ TEST_F(QmSelectTest, HandleInputs) {
qm_resolution_->UpdateContent(content_metrics); qm_resolution_->UpdateContent(content_metrics);
// Content metrics are NULL: Expect success and no down-sampling action. // Content metrics are NULL: Expect success and no down-sampling action.
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0, 1.0, 1.0)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0, 1.0, 1.0, 640, 480,
30.0f));
} }
// No down-sampling action at high rates. // No down-sampling action at high rates.
@ -95,7 +99,7 @@ TEST_F(QmSelectTest, NoActionHighRate) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -111,7 +115,8 @@ TEST_F(QmSelectTest, NoActionHighRate) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(0, qm_resolution_->ComputeContentClass()); EXPECT_EQ(0, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 640, 480,
30.0f));
} }
// Rate is well below transition, down-sampling action is taken, // Rate is well below transition, down-sampling action is taken,
@ -123,7 +128,7 @@ TEST_F(QmSelectTest, DownActionLowRate) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -140,35 +145,40 @@ TEST_F(QmSelectTest, DownActionLowRate) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
30.0f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Low motion, low spatial: 2/3 temporal is expected. // Low motion, low spatial: 2/3 temporal is expected.
UpdateQmContentData(kTemporalLow, kSpatialLow, kSpatialLow, kSpatialLow); UpdateQmContentData(kTemporalLow, kSpatialLow, kSpatialLow, kSpatialLow);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(0, qm_resolution_->ComputeContentClass()); EXPECT_EQ(0, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 640, 480,
20.5f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Medium motion, low spatial: 2x2 spatial expected. // Medium motion, low spatial: 2x2 spatial expected.
UpdateQmContentData(kTemporalMedium, kSpatialLow, kSpatialLow, kSpatialLow); UpdateQmContentData(kTemporalMedium, kSpatialLow, kSpatialLow, kSpatialLow);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(6, qm_resolution_->ComputeContentClass()); EXPECT_EQ(6, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
30.0f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// High motion, high spatial: 1/2 temporal expected. // High motion, high spatial: 2/3 temporal expected.
UpdateQmContentData(kTemporalHigh, kSpatialHigh, kSpatialHigh, kSpatialHigh); UpdateQmContentData(kTemporalHigh, kSpatialHigh, kSpatialHigh, kSpatialHigh);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(4, qm_resolution_->ComputeContentClass()); EXPECT_EQ(4, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 640, 480,
20.5f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Low motion, high spatial: 1/2 temporal expected. // Low motion, high spatial: 1/2 temporal expected.
UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh); UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480,
15.5f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Medium motion, high spatial: 1/2 temporal expected. // Medium motion, high spatial: 1/2 temporal expected.
@ -176,7 +186,8 @@ TEST_F(QmSelectTest, DownActionLowRate) {
kSpatialHigh); kSpatialHigh);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(7, qm_resolution_->ComputeContentClass()); EXPECT_EQ(7, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480,
15.5f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// High motion, medium spatial: 2x2 spatial expected. // High motion, medium spatial: 2x2 spatial expected.
@ -184,7 +195,9 @@ TEST_F(QmSelectTest, DownActionLowRate) {
kSpatialMedium); kSpatialMedium);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(5, qm_resolution_->ComputeContentClass()); EXPECT_EQ(5, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); // Target frame rate for frame dropper should be the same as previous == 15.
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
30.0f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Low motion, medium spatial: high frame rate, so 1/2 temporal expected. // Low motion, medium spatial: high frame rate, so 1/2 temporal expected.
@ -192,7 +205,8 @@ TEST_F(QmSelectTest, DownActionLowRate) {
kSpatialMedium); kSpatialMedium);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(2, qm_resolution_->ComputeContentClass()); EXPECT_EQ(2, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480,
15.5f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Medium motion, medium spatial: high frame rate, so 1/2 temporal expected. // Medium motion, medium spatial: high frame rate, so 1/2 temporal expected.
@ -200,7 +214,8 @@ TEST_F(QmSelectTest, DownActionLowRate) {
kSpatialMedium); kSpatialMedium);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(8, qm_resolution_->ComputeContentClass()); EXPECT_EQ(8, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480,
15.5f));
} }
// Rate mis-match is high, and we have over-shooting. // Rate mis-match is high, and we have over-shooting.
@ -212,7 +227,7 @@ TEST_F(QmSelectTest, DownActionHighRateMMOvershoot) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -230,14 +245,15 @@ TEST_F(QmSelectTest, DownActionHighRateMMOvershoot) {
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f, EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f,
1.0f)); 1.0f, 480, 360, 30.0f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Low motion, high spatial // Low motion, high spatial
UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh); UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 640, 480,
20.5f));
} }
// Rate mis-match is high, target rate is below max for down-sampling, // Rate mis-match is high, target rate is below max for down-sampling,
@ -249,7 +265,7 @@ TEST_F(QmSelectTest, NoActionHighRateMMUndershoot) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -266,14 +282,16 @@ TEST_F(QmSelectTest, NoActionHighRateMMUndershoot) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 640, 480,
30.0f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Low motion, high spatial // Low motion, high spatial
UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh); UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 640, 480,
30.0f));
} }
// Buffer is underflowing, and target rate is below max for down-sampling, // Buffer is underflowing, and target rate is below max for down-sampling,
@ -285,7 +303,7 @@ TEST_F(QmSelectTest, DownActionBufferUnderflow) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update with encoded size over a number of frames. // Update with encoded size over a number of frames.
@ -308,14 +326,15 @@ TEST_F(QmSelectTest, DownActionBufferUnderflow) {
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f, EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f,
1.0f)); 1.0f, 480, 360, 30.0f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Low motion, high spatial // Low motion, high spatial
UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh); UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 640, 480,
20.5f));
} }
// Target rate is below max for down-sampling, but buffer level is stable, // Target rate is below max for down-sampling, but buffer level is stable,
@ -327,7 +346,7 @@ TEST_F(QmSelectTest, NoActionBufferStable) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update with encoded size over a number of frames. // Update with encoded size over a number of frames.
@ -349,14 +368,16 @@ TEST_F(QmSelectTest, NoActionBufferStable) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 640, 480,
30.0f));
qm_resolution_->ResetDownSamplingState(); qm_resolution_->ResetDownSamplingState();
// Low motion, high spatial // Low motion, high spatial
UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh); UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 640, 480,
30.0f));
} }
// Very low rate, but no spatial down-sampling below some size (QCIF). // Very low rate, but no spatial down-sampling below some size (QCIF).
@ -367,7 +388,7 @@ TEST_F(QmSelectTest, LimitDownSpatialAction) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 176; uint16_t codec_width = 176;
uint16_t codec_height = 144; uint16_t codec_height = 144;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(0, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(0, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -384,7 +405,8 @@ TEST_F(QmSelectTest, LimitDownSpatialAction) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 176, 144,
30.0f));
} }
// Very low rate, but no frame reduction below some frame_rate (8fps). // Very low rate, but no frame reduction below some frame_rate (8fps).
@ -395,7 +417,7 @@ TEST_F(QmSelectTest, LimitDownTemporalAction) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(8.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -413,7 +435,8 @@ TEST_F(QmSelectTest, LimitDownTemporalAction) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(2, qm_resolution_->ComputeContentClass()); EXPECT_EQ(2, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 640, 480,
8.0f));
} }
// Two stages: spatial down-sample and then back up spatially, // Two stages: spatial down-sample and then back up spatially,
@ -425,7 +448,7 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -442,11 +465,12 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); 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.
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
qm_resolution_->UpdateCodecFrameSize(320, 240); qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
int target_rate2[] = {400, 400, 400, 400, 400}; int target_rate2[] = {400, 400, 400, 400, 400};
@ -457,7 +481,8 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) {
fraction_lost2, 5); fraction_lost2, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f, 640, 480,
30.0f));
} }
// Two stages: spatial down-sample and then back up spatially, since encoder // Two stages: spatial down-sample and then back up spatially, since encoder
@ -469,7 +494,7 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -486,11 +511,12 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
30.0f));
// Reset rates and simulate under-shooting scenario.: expect to go back up. // Reset rates and simulate under-shooting scenario.: expect to go back up.
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
qm_resolution_->UpdateCodecFrameSize(320, 240); qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
int target_rate2[] = {200, 200, 200, 200, 200}; int target_rate2[] = {200, 200, 200, 200, 200};
@ -501,7 +527,8 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) {
fraction_lost2, 5); fraction_lost2, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f, 640, 480,
30.0f));
} }
// Two stages: spatial down-sample and then no action to go up, // Two stages: spatial down-sample and then no action to go up,
@ -513,7 +540,7 @@ TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -530,11 +557,12 @@ TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
30.0f));
// Reset and simulate large rate mis-match: expect no action to go back up. // Reset and simulate large rate mis-match: expect no action to go back up.
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
qm_resolution_->UpdateCodecFrameSize(320, 240); qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
int target_rate2[] = {400, 400, 400, 400, 400}; int target_rate2[] = {400, 400, 400, 400, 400};
@ -545,8 +573,10 @@ TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) {
fraction_lost2, 5); fraction_lost2, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 320, 240,
30.0f));
} }
// Two stages: temporally down-sample and then back up temporally, // Two stages: temporally down-sample and then back up temporally,
// as rate as increased. // as rate as increased.
TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) { TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) {
@ -556,91 +586,7 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
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 incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
fraction_lost, 3);
// 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));
// Reset rates and go up in rate: expect to go back up.
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate2[] = {400, 400, 400, 400, 400};
int encoder_sent_rate2[] = {400, 400, 400, 400, 400};
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);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f));
}
// 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);
// Update with encoder frame size.
uint16_t codec_width = 640;
uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
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 incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
fraction_lost, 3);
// 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));
// 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 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};
UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
fraction_lost2, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f));
}
// 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);
// Update with encoder frame size.
uint16_t codec_width = 640;
uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -657,9 +603,98 @@ TEST_F(QmSelectTest, 2StageDownTemporalNoActionUp) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480,
15.5f));
// Reset rates and go up in rate: expect to go back up.
qm_resolution_->ResetRates();
// Update rates for a sequence of intervals.
int target_rate2[] = {400, 400, 400, 400, 400};
int encoder_sent_rate2[] = {400, 400, 400, 400, 400};
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);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f, 640, 480,
30.0f));
}
// 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);
// Update with encoder frame size.
uint16_t codec_width = 640;
uint16_t codec_height = 480;
qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
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 incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
fraction_lost, 3);
// 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, 640, 480,
15.5f));
// 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 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};
UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
fraction_lost2, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f, 640, 480,
30.0f));
}
// 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);
// Update with encoder frame size.
uint16_t codec_width = 640;
uint16_t codec_height = 480;
qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
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 incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
fraction_lost, 3);
// 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, 1, 2, 640, 480, 15.5f));
// Reset and simulate large rate mis-match: expect no action to go back up. // Reset and simulate large rate mis-match: expect no action to go back up.
qm_resolution_->UpdateCodecParameters(15.0f, codec_width, codec_height);
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
int target_rate2[] = {600, 600, 600, 600, 600}; int target_rate2[] = {600, 600, 600, 600, 600};
@ -670,7 +705,8 @@ TEST_F(QmSelectTest, 2StageDownTemporalNoActionUp) {
fraction_lost2, 5); fraction_lost2, 5);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 640, 480,
15.0f));
} }
// 3 stages: spatial down-sample, followed by temporal down-sample, // 3 stages: spatial down-sample, followed by temporal down-sample,
// and then go up to full state, as encoding rate has increased. // and then go up to full state, as encoding rate has increased.
@ -681,7 +717,7 @@ TEST_F(QmSelectTest, 3StageDownSpatialTemporlaUpSpatialTemporal) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -698,10 +734,11 @@ TEST_F(QmSelectTest, 3StageDownSpatialTemporlaUpSpatialTemporal) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
30.0f));
// Change content data: expect temporal down-sample. // Change content data: expect temporal down-sample.
qm_resolution_->UpdateCodecFrameSize(320, 240); qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
// Update content: motion level, and 3 spatial prediction errors. // Update content: motion level, and 3 spatial prediction errors.
@ -710,7 +747,8 @@ TEST_F(QmSelectTest, 3StageDownSpatialTemporlaUpSpatialTemporal) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 320, 240,
15.5f));
// Reset rates and go high up in rate: expect to go back up both spatial // Reset rates and go high up in rate: expect to go back up both spatial
// and temporally. // and temporally.
@ -726,7 +764,8 @@ TEST_F(QmSelectTest, 3StageDownSpatialTemporlaUpSpatialTemporal) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 0.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 0.5f, 640, 480,
30.0f));
} }
// No down-sampling below some total amount. // No down-sampling below some total amount.
@ -737,7 +776,7 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 1280; uint16_t codec_width = 1280;
uint16_t codec_height = 720; uint16_t codec_height = 720;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(7, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(7, qm_resolution_->GetImageType(codec_width, codec_height));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -754,11 +793,12 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); 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)
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
qm_resolution_->UpdateCodecFrameSize(640, 360); qm_resolution_->UpdateCodecParameters(30.0f, 640, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(640, 360)); EXPECT_EQ(4, qm_resolution_->GetImageType(640, 360));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
int target_rate2[] = {80, 80, 80, 80, 80}; int target_rate2[] = {80, 80, 80, 80, 80};
@ -776,13 +816,13 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
EXPECT_EQ(5, qm_resolution_->ComputeContentClass()); EXPECT_EQ(5, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f, EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f,
1.0f)); 1.0f, 480, 270, 30.0f));
// Reset and go to very low rate: no action should be taken, // Reset and go to very low rate: no action should be taken,
// we went down too much already. // we went down too much already.
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
qm_resolution_->UpdateCodecFrameSize(320, 180); qm_resolution_->UpdateCodecParameters(30.0f, 480, 270);
EXPECT_EQ(1, qm_resolution_->GetImageType(320, 180)); EXPECT_EQ(3, qm_resolution_->GetImageType(480, 270));
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
int target_rate3[] = {10, 10, 10, 10, 10}; int target_rate3[] = {10, 10, 10, 10, 10};
int encoder_sent_rate3[] = {10, 10, 10, 10, 10}; int encoder_sent_rate3[] = {10, 10, 10, 10, 10};
@ -793,7 +833,8 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(5, qm_resolution_->ComputeContentClass()); EXPECT_EQ(5, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 480, 270,
30.0f));
} }
// Multiple down-sampling stages and then undo all of them. // Multiple down-sampling stages and then undo all of them.
@ -807,7 +848,7 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Go down spatial 3/4x3/4. // Go down spatial 3/4x3/4.
@ -826,9 +867,9 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
EXPECT_EQ(6, qm_resolution_->ComputeContentClass()); EXPECT_EQ(6, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f, EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f,
1.0f)); 1.0f, 480, 360, 30.0f));
// Go down 1/2 temporal. // Go down 1/2 temporal.
qm_resolution_->UpdateCodecFrameSize(480, 360); qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
int target_rate2[] = {100, 100, 100, 100, 100}; int target_rate2[] = {100, 100, 100, 100, 100};
@ -844,9 +885,11 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 480, 360,
15.5f));
// Go down 1/2x1/2 spatial. // Go down 1/2x1/2 spatial.
qm_resolution_->UpdateCodecParameters(15.0f, 480, 360);
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
int target_rate3[] = {50, 50, 50, 50, 50}; int target_rate3[] = {50, 50, 50, 50, 50};
int encoder_sent_rate3[] = {50, 50, 50, 50, 50}; int encoder_sent_rate3[] = {50, 50, 50, 50, 50};
@ -861,14 +904,15 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 240, 180,
15.0f));
// Reset rates and go high up in rate: expect to go up: // Reset rates and go high up in rate: expect to go up:
// should go up first: 1/2x1x2 and 1/2 temporally, // should go up first: 1/2x1x2 and 1/2 temporally,
// and second: 3/4x3/4 spatial. // and second: 3/4x3/4 spatial.
// Go up 1/2x1/2 spatially and 1/2 temporally // Go up 1/2x1/2 spatially and 1/2 temporally
qm_resolution_->UpdateCodecFrameSize(240, 180); qm_resolution_->UpdateCodecParameters(15.0f, 240, 180);
EXPECT_EQ(1, qm_resolution_->GetImageType(240, 180)); EXPECT_EQ(1, qm_resolution_->GetImageType(240, 180));
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -882,10 +926,11 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 0.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 0.5f, 480, 360,
30.0f));
// Go up 3/4x3/4 spatially. // Go up 3/4x3/4 spatially.
qm_resolution_->UpdateCodecFrameSize(480, 360); qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
@ -900,7 +945,7 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) {
EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f,
1.0f)); 1.0f, 640, 480, 30.0f));
} }
// Multiple down-sampling and up-sample stages, with partial undoing. // Multiple down-sampling and up-sample stages, with partial undoing.
@ -914,7 +959,7 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Go down 1/2x1/2 spatial. // Go down 1/2x1/2 spatial.
@ -932,10 +977,11 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(6, qm_resolution_->ComputeContentClass()); EXPECT_EQ(6, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
30.0f));
// Go down 2/3 temporal. // Go down 2/3 temporal.
qm_resolution_->UpdateCodecFrameSize(320, 240); qm_resolution_->UpdateCodecParameters(30.0f, 320, 240);
EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240));
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
int target_rate2[] = {80, 80, 80, 80, 80}; int target_rate2[] = {80, 80, 80, 80, 80};
@ -952,9 +998,11 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(7, qm_resolution_->ComputeContentClass()); EXPECT_EQ(7, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 320, 240,
20.5f));
// Go up 1/2x1/2 spatially. // Go up 1/2x1/2 spatially.
qm_resolution_->UpdateCodecParameters(20.0f, 320, 240);
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
// Update rates for a sequence of intervals. // Update rates for a sequence of intervals.
int target_rate3[] = {300, 300, 300, 300, 300}; int target_rate3[] = {300, 300, 300, 300, 300};
@ -967,10 +1015,11 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(7, qm_resolution_->ComputeContentClass()); EXPECT_EQ(7, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f, 640, 480,
20.0f));
// Go down 1/2 temporal. // Go down 1/2 temporal.
qm_resolution_->UpdateCodecFrameSize(640, 480); qm_resolution_->UpdateCodecParameters(20.0f, 640, 480);
EXPECT_EQ(5, qm_resolution_->GetImageType(640, 480)); EXPECT_EQ(5, qm_resolution_->GetImageType(640, 480));
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
int target_rate4[] = {100, 100, 100, 100, 100}; int target_rate4[] = {100, 100, 100, 100, 100};
@ -986,7 +1035,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480,
10.5f));
// Go up 1/2 temporal. // Go up 1/2 temporal.
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
@ -1001,7 +1051,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f, 640, 480,
20.5f));
} }
// Multiple down-sampling and up-sample stages, with partial undoing. // Multiple down-sampling and up-sample stages, with partial undoing.
@ -1015,7 +1066,7 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
// Update with encoder frame size. // Update with encoder frame size.
uint16_t codec_width = 640; uint16_t codec_width = 640;
uint16_t codec_height = 480; uint16_t codec_height = 480;
qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height); qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// Go down 3/4x3/4 spatial. // Go down 3/4x3/4 spatial.
@ -1034,10 +1085,10 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
EXPECT_EQ(6, qm_resolution_->ComputeContentClass()); EXPECT_EQ(6, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f, EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f,
1.0f)); 1.0f, 480, 360, 30.0f));
// Go down 1/2 temporal. // Go down 1/2 temporal.
qm_resolution_->UpdateCodecFrameSize(480, 360); qm_resolution_->UpdateCodecParameters(30.0f, 480, 360);
EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360));
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
int target_rate2[] = {100, 100, 100, 100, 100}; int target_rate2[] = {100, 100, 100, 100, 100};
@ -1053,7 +1104,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 480, 360,
15.5f));
// Go up 1/2 temporal. // Go up 1/2 temporal.
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
@ -1068,15 +1120,16 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f, 480, 360,
30.0f));
// Go down 2/3 temporal. // Go down 2/3 temporal.
qm_resolution_->UpdateCodecFrameSize(640, 480); qm_resolution_->UpdateCodecParameters(30.0f, 640, 480);
EXPECT_EQ(5, qm_resolution_->GetImageType(640, 480)); EXPECT_EQ(5, qm_resolution_->GetImageType(640, 480));
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
int target_rate4[] = {150, 150, 150, 150, 150}; int target_rate4[] = {200, 200, 200, 200, 200};
int encoder_sent_rate4[] = {150, 150, 150, 150, 150}; int encoder_sent_rate4[] = {200, 200, 200, 200, 200};
int incoming_frame_rate4[] = {20, 20, 20, 20, 20}; int incoming_frame_rate4[] = {30, 30, 30, 30, 30};
uint8_t fraction_lost4[] = {30, 30, 30, 30, 30}; uint8_t fraction_lost4[] = {30, 30, 30, 30, 30};
UpdateQmRateData(target_rate4, encoder_sent_rate4, incoming_frame_rate4, UpdateQmRateData(target_rate4, encoder_sent_rate4, incoming_frame_rate4,
fraction_lost4, 5); fraction_lost4, 5);
@ -1088,7 +1141,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(7, qm_resolution_->ComputeContentClass()); EXPECT_EQ(7, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 640, 480,
20.5f));
// Go up 2/3 temporal. // Go up 2/3 temporal.
qm_resolution_->ResetRates(); qm_resolution_->ResetRates();
@ -1103,7 +1157,58 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) {
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(7, qm_resolution_->ComputeContentClass()); EXPECT_EQ(7, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f / 3.0f)); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f / 3.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);
// Update with encoder frame size.
uint16_t codec_width = 640;
uint16_t codec_height = 480;
qm_resolution_->UpdateCodecParameters(30.0f, codec_width, codec_height);
EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
// 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 incoming_frame_rate[] = {30, 30, 30};
uint8_t fraction_lost[] = {10, 10, 10};
UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
fraction_lost, 3);
// Update content: motion level, and 3 spatial prediction errors.
// Medium motion, low spatial.
UpdateQmContentData(kTemporalMedium, kSpatialLow, kSpatialLow, kSpatialLow);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(6, 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, 360, 30.0f));
// Set rates to go down another 3/4 spatial. Should be converted ton 1/2.
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 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.
// Medium motion, low spatial.
UpdateQmContentData(kTemporalMedium, kSpatialLow, kSpatialLow, kSpatialLow);
EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
EXPECT_EQ(6, qm_resolution_->ComputeContentClass());
EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240,
30.0f));
} }
void QmSelectTest::InitQmNativeData(float initial_bit_rate, void QmSelectTest::InitQmNativeData(float initial_bit_rate,
@ -1160,10 +1265,16 @@ void QmSelectTest::UpdateQmRateData(int* target_rate,
bool QmSelectTest::IsSelectedActionCorrect(VCMResolutionScale* qm_scale, bool QmSelectTest::IsSelectedActionCorrect(VCMResolutionScale* qm_scale,
float fac_width, float fac_width,
float fac_height, float fac_height,
float fac_temp) { float fac_temp,
uint16_t new_width,
uint16_t new_height,
float new_frame_rate) {
if (qm_scale->spatial_width_fact == fac_width && if (qm_scale->spatial_width_fact == fac_width &&
qm_scale->spatial_height_fact == fac_height && qm_scale->spatial_height_fact == fac_height &&
qm_scale->temporal_fact == fac_temp) { qm_scale->temporal_fact == fac_temp &&
qm_scale->codec_width == new_width &&
qm_scale->codec_height == new_height &&
qm_scale->frame_rate == new_frame_rate) {
return true; return true;
} else { } else {
return false; return false;