xref: /aosp_15_r20/external/webrtc/video/decode_synchronizer_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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