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