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