1 /*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "webrtc/modules/video_coding/media_optimization.h"
12
13 #include "webrtc/base/logging.h"
14 #include "webrtc/modules/video_coding/content_metrics_processing.h"
15 #include "webrtc/modules/video_coding/qm_select.h"
16 #include "webrtc/modules/video_coding/utility/frame_dropper.h"
17 #include "webrtc/system_wrappers/include/clock.h"
18
19 namespace webrtc {
20 namespace media_optimization {
21 namespace {
UpdateProtectionCallback(VCMProtectionMethod * selected_method,uint32_t * video_rate_bps,uint32_t * nack_overhead_rate_bps,uint32_t * fec_overhead_rate_bps,VCMProtectionCallback * video_protection_callback)22 void UpdateProtectionCallback(
23 VCMProtectionMethod* selected_method,
24 uint32_t* video_rate_bps,
25 uint32_t* nack_overhead_rate_bps,
26 uint32_t* fec_overhead_rate_bps,
27 VCMProtectionCallback* video_protection_callback) {
28 FecProtectionParams delta_fec_params;
29 FecProtectionParams key_fec_params;
30 // Get the FEC code rate for Key frames (set to 0 when NA).
31 key_fec_params.fec_rate = selected_method->RequiredProtectionFactorK();
32
33 // Get the FEC code rate for Delta frames (set to 0 when NA).
34 delta_fec_params.fec_rate = selected_method->RequiredProtectionFactorD();
35
36 // Get the FEC-UEP protection status for Key frames: UEP on/off.
37 key_fec_params.use_uep_protection = selected_method->RequiredUepProtectionK();
38
39 // Get the FEC-UEP protection status for Delta frames: UEP on/off.
40 delta_fec_params.use_uep_protection =
41 selected_method->RequiredUepProtectionD();
42
43 // The RTP module currently requires the same |max_fec_frames| for both
44 // key and delta frames.
45 delta_fec_params.max_fec_frames = selected_method->MaxFramesFec();
46 key_fec_params.max_fec_frames = selected_method->MaxFramesFec();
47
48 // Set the FEC packet mask type. |kFecMaskBursty| is more effective for
49 // consecutive losses and little/no packet re-ordering. As we currently
50 // do not have feedback data on the degree of correlated losses and packet
51 // re-ordering, we keep default setting to |kFecMaskRandom| for now.
52 delta_fec_params.fec_mask_type = kFecMaskRandom;
53 key_fec_params.fec_mask_type = kFecMaskRandom;
54
55 // TODO(Marco): Pass FEC protection values per layer.
56 video_protection_callback->ProtectionRequest(
57 &delta_fec_params, &key_fec_params, video_rate_bps,
58 nack_overhead_rate_bps, fec_overhead_rate_bps);
59 }
60 } // namespace
61
62 struct MediaOptimization::EncodedFrameSample {
EncodedFrameSamplewebrtc::media_optimization::MediaOptimization::EncodedFrameSample63 EncodedFrameSample(size_t size_bytes,
64 uint32_t timestamp,
65 int64_t time_complete_ms)
66 : size_bytes(size_bytes),
67 timestamp(timestamp),
68 time_complete_ms(time_complete_ms) {}
69
70 size_t size_bytes;
71 uint32_t timestamp;
72 int64_t time_complete_ms;
73 };
74
MediaOptimization(Clock * clock)75 MediaOptimization::MediaOptimization(Clock* clock)
76 : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
77 clock_(clock),
78 max_bit_rate_(0),
79 send_codec_type_(kVideoCodecUnknown),
80 codec_width_(0),
81 codec_height_(0),
82 user_frame_rate_(0),
83 frame_dropper_(new FrameDropper),
84 loss_prot_logic_(
85 new VCMLossProtectionLogic(clock_->TimeInMilliseconds())),
86 fraction_lost_(0),
87 send_statistics_zero_encode_(0),
88 max_payload_size_(1460),
89 video_target_bitrate_(0),
90 incoming_frame_rate_(0),
91 enable_qm_(false),
92 encoded_frame_samples_(),
93 avg_sent_bit_rate_bps_(0),
94 avg_sent_framerate_(0),
95 key_frame_cnt_(0),
96 delta_frame_cnt_(0),
97 content_(new VCMContentMetricsProcessing()),
98 qm_resolution_(new VCMQmResolution()),
99 last_qm_update_time_(0),
100 last_change_time_(0),
101 num_layers_(0),
102 suspension_enabled_(false),
103 video_suspended_(false),
104 suspension_threshold_bps_(0),
105 suspension_window_bps_(0) {
106 memset(send_statistics_, 0, sizeof(send_statistics_));
107 memset(incoming_frame_times_, -1, sizeof(incoming_frame_times_));
108 }
109
~MediaOptimization(void)110 MediaOptimization::~MediaOptimization(void) {
111 loss_prot_logic_->Release();
112 }
113
Reset()114 void MediaOptimization::Reset() {
115 CriticalSectionScoped lock(crit_sect_.get());
116 SetEncodingDataInternal(kVideoCodecUnknown, 0, 0, 0, 0, 0, 0,
117 max_payload_size_);
118 memset(incoming_frame_times_, -1, sizeof(incoming_frame_times_));
119 incoming_frame_rate_ = 0.0;
120 frame_dropper_->Reset();
121 loss_prot_logic_->Reset(clock_->TimeInMilliseconds());
122 frame_dropper_->SetRates(0, 0);
123 content_->Reset();
124 qm_resolution_->Reset();
125 loss_prot_logic_->UpdateFrameRate(incoming_frame_rate_);
126 loss_prot_logic_->Reset(clock_->TimeInMilliseconds());
127 send_statistics_zero_encode_ = 0;
128 video_target_bitrate_ = 0;
129 codec_width_ = 0;
130 codec_height_ = 0;
131 user_frame_rate_ = 0;
132 key_frame_cnt_ = 0;
133 delta_frame_cnt_ = 0;
134 last_qm_update_time_ = 0;
135 last_change_time_ = 0;
136 encoded_frame_samples_.clear();
137 avg_sent_bit_rate_bps_ = 0;
138 num_layers_ = 1;
139 }
140
SetEncodingData(VideoCodecType send_codec_type,int32_t max_bit_rate,uint32_t target_bitrate,uint16_t width,uint16_t height,uint32_t frame_rate,int num_layers,int32_t mtu)141 void MediaOptimization::SetEncodingData(VideoCodecType send_codec_type,
142 int32_t max_bit_rate,
143 uint32_t target_bitrate,
144 uint16_t width,
145 uint16_t height,
146 uint32_t frame_rate,
147 int num_layers,
148 int32_t mtu) {
149 CriticalSectionScoped lock(crit_sect_.get());
150 SetEncodingDataInternal(send_codec_type, max_bit_rate, frame_rate,
151 target_bitrate, width, height, num_layers, mtu);
152 }
153
SetEncodingDataInternal(VideoCodecType send_codec_type,int32_t max_bit_rate,uint32_t frame_rate,uint32_t target_bitrate,uint16_t width,uint16_t height,int num_layers,int32_t mtu)154 void MediaOptimization::SetEncodingDataInternal(VideoCodecType send_codec_type,
155 int32_t max_bit_rate,
156 uint32_t frame_rate,
157 uint32_t target_bitrate,
158 uint16_t width,
159 uint16_t height,
160 int num_layers,
161 int32_t mtu) {
162 // Everything codec specific should be reset here since this means the codec
163 // has changed. If native dimension values have changed, then either user
164 // initiated change, or QM initiated change. Will be able to determine only
165 // after the processing of the first frame.
166 last_change_time_ = clock_->TimeInMilliseconds();
167 content_->Reset();
168 content_->UpdateFrameRate(frame_rate);
169
170 max_bit_rate_ = max_bit_rate;
171 send_codec_type_ = send_codec_type;
172 video_target_bitrate_ = target_bitrate;
173 float target_bitrate_kbps = static_cast<float>(target_bitrate) / 1000.0f;
174 loss_prot_logic_->UpdateBitRate(target_bitrate_kbps);
175 loss_prot_logic_->UpdateFrameRate(static_cast<float>(frame_rate));
176 loss_prot_logic_->UpdateFrameSize(width, height);
177 loss_prot_logic_->UpdateNumLayers(num_layers);
178 frame_dropper_->Reset();
179 frame_dropper_->SetRates(target_bitrate_kbps, static_cast<float>(frame_rate));
180 user_frame_rate_ = static_cast<float>(frame_rate);
181 codec_width_ = width;
182 codec_height_ = height;
183 num_layers_ = (num_layers <= 1) ? 1 : num_layers; // Can also be zero.
184 max_payload_size_ = mtu;
185 qm_resolution_->Initialize(target_bitrate_kbps, user_frame_rate_,
186 codec_width_, codec_height_, num_layers_);
187 }
188
SetTargetRates(uint32_t target_bitrate,uint8_t fraction_lost,int64_t round_trip_time_ms,VCMProtectionCallback * protection_callback,VCMQMSettingsCallback * qmsettings_callback)189 uint32_t MediaOptimization::SetTargetRates(
190 uint32_t target_bitrate,
191 uint8_t fraction_lost,
192 int64_t round_trip_time_ms,
193 VCMProtectionCallback* protection_callback,
194 VCMQMSettingsCallback* qmsettings_callback) {
195 CriticalSectionScoped lock(crit_sect_.get());
196 VCMProtectionMethod* selected_method = loss_prot_logic_->SelectedMethod();
197 float target_bitrate_kbps = static_cast<float>(target_bitrate) / 1000.0f;
198 loss_prot_logic_->UpdateBitRate(target_bitrate_kbps);
199 loss_prot_logic_->UpdateRtt(round_trip_time_ms);
200
201 // Get frame rate for encoder: this is the actual/sent frame rate.
202 float actual_frame_rate = SentFrameRateInternal();
203
204 // Sanity check.
205 if (actual_frame_rate < 1.0) {
206 actual_frame_rate = 1.0;
207 }
208
209 // Update frame rate for the loss protection logic class: frame rate should
210 // be the actual/sent rate.
211 loss_prot_logic_->UpdateFrameRate(actual_frame_rate);
212
213 fraction_lost_ = fraction_lost;
214
215 // Returns the filtered packet loss, used for the protection setting.
216 // The filtered loss may be the received loss (no filter), or some
217 // filtered value (average or max window filter).
218 // Use max window filter for now.
219 FilterPacketLossMode filter_mode = kMaxFilter;
220 uint8_t packet_loss_enc = loss_prot_logic_->FilteredLoss(
221 clock_->TimeInMilliseconds(), filter_mode, fraction_lost);
222
223 // For now use the filtered loss for computing the robustness settings.
224 loss_prot_logic_->UpdateFilteredLossPr(packet_loss_enc);
225
226 // Rate cost of the protection methods.
227 float protection_overhead_rate = 0.0f;
228
229 // Update protection settings, when applicable.
230 float sent_video_rate_kbps = 0.0f;
231 if (loss_prot_logic_->SelectedType() != kNone) {
232 // Update protection method with content metrics.
233 selected_method->UpdateContentMetrics(content_->ShortTermAvgData());
234
235 // Update method will compute the robustness settings for the given
236 // protection method and the overhead cost
237 // the protection method is set by the user via SetVideoProtection.
238 loss_prot_logic_->UpdateMethod();
239
240 // Update protection callback with protection settings.
241 uint32_t sent_video_rate_bps = 0;
242 uint32_t sent_nack_rate_bps = 0;
243 uint32_t sent_fec_rate_bps = 0;
244 // Get the bit cost of protection method, based on the amount of
245 // overhead data actually transmitted (including headers) the last
246 // second.
247 if (protection_callback) {
248 UpdateProtectionCallback(selected_method, &sent_video_rate_bps,
249 &sent_nack_rate_bps, &sent_fec_rate_bps,
250 protection_callback);
251 }
252 uint32_t sent_total_rate_bps =
253 sent_video_rate_bps + sent_nack_rate_bps + sent_fec_rate_bps;
254 // Estimate the overhead costs of the next second as staying the same
255 // wrt the source bitrate.
256 if (sent_total_rate_bps > 0) {
257 protection_overhead_rate =
258 static_cast<float>(sent_nack_rate_bps + sent_fec_rate_bps) /
259 sent_total_rate_bps;
260 }
261 // Cap the overhead estimate to 50%.
262 if (protection_overhead_rate > 0.5)
263 protection_overhead_rate = 0.5;
264
265 // Get the effective packet loss for encoder ER when applicable. Should be
266 // passed to encoder via fraction_lost.
267 packet_loss_enc = selected_method->RequiredPacketLossER();
268 sent_video_rate_kbps = static_cast<float>(sent_video_rate_bps) / 1000.0f;
269 }
270
271 // Source coding rate: total rate - protection overhead.
272 video_target_bitrate_ = target_bitrate * (1.0 - protection_overhead_rate);
273
274 // Cap target video bitrate to codec maximum.
275 if (max_bit_rate_ > 0 && video_target_bitrate_ > max_bit_rate_) {
276 video_target_bitrate_ = max_bit_rate_;
277 }
278
279 // Update encoding rates following protection settings.
280 float target_video_bitrate_kbps =
281 static_cast<float>(video_target_bitrate_) / 1000.0f;
282 frame_dropper_->SetRates(target_video_bitrate_kbps, incoming_frame_rate_);
283
284 if (enable_qm_ && qmsettings_callback) {
285 // Update QM with rates.
286 qm_resolution_->UpdateRates(target_video_bitrate_kbps, sent_video_rate_kbps,
287 incoming_frame_rate_, fraction_lost_);
288 // Check for QM selection.
289 bool select_qm = CheckStatusForQMchange();
290 if (select_qm) {
291 SelectQuality(qmsettings_callback);
292 }
293 // Reset the short-term averaged content data.
294 content_->ResetShortTermAvgData();
295 }
296
297 CheckSuspendConditions();
298
299 return video_target_bitrate_;
300 }
301
SetProtectionMethod(VCMProtectionMethodEnum method)302 void MediaOptimization::SetProtectionMethod(VCMProtectionMethodEnum method) {
303 CriticalSectionScoped lock(crit_sect_.get());
304 loss_prot_logic_->SetMethod(method);
305 }
306
InputFrameRate()307 uint32_t MediaOptimization::InputFrameRate() {
308 CriticalSectionScoped lock(crit_sect_.get());
309 return InputFrameRateInternal();
310 }
311
InputFrameRateInternal()312 uint32_t MediaOptimization::InputFrameRateInternal() {
313 ProcessIncomingFrameRate(clock_->TimeInMilliseconds());
314 return uint32_t(incoming_frame_rate_ + 0.5f);
315 }
316
SentFrameRate()317 uint32_t MediaOptimization::SentFrameRate() {
318 CriticalSectionScoped lock(crit_sect_.get());
319 return SentFrameRateInternal();
320 }
321
SentFrameRateInternal()322 uint32_t MediaOptimization::SentFrameRateInternal() {
323 PurgeOldFrameSamples(clock_->TimeInMilliseconds());
324 UpdateSentFramerate();
325 return avg_sent_framerate_;
326 }
327
SentBitRate()328 uint32_t MediaOptimization::SentBitRate() {
329 CriticalSectionScoped lock(crit_sect_.get());
330 const int64_t now_ms = clock_->TimeInMilliseconds();
331 PurgeOldFrameSamples(now_ms);
332 UpdateSentBitrate(now_ms);
333 return avg_sent_bit_rate_bps_;
334 }
335
UpdateWithEncodedData(const EncodedImage & encoded_image)336 int32_t MediaOptimization::UpdateWithEncodedData(
337 const EncodedImage& encoded_image) {
338 size_t encoded_length = encoded_image._length;
339 uint32_t timestamp = encoded_image._timeStamp;
340 CriticalSectionScoped lock(crit_sect_.get());
341 const int64_t now_ms = clock_->TimeInMilliseconds();
342 PurgeOldFrameSamples(now_ms);
343 if (encoded_frame_samples_.size() > 0 &&
344 encoded_frame_samples_.back().timestamp == timestamp) {
345 // Frames having the same timestamp are generated from the same input
346 // frame. We don't want to double count them, but only increment the
347 // size_bytes.
348 encoded_frame_samples_.back().size_bytes += encoded_length;
349 encoded_frame_samples_.back().time_complete_ms = now_ms;
350 } else {
351 encoded_frame_samples_.push_back(
352 EncodedFrameSample(encoded_length, timestamp, now_ms));
353 }
354 UpdateSentBitrate(now_ms);
355 UpdateSentFramerate();
356 if (encoded_length > 0) {
357 const bool delta_frame = encoded_image._frameType != kVideoFrameKey;
358
359 frame_dropper_->Fill(encoded_length, delta_frame);
360 if (max_payload_size_ > 0 && encoded_length > 0) {
361 const float min_packets_per_frame =
362 encoded_length / static_cast<float>(max_payload_size_);
363 if (delta_frame) {
364 loss_prot_logic_->UpdatePacketsPerFrame(min_packets_per_frame,
365 clock_->TimeInMilliseconds());
366 } else {
367 loss_prot_logic_->UpdatePacketsPerFrameKey(
368 min_packets_per_frame, clock_->TimeInMilliseconds());
369 }
370
371 if (enable_qm_) {
372 // Update quality select with encoded length.
373 qm_resolution_->UpdateEncodedSize(encoded_length);
374 }
375 }
376 if (!delta_frame && encoded_length > 0) {
377 loss_prot_logic_->UpdateKeyFrameSize(static_cast<float>(encoded_length));
378 }
379
380 // Updating counters.
381 if (delta_frame) {
382 delta_frame_cnt_++;
383 } else {
384 key_frame_cnt_++;
385 }
386 }
387
388 return VCM_OK;
389 }
390
EnableQM(bool enable)391 void MediaOptimization::EnableQM(bool enable) {
392 CriticalSectionScoped lock(crit_sect_.get());
393 enable_qm_ = enable;
394 }
395
EnableFrameDropper(bool enable)396 void MediaOptimization::EnableFrameDropper(bool enable) {
397 CriticalSectionScoped lock(crit_sect_.get());
398 frame_dropper_->Enable(enable);
399 }
400
SuspendBelowMinBitrate(int threshold_bps,int window_bps)401 void MediaOptimization::SuspendBelowMinBitrate(int threshold_bps,
402 int window_bps) {
403 CriticalSectionScoped lock(crit_sect_.get());
404 assert(threshold_bps > 0 && window_bps >= 0);
405 suspension_threshold_bps_ = threshold_bps;
406 suspension_window_bps_ = window_bps;
407 suspension_enabled_ = true;
408 video_suspended_ = false;
409 }
410
IsVideoSuspended() const411 bool MediaOptimization::IsVideoSuspended() const {
412 CriticalSectionScoped lock(crit_sect_.get());
413 return video_suspended_;
414 }
415
DropFrame()416 bool MediaOptimization::DropFrame() {
417 CriticalSectionScoped lock(crit_sect_.get());
418 UpdateIncomingFrameRate();
419 // Leak appropriate number of bytes.
420 frame_dropper_->Leak((uint32_t)(InputFrameRateInternal() + 0.5f));
421 if (video_suspended_) {
422 return true; // Drop all frames when muted.
423 }
424 return frame_dropper_->DropFrame();
425 }
426
UpdateContentData(const VideoContentMetrics * content_metrics)427 void MediaOptimization::UpdateContentData(
428 const VideoContentMetrics* content_metrics) {
429 CriticalSectionScoped lock(crit_sect_.get());
430 // Updating content metrics.
431 if (content_metrics == NULL) {
432 // Disable QM if metrics are NULL.
433 enable_qm_ = false;
434 qm_resolution_->Reset();
435 } else {
436 content_->UpdateContentData(content_metrics);
437 }
438 }
439
UpdateIncomingFrameRate()440 void MediaOptimization::UpdateIncomingFrameRate() {
441 int64_t now = clock_->TimeInMilliseconds();
442 if (incoming_frame_times_[0] == 0) {
443 // No shifting if this is the first time.
444 } else {
445 // Shift all times one step.
446 for (int32_t i = (kFrameCountHistorySize - 2); i >= 0; i--) {
447 incoming_frame_times_[i + 1] = incoming_frame_times_[i];
448 }
449 }
450 incoming_frame_times_[0] = now;
451 ProcessIncomingFrameRate(now);
452 }
453
SelectQuality(VCMQMSettingsCallback * video_qmsettings_callback)454 int32_t MediaOptimization::SelectQuality(
455 VCMQMSettingsCallback* video_qmsettings_callback) {
456 // Reset quantities for QM select.
457 qm_resolution_->ResetQM();
458
459 // Update QM will long-term averaged content metrics.
460 qm_resolution_->UpdateContent(content_->LongTermAvgData());
461
462 // Select quality mode.
463 VCMResolutionScale* qm = NULL;
464 int32_t ret = qm_resolution_->SelectResolution(&qm);
465 if (ret < 0) {
466 return ret;
467 }
468
469 // Check for updates to spatial/temporal modes.
470 QMUpdate(qm, video_qmsettings_callback);
471
472 // Reset all the rate and related frame counters quantities.
473 qm_resolution_->ResetRates();
474
475 // Reset counters.
476 last_qm_update_time_ = clock_->TimeInMilliseconds();
477
478 // Reset content metrics.
479 content_->Reset();
480
481 return VCM_OK;
482 }
483
PurgeOldFrameSamples(int64_t now_ms)484 void MediaOptimization::PurgeOldFrameSamples(int64_t now_ms) {
485 while (!encoded_frame_samples_.empty()) {
486 if (now_ms - encoded_frame_samples_.front().time_complete_ms >
487 kBitrateAverageWinMs) {
488 encoded_frame_samples_.pop_front();
489 } else {
490 break;
491 }
492 }
493 }
494
UpdateSentBitrate(int64_t now_ms)495 void MediaOptimization::UpdateSentBitrate(int64_t now_ms) {
496 if (encoded_frame_samples_.empty()) {
497 avg_sent_bit_rate_bps_ = 0;
498 return;
499 }
500 size_t framesize_sum = 0;
501 for (FrameSampleList::iterator it = encoded_frame_samples_.begin();
502 it != encoded_frame_samples_.end(); ++it) {
503 framesize_sum += it->size_bytes;
504 }
505 float denom = static_cast<float>(
506 now_ms - encoded_frame_samples_.front().time_complete_ms);
507 if (denom >= 1.0f) {
508 avg_sent_bit_rate_bps_ =
509 static_cast<uint32_t>(framesize_sum * 8.0f * 1000.0f / denom + 0.5f);
510 } else {
511 avg_sent_bit_rate_bps_ = framesize_sum * 8;
512 }
513 }
514
UpdateSentFramerate()515 void MediaOptimization::UpdateSentFramerate() {
516 if (encoded_frame_samples_.size() <= 1) {
517 avg_sent_framerate_ = encoded_frame_samples_.size();
518 return;
519 }
520 int denom = encoded_frame_samples_.back().timestamp -
521 encoded_frame_samples_.front().timestamp;
522 if (denom > 0) {
523 avg_sent_framerate_ =
524 (90000 * (encoded_frame_samples_.size() - 1) + denom / 2) / denom;
525 } else {
526 avg_sent_framerate_ = encoded_frame_samples_.size();
527 }
528 }
529
QMUpdate(VCMResolutionScale * qm,VCMQMSettingsCallback * video_qmsettings_callback)530 bool MediaOptimization::QMUpdate(
531 VCMResolutionScale* qm,
532 VCMQMSettingsCallback* video_qmsettings_callback) {
533 // Check for no change.
534 if (!qm->change_resolution_spatial && !qm->change_resolution_temporal) {
535 return false;
536 }
537
538 // Check for change in frame rate.
539 if (qm->change_resolution_temporal) {
540 incoming_frame_rate_ = qm->frame_rate;
541 // Reset frame rate estimate.
542 memset(incoming_frame_times_, -1, sizeof(incoming_frame_times_));
543 }
544
545 // Check for change in frame size.
546 if (qm->change_resolution_spatial) {
547 codec_width_ = qm->codec_width;
548 codec_height_ = qm->codec_height;
549 }
550
551 LOG(LS_INFO) << "Media optimizer requests the video resolution to be changed "
552 "to "
553 << qm->codec_width << "x" << qm->codec_height << "@"
554 << qm->frame_rate;
555
556 // Update VPM with new target frame rate and frame size.
557 // Note: use |qm->frame_rate| instead of |_incoming_frame_rate| for updating
558 // target frame rate in VPM frame dropper. The quantity |_incoming_frame_rate|
559 // will vary/fluctuate, and since we don't want to change the state of the
560 // VPM frame dropper, unless a temporal action was selected, we use the
561 // quantity |qm->frame_rate| for updating.
562 video_qmsettings_callback->SetVideoQMSettings(qm->frame_rate, codec_width_,
563 codec_height_);
564 content_->UpdateFrameRate(qm->frame_rate);
565 qm_resolution_->UpdateCodecParameters(qm->frame_rate, codec_width_,
566 codec_height_);
567 return true;
568 }
569
570 // Check timing constraints and look for significant change in:
571 // (1) scene content,
572 // (2) target bit rate.
CheckStatusForQMchange()573 bool MediaOptimization::CheckStatusForQMchange() {
574 bool status = true;
575
576 // Check that we do not call QMSelect too often, and that we waited some time
577 // (to sample the metrics) from the event last_change_time
578 // last_change_time is the time where user changed the size/rate/frame rate
579 // (via SetEncodingData).
580 int64_t now = clock_->TimeInMilliseconds();
581 if ((now - last_qm_update_time_) < kQmMinIntervalMs ||
582 (now - last_change_time_) < kQmMinIntervalMs) {
583 status = false;
584 }
585
586 return status;
587 }
588
589 // Allowing VCM to keep track of incoming frame rate.
ProcessIncomingFrameRate(int64_t now)590 void MediaOptimization::ProcessIncomingFrameRate(int64_t now) {
591 int32_t num = 0;
592 int32_t nr_of_frames = 0;
593 for (num = 1; num < (kFrameCountHistorySize - 1); ++num) {
594 if (incoming_frame_times_[num] <= 0 ||
595 // don't use data older than 2 s
596 now - incoming_frame_times_[num] > kFrameHistoryWinMs) {
597 break;
598 } else {
599 nr_of_frames++;
600 }
601 }
602 if (num > 1) {
603 const int64_t diff =
604 incoming_frame_times_[0] - incoming_frame_times_[num - 1];
605 incoming_frame_rate_ = 0.0; // No frame rate estimate available.
606 if (diff > 0) {
607 incoming_frame_rate_ = nr_of_frames * 1000.0f / static_cast<float>(diff);
608 }
609 }
610 }
611
CheckSuspendConditions()612 void MediaOptimization::CheckSuspendConditions() {
613 // Check conditions for SuspendBelowMinBitrate. |video_target_bitrate_| is in
614 // bps.
615 if (suspension_enabled_) {
616 if (!video_suspended_) {
617 // Check if we just went below the threshold.
618 if (video_target_bitrate_ < suspension_threshold_bps_) {
619 video_suspended_ = true;
620 }
621 } else {
622 // Video is already suspended. Check if we just went over the threshold
623 // with a margin.
624 if (video_target_bitrate_ >
625 suspension_threshold_bps_ + suspension_window_bps_) {
626 video_suspended_ = false;
627 }
628 }
629 }
630 }
631
632 } // namespace media_optimization
633 } // namespace webrtc
634