blob: db9540f0ce374fb72f7a6e02dd5f387c6b1ef0e3 [file] [log] [blame]
Evan Shrubsole6cd6d8e2022-02-11 14:30:261/*
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 "api/metronome/test/fake_metronome.h"
19#include "api/units/time_delta.h"
20#include "test/gmock.h"
21#include "test/gtest.h"
22#include "test/run_loop.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
27using ::testing::_;
28using ::testing::Eq;
29
30namespace webrtc {
31
32class DecodeSynchronizerTest : public ::testing::Test {
33 public:
34 static constexpr TimeDelta kTickPeriod = TimeDelta::Millis(33);
35
36 DecodeSynchronizerTest()
37 : time_controller_(Timestamp::Millis(1337)),
38 clock_(time_controller_.GetClock()),
39 metronome_(kTickPeriod),
40 decode_synchronizer_(clock_, &metronome_, run_loop_.task_queue()) {}
41
42 protected:
43 GlobalSimulatedTimeController time_controller_;
44 Clock* clock_;
45 test::RunLoop run_loop_;
46 test::ForcedTickMetronome metronome_;
47 DecodeSynchronizer decode_synchronizer_;
48};
49
50TEST_F(DecodeSynchronizerTest, AllFramesReadyBeforeNextTickDecoded) {
51 ::testing::MockFunction<void(uint32_t, Timestamp)> mock_callback1;
52 auto scheduler1 = decode_synchronizer_.CreateSynchronizedFrameScheduler();
53
54 testing::MockFunction<void(unsigned int, Timestamp)> mock_callback2;
55 auto scheduler2 = decode_synchronizer_.CreateSynchronizedFrameScheduler();
56
57 {
58 uint32_t frame_rtp = 90000;
59 FrameDecodeTiming::FrameSchedule frame_sched{
60 .latest_decode_time =
61 clock_->CurrentTime() + kTickPeriod - TimeDelta::Millis(3),
62 .render_time = clock_->CurrentTime() + TimeDelta::Millis(60)};
63 scheduler1->ScheduleFrame(frame_rtp, frame_sched,
64 mock_callback1.AsStdFunction());
65 EXPECT_CALL(mock_callback1,
66 Call(Eq(frame_rtp), Eq(frame_sched.render_time)));
67 }
68 {
69 uint32_t frame_rtp = 123456;
70 FrameDecodeTiming::FrameSchedule frame_sched{
71 .latest_decode_time =
72 clock_->CurrentTime() + kTickPeriod - TimeDelta::Millis(2),
73 .render_time = clock_->CurrentTime() + TimeDelta::Millis(70)};
74 scheduler2->ScheduleFrame(frame_rtp, frame_sched,
75 mock_callback2.AsStdFunction());
76 EXPECT_CALL(mock_callback2,
77 Call(Eq(frame_rtp), Eq(frame_sched.render_time)));
78 }
79 metronome_.Tick();
80 run_loop_.Flush();
81
82 // Cleanup
83 scheduler1->Stop();
84 scheduler2->Stop();
85}
86
87TEST_F(DecodeSynchronizerTest, FramesNotDecodedIfDecodeTimeIsInNextInterval) {
88 ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
89 auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
90
91 uint32_t frame_rtp = 90000;
92 FrameDecodeTiming::FrameSchedule frame_sched{
93 .latest_decode_time =
94 clock_->CurrentTime() + kTickPeriod + TimeDelta::Millis(10),
95 .render_time =
96 clock_->CurrentTime() + kTickPeriod + TimeDelta::Millis(30)};
97 scheduler->ScheduleFrame(frame_rtp, frame_sched,
98 mock_callback.AsStdFunction());
99
100 metronome_.Tick();
101 run_loop_.Flush();
102 // No decodes should have happened in this tick.
103 ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
104
105 // Decode should happen on next tick.
106 EXPECT_CALL(mock_callback, Call(Eq(frame_rtp), Eq(frame_sched.render_time)));
107 time_controller_.AdvanceTime(kTickPeriod);
108 metronome_.Tick();
109 run_loop_.Flush();
110
111 // Cleanup
112 scheduler->Stop();
113}
114
115TEST_F(DecodeSynchronizerTest, FrameDecodedOnce) {
116 ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
117 auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
118
119 uint32_t frame_rtp = 90000;
120 FrameDecodeTiming::FrameSchedule frame_sched{
121 .latest_decode_time = clock_->CurrentTime() + TimeDelta::Millis(30),
122 .render_time = clock_->CurrentTime() + TimeDelta::Millis(60)};
123 scheduler->ScheduleFrame(frame_rtp, frame_sched,
124 mock_callback.AsStdFunction());
125 EXPECT_CALL(mock_callback, Call(_, _)).Times(1);
126 metronome_.Tick();
127 run_loop_.Flush();
128 ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
129
130 // Trigger tick again. No frame should be decoded now.
131 time_controller_.AdvanceTime(kTickPeriod);
132 metronome_.Tick();
133 run_loop_.Flush();
134
135 // Cleanup
136 scheduler->Stop();
137}
138
139TEST_F(DecodeSynchronizerTest, FrameWithDecodeTimeInPastDecodedImmediately) {
140 ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
141 auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
142
143 uint32_t frame_rtp = 90000;
144 FrameDecodeTiming::FrameSchedule frame_sched{
145 .latest_decode_time = clock_->CurrentTime() - TimeDelta::Millis(5),
146 .render_time = clock_->CurrentTime() + TimeDelta::Millis(30)};
147 EXPECT_CALL(mock_callback, Call(Eq(90000u), _)).Times(1);
148 scheduler->ScheduleFrame(frame_rtp, frame_sched,
149 mock_callback.AsStdFunction());
150 // Verify the callback was invoked already.
151 ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
152
153 metronome_.Tick();
154 run_loop_.Flush();
155
156 // Cleanup
157 scheduler->Stop();
158}
159
160TEST_F(DecodeSynchronizerTest,
161 FrameWithDecodeTimeFarBeforeNextTickDecodedImmediately) {
162 ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
163 auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
164
165 // Frame which would be behind by more than kMaxAllowedFrameDelay after
166 // the next tick.
167 FrameDecodeTiming::FrameSchedule frame_sched{
168 .latest_decode_time = clock_->CurrentTime() + kTickPeriod -
169 FrameDecodeTiming::kMaxAllowedFrameDelay -
170 TimeDelta::Millis(1),
171 .render_time = clock_->CurrentTime() + TimeDelta::Millis(30)};
172 EXPECT_CALL(mock_callback, Call(Eq(90000u), _)).Times(1);
173 scheduler->ScheduleFrame(90000, frame_sched, mock_callback.AsStdFunction());
174 // Verify the callback was invoked already.
175 ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
176
177 time_controller_.AdvanceTime(kTickPeriod);
178 metronome_.Tick();
179 run_loop_.Flush();
180
181 // A frame that would be behind by exactly kMaxAllowedFrameDelay after next
182 // tick should decode at the next tick.
183 FrameDecodeTiming::FrameSchedule queued_frame{
184 .latest_decode_time = clock_->CurrentTime() + kTickPeriod -
185 FrameDecodeTiming::kMaxAllowedFrameDelay,
186 .render_time = clock_->CurrentTime() + TimeDelta::Millis(30)};
187 scheduler->ScheduleFrame(180000, queued_frame, mock_callback.AsStdFunction());
188 // Verify the callback was invoked already.
189 ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
190
191 EXPECT_CALL(mock_callback, Call(Eq(180000u), _)).Times(1);
192 time_controller_.AdvanceTime(kTickPeriod);
193 metronome_.Tick();
194 run_loop_.Flush();
195
196 // Cleanup
197 scheduler->Stop();
198}
199
200TEST_F(DecodeSynchronizerTest, FramesNotReleasedAfterStop) {
201 ::testing::MockFunction<void(unsigned int, Timestamp)> mock_callback;
202 auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
203
204 uint32_t frame_rtp = 90000;
205 FrameDecodeTiming::FrameSchedule frame_sched{
206 .latest_decode_time = clock_->CurrentTime() + TimeDelta::Millis(30),
207 .render_time = clock_->CurrentTime() + TimeDelta::Millis(60)};
208 scheduler->ScheduleFrame(frame_rtp, frame_sched,
209 mock_callback.AsStdFunction());
210 // Cleanup
211 scheduler->Stop();
212
213 // No callback should occur on this tick since Stop() was called before.
214 metronome_.Tick();
215 run_loop_.Flush();
216}
217
218TEST_F(DecodeSynchronizerTest, MetronomeNotListenedWhenNoStreamsAreActive) {
219 EXPECT_EQ(0u, metronome_.NumListeners());
220
221 auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
222 EXPECT_EQ(1u, metronome_.NumListeners());
223 auto scheduler2 = decode_synchronizer_.CreateSynchronizedFrameScheduler();
224 EXPECT_EQ(1u, metronome_.NumListeners());
225
226 scheduler->Stop();
227 EXPECT_EQ(1u, metronome_.NumListeners());
228 scheduler2->Stop();
229 EXPECT_EQ(0u, metronome_.NumListeners());
230}
231
232} // namespace webrtc