1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2022 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/decode_synchronizer.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <stddef.h>
14*d9f75844SAndroid Build Coastguard Worker
15*d9f75844SAndroid Build Coastguard Worker #include <memory>
16*d9f75844SAndroid Build Coastguard Worker #include <utility>
17*d9f75844SAndroid Build Coastguard Worker
18*d9f75844SAndroid Build Coastguard Worker #include "absl/functional/any_invocable.h"
19*d9f75844SAndroid Build Coastguard Worker #include "api/metronome/test/fake_metronome.h"
20*d9f75844SAndroid Build Coastguard Worker #include "api/units/time_delta.h"
21*d9f75844SAndroid Build Coastguard Worker #include "test/gmock.h"
22*d9f75844SAndroid Build Coastguard Worker #include "test/gtest.h"
23*d9f75844SAndroid Build Coastguard Worker #include "test/time_controller/simulated_time_controller.h"
24*d9f75844SAndroid Build Coastguard Worker #include "video/frame_decode_scheduler.h"
25*d9f75844SAndroid Build Coastguard Worker #include "video/frame_decode_timing.h"
26*d9f75844SAndroid Build Coastguard Worker
27*d9f75844SAndroid Build Coastguard Worker using ::testing::_;
28*d9f75844SAndroid Build Coastguard Worker using ::testing::Eq;
29*d9f75844SAndroid Build Coastguard Worker using ::testing::Invoke;
30*d9f75844SAndroid Build Coastguard Worker using ::testing::Return;
31*d9f75844SAndroid Build Coastguard Worker
32*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
33*d9f75844SAndroid Build Coastguard Worker
34*d9f75844SAndroid Build Coastguard Worker class MockMetronome : public Metronome {
35*d9f75844SAndroid Build Coastguard Worker public:
36*d9f75844SAndroid Build Coastguard Worker MOCK_METHOD(void,
37*d9f75844SAndroid Build Coastguard Worker RequestCallOnNextTick,
38*d9f75844SAndroid Build Coastguard Worker (absl::AnyInvocable<void() &&> callback),
39*d9f75844SAndroid Build Coastguard Worker (override));
40*d9f75844SAndroid Build Coastguard Worker MOCK_METHOD(TimeDelta, TickPeriod, (), (const override));
41*d9f75844SAndroid Build Coastguard Worker };
42*d9f75844SAndroid Build Coastguard Worker
43*d9f75844SAndroid Build Coastguard Worker class DecodeSynchronizerTest : public ::testing::Test {
44*d9f75844SAndroid Build Coastguard Worker public:
45*d9f75844SAndroid Build Coastguard Worker static constexpr TimeDelta kTickPeriod = TimeDelta::Millis(33);
46*d9f75844SAndroid Build Coastguard Worker
DecodeSynchronizerTest()47*d9f75844SAndroid Build Coastguard Worker DecodeSynchronizerTest()
48*d9f75844SAndroid Build Coastguard Worker : time_controller_(Timestamp::Millis(1337)),
49*d9f75844SAndroid Build Coastguard Worker clock_(time_controller_.GetClock()),
50*d9f75844SAndroid Build Coastguard Worker metronome_(kTickPeriod),
51*d9f75844SAndroid Build Coastguard Worker decode_synchronizer_(clock_,
52*d9f75844SAndroid Build Coastguard Worker &metronome_,
53*d9f75844SAndroid Build Coastguard Worker time_controller_.GetMainThread()) {}
54*d9f75844SAndroid Build Coastguard Worker
55*d9f75844SAndroid Build Coastguard Worker protected:
56*d9f75844SAndroid Build Coastguard Worker GlobalSimulatedTimeController time_controller_;
57*d9f75844SAndroid Build Coastguard Worker Clock* clock_;
58*d9f75844SAndroid Build Coastguard Worker test::ForcedTickMetronome metronome_;
59*d9f75844SAndroid Build Coastguard Worker DecodeSynchronizer decode_synchronizer_;
60*d9f75844SAndroid Build Coastguard Worker };
61*d9f75844SAndroid Build Coastguard Worker
TEST_F(DecodeSynchronizerTest,AllFramesReadyBeforeNextTickDecoded)62*d9f75844SAndroid Build Coastguard Worker TEST_F(DecodeSynchronizerTest, AllFramesReadyBeforeNextTickDecoded) {
63*d9f75844SAndroid Build Coastguard Worker ::testing::MockFunction<void(uint32_t, Timestamp)> mock_callback1;
64*d9f75844SAndroid Build Coastguard Worker auto scheduler1 = decode_synchronizer_.CreateSynchronizedFrameScheduler();
65*d9f75844SAndroid Build Coastguard Worker
66*d9f75844SAndroid Build Coastguard Worker testing::MockFunction<void(unsigned int, Timestamp)> mock_callback2;
67*d9f75844SAndroid Build Coastguard Worker auto scheduler2 = decode_synchronizer_.CreateSynchronizedFrameScheduler();
68*d9f75844SAndroid Build Coastguard Worker
69*d9f75844SAndroid Build Coastguard Worker {
70*d9f75844SAndroid Build Coastguard Worker uint32_t frame_rtp = 90000;
71*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::FrameSchedule frame_sched{
72*d9f75844SAndroid Build Coastguard Worker .latest_decode_time =
73*d9f75844SAndroid Build Coastguard Worker clock_->CurrentTime() + kTickPeriod - TimeDelta::Millis(3),
74*d9f75844SAndroid Build Coastguard Worker .render_time = clock_->CurrentTime() + TimeDelta::Millis(60)};
75*d9f75844SAndroid Build Coastguard Worker scheduler1->ScheduleFrame(frame_rtp, frame_sched,
76*d9f75844SAndroid Build Coastguard Worker mock_callback1.AsStdFunction());
77*d9f75844SAndroid Build Coastguard Worker EXPECT_CALL(mock_callback1,
78*d9f75844SAndroid Build Coastguard Worker Call(Eq(frame_rtp), Eq(frame_sched.render_time)));
79*d9f75844SAndroid Build Coastguard Worker }
80*d9f75844SAndroid Build Coastguard Worker {
81*d9f75844SAndroid Build Coastguard Worker uint32_t frame_rtp = 123456;
82*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::FrameSchedule frame_sched{
83*d9f75844SAndroid Build Coastguard Worker .latest_decode_time =
84*d9f75844SAndroid Build Coastguard Worker clock_->CurrentTime() + kTickPeriod - TimeDelta::Millis(2),
85*d9f75844SAndroid Build Coastguard Worker .render_time = clock_->CurrentTime() + TimeDelta::Millis(70)};
86*d9f75844SAndroid Build Coastguard Worker scheduler2->ScheduleFrame(frame_rtp, frame_sched,
87*d9f75844SAndroid Build Coastguard Worker mock_callback2.AsStdFunction());
88*d9f75844SAndroid Build Coastguard Worker EXPECT_CALL(mock_callback2,
89*d9f75844SAndroid Build Coastguard Worker Call(Eq(frame_rtp), Eq(frame_sched.render_time)));
90*d9f75844SAndroid Build Coastguard Worker }
91*d9f75844SAndroid Build Coastguard Worker metronome_.Tick();
92*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(TimeDelta::Zero());
93*d9f75844SAndroid Build Coastguard Worker
94*d9f75844SAndroid Build Coastguard Worker // Cleanup
95*d9f75844SAndroid Build Coastguard Worker scheduler1->Stop();
96*d9f75844SAndroid Build Coastguard Worker scheduler2->Stop();
97*d9f75844SAndroid Build Coastguard Worker }
98*d9f75844SAndroid Build Coastguard Worker
TEST_F(DecodeSynchronizerTest,FramesNotDecodedIfDecodeTimeIsInNextInterval)99*d9f75844SAndroid Build Coastguard Worker TEST_F(DecodeSynchronizerTest, FramesNotDecodedIfDecodeTimeIsInNextInterval) {
100*d9f75844SAndroid Build Coastguard Worker ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
101*d9f75844SAndroid Build Coastguard Worker auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
102*d9f75844SAndroid Build Coastguard Worker
103*d9f75844SAndroid Build Coastguard Worker uint32_t frame_rtp = 90000;
104*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::FrameSchedule frame_sched{
105*d9f75844SAndroid Build Coastguard Worker .latest_decode_time =
106*d9f75844SAndroid Build Coastguard Worker clock_->CurrentTime() + kTickPeriod + TimeDelta::Millis(10),
107*d9f75844SAndroid Build Coastguard Worker .render_time =
108*d9f75844SAndroid Build Coastguard Worker clock_->CurrentTime() + kTickPeriod + TimeDelta::Millis(30)};
109*d9f75844SAndroid Build Coastguard Worker scheduler->ScheduleFrame(frame_rtp, frame_sched,
110*d9f75844SAndroid Build Coastguard Worker mock_callback.AsStdFunction());
111*d9f75844SAndroid Build Coastguard Worker
112*d9f75844SAndroid Build Coastguard Worker metronome_.Tick();
113*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(TimeDelta::Zero());
114*d9f75844SAndroid Build Coastguard Worker // No decodes should have happened in this tick.
115*d9f75844SAndroid Build Coastguard Worker ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
116*d9f75844SAndroid Build Coastguard Worker
117*d9f75844SAndroid Build Coastguard Worker // Decode should happen on next tick.
118*d9f75844SAndroid Build Coastguard Worker EXPECT_CALL(mock_callback, Call(Eq(frame_rtp), Eq(frame_sched.render_time)));
119*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(kTickPeriod);
120*d9f75844SAndroid Build Coastguard Worker metronome_.Tick();
121*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(TimeDelta::Zero());
122*d9f75844SAndroid Build Coastguard Worker
123*d9f75844SAndroid Build Coastguard Worker // Cleanup
124*d9f75844SAndroid Build Coastguard Worker scheduler->Stop();
125*d9f75844SAndroid Build Coastguard Worker }
126*d9f75844SAndroid Build Coastguard Worker
TEST_F(DecodeSynchronizerTest,FrameDecodedOnce)127*d9f75844SAndroid Build Coastguard Worker TEST_F(DecodeSynchronizerTest, FrameDecodedOnce) {
128*d9f75844SAndroid Build Coastguard Worker ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
129*d9f75844SAndroid Build Coastguard Worker auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
130*d9f75844SAndroid Build Coastguard Worker
131*d9f75844SAndroid Build Coastguard Worker uint32_t frame_rtp = 90000;
132*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::FrameSchedule frame_sched{
133*d9f75844SAndroid Build Coastguard Worker .latest_decode_time = clock_->CurrentTime() + TimeDelta::Millis(30),
134*d9f75844SAndroid Build Coastguard Worker .render_time = clock_->CurrentTime() + TimeDelta::Millis(60)};
135*d9f75844SAndroid Build Coastguard Worker scheduler->ScheduleFrame(frame_rtp, frame_sched,
136*d9f75844SAndroid Build Coastguard Worker mock_callback.AsStdFunction());
137*d9f75844SAndroid Build Coastguard Worker EXPECT_CALL(mock_callback, Call(_, _)).Times(1);
138*d9f75844SAndroid Build Coastguard Worker metronome_.Tick();
139*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(TimeDelta::Zero());
140*d9f75844SAndroid Build Coastguard Worker ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
141*d9f75844SAndroid Build Coastguard Worker
142*d9f75844SAndroid Build Coastguard Worker // Trigger tick again. No frame should be decoded now.
143*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(kTickPeriod);
144*d9f75844SAndroid Build Coastguard Worker metronome_.Tick();
145*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(TimeDelta::Zero());
146*d9f75844SAndroid Build Coastguard Worker
147*d9f75844SAndroid Build Coastguard Worker // Cleanup
148*d9f75844SAndroid Build Coastguard Worker scheduler->Stop();
149*d9f75844SAndroid Build Coastguard Worker }
150*d9f75844SAndroid Build Coastguard Worker
TEST_F(DecodeSynchronizerTest,FrameWithDecodeTimeInPastDecodedImmediately)151*d9f75844SAndroid Build Coastguard Worker TEST_F(DecodeSynchronizerTest, FrameWithDecodeTimeInPastDecodedImmediately) {
152*d9f75844SAndroid Build Coastguard Worker ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
153*d9f75844SAndroid Build Coastguard Worker auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
154*d9f75844SAndroid Build Coastguard Worker
155*d9f75844SAndroid Build Coastguard Worker uint32_t frame_rtp = 90000;
156*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::FrameSchedule frame_sched{
157*d9f75844SAndroid Build Coastguard Worker .latest_decode_time = clock_->CurrentTime() - TimeDelta::Millis(5),
158*d9f75844SAndroid Build Coastguard Worker .render_time = clock_->CurrentTime() + TimeDelta::Millis(30)};
159*d9f75844SAndroid Build Coastguard Worker EXPECT_CALL(mock_callback, Call(Eq(90000u), _)).Times(1);
160*d9f75844SAndroid Build Coastguard Worker scheduler->ScheduleFrame(frame_rtp, frame_sched,
161*d9f75844SAndroid Build Coastguard Worker mock_callback.AsStdFunction());
162*d9f75844SAndroid Build Coastguard Worker // Verify the callback was invoked already.
163*d9f75844SAndroid Build Coastguard Worker ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
164*d9f75844SAndroid Build Coastguard Worker
165*d9f75844SAndroid Build Coastguard Worker metronome_.Tick();
166*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(TimeDelta::Zero());
167*d9f75844SAndroid Build Coastguard Worker
168*d9f75844SAndroid Build Coastguard Worker // Cleanup
169*d9f75844SAndroid Build Coastguard Worker scheduler->Stop();
170*d9f75844SAndroid Build Coastguard Worker }
171*d9f75844SAndroid Build Coastguard Worker
TEST_F(DecodeSynchronizerTest,FrameWithDecodeTimeFarBeforeNextTickDecodedImmediately)172*d9f75844SAndroid Build Coastguard Worker TEST_F(DecodeSynchronizerTest,
173*d9f75844SAndroid Build Coastguard Worker FrameWithDecodeTimeFarBeforeNextTickDecodedImmediately) {
174*d9f75844SAndroid Build Coastguard Worker ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
175*d9f75844SAndroid Build Coastguard Worker auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
176*d9f75844SAndroid Build Coastguard Worker
177*d9f75844SAndroid Build Coastguard Worker // Frame which would be behind by more than kMaxAllowedFrameDelay after
178*d9f75844SAndroid Build Coastguard Worker // the next tick.
179*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::FrameSchedule frame_sched{
180*d9f75844SAndroid Build Coastguard Worker .latest_decode_time = clock_->CurrentTime() + kTickPeriod -
181*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::kMaxAllowedFrameDelay -
182*d9f75844SAndroid Build Coastguard Worker TimeDelta::Millis(1),
183*d9f75844SAndroid Build Coastguard Worker .render_time = clock_->CurrentTime() + TimeDelta::Millis(30)};
184*d9f75844SAndroid Build Coastguard Worker EXPECT_CALL(mock_callback, Call(Eq(90000u), _)).Times(1);
185*d9f75844SAndroid Build Coastguard Worker scheduler->ScheduleFrame(90000, frame_sched, mock_callback.AsStdFunction());
186*d9f75844SAndroid Build Coastguard Worker // Verify the callback was invoked already.
187*d9f75844SAndroid Build Coastguard Worker ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
188*d9f75844SAndroid Build Coastguard Worker
189*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(kTickPeriod);
190*d9f75844SAndroid Build Coastguard Worker metronome_.Tick();
191*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(TimeDelta::Zero());
192*d9f75844SAndroid Build Coastguard Worker
193*d9f75844SAndroid Build Coastguard Worker // A frame that would be behind by exactly kMaxAllowedFrameDelay after next
194*d9f75844SAndroid Build Coastguard Worker // tick should decode at the next tick.
195*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::FrameSchedule queued_frame{
196*d9f75844SAndroid Build Coastguard Worker .latest_decode_time = clock_->CurrentTime() + kTickPeriod -
197*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::kMaxAllowedFrameDelay,
198*d9f75844SAndroid Build Coastguard Worker .render_time = clock_->CurrentTime() + TimeDelta::Millis(30)};
199*d9f75844SAndroid Build Coastguard Worker scheduler->ScheduleFrame(180000, queued_frame, mock_callback.AsStdFunction());
200*d9f75844SAndroid Build Coastguard Worker // Verify the callback was invoked already.
201*d9f75844SAndroid Build Coastguard Worker ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
202*d9f75844SAndroid Build Coastguard Worker
203*d9f75844SAndroid Build Coastguard Worker EXPECT_CALL(mock_callback, Call(Eq(180000u), _)).Times(1);
204*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(kTickPeriod);
205*d9f75844SAndroid Build Coastguard Worker metronome_.Tick();
206*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(TimeDelta::Zero());
207*d9f75844SAndroid Build Coastguard Worker
208*d9f75844SAndroid Build Coastguard Worker // Cleanup
209*d9f75844SAndroid Build Coastguard Worker scheduler->Stop();
210*d9f75844SAndroid Build Coastguard Worker }
211*d9f75844SAndroid Build Coastguard Worker
TEST_F(DecodeSynchronizerTest,FramesNotReleasedAfterStop)212*d9f75844SAndroid Build Coastguard Worker TEST_F(DecodeSynchronizerTest, FramesNotReleasedAfterStop) {
213*d9f75844SAndroid Build Coastguard Worker ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
214*d9f75844SAndroid Build Coastguard Worker auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
215*d9f75844SAndroid Build Coastguard Worker
216*d9f75844SAndroid Build Coastguard Worker uint32_t frame_rtp = 90000;
217*d9f75844SAndroid Build Coastguard Worker FrameDecodeTiming::FrameSchedule frame_sched{
218*d9f75844SAndroid Build Coastguard Worker .latest_decode_time = clock_->CurrentTime() + TimeDelta::Millis(30),
219*d9f75844SAndroid Build Coastguard Worker .render_time = clock_->CurrentTime() + TimeDelta::Millis(60)};
220*d9f75844SAndroid Build Coastguard Worker scheduler->ScheduleFrame(frame_rtp, frame_sched,
221*d9f75844SAndroid Build Coastguard Worker mock_callback.AsStdFunction());
222*d9f75844SAndroid Build Coastguard Worker // Cleanup
223*d9f75844SAndroid Build Coastguard Worker scheduler->Stop();
224*d9f75844SAndroid Build Coastguard Worker
225*d9f75844SAndroid Build Coastguard Worker // No callback should occur on this tick since Stop() was called before.
226*d9f75844SAndroid Build Coastguard Worker metronome_.Tick();
227*d9f75844SAndroid Build Coastguard Worker time_controller_.AdvanceTime(TimeDelta::Zero());
228*d9f75844SAndroid Build Coastguard Worker }
229*d9f75844SAndroid Build Coastguard Worker
TEST(DecodeSynchronizerStandaloneTest,MetronomeNotListenedWhenNoStreamsAreActive)230*d9f75844SAndroid Build Coastguard Worker TEST(DecodeSynchronizerStandaloneTest,
231*d9f75844SAndroid Build Coastguard Worker MetronomeNotListenedWhenNoStreamsAreActive) {
232*d9f75844SAndroid Build Coastguard Worker GlobalSimulatedTimeController time_controller(Timestamp::Millis(4711));
233*d9f75844SAndroid Build Coastguard Worker Clock* clock(time_controller.GetClock());
234*d9f75844SAndroid Build Coastguard Worker MockMetronome metronome;
235*d9f75844SAndroid Build Coastguard Worker ON_CALL(metronome, TickPeriod).WillByDefault(Return(TimeDelta::Seconds(1)));
236*d9f75844SAndroid Build Coastguard Worker DecodeSynchronizer decode_synchronizer_(clock, &metronome,
237*d9f75844SAndroid Build Coastguard Worker time_controller.GetMainThread());
238*d9f75844SAndroid Build Coastguard Worker absl::AnyInvocable<void() &&> callback;
239*d9f75844SAndroid Build Coastguard Worker EXPECT_CALL(metronome, RequestCallOnNextTick)
240*d9f75844SAndroid Build Coastguard Worker .WillOnce(Invoke([&callback](absl::AnyInvocable<void() &&> cb) {
241*d9f75844SAndroid Build Coastguard Worker callback = std::move(cb);
242*d9f75844SAndroid Build Coastguard Worker }));
243*d9f75844SAndroid Build Coastguard Worker auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
244*d9f75844SAndroid Build Coastguard Worker auto scheduler2 = decode_synchronizer_.CreateSynchronizedFrameScheduler();
245*d9f75844SAndroid Build Coastguard Worker scheduler->Stop();
246*d9f75844SAndroid Build Coastguard Worker scheduler2->Stop();
247*d9f75844SAndroid Build Coastguard Worker time_controller.AdvanceTime(TimeDelta::Seconds(1));
248*d9f75844SAndroid Build Coastguard Worker ASSERT_TRUE(callback);
249*d9f75844SAndroid Build Coastguard Worker (std::move)(callback)();
250*d9f75844SAndroid Build Coastguard Worker }
251*d9f75844SAndroid Build Coastguard Worker
252*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc
253