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