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/compound_rtcp_builder.h"
6
7 #include <algorithm>
8 #include <chrono>
9
10 #include "cast/streaming/compound_rtcp_parser.h"
11 #include "cast/streaming/constants.h"
12 #include "cast/streaming/mock_compound_rtcp_parser_client.h"
13 #include "cast/streaming/rtcp_session.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 #include "platform/api/time.h"
17 #include "util/chrono_helpers.h"
18
19 using testing::_;
20 using testing::Invoke;
21 using testing::Mock;
22 using testing::SaveArg;
23 using testing::StrictMock;
24
25 namespace openscreen {
26 namespace cast {
27 namespace {
28
29 constexpr Ssrc kSenderSsrc{1};
30 constexpr Ssrc kReceiverSsrc{2};
31
32 class CompoundRtcpBuilderTest : public testing::Test {
33 public:
session()34 RtcpSession* session() { return &session_; }
builder()35 CompoundRtcpBuilder* builder() { return &builder_; }
client()36 StrictMock<MockCompoundRtcpParserClient>* client() { return &client_; }
parser()37 CompoundRtcpParser* parser() { return &parser_; }
38
39 // Return |timestamp| converted to the NtpTimestamp wire format and then
40 // converted back to the local Clock's time_point. The result will be either
41 // exactly equal to |original|, or one tick off from it due to the lossy
42 // conversions.
ViaNtpTimestampTranslation(Clock::time_point timestamp) const43 Clock::time_point ViaNtpTimestampTranslation(
44 Clock::time_point timestamp) const {
45 return session_.ntp_converter().ToLocalTime(
46 session_.ntp_converter().ToNtpTimestamp(timestamp));
47 }
48
49 private:
50 RtcpSession session_{kSenderSsrc, kReceiverSsrc, Clock::now()};
51 CompoundRtcpBuilder builder_{&session_};
52 StrictMock<MockCompoundRtcpParserClient> client_;
53 CompoundRtcpParser parser_{&session_, &client_};
54 };
55
56 // Tests that the builder, by default, produces RTCP packets that always include
57 // the receiver's reference time and checkpoint information.
TEST_F(CompoundRtcpBuilderTest,TheBasics)58 TEST_F(CompoundRtcpBuilderTest, TheBasics) {
59 const FrameId checkpoint = FrameId::first() + 42;
60 builder()->SetCheckpointFrame(checkpoint);
61 const milliseconds playout_delay{321};
62 builder()->SetPlayoutDelay(playout_delay);
63
64 const auto send_time = Clock::now();
65 uint8_t buffer[CompoundRtcpBuilder::kRequiredBufferSize];
66 const auto packet = builder()->BuildPacket(send_time, buffer);
67 ASSERT_TRUE(packet.data());
68
69 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(
70 ViaNtpTimestampTranslation(send_time)));
71 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, playout_delay));
72 ASSERT_TRUE(parser()->Parse(packet, checkpoint));
73 }
74
75 // Tests that the builder correctly serializes a Receiver Report Block and
76 // includes it only in the next-built RTCP packet.
TEST_F(CompoundRtcpBuilderTest,WithReceiverReportBlock)77 TEST_F(CompoundRtcpBuilderTest, WithReceiverReportBlock) {
78 const FrameId checkpoint = FrameId::first() + 42;
79 builder()->SetCheckpointFrame(checkpoint);
80 const auto playout_delay = builder()->playout_delay();
81
82 RtcpReportBlock original;
83 original.ssrc = kSenderSsrc;
84 original.packet_fraction_lost_numerator = 1;
85 original.cumulative_packets_lost = 2;
86 original.extended_high_sequence_number = 3;
87 original.jitter = RtpTimeDelta::FromTicks(4);
88 original.last_status_report_id = StatusReportId{0x05060708};
89 original.delay_since_last_report = RtcpReportBlock::Delay(9);
90 builder()->IncludeReceiverReportInNextPacket(original);
91
92 const auto send_time = Clock::now();
93 uint8_t buffer[CompoundRtcpBuilder::kRequiredBufferSize];
94 const auto packet = builder()->BuildPacket(send_time, buffer);
95 ASSERT_TRUE(packet.data());
96
97 // Expect that the builder has produced a RTCP packet that includes the
98 // receiver report block.
99 const auto max_feedback_frame_id = checkpoint + 2;
100 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(
101 ViaNtpTimestampTranslation(send_time)));
102 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, playout_delay));
103 RtcpReportBlock parsed;
104 EXPECT_CALL(*(client()), OnReceiverReport(_)).WillOnce(SaveArg<0>(&parsed));
105 ASSERT_TRUE(parser()->Parse(packet, max_feedback_frame_id));
106 Mock::VerifyAndClearExpectations(client());
107 EXPECT_EQ(original.ssrc, parsed.ssrc);
108 EXPECT_EQ(original.packet_fraction_lost_numerator,
109 parsed.packet_fraction_lost_numerator);
110 EXPECT_EQ(original.cumulative_packets_lost, parsed.cumulative_packets_lost);
111 EXPECT_EQ(original.extended_high_sequence_number,
112 parsed.extended_high_sequence_number);
113 EXPECT_EQ(original.jitter, parsed.jitter);
114 EXPECT_EQ(original.last_status_report_id, parsed.last_status_report_id);
115 EXPECT_EQ(original.delay_since_last_report, parsed.delay_since_last_report);
116
117 // Build again, but this time the builder should not include the receiver
118 // report block.
119 const auto second_send_time = send_time + milliseconds(500);
120 const auto second_packet = builder()->BuildPacket(second_send_time, buffer);
121 ASSERT_TRUE(second_packet.data());
122 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(
123 ViaNtpTimestampTranslation(second_send_time)));
124 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, playout_delay));
125 EXPECT_CALL(*(client()), OnReceiverReport(_)).Times(0);
126 ASSERT_TRUE(parser()->Parse(second_packet, max_feedback_frame_id));
127 Mock::VerifyAndClearExpectations(client());
128 }
129
130 // Tests that the builder repeatedly produces packets with the PLI message as
131 // long as the PLI flag is set, and produces packets without the PLI message
132 // while the flag is not set.
TEST_F(CompoundRtcpBuilderTest,WithPictureLossIndicator)133 TEST_F(CompoundRtcpBuilderTest, WithPictureLossIndicator) {
134 // Turn the PLI flag off and on twice, generating several packets while the
135 // flag is in each state.
136 FrameId checkpoint = FrameId::first();
137 auto send_time = Clock::now();
138 uint8_t buffer[CompoundRtcpBuilder::kRequiredBufferSize];
139 for (int status = 0; status <= 3; ++status) {
140 const bool pli_flag_set = ((status % 2) != 0);
141 builder()->SetPictureLossIndicator(pli_flag_set);
142
143 // Produce three packets while the PLI flag is not changing, and confirm the
144 // PLI condition is being parsed on the other end.
145 for (int i = 0; i < 3; ++i) {
146 SCOPED_TRACE(testing::Message() << "status=" << status << ", i=" << i);
147
148 EXPECT_EQ(pli_flag_set, builder()->is_picture_loss_indicator_set());
149 builder()->SetCheckpointFrame(checkpoint);
150 const auto playout_delay = builder()->playout_delay();
151 const auto packet = builder()->BuildPacket(send_time, buffer);
152 ASSERT_TRUE(packet.data());
153
154 const auto max_feedback_frame_id = checkpoint + 1;
155 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(
156 ViaNtpTimestampTranslation(send_time)));
157 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, playout_delay));
158 EXPECT_CALL(*(client()), OnReceiverIndicatesPictureLoss())
159 .Times(pli_flag_set ? 1 : 0);
160 ASSERT_TRUE(parser()->Parse(packet, max_feedback_frame_id));
161 Mock::VerifyAndClearExpectations(client());
162
163 ++checkpoint;
164 send_time += milliseconds(500);
165 }
166 }
167 }
168
169 // Tests that the builder produces packets with frame-level and specific-packet
170 // NACKs, but includes this information only in the next-built RTCP packet.
TEST_F(CompoundRtcpBuilderTest,WithNacks)171 TEST_F(CompoundRtcpBuilderTest, WithNacks) {
172 const FrameId checkpoint = FrameId::first() + 15;
173 builder()->SetCheckpointFrame(checkpoint);
174 const auto playout_delay = builder()->playout_delay();
175
176 const std::vector<PacketNack> kPacketNacks = {
177 {FrameId::first() + 16, FramePacketId{0}},
178 {FrameId::first() + 16, FramePacketId{1}},
179 {FrameId::first() + 16, FramePacketId{2}},
180 {FrameId::first() + 16, FramePacketId{7}},
181 {FrameId::first() + 16, FramePacketId{15}},
182 {FrameId::first() + 17, FramePacketId{19}},
183 {FrameId::first() + 18, kAllPacketsLost},
184 {FrameId::first() + 19, kAllPacketsLost},
185 };
186 builder()->IncludeFeedbackInNextPacket(kPacketNacks, {});
187
188 const auto send_time = Clock::now();
189 uint8_t buffer[CompoundRtcpBuilder::kRequiredBufferSize];
190 const auto packet = builder()->BuildPacket(send_time, buffer);
191 ASSERT_TRUE(packet.data());
192
193 // Expect that the builder has produced a RTCP packet that also includes the
194 // NACK feedback.
195 const auto kMaxFeedbackFrameId = FrameId::first() + 19;
196 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(
197 ViaNtpTimestampTranslation(send_time)));
198 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, playout_delay));
199 EXPECT_CALL(*(client()), OnReceiverIsMissingPackets(kPacketNacks));
200 ASSERT_TRUE(parser()->Parse(packet, kMaxFeedbackFrameId));
201 Mock::VerifyAndClearExpectations(client());
202
203 // Build again, but this time the builder should not include the feedback.
204 const auto second_send_time = send_time + milliseconds(500);
205 const auto second_packet = builder()->BuildPacket(second_send_time, buffer);
206 ASSERT_TRUE(second_packet.data());
207 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(
208 ViaNtpTimestampTranslation(second_send_time)));
209 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, playout_delay));
210 EXPECT_CALL(*(client()), OnReceiverIsMissingPackets(_)).Times(0);
211 ASSERT_TRUE(parser()->Parse(second_packet, kMaxFeedbackFrameId));
212 Mock::VerifyAndClearExpectations(client());
213 }
214
215 // Tests that the builder produces packets with frame-level ACKs, but includes
216 // this information only in the next-built RTCP packet. Both a single-frame ACK
217 // and a multi-frame ACK are tested, to exercise the various code paths
218 // containing the serialization logic that auto-extends the ACK bit vector
219 // length when necessary.
TEST_F(CompoundRtcpBuilderTest,WithAcks)220 TEST_F(CompoundRtcpBuilderTest, WithAcks) {
221 const FrameId checkpoint = FrameId::first() + 22;
222 builder()->SetCheckpointFrame(checkpoint);
223 const auto playout_delay = builder()->playout_delay();
224
225 const std::vector<FrameId> kTestCases[] = {
226 // One frame ACK will result in building an ACK bit vector of 2 bytes
227 // only.
228 {FrameId::first() + 24},
229
230 // These frame ACKs were chosen so that the ACK bit vector must expand to
231 // be 6 (2 + 4) bytes long.
232 {FrameId::first() + 25, FrameId::first() + 42, FrameId::first() + 43},
233 };
234 const auto kMaxFeedbackFrameId = FrameId::first() + 50;
235 auto send_time = Clock::now();
236 uint8_t buffer[CompoundRtcpBuilder::kRequiredBufferSize];
237 for (const std::vector<FrameId>& frame_acks : kTestCases) {
238 // Include the frame ACK feedback, and expect that the builder will produce
239 // a RTCP packet that also includes the ACK feedback.
240 builder()->IncludeFeedbackInNextPacket({}, frame_acks);
241 const auto packet = builder()->BuildPacket(send_time, buffer);
242 ASSERT_TRUE(packet.data());
243 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(
244 ViaNtpTimestampTranslation(send_time)));
245 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, playout_delay));
246 EXPECT_CALL(*(client()), OnReceiverHasFrames(frame_acks));
247 ASSERT_TRUE(parser()->Parse(packet, kMaxFeedbackFrameId));
248 Mock::VerifyAndClearExpectations(client());
249
250 // Build again, but this time the builder should not include the feedback
251 // because it was already provided in the prior packet.
252 send_time += milliseconds(500);
253 const auto second_packet = builder()->BuildPacket(send_time, buffer);
254 ASSERT_TRUE(second_packet.data());
255 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(
256 ViaNtpTimestampTranslation(send_time)));
257 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, playout_delay));
258 EXPECT_CALL(*(client()), OnReceiverHasFrames(_)).Times(0);
259 ASSERT_TRUE(parser()->Parse(second_packet, kMaxFeedbackFrameId));
260 Mock::VerifyAndClearExpectations(client());
261
262 send_time += milliseconds(500);
263 }
264 }
265
266 // Tests that the builder handles scenarios where the provided buffer isn't big
267 // enough to hold all the ACK/NACK details. The expected behavior is that it
268 // will include as many of the NACKs as possible, followed by as many of the
269 // ACKs as possible.
TEST_F(CompoundRtcpBuilderTest,WithEverythingThatCanFit)270 TEST_F(CompoundRtcpBuilderTest, WithEverythingThatCanFit) {
271 const FrameId checkpoint = FrameId::first();
272 builder()->SetCheckpointFrame(checkpoint);
273
274 // For this test, use an abnormally-huge, but not impossible, list of NACKs
275 // and ACKs. Each NACK is for a separate frame so that a separate "loss field"
276 // will be generated in the serialized output.
277 std::vector<PacketNack> nacks;
278 for (FrameId f = checkpoint + 1; f != checkpoint + 64; ++f) {
279 nacks.push_back(PacketNack{f, FramePacketId{0}});
280 }
281 std::vector<FrameId> acks;
282 for (FrameId f = checkpoint + 64; f < checkpoint + kMaxUnackedFrames; ++f) {
283 acks.push_back(f);
284 }
285 ASSERT_FALSE(acks.empty());
286
287 const auto max_feedback_frame_id = checkpoint + kMaxUnackedFrames;
288
289 // First test: Include too many NACKs so that some of them will be dropped and
290 // none of the ACKs will be included.
291 builder()->IncludeFeedbackInNextPacket(nacks, acks);
292 uint8_t buffer[CompoundRtcpBuilder::kRequiredBufferSize];
293 const auto packet = builder()->BuildPacket(Clock::now(), buffer);
294 ASSERT_TRUE(packet.data());
295 EXPECT_EQ(sizeof(buffer), packet.size()); // The whole buffer should be used.
296
297 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(_));
298 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, _));
299 // No ACKs could be included.
300 EXPECT_CALL(*(client()), OnReceiverHasFrames(_)).Times(0);
301 EXPECT_CALL(*(client()), OnReceiverIsMissingPackets(_))
302 .WillOnce(Invoke([&](std::vector<PacketNack> parsed_nacks) {
303 // Some should be dropped.
304 ASSERT_LT(parsed_nacks.size(), nacks.size());
305 EXPECT_TRUE(std::equal(parsed_nacks.begin(), parsed_nacks.end(),
306 nacks.begin()));
307 }));
308 ASSERT_TRUE(parser()->Parse(packet, max_feedback_frame_id));
309 Mock::VerifyAndClearExpectations(client());
310
311 // Second test: Include fewer NACKs this time, so that none of the NACKs are
312 // dropped, but not all of the ACKs can be included. With internal knowledge
313 // of the wire format, it turns out that limiting serialization to 48 loss
314 // fields will free-up just enough space for 2 bytes of ACK bit vector.
315 constexpr int kFewerNackCount = 48;
316 builder()->IncludeFeedbackInNextPacket(
317 std::vector<PacketNack>(nacks.begin(), nacks.begin() + kFewerNackCount),
318 acks);
319 const auto second_packet = builder()->BuildPacket(Clock::now(), buffer);
320 ASSERT_TRUE(second_packet.data());
321 // The whole buffer should be used.
322 EXPECT_EQ(sizeof(buffer), second_packet.size());
323
324 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(_));
325 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, _));
326 EXPECT_CALL(*(client()), OnReceiverHasFrames(_))
327 .WillOnce(Invoke([&](std::vector<FrameId> parsed_acks) {
328 // Some of the ACKs should be dropped.
329 ASSERT_LT(parsed_acks.size(), acks.size());
330 EXPECT_TRUE(
331 std::equal(parsed_acks.begin(), parsed_acks.end(), acks.begin()));
332 }));
333 EXPECT_CALL(*(client()), OnReceiverIsMissingPackets(_))
334 .WillOnce(Invoke([&](absl::Span<const PacketNack> parsed_nacks) {
335 // All of the 48 NACKs provided should be present.
336 ASSERT_EQ(kFewerNackCount, static_cast<int>(parsed_nacks.size()));
337 EXPECT_TRUE(std::equal(parsed_nacks.begin(), parsed_nacks.end(),
338 nacks.begin()));
339 }));
340 ASSERT_TRUE(parser()->Parse(second_packet, max_feedback_frame_id));
341 Mock::VerifyAndClearExpectations(client());
342
343 // Third test: Include even fewer NACKs, so that nothing is dropped.
344 constexpr int kEvenFewerNackCount = 46;
345 builder()->IncludeFeedbackInNextPacket(
346 std::vector<PacketNack>(nacks.begin(),
347 nacks.begin() + kEvenFewerNackCount),
348 acks);
349 const auto third_packet = builder()->BuildPacket(Clock::now(), buffer);
350 ASSERT_TRUE(third_packet.data());
351
352 EXPECT_CALL(*(client()), OnReceiverReferenceTimeAdvanced(_));
353 EXPECT_CALL(*(client()), OnReceiverCheckpoint(checkpoint, _));
354 EXPECT_CALL(*(client()), OnReceiverHasFrames(_))
355 .WillOnce(Invoke([&](std::vector<FrameId> parsed_acks) {
356 // All acks should be present.
357 EXPECT_EQ(acks, parsed_acks);
358 }));
359 EXPECT_CALL(*(client()), OnReceiverIsMissingPackets(_))
360 .WillOnce(Invoke([&](absl::Span<const PacketNack> parsed_nacks) {
361 // Only the first 46 NACKs provided should be present.
362 ASSERT_EQ(kEvenFewerNackCount, static_cast<int>(parsed_nacks.size()));
363 EXPECT_TRUE(std::equal(parsed_nacks.begin(), parsed_nacks.end(),
364 nacks.begin()));
365 }));
366 ASSERT_TRUE(parser()->Parse(third_packet, max_feedback_frame_id));
367 Mock::VerifyAndClearExpectations(client());
368 }
369
370 } // namespace
371 } // namespace cast
372 } // namespace openscreen
373