• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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