summaryrefslogtreecommitdiff
path: root/chromium/media/capture/content/video_capture_oracle_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/capture/content/video_capture_oracle_unittest.cc')
-rw-r--r--chromium/media/capture/content/video_capture_oracle_unittest.cc619
1 files changed, 619 insertions, 0 deletions
diff --git a/chromium/media/capture/content/video_capture_oracle_unittest.cc b/chromium/media/capture/content/video_capture_oracle_unittest.cc
new file mode 100644
index 00000000000..64fff4fb4d0
--- /dev/null
+++ b/chromium/media/capture/content/video_capture_oracle_unittest.cc
@@ -0,0 +1,619 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/capture/content/video_capture_oracle.h"
+
+#include "base/strings/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+base::TimeTicks InitialTestTimeTicks() {
+ return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
+}
+
+base::TimeDelta Get30HzPeriod() {
+ return base::TimeDelta::FromSeconds(1) / 30;
+}
+
+gfx::Size Get1080pSize() {
+ return gfx::Size(1920, 1080);
+}
+
+gfx::Size Get720pSize() {
+ return gfx::Size(1280, 720);
+}
+
+gfx::Size Get360pSize() {
+ return gfx::Size(640, 360);
+}
+
+} // namespace
+
+// Tests that VideoCaptureOracle filters out events whose timestamps are
+// decreasing.
+TEST(VideoCaptureOracleTest, EnforcesEventTimeMonotonicity) {
+ const gfx::Rect damage_rect(Get720pSize());
+ const base::TimeDelta event_increment = Get30HzPeriod() * 2;
+
+ VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
+ media::RESOLUTION_POLICY_FIXED_RESOLUTION, false);
+
+ base::TimeTicks t = InitialTestTimeTicks();
+ for (int i = 0; i < 10; ++i) {
+ t += event_increment;
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
+ }
+
+ base::TimeTicks furthest_event_time = t;
+ for (int i = 0; i < 10; ++i) {
+ t -= event_increment;
+ ASSERT_FALSE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
+ }
+
+ t = furthest_event_time;
+ for (int i = 0; i < 10; ++i) {
+ t += event_increment;
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
+ }
+}
+
+// Tests that VideoCaptureOracle is enforcing the requirement that
+// successfully captured frames are delivered in order. Otherwise, downstream
+// consumers could be tripped-up by out-of-order frames or frame timestamps.
+TEST(VideoCaptureOracleTest, EnforcesFramesDeliveredInOrder) {
+ const gfx::Rect damage_rect(Get720pSize());
+ const base::TimeDelta event_increment = Get30HzPeriod() * 2;
+
+ VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
+ media::RESOLUTION_POLICY_FIXED_RESOLUTION, false);
+
+ // Most basic scenario: Frames delivered one at a time, with no additional
+ // captures in-between deliveries.
+ base::TimeTicks t = InitialTestTimeTicks();
+ int last_frame_number;
+ base::TimeTicks ignored;
+ for (int i = 0; i < 10; ++i) {
+ t += event_increment;
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
+ last_frame_number = oracle.RecordCapture(0.0);
+ ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, true, &ignored));
+ }
+
+ // Basic pipelined scenario: More than one frame in-flight at delivery points.
+ for (int i = 0; i < 50; ++i) {
+ const int num_in_flight = 1 + i % 3;
+ for (int j = 0; j < num_in_flight; ++j) {
+ t += event_increment;
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
+ last_frame_number = oracle.RecordCapture(0.0);
+ }
+ for (int j = num_in_flight - 1; j >= 0; --j) {
+ ASSERT_TRUE(
+ oracle.CompleteCapture(last_frame_number - j, true, &ignored));
+ }
+ }
+
+ // Pipelined scenario with successful out-of-order delivery attempts
+ // rejected.
+ for (int i = 0; i < 50; ++i) {
+ const int num_in_flight = 1 + i % 3;
+ for (int j = 0; j < num_in_flight; ++j) {
+ t += event_increment;
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
+ last_frame_number = oracle.RecordCapture(0.0);
+ }
+ ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, true, &ignored));
+ for (int j = 1; j < num_in_flight; ++j) {
+ ASSERT_FALSE(
+ oracle.CompleteCapture(last_frame_number - j, true, &ignored));
+ }
+ }
+
+ // Pipelined scenario with successful delivery attempts accepted after an
+ // unsuccessful out of order delivery attempt.
+ for (int i = 0; i < 50; ++i) {
+ const int num_in_flight = 1 + i % 3;
+ for (int j = 0; j < num_in_flight; ++j) {
+ t += event_increment;
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
+ last_frame_number = oracle.RecordCapture(0.0);
+ }
+ // Report the last frame as an out of order failure.
+ ASSERT_FALSE(oracle.CompleteCapture(last_frame_number, false, &ignored));
+ for (int j = 1; j < num_in_flight - 1; ++j) {
+ ASSERT_TRUE(
+ oracle.CompleteCapture(last_frame_number - j, true, &ignored));
+ }
+ }
+}
+
+// Tests that VideoCaptureOracle transitions between using its two samplers in a
+// way that does not introduce severe jank, pauses, etc.
+TEST(VideoCaptureOracleTest, TransitionsSmoothlyBetweenSamplers) {
+ const gfx::Rect animation_damage_rect(Get720pSize());
+ const base::TimeDelta event_increment = Get30HzPeriod() * 2;
+
+ VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
+ media::RESOLUTION_POLICY_FIXED_RESOLUTION, false);
+
+ // Run sequences of animation events and non-animation events through the
+ // oracle. As the oracle transitions between each sampler, make sure the
+ // frame timestamps won't trip-up downstream consumers.
+ base::TimeTicks t = InitialTestTimeTicks();
+ base::TimeTicks last_frame_timestamp;
+ for (int i = 0; i < 1000; ++i) {
+ t += event_increment;
+
+ // For every 100 events, provide 50 that will cause the
+ // AnimatedContentSampler to lock-in, followed by 50 that will cause it to
+ // lock-out (i.e., the oracle will use the SmoothEventSampler instead).
+ const bool provide_animated_content_event =
+ (i % 100) >= 25 && (i % 100) < 75;
+
+ // Only the few events that trigger the lock-out transition should be
+ // dropped, because the AnimatedContentSampler doesn't yet realize the
+ // animation ended. Otherwise, the oracle should always decide to sample
+ // because one of its samplers says to.
+ const bool require_oracle_says_sample = (i % 100) < 75 || (i % 100) >= 78;
+ const bool oracle_says_sample = oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate,
+ provide_animated_content_event ? animation_damage_rect : gfx::Rect(),
+ t);
+ if (require_oracle_says_sample)
+ ASSERT_TRUE(oracle_says_sample);
+ if (!oracle_says_sample) {
+ ASSERT_EQ(base::TimeDelta(), oracle.estimated_frame_duration());
+ continue;
+ }
+ ASSERT_LT(base::TimeDelta(), oracle.estimated_frame_duration());
+
+ const int frame_number = oracle.RecordCapture(0.0);
+
+ base::TimeTicks frame_timestamp;
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &frame_timestamp));
+ ASSERT_FALSE(frame_timestamp.is_null());
+ if (!last_frame_timestamp.is_null()) {
+ const base::TimeDelta delta = frame_timestamp - last_frame_timestamp;
+ EXPECT_LE(event_increment.InMicroseconds(), delta.InMicroseconds());
+ // Right after the AnimatedContentSampler lock-out transition, there were
+ // a few frames dropped, so allow a gap in the timestamps. Otherwise, the
+ // delta between frame timestamps should never be more than 2X the
+ // |event_increment|.
+ const base::TimeDelta max_acceptable_delta =
+ (i % 100) == 78 ? event_increment * 5 : event_increment * 2;
+ EXPECT_GE(max_acceptable_delta.InMicroseconds(), delta.InMicroseconds());
+ }
+ last_frame_timestamp = frame_timestamp;
+ }
+}
+
+// Tests that VideoCaptureOracle prevents timer polling from initiating
+// simultaneous captures.
+TEST(VideoCaptureOracleTest, SamplesOnlyOneOverdueFrameAtATime) {
+ const base::TimeDelta vsync_interval = base::TimeDelta::FromSeconds(1) / 60;
+ const base::TimeDelta timer_interval = base::TimeDelta::FromMilliseconds(
+ VideoCaptureOracle::kMinTimerPollPeriodMillis);
+
+ VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
+ media::RESOLUTION_POLICY_FIXED_RESOLUTION, false);
+
+ // Have the oracle observe some compositor events. Simulate that each capture
+ // completes successfully.
+ base::TimeTicks t = InitialTestTimeTicks();
+ base::TimeTicks ignored;
+ bool did_complete_a_capture = false;
+ for (int i = 0; i < 10; ++i) {
+ t += vsync_interval;
+ if (oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t)) {
+ ASSERT_TRUE(
+ oracle.CompleteCapture(oracle.RecordCapture(0.0), true, &ignored));
+ did_complete_a_capture = true;
+ }
+ }
+ ASSERT_TRUE(did_complete_a_capture);
+
+ // Start one more compositor-based capture, but do not notify of completion
+ // yet.
+ for (int i = 0; i <= 10; ++i) {
+ ASSERT_GT(10, i) << "BUG: Seems like it'll never happen!";
+ t += vsync_interval;
+ if (oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t)) {
+ break;
+ }
+ }
+ int frame_number = oracle.RecordCapture(0.0);
+
+ // Stop providing the compositor events and start providing timer polling
+ // events. No overdue samplings should be recommended because of the
+ // not-yet-complete compositor-based capture.
+ for (int i = 0; i < 10; ++i) {
+ t += timer_interval;
+ ASSERT_FALSE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kTimerPoll, gfx::Rect(), t));
+ }
+
+ // Now, complete the oustanding compositor-based capture and continue
+ // providing timer polling events. The oracle should start recommending
+ // sampling again.
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
+ did_complete_a_capture = false;
+ for (int i = 0; i < 10; ++i) {
+ t += timer_interval;
+ if (oracle.ObserveEventAndDecideCapture(VideoCaptureOracle::kTimerPoll,
+ gfx::Rect(), t)) {
+ ASSERT_TRUE(
+ oracle.CompleteCapture(oracle.RecordCapture(0.0), true, &ignored));
+ did_complete_a_capture = true;
+ }
+ }
+ ASSERT_TRUE(did_complete_a_capture);
+
+ // Start one more timer-based capture, but do not notify of completion yet.
+ for (int i = 0; i <= 10; ++i) {
+ ASSERT_GT(10, i) << "BUG: Seems like it'll never happen!";
+ t += timer_interval;
+ if (oracle.ObserveEventAndDecideCapture(VideoCaptureOracle::kTimerPoll,
+ gfx::Rect(), t)) {
+ break;
+ }
+ }
+ frame_number = oracle.RecordCapture(0.0);
+
+ // Confirm that the oracle does not recommend sampling until the outstanding
+ // timer-based capture completes.
+ for (int i = 0; i < 10; ++i) {
+ t += timer_interval;
+ ASSERT_FALSE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kTimerPoll, gfx::Rect(), t));
+ }
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
+ for (int i = 0; i <= 10; ++i) {
+ ASSERT_GT(10, i) << "BUG: Seems like it'll never happen!";
+ t += timer_interval;
+ if (oracle.ObserveEventAndDecideCapture(VideoCaptureOracle::kTimerPoll,
+ gfx::Rect(), t)) {
+ break;
+ }
+ }
+}
+
+// Tests that VideoCaptureOracle does not rapidly change proposed capture sizes,
+// to allow both the source content and the rest of the end-to-end system to
+// stabilize.
+TEST(VideoCaptureOracleTest, DoesNotRapidlyChangeCaptureSize) {
+ VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
+ media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT, true);
+
+ // Run 30 seconds of frame captures without any source size changes.
+ base::TimeTicks t = InitialTestTimeTicks();
+ const base::TimeDelta event_increment = Get30HzPeriod() * 2;
+ base::TimeTicks end_t = t + base::TimeDelta::FromSeconds(30);
+ for (; t < end_t; t += event_increment) {
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
+ ASSERT_EQ(Get720pSize(), oracle.capture_size());
+ base::TimeTicks ignored;
+ ASSERT_TRUE(
+ oracle.CompleteCapture(oracle.RecordCapture(0.0), true, &ignored));
+ }
+
+ // Now run 30 seconds of frame captures with lots of random source size
+ // changes. Check that there was no more than one size change per second.
+ gfx::Size source_size = oracle.capture_size();
+ base::TimeTicks time_of_last_size_change = InitialTestTimeTicks();
+ gfx::Size last_capture_size = oracle.capture_size();
+ end_t = t + base::TimeDelta::FromSeconds(30);
+ for (; t < end_t; t += event_increment) {
+ // Change the source size every frame to a random non-empty size.
+ const gfx::Size last_source_size = source_size;
+ source_size.SetSize(((last_source_size.width() * 11 + 12345) % 1280) + 1,
+ ((last_source_size.height() * 11 + 12345) % 720) + 1);
+ ASSERT_NE(last_source_size, source_size);
+ oracle.SetSourceSize(source_size);
+
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
+
+ if (oracle.capture_size() != last_capture_size) {
+ ASSERT_GE(t - time_of_last_size_change, base::TimeDelta::FromSeconds(1));
+ time_of_last_size_change = t;
+ last_capture_size = oracle.capture_size();
+ }
+
+ base::TimeTicks ignored;
+ ASSERT_TRUE(
+ oracle.CompleteCapture(oracle.RecordCapture(0.0), true, &ignored));
+ }
+}
+
+namespace {
+
+// Tests that VideoCaptureOracle can auto-throttle by stepping the capture size
+// up or down. When |is_content_animating| is false, there is more
+// aggressiveness expected in the timing of stepping upwards. If
+// |with_consumer_feedback| is false, only buffer pool utilization varies and no
+// consumer feedback is provided. If |with_consumer_feedback| is true, the
+// buffer pool utilization is held constant at 25%, and the consumer utilization
+// feedback varies.
+void RunAutoThrottleTest(bool is_content_animating,
+ bool with_consumer_feedback) {
+ SCOPED_TRACE(::testing::Message()
+ << "RunAutoThrottleTest("
+ << "(is_content_animating=" << is_content_animating
+ << ", with_consumer_feedback=" << with_consumer_feedback << ")");
+
+ VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
+ media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT, true);
+
+ // Run 10 seconds of frame captures with 90% utilization expect no capture
+ // size changes.
+ base::TimeTicks t = InitialTestTimeTicks();
+ base::TimeTicks time_of_last_size_change = t;
+ const base::TimeDelta event_increment = Get30HzPeriod() * 2;
+ base::TimeTicks end_t = t + base::TimeDelta::FromSeconds(10);
+ for (; t < end_t; t += event_increment) {
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate,
+ is_content_animating ? gfx::Rect(Get720pSize()) : gfx::Rect(), t));
+ ASSERT_EQ(Get720pSize(), oracle.capture_size());
+ const double utilization = 0.9;
+ const int frame_number =
+ oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
+ base::TimeTicks ignored;
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
+ if (with_consumer_feedback)
+ oracle.RecordConsumerFeedback(frame_number, utilization);
+ }
+
+ // Cause two downward steppings in resolution. First, indicate overload
+ // until the resolution steps down. Then, indicate a 90% utilization and
+ // expect the resolution to remain constant. Repeat.
+ for (int i = 0; i < 2; ++i) {
+ const gfx::Size starting_size = oracle.capture_size();
+ SCOPED_TRACE(::testing::Message() << "Stepping down from "
+ << starting_size.ToString()
+ << ", i=" << i);
+
+ gfx::Size stepped_down_size;
+ end_t = t + base::TimeDelta::FromSeconds(10);
+ for (; t < end_t; t += event_increment) {
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate,
+ is_content_animating ? gfx::Rect(Get720pSize()) : gfx::Rect(), t));
+
+ if (stepped_down_size.IsEmpty()) {
+ if (oracle.capture_size() != starting_size) {
+ time_of_last_size_change = t;
+ stepped_down_size = oracle.capture_size();
+ ASSERT_GT(starting_size.width(), stepped_down_size.width());
+ ASSERT_GT(starting_size.height(), stepped_down_size.height());
+ }
+ } else {
+ ASSERT_EQ(stepped_down_size, oracle.capture_size());
+ }
+
+ const double utilization = stepped_down_size.IsEmpty() ? 1.5 : 0.9;
+ const int frame_number =
+ oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
+ base::TimeTicks ignored;
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
+ if (with_consumer_feedback)
+ oracle.RecordConsumerFeedback(frame_number, utilization);
+ }
+ }
+
+ // Now, cause two upward steppings in resolution. First, indicate
+ // under-utilization until the resolution steps up. Then, indicate a 90%
+ // utilization and expect the resolution to remain constant. Repeat.
+ for (int i = 0; i < 2; ++i) {
+ const gfx::Size starting_size = oracle.capture_size();
+ SCOPED_TRACE(::testing::Message() << "Stepping up from "
+ << starting_size.ToString()
+ << ", i=" << i);
+
+ gfx::Size stepped_up_size;
+ end_t = t + base::TimeDelta::FromSeconds(is_content_animating ? 90 : 10);
+ for (; t < end_t; t += event_increment) {
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate,
+ is_content_animating ? gfx::Rect(Get720pSize()) : gfx::Rect(), t));
+
+ if (stepped_up_size.IsEmpty()) {
+ if (oracle.capture_size() != starting_size) {
+ // When content is animating, a much longer amount of time must pass
+ // before the capture size will step up.
+ ASSERT_LT(base::TimeDelta::FromSeconds(is_content_animating ? 15 : 1),
+ t - time_of_last_size_change);
+ time_of_last_size_change = t;
+ stepped_up_size = oracle.capture_size();
+ ASSERT_LT(starting_size.width(), stepped_up_size.width());
+ ASSERT_LT(starting_size.height(), stepped_up_size.height());
+ }
+ } else {
+ ASSERT_EQ(stepped_up_size, oracle.capture_size());
+ }
+
+ const double utilization = stepped_up_size.IsEmpty() ? 0.0 : 0.9;
+ const int frame_number =
+ oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
+ base::TimeTicks ignored;
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
+ if (with_consumer_feedback)
+ oracle.RecordConsumerFeedback(frame_number, utilization);
+ }
+ }
+}
+
+} // namespace
+
+// Tests that VideoCaptureOracle can auto-throttle by stepping the capture size
+// up or down, using utilization feedback signals from either the buffer pool or
+// the consumer, and with slightly different behavior depending on whether
+// content is animating.
+TEST(VideoCaptureOracleTest, AutoThrottlesBasedOnUtilizationFeedback) {
+ RunAutoThrottleTest(false, false);
+ RunAutoThrottleTest(false, true);
+ RunAutoThrottleTest(true, false);
+ RunAutoThrottleTest(true, true);
+}
+
+// Tests that, while content is animating, VideoCaptureOracle can make frequent
+// capture size increases only just after the source size has changed.
+// Otherwise, capture size increases should only be made cautiously, after a
+// long "proving period of under-utilization" has elapsed.
+TEST(VideoCaptureOracleTest, IncreasesFrequentlyOnlyAfterSourceSizeChange) {
+ VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
+ media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT, true);
+
+ // Start out the source size at 360p, so there is room to grow to the 720p
+ // maximum.
+ oracle.SetSourceSize(Get360pSize());
+
+ // Run 10 seconds of frame captures with under-utilization to represent a
+ // machine that can do more, but won't because the source size is small.
+ base::TimeTicks t = InitialTestTimeTicks();
+ const base::TimeDelta event_increment = Get30HzPeriod() * 2;
+ base::TimeTicks end_t = t + base::TimeDelta::FromSeconds(10);
+ for (; t < end_t; t += event_increment) {
+ if (!oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(Get360pSize()),
+ t)) {
+ continue;
+ }
+ ASSERT_EQ(Get360pSize(), oracle.capture_size());
+ const int frame_number = oracle.RecordCapture(0.25);
+ base::TimeTicks ignored;
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
+ }
+
+ // Now, set the source size to 720p, continuing to report under-utilization,
+ // and expect the capture size increases to reach a full 720p within 15
+ // seconds.
+ oracle.SetSourceSize(Get720pSize());
+ gfx::Size last_capture_size = oracle.capture_size();
+ end_t = t + base::TimeDelta::FromSeconds(15);
+ for (; t < end_t; t += event_increment) {
+ if (!oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(Get720pSize()),
+ t)) {
+ continue;
+ }
+ ASSERT_LE(last_capture_size.width(), oracle.capture_size().width());
+ ASSERT_LE(last_capture_size.height(), oracle.capture_size().height());
+ last_capture_size = oracle.capture_size();
+ const int frame_number = oracle.RecordCapture(0.25);
+ base::TimeTicks ignored;
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
+ }
+ ASSERT_EQ(Get720pSize(), oracle.capture_size());
+
+ // Now, change the source size again, but report over-utilization so the
+ // capture size will decrease. Once it decreases one step, report 90%
+ // utilization to achieve a steady-state.
+ oracle.SetSourceSize(Get1080pSize());
+ gfx::Size stepped_down_size;
+ end_t = t + base::TimeDelta::FromSeconds(10);
+ for (; t < end_t; t += event_increment) {
+ if (!oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(Get1080pSize()),
+ t)) {
+ continue;
+ }
+
+ if (stepped_down_size.IsEmpty()) {
+ if (oracle.capture_size() != Get720pSize()) {
+ stepped_down_size = oracle.capture_size();
+ ASSERT_GT(Get720pSize().width(), stepped_down_size.width());
+ ASSERT_GT(Get720pSize().height(), stepped_down_size.height());
+ }
+ } else {
+ ASSERT_EQ(stepped_down_size, oracle.capture_size());
+ }
+
+ const double utilization = stepped_down_size.IsEmpty() ? 1.5 : 0.9;
+ const int frame_number = oracle.RecordCapture(utilization);
+ base::TimeTicks ignored;
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
+ }
+ ASSERT_FALSE(stepped_down_size.IsEmpty());
+
+ // Now, if we report under-utilization again (without any source size change),
+ // there should be a long "proving period" before there is any increase in
+ // capture size made by the oracle.
+ const base::TimeTicks proving_period_end_time =
+ t + base::TimeDelta::FromSeconds(15);
+ gfx::Size stepped_up_size;
+ end_t = t + base::TimeDelta::FromSeconds(60);
+ for (; t < end_t; t += event_increment) {
+ if (!oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(Get1080pSize()),
+ t)) {
+ continue;
+ }
+
+ if (stepped_up_size.IsEmpty()) {
+ if (oracle.capture_size() != stepped_down_size) {
+ ASSERT_LT(proving_period_end_time, t);
+ stepped_up_size = oracle.capture_size();
+ ASSERT_LT(stepped_down_size.width(), stepped_up_size.width());
+ ASSERT_LT(stepped_down_size.height(), stepped_up_size.height());
+ }
+ } else {
+ ASSERT_EQ(stepped_up_size, oracle.capture_size());
+ }
+
+ const double utilization = stepped_up_size.IsEmpty() ? 0.25 : 0.9;
+ const int frame_number = oracle.RecordCapture(utilization);
+ base::TimeTicks ignored;
+ ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
+ }
+ ASSERT_FALSE(stepped_up_size.IsEmpty());
+}
+
+// Tests that VideoCaptureOracle does not change the capture size if
+// auto-throttling is enabled when using a fixed resolution policy.
+TEST(VideoCaptureOracleTest, DoesNotAutoThrottleWhenResolutionIsFixed) {
+ VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
+ media::RESOLUTION_POLICY_FIXED_RESOLUTION, true);
+
+ // Run 10 seconds of frame captures with 90% utilization expect no capture
+ // size changes.
+ base::TimeTicks t = InitialTestTimeTicks();
+ const base::TimeDelta event_increment = Get30HzPeriod() * 2;
+ base::TimeTicks end_t = t + base::TimeDelta::FromSeconds(10);
+ for (; t < end_t; t += event_increment) {
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
+ ASSERT_EQ(Get720pSize(), oracle.capture_size());
+ base::TimeTicks ignored;
+ ASSERT_TRUE(
+ oracle.CompleteCapture(oracle.RecordCapture(0.9), true, &ignored));
+ }
+
+ // Now run 10 seconds with overload indicated. Still, expect no capture size
+ // changes.
+ end_t = t + base::TimeDelta::FromSeconds(10);
+ for (; t < end_t; t += event_increment) {
+ ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
+ VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
+ ASSERT_EQ(Get720pSize(), oracle.capture_size());
+ base::TimeTicks ignored;
+ ASSERT_TRUE(
+ oracle.CompleteCapture(oracle.RecordCapture(2.0), true, &ignored));
+ }
+}
+
+} // namespace media