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