xref: /aosp_15_r20/external/webrtc/video/frame_cadence_adapter_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "video/frame_cadence_adapter.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <utility>
14*d9f75844SAndroid Build Coastguard Worker #include <vector>
15*d9f75844SAndroid Build Coastguard Worker 
16*d9f75844SAndroid Build Coastguard Worker #include "absl/functional/any_invocable.h"
17*d9f75844SAndroid Build Coastguard Worker #include "api/task_queue/default_task_queue_factory.h"
18*d9f75844SAndroid Build Coastguard Worker #include "api/task_queue/task_queue_base.h"
19*d9f75844SAndroid Build Coastguard Worker #include "api/task_queue/task_queue_factory.h"
20*d9f75844SAndroid Build Coastguard Worker #include "api/units/time_delta.h"
21*d9f75844SAndroid Build Coastguard Worker #include "api/units/timestamp.h"
22*d9f75844SAndroid Build Coastguard Worker #include "api/video/nv12_buffer.h"
23*d9f75844SAndroid Build Coastguard Worker #include "api/video/video_frame.h"
24*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/event.h"
25*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/rate_statistics.h"
26*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/time_utils.h"
27*d9f75844SAndroid Build Coastguard Worker #include "system_wrappers/include/metrics.h"
28*d9f75844SAndroid Build Coastguard Worker #include "system_wrappers/include/ntp_time.h"
29*d9f75844SAndroid Build Coastguard Worker #include "system_wrappers/include/sleep.h"
30*d9f75844SAndroid Build Coastguard Worker #include "test/gmock.h"
31*d9f75844SAndroid Build Coastguard Worker #include "test/gtest.h"
32*d9f75844SAndroid Build Coastguard Worker #include "test/scoped_key_value_config.h"
33*d9f75844SAndroid Build Coastguard Worker #include "test/time_controller/simulated_time_controller.h"
34*d9f75844SAndroid Build Coastguard Worker 
35*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
36*d9f75844SAndroid Build Coastguard Worker namespace {
37*d9f75844SAndroid Build Coastguard Worker 
38*d9f75844SAndroid Build Coastguard Worker using ::testing::_;
39*d9f75844SAndroid Build Coastguard Worker using ::testing::ElementsAre;
40*d9f75844SAndroid Build Coastguard Worker using ::testing::Invoke;
41*d9f75844SAndroid Build Coastguard Worker using ::testing::Mock;
42*d9f75844SAndroid Build Coastguard Worker using ::testing::Pair;
43*d9f75844SAndroid Build Coastguard Worker using ::testing::Values;
44*d9f75844SAndroid Build Coastguard Worker 
CreateFrame()45*d9f75844SAndroid Build Coastguard Worker VideoFrame CreateFrame() {
46*d9f75844SAndroid Build Coastguard Worker   return VideoFrame::Builder()
47*d9f75844SAndroid Build Coastguard Worker       .set_video_frame_buffer(
48*d9f75844SAndroid Build Coastguard Worker           rtc::make_ref_counted<NV12Buffer>(/*width=*/16, /*height=*/16))
49*d9f75844SAndroid Build Coastguard Worker       .build();
50*d9f75844SAndroid Build Coastguard Worker }
51*d9f75844SAndroid Build Coastguard Worker 
CreateFrameWithTimestamps(GlobalSimulatedTimeController * time_controller)52*d9f75844SAndroid Build Coastguard Worker VideoFrame CreateFrameWithTimestamps(
53*d9f75844SAndroid Build Coastguard Worker     GlobalSimulatedTimeController* time_controller) {
54*d9f75844SAndroid Build Coastguard Worker   return VideoFrame::Builder()
55*d9f75844SAndroid Build Coastguard Worker       .set_video_frame_buffer(
56*d9f75844SAndroid Build Coastguard Worker           rtc::make_ref_counted<NV12Buffer>(/*width=*/16, /*height=*/16))
57*d9f75844SAndroid Build Coastguard Worker       .set_ntp_time_ms(time_controller->GetClock()->CurrentNtpInMilliseconds())
58*d9f75844SAndroid Build Coastguard Worker       .set_timestamp_us(time_controller->GetClock()->CurrentTime().us())
59*d9f75844SAndroid Build Coastguard Worker       .build();
60*d9f75844SAndroid Build Coastguard Worker }
61*d9f75844SAndroid Build Coastguard Worker 
CreateAdapter(const FieldTrialsView & field_trials,Clock * clock)62*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<FrameCadenceAdapterInterface> CreateAdapter(
63*d9f75844SAndroid Build Coastguard Worker     const FieldTrialsView& field_trials,
64*d9f75844SAndroid Build Coastguard Worker     Clock* clock) {
65*d9f75844SAndroid Build Coastguard Worker   return FrameCadenceAdapterInterface::Create(clock, TaskQueueBase::Current(),
66*d9f75844SAndroid Build Coastguard Worker                                               field_trials);
67*d9f75844SAndroid Build Coastguard Worker }
68*d9f75844SAndroid Build Coastguard Worker 
69*d9f75844SAndroid Build Coastguard Worker class MockCallback : public FrameCadenceAdapterInterface::Callback {
70*d9f75844SAndroid Build Coastguard Worker  public:
71*d9f75844SAndroid Build Coastguard Worker   MOCK_METHOD(void, OnFrame, (Timestamp, int, const VideoFrame&), (override));
72*d9f75844SAndroid Build Coastguard Worker   MOCK_METHOD(void, OnDiscardedFrame, (), (override));
73*d9f75844SAndroid Build Coastguard Worker   MOCK_METHOD(void, RequestRefreshFrame, (), (override));
74*d9f75844SAndroid Build Coastguard Worker };
75*d9f75844SAndroid Build Coastguard Worker 
76*d9f75844SAndroid Build Coastguard Worker class ZeroHertzFieldTrialDisabler : public test::ScopedKeyValueConfig {
77*d9f75844SAndroid Build Coastguard Worker  public:
ZeroHertzFieldTrialDisabler()78*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialDisabler()
79*d9f75844SAndroid Build Coastguard Worker       : test::ScopedKeyValueConfig("WebRTC-ZeroHertzScreenshare/Disabled/") {}
80*d9f75844SAndroid Build Coastguard Worker };
81*d9f75844SAndroid Build Coastguard Worker 
82*d9f75844SAndroid Build Coastguard Worker class ZeroHertzFieldTrialEnabler : public test::ScopedKeyValueConfig {
83*d9f75844SAndroid Build Coastguard Worker  public:
ZeroHertzFieldTrialEnabler()84*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler()
85*d9f75844SAndroid Build Coastguard Worker       : test::ScopedKeyValueConfig("WebRTC-ZeroHertzScreenshare/Enabled/") {}
86*d9f75844SAndroid Build Coastguard Worker };
87*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,ForwardsFramesOnConstructionAndUnderDisabledFieldTrial)88*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest,
89*d9f75844SAndroid Build Coastguard Worker      ForwardsFramesOnConstructionAndUnderDisabledFieldTrial) {
90*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Millis(1));
91*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialDisabler disabled_field_trials;
92*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
93*d9f75844SAndroid Build Coastguard Worker   for (int i = 0; i != 2; i++) {
94*d9f75844SAndroid Build Coastguard Worker     MockCallback callback;
95*d9f75844SAndroid Build Coastguard Worker     auto adapter =
96*d9f75844SAndroid Build Coastguard Worker         CreateAdapter(i == 0 ? disabled_field_trials : no_field_trials,
97*d9f75844SAndroid Build Coastguard Worker                       time_controller.GetClock());
98*d9f75844SAndroid Build Coastguard Worker     adapter->Initialize(&callback);
99*d9f75844SAndroid Build Coastguard Worker     VideoFrame frame = CreateFrame();
100*d9f75844SAndroid Build Coastguard Worker     EXPECT_CALL(callback, OnFrame).Times(1);
101*d9f75844SAndroid Build Coastguard Worker     adapter->OnFrame(frame);
102*d9f75844SAndroid Build Coastguard Worker     time_controller.AdvanceTime(TimeDelta::Zero());
103*d9f75844SAndroid Build Coastguard Worker     Mock::VerifyAndClearExpectations(&callback);
104*d9f75844SAndroid Build Coastguard Worker     EXPECT_CALL(callback, OnDiscardedFrame).Times(1);
105*d9f75844SAndroid Build Coastguard Worker     adapter->OnDiscardedFrame();
106*d9f75844SAndroid Build Coastguard Worker     Mock::VerifyAndClearExpectations(&callback);
107*d9f75844SAndroid Build Coastguard Worker   }
108*d9f75844SAndroid Build Coastguard Worker }
109*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,CountsOutstandingFramesToProcess)110*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, CountsOutstandingFramesToProcess) {
111*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
112*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Millis(1));
113*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
114*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller.GetClock());
115*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
116*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame(_, 2, _)).Times(1);
117*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame(_, 1, _)).Times(1);
118*d9f75844SAndroid Build Coastguard Worker   auto frame = CreateFrame();
119*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(frame);
120*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(frame);
121*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Zero());
122*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame(_, 1, _)).Times(1);
123*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(frame);
124*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Zero());
125*d9f75844SAndroid Build Coastguard Worker }
126*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,FrameRateFollowsRateStatisticsByDefault)127*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, FrameRateFollowsRateStatisticsByDefault) {
128*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
129*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
130*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller.GetClock());
131*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(nullptr);
132*d9f75844SAndroid Build Coastguard Worker 
133*d9f75844SAndroid Build Coastguard Worker   // Create an "oracle" rate statistics which should be followed on a sequence
134*d9f75844SAndroid Build Coastguard Worker   // of frames.
135*d9f75844SAndroid Build Coastguard Worker   RateStatistics rate(
136*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::kFrameRateAveragingWindowSizeMs, 1000);
137*d9f75844SAndroid Build Coastguard Worker 
138*d9f75844SAndroid Build Coastguard Worker   for (int frame = 0; frame != 10; ++frame) {
139*d9f75844SAndroid Build Coastguard Worker     time_controller.AdvanceTime(TimeDelta::Millis(10));
140*d9f75844SAndroid Build Coastguard Worker     rate.Update(1, time_controller.GetClock()->TimeInMilliseconds());
141*d9f75844SAndroid Build Coastguard Worker     adapter->UpdateFrameRate();
142*d9f75844SAndroid Build Coastguard Worker     EXPECT_EQ(rate.Rate(time_controller.GetClock()->TimeInMilliseconds()),
143*d9f75844SAndroid Build Coastguard Worker               adapter->GetInputFrameRateFps())
144*d9f75844SAndroid Build Coastguard Worker         << " failed for frame " << frame;
145*d9f75844SAndroid Build Coastguard Worker   }
146*d9f75844SAndroid Build Coastguard Worker }
147*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,FrameRateFollowsRateStatisticsWhenFeatureDisabled)148*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest,
149*d9f75844SAndroid Build Coastguard Worker      FrameRateFollowsRateStatisticsWhenFeatureDisabled) {
150*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialDisabler feature_disabler;
151*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
152*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(feature_disabler, time_controller.GetClock());
153*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(nullptr);
154*d9f75844SAndroid Build Coastguard Worker 
155*d9f75844SAndroid Build Coastguard Worker   // Create an "oracle" rate statistics which should be followed on a sequence
156*d9f75844SAndroid Build Coastguard Worker   // of frames.
157*d9f75844SAndroid Build Coastguard Worker   RateStatistics rate(
158*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::kFrameRateAveragingWindowSizeMs, 1000);
159*d9f75844SAndroid Build Coastguard Worker 
160*d9f75844SAndroid Build Coastguard Worker   for (int frame = 0; frame != 10; ++frame) {
161*d9f75844SAndroid Build Coastguard Worker     time_controller.AdvanceTime(TimeDelta::Millis(10));
162*d9f75844SAndroid Build Coastguard Worker     rate.Update(1, time_controller.GetClock()->TimeInMilliseconds());
163*d9f75844SAndroid Build Coastguard Worker     adapter->UpdateFrameRate();
164*d9f75844SAndroid Build Coastguard Worker     EXPECT_EQ(rate.Rate(time_controller.GetClock()->TimeInMilliseconds()),
165*d9f75844SAndroid Build Coastguard Worker               adapter->GetInputFrameRateFps())
166*d9f75844SAndroid Build Coastguard Worker         << " failed for frame " << frame;
167*d9f75844SAndroid Build Coastguard Worker   }
168*d9f75844SAndroid Build Coastguard Worker }
169*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,FrameRateFollowsMaxFpsWhenZeroHertzActivated)170*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, FrameRateFollowsMaxFpsWhenZeroHertzActivated) {
171*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
172*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
173*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
174*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(nullptr);
175*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
176*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
177*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
178*d9f75844SAndroid Build Coastguard Worker   for (int frame = 0; frame != 10; ++frame) {
179*d9f75844SAndroid Build Coastguard Worker     time_controller.AdvanceTime(TimeDelta::Millis(10));
180*d9f75844SAndroid Build Coastguard Worker     adapter->UpdateFrameRate();
181*d9f75844SAndroid Build Coastguard Worker     EXPECT_EQ(adapter->GetInputFrameRateFps(), 1u);
182*d9f75844SAndroid Build Coastguard Worker   }
183*d9f75844SAndroid Build Coastguard Worker }
184*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,FrameRateFollowsRateStatisticsAfterZeroHertzDeactivated)185*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest,
186*d9f75844SAndroid Build Coastguard Worker      FrameRateFollowsRateStatisticsAfterZeroHertzDeactivated) {
187*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
188*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
189*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
190*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(nullptr);
191*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
192*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
193*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
194*d9f75844SAndroid Build Coastguard Worker   RateStatistics rate(
195*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::kFrameRateAveragingWindowSizeMs, 1000);
196*d9f75844SAndroid Build Coastguard Worker   constexpr int MAX = 10;
197*d9f75844SAndroid Build Coastguard Worker   for (int frame = 0; frame != MAX; ++frame) {
198*d9f75844SAndroid Build Coastguard Worker     time_controller.AdvanceTime(TimeDelta::Millis(10));
199*d9f75844SAndroid Build Coastguard Worker     rate.Update(1, time_controller.GetClock()->TimeInMilliseconds());
200*d9f75844SAndroid Build Coastguard Worker     adapter->UpdateFrameRate();
201*d9f75844SAndroid Build Coastguard Worker   }
202*d9f75844SAndroid Build Coastguard Worker   // Turn off zero hertz on the next-last frame; after the last frame we
203*d9f75844SAndroid Build Coastguard Worker   // should see a value that tracks the rate oracle.
204*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(absl::nullopt);
205*d9f75844SAndroid Build Coastguard Worker   // Last frame.
206*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Millis(10));
207*d9f75844SAndroid Build Coastguard Worker   rate.Update(1, time_controller.GetClock()->TimeInMilliseconds());
208*d9f75844SAndroid Build Coastguard Worker   adapter->UpdateFrameRate();
209*d9f75844SAndroid Build Coastguard Worker 
210*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(rate.Rate(time_controller.GetClock()->TimeInMilliseconds()),
211*d9f75844SAndroid Build Coastguard Worker             adapter->GetInputFrameRateFps());
212*d9f75844SAndroid Build Coastguard Worker }
213*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,ForwardsFramesDelayed)214*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, ForwardsFramesDelayed) {
215*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
216*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
217*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
218*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
219*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
220*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
221*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
222*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
223*d9f75844SAndroid Build Coastguard Worker   constexpr int kNumFrames = 3;
224*d9f75844SAndroid Build Coastguard Worker   NtpTime original_ntp_time = time_controller.GetClock()->CurrentNtpTime();
225*d9f75844SAndroid Build Coastguard Worker   auto frame = CreateFrameWithTimestamps(&time_controller);
226*d9f75844SAndroid Build Coastguard Worker   int64_t original_timestamp_us = frame.timestamp_us();
227*d9f75844SAndroid Build Coastguard Worker   for (int index = 0; index != kNumFrames; ++index) {
228*d9f75844SAndroid Build Coastguard Worker     EXPECT_CALL(callback, OnFrame).Times(0);
229*d9f75844SAndroid Build Coastguard Worker     adapter->OnFrame(frame);
230*d9f75844SAndroid Build Coastguard Worker     EXPECT_CALL(callback, OnFrame)
231*d9f75844SAndroid Build Coastguard Worker         .WillOnce(Invoke([&](Timestamp post_time, int,
232*d9f75844SAndroid Build Coastguard Worker                              const VideoFrame& frame) {
233*d9f75844SAndroid Build Coastguard Worker           EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
234*d9f75844SAndroid Build Coastguard Worker           EXPECT_EQ(frame.timestamp_us(),
235*d9f75844SAndroid Build Coastguard Worker                     original_timestamp_us + index * rtc::kNumMicrosecsPerSec);
236*d9f75844SAndroid Build Coastguard Worker           EXPECT_EQ(frame.ntp_time_ms(), original_ntp_time.ToMs() +
237*d9f75844SAndroid Build Coastguard Worker                                              index * rtc::kNumMillisecsPerSec);
238*d9f75844SAndroid Build Coastguard Worker         }));
239*d9f75844SAndroid Build Coastguard Worker     time_controller.AdvanceTime(TimeDelta::Seconds(1));
240*d9f75844SAndroid Build Coastguard Worker     frame = CreateFrameWithTimestamps(&time_controller);
241*d9f75844SAndroid Build Coastguard Worker   }
242*d9f75844SAndroid Build Coastguard Worker }
243*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,RepeatsFramesDelayed)244*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, RepeatsFramesDelayed) {
245*d9f75844SAndroid Build Coastguard Worker   // Logic in the frame cadence adapter avoids modifying frame NTP and render
246*d9f75844SAndroid Build Coastguard Worker   // timestamps if these timestamps looks unset, which is the case when the
247*d9f75844SAndroid Build Coastguard Worker   // clock is initialized running from 0. For this reason we choose the
248*d9f75844SAndroid Build Coastguard Worker   // `time_controller` initialization constant to something arbitrary which is
249*d9f75844SAndroid Build Coastguard Worker   // not 0.
250*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
251*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
252*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Millis(47892223));
253*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
254*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
255*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
256*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
257*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
258*d9f75844SAndroid Build Coastguard Worker   NtpTime original_ntp_time = time_controller.GetClock()->CurrentNtpTime();
259*d9f75844SAndroid Build Coastguard Worker 
260*d9f75844SAndroid Build Coastguard Worker   // Send one frame, expect 2 subsequent repeats.
261*d9f75844SAndroid Build Coastguard Worker   auto frame = CreateFrameWithTimestamps(&time_controller);
262*d9f75844SAndroid Build Coastguard Worker   int64_t original_timestamp_us = frame.timestamp_us();
263*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(frame);
264*d9f75844SAndroid Build Coastguard Worker 
265*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame)
266*d9f75844SAndroid Build Coastguard Worker       .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
267*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
268*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.timestamp_us(), original_timestamp_us);
269*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.ntp_time_ms(), original_ntp_time.ToMs());
270*d9f75844SAndroid Build Coastguard Worker       }));
271*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
272*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
273*d9f75844SAndroid Build Coastguard Worker 
274*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame)
275*d9f75844SAndroid Build Coastguard Worker       .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
276*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
277*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.timestamp_us(),
278*d9f75844SAndroid Build Coastguard Worker                   original_timestamp_us + rtc::kNumMicrosecsPerSec);
279*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.ntp_time_ms(),
280*d9f75844SAndroid Build Coastguard Worker                   original_ntp_time.ToMs() + rtc::kNumMillisecsPerSec);
281*d9f75844SAndroid Build Coastguard Worker       }));
282*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
283*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
284*d9f75844SAndroid Build Coastguard Worker 
285*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame)
286*d9f75844SAndroid Build Coastguard Worker       .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
287*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
288*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.timestamp_us(),
289*d9f75844SAndroid Build Coastguard Worker                   original_timestamp_us + 2 * rtc::kNumMicrosecsPerSec);
290*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.ntp_time_ms(),
291*d9f75844SAndroid Build Coastguard Worker                   original_ntp_time.ToMs() + 2 * rtc::kNumMillisecsPerSec);
292*d9f75844SAndroid Build Coastguard Worker       }));
293*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
294*d9f75844SAndroid Build Coastguard Worker }
295*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,RepeatsFramesWithoutTimestampsWithUnsetTimestamps)296*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest,
297*d9f75844SAndroid Build Coastguard Worker      RepeatsFramesWithoutTimestampsWithUnsetTimestamps) {
298*d9f75844SAndroid Build Coastguard Worker   // Logic in the frame cadence adapter avoids modifying frame NTP and render
299*d9f75844SAndroid Build Coastguard Worker   // timestamps if these timestamps looks unset, which is the case when the
300*d9f75844SAndroid Build Coastguard Worker   // clock is initialized running from 0. In this test we deliberately don't set
301*d9f75844SAndroid Build Coastguard Worker   // it to zero, but select unset timestamps in the frames (via CreateFrame())
302*d9f75844SAndroid Build Coastguard Worker   // and verify that the timestamp modifying logic doesn't depend on the current
303*d9f75844SAndroid Build Coastguard Worker   // time.
304*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
305*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
306*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Millis(4711));
307*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
308*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
309*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
310*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
311*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
312*d9f75844SAndroid Build Coastguard Worker 
313*d9f75844SAndroid Build Coastguard Worker   // Send one frame, expect a repeat.
314*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
315*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame)
316*d9f75844SAndroid Build Coastguard Worker       .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
317*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
318*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.timestamp_us(), 0);
319*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.ntp_time_ms(), 0);
320*d9f75844SAndroid Build Coastguard Worker       }));
321*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
322*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
323*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame)
324*d9f75844SAndroid Build Coastguard Worker       .WillOnce(Invoke([&](Timestamp post_time, int, const VideoFrame& frame) {
325*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(post_time, time_controller.GetClock()->CurrentTime());
326*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.timestamp_us(), 0);
327*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.ntp_time_ms(), 0);
328*d9f75844SAndroid Build Coastguard Worker       }));
329*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
330*d9f75844SAndroid Build Coastguard Worker }
331*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,StopsRepeatingFramesDelayed)332*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, StopsRepeatingFramesDelayed) {
333*d9f75844SAndroid Build Coastguard Worker   // At 1s, the initially scheduled frame appears.
334*d9f75844SAndroid Build Coastguard Worker   // At 2s, the repeated initial frame appears.
335*d9f75844SAndroid Build Coastguard Worker   // At 2.5s, we schedule another new frame.
336*d9f75844SAndroid Build Coastguard Worker   // At 3.5s, we receive this frame.
337*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
338*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
339*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
340*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
341*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
342*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
343*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
344*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 1});
345*d9f75844SAndroid Build Coastguard Worker   NtpTime original_ntp_time = time_controller.GetClock()->CurrentNtpTime();
346*d9f75844SAndroid Build Coastguard Worker 
347*d9f75844SAndroid Build Coastguard Worker   // Send one frame, expect 1 subsequent repeat.
348*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrameWithTimestamps(&time_controller));
349*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame).Times(2);
350*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(2.5));
351*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
352*d9f75844SAndroid Build Coastguard Worker 
353*d9f75844SAndroid Build Coastguard Worker   // Send the new frame at 2.5s, which should appear after 3.5s.
354*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrameWithTimestamps(&time_controller));
355*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, OnFrame)
356*d9f75844SAndroid Build Coastguard Worker       .WillOnce(Invoke([&](Timestamp, int, const VideoFrame& frame) {
357*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.timestamp_us(), 5 * rtc::kNumMicrosecsPerSec / 2);
358*d9f75844SAndroid Build Coastguard Worker         EXPECT_EQ(frame.ntp_time_ms(),
359*d9f75844SAndroid Build Coastguard Worker                   original_ntp_time.ToMs() + 5u * rtc::kNumMillisecsPerSec / 2);
360*d9f75844SAndroid Build Coastguard Worker       }));
361*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
362*d9f75844SAndroid Build Coastguard Worker }
363*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,RequestsRefreshFrameOnKeyFrameRequestWhenNew)364*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, RequestsRefreshFrameOnKeyFrameRequestWhenNew) {
365*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
366*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
367*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
368*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
369*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
370*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
371*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
372*d9f75844SAndroid Build Coastguard Worker   constexpr int kMaxFps = 10;
373*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, kMaxFps});
374*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame);
375*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(
376*d9f75844SAndroid Build Coastguard Worker       TimeDelta::Seconds(1) *
377*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::kOnDiscardedFrameRefreshFramePeriod /
378*d9f75844SAndroid Build Coastguard Worker       kMaxFps);
379*d9f75844SAndroid Build Coastguard Worker   adapter->ProcessKeyFrameRequest();
380*d9f75844SAndroid Build Coastguard Worker }
381*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,IgnoresKeyFrameRequestShortlyAfterFrame)382*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, IgnoresKeyFrameRequestShortlyAfterFrame) {
383*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
384*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
385*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
386*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
387*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
388*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
389*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
390*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 10});
391*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
392*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Zero());
393*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame).Times(0);
394*d9f75844SAndroid Build Coastguard Worker   adapter->ProcessKeyFrameRequest();
395*d9f75844SAndroid Build Coastguard Worker }
396*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,RequestsRefreshFramesUntilArrival)397*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, RequestsRefreshFramesUntilArrival) {
398*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
399*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
400*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
401*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
402*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
403*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
404*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
405*d9f75844SAndroid Build Coastguard Worker   constexpr int kMaxFps = 10;
406*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, kMaxFps});
407*d9f75844SAndroid Build Coastguard Worker 
408*d9f75844SAndroid Build Coastguard Worker   // We should see max_fps + 1 -
409*d9f75844SAndroid Build Coastguard Worker   // FrameCadenceAdapterInterface::kOnDiscardedFrameRefreshFramePeriod refresh
410*d9f75844SAndroid Build Coastguard Worker   // frame requests during the one second we wait until we send a single frame,
411*d9f75844SAndroid Build Coastguard Worker   // after which refresh frame requests should cease (we should see no such
412*d9f75844SAndroid Build Coastguard Worker   // requests during a second).
413*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame)
414*d9f75844SAndroid Build Coastguard Worker       .Times(kMaxFps + 1 -
415*d9f75844SAndroid Build Coastguard Worker              FrameCadenceAdapterInterface::kOnDiscardedFrameRefreshFramePeriod);
416*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
417*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
418*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
419*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame).Times(0);
420*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
421*d9f75844SAndroid Build Coastguard Worker }
422*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,RequestsRefreshAfterFrameDrop)423*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, RequestsRefreshAfterFrameDrop) {
424*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
425*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
426*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
427*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
428*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
429*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
430*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
431*d9f75844SAndroid Build Coastguard Worker   constexpr int kMaxFps = 10;
432*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, kMaxFps});
433*d9f75844SAndroid Build Coastguard Worker 
434*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame).Times(0);
435*d9f75844SAndroid Build Coastguard Worker 
436*d9f75844SAndroid Build Coastguard Worker   // Send a frame through to cancel the initial delayed timer waiting for first
437*d9f75844SAndroid Build Coastguard Worker   // frame entry.
438*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
439*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
440*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
441*d9f75844SAndroid Build Coastguard Worker 
442*d9f75844SAndroid Build Coastguard Worker   // Send a dropped frame indication without any following frames received.
443*d9f75844SAndroid Build Coastguard Worker   // After FrameCadenceAdapterInterface::kOnDiscardedFrameRefreshFramePeriod
444*d9f75844SAndroid Build Coastguard Worker   // frame periods, we should receive a first refresh request.
445*d9f75844SAndroid Build Coastguard Worker   adapter->OnDiscardedFrame();
446*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame);
447*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(
448*d9f75844SAndroid Build Coastguard Worker       TimeDelta::Seconds(1) *
449*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::kOnDiscardedFrameRefreshFramePeriod /
450*d9f75844SAndroid Build Coastguard Worker       kMaxFps);
451*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
452*d9f75844SAndroid Build Coastguard Worker 
453*d9f75844SAndroid Build Coastguard Worker   // We will now receive a refresh frame request for every frame period.
454*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame).Times(kMaxFps);
455*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
456*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
457*d9f75844SAndroid Build Coastguard Worker 
458*d9f75844SAndroid Build Coastguard Worker   // After a frame is passed the requests will cease.
459*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame).Times(0);
460*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
461*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
462*d9f75844SAndroid Build Coastguard Worker }
463*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,OmitsRefreshAfterFrameDropWithTimelyFrameEntry)464*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, OmitsRefreshAfterFrameDropWithTimelyFrameEntry) {
465*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
466*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
467*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
468*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
469*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
470*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
471*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
472*d9f75844SAndroid Build Coastguard Worker   constexpr int kMaxFps = 10;
473*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, kMaxFps});
474*d9f75844SAndroid Build Coastguard Worker 
475*d9f75844SAndroid Build Coastguard Worker   // Send a frame through to cancel the initial delayed timer waiting for first
476*d9f75844SAndroid Build Coastguard Worker   // frame entry.
477*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame).Times(0);
478*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
479*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
480*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
481*d9f75844SAndroid Build Coastguard Worker 
482*d9f75844SAndroid Build Coastguard Worker   // Send a frame drop indication. No refresh frames should be requested
483*d9f75844SAndroid Build Coastguard Worker   // until FrameCadenceAdapterInterface::kOnDiscardedFrameRefreshFramePeriod
484*d9f75844SAndroid Build Coastguard Worker   // intervals pass. Stop short of this.
485*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame).Times(0);
486*d9f75844SAndroid Build Coastguard Worker   adapter->OnDiscardedFrame();
487*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(
488*d9f75844SAndroid Build Coastguard Worker       TimeDelta::Seconds(1) *
489*d9f75844SAndroid Build Coastguard Worker           FrameCadenceAdapterInterface::kOnDiscardedFrameRefreshFramePeriod /
490*d9f75844SAndroid Build Coastguard Worker           kMaxFps -
491*d9f75844SAndroid Build Coastguard Worker       TimeDelta::Micros(1));
492*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
493*d9f75844SAndroid Build Coastguard Worker 
494*d9f75844SAndroid Build Coastguard Worker   // Send a frame. The timer to request the refresh frame should be cancelled by
495*d9f75844SAndroid Build Coastguard Worker   // the reception, so no refreshes should be requested.
496*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback, RequestRefreshFrame).Times(0);
497*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
498*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Seconds(1));
499*d9f75844SAndroid Build Coastguard Worker   Mock::VerifyAndClearExpectations(&callback);
500*d9f75844SAndroid Build Coastguard Worker }
501*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterTest,AcceptsUnconfiguredLayerFeedback)502*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterTest, AcceptsUnconfiguredLayerFeedback) {
503*d9f75844SAndroid Build Coastguard Worker   // This is a regression test for bugs.webrtc.org/14417.
504*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
505*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
506*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller(Timestamp::Zero());
507*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(enabler, time_controller.GetClock());
508*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
509*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
510*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{.num_simulcast_layers =
511*d9f75844SAndroid Build Coastguard Worker                                                             1});
512*d9f75844SAndroid Build Coastguard Worker   constexpr int kMaxFps = 10;
513*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, kMaxFps});
514*d9f75844SAndroid Build Coastguard Worker   time_controller.AdvanceTime(TimeDelta::Zero());
515*d9f75844SAndroid Build Coastguard Worker 
516*d9f75844SAndroid Build Coastguard Worker   adapter->UpdateLayerQualityConvergence(2, false);
517*d9f75844SAndroid Build Coastguard Worker   adapter->UpdateLayerStatus(2, false);
518*d9f75844SAndroid Build Coastguard Worker }
519*d9f75844SAndroid Build Coastguard Worker 
520*d9f75844SAndroid Build Coastguard Worker class FrameCadenceAdapterSimulcastLayersParamTest
521*d9f75844SAndroid Build Coastguard Worker     : public ::testing::TestWithParam<int> {
522*d9f75844SAndroid Build Coastguard Worker  public:
523*d9f75844SAndroid Build Coastguard Worker   static constexpr int kMaxFpsHz = 8;
524*d9f75844SAndroid Build Coastguard Worker   static constexpr TimeDelta kMinFrameDelay =
525*d9f75844SAndroid Build Coastguard Worker       TimeDelta::Millis(1000 / kMaxFpsHz);
526*d9f75844SAndroid Build Coastguard Worker   static constexpr TimeDelta kIdleFrameDelay =
527*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::kZeroHertzIdleRepeatRatePeriod;
528*d9f75844SAndroid Build Coastguard Worker 
FrameCadenceAdapterSimulcastLayersParamTest()529*d9f75844SAndroid Build Coastguard Worker   FrameCadenceAdapterSimulcastLayersParamTest() {
530*d9f75844SAndroid Build Coastguard Worker     adapter_->Initialize(&callback_);
531*d9f75844SAndroid Build Coastguard Worker     adapter_->OnConstraintsChanged(VideoTrackSourceConstraints{0, kMaxFpsHz});
532*d9f75844SAndroid Build Coastguard Worker     time_controller_.AdvanceTime(TimeDelta::Zero());
533*d9f75844SAndroid Build Coastguard Worker     adapter_->SetZeroHertzModeEnabled(
534*d9f75844SAndroid Build Coastguard Worker         FrameCadenceAdapterInterface::ZeroHertzModeParams{});
535*d9f75844SAndroid Build Coastguard Worker     const size_t num_spatial_layers = GetParam();
536*d9f75844SAndroid Build Coastguard Worker     adapter_->SetZeroHertzModeEnabled(
537*d9f75844SAndroid Build Coastguard Worker         FrameCadenceAdapterInterface::ZeroHertzModeParams{num_spatial_layers});
538*d9f75844SAndroid Build Coastguard Worker   }
539*d9f75844SAndroid Build Coastguard Worker 
NumSpatialLayers() const540*d9f75844SAndroid Build Coastguard Worker   int NumSpatialLayers() const { return GetParam(); }
541*d9f75844SAndroid Build Coastguard Worker 
542*d9f75844SAndroid Build Coastguard Worker  protected:
543*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler_;
544*d9f75844SAndroid Build Coastguard Worker   MockCallback callback_;
545*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller_{Timestamp::Zero()};
546*d9f75844SAndroid Build Coastguard Worker   const std::unique_ptr<FrameCadenceAdapterInterface> adapter_{
547*d9f75844SAndroid Build Coastguard Worker       CreateAdapter(enabler_, time_controller_.GetClock())};
548*d9f75844SAndroid Build Coastguard Worker };
549*d9f75844SAndroid Build Coastguard Worker 
TEST_P(FrameCadenceAdapterSimulcastLayersParamTest,LayerReconfigurationResetsConvergenceInfo)550*d9f75844SAndroid Build Coastguard Worker TEST_P(FrameCadenceAdapterSimulcastLayersParamTest,
551*d9f75844SAndroid Build Coastguard Worker        LayerReconfigurationResetsConvergenceInfo) {
552*d9f75844SAndroid Build Coastguard Worker   // Assumes layer reconfiguration has just happened.
553*d9f75844SAndroid Build Coastguard Worker   // Verify the state is unconverged.
554*d9f75844SAndroid Build Coastguard Worker   adapter_->OnFrame(CreateFrame());
555*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback_, OnFrame).Times(kMaxFpsHz);
556*d9f75844SAndroid Build Coastguard Worker   time_controller_.AdvanceTime(kMaxFpsHz * kMinFrameDelay);
557*d9f75844SAndroid Build Coastguard Worker }
558*d9f75844SAndroid Build Coastguard Worker 
TEST_P(FrameCadenceAdapterSimulcastLayersParamTest,IgnoresKeyFrameRequestWhileShortRepeating)559*d9f75844SAndroid Build Coastguard Worker TEST_P(FrameCadenceAdapterSimulcastLayersParamTest,
560*d9f75844SAndroid Build Coastguard Worker        IgnoresKeyFrameRequestWhileShortRepeating) {
561*d9f75844SAndroid Build Coastguard Worker   // Plot:
562*d9f75844SAndroid Build Coastguard Worker   // 1. 0 * kMinFrameDelay: Start unconverged. Frame -> adapter.
563*d9f75844SAndroid Build Coastguard Worker   // 2. 1 * kMinFrameDelay: Frame -> callback.
564*d9f75844SAndroid Build Coastguard Worker   // 3. 2 * kMinFrameDelay: 1st short repeat.
565*d9f75844SAndroid Build Coastguard Worker   // Since we're unconverged we assume the process continues.
566*d9f75844SAndroid Build Coastguard Worker   adapter_->OnFrame(CreateFrame());
567*d9f75844SAndroid Build Coastguard Worker   time_controller_.AdvanceTime(2 * kMinFrameDelay);
568*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback_, RequestRefreshFrame).Times(0);
569*d9f75844SAndroid Build Coastguard Worker   adapter_->ProcessKeyFrameRequest();
570*d9f75844SAndroid Build Coastguard Worker 
571*d9f75844SAndroid Build Coastguard Worker   // Expect short repeating as ususal.
572*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback_, OnFrame).Times(8);
573*d9f75844SAndroid Build Coastguard Worker   time_controller_.AdvanceTime(8 * kMinFrameDelay);
574*d9f75844SAndroid Build Coastguard Worker }
575*d9f75844SAndroid Build Coastguard Worker 
TEST_P(FrameCadenceAdapterSimulcastLayersParamTest,IgnoresKeyFrameRequestJustBeforeIdleRepeating)576*d9f75844SAndroid Build Coastguard Worker TEST_P(FrameCadenceAdapterSimulcastLayersParamTest,
577*d9f75844SAndroid Build Coastguard Worker        IgnoresKeyFrameRequestJustBeforeIdleRepeating) {
578*d9f75844SAndroid Build Coastguard Worker   // (Only for > 0 spatial layers as we assume not converged with 0 layers)
579*d9f75844SAndroid Build Coastguard Worker   if (NumSpatialLayers() == 0)
580*d9f75844SAndroid Build Coastguard Worker     return;
581*d9f75844SAndroid Build Coastguard Worker 
582*d9f75844SAndroid Build Coastguard Worker   // Plot:
583*d9f75844SAndroid Build Coastguard Worker   // 1. 0 * kMinFrameDelay: Start converged. Frame -> adapter.
584*d9f75844SAndroid Build Coastguard Worker   // 2. 1 * kMinFrameDelay: Frame -> callback. New repeat scheduled at
585*d9f75844SAndroid Build Coastguard Worker   //    (kMaxFpsHz + 1) * kMinFrameDelay.
586*d9f75844SAndroid Build Coastguard Worker   // 3. kMaxFpsHz * kMinFrameDelay: Process keyframe.
587*d9f75844SAndroid Build Coastguard Worker   // 4. (kMaxFpsHz + N) * kMinFrameDelay (1 <= N <= kMaxFpsHz): Short repeats
588*d9f75844SAndroid Build Coastguard Worker   //    due to not converged.
589*d9f75844SAndroid Build Coastguard Worker   for (int i = 0; i != NumSpatialLayers(); i++) {
590*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerStatus(i, /*enabled=*/true);
591*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerQualityConvergence(i, /*converged=*/true);
592*d9f75844SAndroid Build Coastguard Worker   }
593*d9f75844SAndroid Build Coastguard Worker   adapter_->OnFrame(CreateFrame());
594*d9f75844SAndroid Build Coastguard Worker   time_controller_.AdvanceTime(kIdleFrameDelay);
595*d9f75844SAndroid Build Coastguard Worker 
596*d9f75844SAndroid Build Coastguard Worker   // We process the key frame request kMinFrameDelay before the first idle
597*d9f75844SAndroid Build Coastguard Worker   // repeat should happen. The resulting repeats should happen spaced by
598*d9f75844SAndroid Build Coastguard Worker   // kMinFrameDelay before we get new convergence info.
599*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback_, RequestRefreshFrame).Times(0);
600*d9f75844SAndroid Build Coastguard Worker   adapter_->ProcessKeyFrameRequest();
601*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback_, OnFrame).Times(kMaxFpsHz);
602*d9f75844SAndroid Build Coastguard Worker   time_controller_.AdvanceTime(kMaxFpsHz * kMinFrameDelay);
603*d9f75844SAndroid Build Coastguard Worker }
604*d9f75844SAndroid Build Coastguard Worker 
TEST_P(FrameCadenceAdapterSimulcastLayersParamTest,IgnoresKeyFrameRequestShortRepeatsBeforeIdleRepeat)605*d9f75844SAndroid Build Coastguard Worker TEST_P(FrameCadenceAdapterSimulcastLayersParamTest,
606*d9f75844SAndroid Build Coastguard Worker        IgnoresKeyFrameRequestShortRepeatsBeforeIdleRepeat) {
607*d9f75844SAndroid Build Coastguard Worker   // (Only for > 0 spatial layers as we assume not converged with 0 layers)
608*d9f75844SAndroid Build Coastguard Worker   if (NumSpatialLayers() == 0)
609*d9f75844SAndroid Build Coastguard Worker     return;
610*d9f75844SAndroid Build Coastguard Worker   // Plot:
611*d9f75844SAndroid Build Coastguard Worker   // 1. 0 * kMinFrameDelay: Start converged. Frame -> adapter.
612*d9f75844SAndroid Build Coastguard Worker   // 2. 1 * kMinFrameDelay: Frame -> callback. New repeat scheduled at
613*d9f75844SAndroid Build Coastguard Worker   //    (kMaxFpsHz + 1) * kMinFrameDelay.
614*d9f75844SAndroid Build Coastguard Worker   // 3. 2 * kMinFrameDelay: Process keyframe.
615*d9f75844SAndroid Build Coastguard Worker   // 4. (2 + N) * kMinFrameDelay (1 <= N <= kMaxFpsHz): Short repeats due to not
616*d9f75844SAndroid Build Coastguard Worker   //    converged.
617*d9f75844SAndroid Build Coastguard Worker   for (int i = 0; i != NumSpatialLayers(); i++) {
618*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerStatus(i, /*enabled=*/true);
619*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerQualityConvergence(i, /*converged=*/true);
620*d9f75844SAndroid Build Coastguard Worker   }
621*d9f75844SAndroid Build Coastguard Worker   adapter_->OnFrame(CreateFrame());
622*d9f75844SAndroid Build Coastguard Worker   time_controller_.AdvanceTime(2 * kMinFrameDelay);
623*d9f75844SAndroid Build Coastguard Worker 
624*d9f75844SAndroid Build Coastguard Worker   // We process the key frame request (kMaxFpsHz - 1) * kMinFrameDelay before
625*d9f75844SAndroid Build Coastguard Worker   // the first idle repeat should happen. The resulting repeats should happen
626*d9f75844SAndroid Build Coastguard Worker   // spaced kMinFrameDelay before we get new convergence info.
627*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback_, RequestRefreshFrame).Times(0);
628*d9f75844SAndroid Build Coastguard Worker   adapter_->ProcessKeyFrameRequest();
629*d9f75844SAndroid Build Coastguard Worker   EXPECT_CALL(callback_, OnFrame).Times(kMaxFpsHz);
630*d9f75844SAndroid Build Coastguard Worker   time_controller_.AdvanceTime(kMaxFpsHz * kMinFrameDelay);
631*d9f75844SAndroid Build Coastguard Worker }
632*d9f75844SAndroid Build Coastguard Worker 
633*d9f75844SAndroid Build Coastguard Worker INSTANTIATE_TEST_SUITE_P(,
634*d9f75844SAndroid Build Coastguard Worker                          FrameCadenceAdapterSimulcastLayersParamTest,
635*d9f75844SAndroid Build Coastguard Worker                          Values(0, 1, 2));
636*d9f75844SAndroid Build Coastguard Worker 
637*d9f75844SAndroid Build Coastguard Worker class ZeroHertzLayerQualityConvergenceTest : public ::testing::Test {
638*d9f75844SAndroid Build Coastguard Worker  public:
639*d9f75844SAndroid Build Coastguard Worker   static constexpr TimeDelta kMinFrameDelay = TimeDelta::Millis(100);
640*d9f75844SAndroid Build Coastguard Worker   static constexpr TimeDelta kIdleFrameDelay =
641*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::kZeroHertzIdleRepeatRatePeriod;
642*d9f75844SAndroid Build Coastguard Worker 
ZeroHertzLayerQualityConvergenceTest()643*d9f75844SAndroid Build Coastguard Worker   ZeroHertzLayerQualityConvergenceTest() {
644*d9f75844SAndroid Build Coastguard Worker     adapter_->Initialize(&callback_);
645*d9f75844SAndroid Build Coastguard Worker     adapter_->SetZeroHertzModeEnabled(
646*d9f75844SAndroid Build Coastguard Worker         FrameCadenceAdapterInterface::ZeroHertzModeParams{
647*d9f75844SAndroid Build Coastguard Worker             /*num_simulcast_layers=*/2});
648*d9f75844SAndroid Build Coastguard Worker     adapter_->OnConstraintsChanged(VideoTrackSourceConstraints{
649*d9f75844SAndroid Build Coastguard Worker         /*min_fps=*/0, /*max_fps=*/TimeDelta::Seconds(1) / kMinFrameDelay});
650*d9f75844SAndroid Build Coastguard Worker     time_controller_.AdvanceTime(TimeDelta::Zero());
651*d9f75844SAndroid Build Coastguard Worker   }
652*d9f75844SAndroid Build Coastguard Worker 
PassFrame()653*d9f75844SAndroid Build Coastguard Worker   void PassFrame() { adapter_->OnFrame(CreateFrame()); }
654*d9f75844SAndroid Build Coastguard Worker 
ExpectFrameEntriesAtDelaysFromNow(std::initializer_list<TimeDelta> list)655*d9f75844SAndroid Build Coastguard Worker   void ExpectFrameEntriesAtDelaysFromNow(
656*d9f75844SAndroid Build Coastguard Worker       std::initializer_list<TimeDelta> list) {
657*d9f75844SAndroid Build Coastguard Worker     Timestamp origin = time_controller_.GetClock()->CurrentTime();
658*d9f75844SAndroid Build Coastguard Worker     for (auto delay : list) {
659*d9f75844SAndroid Build Coastguard Worker       EXPECT_CALL(callback_, OnFrame(origin + delay, _, _));
660*d9f75844SAndroid Build Coastguard Worker       time_controller_.AdvanceTime(origin + delay -
661*d9f75844SAndroid Build Coastguard Worker                                    time_controller_.GetClock()->CurrentTime());
662*d9f75844SAndroid Build Coastguard Worker     }
663*d9f75844SAndroid Build Coastguard Worker   }
664*d9f75844SAndroid Build Coastguard Worker 
ScheduleDelayed(TimeDelta delay,absl::AnyInvocable<void ()&&> task)665*d9f75844SAndroid Build Coastguard Worker   void ScheduleDelayed(TimeDelta delay, absl::AnyInvocable<void() &&> task) {
666*d9f75844SAndroid Build Coastguard Worker     TaskQueueBase::Current()->PostDelayedTask(std::move(task), delay);
667*d9f75844SAndroid Build Coastguard Worker   }
668*d9f75844SAndroid Build Coastguard Worker 
669*d9f75844SAndroid Build Coastguard Worker  protected:
670*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler field_trial_enabler_;
671*d9f75844SAndroid Build Coastguard Worker   MockCallback callback_;
672*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller_{Timestamp::Zero()};
673*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<FrameCadenceAdapterInterface> adapter_{
674*d9f75844SAndroid Build Coastguard Worker       CreateAdapter(field_trial_enabler_, time_controller_.GetClock())};
675*d9f75844SAndroid Build Coastguard Worker };
676*d9f75844SAndroid Build Coastguard Worker 
TEST_F(ZeroHertzLayerQualityConvergenceTest,InitialStateUnconverged)677*d9f75844SAndroid Build Coastguard Worker TEST_F(ZeroHertzLayerQualityConvergenceTest, InitialStateUnconverged) {
678*d9f75844SAndroid Build Coastguard Worker   // As the layer count is just configured, assume we start out as unconverged.
679*d9f75844SAndroid Build Coastguard Worker   PassFrame();
680*d9f75844SAndroid Build Coastguard Worker   ExpectFrameEntriesAtDelaysFromNow({
681*d9f75844SAndroid Build Coastguard Worker       1 * kMinFrameDelay,  // Original frame emitted
682*d9f75844SAndroid Build Coastguard Worker       2 * kMinFrameDelay,  // Short repeats.
683*d9f75844SAndroid Build Coastguard Worker       3 * kMinFrameDelay,  // ...
684*d9f75844SAndroid Build Coastguard Worker   });
685*d9f75844SAndroid Build Coastguard Worker }
686*d9f75844SAndroid Build Coastguard Worker 
TEST_F(ZeroHertzLayerQualityConvergenceTest,UnconvergedAfterLayersEnabled)687*d9f75844SAndroid Build Coastguard Worker TEST_F(ZeroHertzLayerQualityConvergenceTest, UnconvergedAfterLayersEnabled) {
688*d9f75844SAndroid Build Coastguard Worker   // With newly enabled layers we assume quality is unconverged.
689*d9f75844SAndroid Build Coastguard Worker   adapter_->UpdateLayerStatus(0, /*enabled=*/true);
690*d9f75844SAndroid Build Coastguard Worker   adapter_->UpdateLayerStatus(1, /*enabled=*/true);
691*d9f75844SAndroid Build Coastguard Worker   PassFrame();
692*d9f75844SAndroid Build Coastguard Worker   ExpectFrameEntriesAtDelaysFromNow({
693*d9f75844SAndroid Build Coastguard Worker       kMinFrameDelay,      // Original frame emitted
694*d9f75844SAndroid Build Coastguard Worker       2 * kMinFrameDelay,  // Unconverged repeats.
695*d9f75844SAndroid Build Coastguard Worker       3 * kMinFrameDelay,  // ...
696*d9f75844SAndroid Build Coastguard Worker   });
697*d9f75844SAndroid Build Coastguard Worker }
698*d9f75844SAndroid Build Coastguard Worker 
TEST_F(ZeroHertzLayerQualityConvergenceTest,RepeatsPassedFramesUntilConvergence)699*d9f75844SAndroid Build Coastguard Worker TEST_F(ZeroHertzLayerQualityConvergenceTest,
700*d9f75844SAndroid Build Coastguard Worker        RepeatsPassedFramesUntilConvergence) {
701*d9f75844SAndroid Build Coastguard Worker   ScheduleDelayed(TimeDelta::Zero(), [&] {
702*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerStatus(0, /*enabled=*/true);
703*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerStatus(1, /*enabled=*/true);
704*d9f75844SAndroid Build Coastguard Worker     PassFrame();
705*d9f75844SAndroid Build Coastguard Worker   });
706*d9f75844SAndroid Build Coastguard Worker   ScheduleDelayed(2.5 * kMinFrameDelay, [&] {
707*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerQualityConvergence(/*spatial_index=*/1, true);
708*d9f75844SAndroid Build Coastguard Worker   });
709*d9f75844SAndroid Build Coastguard Worker   ScheduleDelayed(3.5 * kMinFrameDelay, [&] {
710*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerQualityConvergence(/*spatial_index=*/0, true);
711*d9f75844SAndroid Build Coastguard Worker   });
712*d9f75844SAndroid Build Coastguard Worker   ScheduleDelayed(8 * kMinFrameDelay, [&] { PassFrame(); });
713*d9f75844SAndroid Build Coastguard Worker   ScheduleDelayed(9.5 * kMinFrameDelay, [&] {
714*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerQualityConvergence(/*spatial_index=*/0, true);
715*d9f75844SAndroid Build Coastguard Worker   });
716*d9f75844SAndroid Build Coastguard Worker   ScheduleDelayed(10.5 * kMinFrameDelay, [&] {
717*d9f75844SAndroid Build Coastguard Worker     adapter_->UpdateLayerQualityConvergence(/*spatial_index=*/1, true);
718*d9f75844SAndroid Build Coastguard Worker   });
719*d9f75844SAndroid Build Coastguard Worker   ExpectFrameEntriesAtDelaysFromNow({
720*d9f75844SAndroid Build Coastguard Worker       kMinFrameDelay,      // Original frame emitted
721*d9f75844SAndroid Build Coastguard Worker       2 * kMinFrameDelay,  // Repeat from kMinFrameDelay.
722*d9f75844SAndroid Build Coastguard Worker 
723*d9f75844SAndroid Build Coastguard Worker       // 2.5 * kMinFrameDelay: Converged in layer 1, layer 0 still unconverged.
724*d9f75844SAndroid Build Coastguard Worker       3 * kMinFrameDelay,  // Repeat from 2 * kMinFrameDelay.
725*d9f75844SAndroid Build Coastguard Worker 
726*d9f75844SAndroid Build Coastguard Worker       // 3.5 * kMinFrameDelay: Converged in layer 0 as well.
727*d9f75844SAndroid Build Coastguard Worker       4 * kMinFrameDelay,  // Repeat from 3 * kMinFrameDelay. An idle repeat is
728*d9f75844SAndroid Build Coastguard Worker                            // scheduled for kIdleFrameDelay + 3 *
729*d9f75844SAndroid Build Coastguard Worker                            // kMinFrameDelay.
730*d9f75844SAndroid Build Coastguard Worker 
731*d9f75844SAndroid Build Coastguard Worker       // A new frame is passed at 8 * kMinFrameDelay.
732*d9f75844SAndroid Build Coastguard Worker       9 * kMinFrameDelay,  // Original frame emitted
733*d9f75844SAndroid Build Coastguard Worker 
734*d9f75844SAndroid Build Coastguard Worker       // 9.5 * kMinFrameDelay: Converged in layer 0, layer 1 still unconverged.
735*d9f75844SAndroid Build Coastguard Worker       10 * kMinFrameDelay,  // Repeat from 9 * kMinFrameDelay.
736*d9f75844SAndroid Build Coastguard Worker       // 10.5 * kMinFrameDelay: Converged in layer 0 as well.
737*d9f75844SAndroid Build Coastguard Worker       11 * kMinFrameDelay,                        // Idle repeats from 1000.
738*d9f75844SAndroid Build Coastguard Worker       11 * kMinFrameDelay + kIdleFrameDelay,      // ...
739*d9f75844SAndroid Build Coastguard Worker       11 * kMinFrameDelay + 2 * kIdleFrameDelay,  // ...
740*d9f75844SAndroid Build Coastguard Worker                                                   // ...
741*d9f75844SAndroid Build Coastguard Worker   });
742*d9f75844SAndroid Build Coastguard Worker }
743*d9f75844SAndroid Build Coastguard Worker 
744*d9f75844SAndroid Build Coastguard Worker class FrameCadenceAdapterMetricsTest : public ::testing::Test {
745*d9f75844SAndroid Build Coastguard Worker  public:
FrameCadenceAdapterMetricsTest()746*d9f75844SAndroid Build Coastguard Worker   FrameCadenceAdapterMetricsTest() : time_controller_(Timestamp::Millis(1)) {
747*d9f75844SAndroid Build Coastguard Worker     metrics::Reset();
748*d9f75844SAndroid Build Coastguard Worker   }
DepleteTaskQueues()749*d9f75844SAndroid Build Coastguard Worker   void DepleteTaskQueues() { time_controller_.AdvanceTime(TimeDelta::Zero()); }
750*d9f75844SAndroid Build Coastguard Worker 
751*d9f75844SAndroid Build Coastguard Worker  protected:
752*d9f75844SAndroid Build Coastguard Worker   GlobalSimulatedTimeController time_controller_;
753*d9f75844SAndroid Build Coastguard Worker };
754*d9f75844SAndroid Build Coastguard Worker 
TEST_F(FrameCadenceAdapterMetricsTest,RecordsNoUmasWithNoFrameTransfer)755*d9f75844SAndroid Build Coastguard Worker TEST_F(FrameCadenceAdapterMetricsTest, RecordsNoUmasWithNoFrameTransfer) {
756*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
757*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
758*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, nullptr);
759*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
760*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(
761*d9f75844SAndroid Build Coastguard Worker       VideoTrackSourceConstraints{absl::nullopt, absl::nullopt});
762*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{absl::nullopt, 1});
763*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{2, 3});
764*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{4, 4});
765*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{5, absl::nullopt});
766*d9f75844SAndroid Build Coastguard Worker   DepleteTaskQueues();
767*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Exists")
768*d9f75844SAndroid Build Coastguard Worker                   .empty());
769*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
770*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Exists")
771*d9f75844SAndroid Build Coastguard Worker           .empty());
772*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
773*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Value")
774*d9f75844SAndroid Build Coastguard Worker           .empty());
775*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
776*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Exists")
777*d9f75844SAndroid Build Coastguard Worker           .empty());
778*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
779*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Value")
780*d9f75844SAndroid Build Coastguard Worker           .empty());
781*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
782*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.MinUnset.Max")
783*d9f75844SAndroid Build Coastguard Worker           .empty());
784*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
785*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Min")
786*d9f75844SAndroid Build Coastguard Worker                   .empty());
787*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
788*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Max")
789*d9f75844SAndroid Build Coastguard Worker                   .empty());
790*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
791*d9f75844SAndroid Build Coastguard Worker       metrics::Samples(
792*d9f75844SAndroid Build Coastguard Worker           "WebRTC.Screenshare.FrameRateConstraints.60MinPlusMaxMinusOne")
793*d9f75844SAndroid Build Coastguard Worker           .empty());
794*d9f75844SAndroid Build Coastguard Worker }
795*d9f75844SAndroid Build Coastguard Worker 
TEST_F(FrameCadenceAdapterMetricsTest,RecordsNoUmasWithoutEnabledContentType)796*d9f75844SAndroid Build Coastguard Worker TEST_F(FrameCadenceAdapterMetricsTest, RecordsNoUmasWithoutEnabledContentType) {
797*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
798*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
799*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller_.GetClock());
800*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
801*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
802*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(
803*d9f75844SAndroid Build Coastguard Worker       VideoTrackSourceConstraints{absl::nullopt, absl::nullopt});
804*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{absl::nullopt, 1});
805*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{2, 3});
806*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{4, 4});
807*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{5, absl::nullopt});
808*d9f75844SAndroid Build Coastguard Worker   DepleteTaskQueues();
809*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Exists")
810*d9f75844SAndroid Build Coastguard Worker                   .empty());
811*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
812*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Exists")
813*d9f75844SAndroid Build Coastguard Worker           .empty());
814*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
815*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Value")
816*d9f75844SAndroid Build Coastguard Worker           .empty());
817*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
818*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Exists")
819*d9f75844SAndroid Build Coastguard Worker           .empty());
820*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
821*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Value")
822*d9f75844SAndroid Build Coastguard Worker           .empty());
823*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
824*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.MinUnset.Max")
825*d9f75844SAndroid Build Coastguard Worker           .empty());
826*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
827*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Min")
828*d9f75844SAndroid Build Coastguard Worker                   .empty());
829*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
830*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Max")
831*d9f75844SAndroid Build Coastguard Worker                   .empty());
832*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
833*d9f75844SAndroid Build Coastguard Worker       metrics::Samples(
834*d9f75844SAndroid Build Coastguard Worker           "WebRTC.Screenshare.FrameRateConstraints.60MinPlusMaxMinusOne")
835*d9f75844SAndroid Build Coastguard Worker           .empty());
836*d9f75844SAndroid Build Coastguard Worker }
837*d9f75844SAndroid Build Coastguard Worker 
TEST_F(FrameCadenceAdapterMetricsTest,RecordsNoConstraintsIfUnsetOnFrame)838*d9f75844SAndroid Build Coastguard Worker TEST_F(FrameCadenceAdapterMetricsTest, RecordsNoConstraintsIfUnsetOnFrame) {
839*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
840*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
841*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller_.GetClock());
842*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
843*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
844*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
845*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
846*d9f75844SAndroid Build Coastguard Worker   DepleteTaskQueues();
847*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
848*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Exists"),
849*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(false, 1)));
850*d9f75844SAndroid Build Coastguard Worker }
851*d9f75844SAndroid Build Coastguard Worker 
TEST_F(FrameCadenceAdapterMetricsTest,RecordsEmptyConstraintsIfSetOnFrame)852*d9f75844SAndroid Build Coastguard Worker TEST_F(FrameCadenceAdapterMetricsTest, RecordsEmptyConstraintsIfSetOnFrame) {
853*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
854*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
855*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller_.GetClock());
856*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
857*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
858*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
859*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(
860*d9f75844SAndroid Build Coastguard Worker       VideoTrackSourceConstraints{absl::nullopt, absl::nullopt});
861*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
862*d9f75844SAndroid Build Coastguard Worker   DepleteTaskQueues();
863*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
864*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Exists"),
865*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(true, 1)));
866*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
867*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Exists"),
868*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(false, 1)));
869*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
870*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Value")
871*d9f75844SAndroid Build Coastguard Worker           .empty());
872*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
873*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Exists"),
874*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(false, 1)));
875*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
876*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Value")
877*d9f75844SAndroid Build Coastguard Worker           .empty());
878*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
879*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.MinUnset.Max")
880*d9f75844SAndroid Build Coastguard Worker           .empty());
881*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
882*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Min")
883*d9f75844SAndroid Build Coastguard Worker                   .empty());
884*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
885*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Max")
886*d9f75844SAndroid Build Coastguard Worker                   .empty());
887*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
888*d9f75844SAndroid Build Coastguard Worker       metrics::Samples(
889*d9f75844SAndroid Build Coastguard Worker           "WebRTC.Screenshare.FrameRateConstraints.60MinPlusMaxMinusOne")
890*d9f75844SAndroid Build Coastguard Worker           .empty());
891*d9f75844SAndroid Build Coastguard Worker }
892*d9f75844SAndroid Build Coastguard Worker 
TEST_F(FrameCadenceAdapterMetricsTest,RecordsMaxConstraintIfSetOnFrame)893*d9f75844SAndroid Build Coastguard Worker TEST_F(FrameCadenceAdapterMetricsTest, RecordsMaxConstraintIfSetOnFrame) {
894*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
895*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
896*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller_.GetClock());
897*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
898*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
899*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
900*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(
901*d9f75844SAndroid Build Coastguard Worker       VideoTrackSourceConstraints{absl::nullopt, 2.0});
902*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
903*d9f75844SAndroid Build Coastguard Worker   DepleteTaskQueues();
904*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
905*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Exists"),
906*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(false, 1)));
907*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
908*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Value")
909*d9f75844SAndroid Build Coastguard Worker           .empty());
910*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
911*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Exists"),
912*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(true, 1)));
913*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
914*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Value"),
915*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(2.0, 1)));
916*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
917*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.MinUnset.Max"),
918*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(2.0, 1)));
919*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
920*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Min")
921*d9f75844SAndroid Build Coastguard Worker                   .empty());
922*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
923*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Max")
924*d9f75844SAndroid Build Coastguard Worker                   .empty());
925*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
926*d9f75844SAndroid Build Coastguard Worker       metrics::Samples(
927*d9f75844SAndroid Build Coastguard Worker           "WebRTC.Screenshare.FrameRateConstraints.60MinPlusMaxMinusOne")
928*d9f75844SAndroid Build Coastguard Worker           .empty());
929*d9f75844SAndroid Build Coastguard Worker }
930*d9f75844SAndroid Build Coastguard Worker 
TEST_F(FrameCadenceAdapterMetricsTest,RecordsMinConstraintIfSetOnFrame)931*d9f75844SAndroid Build Coastguard Worker TEST_F(FrameCadenceAdapterMetricsTest, RecordsMinConstraintIfSetOnFrame) {
932*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
933*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
934*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller_.GetClock());
935*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
936*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
937*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
938*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(
939*d9f75844SAndroid Build Coastguard Worker       VideoTrackSourceConstraints{3.0, absl::nullopt});
940*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
941*d9f75844SAndroid Build Coastguard Worker   DepleteTaskQueues();
942*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
943*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Exists"),
944*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(true, 1)));
945*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
946*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Value"),
947*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(3.0, 1)));
948*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
949*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Exists"),
950*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(false, 1)));
951*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
952*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Value")
953*d9f75844SAndroid Build Coastguard Worker           .empty());
954*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
955*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.MinUnset.Max")
956*d9f75844SAndroid Build Coastguard Worker           .empty());
957*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
958*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Min")
959*d9f75844SAndroid Build Coastguard Worker                   .empty());
960*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
961*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Max")
962*d9f75844SAndroid Build Coastguard Worker                   .empty());
963*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
964*d9f75844SAndroid Build Coastguard Worker       metrics::Samples(
965*d9f75844SAndroid Build Coastguard Worker           "WebRTC.Screenshare.FrameRateConstraints.60MinPlusMaxMinusOne")
966*d9f75844SAndroid Build Coastguard Worker           .empty());
967*d9f75844SAndroid Build Coastguard Worker }
968*d9f75844SAndroid Build Coastguard Worker 
TEST_F(FrameCadenceAdapterMetricsTest,RecordsMinGtMaxConstraintIfSetOnFrame)969*d9f75844SAndroid Build Coastguard Worker TEST_F(FrameCadenceAdapterMetricsTest, RecordsMinGtMaxConstraintIfSetOnFrame) {
970*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
971*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
972*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller_.GetClock());
973*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
974*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
975*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
976*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{5.0, 4.0});
977*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
978*d9f75844SAndroid Build Coastguard Worker   DepleteTaskQueues();
979*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
980*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Exists"),
981*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(true, 1)));
982*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
983*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Min.Value"),
984*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(5.0, 1)));
985*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
986*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Exists"),
987*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(true, 1)));
988*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
989*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.Max.Value"),
990*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(4.0, 1)));
991*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(
992*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.FrameRateConstraints.MinUnset.Max")
993*d9f75844SAndroid Build Coastguard Worker           .empty());
994*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
995*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Min")
996*d9f75844SAndroid Build Coastguard Worker                   .empty());
997*d9f75844SAndroid Build Coastguard Worker   EXPECT_TRUE(metrics::Samples(
998*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Max")
999*d9f75844SAndroid Build Coastguard Worker                   .empty());
1000*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
1001*d9f75844SAndroid Build Coastguard Worker       metrics::Samples(
1002*d9f75844SAndroid Build Coastguard Worker           "WebRTC.Screenshare.FrameRateConstraints.60MinPlusMaxMinusOne"),
1003*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(60 * 5.0 + 4.0 - 1, 1)));
1004*d9f75844SAndroid Build Coastguard Worker }
1005*d9f75844SAndroid Build Coastguard Worker 
TEST_F(FrameCadenceAdapterMetricsTest,RecordsMinLtMaxConstraintIfSetOnFrame)1006*d9f75844SAndroid Build Coastguard Worker TEST_F(FrameCadenceAdapterMetricsTest, RecordsMinLtMaxConstraintIfSetOnFrame) {
1007*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
1008*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
1009*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller_.GetClock());
1010*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
1011*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
1012*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
1013*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{4.0, 5.0});
1014*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
1015*d9f75844SAndroid Build Coastguard Worker   DepleteTaskQueues();
1016*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(metrics::Samples(
1017*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Min"),
1018*d9f75844SAndroid Build Coastguard Worker               ElementsAre(Pair(4.0, 1)));
1019*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(metrics::Samples(
1020*d9f75844SAndroid Build Coastguard Worker                   "WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Max"),
1021*d9f75844SAndroid Build Coastguard Worker               ElementsAre(Pair(5.0, 1)));
1022*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
1023*d9f75844SAndroid Build Coastguard Worker       metrics::Samples(
1024*d9f75844SAndroid Build Coastguard Worker           "WebRTC.Screenshare.FrameRateConstraints.60MinPlusMaxMinusOne"),
1025*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(60 * 4.0 + 5.0 - 1, 1)));
1026*d9f75844SAndroid Build Coastguard Worker }
1027*d9f75844SAndroid Build Coastguard Worker 
TEST_F(FrameCadenceAdapterMetricsTest,RecordsTimeUntilFirstFrame)1028*d9f75844SAndroid Build Coastguard Worker TEST_F(FrameCadenceAdapterMetricsTest, RecordsTimeUntilFirstFrame) {
1029*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
1030*d9f75844SAndroid Build Coastguard Worker   test::ScopedKeyValueConfig no_field_trials;
1031*d9f75844SAndroid Build Coastguard Worker   auto adapter = CreateAdapter(no_field_trials, time_controller_.GetClock());
1032*d9f75844SAndroid Build Coastguard Worker   adapter->Initialize(&callback);
1033*d9f75844SAndroid Build Coastguard Worker   adapter->SetZeroHertzModeEnabled(
1034*d9f75844SAndroid Build Coastguard Worker       FrameCadenceAdapterInterface::ZeroHertzModeParams{});
1035*d9f75844SAndroid Build Coastguard Worker   adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 5.0});
1036*d9f75844SAndroid Build Coastguard Worker   time_controller_.AdvanceTime(TimeDelta::Millis(666));
1037*d9f75844SAndroid Build Coastguard Worker   adapter->OnFrame(CreateFrame());
1038*d9f75844SAndroid Build Coastguard Worker   DepleteTaskQueues();
1039*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(
1040*d9f75844SAndroid Build Coastguard Worker       metrics::Samples("WebRTC.Screenshare.ZeroHz.TimeUntilFirstFrameMs"),
1041*d9f75844SAndroid Build Coastguard Worker       ElementsAre(Pair(666, 1)));
1042*d9f75844SAndroid Build Coastguard Worker }
1043*d9f75844SAndroid Build Coastguard Worker 
TEST(FrameCadenceAdapterRealTimeTest,TimestampsDoNotDrift)1044*d9f75844SAndroid Build Coastguard Worker TEST(FrameCadenceAdapterRealTimeTest, TimestampsDoNotDrift) {
1045*d9f75844SAndroid Build Coastguard Worker   // This regression test must be performed in realtime because of limitations
1046*d9f75844SAndroid Build Coastguard Worker   // in GlobalSimulatedTimeController.
1047*d9f75844SAndroid Build Coastguard Worker   //
1048*d9f75844SAndroid Build Coastguard Worker   // We sleep for a long while in OnFrame when a repeat was scheduled which
1049*d9f75844SAndroid Build Coastguard Worker   // should reflect in accordingly increased ntp_time_ms() and timestamp_us() in
1050*d9f75844SAndroid Build Coastguard Worker   // the repeated frames.
1051*d9f75844SAndroid Build Coastguard Worker   auto factory = CreateDefaultTaskQueueFactory();
1052*d9f75844SAndroid Build Coastguard Worker   auto queue =
1053*d9f75844SAndroid Build Coastguard Worker       factory->CreateTaskQueue("test", TaskQueueFactory::Priority::NORMAL);
1054*d9f75844SAndroid Build Coastguard Worker   ZeroHertzFieldTrialEnabler enabler;
1055*d9f75844SAndroid Build Coastguard Worker   MockCallback callback;
1056*d9f75844SAndroid Build Coastguard Worker   Clock* clock = Clock::GetRealTimeClock();
1057*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<FrameCadenceAdapterInterface> adapter;
1058*d9f75844SAndroid Build Coastguard Worker   int frame_counter = 0;
1059*d9f75844SAndroid Build Coastguard Worker   int64_t original_ntp_time_ms;
1060*d9f75844SAndroid Build Coastguard Worker   int64_t original_timestamp_us;
1061*d9f75844SAndroid Build Coastguard Worker   rtc::Event event;
1062*d9f75844SAndroid Build Coastguard Worker   queue->PostTask([&] {
1063*d9f75844SAndroid Build Coastguard Worker     adapter = CreateAdapter(enabler, clock);
1064*d9f75844SAndroid Build Coastguard Worker     adapter->Initialize(&callback);
1065*d9f75844SAndroid Build Coastguard Worker     adapter->SetZeroHertzModeEnabled(
1066*d9f75844SAndroid Build Coastguard Worker         FrameCadenceAdapterInterface::ZeroHertzModeParams{});
1067*d9f75844SAndroid Build Coastguard Worker     adapter->OnConstraintsChanged(VideoTrackSourceConstraints{0, 30});
1068*d9f75844SAndroid Build Coastguard Worker     auto frame = CreateFrame();
1069*d9f75844SAndroid Build Coastguard Worker     original_ntp_time_ms = clock->CurrentNtpInMilliseconds();
1070*d9f75844SAndroid Build Coastguard Worker     frame.set_ntp_time_ms(original_ntp_time_ms);
1071*d9f75844SAndroid Build Coastguard Worker     original_timestamp_us = clock->CurrentTime().us();
1072*d9f75844SAndroid Build Coastguard Worker     frame.set_timestamp_us(original_timestamp_us);
1073*d9f75844SAndroid Build Coastguard Worker     constexpr int kSleepMs = rtc::kNumMillisecsPerSec / 2;
1074*d9f75844SAndroid Build Coastguard Worker     EXPECT_CALL(callback, OnFrame)
1075*d9f75844SAndroid Build Coastguard Worker         .WillRepeatedly(
1076*d9f75844SAndroid Build Coastguard Worker             Invoke([&](Timestamp, int, const VideoFrame& incoming_frame) {
1077*d9f75844SAndroid Build Coastguard Worker               ++frame_counter;
1078*d9f75844SAndroid Build Coastguard Worker               // Avoid the first OnFrame and sleep on the second.
1079*d9f75844SAndroid Build Coastguard Worker               if (frame_counter == 2) {
1080*d9f75844SAndroid Build Coastguard Worker                 SleepMs(kSleepMs);
1081*d9f75844SAndroid Build Coastguard Worker               } else if (frame_counter == 3) {
1082*d9f75844SAndroid Build Coastguard Worker                 EXPECT_GE(incoming_frame.ntp_time_ms(),
1083*d9f75844SAndroid Build Coastguard Worker                           original_ntp_time_ms + kSleepMs);
1084*d9f75844SAndroid Build Coastguard Worker                 EXPECT_GE(incoming_frame.timestamp_us(),
1085*d9f75844SAndroid Build Coastguard Worker                           original_timestamp_us + kSleepMs);
1086*d9f75844SAndroid Build Coastguard Worker                 event.Set();
1087*d9f75844SAndroid Build Coastguard Worker               }
1088*d9f75844SAndroid Build Coastguard Worker             }));
1089*d9f75844SAndroid Build Coastguard Worker     adapter->OnFrame(frame);
1090*d9f75844SAndroid Build Coastguard Worker   });
1091*d9f75844SAndroid Build Coastguard Worker   event.Wait(rtc::Event::kForever);
1092*d9f75844SAndroid Build Coastguard Worker   rtc::Event finalized;
1093*d9f75844SAndroid Build Coastguard Worker   queue->PostTask([&] {
1094*d9f75844SAndroid Build Coastguard Worker     adapter = nullptr;
1095*d9f75844SAndroid Build Coastguard Worker     finalized.Set();
1096*d9f75844SAndroid Build Coastguard Worker   });
1097*d9f75844SAndroid Build Coastguard Worker   finalized.Wait(rtc::Event::kForever);
1098*d9f75844SAndroid Build Coastguard Worker }
1099*d9f75844SAndroid Build Coastguard Worker 
1100*d9f75844SAndroid Build Coastguard Worker }  // namespace
1101*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
1102