blob: 1e3989bfeb89bf87556db76bd6822bce6e605492 [file] [log] [blame]
Zijie Hee5f381f12017-10-25 20:28:451// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/codec/frame_processing_time_estimator.h"
6
Zijie He2a5ffb82017-11-10 20:51:087#include <algorithm>
8
Zijie Hee5f381f12017-10-25 20:28:459#include "base/logging.h"
10#include "remoting/base/constants.h"
11
12namespace remoting {
13
14namespace {
15
16// We tracks the frame information in last 6 seconds.
17static constexpr int kWindowSizeInSeconds = 6;
18
19// A key-frame is assumed to be generated roughly every 3 seconds, though the
20// accurate frequency is dependent on host/client software versions, the encoder
21// being used, and the quality of the network.
22static constexpr int kKeyFrameWindowSize = kWindowSizeInSeconds / 3;
23
24// The count of delta frames we are tracking.
25static constexpr int kDeltaFrameWindowSize =
26 kTargetFrameRate * kWindowSizeInSeconds - kKeyFrameWindowSize;
27
Zijie He2a5ffb82017-11-10 20:51:0828// The count of bandwidth estimates we are tracking.
29static constexpr int kBandwidthEstimateWindowSize =
30 kTargetFrameRate * kWindowSizeInSeconds;
31
32// The size of the circular_deque<TimeTicks> we are using to track the time
33// interval between frames.
34static constexpr int kFrameFinishTicksCount = kBandwidthEstimateWindowSize;
35
36base::TimeDelta CalculateEstimatedTransitTime(int size, int kbps) {
37 return base::TimeDelta::FromMicroseconds(size * 1000 * 8 / kbps);
38}
39
40// Uses the |time| to estimate the frame rate, and round the result in ceiling.
41// May return values over |kTargetFrameRate|.
42int CalculateEstimatedFrameRate(base::TimeDelta time) {
43 if (time.is_zero()) {
44 return kTargetFrameRate;
45 } else {
46 int64_t us = time.InMicroseconds();
47 return (base::Time::kMicrosecondsPerSecond + us - 1) / us;
48 }
49}
50
Zijie Hee5f381f12017-10-25 20:28:4551} // namespace
52
53FrameProcessingTimeEstimator::FrameProcessingTimeEstimator()
54 : delta_frame_processing_us_(kDeltaFrameWindowSize),
55 delta_frame_size_(kDeltaFrameWindowSize),
56 key_frame_processing_us_(kKeyFrameWindowSize),
57 key_frame_size_(kKeyFrameWindowSize),
Zijie He2a5ffb82017-11-10 20:51:0858 frame_finish_ticks_(),
59 bandwidth_kbps_(kBandwidthEstimateWindowSize) {
60 frame_finish_ticks_.reserve(kFrameFinishTicksCount);
61}
Zijie Hee5f381f12017-10-25 20:28:4562
63FrameProcessingTimeEstimator::~FrameProcessingTimeEstimator() = default;
64
65void FrameProcessingTimeEstimator::StartFrame() {
Zijie Hee5f381f12017-10-25 20:28:4566 start_time_ = Now();
67}
68
69void FrameProcessingTimeEstimator::FinishFrame(
70 const WebrtcVideoEncoder::EncodedFrame& frame) {
71 DCHECK(!start_time_.is_null());
72 base::TimeTicks now = Now();
Zijie He2a5ffb82017-11-10 20:51:0873 if (frame_finish_ticks_.size() == kFrameFinishTicksCount) {
74 frame_finish_ticks_.pop_front();
75 }
76 frame_finish_ticks_.push_back(now);
77 DCHECK(frame_finish_ticks_.size() <= kFrameFinishTicksCount);
Zijie Hee5f381f12017-10-25 20:28:4578 if (frame.key_frame) {
79 key_frame_processing_us_.Record((now - start_time_).InMicroseconds());
80 key_frame_size_.Record(frame.data.length());
Zijie He2a5ffb82017-11-10 20:51:0881 key_frame_count_++;
Zijie Hee5f381f12017-10-25 20:28:4582 } else {
83 delta_frame_processing_us_.Record((now - start_time_).InMicroseconds());
84 delta_frame_size_.Record(frame.data.length());
Zijie He2a5ffb82017-11-10 20:51:0885 delta_frame_count_++;
Zijie Hee5f381f12017-10-25 20:28:4586 }
87 start_time_ = base::TimeTicks();
88}
89
90void FrameProcessingTimeEstimator::SetBandwidthKbps(int bandwidth_kbps) {
91 if (bandwidth_kbps >= 0) {
92 bandwidth_kbps_.Record(bandwidth_kbps);
93 }
94}
95
96base::TimeDelta FrameProcessingTimeEstimator::EstimatedProcessingTime(
97 bool key_frame) const {
Zijie He4c6ff792017-10-27 01:26:5498 // Avoid returning 0 if there are no records for delta-frames.
99 if ((key_frame && !key_frame_processing_us_.IsEmpty()) ||
100 delta_frame_processing_us_.IsEmpty()) {
Zijie Hee5f381f12017-10-25 20:28:45101 return base::TimeDelta::FromMicroseconds(
102 key_frame_processing_us_.Average());
103 }
104 return base::TimeDelta::FromMicroseconds(
105 delta_frame_processing_us_.Average());
106}
107
108base::TimeDelta FrameProcessingTimeEstimator::EstimatedTransitTime(
109 bool key_frame) const {
Zijie He2a5ffb82017-11-10 20:51:08110 if (bandwidth_kbps_.IsEmpty()) {
Zijie Hee5f381f12017-10-25 20:28:45111 // To avoid unnecessary complexity in WebrtcFrameSchedulerSimple, we return
112 // a fairly large value (1 minute) here. So WebrtcFrameSchedulerSimple does
113 // not need to handle the overflow issue caused by returning
114 // TimeDelta::Max().
115 return base::TimeDelta::FromMinutes(1);
116 }
Zijie He4c6ff792017-10-27 01:26:54117 // Avoid returning 0 if there are no records for delta-frames.
118 if ((key_frame && !key_frame_size_.IsEmpty()) ||
119 delta_frame_size_.IsEmpty()) {
Zijie He2a5ffb82017-11-10 20:51:08120 return CalculateEstimatedTransitTime(
121 key_frame_size_.Average(), AverageBandwidthKbps());
Zijie Hee5f381f12017-10-25 20:28:45122 }
Zijie He2a5ffb82017-11-10 20:51:08123 return CalculateEstimatedTransitTime(
124 delta_frame_size_.Average(), AverageBandwidthKbps());
125}
126
127int FrameProcessingTimeEstimator::AverageBandwidthKbps() const {
128 return bandwidth_kbps_.Average();
129}
130
131int FrameProcessingTimeEstimator::EstimatedFrameSize() const {
132 if (delta_frame_count_ + key_frame_count_ == 0) {
133 return 0;
134 }
135 double key_frame_rate = key_frame_count_;
136 key_frame_rate /= (delta_frame_count_ + key_frame_count_);
137 return key_frame_rate * key_frame_size_.Average() +
138 (1 - key_frame_rate) * delta_frame_size_.Average();
139}
140
141base::TimeDelta FrameProcessingTimeEstimator::EstimatedProcessingTime() const {
142 if (delta_frame_count_ + key_frame_count_ == 0) {
143 return base::TimeDelta();
144 }
145 double key_frame_rate = key_frame_count_;
146 key_frame_rate /= (delta_frame_count_ + key_frame_count_);
147 return base::TimeDelta::FromMicroseconds(
148 key_frame_rate * key_frame_processing_us_.Average() +
149 (1 - key_frame_rate) * delta_frame_processing_us_.Average());
150}
151
152base::TimeDelta FrameProcessingTimeEstimator::EstimatedTransitTime() const {
153 if (bandwidth_kbps_.IsEmpty()) {
154 return base::TimeDelta::FromMinutes(1);
155 }
156 return CalculateEstimatedTransitTime(
157 EstimatedFrameSize(), AverageBandwidthKbps());
158}
159
160base::TimeDelta FrameProcessingTimeEstimator::
161RecentAverageFrameInterval() const {
162 if (frame_finish_ticks_.size() < 2) {
163 return base::TimeDelta();
164 }
165
166 return (frame_finish_ticks_.back() - frame_finish_ticks_.front()) /
167 (frame_finish_ticks_.size() - 1);
168}
169
170int FrameProcessingTimeEstimator::RecentFrameRate() const {
171 return std::min(kTargetFrameRate,
172 CalculateEstimatedFrameRate(RecentAverageFrameInterval()));
173}
174
175int FrameProcessingTimeEstimator::PredictedFrameRate() const {
176 return std::min({
177 kTargetFrameRate,
178 CalculateEstimatedFrameRate(EstimatedProcessingTime()),
179 CalculateEstimatedFrameRate(EstimatedTransitTime())
180 });
181}
182
183int FrameProcessingTimeEstimator::EstimatedFrameRate() const {
184 return std::min(RecentFrameRate(), PredictedFrameRate());
Zijie Hee5f381f12017-10-25 20:28:45185}
186
187base::TimeTicks FrameProcessingTimeEstimator::Now() const {
188 return base::TimeTicks::Now();
189}
190
191} // namespace remoting