blob: fb261fe1eeb8283e3bb0a463af934d0517c63667 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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 Kjellander2557b862015-11-18 22:00:21 +010011#include "webrtc/modules/video_coding/qm_select.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
13#include <math.h>
14
Henrik Kjellanderff761fb2015-11-04 08:31:52 +010015#include "webrtc/modules/include/module_common_types.h"
Henrik Kjellander2557b862015-11-18 22:00:21 +010016#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 Kjellander98f53512015-10-28 18:17:40 +010019#include "webrtc/system_wrappers/include/trace.h"
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000020
niklase@google.com470e71d2011-07-07 08:21:25 +000021namespace webrtc {
22
marpan@google.com86548c62011-07-12 17:12:57 +000023// QM-METHOD class
24
25VCMQmMethod::VCMQmMethod()
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000026 : content_metrics_(NULL),
27 width_(0),
28 height_(0),
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +000029 user_frame_rate_(0.0f),
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000030 native_width_(0),
31 native_height_(0),
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +000032 native_frame_rate_(0.0f),
marpan@webrtc.orgcb8050c2012-09-11 20:34:15 +000033 image_type_(kVGA),
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +000034 framerate_level_(kFrameRateHigh),
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000035 init_(false) {
marpan@webrtc.org6584e582012-02-06 19:02:54 +000036 ResetQM();
niklase@google.com470e71d2011-07-07 08:21:25 +000037}
38
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000039VCMQmMethod::~VCMQmMethod() {
niklase@google.com470e71d2011-07-07 08:21:25 +000040}
41
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000042void VCMQmMethod::ResetQM() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000043 aspect_ratio_ = 1.0f;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000044 motion_.Reset();
45 spatial_.Reset();
46 content_class_ = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +000047}
48
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000049uint8_t VCMQmMethod::ComputeContentClass() {
50 ComputeMotionNFD();
51 ComputeSpatial();
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000052 return content_class_ = 3 * motion_.level + spatial_.level;
marpan@google.com86548c62011-07-12 17:12:57 +000053}
54
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000055void VCMQmMethod::UpdateContent(const VideoContentMetrics* contentMetrics) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000056 content_metrics_ = contentMetrics;
marpan@google.com86548c62011-07-12 17:12:57 +000057}
58
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000059void VCMQmMethod::ComputeMotionNFD() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000060 if (content_metrics_) {
61 motion_.value = content_metrics_->motion_magnitude;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000062 }
63 // Determine motion level.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000064 if (motion_.value < kLowMotionNfd) {
65 motion_.level = kLow;
66 } else if (motion_.value > kHighMotionNfd) {
67 motion_.level = kHigh;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000068 } else {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000069 motion_.level = kDefault;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000070 }
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +000071}
72
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000073void VCMQmMethod::ComputeSpatial() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000074 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.org9d76b4e2012-02-28 23:39:31 +000081 }
82 // Spatial measure: take average of 3 prediction errors.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000083 spatial_.value = (spatial_err + spatial_err_h + spatial_err_v) / 3.0f;
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +000084
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000085 // Reduce thresholds for large scenes/higher pixel correlation.
86 float scale2 = image_type_ > kVGA ? kScaleTexture : 1.0;
marpan@google.com86548c62011-07-12 17:12:57 +000087
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000088 if (spatial_.value > scale2 * kHighTexture) {
89 spatial_.level = kHigh;
90 } else if (spatial_.value < scale2 * kLowTexture) {
91 spatial_.level = kLow;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000092 } else {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000093 spatial_.level = kDefault;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +000094 }
marpan@google.com86548c62011-07-12 17:12:57 +000095}
96
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +000097ImageType 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.org9d76b4e2012-02-28 23:39:31 +0000119 } else {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000120 // No exact match, find closet one.
121 return FindClosestImageType(width, height);
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000122 }
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000123}
124
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000125ImageType 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.orge22d81c2012-03-20 18:21:53 +0000139FrameRateLevelClass VCMQmMethod::FrameRateLevel(float avg_framerate) {
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000140 if (avg_framerate <= kLowFrameRate) {
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000141 return kFrameRateLow;
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000142 } else if (avg_framerate <= kMiddleFrameRate) {
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000143 return kFrameRateMiddle1;
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000144 } else if (avg_framerate <= kHighFrameRate) {
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000145 return kFrameRateMiddle2;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000146 } else {
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000147 return kFrameRateHigh;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000148 }
marpan@google.com86548c62011-07-12 17:12:57 +0000149}
150
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000151// RESOLUTION CLASS
marpan@google.com86548c62011-07-12 17:12:57 +0000152
153VCMQmResolution::VCMQmResolution()
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000154 : qm_(new VCMResolutionScale()) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000155 Reset();
marpan@google.com86548c62011-07-12 17:12:57 +0000156}
157
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000158VCMQmResolution::~VCMQmResolution() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000159 delete qm_;
marpan@google.com86548c62011-07-12 17:12:57 +0000160}
161
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000162void VCMQmResolution::ResetRates() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000163 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.orgc5b392e2012-06-29 21:44:55 +0000168 buffer_level_ = kInitBufferLevel * target_bitrate_;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000169 frame_cnt_ = 0;
170 frame_cnt_delta_ = 0;
171 low_buffer_cnt_ = 0;
172 update_rate_cnt_ = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000173}
174
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000175void VCMQmResolution::ResetDownSamplingState() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000176 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.com470e71d2011-07-07 08:21:25 +0000182}
183
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000184void VCMQmResolution::Reset() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000185 target_bitrate_ = 0.0f;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000186 incoming_framerate_ = 0.0f;
187 buffer_level_ = 0.0f;
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000188 per_frame_bandwidth_ = 0.0f;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000189 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.org9d76b4e2012-02-28 23:39:31 +0000197 ResetRates();
198 ResetDownSamplingState();
199 ResetQM();
200}
niklase@google.com470e71d2011-07-07 08:21:25 +0000201
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000202EncoderState VCMQmResolution::GetEncoderState() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000203 return encoder_state_;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000204}
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000205
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000206// Initialize state after re-initializing the encoder,
207// i.e., after SetEncodingData() in mediaOpt.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000208int VCMQmResolution::Initialize(float bitrate,
209 float user_framerate,
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000210 uint16_t width,
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000211 uint16_t height,
212 int num_layers) {
213 if (user_framerate == 0.0f || width == 0 || height == 0) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000214 return VCM_PARAMETER_ERROR;
215 }
216 Reset();
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000217 target_bitrate_ = bitrate;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000218 incoming_framerate_ = user_framerate;
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000219 UpdateCodecParameters(user_framerate, width, height);
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000220 native_width_ = width;
221 native_height_ = height;
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000222 native_frame_rate_ = user_framerate;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000223 num_layers_ = num_layers;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000224 // Initial buffer level.
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000225 buffer_level_ = kInitBufferLevel * target_bitrate_;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000226 // Per-frame bandwidth.
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000227 per_frame_bandwidth_ = target_bitrate_ / user_framerate;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000228 init_ = true;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000229 return VCM_OK;
230}
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000231
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000232void VCMQmResolution::UpdateCodecParameters(float frame_rate, uint16_t width,
233 uint16_t height) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000234 width_ = width;
235 height_ = height;
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000236 // |user_frame_rate| is the target frame rate for VPM frame dropper.
237 user_frame_rate_ = frame_rate;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000238 image_type_ = GetImageType(width, height);
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000239}
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000240
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000241// Update rate data after every encoded frame.
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000242void VCMQmResolution::UpdateEncodedSize(size_t encoded_size) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000243 frame_cnt_++;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000244 // Convert to Kbps.
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000245 float encoded_size_kbits = 8.0f * static_cast<float>(encoded_size) / 1000.0f;
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000246
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000247 // Update the buffer level:
248 // Note this is not the actual encoder buffer level.
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000249 // |buffer_level_| is reset to an initial value after SelectResolution is
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000250 // called, and does not account for frame dropping by encoder or VCM.
251 buffer_level_ += per_frame_bandwidth_ - encoded_size_kbits;
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000252
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000253 // Counter for occurrences of low buffer level:
254 // low/negative values means encoder is likely dropping frames.
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000255 if (buffer_level_ <= kPercBufferThr * kInitBufferLevel * target_bitrate_) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000256 low_buffer_cnt_++;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000257 }
258}
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000259
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000260// Update various quantities after SetTargetRates in MediaOpt.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000261void VCMQmResolution::UpdateRates(float target_bitrate,
262 float encoder_sent_rate,
263 float incoming_framerate,
264 uint8_t packet_loss) {
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000265 // Sum the target bitrate: this is the encoder rate from previous update
266 // (~1sec), i.e, before the update for next ~1sec.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000267 sum_target_rate_ += target_bitrate_;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000268 update_rate_cnt_++;
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000269
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000270 // Sum the received (from RTCP reports) packet loss rates.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000271 sum_packet_loss_ += static_cast<float>(packet_loss / 255.0);
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000272
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.orgaccf6072012-03-07 17:16:10 +0000277 float diff = target_bitrate_ - encoder_sent_rate;
278 if (target_bitrate_ > 0.0)
279 sum_rate_MM_ += fabs(diff) / target_bitrate_;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000280 int sgnDiff = diff > 0 ? 1 : (diff < 0 ? -1 : 0);
281 // To check for consistent under(+)/over_shooting(-) of target rate.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000282 sum_rate_MM_sgn_ += sgnDiff;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000283
284 // Update with the current new target and frame rate:
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000285 // these values are ones the encoder will use for the current/next ~1sec.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000286 target_bitrate_ = target_bitrate;
287 incoming_framerate_ = incoming_framerate;
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000288 sum_incoming_framerate_ += incoming_framerate_;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000289 // Update the per_frame_bandwidth:
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000290 // this is the per_frame_bw for the current/next ~1sec.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000291 per_frame_bandwidth_ = 0.0f;
292 if (incoming_framerate_ > 0.0f) {
293 per_frame_bandwidth_ = target_bitrate_ / incoming_framerate_;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000294 }
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.orgaccf6072012-03-07 17:16:10 +0000302// 1) We only allow for one action, either down or up, at a given time.
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000303// 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.orgaccf6072012-03-07 17:16:10 +0000305// 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.org9d76b4e2012-02-28 23:39:31 +0000310int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000311 if (!init_) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000312 return VCM_UNINITIALIZED;
313 }
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000314 if (content_metrics_ == NULL) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000315 Reset();
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000316 *qm = qm_;
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000317 return VCM_OK;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000318 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000319
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000320 // 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.org9d76b4e2012-02-28 23:39:31 +0000328 // Compute content class for selection.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000329 content_class_ = ComputeContentClass();
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000330 // Compute various rate quantities for selection.
331 ComputeRatesForSelection();
niklase@google.com470e71d2011-07-07 08:21:25 +0000332
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000333 // Get the encoder state.
334 ComputeEncoderState();
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000335
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000336 // Default settings: no action.
337 SetDefaultAction();
338 *qm = qm_;
339
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000340 // Check for going back up in resolution, if we have had some down-sampling
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000341 // relative to native state in Initialize().
342 if (down_action_history_[0].spatial != kNoChangeSpatial ||
343 down_action_history_[0].temporal != kNoChangeTemporal) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000344 if (GoingUpResolution()) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000345 *qm = qm_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000346 return VCM_OK;
347 }
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000348 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000349
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000350 // Check for going down in resolution.
351 if (GoingDownResolution()) {
352 *qm = qm_;
353 return VCM_OK;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000354 }
355 return VCM_OK;
niklase@google.com470e71d2011-07-07 08:21:25 +0000356}
357
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000358void VCMQmResolution::SetDefaultAction() {
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000359 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.orgaccf6072012-03-07 17:16:10 +0000364 qm_->spatial_width_fact = 1.0f;
365 qm_->spatial_height_fact = 1.0f;
366 qm_->temporal_fact = 1.0f;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000367 action_.spatial = kNoChangeSpatial;
368 action_.temporal = kNoChangeTemporal;
369}
370
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000371void VCMQmResolution::ComputeRatesForSelection() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000372 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.org9d76b4e2012-02-28 23:39:31 +0000381 }
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000382 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.org9d76b4e2012-02-28 23:39:31 +0000393 }
394 // For selection we may want to weight some quantities more heavily
395 // with the current (i.e., next ~1sec) rate values.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000396 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.orge22d81c2012-03-20 18:21:53 +0000400 // Use base layer frame rate for temporal layers: this will favor spatial.
marpan@webrtc.org3fe32522012-03-20 22:13:24 +0000401 assert(num_layers_ > 0);
402 framerate_level_ = FrameRateLevel(
403 avg_incoming_framerate_ / static_cast<float>(1 << (num_layers_ - 1)));
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000404}
niklase@google.com470e71d2011-07-07 08:21:25 +0000405
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000406void VCMQmResolution::ComputeEncoderState() {
407 // Default.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000408 encoder_state_ = kStableEncoding;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000409
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.orgaccf6072012-03-07 17:16:10 +0000413 if ((avg_ratio_buffer_low_ > kMaxBufferLow) ||
414 ((avg_rate_mismatch_ > kMaxRateMisMatch) &&
415 (avg_rate_mismatch_sgn_ < -kRateOverShoot))) {
416 encoder_state_ = kStressedEncoding;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000417 }
418 // Assign easy state if:
419 // 1) rate mis-match is high, and
420 // 2) consistent under-shooting by encoder.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000421 if ((avg_rate_mismatch_ > kMaxRateMisMatch) &&
422 (avg_rate_mismatch_sgn_ > kRateUnderShoot)) {
423 encoder_state_ = kEasyEncoding;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000424 }
425}
426
427bool VCMQmResolution::GoingUpResolution() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000428 // For going up, we check for undoing the previous down-sampling action.
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000429
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000430 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.orgc5b392e2012-06-29 21:44:55 +0000433 // 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.orgaccf6072012-03-07 17:16:10 +0000442
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000443 // Check if we should go up both spatially and temporally.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000444 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.org9d76b4e2012-02-28 23:39:31 +0000450 UpdateDownsamplingState(kUpResolution);
451 return true;
452 }
453 }
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000454 // 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.org9d76b4e2012-02-28 23:39:31 +0000480 return false;
481}
482
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000483bool 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.org9d76b4e2012-02-28 23:39:31 +0000489 // 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.orgaccf6072012-03-07 17:16:10 +0000492 if (((avg_target_rate_ > estimated_transition_rate_up) &&
493 (encoder_state_ == kStableEncoding)) ||
494 (encoder_state_ == kEasyEncoding)) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000495 return true;
496 } else {
497 return false;
498 }
499}
500
501bool VCMQmResolution::GoingDownResolution() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000502 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.org9d76b4e2012-02-28 23:39:31 +0000505 // 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.orgaccf6072012-03-07 17:16:10 +0000508 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.org9d76b4e2012-02-28 23:39:31 +0000518
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000519 switch (spatial_fact) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000520 case 4: {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000521 action_.spatial = kOneQuarterSpatialUniform;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000522 break;
523 }
524 case 2: {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000525 action_.spatial = kOneHalfSpatialUniform;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000526 break;
527 }
528 case 1: {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000529 action_.spatial = kNoChangeSpatial;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000530 break;
531 }
532 default: {
533 assert(false);
534 }
535 }
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000536 switch (temp_fact) {
537 case 3: {
538 action_.temporal = kTwoThirdsTemporal;
539 break;
540 }
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000541 case 2: {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000542 action_.temporal = kOneHalfTemporal;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000543 break;
544 }
545 case 1: {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000546 action_.temporal = kNoChangeTemporal;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000547 break;
548 }
549 default: {
550 assert(false);
551 }
552 }
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000553 // Only allow for one action (spatial or temporal) at a given time.
554 assert(action_.temporal == kNoChangeTemporal ||
555 action_.spatial == kNoChangeSpatial);
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000556
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000557 // Adjust cases not captured in tables, mainly based on frame rate, and
558 // also check for odd frame sizes.
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000559 AdjustAction();
560
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000561 // Update down-sampling state.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000562 if (action_.spatial != kNoChangeSpatial ||
563 action_.temporal != kNoChangeTemporal) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000564 UpdateDownsamplingState(kDownResolution);
565 return true;
566 }
567 }
568 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000569}
570
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000571float 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.orge22d81c2012-03-20 18:21:53 +0000579 FrameRateLevelClass framerate_level =
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000580 FrameRateLevel(fac_temp * avg_incoming_framerate_);
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000581 // 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.com86548c62011-07-12 17:12:57 +0000587
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000588 // The maximum allowed rate below which down-sampling is allowed:
589 // Nominal values based on image format (frame size and frame rate).
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000590 float max_rate = kFrameRateFac[framerate_level] * kMaxRateQm[image_type];
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000591
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000592 uint8_t image_class = image_type > kVGA ? 1: 0;
593 uint8_t table_index = image_class * 9 + content_class_;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000594 // Scale factor for down-sampling transition threshold:
595 // factor based on the content class and the image size.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000596 float scaleTransRate = kScaleTransRateQm[table_index];
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000597 // Threshold bitrate for resolution action.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000598 return static_cast<float> (scale_fac * scaleTransRate * max_rate);
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000599}
600
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000601void VCMQmResolution::UpdateDownsamplingState(UpDownAction up_down) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000602 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.orgc5b392e2012-06-29 21:44:55 +0000605 // 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.orgaccf6072012-03-07 17:16:10 +0000615 qm_->temporal_fact = 1.0f / kFactorTemporal[action_.temporal];
616 RemoveLastDownAction();
617 } else if (up_down == kDownResolution) {
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000618 ConstrainAmountOfDownSampling();
619 ConvertSpatialFractionalToWhole();
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000620 qm_->spatial_width_fact = kFactorWidthSpatial[action_.spatial];
621 qm_->spatial_height_fact = kFactorHeightSpatial[action_.spatial];
622 qm_->temporal_fact = kFactorTemporal[action_.temporal];
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000623 InsertLatestDownAction();
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000624 } else {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000625 // This function should only be called if either the Up or Down action
626 // has been selected.
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000627 assert(false);
628 }
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000629 UpdateCodecResolution();
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000630 state_dec_factor_spatial_ = state_dec_factor_spatial_ *
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000631 qm_->spatial_width_fact * qm_->spatial_height_fact;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000632 state_dec_factor_temporal_ = state_dec_factor_temporal_ * qm_->temporal_fact;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000633}
634
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000635void VCMQmResolution::UpdateCodecResolution() {
636 if (action_.spatial != kNoChangeSpatial) {
637 qm_->change_resolution_spatial = true;
marpan@webrtc.org6503ecd2012-03-21 00:18:13 +0000638 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.orgc5b392e2012-06-29 21:44:55 +0000642 // Size should not exceed native sizes.
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000643 assert(qm_->codec_width <= native_width_);
644 assert(qm_->codec_height <= native_height_);
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000645 // New sizes should be multiple of 2, otherwise spatial should not have
646 // been selected.
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000647 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.orgaccf6072012-03-07 17:16:10 +0000664uint8_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.org9d76b4e2012-02-28 23:39:31 +0000667}
668
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000669// TODO(marpan): Would be better to capture these frame rate adjustments by
670// extending the table data (qm_select_data.h).
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000671void VCMQmResolution::AdjustAction() {
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000672 // 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.orgaccf6072012-03-07 17:16:10 +0000675 if (spatial_.level == kDefault && motion_.level != kHigh &&
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000676 action_.spatial != kNoChangeSpatial &&
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000677 framerate_level_ == kFrameRateHigh) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000678 action_.spatial = kNoChangeSpatial;
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000679 action_.temporal = kTwoThirdsTemporal;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000680 }
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000681 // 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.orgaccf6072012-03-07 17:16:10 +0000684 if (motion_.level == kLow && spatial_.level == kLow &&
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000685 framerate_level_ <= kFrameRateMiddle1 &&
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000686 action_.temporal != kNoChangeTemporal) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000687 action_.spatial = kOneHalfSpatialUniform;
688 action_.temporal = kNoChangeTemporal;
689 }
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000690 // 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.orge22d81c2012-03-20 18:21:53 +0000694 down_action_history_[0].spatial == kOneQuarterSpatialUniform &&
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000695 framerate_level_ != kFrameRateLow) {
696 action_.spatial = kNoChangeSpatial;
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000697 action_.temporal = kTwoThirdsTemporal;
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000698 }
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000699 // 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.orgc5b392e2012-06-29 21:44:55 +0000706 // 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.orge22d81c2012-03-20 18:21:53 +0000715}
716
717void 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.orgc5b392e2012-06-29 21:44:55 +0000732 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.orge22d81c2012-03-20 18:21:53 +0000754 }
755 }
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000756}
757
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000758// Returns false if the new frame sizes, under the current spatial action,
759// are not multiples of two.
760bool VCMQmResolution::EvenFrameSize() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000761 if (action_.spatial == kOneHalfSpatialUniform) {
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000762 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.orgaccf6072012-03-07 17:16:10 +0000768 }
769 }
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000770 return true;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000771}
772
773void VCMQmResolution::InsertLatestDownAction() {
774 if (action_.spatial != kNoChangeSpatial) {
775 for (int i = kDownActionHistorySize - 1; i > 0; --i) {
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000776 down_action_history_[i].spatial = down_action_history_[i - 1].spatial;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000777 }
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.orgc5b392e2012-06-29 21:44:55 +0000782 down_action_history_[i].temporal = down_action_history_[i - 1].temporal;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000783 }
784 down_action_history_[0].temporal = action_.temporal;
785 }
786}
787
788void VCMQmResolution::RemoveLastDownAction() {
789 if (action_.spatial != kNoChangeSpatial) {
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000790 // 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.orgaccf6072012-03-07 17:16:10 +0000799 }
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000800 }
801 if (action_.temporal != kNoChangeTemporal) {
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000802 for (int i = 0; i < kDownActionHistorySize - 1; ++i) {
803 down_action_history_[i].temporal = down_action_history_[i + 1].temporal;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000804 }
805 down_action_history_[kDownActionHistorySize - 1].temporal =
806 kNoChangeTemporal;
807 }
808}
809
810void 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.orgc5b392e2012-06-29 21:44:55 +0000815 float spatial_width_fact = kFactorWidthSpatial[action_.spatial];
816 float spatial_height_fact = kFactorHeightSpatial[action_.spatial];
817 float temporal_fact = kFactorTemporal[action_.temporal];
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000818 float new_dec_factor_spatial = state_dec_factor_spatial_ *
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000819 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.orgaccf6072012-03-07 17:16:10 +0000824 if ((width_ * height_) <= kMinImageSize ||
825 new_dec_factor_spatial > kMaxSpatialDown) {
826 action_.spatial = kNoChangeSpatial;
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000827 new_dec_factor_spatial = state_dec_factor_spatial_;
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000828 }
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000829 // 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.orgaccf6072012-03-07 17:16:10 +0000831 if (avg_incoming_framerate_ <= kMinFrameRate ||
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000832 new_dec_factor_temp > kMaxTempDown) {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000833 action_.temporal = kNoChangeTemporal;
marpan@webrtc.orgc5b392e2012-06-29 21:44:55 +0000834 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.orgaccf6072012-03-07 17:16:10 +0000851 }
852}
853
854void 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.org9d76b4e2012-02-28 23:39:31 +0000862 }
863}
864
marpan@webrtc.orge22d81c2012-03-20 18:21:53 +0000865// TODO(marpan): Update when we allow for directional spatial down-sampling.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000866void VCMQmResolution::SelectSpatialDirectionMode(float transition_rate) {
867 // Default is 4/3x4/3
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000868 // For bit rates well below transitional rate, we select 2x2.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000869 if (avg_target_rate_ < transition_rate * kRateRedSpatial2X2) {
870 qm_->spatial_width_fact = 2.0f;
871 qm_->spatial_height_fact = 2.0f;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000872 }
873 // Otherwise check prediction errors and aspect ratio.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000874 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.org9d76b4e2012-02-28 23:39:31 +0000881 }
882
883 // Favor 1x2 if aspect_ratio is 16:9.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000884 if (aspect_ratio_ >= 16.0f / 9.0f) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000885 // Check if 1x2 has lowest prediction error.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000886 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.org9d76b4e2012-02-28 23:39:31 +0000889 }
890 }
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000891 // 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.org9d76b4e2012-02-28 23:39:31 +0000896 }
897 // Check for 2x1 selection.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000898 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.org9d76b4e2012-02-28 23:39:31 +0000902 }
903}
marpan@google.com86548c62011-07-12 17:12:57 +0000904
905// ROBUSTNESS CLASS
906
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000907VCMQmRobustness::VCMQmRobustness() {
908 Reset();
marpan@google.com86548c62011-07-12 17:12:57 +0000909}
niklase@google.com470e71d2011-07-07 08:21:25 +0000910
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000911VCMQmRobustness::~VCMQmRobustness() {
niklase@google.com470e71d2011-07-07 08:21:25 +0000912}
913
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000914void VCMQmRobustness::Reset() {
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000915 prev_total_rate_ = 0.0f;
916 prev_rtt_time_ = 0;
917 prev_packet_loss_ = 0;
918 prev_code_rate_delta_ = 0;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000919 ResetQM();
niklase@google.com470e71d2011-07-07 08:21:25 +0000920}
921
marpan@google.com86548c62011-07-12 17:12:57 +0000922// 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.orgaccf6072012-03-07 17:16:10 +0000925float VCMQmRobustness::AdjustFecFactor(uint8_t code_rate_delta,
926 float total_rate,
927 float framerate,
pkasting@chromium.org16825b12015-01-12 21:51:21 +0000928 int64_t rtt_time,
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000929 uint8_t packet_loss) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000930 // Default: no adjustment
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000931 float adjust_fec = 1.0f;
932 if (content_metrics_ == NULL) {
933 return adjust_fec;
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000934 }
935 // Compute class state of the content.
936 ComputeMotionNFD();
937 ComputeSpatial();
marpan@webrtc.orgbd5648f2012-02-17 23:16:58 +0000938
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000939 // 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.orgaccf6072012-03-07 17:16:10 +0000943 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.com470e71d2011-07-07 08:21:25 +0000948}
949
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000950// Set the UEP (unequal-protection across packets) on/off for the FEC.
marpan@webrtc.orgaccf6072012-03-07 17:16:10 +0000951bool VCMQmRobustness::SetUepProtection(uint8_t code_rate_delta,
952 float total_rate,
953 uint8_t packet_loss,
954 bool frame_type) {
marpan@webrtc.org9d76b4e2012-02-28 23:39:31 +0000955 // Default.
956 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000957}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000958} // namespace