From c5b392e9d6067459f7697cbf8772a5e3b0527afd Mon Sep 17 00:00:00 2001 From: "marpan@webrtc.org" Date: Fri, 29 Jun 2012 21:44:55 +0000 Subject: [PATCH] Updates t resolution adaptation (cama): -set image type when QM is reset. -fix for undoing two stages of spatial downsampling. -some adjustments and code clean-up. -updates to control parameters and unittest. Review URL: https://webrtc-codereview.appspot.com/641010 git-svn-id: http://webrtc.googlecode.com/svn/trunk@2473 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../video_coding/main/source/qm_select.cc | 235 ++++++---- .../video_coding/main/source/qm_select.h | 5 +- .../video_coding/main/source/qm_select_data.h | 76 ++-- .../main/source/qm_select_unittest.cc | 410 ++++++++++-------- 4 files changed, 409 insertions(+), 317 deletions(-) diff --git a/src/modules/video_coding/main/source/qm_select.cc b/src/modules/video_coding/main/source/qm_select.cc index ba1e3af02b..507becd33c 100644 --- a/src/modules/video_coding/main/source/qm_select.cc +++ b/src/modules/video_coding/main/source/qm_select.cc @@ -40,7 +40,6 @@ VCMQmMethod::~VCMQmMethod() { void VCMQmMethod::ResetQM() { aspect_ratio_ = 1.0f; - image_type_ = kVGA; motion_.Reset(); spatial_.Reset(); content_class_ = 0; @@ -137,11 +136,11 @@ ImageType VCMQmMethod::FindClosestImageType(uint16_t width, uint16_t height) { } FrameRateLevelClass VCMQmMethod::FrameRateLevel(float avg_framerate) { - if (avg_framerate < kLowFrameRate) { + if (avg_framerate <= kLowFrameRate) { return kFrameRateLow; - } else if (avg_framerate < kMiddleFrameRate) { + } else if (avg_framerate <= kMiddleFrameRate) { return kFrameRateMiddle1; - } else if (avg_framerate < kHighFrameRate) { + } else if (avg_framerate <= kHighFrameRate) { return kFrameRateMiddle2; } else { return kFrameRateHigh; @@ -165,7 +164,7 @@ void VCMQmResolution::ResetRates() { sum_rate_MM_ = 0.0f; sum_rate_MM_sgn_ = 0.0f; sum_packet_loss_ = 0.0f; - buffer_level_ = kOptBufferLevel * target_bitrate_; + buffer_level_ = kInitBufferLevel * target_bitrate_; frame_cnt_ = 0; frame_cnt_delta_ = 0; low_buffer_cnt_ = 0; @@ -185,7 +184,7 @@ void VCMQmResolution::Reset() { target_bitrate_ = 0.0f; incoming_framerate_ = 0.0f; buffer_level_ = 0.0f; - per_frame_bandwidth_ =0.0f; + per_frame_bandwidth_ = 0.0f; avg_target_rate_ = 0.0f; avg_incoming_framerate_ = 0.0f; avg_ratio_buffer_low_ = 0.0f; @@ -222,7 +221,7 @@ int VCMQmResolution::Initialize(float bitrate, native_frame_rate_ = user_framerate; num_layers_ = num_layers; // Initial buffer level. - buffer_level_ = kOptBufferLevel * target_bitrate_; + buffer_level_ = kInitBufferLevel * target_bitrate_; // Per-frame bandwidth. per_frame_bandwidth_ = target_bitrate_ / user_framerate; init_ = true; @@ -247,12 +246,13 @@ void VCMQmResolution::UpdateEncodedSize(int encoded_size, // Update the buffer level: // Note this is not the actual encoder buffer level. - // |buffer_level_| is reset to average value every time SelectResolution is + // |buffer_level_| is reset to an initial value after SelectResolution is // called, and does not account for frame dropping by encoder or VCM. buffer_level_ += per_frame_bandwidth_ - encoded_size_kbits; + // Counter for occurrences of low buffer level: // low/negative values means encoder is likely dropping frames. - if (buffer_level_ <= kPercBufferThr * kOptBufferLevel * target_bitrate_) { + if (buffer_level_ <= kPercBufferThr * kInitBufferLevel * target_bitrate_) { low_buffer_cnt_++; } } @@ -282,13 +282,12 @@ void VCMQmResolution::UpdateRates(float target_bitrate, sum_rate_MM_sgn_ += sgnDiff; // Update with the current new target and frame rate: - // these values are ones the encoder will use for the current/next ~1sec + // these values are ones the encoder will use for the current/next ~1sec. target_bitrate_ = target_bitrate; incoming_framerate_ = incoming_framerate; sum_incoming_framerate_ += incoming_framerate_; - // Update the per_frame_bandwidth: - // this is the per_frame_bw for the current/next ~1sec + // this is the per_frame_bw for the current/next ~1sec. per_frame_bandwidth_ = 0.0f; if (incoming_framerate_ > 0.0f) { per_frame_bandwidth_ = target_bitrate_ / incoming_framerate_; @@ -318,9 +317,16 @@ int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) { return VCM_OK; } + // Check conditions on down-sampling state. + assert(state_dec_factor_spatial_ >= 1.0f); + assert(state_dec_factor_temporal_ >= 1.0f); + assert(state_dec_factor_spatial_ <= kMaxSpatialDown); + assert(state_dec_factor_temporal_ <= kMaxTempDown); + assert(state_dec_factor_temporal_ * state_dec_factor_spatial_ <= + kMaxTotalDown); + // Compute content class for selection. content_class_ = ComputeContentClass(); - // Compute various rate quantities for selection. ComputeRatesForSelection(); @@ -341,13 +347,10 @@ int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) { } } - // Check for going down in resolution, only if current total amount of - // down-sampling state is below threshold. - if (state_dec_factor_temporal_ * state_dec_factor_spatial_ < kMaxDownSample) { - if (GoingDownResolution()) { - *qm = qm_; - return VCM_OK; - } + // Check for going down in resolution. + if (GoingDownResolution()) { + *qm = qm_; + return VCM_OK; } return VCM_OK; } @@ -423,9 +426,19 @@ void VCMQmResolution::ComputeEncoderState() { bool VCMQmResolution::GoingUpResolution() { // For going up, we check for undoing the previous down-sampling action. + float fac_width = kFactorWidthSpatial[down_action_history_[0].spatial]; float fac_height = kFactorHeightSpatial[down_action_history_[0].spatial]; float fac_temp = kFactorTemporal[down_action_history_[0].temporal]; + // For going up spatially, we allow for going up by 3/4x3/4 at each stage. + // So if the last spatial action was 1/2x1/2 it would be undone in 2 stages. + // Modify the fac_width/height for this case. + if (down_action_history_[0].spatial == kOneQuarterSpatialUniform) { + fac_width = kFactorWidthSpatial[kOneQuarterSpatialUniform] / + kFactorWidthSpatial[kOneHalfSpatialUniform]; + fac_height = kFactorHeightSpatial[kOneQuarterSpatialUniform] / + kFactorHeightSpatial[kOneHalfSpatialUniform]; + } // Check if we should go up both spatially and temporally. if (down_action_history_[0].spatial != kNoChangeSpatial && @@ -473,7 +486,6 @@ bool VCMQmResolution::ConditionForGoingUp(float fac_width, float scale_fac) { float estimated_transition_rate_up = GetTransitionRate(fac_width, fac_height, fac_temp, scale_fac); - // Go back up if: // 1) target rate is above threshold and current encoder state is stable, or // 2) encoder state is easy (encoder is significantly under-shooting target). @@ -490,7 +502,6 @@ bool VCMQmResolution::GoingDownResolution() { float estimated_transition_rate_down = GetTransitionRate(1.0f, 1.0f, 1.0f, 1.0f); float max_rate = kFrameRateFac[framerate_level_] * kMaxRateQm[image_type_]; - // Resolution reduction if: // (1) target rate is below transition rate, or // (2) encoder is in stressed state and target rate below a max threshold. @@ -539,18 +550,14 @@ bool VCMQmResolution::GoingDownResolution() { assert(false); } } - // Only allow for one action (spatial or temporal) at a given time. assert(action_.temporal == kNoChangeTemporal || action_.spatial == kNoChangeSpatial); - // Adjust cases not captured in tables, mainly based on frame rate. + // Adjust cases not captured in tables, mainly based on frame rate, and + // also check for odd frame sizes. AdjustAction(); - ConvertSpatialFractionalToWhole(); - - CheckForEvenFrameSize(); - // Update down-sampling state. if (action_.spatial != kNoChangeSpatial || action_.temporal != kNoChangeTemporal) { @@ -587,7 +594,6 @@ float VCMQmResolution::GetTransitionRate(float fac_width, // Scale factor for down-sampling transition threshold: // factor based on the content class and the image size. float scaleTransRate = kScaleTransRateQm[table_index]; - // Threshold bitrate for resolution action. return static_cast (scale_fac * scaleTransRate * max_rate); } @@ -596,13 +602,24 @@ void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) { if (up_down == kUpResolution) { qm_->spatial_width_fact = 1.0f / kFactorWidthSpatial[action_.spatial]; qm_->spatial_height_fact = 1.0f / kFactorHeightSpatial[action_.spatial]; + // If last spatial action was 1/2x1/2, we undo it in two steps, so the + // spatial scale factor in this first step is modified as (4.0/3.0 / 2.0). + if (action_.spatial == kOneQuarterSpatialUniform) { + qm_->spatial_width_fact = + 1.0f * kFactorWidthSpatial[kOneHalfSpatialUniform] / + kFactorWidthSpatial[kOneQuarterSpatialUniform]; + qm_->spatial_height_fact = + 1.0f * kFactorHeightSpatial[kOneHalfSpatialUniform] / + kFactorHeightSpatial[kOneQuarterSpatialUniform]; + } qm_->temporal_fact = 1.0f / kFactorTemporal[action_.temporal]; RemoveLastDownAction(); } else if (up_down == kDownResolution) { + ConstrainAmountOfDownSampling(); + ConvertSpatialFractionalToWhole(); qm_->spatial_width_fact = kFactorWidthSpatial[action_.spatial]; qm_->spatial_height_fact = kFactorHeightSpatial[action_.spatial]; qm_->temporal_fact = kFactorTemporal[action_.temporal]; - ConstrainAmountOfDownSampling(); InsertLatestDownAction(); } else { // This function should only be called if either the Up or Down action @@ -613,10 +630,6 @@ void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) { state_dec_factor_spatial_ = state_dec_factor_spatial_ * qm_->spatial_width_fact * qm_->spatial_height_fact; state_dec_factor_temporal_ = state_dec_factor_temporal_ * qm_->temporal_fact; - assert(state_dec_factor_spatial_ >= 1.0f); - assert(state_dec_factor_spatial_ <= kMaxSpatialDown); - assert(state_dec_factor_temporal_ >= 1.0f); - assert(state_dec_factor_temporal_ <= kMaxTempDown); } void VCMQmResolution::UpdateCodecResolution() { @@ -626,10 +639,11 @@ void VCMQmResolution::UpdateCodecResolution() { qm_->spatial_width_fact + 0.5f); qm_->codec_height = static_cast(height_ / qm_->spatial_height_fact + 0.5f); - // Size can never exceed native sizes. + // Size should not exceed native sizes. assert(qm_->codec_width <= native_width_); assert(qm_->codec_height <= native_height_); - // Size should be multiple of 2. + // New sizes should be multiple of 2, otherwise spatial should not have + // been selected. assert(qm_->codec_width % 2 == 0); assert(qm_->codec_height % 2 == 0); } @@ -652,34 +666,36 @@ uint8_t VCMQmResolution::RateClass(float transition_rate) { (avg_target_rate_ >= transition_rate ? 2 : 1); } +// TODO(marpan): Would be better to capture these frame rate adjustments by +// extending the table data (qm_select_data.h). void VCMQmResolution::AdjustAction() { - // If the spatial level is default state (neither low or high) and motion - // is not high, then safer to take frame rate reduction if the - // average incoming frame rate is high. + // If the spatial level is default state (neither low or high), motion level + // is not high, and spatial action was selected, switch to 2/3 frame rate + // reduction if the average incoming frame rate is high. if (spatial_.level == kDefault && motion_.level != kHigh && + action_.spatial != kNoChangeSpatial && framerate_level_ == kFrameRateHigh) { action_.spatial = kNoChangeSpatial; - action_.temporal = kOneHalfTemporal; + action_.temporal = kTwoThirdsTemporal; } - // If both motion and spatial level are low, and temporal down action - // was selected, switch to spatial 3/4x3/4 if the frame rate is low. + // If both motion and spatial level are low, and temporal down action was + // selected, switch to spatial 3/4x3/4 if the frame rate is not above the + // lower middle level (|kFrameRateMiddle1|). if (motion_.level == kLow && spatial_.level == kLow && - framerate_level_ == kFrameRateLow && + framerate_level_ <= kFrameRateMiddle1 && action_.temporal != kNoChangeTemporal) { action_.spatial = kOneHalfSpatialUniform; action_.temporal = kNoChangeTemporal; } - - // If too much spatial action, and temporal action has not yet been chosen, - // then change to temporal action if the average frame rate is not low. - if (action_.spatial == kOneQuarterSpatialUniform && + // If spatial action is selected, and there has been too much spatial + // reduction already (i.e., 1/4), then switch to temporal action if the + // average frame rate is not low. + if (action_.spatial != kNoChangeSpatial && down_action_history_[0].spatial == kOneQuarterSpatialUniform && - down_action_history_[0].temporal == kNoChangeTemporal && framerate_level_ != kFrameRateLow) { action_.spatial = kNoChangeSpatial; - action_.temporal = kOneHalfTemporal; + action_.temporal = kTwoThirdsTemporal; } - // Never use temporal action if number of temporal layers is above 2. if (num_layers_ > 2) { if (action_.temporal != kNoChangeTemporal) { @@ -687,6 +703,15 @@ void VCMQmResolution::AdjustAction() { } action_.temporal = kNoChangeTemporal; } + // If spatial action was selected, we need to make sure the frame sizes + // are multiples of two. Otherwise switch to 2/3 temporal. + if (action_.spatial != kNoChangeSpatial && + !EvenFrameSize()) { + action_.spatial = kNoChangeSpatial; + // Only one action (spatial or temporal) is allowed at a given time, so need + // to check whether temporal action is currently selected. + action_.temporal = kTwoThirdsTemporal; + } } void VCMQmResolution::ConvertSpatialFractionalToWhole() { @@ -704,42 +729,57 @@ void VCMQmResolution::ConvertSpatialFractionalToWhole() { } } if (found) { - // Update state for removing 3/4 spatial. - state_dec_factor_spatial_ = state_dec_factor_spatial_ / - (kFactorWidthSpatial[kOneHalfSpatialUniform] * - kFactorHeightSpatial[kOneHalfSpatialUniform]); - width_ = width_ * kFactorWidthSpatial[kOneHalfSpatialUniform]; - height_ = height_ * kFactorHeightSpatial[kOneHalfSpatialUniform]; - // Remove 3/4 from the history. - for (int i = isel; i < kDownActionHistorySize - 1; ++i) { - down_action_history_[i].spatial = down_action_history_[i + 1].spatial; - } - // Update current selection action to be 1/2x1/2 (=1/4) spatial. - action_.spatial = kOneQuarterSpatialUniform; + action_.spatial = kOneQuarterSpatialUniform; + state_dec_factor_spatial_ = state_dec_factor_spatial_ / + (kFactorWidthSpatial[kOneHalfSpatialUniform] * + kFactorHeightSpatial[kOneHalfSpatialUniform]); + // Check if switching to 1/2x1/2 (=1/4) spatial is allowed. + ConstrainAmountOfDownSampling(); + if (action_.spatial == kNoChangeSpatial) { + // Not allowed. Go back to 3/4x3/4 spatial. + action_.spatial = kOneHalfSpatialUniform; + state_dec_factor_spatial_ = state_dec_factor_spatial_ * + kFactorWidthSpatial[kOneHalfSpatialUniform] * + kFactorHeightSpatial[kOneHalfSpatialUniform]; + } else { + // Switching is allowed. Remove 3/4x3/4 from the history, and update + // the frame size. + for (int i = isel; i < kDownActionHistorySize - 1; ++i) { + down_action_history_[i].spatial = + down_action_history_[i + 1].spatial; + } + width_ = width_ * kFactorWidthSpatial[kOneHalfSpatialUniform]; + height_ = height_ * kFactorHeightSpatial[kOneHalfSpatialUniform]; + } } } } -void VCMQmResolution::CheckForEvenFrameSize() { - // If 3/4 is selected, check that the new frame size is still multiple of 2, - // otherwise take 1/2. +// Returns false if the new frame sizes, under the current spatial action, +// are not multiples of two. +bool VCMQmResolution::EvenFrameSize() { if (action_.spatial == kOneHalfSpatialUniform) { - if ((width_ * 3 / 4)%2 != 0 || (height_ * 3 / 4)%2 != 0) { - action_.spatial = kOneQuarterSpatialUniform; + if ((width_ * 3 / 4) % 2 != 0 || (height_ * 3 / 4) % 2 != 0) { + return false; + } + } else if (action_.spatial == kOneQuarterSpatialUniform) { + if ((width_ * 1 / 2) % 2 != 0 || (height_ * 1 / 2) % 2 != 0) { + return false; } } + return true; } void VCMQmResolution::InsertLatestDownAction() { if (action_.spatial != kNoChangeSpatial) { for (int i = kDownActionHistorySize - 1; i > 0; --i) { - down_action_history_[i].spatial = down_action_history_[i - 1].spatial; + down_action_history_[i].spatial = down_action_history_[i - 1].spatial; } down_action_history_[0].spatial = action_.spatial; } if (action_.temporal != kNoChangeTemporal) { for (int i = kDownActionHistorySize - 1; i > 0; --i) { - down_action_history_[i].temporal = down_action_history_[i - 1].temporal; + down_action_history_[i].temporal = down_action_history_[i - 1].temporal; } down_action_history_[0].temporal = action_.temporal; } @@ -747,14 +787,20 @@ void VCMQmResolution::InsertLatestDownAction() { void VCMQmResolution::RemoveLastDownAction() { if (action_.spatial != kNoChangeSpatial) { - for (int i = 0; i< kDownActionHistorySize - 1; ++i) { - down_action_history_[i].spatial = down_action_history_[i + 1].spatial; + // If the last spatial action was 1/2x1/2 we replace it with 3/4x3/4. + if (action_.spatial == kOneQuarterSpatialUniform) { + down_action_history_[0].spatial = kOneHalfSpatialUniform; + } else { + for (int i = 0; i < kDownActionHistorySize - 1; ++i) { + down_action_history_[i].spatial = down_action_history_[i + 1].spatial; + } + down_action_history_[kDownActionHistorySize - 1].spatial = + kNoChangeSpatial; } - down_action_history_[kDownActionHistorySize - 1].spatial = kNoChangeSpatial; } if (action_.temporal != kNoChangeTemporal) { - for (int i = 0; i< kDownActionHistorySize - 1; ++i) { - down_action_history_[i].temporal = down_action_history_[i + 1].temporal; + for (int i = 0; i < kDownActionHistorySize - 1; ++i) { + down_action_history_[i].temporal = down_action_history_[i + 1].temporal; } down_action_history_[kDownActionHistorySize - 1].temporal = kNoChangeTemporal; @@ -766,25 +812,42 @@ void VCMQmResolution::ConstrainAmountOfDownSampling() { // override the settings for too small image size and/or frame rate. // Also check the limit on current down-sampling states. - // No spatial sampling if current frame size is too small (QCIF), - // or if the amount of spatial down-sampling will be too much. + float spatial_width_fact = kFactorWidthSpatial[action_.spatial]; + float spatial_height_fact = kFactorHeightSpatial[action_.spatial]; + float temporal_fact = kFactorTemporal[action_.temporal]; float new_dec_factor_spatial = state_dec_factor_spatial_ * - qm_->spatial_width_fact * qm_->spatial_height_fact; + spatial_width_fact * spatial_height_fact; + float new_dec_factor_temp = state_dec_factor_temporal_ * temporal_fact; + + // No spatial sampling if current frame size is too small, or if the + // amount of spatial down-sampling is above maximum spatial down-action. if ((width_ * height_) <= kMinImageSize || new_dec_factor_spatial > kMaxSpatialDown) { action_.spatial = kNoChangeSpatial; - qm_->change_resolution_spatial = false; - qm_->spatial_width_fact = 1.0f; - qm_->spatial_height_fact = 1.0f; + new_dec_factor_spatial = state_dec_factor_spatial_; } - // No frame rate reduction if average frame rate is below some point, - // or if the amount of temporal down-sampling will be too much. - float new_dec_factor_temp = state_dec_factor_temporal_ * qm_->temporal_fact; + // No frame rate reduction if average frame rate is below some point, or if + // the amount of temporal down-sampling is above maximum temporal down-action. if (avg_incoming_framerate_ <= kMinFrameRate || - new_dec_factor_temp >= kMaxTempDown) { + new_dec_factor_temp > kMaxTempDown) { action_.temporal = kNoChangeTemporal; - qm_->change_resolution_temporal = false; - qm_->temporal_fact = 1.0f; + new_dec_factor_temp = state_dec_factor_temporal_; + } + // Check if the total (spatial-temporal) down-action is above maximum allowed, + // if so, disallow the current selected down-action. + if (new_dec_factor_spatial * new_dec_factor_temp > kMaxTotalDown) { + if (action_.spatial != kNoChangeSpatial) { + action_.spatial = kNoChangeSpatial; + } else if (action_.temporal != kNoChangeTemporal) { + action_.temporal = kNoChangeTemporal; + } else { + // We only allow for one action (spatial or temporal) at a given time, so + // either spatial or temporal action is selected when this function is + // called. If the selected action is disallowed from one of the above + // 2 prior conditions (on spatial & temporal max down-action), then this + // condition "total down-action > |kMaxTotalDown|" would not be entered. + assert(false); + } } } diff --git a/src/modules/video_coding/main/source/qm_select.h b/src/modules/video_coding/main/source/qm_select.h index f5b9d210d3..4a74fa54bd 100644 --- a/src/modules/video_coding/main/source/qm_select.h +++ b/src/modules/video_coding/main/source/qm_select.h @@ -278,8 +278,9 @@ class VCMQmResolution : public VCMQmMethod { // Covert 2 stages of 3/4 (=9/16) spatial decimation to 1/2. void ConvertSpatialFractionalToWhole(); - // Check if the new frame sizes are still divisible by 2. - void CheckForEvenFrameSize(); + // Returns true if the new frame sizes, under the selected spatial action, + // are of even size. + bool EvenFrameSize(); // Insert latest down-sampling action into the history list. void InsertLatestDownAction(); diff --git a/src/modules/video_coding/main/source/qm_select_data.h b/src/modules/video_coding/main/source/qm_select_data.h index 1c9858d505..4d4f3397b1 100644 --- a/src/modules/video_coding/main/source/qm_select_data.h +++ b/src/modules/video_coding/main/source/qm_select_data.h @@ -23,16 +23,16 @@ namespace webrtc { // PARAMETERS FOR RESOLUTION ADAPTATION // -// Optimal level of buffer in secs: should corresponds to wrapper settings. -const float kOptBufferLevel = 0.5f; +// Initial level of buffer in secs. +const float kInitBufferLevel = 0.5f; // Threshold of (max) buffer size below which we consider too low (underflow). const float kPercBufferThr = 0.10f; // Threshold on the occurrences of low buffer levels. -const float kMaxBufferLow = 0.5f; +const float kMaxBufferLow = 0.30f; -// Threshold on rate mismatch +// Threshold on rate mismatch. const float kMaxRateMisMatch = 0.5f; // Threshold on amount of under/over encoder shooting. @@ -50,21 +50,21 @@ const float kTransRateScaleUpSpatialTemp = 1.25f; // Threshold on packet loss rate, above which favor resolution reduction. const float kPacketLossThr = 0.1f; -// Factor for reducing transitonal bitrate under packet loss. +// Factor for reducing transitional bitrate under packet loss. const float kPacketLossRateFac = 1.0f; // Maximum possible transitional rate for down-sampling: // (units in kbps), for 30fps. const uint16_t kMaxRateQm[9] = { - 50, // QCIF - 100, // kHCIF - 175, // kQVGA - 250, // CIF - 350, // HVGA - 500, // VGA - 1000, // QFULLHD - 1500, // WHD - 2000 // FULLHD + 0, // QCIF + 50, // kHCIF + 125, // kQVGA + 200, // CIF + 280, // HVGA + 400, // VGA + 700, // QFULLHD + 1000, // WHD + 1500 // FULLHD }; // Frame rate scale for maximum transition rate. @@ -79,30 +79,30 @@ const float kFrameRateFac[4] = { // motion=L/H/D,spatial==L/H/D: for low, high, middle levels const float kScaleTransRateQm[18] = { // VGA and lower - 0.50f, // L, L + 0.40f, // L, L 0.50f, // L, H - 0.50f, // L, D - 0.50f, // H ,L - 0.35f, // H, H - 0.35f, // H, D + 0.40f, // L, D + 0.60f, // H ,L + 0.60f, // H, H + 0.60f, // H, D 0.50f, // D, L 0.50f, // D, D - 0.35f, // D, H + 0.50f, // D, H // over VGA - 0.50f, // L, L + 0.40f, // L, L 0.50f, // L, H - 0.50f, // L, D - 0.50f, // H ,L - 0.35f, // H, H - 0.35f, // H, D + 0.40f, // L, D + 0.60f, // H ,L + 0.60f, // H, H + 0.60f, // H, D 0.50f, // D, L 0.50f, // D, D - 0.35f, // D, H + 0.50f, // D, H }; // Threshold on the target rate relative to transitional rate. -const float kFacLowRate = 0.75f; +const float kFacLowRate = 0.5f; // Action for down-sampling: // motion=L/H/D,spatial==L/H/D, for low, high, middle levels; @@ -123,7 +123,7 @@ const uint8_t kSpatialAction[27] = { 1, // L, L 1, // L, H 1, // L, D - 4, // H ,L + 2, // H ,L 1, // H, H 2, // H, D 2, // D, L @@ -156,7 +156,7 @@ const uint8_t kTemporalAction[27] = { // rateClass = 1: 3, // L, L - 2, // L, H + 3, // L, H 3, // L, D 1, // H ,L 3, // H, H @@ -179,14 +179,14 @@ const uint8_t kTemporalAction[27] = { // Control the total amount of down-sampling allowed. const float kMaxSpatialDown = 8.0f; -const float kMaxTempDown = 4.0f; -const float kMaxDownSample = 12.0f; +const float kMaxTempDown = 3.0f; +const float kMaxTotalDown = 9.0f; // Minimum image size for a spatial down-sampling. -const int kMinImageSize= 176 * 144; +const int kMinImageSize = 176 * 144; // Minimum frame rate for temporal down-sampling: -// no frame rate reduction if incomingFrameRate <= MIN_FRAME_RATE +// no frame rate reduction if incomingFrameRate <= MIN_FRAME_RATE. const int kMinFrameRate = 8; // @@ -202,20 +202,20 @@ const int kLowFrameRate = 10; const int kMiddleFrameRate = 15; const int kHighFrameRate = 25; -// Thresholds for motion: motion level is from NFD +// Thresholds for motion: motion level is from NFD. const float kHighMotionNfd = 0.075f; -const float kLowMotionNfd = 0.04f; +const float kLowMotionNfd = 0.03f; // Thresholds for spatial prediction error: -// this is applied on the min(2x2,1x2,2x1) +// this is applied on the average of (2x2,1x2,2x1). const float kHighTexture = 0.035f; -const float kLowTexture = 0.025f; +const float kLowTexture = 0.020f; // Used to reduce thresholds for larger/HD scenes: correction factor since // higher correlation in HD scenes means lower spatial prediction error. const float kScaleTexture = 0.9f; -// percentage reduction in transitional bitrate for 2x2 selected over 1x2/2x1 +// Percentage reduction in transitional bitrate for 2x2 selected over 1x2/2x1. const float kRateRedSpatial2X2 = 0.6f; const float kSpatialErr2x2VsHoriz = 0.1f; // percentage to favor 2x2 over H diff --git a/src/modules/video_coding/main/source/qm_select_unittest.cc b/src/modules/video_coding/main/source/qm_select_unittest.cc index f6990a6403..7f309ceb2b 100644 --- a/src/modules/video_coding/main/source/qm_select_unittest.cc +++ b/src/modules/video_coding/main/source/qm_select_unittest.cc @@ -91,9 +91,12 @@ TEST_F(QmSelectTest, HandleInputs) { 30.0f)); } +// TODO(marpan): Add a test for number of temporal layers > 1. + // No down-sampling action at high rates. TEST_F(QmSelectTest, NoActionHighRate) { - // Initialize with bitrate, frame rate, and native system width/height. + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. InitQmNativeData(800, 30, 640, 480, 1); // Update with encoder frame size. @@ -122,8 +125,9 @@ TEST_F(QmSelectTest, NoActionHighRate) { // Rate is well below transition, down-sampling action is taken, // depending on the content state. TEST_F(QmSelectTest, DownActionLowRate) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(100, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(50, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -132,8 +136,8 @@ TEST_F(QmSelectTest, DownActionLowRate) { EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {100, 100, 100}; - int encoder_sent_rate[] = {100, 100, 100}; + int target_rate[] = {50, 50, 50}; + int encoder_sent_rate[] = {50, 50, 50}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -209,20 +213,21 @@ TEST_F(QmSelectTest, DownActionLowRate) { 15.5f)); qm_resolution_->ResetDownSamplingState(); - // Medium motion, medium spatial: high frame rate, so 1/2 temporal expected. + // Medium motion, medium spatial: high frame rate, so 2/3 temporal expected. UpdateQmContentData(kTemporalMedium, kSpatialMedium, kSpatialMedium, kSpatialMedium); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(8, qm_resolution_->ComputeContentClass()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480, - 15.5f)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 640, 480, + 20.5f)); } // Rate mis-match is high, and we have over-shooting. // since target rate is below max for down-sampling, down-sampling is selected. TEST_F(QmSelectTest, DownActionHighRateMMOvershoot) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(450, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(300, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -231,7 +236,7 @@ TEST_F(QmSelectTest, DownActionHighRateMMOvershoot) { EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {450, 450, 450}; + int target_rate[] = {300, 300, 300}; int encoder_sent_rate[] = {900, 900, 900}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; @@ -259,8 +264,9 @@ TEST_F(QmSelectTest, DownActionHighRateMMOvershoot) { // Rate mis-match is high, target rate is below max for down-sampling, // but since we have consistent under-shooting, no down-sampling action. TEST_F(QmSelectTest, NoActionHighRateMMUndershoot) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(450, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(300, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -269,7 +275,7 @@ TEST_F(QmSelectTest, NoActionHighRateMMUndershoot) { EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {450, 450, 450}; + int target_rate[] = {300, 300, 300}; int encoder_sent_rate[] = {100, 100, 100}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; @@ -297,8 +303,9 @@ TEST_F(QmSelectTest, NoActionHighRateMMUndershoot) { // Buffer is underflowing, and target rate is below max for down-sampling, // so action is taken. TEST_F(QmSelectTest, DownActionBufferUnderflow) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(450, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(300, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -312,7 +319,7 @@ TEST_F(QmSelectTest, DownActionBufferUnderflow) { UpdateQmEncodedFrame(encoded_size, 10); // Update rates for a sequence of intervals. - int target_rate[] = {450, 450, 450}; + int target_rate[] = {300, 300, 300}; int encoder_sent_rate[] = {450, 450, 450}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; @@ -340,8 +347,9 @@ TEST_F(QmSelectTest, DownActionBufferUnderflow) { // Target rate is below max for down-sampling, but buffer level is stable, // so no action is taken. TEST_F(QmSelectTest, NoActionBufferStable) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(450, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(350, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -355,8 +363,8 @@ TEST_F(QmSelectTest, NoActionBufferStable) { UpdateQmEncodedFrame(encoded_size, 10); // Update rates for a sequence of intervals. - int target_rate[] = {450, 450, 450}; - int encoder_sent_rate[] = {450, 450, 450}; + int target_rate[] = {350, 350, 350}; + int encoder_sent_rate[] = {350, 450, 450}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -382,7 +390,8 @@ TEST_F(QmSelectTest, NoActionBufferStable) { // Very low rate, but no spatial down-sampling below some size (QCIF). TEST_F(QmSelectTest, LimitDownSpatialAction) { - // Initialize with bitrate, frame rate, and native system width/height. + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. InitQmNativeData(10, 30, 176, 144, 1); // Update with encoder frame size. @@ -411,7 +420,8 @@ TEST_F(QmSelectTest, LimitDownSpatialAction) { // Very low rate, but no frame reduction below some frame_rate (8fps). TEST_F(QmSelectTest, LimitDownTemporalAction) { - // Initialize with bitrate, frame rate, and native system width/height. + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. InitQmNativeData(10, 8, 640, 480, 1); // Update with encoder frame size. @@ -442,8 +452,9 @@ TEST_F(QmSelectTest, LimitDownTemporalAction) { // Two stages: spatial down-sample and then back up spatially, // as rate as increased. TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(100, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(50, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -452,8 +463,8 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) { EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {100, 100, 100}; - int encoder_sent_rate[] = {100, 100, 100}; + int target_rate[] = {50, 50, 50}; + int encoder_sent_rate[] = {50, 50, 50}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -468,7 +479,7 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) { EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240, 30.0f)); - // Reset and go up in rate: expected to go back up. + // Reset and go up in rate: expected to go back up, in 2 stages of 3/4. qm_resolution_->ResetRates(); qm_resolution_->UpdateCodecParameters(30.0f, 320, 240); EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); @@ -481,15 +492,23 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) { fraction_lost2, 5); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f, 640, 480, + float scale = (4.0f / 3.0f) / 2.0f; + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 1.0f, 480, 360, 30.0f)); + + qm_resolution_->UpdateCodecParameters(30.0f, 480, 360); + EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); + EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f, + 640, 480, 30.0f)); } // Two stages: spatial down-sample and then back up spatially, since encoder // is under-shooting target even though rate has not increased much. TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(100, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(50, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -498,8 +517,8 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) { EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {100, 100, 100}; - int encoder_sent_rate[] = {100, 100, 100}; + int target_rate[] = {50, 50, 50}; + int encoder_sent_rate[] = {50, 50, 50}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -515,6 +534,7 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) { 30.0f)); // Reset rates and simulate under-shooting scenario.: expect to go back up. + // Goes up spatially in two stages for 1/2x1/2 down-sampling. qm_resolution_->ResetRates(); qm_resolution_->UpdateCodecParameters(30.0f, 320, 240); EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); @@ -527,15 +547,23 @@ TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) { fraction_lost2, 5); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f, 640, 480, + float scale = (4.0f / 3.0f) / 2.0f; + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 1.0f, 480, 360, 30.0f)); + + qm_resolution_->UpdateCodecParameters(30.0f, 480, 360); + EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); + EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f, + 640, 480, 30.0f)); } // Two stages: spatial down-sample and then no action to go up, // as encoding rate mis-match is too high. TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(100, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(50, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -544,8 +572,8 @@ TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) { EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {100, 100, 100}; - int encoder_sent_rate[] = {100, 100, 100}; + int target_rate[] = {50, 50, 50}; + int encoder_sent_rate[] = {50, 50, 50}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -580,8 +608,9 @@ TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) { // Two stages: temporally down-sample and then back up temporally, // as rate as increased. TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(100, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(50, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -590,8 +619,8 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) { EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {100, 100, 100}; - int encoder_sent_rate[] = {100, 100, 100}; + int target_rate[] = {50, 50, 50}; + int encoder_sent_rate[] = {50, 50, 50}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -624,8 +653,9 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) { // Two stages: temporal down-sample and then back up temporally, since encoder // is under-shooting target even though rate has not increased much. TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporalUndershoot) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(100, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(50, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -634,8 +664,8 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporalUndershoot) { EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {100, 100, 100}; - int encoder_sent_rate[] = {100, 100, 100}; + int target_rate[] = {50, 50, 50}; + int encoder_sent_rate[] = {50, 50, 50}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -653,7 +683,7 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporalUndershoot) { // Reset rates and simulate under-shooting scenario.: expect to go back up. qm_resolution_->ResetRates(); // Update rates for a sequence of intervals. - int target_rate2[] = {200, 200, 200, 200, 200}; + int target_rate2[] = {150, 150, 150, 150, 150}; int encoder_sent_rate2[] = {50, 50, 50, 50, 50}; int incoming_frame_rate2[] = {15, 15, 15, 15, 15}; uint8_t fraction_lost2[] = {10, 10, 10, 10, 10}; @@ -668,8 +698,9 @@ TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporalUndershoot) { // Two stages: temporal down-sample and then no action to go up, // as encoding rate mis-match is too high. TEST_F(QmSelectTest, 2StageDownTemporalNoActionUp) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(100, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(50, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -678,8 +709,8 @@ TEST_F(QmSelectTest, 2StageDownTemporalNoActionUp) { EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {100, 100, 100}; - int encoder_sent_rate[] = {100, 100, 100}; + int target_rate[] = {50, 50, 50}; + int encoder_sent_rate[] = {50, 50, 50}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -711,8 +742,9 @@ TEST_F(QmSelectTest, 2StageDownTemporalNoActionUp) { // 3 stages: spatial down-sample, followed by temporal down-sample, // and then go up to full state, as encoding rate has increased. TEST_F(QmSelectTest, 3StageDownSpatialTemporlaUpSpatialTemporal) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(100, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(80, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -741,37 +773,54 @@ TEST_F(QmSelectTest, 3StageDownSpatialTemporlaUpSpatialTemporal) { qm_resolution_->UpdateCodecParameters(30.0f, 320, 240); EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); + // Reset rates and go lower in rate. + qm_resolution_->ResetRates(); + int target_rate2[] = {40, 40, 40, 40, 40}; + int encoder_sent_rate2[] = {40, 40, 40, 40, 40}; + int incoming_frame_rate2[] = {30, 30, 30, 30, 30}; + uint8_t fraction_lost2[] = {10, 10, 10, 10, 10}; + UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2, + fraction_lost2, 5); + // Update content: motion level, and 3 spatial prediction errors. // Low motion, high spatial. UpdateQmContentData(kTemporalLow, kSpatialHigh, kSpatialHigh, kSpatialHigh); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 320, 240, - 15.5f)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 320, 240, + 20.5f)); // Reset rates and go high up in rate: expect to go back up both spatial - // and temporally. + // and temporally. The 1/2x1/2 spatial is undone in two stages. qm_resolution_->ResetRates(); // Update rates for a sequence of intervals. - int target_rate2[] = {1000, 1000, 1000, 1000, 1000}; - int encoder_sent_rate2[] = {1000, 1000, 1000, 1000, 1000}; - int incoming_frame_rate2[] = {15, 15, 15, 15, 15}; - uint8_t fraction_lost2[] = {10, 10, 10, 10, 10}; - UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2, - fraction_lost2, 5); + int target_rate3[] = {1000, 1000, 1000, 1000, 1000}; + int encoder_sent_rate3[] = {1000, 1000, 1000, 1000, 1000}; + int incoming_frame_rate3[] = {20, 20, 20, 20, 20}; + uint8_t fraction_lost3[] = {10, 10, 10, 10, 10}; + UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3, + fraction_lost3, 5); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 0.5f, 640, 480, - 30.0f)); + float scale = (4.0f / 3.0f) / 2.0f; + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 2.0f / 3.0f, + 480, 360, 30.0f)); + + qm_resolution_->UpdateCodecParameters(30.0f, 480, 360); + EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); + EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f, + 640, 480, 30.0f)); } // No down-sampling below some total amount. TEST_F(QmSelectTest, NoActionTooMuchDownSampling) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(400, 30, 1280, 720, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(150, 30, 1280, 720, 1); // Update with encoder frame size. uint16_t codec_width = 1280; @@ -780,8 +829,8 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) { EXPECT_EQ(7, qm_resolution_->GetImageType(codec_width, codec_height)); // Update rates for a sequence of intervals. - int target_rate[] = {200, 200, 200}; - int encoder_sent_rate[] = {200, 200, 200}; + int target_rate[] = {150, 150, 150}; + int encoder_sent_rate[] = {150, 150, 150}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -796,14 +845,15 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) { EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 640, 360, 30.0f)); - // Reset and lower rates to get another spatial action (3/4x3/4) + // Reset and lower rates to get another spatial action (3/4x3/4). + // Lower the frame rate for spatial to be selected again. qm_resolution_->ResetRates(); - qm_resolution_->UpdateCodecParameters(30.0f, 640, 360); + qm_resolution_->UpdateCodecParameters(10.0f, 640, 360); EXPECT_EQ(4, qm_resolution_->GetImageType(640, 360)); // Update rates for a sequence of intervals. - int target_rate2[] = {80, 80, 80, 80, 80}; - int encoder_sent_rate2[] = {80, 80, 80, 80, 80}; - int incoming_frame_rate2[] = {30, 30, 30, 30, 30}; + int target_rate2[] = {70, 70, 70, 70, 70}; + int encoder_sent_rate2[] = {70, 70, 70, 70, 70}; + int incoming_frame_rate2[] = {10, 10, 10, 10, 10}; uint8_t fraction_lost2[] = {10, 10, 10, 10, 10}; UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2, fraction_lost2, 5); @@ -816,17 +866,17 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) { EXPECT_EQ(5, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f, - 1.0f, 480, 270, 30.0f)); + 1.0f, 480, 270, 10.0f)); // Reset and go to very low rate: no action should be taken, // we went down too much already. qm_resolution_->ResetRates(); - qm_resolution_->UpdateCodecParameters(30.0f, 480, 270); + qm_resolution_->UpdateCodecParameters(10.0f, 480, 270); EXPECT_EQ(3, qm_resolution_->GetImageType(480, 270)); // Update rates for a sequence of intervals. int target_rate3[] = {10, 10, 10, 10, 10}; int encoder_sent_rate3[] = {10, 10, 10, 10, 10}; - int incoming_frame_rate3[] = {30, 30, 30, 30, 30}; + int incoming_frame_rate3[] = {10, 10, 10, 10, 10}; uint8_t fraction_lost3[] = {10, 10, 10, 10, 10}; UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3, fraction_lost3, 5); @@ -834,16 +884,17 @@ TEST_F(QmSelectTest, NoActionTooMuchDownSampling) { EXPECT_EQ(5, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.0f, 480, 270, - 30.0f)); + 10.0f)); } // Multiple down-sampling stages and then undo all of them. // Spatial down-sample 3/4x3/4, followed by temporal down-sample 2/3, -// followed by spatial 1/2x1/2. Then go up to full state, +// followed by spatial 3/4x3/4. Then go up to full state, // as encoding rate has increased. TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(200, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(150, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -853,8 +904,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) { // Go down spatial 3/4x3/4. // Update rates for a sequence of intervals. - int target_rate[] = {200, 200, 200}; - int encoder_sent_rate[] = {200, 200, 200}; + int target_rate[] = {150, 150, 150}; + int encoder_sent_rate[] = {150, 150, 150}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -868,7 +919,7 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) { EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f, 1.0f, 480, 360, 30.0f)); - // Go down 1/2 temporal. + // Go down 2/3 temporal. qm_resolution_->UpdateCodecParameters(30.0f, 480, 360); EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); qm_resolution_->ResetRates(); @@ -885,15 +936,15 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) { EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 480, 360, - 15.5f)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 480, 360, + 20.5f)); - // Go down 1/2x1/2 spatial. - qm_resolution_->UpdateCodecParameters(15.0f, 480, 360); + // Go down 3/4x3/4 spatial: + qm_resolution_->UpdateCodecParameters(20.0f, 480, 360); qm_resolution_->ResetRates(); - int target_rate3[] = {50, 50, 50, 50, 50}; - int encoder_sent_rate3[] = {50, 50, 50, 50, 50}; - int incoming_frame_rate3[] = {15, 15, 15, 15, 15}; + int target_rate3[] = {80, 80, 80, 80, 80}; + int encoder_sent_rate3[] = {80, 80, 80, 80, 80}; + int incoming_frame_rate3[] = {20, 20, 20, 20, 20}; uint8_t fraction_lost3[] = {10, 10, 10, 10, 10}; UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3, fraction_lost3, 5); @@ -904,16 +955,17 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) { EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 240, 180, - 15.0f)); + // The two spatial actions of 3/4x3/4 are converted to 1/2x1/2, + // so scale factor is 2.0. + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2.0f, 2.0f, 1.0f, 320, 240, + 20.0f)); // Reset rates and go high up in rate: expect to go up: - // should go up first: 1/2x1x2 and 1/2 temporally, - // and second: 3/4x3/4 spatial. + // 1/2x1x2 spatial and 1/2 temporally. - // Go up 1/2x1/2 spatially and 1/2 temporally - qm_resolution_->UpdateCodecParameters(15.0f, 240, 180); - EXPECT_EQ(1, qm_resolution_->GetImageType(240, 180)); + // Go up 1/2x1/2 spatially and 1/2 temporally. Spatial is done in 2 stages. + qm_resolution_->UpdateCodecParameters(15.0f, 320, 240); + EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); qm_resolution_->ResetRates(); // Update rates for a sequence of intervals. int target_rate4[] = {1000, 1000, 1000, 1000, 1000}; @@ -926,35 +978,24 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory1) { EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 0.5f, 480, 360, - 30.0f)); + float scale = (4.0f / 3.0f) / 2.0f; + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 2.0f / 3.0f, 480, + 360, 30.0f)); - // Go up 3/4x3/4 spatially. qm_resolution_->UpdateCodecParameters(30.0f, 480, 360); EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); - qm_resolution_->ResetRates(); - // Update rates for a sequence of intervals. - int target_rate5[] = {1000, 1000, 1000, 1000, 1000}; - int encoder_sent_rate5[] = {1000, 1000, 1000, 1000, 1000}; - int incoming_frame_rate5[] = {30, 30, 30, 30, 30}; - uint8_t fraction_lost5[] = {10, 10, 10, 10, 10}; - UpdateQmRateData(target_rate5, encoder_sent_rate5, incoming_frame_rate5, - fraction_lost5, 5); - EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); - EXPECT_EQ(3, qm_resolution_->ComputeContentClass()); - EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, - 1.0f, 640, 480, 30.0f)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f, + 640, 480, 30.0f)); } // Multiple down-sampling and up-sample stages, with partial undoing. -// Spatial down-sample 1/2x1/2, followed by temporal down-sample 2/3, -// undo the spatial 1/2x1/2, then another temporal 1/2, and undo -// the 1/2 temporal. +// Spatial down-sample 1/2x1/2, followed by temporal down-sample 2/3, undo the +// temporal, then another temporal, and then undo both spatial and temporal. TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(100, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(80, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -964,8 +1005,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) { // Go down 1/2x1/2 spatial. // Update rates for a sequence of intervals. - int target_rate[] = {100, 100, 100}; - int encoder_sent_rate[] = {100, 100, 100}; + int target_rate[] = {80, 80, 80}; + int encoder_sent_rate[] = {80, 80, 80}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -984,8 +1025,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) { qm_resolution_->UpdateCodecParameters(30.0f, 320, 240); EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); qm_resolution_->ResetRates(); - int target_rate2[] = {80, 80, 80, 80, 80}; - int encoder_sent_rate2[] = {80, 80, 80, 80, 80}; + int target_rate2[] = {40, 40, 40, 40, 40}; + int encoder_sent_rate2[] = {40, 40, 40, 40, 40}; int incoming_frame_rate2[] = {30, 30, 30, 30, 30}; uint8_t fraction_lost2[] = {10, 10, 10, 10, 10}; UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2, @@ -1001,12 +1042,12 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) { EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 320, 240, 20.5f)); - // Go up 1/2x1/2 spatially. + // Go up 2/3 temporally. qm_resolution_->UpdateCodecParameters(20.0f, 320, 240); qm_resolution_->ResetRates(); // Update rates for a sequence of intervals. - int target_rate3[] = {300, 300, 300, 300, 300}; - int encoder_sent_rate3[] = {300, 300, 300, 300, 300}; + int target_rate3[] = {150, 150, 150, 150, 150}; + int encoder_sent_rate3[] = {150, 150, 150, 150, 150}; int incoming_frame_rate3[] = {20, 20, 20, 20, 20}; uint8_t fraction_lost3[] = {10, 10, 10, 10, 10}; UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3, @@ -1015,16 +1056,16 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) { EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(7, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0.5f, 0.5f, 1.0f, 640, 480, - 20.0f)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f / 3.0f, 320, + 240, 30.0f)); - // Go down 1/2 temporal. - qm_resolution_->UpdateCodecParameters(20.0f, 640, 480); - EXPECT_EQ(5, qm_resolution_->GetImageType(640, 480)); + // Go down 2/3 temporal. + qm_resolution_->UpdateCodecParameters(30.0f, 320, 240); + EXPECT_EQ(2, qm_resolution_->GetImageType(320, 240)); qm_resolution_->ResetRates(); - int target_rate4[] = {100, 100, 100, 100, 100}; - int encoder_sent_rate4[] = {100, 100, 100, 100, 100}; - int incoming_frame_rate4[] = {20, 20, 20, 20, 20}; + int target_rate4[] = {40, 40, 40, 40, 40}; + int encoder_sent_rate4[] = {40, 40, 40, 40, 40}; + int incoming_frame_rate4[] = {30, 30, 30, 30, 30}; uint8_t fraction_lost4[] = {10, 10, 10, 10, 10}; UpdateQmRateData(target_rate4, encoder_sent_rate4, incoming_frame_rate4, fraction_lost4, 5); @@ -1035,33 +1076,39 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory2) { EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 640, 480, - 10.5f)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 320, 240, + 20.5f)); - // Go up 1/2 temporal. + // Go up spatial and temporal. Spatial undoing is done in 2 stages. + qm_resolution_->UpdateCodecParameters(20.5f, 320, 240); qm_resolution_->ResetRates(); // Update rates for a sequence of intervals. int target_rate5[] = {1000, 1000, 1000, 1000, 1000}; int encoder_sent_rate5[] = {1000, 1000, 1000, 1000, 1000}; - int incoming_frame_rate5[] = {10, 10, 10, 10, 10}; + int incoming_frame_rate5[] = {20, 20, 20, 20, 20}; uint8_t fraction_lost5[] = {10, 10, 10, 10, 10}; UpdateQmRateData(target_rate5, encoder_sent_rate5, incoming_frame_rate5, fraction_lost5, 5); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); - EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); - EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f, 640, 480, - 20.5f)); + float scale = (4.0f / 3.0f) / 2.0f; + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, scale, scale, 2.0f / 3.0f, + 480, 360, 30.0f)); + + qm_resolution_->UpdateCodecParameters(30.0f, 480, 360); + EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); + EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, 1.0f, + 640, 480, 30.0f)); } // Multiple down-sampling and up-sample stages, with partial undoing. -// Spatial down-sample 3/4x3/4, followed by temporal down-sample 1/2, -// undo the temporal 1/2, then another temporal 2/3 down, and undo -// the 2/3 temporal. +// Spatial down-sample 3/4x3/4, followed by temporal down-sample 2/3, +// undo the temporal 2/3, and then undo the spatial. TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(200, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(100, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -1071,8 +1118,8 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) { // Go down 3/4x3/4 spatial. // Update rates for a sequence of intervals. - int target_rate[] = {200, 200, 200}; - int encoder_sent_rate[] = {200, 200, 200}; + int target_rate[] = {100, 100, 100}; + int encoder_sent_rate[] = {100, 100, 100}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -1087,7 +1134,7 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) { EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 4.0f / 3.0f, 4.0f / 3.0f, 1.0f, 480, 360, 30.0f)); - // Go down 1/2 temporal. + // Go down 2/3 temporal. qm_resolution_->UpdateCodecParameters(30.0f, 480, 360); EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); qm_resolution_->ResetRates(); @@ -1104,15 +1151,16 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) { EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f, 480, 360, - 15.5f)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 480, 360, + 20.5f)); - // Go up 1/2 temporal. + // Go up 2/3 temporal. + qm_resolution_->UpdateCodecParameters(20.5f, 480, 360); qm_resolution_->ResetRates(); // Update rates for a sequence of intervals. - int target_rate3[] = {300, 300, 300, 300, 300}; - int encoder_sent_rate3[] = {300, 300, 300, 300, 300}; - int incoming_frame_rate3[] = {15, 15, 15, 15, 15}; + int target_rate3[] = {250, 250, 250, 250, 250}; + int encoder_sent_rate3[] = {250, 250, 250, 250, 250}; + int incoming_frame_rate3[] = {20, 20, 20, 20, 120}; uint8_t fraction_lost3[] = {10, 10, 10, 10, 10}; UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3, fraction_lost3, 5); @@ -1120,51 +1168,31 @@ TEST_F(QmSelectTest, MultipleStagesCheckActionHistory3) { EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); EXPECT_EQ(1, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 0.5f, 480, 360, - 30.0f)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f / 3.0f, 480, + 360, 30.0f)); - // Go down 2/3 temporal. - qm_resolution_->UpdateCodecParameters(30.0f, 640, 480); - EXPECT_EQ(5, qm_resolution_->GetImageType(640, 480)); + // Go up spatial. + qm_resolution_->UpdateCodecParameters(30.0f, 480, 360); + EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); qm_resolution_->ResetRates(); - int target_rate4[] = {200, 200, 200, 200, 200}; - int encoder_sent_rate4[] = {200, 200, 200, 200, 200}; + int target_rate4[] = {500, 500, 500, 500, 500}; + int encoder_sent_rate4[] = {500, 500, 500, 500, 500}; int incoming_frame_rate4[] = {30, 30, 30, 30, 30}; uint8_t fraction_lost4[] = {30, 30, 30, 30, 30}; UpdateQmRateData(target_rate4, encoder_sent_rate4, incoming_frame_rate4, fraction_lost4, 5); - // Update content: motion level, and 3 spatial prediction errors. - // Medium motion, high spatial. - UpdateQmContentData(kTemporalMedium, kSpatialHigh, kSpatialHigh, - kSpatialHigh); EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); - EXPECT_EQ(7, qm_resolution_->ComputeContentClass()); EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 1.5f, 640, 480, - 20.5f)); - - // Go up 2/3 temporal. - qm_resolution_->ResetRates(); - // Update rates for a sequence of intervals. - int target_rate5[] = {500, 500, 500, 500, 500}; - int encoder_sent_rate5[] = {500, 500, 500, 500, 500}; - int incoming_frame_rate5[] = {20, 20, 20, 20, 20}; - uint8_t fraction_lost5[] = {10, 10, 10, 10, 10}; - UpdateQmRateData(target_rate5, encoder_sent_rate5, incoming_frame_rate5, - fraction_lost5, 5); - - EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_)); - EXPECT_EQ(7, qm_resolution_->ComputeContentClass()); - EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState()); - EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1.0f, 1.0f, 2.0f / 3.0f, 640, - 480, 30.0f)); + EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 3.0f / 4.0f, 3.0f / 4.0f, + 1.0f, 640, 480, 30.0f)); } // Two stages of 3/4x3/4 converted to one stage of 1/2x1/2. TEST_F(QmSelectTest, ConvertThreeQuartersToOneHalf) { - // Initialize with bitrate, frame rate, and native system width/height. - InitQmNativeData(200, 30, 640, 480, 1); + // Initialize with bitrate, frame rate, native system width/height, and + // number of temporal layers. + InitQmNativeData(150, 30, 640, 480, 1); // Update with encoder frame size. uint16_t codec_width = 640; @@ -1174,8 +1202,8 @@ TEST_F(QmSelectTest, ConvertThreeQuartersToOneHalf) { // Go down 3/4x3/4 spatial. // Update rates for a sequence of intervals. - int target_rate[] = {200, 200, 200}; - int encoder_sent_rate[] = {200, 200, 200}; + int target_rate[] = {150, 150, 150}; + int encoder_sent_rate[] = {150, 150, 150}; int incoming_frame_rate[] = {30, 30, 30}; uint8_t fraction_lost[] = {10, 10, 10}; UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate, @@ -1194,8 +1222,8 @@ TEST_F(QmSelectTest, ConvertThreeQuartersToOneHalf) { qm_resolution_->UpdateCodecParameters(30.0f, 480, 360); EXPECT_EQ(4, qm_resolution_->GetImageType(480, 360)); qm_resolution_->ResetRates(); - int target_rate2[] = {150, 150, 150, 150, 150}; - int encoder_sent_rate2[] = {150, 150, 150, 150, 150}; + int target_rate2[] = {100, 100, 100, 100, 100}; + int encoder_sent_rate2[] = {100, 100, 100, 100, 100}; int incoming_frame_rate2[] = {30, 30, 30, 30, 30}; uint8_t fraction_lost2[] = {10, 10, 10, 10, 10}; UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,