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