stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2011 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 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 11 | #include "modules/video_coding/timing.h" |
Jonas Olsson | a4d8737 | 2019-07-05 19:08:33 +0200 | [diff] [blame] | 12 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 13 | #include "system_wrappers/include/clock.h" |
Johannes Kron | 985905d | 2021-06-29 11:37:06 +0200 | [diff] [blame] | 14 | #include "test/field_trial.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 15 | #include "test/gtest.h" |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 16 | |
| 17 | namespace webrtc { |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 18 | namespace { |
| 19 | const int kFps = 25; |
| 20 | } // namespace |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 21 | |
Johannes Kron | 985905d | 2021-06-29 11:37:06 +0200 | [diff] [blame] | 22 | TEST(ReceiverTimingTest, JitterDelay) { |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 23 | SimulatedClock clock(0); |
| 24 | VCMTiming timing(&clock); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 25 | timing.Reset(); |
| 26 | |
| 27 | uint32_t timestamp = 0; |
| 28 | timing.UpdateCurrentDelay(timestamp); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 29 | |
| 30 | timing.Reset(); |
| 31 | |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 32 | timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); |
| 33 | uint32_t jitter_delay_ms = 20; |
| 34 | timing.SetJitterDelay(jitter_delay_ms); |
| 35 | timing.UpdateCurrentDelay(timestamp); |
mikhal@webrtc.org | adc64a7 | 2013-05-30 16:20:18 +0000 | [diff] [blame] | 36 | timing.set_render_delay(0); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 37 | uint32_t wait_time_ms = timing.MaxWaitingTime( |
| 38 | timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 39 | clock.TimeInMilliseconds()); |
| 40 | // First update initializes the render time. Since we have no decode delay |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 41 | // we get wait_time_ms = renderTime - now - renderDelay = jitter. |
| 42 | EXPECT_EQ(jitter_delay_ms, wait_time_ms); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 43 | |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 44 | jitter_delay_ms += VCMTiming::kDelayMaxChangeMsPerS + 10; |
| 45 | timestamp += 90000; |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 46 | clock.AdvanceTimeMilliseconds(1000); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 47 | timing.SetJitterDelay(jitter_delay_ms); |
| 48 | timing.UpdateCurrentDelay(timestamp); |
| 49 | wait_time_ms = timing.MaxWaitingTime( |
| 50 | timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), |
philipel | 5908c71 | 2015-12-21 08:23:20 -0800 | [diff] [blame] | 51 | clock.TimeInMilliseconds()); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 52 | // Since we gradually increase the delay we only get 100 ms every second. |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 53 | EXPECT_EQ(jitter_delay_ms - 10, wait_time_ms); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 54 | |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 55 | timestamp += 90000; |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 56 | clock.AdvanceTimeMilliseconds(1000); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 57 | timing.UpdateCurrentDelay(timestamp); |
| 58 | wait_time_ms = timing.MaxWaitingTime( |
| 59 | timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 60 | clock.TimeInMilliseconds()); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 61 | EXPECT_EQ(jitter_delay_ms, wait_time_ms); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 62 | |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 63 | // Insert frames without jitter, verify that this gives the exact wait time. |
| 64 | const int kNumFrames = 300; |
| 65 | for (int i = 0; i < kNumFrames; i++) { |
| 66 | clock.AdvanceTimeMilliseconds(1000 / kFps); |
| 67 | timestamp += 90000 / kFps; |
| 68 | timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 69 | } |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 70 | timing.UpdateCurrentDelay(timestamp); |
| 71 | wait_time_ms = timing.MaxWaitingTime( |
| 72 | timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 73 | clock.TimeInMilliseconds()); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 74 | EXPECT_EQ(jitter_delay_ms, wait_time_ms); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 75 | |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 76 | // Add decode time estimates for 1 second. |
| 77 | const uint32_t kDecodeTimeMs = 10; |
| 78 | for (int i = 0; i < kFps; i++) { |
| 79 | clock.AdvanceTimeMilliseconds(kDecodeTimeMs); |
Johannes Kron | bfd343b | 2019-07-01 10:07:50 +0200 | [diff] [blame] | 80 | timing.StopDecodeTimer(kDecodeTimeMs, clock.TimeInMilliseconds()); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 81 | timestamp += 90000 / kFps; |
| 82 | clock.AdvanceTimeMilliseconds(1000 / kFps - kDecodeTimeMs); |
| 83 | timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 84 | } |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 85 | timing.UpdateCurrentDelay(timestamp); |
| 86 | wait_time_ms = timing.MaxWaitingTime( |
| 87 | timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 88 | clock.TimeInMilliseconds()); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 89 | EXPECT_EQ(jitter_delay_ms, wait_time_ms); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 90 | |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 91 | const int kMinTotalDelayMs = 200; |
| 92 | timing.set_min_playout_delay(kMinTotalDelayMs); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 93 | clock.AdvanceTimeMilliseconds(5000); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 94 | timestamp += 5 * 90000; |
| 95 | timing.UpdateCurrentDelay(timestamp); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 96 | const int kRenderDelayMs = 10; |
mikhal@webrtc.org | adc64a7 | 2013-05-30 16:20:18 +0000 | [diff] [blame] | 97 | timing.set_render_delay(kRenderDelayMs); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 98 | wait_time_ms = timing.MaxWaitingTime( |
| 99 | timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 100 | clock.TimeInMilliseconds()); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 101 | // We should at least have kMinTotalDelayMs - decodeTime (10) - renderTime |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 102 | // (10) to wait. |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 103 | EXPECT_EQ(kMinTotalDelayMs - kDecodeTimeMs - kRenderDelayMs, wait_time_ms); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 104 | // The total video delay should be equal to the min total delay. |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 105 | EXPECT_EQ(kMinTotalDelayMs, timing.TargetVideoDelay()); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 106 | |
mikhal@webrtc.org | adc64a7 | 2013-05-30 16:20:18 +0000 | [diff] [blame] | 107 | // Reset playout delay. |
| 108 | timing.set_min_playout_delay(0); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 109 | clock.AdvanceTimeMilliseconds(5000); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 110 | timestamp += 5 * 90000; |
| 111 | timing.UpdateCurrentDelay(timestamp); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 112 | } |
| 113 | |
Johannes Kron | 985905d | 2021-06-29 11:37:06 +0200 | [diff] [blame] | 114 | TEST(ReceiverTimingTest, TimestampWrapAround) { |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 115 | SimulatedClock clock(0); |
| 116 | VCMTiming timing(&clock); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 117 | // Provoke a wrap-around. The fifth frame will have wrapped at 25 fps. |
| 118 | uint32_t timestamp = 0xFFFFFFFFu - 3 * 90000 / kFps; |
| 119 | for (int i = 0; i < 5; ++i) { |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 120 | timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); |
Åsa Persson | 8368d1a | 2018-01-05 12:44:45 +0100 | [diff] [blame] | 121 | clock.AdvanceTimeMilliseconds(1000 / kFps); |
| 122 | timestamp += 90000 / kFps; |
| 123 | EXPECT_EQ(3 * 1000 / kFps, |
| 124 | timing.RenderTimeMs(0xFFFFFFFFu, clock.TimeInMilliseconds())); |
| 125 | EXPECT_EQ(3 * 1000 / kFps + 1, |
| 126 | timing.RenderTimeMs(89u, // One ms later in 90 kHz. |
| 127 | clock.TimeInMilliseconds())); |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 128 | } |
| 129 | } |
| 130 | |
Johannes Kron | 985905d | 2021-06-29 11:37:06 +0200 | [diff] [blame] | 131 | TEST(ReceiverTimingTest, MaxWaitingTimeIsZeroForZeroRenderTime) { |
| 132 | // This is the default path when the RTP playout delay header extension is set |
| 133 | // to min==0. |
| 134 | constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. |
| 135 | constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; |
| 136 | constexpr int64_t kZeroRenderTimeMs = 0; |
| 137 | SimulatedClock clock(kStartTimeUs); |
| 138 | VCMTiming timing(&clock); |
| 139 | timing.Reset(); |
| 140 | for (int i = 0; i < 10; ++i) { |
| 141 | clock.AdvanceTimeMilliseconds(kTimeDeltaMs); |
| 142 | int64_t now_ms = clock.TimeInMilliseconds(); |
| 143 | EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), 0); |
| 144 | } |
| 145 | // Another frame submitted at the same time also returns a negative max |
| 146 | // waiting time. |
| 147 | int64_t now_ms = clock.TimeInMilliseconds(); |
| 148 | EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), 0); |
| 149 | // MaxWaitingTime should be less than zero even if there's a burst of frames. |
| 150 | EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), 0); |
| 151 | EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), 0); |
| 152 | EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), 0); |
| 153 | } |
| 154 | |
| 155 | TEST(ReceiverTimingTest, MaxWaitingTimeZeroDelayPacingExperiment) { |
| 156 | // The minimum pacing is enabled by a field trial and active if the RTP |
| 157 | // playout delay header extension is set to min==0. |
| 158 | constexpr int64_t kMinPacingMs = 3; |
| 159 | test::ScopedFieldTrials override_field_trials( |
| 160 | "WebRTC-ZeroPlayoutDelay/min_pacing:3ms/"); |
| 161 | constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. |
| 162 | constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; |
| 163 | constexpr int64_t kZeroRenderTimeMs = 0; |
| 164 | SimulatedClock clock(kStartTimeUs); |
| 165 | VCMTiming timing(&clock); |
| 166 | timing.Reset(); |
| 167 | // MaxWaitingTime() returns zero for evenly spaced video frames. |
| 168 | for (int i = 0; i < 10; ++i) { |
| 169 | clock.AdvanceTimeMilliseconds(kTimeDeltaMs); |
| 170 | int64_t now_ms = clock.TimeInMilliseconds(); |
| 171 | EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), 0); |
| 172 | } |
| 173 | // Another frame submitted at the same time is paced according to the field |
| 174 | // trial setting. |
| 175 | int64_t now_ms = clock.TimeInMilliseconds(); |
| 176 | EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), kMinPacingMs); |
| 177 | // If there's a burst of frames, the min pacing interval is summed. |
| 178 | EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), 2 * kMinPacingMs); |
| 179 | EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), 3 * kMinPacingMs); |
| 180 | EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), 4 * kMinPacingMs); |
| 181 | // Allow a few ms to pass, this should be subtracted from the MaxWaitingTime. |
| 182 | constexpr int64_t kTwoMs = 2; |
| 183 | clock.AdvanceTimeMilliseconds(kTwoMs); |
| 184 | now_ms = clock.TimeInMilliseconds(); |
| 185 | EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms), |
| 186 | 5 * kMinPacingMs - kTwoMs); |
| 187 | } |
| 188 | |
| 189 | TEST(ReceiverTimingTest, DefaultMaxWaitingTimeUnaffectedByPacingExperiment) { |
| 190 | // The minimum pacing is enabled by a field trial but should not have any |
| 191 | // effect if render_time_ms is greater than 0; |
| 192 | test::ScopedFieldTrials override_field_trials( |
| 193 | "WebRTC-ZeroPlayoutDelay/min_pacing:3ms/"); |
| 194 | constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. |
| 195 | constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; |
| 196 | SimulatedClock clock(kStartTimeUs); |
| 197 | VCMTiming timing(&clock); |
| 198 | timing.Reset(); |
| 199 | clock.AdvanceTimeMilliseconds(kTimeDeltaMs); |
| 200 | int64_t now_ms = clock.TimeInMilliseconds(); |
| 201 | int64_t render_time_ms = now_ms + 30; |
| 202 | // Estimate the internal processing delay from the first frame. |
| 203 | int64_t estimated_processing_delay = |
| 204 | (render_time_ms - now_ms) - timing.MaxWaitingTime(render_time_ms, now_ms); |
| 205 | EXPECT_GT(estimated_processing_delay, 0); |
| 206 | |
| 207 | // Any other frame submitted at the same time should be scheduled according to |
| 208 | // its render time. |
| 209 | for (int i = 0; i < 5; ++i) { |
| 210 | render_time_ms += kTimeDeltaMs; |
| 211 | EXPECT_EQ(timing.MaxWaitingTime(render_time_ms, now_ms), |
| 212 | render_time_ms - now_ms - estimated_processing_delay); |
| 213 | } |
| 214 | } |
| 215 | |
stefan@webrtc.org | 9f557c1 | 2013-05-17 12:55:07 +0000 | [diff] [blame] | 216 | } // namespace webrtc |