xref: /aosp_15_r20/external/webrtc/modules/video_coding/timing/timestamp_extrapolator_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 "modules/video_coding/timing/timestamp_extrapolator.h"
12 
13 #include <stdint.h>
14 
15 #include <limits>
16 
17 #include "absl/types/optional.h"
18 #include "api/units/frequency.h"
19 #include "api/units/time_delta.h"
20 #include "api/units/timestamp.h"
21 #include "system_wrappers/include/clock.h"
22 #include "test/gmock.h"
23 #include "test/gtest.h"
24 
25 namespace webrtc {
26 
27 using ::testing::Eq;
28 using ::testing::Optional;
29 
30 namespace {
31 
32 constexpr Frequency kRtpHz = Frequency::KiloHertz(90);
33 constexpr Frequency k25Fps = Frequency::Hertz(25);
34 constexpr TimeDelta k25FpsDelay = 1 / k25Fps;
35 
36 }  // namespace
37 
TEST(TimestampExtrapolatorTest,ExtrapolationOccursAfter2Packets)38 TEST(TimestampExtrapolatorTest, ExtrapolationOccursAfter2Packets) {
39   SimulatedClock clock(Timestamp::Millis(1337));
40   TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
41 
42   // No packets so no timestamp.
43   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(90000), Eq(absl::nullopt));
44 
45   uint32_t rtp = 90000;
46   clock.AdvanceTime(k25FpsDelay);
47   // First result is a bit confusing since it is based off the "start" time,
48   // which is arbitrary.
49   ts_extrapolator.Update(clock.CurrentTime(), rtp);
50   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
51               Optional(clock.CurrentTime()));
52 
53   rtp += kRtpHz / k25Fps;
54   clock.AdvanceTime(k25FpsDelay);
55   ts_extrapolator.Update(clock.CurrentTime(), rtp);
56   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
57               Optional(clock.CurrentTime()));
58   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp + 90000),
59               Optional(clock.CurrentTime() + TimeDelta::Seconds(1)));
60 }
61 
TEST(TimestampExtrapolatorTest,ResetsAfter10SecondPause)62 TEST(TimestampExtrapolatorTest, ResetsAfter10SecondPause) {
63   SimulatedClock clock(Timestamp::Millis(1337));
64   TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
65 
66   uint32_t rtp = 90000;
67   ts_extrapolator.Update(clock.CurrentTime(), rtp);
68   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
69               Optional(clock.CurrentTime()));
70 
71   rtp += kRtpHz / k25Fps;
72   clock.AdvanceTime(k25FpsDelay);
73   ts_extrapolator.Update(clock.CurrentTime(), rtp);
74   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
75               Optional(clock.CurrentTime()));
76 
77   rtp += 10 * kRtpHz.hertz();
78   clock.AdvanceTime(TimeDelta::Seconds(10) + TimeDelta::Micros(1));
79   ts_extrapolator.Update(clock.CurrentTime(), rtp);
80   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
81               Optional(clock.CurrentTime()));
82 }
83 
TEST(TimestampExtrapolatorTest,TimestampExtrapolatesMultipleRtpWrapArounds)84 TEST(TimestampExtrapolatorTest, TimestampExtrapolatesMultipleRtpWrapArounds) {
85   SimulatedClock clock(Timestamp::Millis(1337));
86   TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
87 
88   uint32_t rtp = std::numeric_limits<uint32_t>::max();
89   ts_extrapolator.Update(clock.CurrentTime(), rtp);
90   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
91               Optional(clock.CurrentTime()));
92 
93   // One overflow. Static cast to avoid undefined behaviour with +=.
94   rtp += static_cast<uint32_t>(kRtpHz / k25Fps);
95   clock.AdvanceTime(k25FpsDelay);
96   ts_extrapolator.Update(clock.CurrentTime(), rtp);
97   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
98               Optional(clock.CurrentTime()));
99 
100   // Assert that extrapolation works across the boundary as expected.
101   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp + 90000),
102               Optional(clock.CurrentTime() + TimeDelta::Seconds(1)));
103   // This is not quite 1s since the math always rounds up.
104   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp - 90000),
105               Optional(clock.CurrentTime() - TimeDelta::Millis(999)));
106 
107   // In order to avoid a wrap arounds reset, add a packet every 10s until we
108   // overflow twice.
109   constexpr TimeDelta kRtpOverflowDelay =
110       std::numeric_limits<uint32_t>::max() / kRtpHz;
111   const Timestamp overflow_time = clock.CurrentTime() + kRtpOverflowDelay * 2;
112 
113   while (clock.CurrentTime() < overflow_time) {
114     clock.AdvanceTime(TimeDelta::Seconds(10));
115     // Static-cast before += to avoid undefined behaviour of overflow.
116     rtp += static_cast<uint32_t>(kRtpHz * TimeDelta::Seconds(10));
117     ts_extrapolator.Update(clock.CurrentTime(), rtp);
118     EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
119                 Optional(clock.CurrentTime()));
120   }
121 }
122 
TEST(TimestampExtrapolatorTest,Slow90KHzClock)123 TEST(TimestampExtrapolatorTest, Slow90KHzClock) {
124   // This simulates a slow camera, which produces frames at 24Hz instead of
125   // 25Hz. The extrapolator should be able to resolve this with enough data.
126   SimulatedClock clock(Timestamp::Millis(1337));
127   TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
128 
129   constexpr TimeDelta k24FpsDelay = 1 / Frequency::Hertz(24);
130   uint32_t rtp = 90000;
131   ts_extrapolator.Update(clock.CurrentTime(), rtp);
132 
133   // Slow camera will increment RTP at 25 FPS rate even though its producing at
134   // 24 FPS. After 25 frames the extrapolator should settle at this rate.
135   for (int i = 0; i < 25; ++i) {
136     rtp += kRtpHz / k25Fps;
137     clock.AdvanceTime(k24FpsDelay);
138     ts_extrapolator.Update(clock.CurrentTime(), rtp);
139   }
140 
141   // The camera would normally produce 25 frames in 90K ticks, but is slow
142   // so takes 1s + k24FpsDelay for 90K ticks.
143   constexpr Frequency kSlowRtpHz = 90000 / (25 * k24FpsDelay);
144   // The extrapolator will be predicting that time at millisecond precision.
145   auto ts = ts_extrapolator.ExtrapolateLocalTime(rtp + kSlowRtpHz.hertz());
146   ASSERT_TRUE(ts.has_value());
147   EXPECT_EQ(ts->ms(), clock.TimeInMilliseconds() + 1000);
148 }
149 
TEST(TimestampExtrapolatorTest,Fast90KHzClock)150 TEST(TimestampExtrapolatorTest, Fast90KHzClock) {
151   // This simulates a fast camera, which produces frames at 26Hz instead of
152   // 25Hz. The extrapolator should be able to resolve this with enough data.
153   SimulatedClock clock(Timestamp::Millis(1337));
154   TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
155 
156   constexpr TimeDelta k26FpsDelay = 1 / Frequency::Hertz(26);
157   uint32_t rtp = 90000;
158   ts_extrapolator.Update(clock.CurrentTime(), rtp);
159 
160   // Fast camera will increment RTP at 25 FPS rate even though its producing at
161   // 26 FPS. After 25 frames the extrapolator should settle at this rate.
162   for (int i = 0; i < 25; ++i) {
163     rtp += kRtpHz / k25Fps;
164     clock.AdvanceTime(k26FpsDelay);
165     ts_extrapolator.Update(clock.CurrentTime(), rtp);
166   }
167 
168   // The camera would normally produce 25 frames in 90K ticks, but is slow
169   // so takes 1s + k24FpsDelay for 90K ticks.
170   constexpr Frequency kSlowRtpHz = 90000 / (25 * k26FpsDelay);
171   // The extrapolator will be predicting that time at millisecond precision.
172   auto ts = ts_extrapolator.ExtrapolateLocalTime(rtp + kSlowRtpHz.hertz());
173   ASSERT_TRUE(ts.has_value());
174   EXPECT_EQ(ts->ms(), clock.TimeInMilliseconds() + 1000);
175 }
176 
TEST(TimestampExtrapolatorTest,TimestampJump)177 TEST(TimestampExtrapolatorTest, TimestampJump) {
178   // This simulates a jump in RTP timestamp, which could occur if a camera was
179   // swapped for example.
180   SimulatedClock clock(Timestamp::Millis(1337));
181   TimestampExtrapolator ts_extrapolator(clock.CurrentTime());
182 
183   uint32_t rtp = 90000;
184   clock.AdvanceTime(k25FpsDelay);
185   ts_extrapolator.Update(clock.CurrentTime(), rtp);
186   rtp += kRtpHz / k25Fps;
187   clock.AdvanceTime(k25FpsDelay);
188   ts_extrapolator.Update(clock.CurrentTime(), rtp);
189   rtp += kRtpHz / k25Fps;
190   clock.AdvanceTime(k25FpsDelay);
191   ts_extrapolator.Update(clock.CurrentTime(), rtp);
192   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp),
193               Optional(clock.CurrentTime()));
194   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp + 90000),
195               Optional(clock.CurrentTime() + TimeDelta::Seconds(1)));
196 
197   // Jump RTP.
198   uint32_t new_rtp = 1337 * 90000;
199   clock.AdvanceTime(k25FpsDelay);
200   ts_extrapolator.Update(clock.CurrentTime(), new_rtp);
201   new_rtp += kRtpHz / k25Fps;
202   clock.AdvanceTime(k25FpsDelay);
203   ts_extrapolator.Update(clock.CurrentTime(), new_rtp);
204   EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(new_rtp),
205               Optional(clock.CurrentTime()));
206 }
207 
208 }  // namespace webrtc
209