niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 1 | /* |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 2 | * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 3 | * |
| 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 |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
Henrik Kjellander | 2557b86 | 2015-11-18 22:00:21 +0100 | [diff] [blame] | 11 | #include "webrtc/modules/video_coding/qm_select.h" |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 12 | |
| 13 | #include <math.h> |
| 14 | |
Henrik Kjellander | ff761fb | 2015-11-04 08:31:52 +0100 | [diff] [blame] | 15 | #include "webrtc/modules/include/module_common_types.h" |
Henrik Kjellander | 2557b86 | 2015-11-18 22:00:21 +0100 | [diff] [blame] | 16 | #include "webrtc/modules/video_coding/include/video_coding_defines.h" |
| 17 | #include "webrtc/modules/video_coding/internal_defines.h" |
| 18 | #include "webrtc/modules/video_coding/qm_select_data.h" |
Henrik Kjellander | 98f5351 | 2015-10-28 18:17:40 +0100 | [diff] [blame] | 19 | #include "webrtc/system_wrappers/include/trace.h" |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 20 | |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 21 | namespace webrtc { |
| 22 | |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 23 | // QM-METHOD class |
| 24 | |
| 25 | VCMQmMethod::VCMQmMethod() |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 26 | : content_metrics_(NULL), |
| 27 | width_(0), |
| 28 | height_(0), |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 29 | user_frame_rate_(0.0f), |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 30 | native_width_(0), |
| 31 | native_height_(0), |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 32 | native_frame_rate_(0.0f), |
marpan@webrtc.org | cb8050c | 2012-09-11 20:34:15 +0000 | [diff] [blame] | 33 | image_type_(kVGA), |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 34 | framerate_level_(kFrameRateHigh), |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 35 | init_(false) { |
marpan@webrtc.org | 6584e58 | 2012-02-06 19:02:54 +0000 | [diff] [blame] | 36 | ResetQM(); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 37 | } |
| 38 | |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 39 | VCMQmMethod::~VCMQmMethod() {} |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 40 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 41 | void VCMQmMethod::ResetQM() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 42 | aspect_ratio_ = 1.0f; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 43 | motion_.Reset(); |
| 44 | spatial_.Reset(); |
| 45 | content_class_ = 0; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 46 | } |
| 47 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 48 | uint8_t VCMQmMethod::ComputeContentClass() { |
| 49 | ComputeMotionNFD(); |
| 50 | ComputeSpatial(); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 51 | return content_class_ = 3 * motion_.level + spatial_.level; |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 52 | } |
| 53 | |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 54 | void VCMQmMethod::UpdateContent(const VideoContentMetrics* contentMetrics) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 55 | content_metrics_ = contentMetrics; |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 56 | } |
| 57 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 58 | void VCMQmMethod::ComputeMotionNFD() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 59 | if (content_metrics_) { |
| 60 | motion_.value = content_metrics_->motion_magnitude; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 61 | } |
| 62 | // Determine motion level. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 63 | if (motion_.value < kLowMotionNfd) { |
| 64 | motion_.level = kLow; |
| 65 | } else if (motion_.value > kHighMotionNfd) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 66 | motion_.level = kHigh; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 67 | } else { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 68 | motion_.level = kDefault; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 69 | } |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 70 | } |
| 71 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 72 | void VCMQmMethod::ComputeSpatial() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 73 | float spatial_err = 0.0; |
| 74 | float spatial_err_h = 0.0; |
| 75 | float spatial_err_v = 0.0; |
| 76 | if (content_metrics_) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 77 | spatial_err = content_metrics_->spatial_pred_err; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 78 | spatial_err_h = content_metrics_->spatial_pred_err_h; |
| 79 | spatial_err_v = content_metrics_->spatial_pred_err_v; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 80 | } |
| 81 | // Spatial measure: take average of 3 prediction errors. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 82 | spatial_.value = (spatial_err + spatial_err_h + spatial_err_v) / 3.0f; |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 83 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 84 | // Reduce thresholds for large scenes/higher pixel correlation. |
| 85 | float scale2 = image_type_ > kVGA ? kScaleTexture : 1.0; |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 86 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 87 | if (spatial_.value > scale2 * kHighTexture) { |
| 88 | spatial_.level = kHigh; |
| 89 | } else if (spatial_.value < scale2 * kLowTexture) { |
| 90 | spatial_.level = kLow; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 91 | } else { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 92 | spatial_.level = kDefault; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 93 | } |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 94 | } |
| 95 | |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 96 | ImageType VCMQmMethod::GetImageType(uint16_t width, uint16_t height) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 97 | // Get the image type for the encoder frame size. |
| 98 | uint32_t image_size = width * height; |
| 99 | if (image_size == kSizeOfImageType[kQCIF]) { |
| 100 | return kQCIF; |
| 101 | } else if (image_size == kSizeOfImageType[kHCIF]) { |
| 102 | return kHCIF; |
| 103 | } else if (image_size == kSizeOfImageType[kQVGA]) { |
| 104 | return kQVGA; |
| 105 | } else if (image_size == kSizeOfImageType[kCIF]) { |
| 106 | return kCIF; |
| 107 | } else if (image_size == kSizeOfImageType[kHVGA]) { |
| 108 | return kHVGA; |
| 109 | } else if (image_size == kSizeOfImageType[kVGA]) { |
| 110 | return kVGA; |
| 111 | } else if (image_size == kSizeOfImageType[kQFULLHD]) { |
| 112 | return kQFULLHD; |
| 113 | } else if (image_size == kSizeOfImageType[kWHD]) { |
| 114 | return kWHD; |
| 115 | } else if (image_size == kSizeOfImageType[kFULLHD]) { |
| 116 | return kFULLHD; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 117 | } else { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 118 | // No exact match, find closet one. |
| 119 | return FindClosestImageType(width, height); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 120 | } |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 121 | } |
| 122 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 123 | ImageType VCMQmMethod::FindClosestImageType(uint16_t width, uint16_t height) { |
| 124 | float size = static_cast<float>(width * height); |
| 125 | float min = size; |
| 126 | int isel = 0; |
| 127 | for (int i = 0; i < kNumImageTypes; ++i) { |
| 128 | float dist = fabs(size - kSizeOfImageType[i]); |
| 129 | if (dist < min) { |
| 130 | min = dist; |
| 131 | isel = i; |
| 132 | } |
| 133 | } |
| 134 | return static_cast<ImageType>(isel); |
| 135 | } |
| 136 | |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 137 | FrameRateLevelClass VCMQmMethod::FrameRateLevel(float avg_framerate) { |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 138 | if (avg_framerate <= kLowFrameRate) { |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 139 | return kFrameRateLow; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 140 | } else if (avg_framerate <= kMiddleFrameRate) { |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 141 | return kFrameRateMiddle1; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 142 | } else if (avg_framerate <= kHighFrameRate) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 143 | return kFrameRateMiddle2; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 144 | } else { |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 145 | return kFrameRateHigh; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 146 | } |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 147 | } |
| 148 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 149 | // RESOLUTION CLASS |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 150 | |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 151 | VCMQmResolution::VCMQmResolution() : qm_(new VCMResolutionScale()) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 152 | Reset(); |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 153 | } |
| 154 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 155 | VCMQmResolution::~VCMQmResolution() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 156 | delete qm_; |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 157 | } |
| 158 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 159 | void VCMQmResolution::ResetRates() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 160 | sum_target_rate_ = 0.0f; |
| 161 | sum_incoming_framerate_ = 0.0f; |
| 162 | sum_rate_MM_ = 0.0f; |
| 163 | sum_rate_MM_sgn_ = 0.0f; |
| 164 | sum_packet_loss_ = 0.0f; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 165 | buffer_level_ = kInitBufferLevel * target_bitrate_; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 166 | frame_cnt_ = 0; |
| 167 | frame_cnt_delta_ = 0; |
| 168 | low_buffer_cnt_ = 0; |
| 169 | update_rate_cnt_ = 0; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 170 | } |
| 171 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 172 | void VCMQmResolution::ResetDownSamplingState() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 173 | state_dec_factor_spatial_ = 1.0; |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 174 | state_dec_factor_temporal_ = 1.0; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 175 | for (int i = 0; i < kDownActionHistorySize; i++) { |
| 176 | down_action_history_[i].spatial = kNoChangeSpatial; |
| 177 | down_action_history_[i].temporal = kNoChangeTemporal; |
| 178 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 179 | } |
| 180 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 181 | void VCMQmResolution::Reset() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 182 | target_bitrate_ = 0.0f; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 183 | incoming_framerate_ = 0.0f; |
| 184 | buffer_level_ = 0.0f; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 185 | per_frame_bandwidth_ = 0.0f; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 186 | avg_target_rate_ = 0.0f; |
| 187 | avg_incoming_framerate_ = 0.0f; |
| 188 | avg_ratio_buffer_low_ = 0.0f; |
| 189 | avg_rate_mismatch_ = 0.0f; |
| 190 | avg_rate_mismatch_sgn_ = 0.0f; |
| 191 | avg_packet_loss_ = 0.0f; |
| 192 | encoder_state_ = kStableEncoding; |
| 193 | num_layers_ = 1; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 194 | ResetRates(); |
| 195 | ResetDownSamplingState(); |
| 196 | ResetQM(); |
| 197 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 198 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 199 | EncoderState VCMQmResolution::GetEncoderState() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 200 | return encoder_state_; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 201 | } |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 202 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 203 | // Initialize state after re-initializing the encoder, |
| 204 | // i.e., after SetEncodingData() in mediaOpt. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 205 | int VCMQmResolution::Initialize(float bitrate, |
| 206 | float user_framerate, |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 207 | uint16_t width, |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 208 | uint16_t height, |
| 209 | int num_layers) { |
| 210 | if (user_framerate == 0.0f || width == 0 || height == 0) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 211 | return VCM_PARAMETER_ERROR; |
| 212 | } |
| 213 | Reset(); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 214 | target_bitrate_ = bitrate; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 215 | incoming_framerate_ = user_framerate; |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 216 | UpdateCodecParameters(user_framerate, width, height); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 217 | native_width_ = width; |
| 218 | native_height_ = height; |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 219 | native_frame_rate_ = user_framerate; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 220 | num_layers_ = num_layers; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 221 | // Initial buffer level. |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 222 | buffer_level_ = kInitBufferLevel * target_bitrate_; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 223 | // Per-frame bandwidth. |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 224 | per_frame_bandwidth_ = target_bitrate_ / user_framerate; |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 225 | init_ = true; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 226 | return VCM_OK; |
| 227 | } |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 228 | |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 229 | void VCMQmResolution::UpdateCodecParameters(float frame_rate, |
| 230 | uint16_t width, |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 231 | uint16_t height) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 232 | width_ = width; |
| 233 | height_ = height; |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 234 | // |user_frame_rate| is the target frame rate for VPM frame dropper. |
| 235 | user_frame_rate_ = frame_rate; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 236 | image_type_ = GetImageType(width, height); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 237 | } |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 238 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 239 | // Update rate data after every encoded frame. |
pbos@webrtc.org | 273a414 | 2014-12-01 15:23:21 +0000 | [diff] [blame] | 240 | void VCMQmResolution::UpdateEncodedSize(size_t encoded_size) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 241 | frame_cnt_++; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 242 | // Convert to Kbps. |
pkasting@chromium.org | 4591fbd | 2014-11-20 22:28:14 +0000 | [diff] [blame] | 243 | float encoded_size_kbits = 8.0f * static_cast<float>(encoded_size) / 1000.0f; |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 244 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 245 | // Update the buffer level: |
| 246 | // Note this is not the actual encoder buffer level. |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 247 | // |buffer_level_| is reset to an initial value after SelectResolution is |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 248 | // called, and does not account for frame dropping by encoder or VCM. |
| 249 | buffer_level_ += per_frame_bandwidth_ - encoded_size_kbits; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 250 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 251 | // Counter for occurrences of low buffer level: |
| 252 | // low/negative values means encoder is likely dropping frames. |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 253 | if (buffer_level_ <= kPercBufferThr * kInitBufferLevel * target_bitrate_) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 254 | low_buffer_cnt_++; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 255 | } |
| 256 | } |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 257 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 258 | // Update various quantities after SetTargetRates in MediaOpt. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 259 | void VCMQmResolution::UpdateRates(float target_bitrate, |
| 260 | float encoder_sent_rate, |
| 261 | float incoming_framerate, |
| 262 | uint8_t packet_loss) { |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 263 | // Sum the target bitrate: this is the encoder rate from previous update |
| 264 | // (~1sec), i.e, before the update for next ~1sec. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 265 | sum_target_rate_ += target_bitrate_; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 266 | update_rate_cnt_++; |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 267 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 268 | // Sum the received (from RTCP reports) packet loss rates. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 269 | sum_packet_loss_ += static_cast<float>(packet_loss / 255.0); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 270 | |
| 271 | // Sum the sequence rate mismatch: |
| 272 | // Mismatch here is based on the difference between the target rate |
| 273 | // used (in previous ~1sec) and the average actual encoding rate measured |
| 274 | // at previous ~1sec. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 275 | float diff = target_bitrate_ - encoder_sent_rate; |
| 276 | if (target_bitrate_ > 0.0) |
| 277 | sum_rate_MM_ += fabs(diff) / target_bitrate_; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 278 | int sgnDiff = diff > 0 ? 1 : (diff < 0 ? -1 : 0); |
| 279 | // To check for consistent under(+)/over_shooting(-) of target rate. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 280 | sum_rate_MM_sgn_ += sgnDiff; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 281 | |
| 282 | // Update with the current new target and frame rate: |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 283 | // these values are ones the encoder will use for the current/next ~1sec. |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 284 | target_bitrate_ = target_bitrate; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 285 | incoming_framerate_ = incoming_framerate; |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 286 | sum_incoming_framerate_ += incoming_framerate_; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 287 | // Update the per_frame_bandwidth: |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 288 | // this is the per_frame_bw for the current/next ~1sec. |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 289 | per_frame_bandwidth_ = 0.0f; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 290 | if (incoming_framerate_ > 0.0f) { |
| 291 | per_frame_bandwidth_ = target_bitrate_ / incoming_framerate_; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 292 | } |
| 293 | } |
| 294 | |
| 295 | // Select the resolution factors: frame size and frame rate change (qm scales). |
| 296 | // Selection is for going down in resolution, or for going back up |
| 297 | // (if a previous down-sampling action was taken). |
| 298 | |
| 299 | // In the current version the following constraints are imposed: |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 300 | // 1) We only allow for one action, either down or up, at a given time. |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 301 | // 2) The possible down-sampling actions are: spatial by 1/2x1/2, 3/4x3/4; |
| 302 | // temporal/frame rate reduction by 1/2 and 2/3. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 303 | // 3) The action for going back up is the reverse of last (spatial or temporal) |
| 304 | // down-sampling action. The list of down-sampling actions from the |
| 305 | // Initialize() state are kept in |down_action_history_|. |
| 306 | // 4) The total amount of down-sampling (spatial and/or temporal) from the |
| 307 | // Initialize() state (native resolution) is limited by various factors. |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 308 | int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 309 | if (!init_) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 310 | return VCM_UNINITIALIZED; |
| 311 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 312 | if (content_metrics_ == NULL) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 313 | Reset(); |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 314 | *qm = qm_; |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 315 | return VCM_OK; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 316 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 317 | |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 318 | // Check conditions on down-sampling state. |
| 319 | assert(state_dec_factor_spatial_ >= 1.0f); |
| 320 | assert(state_dec_factor_temporal_ >= 1.0f); |
| 321 | assert(state_dec_factor_spatial_ <= kMaxSpatialDown); |
| 322 | assert(state_dec_factor_temporal_ <= kMaxTempDown); |
| 323 | assert(state_dec_factor_temporal_ * state_dec_factor_spatial_ <= |
| 324 | kMaxTotalDown); |
| 325 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 326 | // Compute content class for selection. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 327 | content_class_ = ComputeContentClass(); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 328 | // Compute various rate quantities for selection. |
| 329 | ComputeRatesForSelection(); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 330 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 331 | // Get the encoder state. |
| 332 | ComputeEncoderState(); |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 333 | |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 334 | // Default settings: no action. |
| 335 | SetDefaultAction(); |
| 336 | *qm = qm_; |
| 337 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 338 | // Check for going back up in resolution, if we have had some down-sampling |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 339 | // relative to native state in Initialize(). |
| 340 | if (down_action_history_[0].spatial != kNoChangeSpatial || |
| 341 | down_action_history_[0].temporal != kNoChangeTemporal) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 342 | if (GoingUpResolution()) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 343 | *qm = qm_; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 344 | return VCM_OK; |
| 345 | } |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 346 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 347 | |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 348 | // Check for going down in resolution. |
| 349 | if (GoingDownResolution()) { |
| 350 | *qm = qm_; |
| 351 | return VCM_OK; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 352 | } |
| 353 | return VCM_OK; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 354 | } |
| 355 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 356 | void VCMQmResolution::SetDefaultAction() { |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 357 | qm_->codec_width = width_; |
| 358 | qm_->codec_height = height_; |
| 359 | qm_->frame_rate = user_frame_rate_; |
| 360 | qm_->change_resolution_spatial = false; |
| 361 | qm_->change_resolution_temporal = false; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 362 | qm_->spatial_width_fact = 1.0f; |
| 363 | qm_->spatial_height_fact = 1.0f; |
| 364 | qm_->temporal_fact = 1.0f; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 365 | action_.spatial = kNoChangeSpatial; |
| 366 | action_.temporal = kNoChangeTemporal; |
| 367 | } |
| 368 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 369 | void VCMQmResolution::ComputeRatesForSelection() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 370 | avg_target_rate_ = 0.0f; |
| 371 | avg_incoming_framerate_ = 0.0f; |
| 372 | avg_ratio_buffer_low_ = 0.0f; |
| 373 | avg_rate_mismatch_ = 0.0f; |
| 374 | avg_rate_mismatch_sgn_ = 0.0f; |
| 375 | avg_packet_loss_ = 0.0f; |
| 376 | if (frame_cnt_ > 0) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 377 | avg_ratio_buffer_low_ = |
| 378 | static_cast<float>(low_buffer_cnt_) / static_cast<float>(frame_cnt_); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 379 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 380 | if (update_rate_cnt_ > 0) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 381 | avg_rate_mismatch_ = |
| 382 | static_cast<float>(sum_rate_MM_) / static_cast<float>(update_rate_cnt_); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 383 | avg_rate_mismatch_sgn_ = static_cast<float>(sum_rate_MM_sgn_) / |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 384 | static_cast<float>(update_rate_cnt_); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 385 | avg_target_rate_ = static_cast<float>(sum_target_rate_) / |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 386 | static_cast<float>(update_rate_cnt_); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 387 | avg_incoming_framerate_ = static_cast<float>(sum_incoming_framerate_) / |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 388 | static_cast<float>(update_rate_cnt_); |
| 389 | avg_packet_loss_ = static_cast<float>(sum_packet_loss_) / |
| 390 | static_cast<float>(update_rate_cnt_); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 391 | } |
| 392 | // For selection we may want to weight some quantities more heavily |
| 393 | // with the current (i.e., next ~1sec) rate values. |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 394 | avg_target_rate_ = |
| 395 | kWeightRate * avg_target_rate_ + (1.0 - kWeightRate) * target_bitrate_; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 396 | avg_incoming_framerate_ = kWeightRate * avg_incoming_framerate_ + |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 397 | (1.0 - kWeightRate) * incoming_framerate_; |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 398 | // Use base layer frame rate for temporal layers: this will favor spatial. |
marpan@webrtc.org | 3fe3252 | 2012-03-20 22:13:24 +0000 | [diff] [blame] | 399 | assert(num_layers_ > 0); |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 400 | framerate_level_ = FrameRateLevel(avg_incoming_framerate_ / |
| 401 | static_cast<float>(1 << (num_layers_ - 1))); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 402 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 403 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 404 | void VCMQmResolution::ComputeEncoderState() { |
| 405 | // Default. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 406 | encoder_state_ = kStableEncoding; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 407 | |
| 408 | // Assign stressed state if: |
| 409 | // 1) occurrences of low buffer levels is high, or |
| 410 | // 2) rate mis-match is high, and consistent over-shooting by encoder. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 411 | if ((avg_ratio_buffer_low_ > kMaxBufferLow) || |
| 412 | ((avg_rate_mismatch_ > kMaxRateMisMatch) && |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 413 | (avg_rate_mismatch_sgn_ < -kRateOverShoot))) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 414 | encoder_state_ = kStressedEncoding; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 415 | } |
| 416 | // Assign easy state if: |
| 417 | // 1) rate mis-match is high, and |
| 418 | // 2) consistent under-shooting by encoder. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 419 | if ((avg_rate_mismatch_ > kMaxRateMisMatch) && |
| 420 | (avg_rate_mismatch_sgn_ > kRateUnderShoot)) { |
| 421 | encoder_state_ = kEasyEncoding; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 422 | } |
| 423 | } |
| 424 | |
| 425 | bool VCMQmResolution::GoingUpResolution() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 426 | // For going up, we check for undoing the previous down-sampling action. |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 427 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 428 | float fac_width = kFactorWidthSpatial[down_action_history_[0].spatial]; |
| 429 | float fac_height = kFactorHeightSpatial[down_action_history_[0].spatial]; |
| 430 | float fac_temp = kFactorTemporal[down_action_history_[0].temporal]; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 431 | // For going up spatially, we allow for going up by 3/4x3/4 at each stage. |
| 432 | // So if the last spatial action was 1/2x1/2 it would be undone in 2 stages. |
| 433 | // Modify the fac_width/height for this case. |
| 434 | if (down_action_history_[0].spatial == kOneQuarterSpatialUniform) { |
| 435 | fac_width = kFactorWidthSpatial[kOneQuarterSpatialUniform] / |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 436 | kFactorWidthSpatial[kOneHalfSpatialUniform]; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 437 | fac_height = kFactorHeightSpatial[kOneQuarterSpatialUniform] / |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 438 | kFactorHeightSpatial[kOneHalfSpatialUniform]; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 439 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 440 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 441 | // Check if we should go up both spatially and temporally. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 442 | if (down_action_history_[0].spatial != kNoChangeSpatial && |
| 443 | down_action_history_[0].temporal != kNoChangeTemporal) { |
| 444 | if (ConditionForGoingUp(fac_width, fac_height, fac_temp, |
| 445 | kTransRateScaleUpSpatialTemp)) { |
| 446 | action_.spatial = down_action_history_[0].spatial; |
| 447 | action_.temporal = down_action_history_[0].temporal; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 448 | UpdateDownsamplingState(kUpResolution); |
| 449 | return true; |
| 450 | } |
| 451 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 452 | // Check if we should go up either spatially or temporally. |
| 453 | bool selected_up_spatial = false; |
| 454 | bool selected_up_temporal = false; |
| 455 | if (down_action_history_[0].spatial != kNoChangeSpatial) { |
| 456 | selected_up_spatial = ConditionForGoingUp(fac_width, fac_height, 1.0f, |
| 457 | kTransRateScaleUpSpatial); |
| 458 | } |
| 459 | if (down_action_history_[0].temporal != kNoChangeTemporal) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 460 | selected_up_temporal = |
| 461 | ConditionForGoingUp(1.0f, 1.0f, fac_temp, kTransRateScaleUpTemp); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 462 | } |
| 463 | if (selected_up_spatial && !selected_up_temporal) { |
| 464 | action_.spatial = down_action_history_[0].spatial; |
| 465 | action_.temporal = kNoChangeTemporal; |
| 466 | UpdateDownsamplingState(kUpResolution); |
| 467 | return true; |
| 468 | } else if (!selected_up_spatial && selected_up_temporal) { |
| 469 | action_.spatial = kNoChangeSpatial; |
| 470 | action_.temporal = down_action_history_[0].temporal; |
| 471 | UpdateDownsamplingState(kUpResolution); |
| 472 | return true; |
| 473 | } else if (selected_up_spatial && selected_up_temporal) { |
| 474 | PickSpatialOrTemporal(); |
| 475 | UpdateDownsamplingState(kUpResolution); |
| 476 | return true; |
| 477 | } |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 478 | return false; |
| 479 | } |
| 480 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 481 | bool VCMQmResolution::ConditionForGoingUp(float fac_width, |
| 482 | float fac_height, |
| 483 | float fac_temp, |
| 484 | float scale_fac) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 485 | float estimated_transition_rate_up = |
| 486 | GetTransitionRate(fac_width, fac_height, fac_temp, scale_fac); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 487 | // Go back up if: |
| 488 | // 1) target rate is above threshold and current encoder state is stable, or |
| 489 | // 2) encoder state is easy (encoder is significantly under-shooting target). |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 490 | if (((avg_target_rate_ > estimated_transition_rate_up) && |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 491 | (encoder_state_ == kStableEncoding)) || |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 492 | (encoder_state_ == kEasyEncoding)) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 493 | return true; |
| 494 | } else { |
| 495 | return false; |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | bool VCMQmResolution::GoingDownResolution() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 500 | float estimated_transition_rate_down = |
| 501 | GetTransitionRate(1.0f, 1.0f, 1.0f, 1.0f); |
| 502 | float max_rate = kFrameRateFac[framerate_level_] * kMaxRateQm[image_type_]; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 503 | // Resolution reduction if: |
| 504 | // (1) target rate is below transition rate, or |
| 505 | // (2) encoder is in stressed state and target rate below a max threshold. |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 506 | if ((avg_target_rate_ < estimated_transition_rate_down) || |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 507 | (encoder_state_ == kStressedEncoding && avg_target_rate_ < max_rate)) { |
| 508 | // Get the down-sampling action: based on content class, and how low |
| 509 | // average target rate is relative to transition rate. |
| 510 | uint8_t spatial_fact = |
| 511 | kSpatialAction[content_class_ + |
| 512 | 9 * RateClass(estimated_transition_rate_down)]; |
| 513 | uint8_t temp_fact = |
| 514 | kTemporalAction[content_class_ + |
| 515 | 9 * RateClass(estimated_transition_rate_down)]; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 516 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 517 | switch (spatial_fact) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 518 | case 4: { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 519 | action_.spatial = kOneQuarterSpatialUniform; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 520 | break; |
| 521 | } |
| 522 | case 2: { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 523 | action_.spatial = kOneHalfSpatialUniform; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 524 | break; |
| 525 | } |
| 526 | case 1: { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 527 | action_.spatial = kNoChangeSpatial; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 528 | break; |
| 529 | } |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 530 | default: { assert(false); } |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 531 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 532 | switch (temp_fact) { |
| 533 | case 3: { |
| 534 | action_.temporal = kTwoThirdsTemporal; |
| 535 | break; |
| 536 | } |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 537 | case 2: { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 538 | action_.temporal = kOneHalfTemporal; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 539 | break; |
| 540 | } |
| 541 | case 1: { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 542 | action_.temporal = kNoChangeTemporal; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 543 | break; |
| 544 | } |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 545 | default: { assert(false); } |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 546 | } |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 547 | // Only allow for one action (spatial or temporal) at a given time. |
| 548 | assert(action_.temporal == kNoChangeTemporal || |
| 549 | action_.spatial == kNoChangeSpatial); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 550 | |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 551 | // Adjust cases not captured in tables, mainly based on frame rate, and |
| 552 | // also check for odd frame sizes. |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 553 | AdjustAction(); |
| 554 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 555 | // Update down-sampling state. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 556 | if (action_.spatial != kNoChangeSpatial || |
| 557 | action_.temporal != kNoChangeTemporal) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 558 | UpdateDownsamplingState(kDownResolution); |
| 559 | return true; |
| 560 | } |
| 561 | } |
| 562 | return false; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 563 | } |
| 564 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 565 | float VCMQmResolution::GetTransitionRate(float fac_width, |
| 566 | float fac_height, |
| 567 | float fac_temp, |
| 568 | float scale_fac) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 569 | ImageType image_type = |
| 570 | GetImageType(static_cast<uint16_t>(fac_width * width_), |
| 571 | static_cast<uint16_t>(fac_height * height_)); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 572 | |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 573 | FrameRateLevelClass framerate_level = |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 574 | FrameRateLevel(fac_temp * avg_incoming_framerate_); |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 575 | // If we are checking for going up temporally, and this is the last |
| 576 | // temporal action, then use native frame rate. |
| 577 | if (down_action_history_[1].temporal == kNoChangeTemporal && |
| 578 | fac_temp > 1.0f) { |
| 579 | framerate_level = FrameRateLevel(native_frame_rate_); |
| 580 | } |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 581 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 582 | // The maximum allowed rate below which down-sampling is allowed: |
| 583 | // Nominal values based on image format (frame size and frame rate). |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 584 | float max_rate = kFrameRateFac[framerate_level] * kMaxRateQm[image_type]; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 585 | |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 586 | uint8_t image_class = image_type > kVGA ? 1 : 0; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 587 | uint8_t table_index = image_class * 9 + content_class_; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 588 | // Scale factor for down-sampling transition threshold: |
| 589 | // factor based on the content class and the image size. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 590 | float scaleTransRate = kScaleTransRateQm[table_index]; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 591 | // Threshold bitrate for resolution action. |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 592 | return static_cast<float>(scale_fac * scaleTransRate * max_rate); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 593 | } |
| 594 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 595 | void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 596 | if (up_down == kUpResolution) { |
| 597 | qm_->spatial_width_fact = 1.0f / kFactorWidthSpatial[action_.spatial]; |
| 598 | qm_->spatial_height_fact = 1.0f / kFactorHeightSpatial[action_.spatial]; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 599 | // If last spatial action was 1/2x1/2, we undo it in two steps, so the |
| 600 | // spatial scale factor in this first step is modified as (4.0/3.0 / 2.0). |
| 601 | if (action_.spatial == kOneQuarterSpatialUniform) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 602 | qm_->spatial_width_fact = 1.0f * |
| 603 | kFactorWidthSpatial[kOneHalfSpatialUniform] / |
| 604 | kFactorWidthSpatial[kOneQuarterSpatialUniform]; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 605 | qm_->spatial_height_fact = |
| 606 | 1.0f * kFactorHeightSpatial[kOneHalfSpatialUniform] / |
| 607 | kFactorHeightSpatial[kOneQuarterSpatialUniform]; |
| 608 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 609 | qm_->temporal_fact = 1.0f / kFactorTemporal[action_.temporal]; |
| 610 | RemoveLastDownAction(); |
| 611 | } else if (up_down == kDownResolution) { |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 612 | ConstrainAmountOfDownSampling(); |
| 613 | ConvertSpatialFractionalToWhole(); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 614 | qm_->spatial_width_fact = kFactorWidthSpatial[action_.spatial]; |
| 615 | qm_->spatial_height_fact = kFactorHeightSpatial[action_.spatial]; |
| 616 | qm_->temporal_fact = kFactorTemporal[action_.temporal]; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 617 | InsertLatestDownAction(); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 618 | } else { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 619 | // This function should only be called if either the Up or Down action |
| 620 | // has been selected. |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 621 | assert(false); |
| 622 | } |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 623 | UpdateCodecResolution(); |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 624 | state_dec_factor_spatial_ = state_dec_factor_spatial_ * |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 625 | qm_->spatial_width_fact * |
| 626 | qm_->spatial_height_fact; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 627 | state_dec_factor_temporal_ = state_dec_factor_temporal_ * qm_->temporal_fact; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 628 | } |
| 629 | |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 630 | void VCMQmResolution::UpdateCodecResolution() { |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 631 | if (action_.spatial != kNoChangeSpatial) { |
| 632 | qm_->change_resolution_spatial = true; |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 633 | qm_->codec_width = |
| 634 | static_cast<uint16_t>(width_ / qm_->spatial_width_fact + 0.5f); |
| 635 | qm_->codec_height = |
| 636 | static_cast<uint16_t>(height_ / qm_->spatial_height_fact + 0.5f); |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 637 | // Size should not exceed native sizes. |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 638 | assert(qm_->codec_width <= native_width_); |
| 639 | assert(qm_->codec_height <= native_height_); |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 640 | // New sizes should be multiple of 2, otherwise spatial should not have |
| 641 | // been selected. |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 642 | assert(qm_->codec_width % 2 == 0); |
| 643 | assert(qm_->codec_height % 2 == 0); |
| 644 | } |
| 645 | if (action_.temporal != kNoChangeTemporal) { |
| 646 | qm_->change_resolution_temporal = true; |
| 647 | // Update the frame rate based on the average incoming frame rate. |
| 648 | qm_->frame_rate = avg_incoming_framerate_ / qm_->temporal_fact + 0.5f; |
| 649 | if (down_action_history_[0].temporal == 0) { |
| 650 | // When we undo the last temporal-down action, make sure we go back up |
| 651 | // to the native frame rate. Since the incoming frame rate may |
| 652 | // fluctuate over time, |avg_incoming_framerate_| scaled back up may |
| 653 | // be smaller than |native_frame rate_|. |
| 654 | qm_->frame_rate = native_frame_rate_; |
| 655 | } |
| 656 | } |
| 657 | } |
| 658 | |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 659 | uint8_t VCMQmResolution::RateClass(float transition_rate) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 660 | return avg_target_rate_ < (kFacLowRate * transition_rate) |
| 661 | ? 0 |
| 662 | : (avg_target_rate_ >= transition_rate ? 2 : 1); |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 663 | } |
| 664 | |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 665 | // TODO(marpan): Would be better to capture these frame rate adjustments by |
| 666 | // extending the table data (qm_select_data.h). |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 667 | void VCMQmResolution::AdjustAction() { |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 668 | // If the spatial level is default state (neither low or high), motion level |
| 669 | // is not high, and spatial action was selected, switch to 2/3 frame rate |
| 670 | // reduction if the average incoming frame rate is high. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 671 | if (spatial_.level == kDefault && motion_.level != kHigh && |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 672 | action_.spatial != kNoChangeSpatial && |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 673 | framerate_level_ == kFrameRateHigh) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 674 | action_.spatial = kNoChangeSpatial; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 675 | action_.temporal = kTwoThirdsTemporal; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 676 | } |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 677 | // If both motion and spatial level are low, and temporal down action was |
| 678 | // selected, switch to spatial 3/4x3/4 if the frame rate is not above the |
| 679 | // lower middle level (|kFrameRateMiddle1|). |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 680 | if (motion_.level == kLow && spatial_.level == kLow && |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 681 | framerate_level_ <= kFrameRateMiddle1 && |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 682 | action_.temporal != kNoChangeTemporal) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 683 | action_.spatial = kOneHalfSpatialUniform; |
| 684 | action_.temporal = kNoChangeTemporal; |
| 685 | } |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 686 | // If spatial action is selected, and there has been too much spatial |
| 687 | // reduction already (i.e., 1/4), then switch to temporal action if the |
| 688 | // average frame rate is not low. |
| 689 | if (action_.spatial != kNoChangeSpatial && |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 690 | down_action_history_[0].spatial == kOneQuarterSpatialUniform && |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 691 | framerate_level_ != kFrameRateLow) { |
| 692 | action_.spatial = kNoChangeSpatial; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 693 | action_.temporal = kTwoThirdsTemporal; |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 694 | } |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 695 | // Never use temporal action if number of temporal layers is above 2. |
| 696 | if (num_layers_ > 2) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 697 | if (action_.temporal != kNoChangeTemporal) { |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 698 | action_.spatial = kOneHalfSpatialUniform; |
| 699 | } |
| 700 | action_.temporal = kNoChangeTemporal; |
| 701 | } |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 702 | // If spatial action was selected, we need to make sure the frame sizes |
| 703 | // are multiples of two. Otherwise switch to 2/3 temporal. |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 704 | if (action_.spatial != kNoChangeSpatial && !EvenFrameSize()) { |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 705 | action_.spatial = kNoChangeSpatial; |
| 706 | // Only one action (spatial or temporal) is allowed at a given time, so need |
| 707 | // to check whether temporal action is currently selected. |
| 708 | action_.temporal = kTwoThirdsTemporal; |
| 709 | } |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 710 | } |
| 711 | |
| 712 | void VCMQmResolution::ConvertSpatialFractionalToWhole() { |
| 713 | // If 3/4 spatial is selected, check if there has been another 3/4, |
| 714 | // and if so, combine them into 1/2. 1/2 scaling is more efficient than 9/16. |
| 715 | // Note we define 3/4x3/4 spatial as kOneHalfSpatialUniform. |
| 716 | if (action_.spatial == kOneHalfSpatialUniform) { |
| 717 | bool found = false; |
| 718 | int isel = kDownActionHistorySize; |
| 719 | for (int i = 0; i < kDownActionHistorySize; ++i) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 720 | if (down_action_history_[i].spatial == kOneHalfSpatialUniform) { |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 721 | isel = i; |
| 722 | found = true; |
| 723 | break; |
| 724 | } |
| 725 | } |
| 726 | if (found) { |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 727 | action_.spatial = kOneQuarterSpatialUniform; |
| 728 | state_dec_factor_spatial_ = |
| 729 | state_dec_factor_spatial_ / |
| 730 | (kFactorWidthSpatial[kOneHalfSpatialUniform] * |
| 731 | kFactorHeightSpatial[kOneHalfSpatialUniform]); |
| 732 | // Check if switching to 1/2x1/2 (=1/4) spatial is allowed. |
| 733 | ConstrainAmountOfDownSampling(); |
| 734 | if (action_.spatial == kNoChangeSpatial) { |
| 735 | // Not allowed. Go back to 3/4x3/4 spatial. |
| 736 | action_.spatial = kOneHalfSpatialUniform; |
| 737 | state_dec_factor_spatial_ = |
| 738 | state_dec_factor_spatial_ * |
| 739 | kFactorWidthSpatial[kOneHalfSpatialUniform] * |
| 740 | kFactorHeightSpatial[kOneHalfSpatialUniform]; |
| 741 | } else { |
| 742 | // Switching is allowed. Remove 3/4x3/4 from the history, and update |
| 743 | // the frame size. |
| 744 | for (int i = isel; i < kDownActionHistorySize - 1; ++i) { |
| 745 | down_action_history_[i].spatial = down_action_history_[i + 1].spatial; |
| 746 | } |
| 747 | width_ = width_ * kFactorWidthSpatial[kOneHalfSpatialUniform]; |
| 748 | height_ = height_ * kFactorHeightSpatial[kOneHalfSpatialUniform]; |
| 749 | } |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 750 | } |
| 751 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 752 | } |
| 753 | |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 754 | // Returns false if the new frame sizes, under the current spatial action, |
| 755 | // are not multiples of two. |
| 756 | bool VCMQmResolution::EvenFrameSize() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 757 | if (action_.spatial == kOneHalfSpatialUniform) { |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 758 | if ((width_ * 3 / 4) % 2 != 0 || (height_ * 3 / 4) % 2 != 0) { |
| 759 | return false; |
| 760 | } |
| 761 | } else if (action_.spatial == kOneQuarterSpatialUniform) { |
| 762 | if ((width_ * 1 / 2) % 2 != 0 || (height_ * 1 / 2) % 2 != 0) { |
| 763 | return false; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 764 | } |
| 765 | } |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 766 | return true; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 767 | } |
| 768 | |
| 769 | void VCMQmResolution::InsertLatestDownAction() { |
| 770 | if (action_.spatial != kNoChangeSpatial) { |
| 771 | for (int i = kDownActionHistorySize - 1; i > 0; --i) { |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 772 | down_action_history_[i].spatial = down_action_history_[i - 1].spatial; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 773 | } |
| 774 | down_action_history_[0].spatial = action_.spatial; |
| 775 | } |
| 776 | if (action_.temporal != kNoChangeTemporal) { |
| 777 | for (int i = kDownActionHistorySize - 1; i > 0; --i) { |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 778 | down_action_history_[i].temporal = down_action_history_[i - 1].temporal; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 779 | } |
| 780 | down_action_history_[0].temporal = action_.temporal; |
| 781 | } |
| 782 | } |
| 783 | |
| 784 | void VCMQmResolution::RemoveLastDownAction() { |
| 785 | if (action_.spatial != kNoChangeSpatial) { |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 786 | // If the last spatial action was 1/2x1/2 we replace it with 3/4x3/4. |
| 787 | if (action_.spatial == kOneQuarterSpatialUniform) { |
| 788 | down_action_history_[0].spatial = kOneHalfSpatialUniform; |
| 789 | } else { |
| 790 | for (int i = 0; i < kDownActionHistorySize - 1; ++i) { |
| 791 | down_action_history_[i].spatial = down_action_history_[i + 1].spatial; |
| 792 | } |
| 793 | down_action_history_[kDownActionHistorySize - 1].spatial = |
| 794 | kNoChangeSpatial; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 795 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 796 | } |
| 797 | if (action_.temporal != kNoChangeTemporal) { |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 798 | for (int i = 0; i < kDownActionHistorySize - 1; ++i) { |
| 799 | down_action_history_[i].temporal = down_action_history_[i + 1].temporal; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 800 | } |
| 801 | down_action_history_[kDownActionHistorySize - 1].temporal = |
| 802 | kNoChangeTemporal; |
| 803 | } |
| 804 | } |
| 805 | |
| 806 | void VCMQmResolution::ConstrainAmountOfDownSampling() { |
| 807 | // Sanity checks on down-sampling selection: |
| 808 | // override the settings for too small image size and/or frame rate. |
| 809 | // Also check the limit on current down-sampling states. |
| 810 | |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 811 | float spatial_width_fact = kFactorWidthSpatial[action_.spatial]; |
| 812 | float spatial_height_fact = kFactorHeightSpatial[action_.spatial]; |
| 813 | float temporal_fact = kFactorTemporal[action_.temporal]; |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 814 | float new_dec_factor_spatial = |
| 815 | state_dec_factor_spatial_ * spatial_width_fact * spatial_height_fact; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 816 | float new_dec_factor_temp = state_dec_factor_temporal_ * temporal_fact; |
| 817 | |
| 818 | // No spatial sampling if current frame size is too small, or if the |
| 819 | // amount of spatial down-sampling is above maximum spatial down-action. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 820 | if ((width_ * height_) <= kMinImageSize || |
| 821 | new_dec_factor_spatial > kMaxSpatialDown) { |
| 822 | action_.spatial = kNoChangeSpatial; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 823 | new_dec_factor_spatial = state_dec_factor_spatial_; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 824 | } |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 825 | // No frame rate reduction if average frame rate is below some point, or if |
| 826 | // the amount of temporal down-sampling is above maximum temporal down-action. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 827 | if (avg_incoming_framerate_ <= kMinFrameRate || |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 828 | new_dec_factor_temp > kMaxTempDown) { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 829 | action_.temporal = kNoChangeTemporal; |
marpan@webrtc.org | c5b392e | 2012-06-29 21:44:55 +0000 | [diff] [blame] | 830 | new_dec_factor_temp = state_dec_factor_temporal_; |
| 831 | } |
| 832 | // Check if the total (spatial-temporal) down-action is above maximum allowed, |
| 833 | // if so, disallow the current selected down-action. |
| 834 | if (new_dec_factor_spatial * new_dec_factor_temp > kMaxTotalDown) { |
| 835 | if (action_.spatial != kNoChangeSpatial) { |
| 836 | action_.spatial = kNoChangeSpatial; |
| 837 | } else if (action_.temporal != kNoChangeTemporal) { |
| 838 | action_.temporal = kNoChangeTemporal; |
| 839 | } else { |
| 840 | // We only allow for one action (spatial or temporal) at a given time, so |
| 841 | // either spatial or temporal action is selected when this function is |
| 842 | // called. If the selected action is disallowed from one of the above |
| 843 | // 2 prior conditions (on spatial & temporal max down-action), then this |
| 844 | // condition "total down-action > |kMaxTotalDown|" would not be entered. |
| 845 | assert(false); |
| 846 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 847 | } |
| 848 | } |
| 849 | |
| 850 | void VCMQmResolution::PickSpatialOrTemporal() { |
| 851 | // Pick the one that has had the most down-sampling thus far. |
| 852 | if (state_dec_factor_spatial_ > state_dec_factor_temporal_) { |
| 853 | action_.spatial = down_action_history_[0].spatial; |
| 854 | action_.temporal = kNoChangeTemporal; |
| 855 | } else { |
| 856 | action_.spatial = kNoChangeSpatial; |
| 857 | action_.temporal = down_action_history_[0].temporal; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 858 | } |
| 859 | } |
| 860 | |
marpan@webrtc.org | e22d81c | 2012-03-20 18:21:53 +0000 | [diff] [blame] | 861 | // TODO(marpan): Update when we allow for directional spatial down-sampling. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 862 | void VCMQmResolution::SelectSpatialDirectionMode(float transition_rate) { |
| 863 | // Default is 4/3x4/3 |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 864 | // For bit rates well below transitional rate, we select 2x2. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 865 | if (avg_target_rate_ < transition_rate * kRateRedSpatial2X2) { |
| 866 | qm_->spatial_width_fact = 2.0f; |
| 867 | qm_->spatial_height_fact = 2.0f; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 868 | } |
| 869 | // Otherwise check prediction errors and aspect ratio. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 870 | float spatial_err = 0.0f; |
| 871 | float spatial_err_h = 0.0f; |
| 872 | float spatial_err_v = 0.0f; |
| 873 | if (content_metrics_) { |
| 874 | spatial_err = content_metrics_->spatial_pred_err; |
| 875 | spatial_err_h = content_metrics_->spatial_pred_err_h; |
| 876 | spatial_err_v = content_metrics_->spatial_pred_err_v; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 877 | } |
| 878 | |
| 879 | // Favor 1x2 if aspect_ratio is 16:9. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 880 | if (aspect_ratio_ >= 16.0f / 9.0f) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 881 | // Check if 1x2 has lowest prediction error. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 882 | if (spatial_err_h < spatial_err && spatial_err_h < spatial_err_v) { |
| 883 | qm_->spatial_width_fact = 2.0f; |
| 884 | qm_->spatial_height_fact = 1.0f; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 885 | } |
| 886 | } |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 887 | // Check for 4/3x4/3 selection: favor 2x2 over 1x2 and 2x1. |
| 888 | if (spatial_err < spatial_err_h * (1.0f + kSpatialErr2x2VsHoriz) && |
| 889 | spatial_err < spatial_err_v * (1.0f + kSpatialErr2X2VsVert)) { |
| 890 | qm_->spatial_width_fact = 4.0f / 3.0f; |
| 891 | qm_->spatial_height_fact = 4.0f / 3.0f; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 892 | } |
| 893 | // Check for 2x1 selection. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 894 | if (spatial_err_v < spatial_err_h * (1.0f - kSpatialErrVertVsHoriz) && |
| 895 | spatial_err_v < spatial_err * (1.0f - kSpatialErr2X2VsVert)) { |
| 896 | qm_->spatial_width_fact = 1.0f; |
| 897 | qm_->spatial_height_fact = 2.0f; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 898 | } |
| 899 | } |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 900 | |
| 901 | // ROBUSTNESS CLASS |
| 902 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 903 | VCMQmRobustness::VCMQmRobustness() { |
| 904 | Reset(); |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 905 | } |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 906 | |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 907 | VCMQmRobustness::~VCMQmRobustness() {} |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 908 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 909 | void VCMQmRobustness::Reset() { |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 910 | prev_total_rate_ = 0.0f; |
| 911 | prev_rtt_time_ = 0; |
| 912 | prev_packet_loss_ = 0; |
| 913 | prev_code_rate_delta_ = 0; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 914 | ResetQM(); |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 915 | } |
| 916 | |
marpan@google.com | 86548c6 | 2011-07-12 17:12:57 +0000 | [diff] [blame] | 917 | // Adjust the FEC rate based on the content and the network state |
| 918 | // (packet loss rate, total rate/bandwidth, round trip time). |
| 919 | // Note that packetLoss here is the filtered loss value. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 920 | float VCMQmRobustness::AdjustFecFactor(uint8_t code_rate_delta, |
| 921 | float total_rate, |
| 922 | float framerate, |
pkasting@chromium.org | 16825b1 | 2015-01-12 21:51:21 +0000 | [diff] [blame] | 923 | int64_t rtt_time, |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 924 | uint8_t packet_loss) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 925 | // Default: no adjustment |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 926 | float adjust_fec = 1.0f; |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 927 | if (content_metrics_ == NULL) { |
| 928 | return adjust_fec; |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 929 | } |
| 930 | // Compute class state of the content. |
| 931 | ComputeMotionNFD(); |
| 932 | ComputeSpatial(); |
marpan@webrtc.org | bd5648f | 2012-02-17 23:16:58 +0000 | [diff] [blame] | 933 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 934 | // TODO(marpan): Set FEC adjustment factor. |
| 935 | |
| 936 | // Keep track of previous values of network state: |
| 937 | // adjustment may be also based on pattern of changes in network state. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 938 | prev_total_rate_ = total_rate; |
| 939 | prev_rtt_time_ = rtt_time; |
| 940 | prev_packet_loss_ = packet_loss; |
| 941 | prev_code_rate_delta_ = code_rate_delta; |
| 942 | return adjust_fec; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 943 | } |
| 944 | |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 945 | // Set the UEP (unequal-protection across packets) on/off for the FEC. |
marpan@webrtc.org | accf607 | 2012-03-07 17:16:10 +0000 | [diff] [blame] | 946 | bool VCMQmRobustness::SetUepProtection(uint8_t code_rate_delta, |
| 947 | float total_rate, |
| 948 | uint8_t packet_loss, |
| 949 | bool frame_type) { |
marpan@webrtc.org | 9d76b4e | 2012-02-28 23:39:31 +0000 | [diff] [blame] | 950 | // Default. |
| 951 | return false; |
niklase@google.com | 470e71d | 2011-07-07 08:21:25 +0000 | [diff] [blame] | 952 | } |
philipel | 9d3ab61 | 2015-12-21 04:12:39 -0800 | [diff] [blame] | 953 | } // namespace webrtc |