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