blob: 7a0d833812b0455a42f340ebb692e95998fe4428 [file] [log] [blame]
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +01001/*
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
Markus Handellbe400e42022-11-08 12:14:23 +010018#include "absl/functional/any_invocable.h"
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +010019#include "api/metronome/test/fake_metronome.h"
20#include "api/units/time_delta.h"
21#include "test/gmock.h"
22#include "test/gtest.h"
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +010023#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;
Markus Handellbe400e42022-11-08 12:14:23 +010029using ::testing::Invoke;
30using ::testing::Return;
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +010031
32namespace webrtc {
33
Markus Handellbe400e42022-11-08 12:14:23 +010034class 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
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +010043class DecodeSynchronizerTest : public ::testing::Test {
44 public:
45 static constexpr TimeDelta kTickPeriod = TimeDelta::Millis(33);
46
47 DecodeSynchronizerTest()
48 : time_controller_(Timestamp::Millis(1337)),
49 clock_(time_controller_.GetClock()),
50 metronome_(kTickPeriod),
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +000051 decode_synchronizer_(clock_,
52 &metronome_,
53 time_controller_.GetMainThread()) {}
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +010054
55 protected:
56 GlobalSimulatedTimeController time_controller_;
57 Clock* clock_;
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +010058 test::ForcedTickMetronome metronome_;
59 DecodeSynchronizer decode_synchronizer_;
60};
61
62TEST_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();
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +000092 time_controller_.AdvanceTime(TimeDelta::Zero());
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +010093
94 // Cleanup
95 scheduler1->Stop();
96 scheduler2->Stop();
97}
98
99TEST_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();
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +0000113 time_controller_.AdvanceTime(TimeDelta::Zero());
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100114 // 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();
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +0000121 time_controller_.AdvanceTime(TimeDelta::Zero());
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100122
123 // Cleanup
124 scheduler->Stop();
125}
126
127TEST_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();
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +0000139 time_controller_.AdvanceTime(TimeDelta::Zero());
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100140 ::testing::Mock::VerifyAndClearExpectations(&mock_callback);
141
142 // Trigger tick again. No frame should be decoded now.
143 time_controller_.AdvanceTime(kTickPeriod);
144 metronome_.Tick();
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +0000145 time_controller_.AdvanceTime(TimeDelta::Zero());
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100146
147 // Cleanup
148 scheduler->Stop();
149}
150
151TEST_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();
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +0000166 time_controller_.AdvanceTime(TimeDelta::Zero());
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100167
168 // Cleanup
169 scheduler->Stop();
170}
171
172TEST_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();
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +0000191 time_controller_.AdvanceTime(TimeDelta::Zero());
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100192
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();
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +0000206 time_controller_.AdvanceTime(TimeDelta::Zero());
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100207
208 // Cleanup
209 scheduler->Stop();
210}
211
212TEST_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();
Evan Shrubsole7cbd8de2022-08-16 08:08:53 +0000227 time_controller_.AdvanceTime(TimeDelta::Zero());
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100228}
229
Markus Handellbe400e42022-11-08 12:14:23 +0100230TEST(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 }));
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100243 auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100244 auto scheduler2 = decode_synchronizer_.CreateSynchronizedFrameScheduler();
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100245 scheduler->Stop();
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100246 scheduler2->Stop();
Markus Handellbe400e42022-11-08 12:14:23 +0100247 time_controller.AdvanceTime(TimeDelta::Seconds(1));
248 ASSERT_TRUE(callback);
249 (std::move)(callback)();
Evan Shrubsole6cd6d8e2022-02-11 15:30:26 +0100250}
251
252} // namespace webrtc