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 <iterator>
9 #include <limits>
10 
11 #include "cast/streaming/packet_util.h"
12 #include "cast/streaming/rtcp_session.h"
13 #include "util/integer_division.h"
14 #include "util/osp_logging.h"
15 #include "util/std_util.h"
16 
17 namespace openscreen {
18 namespace cast {
19 
CompoundRtcpBuilder(RtcpSession * session)20 CompoundRtcpBuilder::CompoundRtcpBuilder(RtcpSession* session)
21     : session_(session) {
22   OSP_DCHECK(session_);
23 }
24 
25 CompoundRtcpBuilder::~CompoundRtcpBuilder() = default;
26 
SetCheckpointFrame(FrameId frame_id)27 void CompoundRtcpBuilder::SetCheckpointFrame(FrameId frame_id) {
28   OSP_DCHECK_GE(frame_id, checkpoint_frame_id_);
29   checkpoint_frame_id_ = frame_id;
30 }
31 
SetPlayoutDelay(std::chrono::milliseconds delay)32 void CompoundRtcpBuilder::SetPlayoutDelay(std::chrono::milliseconds delay) {
33   playout_delay_ = delay;
34 }
35 
SetPictureLossIndicator(bool picture_is_lost)36 void CompoundRtcpBuilder::SetPictureLossIndicator(bool picture_is_lost) {
37   picture_loss_indicator_ = picture_is_lost;
38 }
39 
IncludeReceiverReportInNextPacket(const RtcpReportBlock & receiver_report)40 void CompoundRtcpBuilder::IncludeReceiverReportInNextPacket(
41     const RtcpReportBlock& receiver_report) {
42   receiver_report_for_next_packet_ = receiver_report;
43 }
44 
IncludeFeedbackInNextPacket(std::vector<PacketNack> packet_nacks,std::vector<FrameId> frame_acks)45 void CompoundRtcpBuilder::IncludeFeedbackInNextPacket(
46     std::vector<PacketNack> packet_nacks,
47     std::vector<FrameId> frame_acks) {
48   // Note: Serialization of these lists will depend on the value of
49   // |checkpoint_frame_id_| when BuildPacket() is called later.
50 
51   nacks_for_next_packet_ = std::move(packet_nacks);
52   acks_for_next_packet_ = std::move(frame_acks);
53 
54 #if OSP_DCHECK_IS_ON()
55   OSP_DCHECK(AreElementsSortedAndUnique(nacks_for_next_packet_));
56   OSP_DCHECK(AreElementsSortedAndUnique(acks_for_next_packet_));
57 
58   // Consistency-check: An ACKed frame should not also be NACKed.
59   for (size_t ack_i = 0, nack_i = 0; ack_i < acks_for_next_packet_.size() &&
60                                      nack_i < nacks_for_next_packet_.size();) {
61     const FrameId ack_frame_id = acks_for_next_packet_[ack_i];
62     const FrameId nack_frame_id = nacks_for_next_packet_[nack_i].frame_id;
63     if (ack_frame_id < nack_frame_id) {
64       ++ack_i;
65     } else if (nack_frame_id < ack_frame_id) {
66       ++nack_i;
67     } else {
68       OSP_DCHECK_NE(ack_frame_id, nack_frame_id);
69     }
70   }
71 
72   // Redundancy-check: For any PacketNack whose packet ID is kAllPacketsLost,
73   // there should be no other PacketNack having the same FrameId.
74   for (size_t i = 1; i < nacks_for_next_packet_.size(); ++i) {
75     if (nacks_for_next_packet_[i].packet_id == kAllPacketsLost) {
76       // Since the elements are sorted, it's only necessary to check the
77       // immediately preceeding element to make sure it does not have the same
78       // FrameId.
79       OSP_DCHECK_NE(nacks_for_next_packet_[i].frame_id,
80                     nacks_for_next_packet_[i - 1].frame_id);
81     }
82   }
83 #endif
84 }
85 
BuildPacket(Clock::time_point send_time,absl::Span<uint8_t> buffer)86 absl::Span<uint8_t> CompoundRtcpBuilder::BuildPacket(
87     Clock::time_point send_time,
88     absl::Span<uint8_t> buffer) {
89   OSP_CHECK_GE(buffer.size(), kRequiredBufferSize);
90 
91   uint8_t* const packet_begin = buffer.data();
92 
93   // Receiver Report: Per RFC 3550, Section 6.4.2, all RTCP compound packets
94   // from receivers must include at least an empty receiver report at the start.
95   // It's not clear whether the Cast RTCP spec requires this, but it costs very
96   // little to do so.
97   AppendReceiverReportPacket(&buffer);
98 
99   // Receiver Reference Time Report: While this is optional in the Cast
100   // Streaming spec, it is always included by this implementation to improve the
101   // stability of the end-to-end system.
102   AppendReceiverReferenceTimeReportPacket(send_time, &buffer);
103 
104   // Picture Loss Indicator: Only included if the flag is currently set.
105   if (picture_loss_indicator_) {
106     AppendPictureLossIndicatorPacket(&buffer);
107   }
108 
109   // Cast Feedback: Checkpoint information, and add as many NACKs and ACKs as
110   // the remaning space available in the buffer will allow for.
111   AppendCastFeedbackPacket(&buffer);
112 
113   uint8_t* const packet_end = buffer.data();
114   return absl::Span<uint8_t>(packet_begin, packet_end - packet_begin);
115 }
116 
AppendReceiverReportPacket(absl::Span<uint8_t> * buffer)117 void CompoundRtcpBuilder::AppendReceiverReportPacket(
118     absl::Span<uint8_t>* buffer) {
119   RtcpCommonHeader header;
120   header.packet_type = RtcpPacketType::kReceiverReport;
121   header.payload_size = kRtcpReceiverReportSize;
122   if (receiver_report_for_next_packet_) {
123     header.with.report_count = 1;
124     header.payload_size += kRtcpReportBlockSize;
125   } else {
126     header.with.report_count = 0;
127   }
128   header.AppendFields(buffer);
129   AppendField<uint32_t>(session_->receiver_ssrc(), buffer);
130   if (receiver_report_for_next_packet_) {
131     receiver_report_for_next_packet_->AppendFields(buffer);
132     receiver_report_for_next_packet_ = absl::nullopt;
133   }
134 }
135 
AppendReceiverReferenceTimeReportPacket(Clock::time_point send_time,absl::Span<uint8_t> * buffer)136 void CompoundRtcpBuilder::AppendReceiverReferenceTimeReportPacket(
137     Clock::time_point send_time,
138     absl::Span<uint8_t>* buffer) {
139   RtcpCommonHeader header;
140   header.packet_type = RtcpPacketType::kExtendedReports;
141   header.payload_size = kRtcpExtendedReportHeaderSize +
142                         kRtcpExtendedReportBlockHeaderSize +
143                         kRtcpReceiverReferenceTimeReportBlockSize;
144   header.AppendFields(buffer);
145   AppendField<uint32_t>(session_->receiver_ssrc(), buffer);
146   AppendField<uint8_t>(kRtcpReceiverReferenceTimeReportBlockType, buffer);
147   AppendField<uint8_t>(0 /* reserved/unused byte */, buffer);
148   AppendField<uint16_t>(
149       kRtcpReceiverReferenceTimeReportBlockSize / sizeof(uint32_t), buffer);
150   AppendField<uint64_t>(session_->ntp_converter().ToNtpTimestamp(send_time),
151                         buffer);
152 }
153 
AppendPictureLossIndicatorPacket(absl::Span<uint8_t> * buffer)154 void CompoundRtcpBuilder::AppendPictureLossIndicatorPacket(
155     absl::Span<uint8_t>* buffer) {
156   RtcpCommonHeader header;
157   header.packet_type = RtcpPacketType::kPayloadSpecific;
158   header.with.subtype = RtcpSubtype::kPictureLossIndicator;
159   header.payload_size = kRtcpPictureLossIndicatorHeaderSize;
160   header.AppendFields(buffer);
161   AppendField<uint32_t>(session_->receiver_ssrc(), buffer);
162   AppendField<uint32_t>(session_->sender_ssrc(), buffer);
163 }
164 
AppendCastFeedbackPacket(absl::Span<uint8_t> * buffer)165 void CompoundRtcpBuilder::AppendCastFeedbackPacket(
166     absl::Span<uint8_t>* buffer) {
167   // Reserve space for the RTCP Common Header. It will be serialized later,
168   // after the total size of the Cast Feedback message is known.
169   absl::Span<uint8_t> space_for_header =
170       ReserveSpace(kRtcpCommonHeaderSize, buffer);
171   uint8_t* const feedback_fields_begin = buffer->data();
172 
173   // Append the mandatory fields.
174   AppendField<uint32_t>(session_->receiver_ssrc(), buffer);
175   AppendField<uint32_t>(session_->sender_ssrc(), buffer);
176   AppendField<uint32_t>(kRtcpCastIdentifierWord, buffer);
177   AppendField<uint8_t>(checkpoint_frame_id_.lower_8_bits(), buffer);
178   // The |loss_count_field| will be set after the Loss Fields are generated
179   // and the total count is known.
180   uint8_t* const loss_count_field =
181       ReserveSpace(sizeof(uint8_t), buffer).data();
182   OSP_DCHECK_GT(playout_delay_.count(), 0);
183   OSP_DCHECK_LE(playout_delay_.count(), std::numeric_limits<uint16_t>::max());
184   AppendField<uint16_t>(playout_delay_.count(), buffer);
185 
186   // Try to include as many Loss Fields as possible. Some of the NACKs might
187   // be dropped if the remaining space in the buffer is insufficient to
188   // include them all.
189   const int num_loss_fields = AppendCastFeedbackLossFields(buffer);
190   OSP_DCHECK_LE(num_loss_fields, std::numeric_limits<uint8_t>::max());
191   *loss_count_field = num_loss_fields;
192 
193   // Try to include the CST2 header and ACK bit vector. Again, some of the
194   // ACKs might be dropped if the remaining space in the buffer is
195   // insufficient.
196   AppendCastFeedbackAckFields(buffer);
197 
198   // Go back and fill-in the header fields, now that the total size is known.
199   RtcpCommonHeader header;
200   header.packet_type = RtcpPacketType::kPayloadSpecific;
201   header.with.subtype = RtcpSubtype::kFeedback;
202   uint8_t* const feedback_fields_end = buffer->data();
203   header.payload_size = feedback_fields_end - feedback_fields_begin;
204   header.AppendFields(&space_for_header);
205 
206   ++feedback_count_;
207 }
208 
AppendCastFeedbackLossFields(absl::Span<uint8_t> * buffer)209 int CompoundRtcpBuilder::AppendCastFeedbackLossFields(
210     absl::Span<uint8_t>* buffer) {
211   if (nacks_for_next_packet_.empty()) {
212     return 0;
213   }
214 
215   // The maximum number of entries is limited by available packet buffer space
216   // and the 8-bit |loss_count_field|.
217   const int max_num_loss_fields =
218       std::min<int>(buffer->size() / kRtcpFeedbackLossFieldSize,
219                     std::numeric_limits<uint8_t>::max());
220 
221   // Translate the |nacks_for_next_packet_| list into one or more entries
222   // representing specific packet losses. Omit any NACKs before the checkpoint.
223   OSP_DCHECK(AreElementsSortedAndUnique(nacks_for_next_packet_));
224   auto it =
225       std::find_if(nacks_for_next_packet_.begin(), nacks_for_next_packet_.end(),
226                    [this](const PacketNack& nack) {
227                      return nack.frame_id > checkpoint_frame_id_;
228                    });
229   int num_loss_fields = 0;
230   while (it != nacks_for_next_packet_.end() &&
231          num_loss_fields != max_num_loss_fields) {
232     const FrameId frame_id = it->frame_id;
233     const FramePacketId first_packet_id = it->packet_id;
234     uint8_t bit_vector = 0;
235     for (++it; it != nacks_for_next_packet_.end() && it->frame_id == frame_id;
236          ++it) {
237       const int shift = it->packet_id - first_packet_id - 1;
238       if (shift >= 8) {
239         break;
240       }
241       bit_vector |= 1 << shift;
242     }
243     AppendField<uint8_t>(frame_id.lower_8_bits(), buffer);
244     AppendField<uint16_t>(first_packet_id, buffer);
245     AppendField<uint8_t>(bit_vector, buffer);
246     ++num_loss_fields;
247   }
248 
249   nacks_for_next_packet_.clear();
250   return num_loss_fields;
251 }
252 
AppendCastFeedbackAckFields(absl::Span<uint8_t> * buffer)253 void CompoundRtcpBuilder::AppendCastFeedbackAckFields(
254     absl::Span<uint8_t>* buffer) {
255   // Return if there is not enough space for the CST2 header and the
256   // smallest-possible ACK bit vector.
257   if (buffer->size() <
258       (kRtcpFeedbackAckHeaderSize + kRtcpMinAckBitVectorOctets)) {
259     return;
260   }
261 
262   // Write the CST2 header and reserve/initialize the start of the ACK bit
263   // vector.
264   AppendField<uint32_t>(kRtcpCst2IdentifierWord, buffer);
265   AppendField<uint8_t>(feedback_count_, buffer);
266   // The octet count field is set later, after the total is known.
267   uint8_t* const octet_count_field =
268       ReserveSpace(sizeof(uint8_t), buffer).data();
269   // Start with the minimum required number of bit vector octets.
270   uint8_t* const ack_bitvector =
271       ReserveSpace(kRtcpMinAckBitVectorOctets, buffer).data();
272   int num_ack_bitvector_octets = kRtcpMinAckBitVectorOctets;
273   memset(ack_bitvector, 0, kRtcpMinAckBitVectorOctets);
274 
275   // Set the bits of the ACK bit vector, auto-expanding the number of ACK octets
276   // if necessary (and while there is still room in the buffer).
277   if (!acks_for_next_packet_.empty()) {
278     OSP_DCHECK(AreElementsSortedAndUnique(acks_for_next_packet_));
279     const FrameId first_frame_id = checkpoint_frame_id_ + 2;
280     for (const FrameId& frame_id : acks_for_next_packet_) {
281       const int bit_index = frame_id - first_frame_id;
282       if (bit_index < 0) {
283         continue;
284       }
285       constexpr int kBitsPerOctet = 8;
286       const int octet_index = bit_index / kBitsPerOctet;
287 
288       // If needed, attempt to increase the number of ACK octets.
289       if (octet_index >= num_ack_bitvector_octets) {
290         // Compute how many additional octets are needed.
291         constexpr int kIncrement = sizeof(uint32_t);
292         const int num_additional =
293             DividePositivesRoundingUp(
294                 (octet_index + 1) - num_ack_bitvector_octets, kIncrement) *
295             kIncrement;
296 
297         // If there is not enough room in the buffer to add more ACKs, then do
298         // not continue. Also, if the new total count would exceed the design
299         // limit, do not continue.
300         if (static_cast<int>(buffer->size()) < num_additional) {
301           break;
302         }
303         const int new_count = num_ack_bitvector_octets + num_additional;
304         if (new_count > kRtcpMaxAckBitVectorOctets) {
305           break;
306         }
307 
308         // Reserve the additional space from the buffer, and initialize to zero.
309         memset(ReserveSpace(num_additional, buffer).data(), 0, num_additional);
310         num_ack_bitvector_octets = new_count;
311       }
312 
313       // At this point, the ACK bit vector is valid at |octet_index|. Set the
314       // bit representing the ACK for |frame_id|.
315       const int shift = bit_index % kBitsPerOctet;
316       ack_bitvector[octet_index] |= 1 << shift;
317     }
318   }
319 
320   // Now that the total size of the ACK bit vector is known, go back and set the
321   // octet count field.
322   OSP_DCHECK_LE(num_ack_bitvector_octets, std::numeric_limits<uint8_t>::max());
323   *octet_count_field = num_ack_bitvector_octets;
324 
325   acks_for_next_packet_.clear();
326 }
327 
328 }  // namespace cast
329 }  // namespace openscreen
330