| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 user_frame_rate_(0.0f), | 29 user_frame_rate_(0.0f), |
| 30 native_width_(0), | 30 native_width_(0), |
| 31 native_height_(0), | 31 native_height_(0), |
| 32 native_frame_rate_(0.0f), | 32 native_frame_rate_(0.0f), |
| 33 image_type_(kVGA), | 33 image_type_(kVGA), |
| 34 framerate_level_(kFrameRateHigh), | 34 framerate_level_(kFrameRateHigh), |
| 35 init_(false) { | 35 init_(false) { |
| 36 ResetQM(); | 36 ResetQM(); |
| 37 } | 37 } |
| 38 | 38 |
| 39 VCMQmMethod::~VCMQmMethod() { | 39 VCMQmMethod::~VCMQmMethod() {} |
| 40 } | |
| 41 | 40 |
| 42 void VCMQmMethod::ResetQM() { | 41 void VCMQmMethod::ResetQM() { |
| 43 aspect_ratio_ = 1.0f; | 42 aspect_ratio_ = 1.0f; |
| 44 motion_.Reset(); | 43 motion_.Reset(); |
| 45 spatial_.Reset(); | 44 spatial_.Reset(); |
| 46 content_class_ = 0; | 45 content_class_ = 0; |
| 47 } | 46 } |
| 48 | 47 |
| 49 uint8_t VCMQmMethod::ComputeContentClass() { | 48 uint8_t VCMQmMethod::ComputeContentClass() { |
| 50 ComputeMotionNFD(); | 49 ComputeMotionNFD(); |
| 51 ComputeSpatial(); | 50 ComputeSpatial(); |
| 52 return content_class_ = 3 * motion_.level + spatial_.level; | 51 return content_class_ = 3 * motion_.level + spatial_.level; |
| 53 } | 52 } |
| 54 | 53 |
| 55 void VCMQmMethod::UpdateContent(const VideoContentMetrics* contentMetrics) { | 54 void VCMQmMethod::UpdateContent(const VideoContentMetrics* contentMetrics) { |
| 56 content_metrics_ = contentMetrics; | 55 content_metrics_ = contentMetrics; |
| 57 } | 56 } |
| 58 | 57 |
| 59 void VCMQmMethod::ComputeMotionNFD() { | 58 void VCMQmMethod::ComputeMotionNFD() { |
| 60 if (content_metrics_) { | 59 if (content_metrics_) { |
| 61 motion_.value = content_metrics_->motion_magnitude; | 60 motion_.value = content_metrics_->motion_magnitude; |
| 62 } | 61 } |
| 63 // Determine motion level. | 62 // Determine motion level. |
| 64 if (motion_.value < kLowMotionNfd) { | 63 if (motion_.value < kLowMotionNfd) { |
| 65 motion_.level = kLow; | 64 motion_.level = kLow; |
| 66 } else if (motion_.value > kHighMotionNfd) { | 65 } else if (motion_.value > kHighMotionNfd) { |
| 67 motion_.level = kHigh; | 66 motion_.level = kHigh; |
| 68 } else { | 67 } else { |
| 69 motion_.level = kDefault; | 68 motion_.level = kDefault; |
| 70 } | 69 } |
| 71 } | 70 } |
| 72 | 71 |
| 73 void VCMQmMethod::ComputeSpatial() { | 72 void VCMQmMethod::ComputeSpatial() { |
| 74 float spatial_err = 0.0; | 73 float spatial_err = 0.0; |
| 75 float spatial_err_h = 0.0; | 74 float spatial_err_h = 0.0; |
| 76 float spatial_err_v = 0.0; | 75 float spatial_err_v = 0.0; |
| 77 if (content_metrics_) { | 76 if (content_metrics_) { |
| 78 spatial_err = content_metrics_->spatial_pred_err; | 77 spatial_err = content_metrics_->spatial_pred_err; |
| 79 spatial_err_h = content_metrics_->spatial_pred_err_h; | 78 spatial_err_h = content_metrics_->spatial_pred_err_h; |
| 80 spatial_err_v = content_metrics_->spatial_pred_err_v; | 79 spatial_err_v = content_metrics_->spatial_pred_err_v; |
| 81 } | 80 } |
| 82 // Spatial measure: take average of 3 prediction errors. | 81 // Spatial measure: take average of 3 prediction errors. |
| 83 spatial_.value = (spatial_err + spatial_err_h + spatial_err_v) / 3.0f; | 82 spatial_.value = (spatial_err + spatial_err_h + spatial_err_v) / 3.0f; |
| 84 | 83 |
| 85 // Reduce thresholds for large scenes/higher pixel correlation. | 84 // Reduce thresholds for large scenes/higher pixel correlation. |
| 86 float scale2 = image_type_ > kVGA ? kScaleTexture : 1.0; | 85 float scale2 = image_type_ > kVGA ? kScaleTexture : 1.0; |
| 87 | 86 |
| 88 if (spatial_.value > scale2 * kHighTexture) { | 87 if (spatial_.value > scale2 * kHighTexture) { |
| 89 spatial_.level = kHigh; | 88 spatial_.level = kHigh; |
| 90 } else if (spatial_.value < scale2 * kLowTexture) { | 89 } else if (spatial_.value < scale2 * kLowTexture) { |
| 91 spatial_.level = kLow; | 90 spatial_.level = kLow; |
| 92 } else { | 91 } else { |
| 93 spatial_.level = kDefault; | 92 spatial_.level = kDefault; |
| 94 } | 93 } |
| 95 } | 94 } |
| 96 | 95 |
| 97 ImageType VCMQmMethod::GetImageType(uint16_t width, | 96 ImageType VCMQmMethod::GetImageType(uint16_t width, uint16_t height) { |
| 98 uint16_t height) { | |
| 99 // Get the image type for the encoder frame size. | 97 // Get the image type for the encoder frame size. |
| 100 uint32_t image_size = width * height; | 98 uint32_t image_size = width * height; |
| 101 if (image_size == kSizeOfImageType[kQCIF]) { | 99 if (image_size == kSizeOfImageType[kQCIF]) { |
| 102 return kQCIF; | 100 return kQCIF; |
| 103 } else if (image_size == kSizeOfImageType[kHCIF]) { | 101 } else if (image_size == kSizeOfImageType[kHCIF]) { |
| 104 return kHCIF; | 102 return kHCIF; |
| 105 } else if (image_size == kSizeOfImageType[kQVGA]) { | 103 } else if (image_size == kSizeOfImageType[kQVGA]) { |
| 106 return kQVGA; | 104 return kQVGA; |
| 107 } else if (image_size == kSizeOfImageType[kCIF]) { | 105 } else if (image_size == kSizeOfImageType[kCIF]) { |
| 108 return kCIF; | 106 return kCIF; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 135 } | 133 } |
| 136 return static_cast<ImageType>(isel); | 134 return static_cast<ImageType>(isel); |
| 137 } | 135 } |
| 138 | 136 |
| 139 FrameRateLevelClass VCMQmMethod::FrameRateLevel(float avg_framerate) { | 137 FrameRateLevelClass VCMQmMethod::FrameRateLevel(float avg_framerate) { |
| 140 if (avg_framerate <= kLowFrameRate) { | 138 if (avg_framerate <= kLowFrameRate) { |
| 141 return kFrameRateLow; | 139 return kFrameRateLow; |
| 142 } else if (avg_framerate <= kMiddleFrameRate) { | 140 } else if (avg_framerate <= kMiddleFrameRate) { |
| 143 return kFrameRateMiddle1; | 141 return kFrameRateMiddle1; |
| 144 } else if (avg_framerate <= kHighFrameRate) { | 142 } else if (avg_framerate <= kHighFrameRate) { |
| 145 return kFrameRateMiddle2; | 143 return kFrameRateMiddle2; |
| 146 } else { | 144 } else { |
| 147 return kFrameRateHigh; | 145 return kFrameRateHigh; |
| 148 } | 146 } |
| 149 } | 147 } |
| 150 | 148 |
| 151 // RESOLUTION CLASS | 149 // RESOLUTION CLASS |
| 152 | 150 |
| 153 VCMQmResolution::VCMQmResolution() | 151 VCMQmResolution::VCMQmResolution() : qm_(new VCMResolutionScale()) { |
| 154 : qm_(new VCMResolutionScale()) { | |
| 155 Reset(); | 152 Reset(); |
| 156 } | 153 } |
| 157 | 154 |
| 158 VCMQmResolution::~VCMQmResolution() { | 155 VCMQmResolution::~VCMQmResolution() { |
| 159 delete qm_; | 156 delete qm_; |
| 160 } | 157 } |
| 161 | 158 |
| 162 void VCMQmResolution::ResetRates() { | 159 void VCMQmResolution::ResetRates() { |
| 163 sum_target_rate_ = 0.0f; | 160 sum_target_rate_ = 0.0f; |
| 164 sum_incoming_framerate_ = 0.0f; | 161 sum_incoming_framerate_ = 0.0f; |
| 165 sum_rate_MM_ = 0.0f; | 162 sum_rate_MM_ = 0.0f; |
| 166 sum_rate_MM_sgn_ = 0.0f; | 163 sum_rate_MM_sgn_ = 0.0f; |
| 167 sum_packet_loss_ = 0.0f; | 164 sum_packet_loss_ = 0.0f; |
| 168 buffer_level_ = kInitBufferLevel * target_bitrate_; | 165 buffer_level_ = kInitBufferLevel * target_bitrate_; |
| 169 frame_cnt_ = 0; | 166 frame_cnt_ = 0; |
| 170 frame_cnt_delta_ = 0; | 167 frame_cnt_delta_ = 0; |
| 171 low_buffer_cnt_ = 0; | 168 low_buffer_cnt_ = 0; |
| 172 update_rate_cnt_ = 0; | 169 update_rate_cnt_ = 0; |
| 173 } | 170 } |
| 174 | 171 |
| 175 void VCMQmResolution::ResetDownSamplingState() { | 172 void VCMQmResolution::ResetDownSamplingState() { |
| 176 state_dec_factor_spatial_ = 1.0; | 173 state_dec_factor_spatial_ = 1.0; |
| 177 state_dec_factor_temporal_ = 1.0; | 174 state_dec_factor_temporal_ = 1.0; |
| 178 for (int i = 0; i < kDownActionHistorySize; i++) { | 175 for (int i = 0; i < kDownActionHistorySize; i++) { |
| 179 down_action_history_[i].spatial = kNoChangeSpatial; | 176 down_action_history_[i].spatial = kNoChangeSpatial; |
| 180 down_action_history_[i].temporal = kNoChangeTemporal; | 177 down_action_history_[i].temporal = kNoChangeTemporal; |
| 181 } | 178 } |
| 182 } | 179 } |
| 183 | 180 |
| 184 void VCMQmResolution::Reset() { | 181 void VCMQmResolution::Reset() { |
| 185 target_bitrate_ = 0.0f; | 182 target_bitrate_ = 0.0f; |
| 186 incoming_framerate_ = 0.0f; | 183 incoming_framerate_ = 0.0f; |
| 187 buffer_level_ = 0.0f; | 184 buffer_level_ = 0.0f; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 218 incoming_framerate_ = user_framerate; | 215 incoming_framerate_ = user_framerate; |
| 219 UpdateCodecParameters(user_framerate, width, height); | 216 UpdateCodecParameters(user_framerate, width, height); |
| 220 native_width_ = width; | 217 native_width_ = width; |
| 221 native_height_ = height; | 218 native_height_ = height; |
| 222 native_frame_rate_ = user_framerate; | 219 native_frame_rate_ = user_framerate; |
| 223 num_layers_ = num_layers; | 220 num_layers_ = num_layers; |
| 224 // Initial buffer level. | 221 // Initial buffer level. |
| 225 buffer_level_ = kInitBufferLevel * target_bitrate_; | 222 buffer_level_ = kInitBufferLevel * target_bitrate_; |
| 226 // Per-frame bandwidth. | 223 // Per-frame bandwidth. |
| 227 per_frame_bandwidth_ = target_bitrate_ / user_framerate; | 224 per_frame_bandwidth_ = target_bitrate_ / user_framerate; |
| 228 init_ = true; | 225 init_ = true; |
| 229 return VCM_OK; | 226 return VCM_OK; |
| 230 } | 227 } |
| 231 | 228 |
| 232 void VCMQmResolution::UpdateCodecParameters(float frame_rate, uint16_t width, | 229 void VCMQmResolution::UpdateCodecParameters(float frame_rate, |
| 230 uint16_t width, |
| 233 uint16_t height) { | 231 uint16_t height) { |
| 234 width_ = width; | 232 width_ = width; |
| 235 height_ = height; | 233 height_ = height; |
| 236 // |user_frame_rate| is the target frame rate for VPM frame dropper. | 234 // |user_frame_rate| is the target frame rate for VPM frame dropper. |
| 237 user_frame_rate_ = frame_rate; | 235 user_frame_rate_ = frame_rate; |
| 238 image_type_ = GetImageType(width, height); | 236 image_type_ = GetImageType(width, height); |
| 239 } | 237 } |
| 240 | 238 |
| 241 // Update rate data after every encoded frame. | 239 // Update rate data after every encoded frame. |
| 242 void VCMQmResolution::UpdateEncodedSize(size_t encoded_size) { | 240 void VCMQmResolution::UpdateEncodedSize(size_t encoded_size) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 276 // at previous ~1sec. | 274 // at previous ~1sec. |
| 277 float diff = target_bitrate_ - encoder_sent_rate; | 275 float diff = target_bitrate_ - encoder_sent_rate; |
| 278 if (target_bitrate_ > 0.0) | 276 if (target_bitrate_ > 0.0) |
| 279 sum_rate_MM_ += fabs(diff) / target_bitrate_; | 277 sum_rate_MM_ += fabs(diff) / target_bitrate_; |
| 280 int sgnDiff = diff > 0 ? 1 : (diff < 0 ? -1 : 0); | 278 int sgnDiff = diff > 0 ? 1 : (diff < 0 ? -1 : 0); |
| 281 // To check for consistent under(+)/over_shooting(-) of target rate. | 279 // To check for consistent under(+)/over_shooting(-) of target rate. |
| 282 sum_rate_MM_sgn_ += sgnDiff; | 280 sum_rate_MM_sgn_ += sgnDiff; |
| 283 | 281 |
| 284 // Update with the current new target and frame rate: | 282 // Update with the current new target and frame rate: |
| 285 // these values are ones the encoder will use for the current/next ~1sec. | 283 // these values are ones the encoder will use for the current/next ~1sec. |
| 286 target_bitrate_ = target_bitrate; | 284 target_bitrate_ = target_bitrate; |
| 287 incoming_framerate_ = incoming_framerate; | 285 incoming_framerate_ = incoming_framerate; |
| 288 sum_incoming_framerate_ += incoming_framerate_; | 286 sum_incoming_framerate_ += incoming_framerate_; |
| 289 // Update the per_frame_bandwidth: | 287 // Update the per_frame_bandwidth: |
| 290 // this is the per_frame_bw for the current/next ~1sec. | 288 // this is the per_frame_bw for the current/next ~1sec. |
| 291 per_frame_bandwidth_ = 0.0f; | 289 per_frame_bandwidth_ = 0.0f; |
| 292 if (incoming_framerate_ > 0.0f) { | 290 if (incoming_framerate_ > 0.0f) { |
| 293 per_frame_bandwidth_ = target_bitrate_ / incoming_framerate_; | 291 per_frame_bandwidth_ = target_bitrate_ / incoming_framerate_; |
| 294 } | 292 } |
| 295 } | 293 } |
| 296 | 294 |
| 297 // Select the resolution factors: frame size and frame rate change (qm scales). | 295 // Select the resolution factors: frame size and frame rate change (qm scales). |
| 298 // Selection is for going down in resolution, or for going back up | 296 // Selection is for going down in resolution, or for going back up |
| 299 // (if a previous down-sampling action was taken). | 297 // (if a previous down-sampling action was taken). |
| 300 | 298 |
| 301 // In the current version the following constraints are imposed: | 299 // In the current version the following constraints are imposed: |
| 302 // 1) We only allow for one action, either down or up, at a given time. | 300 // 1) We only allow for one action, either down or up, at a given time. |
| 303 // 2) The possible down-sampling actions are: spatial by 1/2x1/2, 3/4x3/4; | 301 // 2) The possible down-sampling actions are: spatial by 1/2x1/2, 3/4x3/4; |
| 304 // temporal/frame rate reduction by 1/2 and 2/3. | 302 // temporal/frame rate reduction by 1/2 and 2/3. |
| 305 // 3) The action for going back up is the reverse of last (spatial or temporal) | 303 // 3) The action for going back up is the reverse of last (spatial or temporal) |
| 306 // down-sampling action. The list of down-sampling actions from the | 304 // down-sampling action. The list of down-sampling actions from the |
| 307 // Initialize() state are kept in |down_action_history_|. | 305 // Initialize() state are kept in |down_action_history_|. |
| 308 // 4) The total amount of down-sampling (spatial and/or temporal) from the | 306 // 4) The total amount of down-sampling (spatial and/or temporal) from the |
| 309 // Initialize() state (native resolution) is limited by various factors. | 307 // Initialize() state (native resolution) is limited by various factors. |
| 310 int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) { | 308 int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) { |
| 311 if (!init_) { | 309 if (!init_) { |
| 312 return VCM_UNINITIALIZED; | 310 return VCM_UNINITIALIZED; |
| 313 } | 311 } |
| 314 if (content_metrics_ == NULL) { | 312 if (content_metrics_ == NULL) { |
| 315 Reset(); | 313 Reset(); |
| 316 *qm = qm_; | 314 *qm = qm_; |
| 317 return VCM_OK; | 315 return VCM_OK; |
| 318 } | 316 } |
| 319 | 317 |
| 320 // Check conditions on down-sampling state. | 318 // Check conditions on down-sampling state. |
| 321 assert(state_dec_factor_spatial_ >= 1.0f); | 319 assert(state_dec_factor_spatial_ >= 1.0f); |
| 322 assert(state_dec_factor_temporal_ >= 1.0f); | 320 assert(state_dec_factor_temporal_ >= 1.0f); |
| 323 assert(state_dec_factor_spatial_ <= kMaxSpatialDown); | 321 assert(state_dec_factor_spatial_ <= kMaxSpatialDown); |
| 324 assert(state_dec_factor_temporal_ <= kMaxTempDown); | 322 assert(state_dec_factor_temporal_ <= kMaxTempDown); |
| 325 assert(state_dec_factor_temporal_ * state_dec_factor_spatial_ <= | 323 assert(state_dec_factor_temporal_ * state_dec_factor_spatial_ <= |
| 326 kMaxTotalDown); | 324 kMaxTotalDown); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 369 } | 367 } |
| 370 | 368 |
| 371 void VCMQmResolution::ComputeRatesForSelection() { | 369 void VCMQmResolution::ComputeRatesForSelection() { |
| 372 avg_target_rate_ = 0.0f; | 370 avg_target_rate_ = 0.0f; |
| 373 avg_incoming_framerate_ = 0.0f; | 371 avg_incoming_framerate_ = 0.0f; |
| 374 avg_ratio_buffer_low_ = 0.0f; | 372 avg_ratio_buffer_low_ = 0.0f; |
| 375 avg_rate_mismatch_ = 0.0f; | 373 avg_rate_mismatch_ = 0.0f; |
| 376 avg_rate_mismatch_sgn_ = 0.0f; | 374 avg_rate_mismatch_sgn_ = 0.0f; |
| 377 avg_packet_loss_ = 0.0f; | 375 avg_packet_loss_ = 0.0f; |
| 378 if (frame_cnt_ > 0) { | 376 if (frame_cnt_ > 0) { |
| 379 avg_ratio_buffer_low_ = static_cast<float>(low_buffer_cnt_) / | 377 avg_ratio_buffer_low_ = |
| 380 static_cast<float>(frame_cnt_); | 378 static_cast<float>(low_buffer_cnt_) / static_cast<float>(frame_cnt_); |
| 381 } | 379 } |
| 382 if (update_rate_cnt_ > 0) { | 380 if (update_rate_cnt_ > 0) { |
| 383 avg_rate_mismatch_ = static_cast<float>(sum_rate_MM_) / | 381 avg_rate_mismatch_ = |
| 384 static_cast<float>(update_rate_cnt_); | 382 static_cast<float>(sum_rate_MM_) / static_cast<float>(update_rate_cnt_); |
| 385 avg_rate_mismatch_sgn_ = static_cast<float>(sum_rate_MM_sgn_) / | 383 avg_rate_mismatch_sgn_ = static_cast<float>(sum_rate_MM_sgn_) / |
| 386 static_cast<float>(update_rate_cnt_); | 384 static_cast<float>(update_rate_cnt_); |
| 387 avg_target_rate_ = static_cast<float>(sum_target_rate_) / | 385 avg_target_rate_ = static_cast<float>(sum_target_rate_) / |
| 388 static_cast<float>(update_rate_cnt_); | 386 static_cast<float>(update_rate_cnt_); |
| 389 avg_incoming_framerate_ = static_cast<float>(sum_incoming_framerate_) / | 387 avg_incoming_framerate_ = static_cast<float>(sum_incoming_framerate_) / |
| 390 static_cast<float>(update_rate_cnt_); | 388 static_cast<float>(update_rate_cnt_); |
| 391 avg_packet_loss_ = static_cast<float>(sum_packet_loss_) / | 389 avg_packet_loss_ = static_cast<float>(sum_packet_loss_) / |
| 392 static_cast<float>(update_rate_cnt_); | 390 static_cast<float>(update_rate_cnt_); |
| 393 } | 391 } |
| 394 // For selection we may want to weight some quantities more heavily | 392 // For selection we may want to weight some quantities more heavily |
| 395 // with the current (i.e., next ~1sec) rate values. | 393 // with the current (i.e., next ~1sec) rate values. |
| 396 avg_target_rate_ = kWeightRate * avg_target_rate_ + | 394 avg_target_rate_ = |
| 397 (1.0 - kWeightRate) * target_bitrate_; | 395 kWeightRate * avg_target_rate_ + (1.0 - kWeightRate) * target_bitrate_; |
| 398 avg_incoming_framerate_ = kWeightRate * avg_incoming_framerate_ + | 396 avg_incoming_framerate_ = kWeightRate * avg_incoming_framerate_ + |
| 399 (1.0 - kWeightRate) * incoming_framerate_; | 397 (1.0 - kWeightRate) * incoming_framerate_; |
| 400 // Use base layer frame rate for temporal layers: this will favor spatial. | 398 // Use base layer frame rate for temporal layers: this will favor spatial. |
| 401 assert(num_layers_ > 0); | 399 assert(num_layers_ > 0); |
| 402 framerate_level_ = FrameRateLevel( | 400 framerate_level_ = FrameRateLevel(avg_incoming_framerate_ / |
| 403 avg_incoming_framerate_ / static_cast<float>(1 << (num_layers_ - 1))); | 401 static_cast<float>(1 << (num_layers_ - 1))); |
| 404 } | 402 } |
| 405 | 403 |
| 406 void VCMQmResolution::ComputeEncoderState() { | 404 void VCMQmResolution::ComputeEncoderState() { |
| 407 // Default. | 405 // Default. |
| 408 encoder_state_ = kStableEncoding; | 406 encoder_state_ = kStableEncoding; |
| 409 | 407 |
| 410 // Assign stressed state if: | 408 // Assign stressed state if: |
| 411 // 1) occurrences of low buffer levels is high, or | 409 // 1) occurrences of low buffer levels is high, or |
| 412 // 2) rate mis-match is high, and consistent over-shooting by encoder. | 410 // 2) rate mis-match is high, and consistent over-shooting by encoder. |
| 413 if ((avg_ratio_buffer_low_ > kMaxBufferLow) || | 411 if ((avg_ratio_buffer_low_ > kMaxBufferLow) || |
| 414 ((avg_rate_mismatch_ > kMaxRateMisMatch) && | 412 ((avg_rate_mismatch_ > kMaxRateMisMatch) && |
| 415 (avg_rate_mismatch_sgn_ < -kRateOverShoot))) { | 413 (avg_rate_mismatch_sgn_ < -kRateOverShoot))) { |
| 416 encoder_state_ = kStressedEncoding; | 414 encoder_state_ = kStressedEncoding; |
| 417 } | 415 } |
| 418 // Assign easy state if: | 416 // Assign easy state if: |
| 419 // 1) rate mis-match is high, and | 417 // 1) rate mis-match is high, and |
| 420 // 2) consistent under-shooting by encoder. | 418 // 2) consistent under-shooting by encoder. |
| 421 if ((avg_rate_mismatch_ > kMaxRateMisMatch) && | 419 if ((avg_rate_mismatch_ > kMaxRateMisMatch) && |
| 422 (avg_rate_mismatch_sgn_ > kRateUnderShoot)) { | 420 (avg_rate_mismatch_sgn_ > kRateUnderShoot)) { |
| 423 encoder_state_ = kEasyEncoding; | 421 encoder_state_ = kEasyEncoding; |
| 424 } | 422 } |
| 425 } | 423 } |
| 426 | 424 |
| 427 bool VCMQmResolution::GoingUpResolution() { | 425 bool VCMQmResolution::GoingUpResolution() { |
| 428 // For going up, we check for undoing the previous down-sampling action. | 426 // For going up, we check for undoing the previous down-sampling action. |
| 429 | 427 |
| 430 float fac_width = kFactorWidthSpatial[down_action_history_[0].spatial]; | 428 float fac_width = kFactorWidthSpatial[down_action_history_[0].spatial]; |
| 431 float fac_height = kFactorHeightSpatial[down_action_history_[0].spatial]; | 429 float fac_height = kFactorHeightSpatial[down_action_history_[0].spatial]; |
| 432 float fac_temp = kFactorTemporal[down_action_history_[0].temporal]; | 430 float fac_temp = kFactorTemporal[down_action_history_[0].temporal]; |
| 433 // For going up spatially, we allow for going up by 3/4x3/4 at each stage. | 431 // For going up spatially, we allow for going up by 3/4x3/4 at each stage. |
| 434 // So if the last spatial action was 1/2x1/2 it would be undone in 2 stages. | 432 // So if the last spatial action was 1/2x1/2 it would be undone in 2 stages. |
| 435 // Modify the fac_width/height for this case. | 433 // Modify the fac_width/height for this case. |
| 436 if (down_action_history_[0].spatial == kOneQuarterSpatialUniform) { | 434 if (down_action_history_[0].spatial == kOneQuarterSpatialUniform) { |
| 437 fac_width = kFactorWidthSpatial[kOneQuarterSpatialUniform] / | 435 fac_width = kFactorWidthSpatial[kOneQuarterSpatialUniform] / |
| 438 kFactorWidthSpatial[kOneHalfSpatialUniform]; | 436 kFactorWidthSpatial[kOneHalfSpatialUniform]; |
| 439 fac_height = kFactorHeightSpatial[kOneQuarterSpatialUniform] / | 437 fac_height = kFactorHeightSpatial[kOneQuarterSpatialUniform] / |
| 440 kFactorHeightSpatial[kOneHalfSpatialUniform]; | 438 kFactorHeightSpatial[kOneHalfSpatialUniform]; |
| 441 } | 439 } |
| 442 | 440 |
| 443 // Check if we should go up both spatially and temporally. | 441 // Check if we should go up both spatially and temporally. |
| 444 if (down_action_history_[0].spatial != kNoChangeSpatial && | 442 if (down_action_history_[0].spatial != kNoChangeSpatial && |
| 445 down_action_history_[0].temporal != kNoChangeTemporal) { | 443 down_action_history_[0].temporal != kNoChangeTemporal) { |
| 446 if (ConditionForGoingUp(fac_width, fac_height, fac_temp, | 444 if (ConditionForGoingUp(fac_width, fac_height, fac_temp, |
| 447 kTransRateScaleUpSpatialTemp)) { | 445 kTransRateScaleUpSpatialTemp)) { |
| 448 action_.spatial = down_action_history_[0].spatial; | 446 action_.spatial = down_action_history_[0].spatial; |
| 449 action_.temporal = down_action_history_[0].temporal; | 447 action_.temporal = down_action_history_[0].temporal; |
| 450 UpdateDownsamplingState(kUpResolution); | 448 UpdateDownsamplingState(kUpResolution); |
| 451 return true; | 449 return true; |
| 452 } | 450 } |
| 453 } | 451 } |
| 454 // Check if we should go up either spatially or temporally. | 452 // Check if we should go up either spatially or temporally. |
| 455 bool selected_up_spatial = false; | 453 bool selected_up_spatial = false; |
| 456 bool selected_up_temporal = false; | 454 bool selected_up_temporal = false; |
| 457 if (down_action_history_[0].spatial != kNoChangeSpatial) { | 455 if (down_action_history_[0].spatial != kNoChangeSpatial) { |
| 458 selected_up_spatial = ConditionForGoingUp(fac_width, fac_height, 1.0f, | 456 selected_up_spatial = ConditionForGoingUp(fac_width, fac_height, 1.0f, |
| 459 kTransRateScaleUpSpatial); | 457 kTransRateScaleUpSpatial); |
| 460 } | 458 } |
| 461 if (down_action_history_[0].temporal != kNoChangeTemporal) { | 459 if (down_action_history_[0].temporal != kNoChangeTemporal) { |
| 462 selected_up_temporal = ConditionForGoingUp(1.0f, 1.0f, fac_temp, | 460 selected_up_temporal = |
| 463 kTransRateScaleUpTemp); | 461 ConditionForGoingUp(1.0f, 1.0f, fac_temp, kTransRateScaleUpTemp); |
| 464 } | 462 } |
| 465 if (selected_up_spatial && !selected_up_temporal) { | 463 if (selected_up_spatial && !selected_up_temporal) { |
| 466 action_.spatial = down_action_history_[0].spatial; | 464 action_.spatial = down_action_history_[0].spatial; |
| 467 action_.temporal = kNoChangeTemporal; | 465 action_.temporal = kNoChangeTemporal; |
| 468 UpdateDownsamplingState(kUpResolution); | 466 UpdateDownsamplingState(kUpResolution); |
| 469 return true; | 467 return true; |
| 470 } else if (!selected_up_spatial && selected_up_temporal) { | 468 } else if (!selected_up_spatial && selected_up_temporal) { |
| 471 action_.spatial = kNoChangeSpatial; | 469 action_.spatial = kNoChangeSpatial; |
| 472 action_.temporal = down_action_history_[0].temporal; | 470 action_.temporal = down_action_history_[0].temporal; |
| 473 UpdateDownsamplingState(kUpResolution); | 471 UpdateDownsamplingState(kUpResolution); |
| 474 return true; | 472 return true; |
| 475 } else if (selected_up_spatial && selected_up_temporal) { | 473 } else if (selected_up_spatial && selected_up_temporal) { |
| 476 PickSpatialOrTemporal(); | 474 PickSpatialOrTemporal(); |
| 477 UpdateDownsamplingState(kUpResolution); | 475 UpdateDownsamplingState(kUpResolution); |
| 478 return true; | 476 return true; |
| 479 } | 477 } |
| 480 return false; | 478 return false; |
| 481 } | 479 } |
| 482 | 480 |
| 483 bool VCMQmResolution::ConditionForGoingUp(float fac_width, | 481 bool VCMQmResolution::ConditionForGoingUp(float fac_width, |
| 484 float fac_height, | 482 float fac_height, |
| 485 float fac_temp, | 483 float fac_temp, |
| 486 float scale_fac) { | 484 float scale_fac) { |
| 487 float estimated_transition_rate_up = GetTransitionRate(fac_width, fac_height, | 485 float estimated_transition_rate_up = |
| 488 fac_temp, scale_fac); | 486 GetTransitionRate(fac_width, fac_height, fac_temp, scale_fac); |
| 489 // Go back up if: | 487 // Go back up if: |
| 490 // 1) target rate is above threshold and current encoder state is stable, or | 488 // 1) target rate is above threshold and current encoder state is stable, or |
| 491 // 2) encoder state is easy (encoder is significantly under-shooting target). | 489 // 2) encoder state is easy (encoder is significantly under-shooting target). |
| 492 if (((avg_target_rate_ > estimated_transition_rate_up) && | 490 if (((avg_target_rate_ > estimated_transition_rate_up) && |
| 493 (encoder_state_ == kStableEncoding)) || | 491 (encoder_state_ == kStableEncoding)) || |
| 494 (encoder_state_ == kEasyEncoding)) { | 492 (encoder_state_ == kEasyEncoding)) { |
| 495 return true; | 493 return true; |
| 496 } else { | 494 } else { |
| 497 return false; | 495 return false; |
| 498 } | 496 } |
| 499 } | 497 } |
| 500 | 498 |
| 501 bool VCMQmResolution::GoingDownResolution() { | 499 bool VCMQmResolution::GoingDownResolution() { |
| 502 float estimated_transition_rate_down = | 500 float estimated_transition_rate_down = |
| 503 GetTransitionRate(1.0f, 1.0f, 1.0f, 1.0f); | 501 GetTransitionRate(1.0f, 1.0f, 1.0f, 1.0f); |
| 504 float max_rate = kFrameRateFac[framerate_level_] * kMaxRateQm[image_type_]; | 502 float max_rate = kFrameRateFac[framerate_level_] * kMaxRateQm[image_type_]; |
| 505 // Resolution reduction if: | 503 // Resolution reduction if: |
| 506 // (1) target rate is below transition rate, or | 504 // (1) target rate is below transition rate, or |
| 507 // (2) encoder is in stressed state and target rate below a max threshold. | 505 // (2) encoder is in stressed state and target rate below a max threshold. |
| 508 if ((avg_target_rate_ < estimated_transition_rate_down ) || | 506 if ((avg_target_rate_ < estimated_transition_rate_down) || |
| 509 (encoder_state_ == kStressedEncoding && avg_target_rate_ < max_rate)) { | 507 (encoder_state_ == kStressedEncoding && avg_target_rate_ < max_rate)) { |
| 510 // Get the down-sampling action: based on content class, and how low | 508 // Get the down-sampling action: based on content class, and how low |
| 511 // average target rate is relative to transition rate. | 509 // average target rate is relative to transition rate. |
| 512 uint8_t spatial_fact = | 510 uint8_t spatial_fact = |
| 513 kSpatialAction[content_class_ + | 511 kSpatialAction[content_class_ + |
| 514 9 * RateClass(estimated_transition_rate_down)]; | 512 9 * RateClass(estimated_transition_rate_down)]; |
| 515 uint8_t temp_fact = | 513 uint8_t temp_fact = |
| 516 kTemporalAction[content_class_ + | 514 kTemporalAction[content_class_ + |
| 517 9 * RateClass(estimated_transition_rate_down)]; | 515 9 * RateClass(estimated_transition_rate_down)]; |
| 518 | 516 |
| 519 switch (spatial_fact) { | 517 switch (spatial_fact) { |
| 520 case 4: { | 518 case 4: { |
| 521 action_.spatial = kOneQuarterSpatialUniform; | 519 action_.spatial = kOneQuarterSpatialUniform; |
| 522 break; | 520 break; |
| 523 } | 521 } |
| 524 case 2: { | 522 case 2: { |
| 525 action_.spatial = kOneHalfSpatialUniform; | 523 action_.spatial = kOneHalfSpatialUniform; |
| 526 break; | 524 break; |
| 527 } | 525 } |
| 528 case 1: { | 526 case 1: { |
| 529 action_.spatial = kNoChangeSpatial; | 527 action_.spatial = kNoChangeSpatial; |
| 530 break; | 528 break; |
| 531 } | 529 } |
| 532 default: { | 530 default: { assert(false); } |
| 533 assert(false); | |
| 534 } | |
| 535 } | 531 } |
| 536 switch (temp_fact) { | 532 switch (temp_fact) { |
| 537 case 3: { | 533 case 3: { |
| 538 action_.temporal = kTwoThirdsTemporal; | 534 action_.temporal = kTwoThirdsTemporal; |
| 539 break; | 535 break; |
| 540 } | 536 } |
| 541 case 2: { | 537 case 2: { |
| 542 action_.temporal = kOneHalfTemporal; | 538 action_.temporal = kOneHalfTemporal; |
| 543 break; | 539 break; |
| 544 } | 540 } |
| 545 case 1: { | 541 case 1: { |
| 546 action_.temporal = kNoChangeTemporal; | 542 action_.temporal = kNoChangeTemporal; |
| 547 break; | 543 break; |
| 548 } | 544 } |
| 549 default: { | 545 default: { assert(false); } |
| 550 assert(false); | |
| 551 } | |
| 552 } | 546 } |
| 553 // Only allow for one action (spatial or temporal) at a given time. | 547 // Only allow for one action (spatial or temporal) at a given time. |
| 554 assert(action_.temporal == kNoChangeTemporal || | 548 assert(action_.temporal == kNoChangeTemporal || |
| 555 action_.spatial == kNoChangeSpatial); | 549 action_.spatial == kNoChangeSpatial); |
| 556 | 550 |
| 557 // Adjust cases not captured in tables, mainly based on frame rate, and | 551 // Adjust cases not captured in tables, mainly based on frame rate, and |
| 558 // also check for odd frame sizes. | 552 // also check for odd frame sizes. |
| 559 AdjustAction(); | 553 AdjustAction(); |
| 560 | 554 |
| 561 // Update down-sampling state. | 555 // Update down-sampling state. |
| 562 if (action_.spatial != kNoChangeSpatial || | 556 if (action_.spatial != kNoChangeSpatial || |
| 563 action_.temporal != kNoChangeTemporal) { | 557 action_.temporal != kNoChangeTemporal) { |
| 564 UpdateDownsamplingState(kDownResolution); | 558 UpdateDownsamplingState(kDownResolution); |
| 565 return true; | 559 return true; |
| 566 } | 560 } |
| 567 } | 561 } |
| 568 return false; | 562 return false; |
| 569 } | 563 } |
| 570 | 564 |
| 571 float VCMQmResolution::GetTransitionRate(float fac_width, | 565 float VCMQmResolution::GetTransitionRate(float fac_width, |
| 572 float fac_height, | 566 float fac_height, |
| 573 float fac_temp, | 567 float fac_temp, |
| 574 float scale_fac) { | 568 float scale_fac) { |
| 575 ImageType image_type = GetImageType( | 569 ImageType image_type = |
| 576 static_cast<uint16_t>(fac_width * width_), | 570 GetImageType(static_cast<uint16_t>(fac_width * width_), |
| 577 static_cast<uint16_t>(fac_height * height_)); | 571 static_cast<uint16_t>(fac_height * height_)); |
| 578 | 572 |
| 579 FrameRateLevelClass framerate_level = | 573 FrameRateLevelClass framerate_level = |
| 580 FrameRateLevel(fac_temp * avg_incoming_framerate_); | 574 FrameRateLevel(fac_temp * avg_incoming_framerate_); |
| 581 // If we are checking for going up temporally, and this is the last | 575 // If we are checking for going up temporally, and this is the last |
| 582 // temporal action, then use native frame rate. | 576 // temporal action, then use native frame rate. |
| 583 if (down_action_history_[1].temporal == kNoChangeTemporal && | 577 if (down_action_history_[1].temporal == kNoChangeTemporal && |
| 584 fac_temp > 1.0f) { | 578 fac_temp > 1.0f) { |
| 585 framerate_level = FrameRateLevel(native_frame_rate_); | 579 framerate_level = FrameRateLevel(native_frame_rate_); |
| 586 } | 580 } |
| 587 | 581 |
| 588 // The maximum allowed rate below which down-sampling is allowed: | 582 // The maximum allowed rate below which down-sampling is allowed: |
| 589 // Nominal values based on image format (frame size and frame rate). | 583 // Nominal values based on image format (frame size and frame rate). |
| 590 float max_rate = kFrameRateFac[framerate_level] * kMaxRateQm[image_type]; | 584 float max_rate = kFrameRateFac[framerate_level] * kMaxRateQm[image_type]; |
| 591 | 585 |
| 592 uint8_t image_class = image_type > kVGA ? 1: 0; | 586 uint8_t image_class = image_type > kVGA ? 1 : 0; |
| 593 uint8_t table_index = image_class * 9 + content_class_; | 587 uint8_t table_index = image_class * 9 + content_class_; |
| 594 // Scale factor for down-sampling transition threshold: | 588 // Scale factor for down-sampling transition threshold: |
| 595 // factor based on the content class and the image size. | 589 // factor based on the content class and the image size. |
| 596 float scaleTransRate = kScaleTransRateQm[table_index]; | 590 float scaleTransRate = kScaleTransRateQm[table_index]; |
| 597 // Threshold bitrate for resolution action. | 591 // Threshold bitrate for resolution action. |
| 598 return static_cast<float> (scale_fac * scaleTransRate * max_rate); | 592 return static_cast<float>(scale_fac * scaleTransRate * max_rate); |
| 599 } | 593 } |
| 600 | 594 |
| 601 void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) { | 595 void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) { |
| 602 if (up_down == kUpResolution) { | 596 if (up_down == kUpResolution) { |
| 603 qm_->spatial_width_fact = 1.0f / kFactorWidthSpatial[action_.spatial]; | 597 qm_->spatial_width_fact = 1.0f / kFactorWidthSpatial[action_.spatial]; |
| 604 qm_->spatial_height_fact = 1.0f / kFactorHeightSpatial[action_.spatial]; | 598 qm_->spatial_height_fact = 1.0f / kFactorHeightSpatial[action_.spatial]; |
| 605 // If last spatial action was 1/2x1/2, we undo it in two steps, so the | 599 // If last spatial action was 1/2x1/2, we undo it in two steps, so the |
| 606 // spatial scale factor in this first step is modified as (4.0/3.0 / 2.0). | 600 // spatial scale factor in this first step is modified as (4.0/3.0 / 2.0). |
| 607 if (action_.spatial == kOneQuarterSpatialUniform) { | 601 if (action_.spatial == kOneQuarterSpatialUniform) { |
| 608 qm_->spatial_width_fact = | 602 qm_->spatial_width_fact = 1.0f * |
| 609 1.0f * kFactorWidthSpatial[kOneHalfSpatialUniform] / | 603 kFactorWidthSpatial[kOneHalfSpatialUniform] / |
| 610 kFactorWidthSpatial[kOneQuarterSpatialUniform]; | 604 kFactorWidthSpatial[kOneQuarterSpatialUniform]; |
| 611 qm_->spatial_height_fact = | 605 qm_->spatial_height_fact = |
| 612 1.0f * kFactorHeightSpatial[kOneHalfSpatialUniform] / | 606 1.0f * kFactorHeightSpatial[kOneHalfSpatialUniform] / |
| 613 kFactorHeightSpatial[kOneQuarterSpatialUniform]; | 607 kFactorHeightSpatial[kOneQuarterSpatialUniform]; |
| 614 } | 608 } |
| 615 qm_->temporal_fact = 1.0f / kFactorTemporal[action_.temporal]; | 609 qm_->temporal_fact = 1.0f / kFactorTemporal[action_.temporal]; |
| 616 RemoveLastDownAction(); | 610 RemoveLastDownAction(); |
| 617 } else if (up_down == kDownResolution) { | 611 } else if (up_down == kDownResolution) { |
| 618 ConstrainAmountOfDownSampling(); | 612 ConstrainAmountOfDownSampling(); |
| 619 ConvertSpatialFractionalToWhole(); | 613 ConvertSpatialFractionalToWhole(); |
| 620 qm_->spatial_width_fact = kFactorWidthSpatial[action_.spatial]; | 614 qm_->spatial_width_fact = kFactorWidthSpatial[action_.spatial]; |
| 621 qm_->spatial_height_fact = kFactorHeightSpatial[action_.spatial]; | 615 qm_->spatial_height_fact = kFactorHeightSpatial[action_.spatial]; |
| 622 qm_->temporal_fact = kFactorTemporal[action_.temporal]; | 616 qm_->temporal_fact = kFactorTemporal[action_.temporal]; |
| 623 InsertLatestDownAction(); | 617 InsertLatestDownAction(); |
| 624 } else { | 618 } else { |
| 625 // This function should only be called if either the Up or Down action | 619 // This function should only be called if either the Up or Down action |
| 626 // has been selected. | 620 // has been selected. |
| 627 assert(false); | 621 assert(false); |
| 628 } | 622 } |
| 629 UpdateCodecResolution(); | 623 UpdateCodecResolution(); |
| 630 state_dec_factor_spatial_ = state_dec_factor_spatial_ * | 624 state_dec_factor_spatial_ = state_dec_factor_spatial_ * |
| 631 qm_->spatial_width_fact * qm_->spatial_height_fact; | 625 qm_->spatial_width_fact * |
| 626 qm_->spatial_height_fact; |
| 632 state_dec_factor_temporal_ = state_dec_factor_temporal_ * qm_->temporal_fact; | 627 state_dec_factor_temporal_ = state_dec_factor_temporal_ * qm_->temporal_fact; |
| 633 } | 628 } |
| 634 | 629 |
| 635 void VCMQmResolution::UpdateCodecResolution() { | 630 void VCMQmResolution::UpdateCodecResolution() { |
| 636 if (action_.spatial != kNoChangeSpatial) { | 631 if (action_.spatial != kNoChangeSpatial) { |
| 637 qm_->change_resolution_spatial = true; | 632 qm_->change_resolution_spatial = true; |
| 638 qm_->codec_width = static_cast<uint16_t>(width_ / | 633 qm_->codec_width = |
| 639 qm_->spatial_width_fact + 0.5f); | 634 static_cast<uint16_t>(width_ / qm_->spatial_width_fact + 0.5f); |
| 640 qm_->codec_height = static_cast<uint16_t>(height_ / | 635 qm_->codec_height = |
| 641 qm_->spatial_height_fact + 0.5f); | 636 static_cast<uint16_t>(height_ / qm_->spatial_height_fact + 0.5f); |
| 642 // Size should not exceed native sizes. | 637 // Size should not exceed native sizes. |
| 643 assert(qm_->codec_width <= native_width_); | 638 assert(qm_->codec_width <= native_width_); |
| 644 assert(qm_->codec_height <= native_height_); | 639 assert(qm_->codec_height <= native_height_); |
| 645 // New sizes should be multiple of 2, otherwise spatial should not have | 640 // New sizes should be multiple of 2, otherwise spatial should not have |
| 646 // been selected. | 641 // been selected. |
| 647 assert(qm_->codec_width % 2 == 0); | 642 assert(qm_->codec_width % 2 == 0); |
| 648 assert(qm_->codec_height % 2 == 0); | 643 assert(qm_->codec_height % 2 == 0); |
| 649 } | 644 } |
| 650 if (action_.temporal != kNoChangeTemporal) { | 645 if (action_.temporal != kNoChangeTemporal) { |
| 651 qm_->change_resolution_temporal = true; | 646 qm_->change_resolution_temporal = true; |
| 652 // Update the frame rate based on the average incoming frame rate. | 647 // Update the frame rate based on the average incoming frame rate. |
| 653 qm_->frame_rate = avg_incoming_framerate_ / qm_->temporal_fact + 0.5f; | 648 qm_->frame_rate = avg_incoming_framerate_ / qm_->temporal_fact + 0.5f; |
| 654 if (down_action_history_[0].temporal == 0) { | 649 if (down_action_history_[0].temporal == 0) { |
| 655 // When we undo the last temporal-down action, make sure we go back up | 650 // When we undo the last temporal-down action, make sure we go back up |
| 656 // to the native frame rate. Since the incoming frame rate may | 651 // to the native frame rate. Since the incoming frame rate may |
| 657 // fluctuate over time, |avg_incoming_framerate_| scaled back up may | 652 // fluctuate over time, |avg_incoming_framerate_| scaled back up may |
| 658 // be smaller than |native_frame rate_|. | 653 // be smaller than |native_frame rate_|. |
| 659 qm_->frame_rate = native_frame_rate_; | 654 qm_->frame_rate = native_frame_rate_; |
| 660 } | 655 } |
| 661 } | 656 } |
| 662 } | 657 } |
| 663 | 658 |
| 664 uint8_t VCMQmResolution::RateClass(float transition_rate) { | 659 uint8_t VCMQmResolution::RateClass(float transition_rate) { |
| 665 return avg_target_rate_ < (kFacLowRate * transition_rate) ? 0: | 660 return avg_target_rate_ < (kFacLowRate * transition_rate) |
| 666 (avg_target_rate_ >= transition_rate ? 2 : 1); | 661 ? 0 |
| 662 : (avg_target_rate_ >= transition_rate ? 2 : 1); |
| 667 } | 663 } |
| 668 | 664 |
| 669 // TODO(marpan): Would be better to capture these frame rate adjustments by | 665 // TODO(marpan): Would be better to capture these frame rate adjustments by |
| 670 // extending the table data (qm_select_data.h). | 666 // extending the table data (qm_select_data.h). |
| 671 void VCMQmResolution::AdjustAction() { | 667 void VCMQmResolution::AdjustAction() { |
| 672 // If the spatial level is default state (neither low or high), motion level | 668 // If the spatial level is default state (neither low or high), motion level |
| 673 // is not high, and spatial action was selected, switch to 2/3 frame rate | 669 // is not high, and spatial action was selected, switch to 2/3 frame rate |
| 674 // reduction if the average incoming frame rate is high. | 670 // reduction if the average incoming frame rate is high. |
| 675 if (spatial_.level == kDefault && motion_.level != kHigh && | 671 if (spatial_.level == kDefault && motion_.level != kHigh && |
| 676 action_.spatial != kNoChangeSpatial && | 672 action_.spatial != kNoChangeSpatial && |
| (...skipping 14 matching lines...) Expand all Loading... |
| 691 // reduction already (i.e., 1/4), then switch to temporal action if the | 687 // reduction already (i.e., 1/4), then switch to temporal action if the |
| 692 // average frame rate is not low. | 688 // average frame rate is not low. |
| 693 if (action_.spatial != kNoChangeSpatial && | 689 if (action_.spatial != kNoChangeSpatial && |
| 694 down_action_history_[0].spatial == kOneQuarterSpatialUniform && | 690 down_action_history_[0].spatial == kOneQuarterSpatialUniform && |
| 695 framerate_level_ != kFrameRateLow) { | 691 framerate_level_ != kFrameRateLow) { |
| 696 action_.spatial = kNoChangeSpatial; | 692 action_.spatial = kNoChangeSpatial; |
| 697 action_.temporal = kTwoThirdsTemporal; | 693 action_.temporal = kTwoThirdsTemporal; |
| 698 } | 694 } |
| 699 // Never use temporal action if number of temporal layers is above 2. | 695 // Never use temporal action if number of temporal layers is above 2. |
| 700 if (num_layers_ > 2) { | 696 if (num_layers_ > 2) { |
| 701 if (action_.temporal != kNoChangeTemporal) { | 697 if (action_.temporal != kNoChangeTemporal) { |
| 702 action_.spatial = kOneHalfSpatialUniform; | 698 action_.spatial = kOneHalfSpatialUniform; |
| 703 } | 699 } |
| 704 action_.temporal = kNoChangeTemporal; | 700 action_.temporal = kNoChangeTemporal; |
| 705 } | 701 } |
| 706 // If spatial action was selected, we need to make sure the frame sizes | 702 // If spatial action was selected, we need to make sure the frame sizes |
| 707 // are multiples of two. Otherwise switch to 2/3 temporal. | 703 // are multiples of two. Otherwise switch to 2/3 temporal. |
| 708 if (action_.spatial != kNoChangeSpatial && | 704 if (action_.spatial != kNoChangeSpatial && !EvenFrameSize()) { |
| 709 !EvenFrameSize()) { | |
| 710 action_.spatial = kNoChangeSpatial; | 705 action_.spatial = kNoChangeSpatial; |
| 711 // Only one action (spatial or temporal) is allowed at a given time, so need | 706 // Only one action (spatial or temporal) is allowed at a given time, so need |
| 712 // to check whether temporal action is currently selected. | 707 // to check whether temporal action is currently selected. |
| 713 action_.temporal = kTwoThirdsTemporal; | 708 action_.temporal = kTwoThirdsTemporal; |
| 714 } | 709 } |
| 715 } | 710 } |
| 716 | 711 |
| 717 void VCMQmResolution::ConvertSpatialFractionalToWhole() { | 712 void VCMQmResolution::ConvertSpatialFractionalToWhole() { |
| 718 // If 3/4 spatial is selected, check if there has been another 3/4, | 713 // If 3/4 spatial is selected, check if there has been another 3/4, |
| 719 // and if so, combine them into 1/2. 1/2 scaling is more efficient than 9/16. | 714 // and if so, combine them into 1/2. 1/2 scaling is more efficient than 9/16. |
| 720 // Note we define 3/4x3/4 spatial as kOneHalfSpatialUniform. | 715 // Note we define 3/4x3/4 spatial as kOneHalfSpatialUniform. |
| 721 if (action_.spatial == kOneHalfSpatialUniform) { | 716 if (action_.spatial == kOneHalfSpatialUniform) { |
| 722 bool found = false; | 717 bool found = false; |
| 723 int isel = kDownActionHistorySize; | 718 int isel = kDownActionHistorySize; |
| 724 for (int i = 0; i < kDownActionHistorySize; ++i) { | 719 for (int i = 0; i < kDownActionHistorySize; ++i) { |
| 725 if (down_action_history_[i].spatial == kOneHalfSpatialUniform) { | 720 if (down_action_history_[i].spatial == kOneHalfSpatialUniform) { |
| 726 isel = i; | 721 isel = i; |
| 727 found = true; | 722 found = true; |
| 728 break; | 723 break; |
| 729 } | 724 } |
| 730 } | 725 } |
| 731 if (found) { | 726 if (found) { |
| 732 action_.spatial = kOneQuarterSpatialUniform; | 727 action_.spatial = kOneQuarterSpatialUniform; |
| 733 state_dec_factor_spatial_ = state_dec_factor_spatial_ / | 728 state_dec_factor_spatial_ = |
| 734 (kFactorWidthSpatial[kOneHalfSpatialUniform] * | 729 state_dec_factor_spatial_ / |
| 735 kFactorHeightSpatial[kOneHalfSpatialUniform]); | 730 (kFactorWidthSpatial[kOneHalfSpatialUniform] * |
| 736 // Check if switching to 1/2x1/2 (=1/4) spatial is allowed. | 731 kFactorHeightSpatial[kOneHalfSpatialUniform]); |
| 737 ConstrainAmountOfDownSampling(); | 732 // Check if switching to 1/2x1/2 (=1/4) spatial is allowed. |
| 738 if (action_.spatial == kNoChangeSpatial) { | 733 ConstrainAmountOfDownSampling(); |
| 739 // Not allowed. Go back to 3/4x3/4 spatial. | 734 if (action_.spatial == kNoChangeSpatial) { |
| 740 action_.spatial = kOneHalfSpatialUniform; | 735 // Not allowed. Go back to 3/4x3/4 spatial. |
| 741 state_dec_factor_spatial_ = state_dec_factor_spatial_ * | 736 action_.spatial = kOneHalfSpatialUniform; |
| 742 kFactorWidthSpatial[kOneHalfSpatialUniform] * | 737 state_dec_factor_spatial_ = |
| 743 kFactorHeightSpatial[kOneHalfSpatialUniform]; | 738 state_dec_factor_spatial_ * |
| 744 } else { | 739 kFactorWidthSpatial[kOneHalfSpatialUniform] * |
| 745 // Switching is allowed. Remove 3/4x3/4 from the history, and update | 740 kFactorHeightSpatial[kOneHalfSpatialUniform]; |
| 746 // the frame size. | 741 } else { |
| 747 for (int i = isel; i < kDownActionHistorySize - 1; ++i) { | 742 // Switching is allowed. Remove 3/4x3/4 from the history, and update |
| 748 down_action_history_[i].spatial = | 743 // the frame size. |
| 749 down_action_history_[i + 1].spatial; | 744 for (int i = isel; i < kDownActionHistorySize - 1; ++i) { |
| 750 } | 745 down_action_history_[i].spatial = down_action_history_[i + 1].spatial; |
| 751 width_ = width_ * kFactorWidthSpatial[kOneHalfSpatialUniform]; | 746 } |
| 752 height_ = height_ * kFactorHeightSpatial[kOneHalfSpatialUniform]; | 747 width_ = width_ * kFactorWidthSpatial[kOneHalfSpatialUniform]; |
| 753 } | 748 height_ = height_ * kFactorHeightSpatial[kOneHalfSpatialUniform]; |
| 749 } |
| 754 } | 750 } |
| 755 } | 751 } |
| 756 } | 752 } |
| 757 | 753 |
| 758 // Returns false if the new frame sizes, under the current spatial action, | 754 // Returns false if the new frame sizes, under the current spatial action, |
| 759 // are not multiples of two. | 755 // are not multiples of two. |
| 760 bool VCMQmResolution::EvenFrameSize() { | 756 bool VCMQmResolution::EvenFrameSize() { |
| 761 if (action_.spatial == kOneHalfSpatialUniform) { | 757 if (action_.spatial == kOneHalfSpatialUniform) { |
| 762 if ((width_ * 3 / 4) % 2 != 0 || (height_ * 3 / 4) % 2 != 0) { | 758 if ((width_ * 3 / 4) % 2 != 0 || (height_ * 3 / 4) % 2 != 0) { |
| 763 return false; | 759 return false; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 808 } | 804 } |
| 809 | 805 |
| 810 void VCMQmResolution::ConstrainAmountOfDownSampling() { | 806 void VCMQmResolution::ConstrainAmountOfDownSampling() { |
| 811 // Sanity checks on down-sampling selection: | 807 // Sanity checks on down-sampling selection: |
| 812 // override the settings for too small image size and/or frame rate. | 808 // override the settings for too small image size and/or frame rate. |
| 813 // Also check the limit on current down-sampling states. | 809 // Also check the limit on current down-sampling states. |
| 814 | 810 |
| 815 float spatial_width_fact = kFactorWidthSpatial[action_.spatial]; | 811 float spatial_width_fact = kFactorWidthSpatial[action_.spatial]; |
| 816 float spatial_height_fact = kFactorHeightSpatial[action_.spatial]; | 812 float spatial_height_fact = kFactorHeightSpatial[action_.spatial]; |
| 817 float temporal_fact = kFactorTemporal[action_.temporal]; | 813 float temporal_fact = kFactorTemporal[action_.temporal]; |
| 818 float new_dec_factor_spatial = state_dec_factor_spatial_ * | 814 float new_dec_factor_spatial = |
| 819 spatial_width_fact * spatial_height_fact; | 815 state_dec_factor_spatial_ * spatial_width_fact * spatial_height_fact; |
| 820 float new_dec_factor_temp = state_dec_factor_temporal_ * temporal_fact; | 816 float new_dec_factor_temp = state_dec_factor_temporal_ * temporal_fact; |
| 821 | 817 |
| 822 // No spatial sampling if current frame size is too small, or if the | 818 // No spatial sampling if current frame size is too small, or if the |
| 823 // amount of spatial down-sampling is above maximum spatial down-action. | 819 // amount of spatial down-sampling is above maximum spatial down-action. |
| 824 if ((width_ * height_) <= kMinImageSize || | 820 if ((width_ * height_) <= kMinImageSize || |
| 825 new_dec_factor_spatial > kMaxSpatialDown) { | 821 new_dec_factor_spatial > kMaxSpatialDown) { |
| 826 action_.spatial = kNoChangeSpatial; | 822 action_.spatial = kNoChangeSpatial; |
| 827 new_dec_factor_spatial = state_dec_factor_spatial_; | 823 new_dec_factor_spatial = state_dec_factor_spatial_; |
| 828 } | 824 } |
| 829 // No frame rate reduction if average frame rate is below some point, or if | 825 // No frame rate reduction if average frame rate is below some point, or if |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 901 qm_->spatial_height_fact = 2.0f; | 897 qm_->spatial_height_fact = 2.0f; |
| 902 } | 898 } |
| 903 } | 899 } |
| 904 | 900 |
| 905 // ROBUSTNESS CLASS | 901 // ROBUSTNESS CLASS |
| 906 | 902 |
| 907 VCMQmRobustness::VCMQmRobustness() { | 903 VCMQmRobustness::VCMQmRobustness() { |
| 908 Reset(); | 904 Reset(); |
| 909 } | 905 } |
| 910 | 906 |
| 911 VCMQmRobustness::~VCMQmRobustness() { | 907 VCMQmRobustness::~VCMQmRobustness() {} |
| 912 } | |
| 913 | 908 |
| 914 void VCMQmRobustness::Reset() { | 909 void VCMQmRobustness::Reset() { |
| 915 prev_total_rate_ = 0.0f; | 910 prev_total_rate_ = 0.0f; |
| 916 prev_rtt_time_ = 0; | 911 prev_rtt_time_ = 0; |
| 917 prev_packet_loss_ = 0; | 912 prev_packet_loss_ = 0; |
| 918 prev_code_rate_delta_ = 0; | 913 prev_code_rate_delta_ = 0; |
| 919 ResetQM(); | 914 ResetQM(); |
| 920 } | 915 } |
| 921 | 916 |
| 922 // Adjust the FEC rate based on the content and the network state | 917 // Adjust the FEC rate based on the content and the network state |
| 923 // (packet loss rate, total rate/bandwidth, round trip time). | 918 // (packet loss rate, total rate/bandwidth, round trip time). |
| 924 // Note that packetLoss here is the filtered loss value. | 919 // Note that packetLoss here is the filtered loss value. |
| 925 float VCMQmRobustness::AdjustFecFactor(uint8_t code_rate_delta, | 920 float VCMQmRobustness::AdjustFecFactor(uint8_t code_rate_delta, |
| 926 float total_rate, | 921 float total_rate, |
| 927 float framerate, | 922 float framerate, |
| 928 int64_t rtt_time, | 923 int64_t rtt_time, |
| 929 uint8_t packet_loss) { | 924 uint8_t packet_loss) { |
| 930 // Default: no adjustment | 925 // Default: no adjustment |
| 931 float adjust_fec = 1.0f; | 926 float adjust_fec = 1.0f; |
| 932 if (content_metrics_ == NULL) { | 927 if (content_metrics_ == NULL) { |
| 933 return adjust_fec; | 928 return adjust_fec; |
| 934 } | 929 } |
| 935 // Compute class state of the content. | 930 // Compute class state of the content. |
| 936 ComputeMotionNFD(); | 931 ComputeMotionNFD(); |
| 937 ComputeSpatial(); | 932 ComputeSpatial(); |
| 938 | 933 |
| 939 // TODO(marpan): Set FEC adjustment factor. | 934 // TODO(marpan): Set FEC adjustment factor. |
| 940 | 935 |
| 941 // Keep track of previous values of network state: | 936 // Keep track of previous values of network state: |
| 942 // adjustment may be also based on pattern of changes in network state. | 937 // adjustment may be also based on pattern of changes in network state. |
| 943 prev_total_rate_ = total_rate; | 938 prev_total_rate_ = total_rate; |
| 944 prev_rtt_time_ = rtt_time; | 939 prev_rtt_time_ = rtt_time; |
| 945 prev_packet_loss_ = packet_loss; | 940 prev_packet_loss_ = packet_loss; |
| 946 prev_code_rate_delta_ = code_rate_delta; | 941 prev_code_rate_delta_ = code_rate_delta; |
| 947 return adjust_fec; | 942 return adjust_fec; |
| 948 } | 943 } |
| 949 | 944 |
| 950 // Set the UEP (unequal-protection across packets) on/off for the FEC. | 945 // Set the UEP (unequal-protection across packets) on/off for the FEC. |
| 951 bool VCMQmRobustness::SetUepProtection(uint8_t code_rate_delta, | 946 bool VCMQmRobustness::SetUepProtection(uint8_t code_rate_delta, |
| 952 float total_rate, | 947 float total_rate, |
| 953 uint8_t packet_loss, | 948 uint8_t packet_loss, |
| 954 bool frame_type) { | 949 bool frame_type) { |
| 955 // Default. | 950 // Default. |
| 956 return false; | 951 return false; |
| 957 } | 952 } |
| 958 } // namespace | 953 } // namespace webrtc |
| OLD | NEW |