1/*
2 *  Copyright (c) 2013 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/video/overuse_frame_detector.h"
12
13#include "testing/gmock/include/gmock/gmock.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16#include "webrtc/base/scoped_ptr.h"
17#include "webrtc/system_wrappers/include/clock.h"
18
19namespace webrtc {
20namespace {
21  const int kWidth = 640;
22  const int kHeight = 480;
23  const int kFrameInterval33ms = 33;
24  const int kProcessIntervalMs = 5000;
25  const int kProcessTime5ms = 5;
26}  // namespace
27
28class MockCpuOveruseObserver : public CpuOveruseObserver {
29 public:
30  MockCpuOveruseObserver() {}
31  virtual ~MockCpuOveruseObserver() {}
32
33  MOCK_METHOD0(OveruseDetected, void());
34  MOCK_METHOD0(NormalUsage, void());
35};
36
37class CpuOveruseObserverImpl : public CpuOveruseObserver {
38 public:
39  CpuOveruseObserverImpl() :
40    overuse_(0),
41    normaluse_(0) {}
42  virtual ~CpuOveruseObserverImpl() {}
43
44  void OveruseDetected() { ++overuse_; }
45  void NormalUsage() { ++normaluse_; }
46
47  int overuse_;
48  int normaluse_;
49};
50
51class OveruseFrameDetectorTest : public ::testing::Test,
52                                 public CpuOveruseMetricsObserver {
53 protected:
54  virtual void SetUp() {
55    clock_.reset(new SimulatedClock(1234));
56    observer_.reset(new MockCpuOveruseObserver());
57    options_.min_process_count = 0;
58    ReinitializeOveruseDetector();
59  }
60
61  void ReinitializeOveruseDetector() {
62    overuse_detector_.reset(new OveruseFrameDetector(clock_.get(), options_,
63                                                     observer_.get(), this));
64  }
65
66  void CpuOveruseMetricsUpdated(const CpuOveruseMetrics& metrics) override {
67    metrics_ = metrics;
68  }
69
70  int InitialUsage() {
71    return ((options_.low_encode_usage_threshold_percent +
72             options_.high_encode_usage_threshold_percent) / 2.0f) + 0.5;
73  }
74
75  void InsertAndSendFramesWithInterval(
76      int num_frames, int interval_ms, int width, int height, int delay_ms) {
77    while (num_frames-- > 0) {
78      int64_t capture_time_ms = clock_->TimeInMilliseconds();
79      overuse_detector_->FrameCaptured(width, height, capture_time_ms);
80      clock_->AdvanceTimeMilliseconds(delay_ms);
81      overuse_detector_->FrameSent(capture_time_ms);
82      clock_->AdvanceTimeMilliseconds(interval_ms - delay_ms);
83    }
84  }
85
86  void TriggerOveruse(int num_times) {
87    const int kDelayMs = 32;
88    for (int i = 0; i < num_times; ++i) {
89      InsertAndSendFramesWithInterval(
90          1000, kFrameInterval33ms, kWidth, kHeight, kDelayMs);
91      overuse_detector_->Process();
92    }
93  }
94
95  void TriggerUnderuse() {
96    const int kDelayMs1 = 5;
97    const int kDelayMs2 = 6;
98    InsertAndSendFramesWithInterval(
99        1300, kFrameInterval33ms, kWidth, kHeight, kDelayMs1);
100    InsertAndSendFramesWithInterval(
101        1, kFrameInterval33ms, kWidth, kHeight, kDelayMs2);
102    overuse_detector_->Process();
103  }
104
105  int UsagePercent() { return metrics_.encode_usage_percent; }
106
107  CpuOveruseOptions options_;
108  rtc::scoped_ptr<SimulatedClock> clock_;
109  rtc::scoped_ptr<MockCpuOveruseObserver> observer_;
110  rtc::scoped_ptr<OveruseFrameDetector> overuse_detector_;
111  CpuOveruseMetrics metrics_;
112};
113
114
115// UsagePercent() > high_encode_usage_threshold_percent => overuse.
116// UsagePercent() < low_encode_usage_threshold_percent => underuse.
117TEST_F(OveruseFrameDetectorTest, TriggerOveruse) {
118  // usage > high => overuse
119  EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
120  TriggerOveruse(options_.high_threshold_consecutive_count);
121}
122
123TEST_F(OveruseFrameDetectorTest, OveruseAndRecover) {
124  // usage > high => overuse
125  EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
126  TriggerOveruse(options_.high_threshold_consecutive_count);
127  // usage < low => underuse
128  EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1));
129  TriggerUnderuse();
130}
131
132TEST_F(OveruseFrameDetectorTest, OveruseAndRecoverWithNoObserver) {
133  overuse_detector_.reset(
134      new OveruseFrameDetector(clock_.get(), options_, nullptr, this));
135  EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0);
136  TriggerOveruse(options_.high_threshold_consecutive_count);
137  EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0);
138  TriggerUnderuse();
139}
140
141TEST_F(OveruseFrameDetectorTest, DoubleOveruseAndRecover) {
142  EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(2);
143  TriggerOveruse(options_.high_threshold_consecutive_count);
144  TriggerOveruse(options_.high_threshold_consecutive_count);
145  EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1));
146  TriggerUnderuse();
147}
148
149TEST_F(OveruseFrameDetectorTest, TriggerUnderuseWithMinProcessCount) {
150  options_.min_process_count = 1;
151  CpuOveruseObserverImpl overuse_observer;
152  overuse_detector_.reset(new OveruseFrameDetector(clock_.get(), options_,
153                                                   &overuse_observer, this));
154  InsertAndSendFramesWithInterval(
155      1200, kFrameInterval33ms, kWidth, kHeight, kProcessTime5ms);
156  overuse_detector_->Process();
157  EXPECT_EQ(0, overuse_observer.normaluse_);
158  clock_->AdvanceTimeMilliseconds(kProcessIntervalMs);
159  overuse_detector_->Process();
160  EXPECT_EQ(1, overuse_observer.normaluse_);
161}
162
163TEST_F(OveruseFrameDetectorTest, ConstantOveruseGivesNoNormalUsage) {
164  EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0);
165  EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(64);
166  for (size_t i = 0; i < 64; ++i) {
167    TriggerOveruse(options_.high_threshold_consecutive_count);
168  }
169}
170
171TEST_F(OveruseFrameDetectorTest, ConsecutiveCountTriggersOveruse) {
172  EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
173  options_.high_threshold_consecutive_count = 2;
174  ReinitializeOveruseDetector();
175  TriggerOveruse(2);
176}
177
178TEST_F(OveruseFrameDetectorTest, IncorrectConsecutiveCountTriggersNoOveruse) {
179  EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0);
180  options_.high_threshold_consecutive_count = 2;
181  ReinitializeOveruseDetector();
182  TriggerOveruse(1);
183}
184
185TEST_F(OveruseFrameDetectorTest, ProcessingUsage) {
186  InsertAndSendFramesWithInterval(
187      1000, kFrameInterval33ms, kWidth, kHeight, kProcessTime5ms);
188  EXPECT_EQ(kProcessTime5ms * 100 / kFrameInterval33ms, UsagePercent());
189}
190
191TEST_F(OveruseFrameDetectorTest, ResetAfterResolutionChange) {
192  EXPECT_EQ(InitialUsage(), UsagePercent());
193  InsertAndSendFramesWithInterval(
194      1000, kFrameInterval33ms, kWidth, kHeight, kProcessTime5ms);
195  EXPECT_NE(InitialUsage(), UsagePercent());
196  // Verify reset.
197  InsertAndSendFramesWithInterval(
198      1, kFrameInterval33ms, kWidth, kHeight + 1, kProcessTime5ms);
199  EXPECT_EQ(InitialUsage(), UsagePercent());
200}
201
202TEST_F(OveruseFrameDetectorTest, ResetAfterFrameTimeout) {
203  EXPECT_EQ(InitialUsage(), UsagePercent());
204  InsertAndSendFramesWithInterval(
205      1000, kFrameInterval33ms, kWidth, kHeight, kProcessTime5ms);
206  EXPECT_NE(InitialUsage(), UsagePercent());
207  InsertAndSendFramesWithInterval(
208      2, options_.frame_timeout_interval_ms, kWidth, kHeight, kProcessTime5ms);
209  EXPECT_NE(InitialUsage(), UsagePercent());
210  // Verify reset.
211  InsertAndSendFramesWithInterval(
212      2, options_.frame_timeout_interval_ms + 1, kWidth, kHeight,
213      kProcessTime5ms);
214  EXPECT_EQ(InitialUsage(), UsagePercent());
215}
216
217TEST_F(OveruseFrameDetectorTest, MinFrameSamplesBeforeUpdating) {
218  options_.min_frame_samples = 40;
219  ReinitializeOveruseDetector();
220  InsertAndSendFramesWithInterval(
221      40, kFrameInterval33ms, kWidth, kHeight, kProcessTime5ms);
222  EXPECT_EQ(InitialUsage(), UsagePercent());
223  InsertAndSendFramesWithInterval(
224      1, kFrameInterval33ms, kWidth, kHeight, kProcessTime5ms);
225  EXPECT_NE(InitialUsage(), UsagePercent());
226}
227
228TEST_F(OveruseFrameDetectorTest, InitialProcessingUsage) {
229  EXPECT_EQ(InitialUsage(), UsagePercent());
230}
231
232TEST_F(OveruseFrameDetectorTest, FrameDelay_OneFrame) {
233  const int kProcessingTimeMs = 100;
234  overuse_detector_->FrameCaptured(kWidth, kHeight, 33);
235  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs);
236  EXPECT_EQ(-1, overuse_detector_->LastProcessingTimeMs());
237  overuse_detector_->FrameSent(33);
238  EXPECT_EQ(kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs());
239  EXPECT_EQ(0, overuse_detector_->FramesInQueue());
240}
241
242TEST_F(OveruseFrameDetectorTest, FrameDelay_TwoFrames) {
243  const int kProcessingTimeMs1 = 100;
244  const int kProcessingTimeMs2 = 50;
245  const int kTimeBetweenFramesMs = 200;
246  overuse_detector_->FrameCaptured(kWidth, kHeight, 33);
247  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs1);
248  overuse_detector_->FrameSent(33);
249  EXPECT_EQ(kProcessingTimeMs1, overuse_detector_->LastProcessingTimeMs());
250  clock_->AdvanceTimeMilliseconds(kTimeBetweenFramesMs);
251  overuse_detector_->FrameCaptured(kWidth, kHeight, 66);
252  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs2);
253  overuse_detector_->FrameSent(66);
254  EXPECT_EQ(kProcessingTimeMs2, overuse_detector_->LastProcessingTimeMs());
255}
256
257TEST_F(OveruseFrameDetectorTest, FrameDelay_MaxQueueSize) {
258  const int kMaxQueueSize = 91;
259  for (int i = 0; i < kMaxQueueSize * 2; ++i) {
260    overuse_detector_->FrameCaptured(kWidth, kHeight, i);
261  }
262  EXPECT_EQ(kMaxQueueSize, overuse_detector_->FramesInQueue());
263}
264
265TEST_F(OveruseFrameDetectorTest, FrameDelay_NonProcessedFramesRemoved) {
266  const int kProcessingTimeMs = 100;
267  overuse_detector_->FrameCaptured(kWidth, kHeight, 33);
268  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs);
269  overuse_detector_->FrameCaptured(kWidth, kHeight, 35);
270  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs);
271  overuse_detector_->FrameCaptured(kWidth, kHeight, 66);
272  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs);
273  overuse_detector_->FrameCaptured(kWidth, kHeight, 99);
274  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs);
275  EXPECT_EQ(-1, overuse_detector_->LastProcessingTimeMs());
276  EXPECT_EQ(4, overuse_detector_->FramesInQueue());
277  overuse_detector_->FrameSent(66);
278  // Frame 33, 35 removed, 66 processed, 99 not processed.
279  EXPECT_EQ(2 * kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs());
280  EXPECT_EQ(1, overuse_detector_->FramesInQueue());
281  overuse_detector_->FrameSent(99);
282  EXPECT_EQ(kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs());
283  EXPECT_EQ(0, overuse_detector_->FramesInQueue());
284}
285
286TEST_F(OveruseFrameDetectorTest, FrameDelay_ResetClearsFrames) {
287  const int kProcessingTimeMs = 100;
288  overuse_detector_->FrameCaptured(kWidth, kHeight, 33);
289  EXPECT_EQ(1, overuse_detector_->FramesInQueue());
290  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs);
291  // Verify reset (resolution changed).
292  overuse_detector_->FrameCaptured(kWidth, kHeight + 1, 66);
293  EXPECT_EQ(1, overuse_detector_->FramesInQueue());
294  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs);
295  overuse_detector_->FrameSent(66);
296  EXPECT_EQ(kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs());
297  EXPECT_EQ(0, overuse_detector_->FramesInQueue());
298}
299
300TEST_F(OveruseFrameDetectorTest, FrameDelay_NonMatchingSendFrameIgnored) {
301  const int kProcessingTimeMs = 100;
302  overuse_detector_->FrameCaptured(kWidth, kHeight, 33);
303  clock_->AdvanceTimeMilliseconds(kProcessingTimeMs);
304  overuse_detector_->FrameSent(34);
305  EXPECT_EQ(-1, overuse_detector_->LastProcessingTimeMs());
306  overuse_detector_->FrameSent(33);
307  EXPECT_EQ(kProcessingTimeMs, overuse_detector_->LastProcessingTimeMs());
308}
309
310}  // namespace webrtc
311