1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "cast/streaming/packet_receive_stats_tracker.h"
6
7 #include <chrono>
8 #include <limits>
9
10 #include "cast/streaming/constants.h"
11 #include "gtest/gtest.h"
12 #include "util/chrono_helpers.h"
13
14 namespace openscreen {
15 namespace cast {
16 namespace {
17
18 // Returns a RtcpReportBlock with all fields set to known values to see how the
19 // fields are modified by functions called during the tests.
GetSentinel()20 RtcpReportBlock GetSentinel() {
21 RtcpReportBlock report;
22 report.ssrc = Ssrc{0x1337beef};
23 report.packet_fraction_lost_numerator = -999;
24 report.cumulative_packets_lost = -0x1337cafe;
25 report.extended_high_sequence_number = 0x98765432;
26 report.jitter =
27 RtpTimeDelta::FromTicks(std::numeric_limits<int64_t>::max() - 42);
28 report.last_status_report_id = StatusReportId{2222222222};
29 report.delay_since_last_report = RtcpReportBlock::Delay(-0x3550641);
30 return report;
31 }
32
33 // Run gtest expectations, that no fields were changed.
34 #define EXPECT_FIELDS_NOT_POPULATED(x) \
35 do { \
36 const RtcpReportBlock sentinel = GetSentinel(); \
37 EXPECT_EQ(sentinel.ssrc, (x).ssrc); \
38 EXPECT_EQ(sentinel.packet_fraction_lost_numerator, \
39 (x).packet_fraction_lost_numerator); \
40 EXPECT_EQ(sentinel.cumulative_packets_lost, (x).cumulative_packets_lost); \
41 EXPECT_EQ(sentinel.extended_high_sequence_number, \
42 (x).extended_high_sequence_number); \
43 EXPECT_EQ(sentinel.jitter, (x).jitter); \
44 EXPECT_EQ(sentinel.last_status_report_id, (x).last_status_report_id); \
45 EXPECT_EQ(sentinel.delay_since_last_report, (x).delay_since_last_report); \
46 } while (false)
47
48 // Run gtest expectations, that only the fields changed by
49 // PacketReceiveStatsTracker::PopulateNextReport() were changed.
50 #define EXPECT_FIELDS_POPULATED(x) \
51 do { \
52 const RtcpReportBlock sentinel = GetSentinel(); \
53 /* Fields that should remain untouched by PopulateNextReport(). */ \
54 EXPECT_EQ(sentinel.ssrc, (x).ssrc); \
55 EXPECT_EQ(sentinel.last_status_report_id, (x).last_status_report_id); \
56 EXPECT_EQ(sentinel.delay_since_last_report, (x).delay_since_last_report); \
57 /* Fields that should have changed.*/ \
58 EXPECT_NE(sentinel.packet_fraction_lost_numerator, \
59 (x).packet_fraction_lost_numerator); \
60 EXPECT_NE(sentinel.cumulative_packets_lost, (x).cumulative_packets_lost); \
61 EXPECT_NE(sentinel.extended_high_sequence_number, \
62 (x).extended_high_sequence_number); \
63 EXPECT_NE(sentinel.jitter, (x).jitter); \
64 } while (false)
65
TEST(PacketReceiveStatsTrackerTest,DoesNotPopulateReportWithoutData)66 TEST(PacketReceiveStatsTrackerTest, DoesNotPopulateReportWithoutData) {
67 PacketReceiveStatsTracker tracker(kRtpVideoTimebase);
68 RtcpReportBlock report = GetSentinel();
69 tracker.PopulateNextReport(&report);
70 EXPECT_FIELDS_NOT_POPULATED(report);
71 }
72
TEST(PacketReceiveStatsTrackerTest,PopulatesReportWithOnePacketTracked)73 TEST(PacketReceiveStatsTrackerTest, PopulatesReportWithOnePacketTracked) {
74 constexpr uint16_t kSequenceNumber = 1234;
75 constexpr RtpTimeTicks kRtpTimestamp =
76 RtpTimeTicks() + RtpTimeDelta::FromTicks(42);
77 constexpr auto kArrivalTime = Clock::time_point() + seconds(3600);
78
79 PacketReceiveStatsTracker tracker(kRtpVideoTimebase);
80 tracker.OnReceivedValidRtpPacket(kSequenceNumber, kRtpTimestamp,
81 kArrivalTime);
82
83 RtcpReportBlock report = GetSentinel();
84 tracker.PopulateNextReport(&report);
85 EXPECT_FIELDS_POPULATED(report);
86 EXPECT_EQ(0, report.packet_fraction_lost_numerator);
87 EXPECT_EQ(0, report.cumulative_packets_lost);
88 EXPECT_EQ(kSequenceNumber, report.extended_high_sequence_number);
89 EXPECT_EQ(RtpTimeDelta(), report.jitter);
90 }
91
TEST(PacketReceiveStatsTrackerTest,WhenReceivingAllPackets)92 TEST(PacketReceiveStatsTrackerTest, WhenReceivingAllPackets) {
93 // Set the first sequence number such that wraparound is going to be tested.
94 constexpr uint16_t kFirstSequenceNumber =
95 std::numeric_limits<uint16_t>::max() - 2;
96 constexpr RtpTimeTicks kFirstRtpTimestamp =
97 RtpTimeTicks() + RtpTimeDelta::FromTicks(42);
98 constexpr auto kFirstArrivalTime = Clock::time_point() + seconds(3600);
99
100 PacketReceiveStatsTracker tracker(kRtpVideoTimebase);
101
102 // Record 10 packets arrived exactly one second apart with media timestamps
103 // also exactly one second apart.
104 for (int i = 0; i < 10; ++i) {
105 tracker.OnReceivedValidRtpPacket(
106 kFirstSequenceNumber + i,
107 kFirstRtpTimestamp + RtpTimeDelta::FromTicks(kRtpVideoTimebase) * i,
108 kFirstArrivalTime + seconds(i));
109 }
110
111 RtcpReportBlock report = GetSentinel();
112 tracker.PopulateNextReport(&report);
113 EXPECT_FIELDS_POPULATED(report);
114
115 // Nothing should indicate to the tracker that any packets were dropped.
116 EXPECT_EQ(0, report.packet_fraction_lost_numerator);
117 EXPECT_EQ(0, report.cumulative_packets_lost);
118
119 // The |extended_high_sequence_number| should reflect the wraparound of the
120 // 16-bit counter value.
121 EXPECT_EQ(uint32_t{65542}, report.extended_high_sequence_number);
122
123 // There should be zero jitter, based on the timing information that was given
124 // for each RTP packet.
125 EXPECT_EQ(RtpTimeDelta(), report.jitter);
126 }
127
TEST(PacketReceiveStatsTrackerTest,WhenReceivingAboutHalfThePackets)128 TEST(PacketReceiveStatsTrackerTest, WhenReceivingAboutHalfThePackets) {
129 constexpr uint16_t kFirstSequenceNumber = 3;
130 constexpr RtpTimeTicks kFirstRtpTimestamp =
131 RtpTimeTicks() + RtpTimeDelta::FromTicks(99);
132 constexpr auto kFirstArrivalTime = Clock::time_point() + seconds(8888);
133
134 PacketReceiveStatsTracker tracker(kRtpVideoTimebase);
135
136 // Record 10 packet arrivals whose sequence numbers step by 2, which should
137 // indicate half of the packets didn't arrive.
138 //
139 // Ten arrived: 1, 3, 5, 7, 9, 11, 13, 15, 17, 19
140 // Nine inferred missing: 2, 4, 6, 8, 10, 12, 14, 16, 18
141 for (int i = 0; i < 10; ++i) {
142 tracker.OnReceivedValidRtpPacket(
143 kFirstSequenceNumber + (i * 2 + 1),
144 kFirstRtpTimestamp + RtpTimeDelta::FromTicks(kRtpVideoTimebase) * i,
145 kFirstArrivalTime + seconds(i));
146 }
147
148 RtcpReportBlock report = GetSentinel();
149 tracker.PopulateNextReport(&report);
150 EXPECT_FIELDS_POPULATED(report);
151 EXPECT_EQ(121, report.packet_fraction_lost_numerator);
152 EXPECT_EQ(9, report.cumulative_packets_lost);
153 EXPECT_EQ(uint32_t{22}, report.extended_high_sequence_number);
154 // There should be zero jitter, based on the timing information that was given
155 // for each RTP packet.
156 EXPECT_EQ(RtpTimeDelta(), report.jitter);
157 }
158
TEST(PacketReceiveStatsTrackerTest,ComputesJitterCorrectly)159 TEST(PacketReceiveStatsTrackerTest, ComputesJitterCorrectly) {
160 constexpr uint16_t kFirstSequenceNumber = 3;
161 constexpr RtpTimeTicks kFirstRtpTimestamp =
162 RtpTimeTicks() + RtpTimeDelta::FromTicks(99);
163 constexpr auto kFirstArrivalTime = Clock::time_point() + seconds(8888);
164
165 // Record 100 packet arrivals, one second apart, where each packet's RTP
166 // timestamps are progressing 2 seconds forward. Thus, the jitter calculation
167 // should gradually converge towards a difference of one second.
168 constexpr auto kTrueJitter = Clock::to_duration(seconds(1));
169 PacketReceiveStatsTracker tracker(kRtpVideoTimebase);
170 Clock::duration last_diff = Clock::duration::max();
171 for (int i = 0; i < 100; ++i) {
172 tracker.OnReceivedValidRtpPacket(
173 kFirstSequenceNumber + i,
174 kFirstRtpTimestamp +
175 RtpTimeDelta::FromTicks(kRtpVideoTimebase) * (i * 2),
176 kFirstArrivalTime + seconds(i));
177
178 // Expect that the jitter is becoming closer to the actual value in each
179 // iteration.
180 RtcpReportBlock report;
181 tracker.PopulateNextReport(&report);
182 const auto diff = kTrueJitter - report.jitter.ToDuration<Clock::duration>(
183 kRtpVideoTimebase);
184 EXPECT_LT(diff, last_diff);
185 last_diff = diff;
186 }
187
188 // Because the jitter calculation is a weighted moving average, and also
189 // because the timebase has to be converted here, the metric might not ever
190 // become exactly kTrueJitter. Ensure that it has converged reasonably close
191 // to that value.
192 RtcpReportBlock report;
193 tracker.PopulateNextReport(&report);
194 const auto diff = kTrueJitter - report.jitter.ToDuration<Clock::duration>(
195 kRtpVideoTimebase);
196 constexpr auto kMaxDiffAtEnd = Clock::to_duration(milliseconds(2));
197 EXPECT_NEAR(0, diff.count(), kMaxDiffAtEnd.count());
198 }
199
200 } // namespace
201 } // namespace cast
202 } // namespace openscreen
203