xref: /aosp_15_r20/external/openscreen/cast/streaming/compound_rtcp_parser.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1*3f982cf4SFabien Sanglard // Copyright 2019 The Chromium Authors. All rights reserved.
2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be
3*3f982cf4SFabien Sanglard // found in the LICENSE file.
4*3f982cf4SFabien Sanglard 
5*3f982cf4SFabien Sanglard #include "cast/streaming/compound_rtcp_parser.h"
6*3f982cf4SFabien Sanglard 
7*3f982cf4SFabien Sanglard #include <algorithm>
8*3f982cf4SFabien Sanglard 
9*3f982cf4SFabien Sanglard #include "cast/streaming/packet_util.h"
10*3f982cf4SFabien Sanglard #include "cast/streaming/rtcp_session.h"
11*3f982cf4SFabien Sanglard #include "util/osp_logging.h"
12*3f982cf4SFabien Sanglard #include "util/std_util.h"
13*3f982cf4SFabien Sanglard 
14*3f982cf4SFabien Sanglard namespace openscreen {
15*3f982cf4SFabien Sanglard namespace cast {
16*3f982cf4SFabien Sanglard 
17*3f982cf4SFabien Sanglard namespace {
18*3f982cf4SFabien Sanglard 
19*3f982cf4SFabien Sanglard // Use the Clock's minimum time value (an impossible value, waaaaay before epoch
20*3f982cf4SFabien Sanglard // time) to represent unset time_point values.
21*3f982cf4SFabien Sanglard constexpr auto kNullTimePoint = Clock::time_point::min();
22*3f982cf4SFabien Sanglard 
23*3f982cf4SFabien Sanglard // Canonicalizes the just-parsed list of packet-specific NACKs so that the
24*3f982cf4SFabien Sanglard // CompoundRtcpParser::Client can make several simplifying assumptions when
25*3f982cf4SFabien Sanglard // processing the results.
CanonicalizePacketNackVector(std::vector<PacketNack> * packets)26*3f982cf4SFabien Sanglard void CanonicalizePacketNackVector(std::vector<PacketNack>* packets) {
27*3f982cf4SFabien Sanglard   // First, sort all elements. The sort order is the normal lexicographical
28*3f982cf4SFabien Sanglard   // ordering, with one exception: The special kAllPacketsLost packet_id value
29*3f982cf4SFabien Sanglard   // should be treated as coming before all others. This special sort order
30*3f982cf4SFabien Sanglard   // allows the filtering algorithm below to be simpler, and only require one
31*3f982cf4SFabien Sanglard   // pass; and the final result will be the normal lexicographically-sorted
32*3f982cf4SFabien Sanglard   // output the CompoundRtcpParser::Client expects.
33*3f982cf4SFabien Sanglard   std::sort(packets->begin(), packets->end(),
34*3f982cf4SFabien Sanglard             [](const PacketNack& a, const PacketNack& b) {
35*3f982cf4SFabien Sanglard               // Since the comparator is a hot code path, use a simple modular
36*3f982cf4SFabien Sanglard               // arithmetic trick in lieu of extra branching: When comparing the
37*3f982cf4SFabien Sanglard               // tuples, map all packet_id values to packet_id + 1, mod 0x10000.
38*3f982cf4SFabien Sanglard               // This results in the desired sorting behavior since
39*3f982cf4SFabien Sanglard               // kAllPacketsLost (0xffff) wraps-around to 0x0000, and all other
40*3f982cf4SFabien Sanglard               // values become N + 1.
41*3f982cf4SFabien Sanglard               static_assert(static_cast<FramePacketId>(kAllPacketsLost + 1) <
42*3f982cf4SFabien Sanglard                                 FramePacketId{0x0000 + 1},
43*3f982cf4SFabien Sanglard                             "comparison requires integer wrap-around");
44*3f982cf4SFabien Sanglard               return PacketNack{a.frame_id,
45*3f982cf4SFabien Sanglard                                 static_cast<FramePacketId>(a.packet_id + 1)} <
46*3f982cf4SFabien Sanglard                      PacketNack{b.frame_id,
47*3f982cf4SFabien Sanglard                                 static_cast<FramePacketId>(b.packet_id + 1)};
48*3f982cf4SFabien Sanglard             });
49*3f982cf4SFabien Sanglard 
50*3f982cf4SFabien Sanglard   // De-duplicate elements. Two possible cases:
51*3f982cf4SFabien Sanglard   //
52*3f982cf4SFabien Sanglard   //   1. Identical elements (same FrameId+FramePacketId).
53*3f982cf4SFabien Sanglard   //   2. If there are any elements with kAllPacketsLost as the packet ID,
54*3f982cf4SFabien Sanglard   //      prune-out all other elements having the same frame ID, as they are
55*3f982cf4SFabien Sanglard   //      redundant.
56*3f982cf4SFabien Sanglard   //
57*3f982cf4SFabien Sanglard   // This is done by walking forwards over the sorted vector and deciding which
58*3f982cf4SFabien Sanglard   // elements to keep. Those that are kept are stacked-up at the front of the
59*3f982cf4SFabien Sanglard   // vector. After the "to-keep" pass, the vector is truncated to remove the
60*3f982cf4SFabien Sanglard   // left-over garbage at the end.
61*3f982cf4SFabien Sanglard   auto have_it = packets->begin();
62*3f982cf4SFabien Sanglard   if (have_it != packets->end()) {
63*3f982cf4SFabien Sanglard     auto kept_it = have_it;  // Always keep the first element.
64*3f982cf4SFabien Sanglard     for (++have_it; have_it != packets->end(); ++have_it) {
65*3f982cf4SFabien Sanglard       if (have_it->frame_id != kept_it->frame_id ||
66*3f982cf4SFabien Sanglard           (kept_it->packet_id != kAllPacketsLost &&
67*3f982cf4SFabien Sanglard            have_it->packet_id != kept_it->packet_id)) {  // Keep it.
68*3f982cf4SFabien Sanglard         ++kept_it;
69*3f982cf4SFabien Sanglard         *kept_it = *have_it;
70*3f982cf4SFabien Sanglard       }
71*3f982cf4SFabien Sanglard     }
72*3f982cf4SFabien Sanglard     packets->erase(++kept_it, packets->end());
73*3f982cf4SFabien Sanglard   }
74*3f982cf4SFabien Sanglard }
75*3f982cf4SFabien Sanglard 
76*3f982cf4SFabien Sanglard }  // namespace
77*3f982cf4SFabien Sanglard 
CompoundRtcpParser(RtcpSession * session,CompoundRtcpParser::Client * client)78*3f982cf4SFabien Sanglard CompoundRtcpParser::CompoundRtcpParser(RtcpSession* session,
79*3f982cf4SFabien Sanglard                                        CompoundRtcpParser::Client* client)
80*3f982cf4SFabien Sanglard     : session_(session),
81*3f982cf4SFabien Sanglard       client_(client),
82*3f982cf4SFabien Sanglard       latest_receiver_timestamp_(kNullTimePoint) {
83*3f982cf4SFabien Sanglard   OSP_DCHECK(session_);
84*3f982cf4SFabien Sanglard   OSP_DCHECK(client_);
85*3f982cf4SFabien Sanglard }
86*3f982cf4SFabien Sanglard 
87*3f982cf4SFabien Sanglard CompoundRtcpParser::~CompoundRtcpParser() = default;
88*3f982cf4SFabien Sanglard 
Parse(absl::Span<const uint8_t> buffer,FrameId max_feedback_frame_id)89*3f982cf4SFabien Sanglard bool CompoundRtcpParser::Parse(absl::Span<const uint8_t> buffer,
90*3f982cf4SFabien Sanglard                                FrameId max_feedback_frame_id) {
91*3f982cf4SFabien Sanglard   // These will contain the results from the various ParseXYZ() methods. None of
92*3f982cf4SFabien Sanglard   // the results will be dispatched to the Client until the entire parse
93*3f982cf4SFabien Sanglard   // succeeds.
94*3f982cf4SFabien Sanglard   Clock::time_point receiver_reference_time = kNullTimePoint;
95*3f982cf4SFabien Sanglard   absl::optional<RtcpReportBlock> receiver_report;
96*3f982cf4SFabien Sanglard   FrameId checkpoint_frame_id;
97*3f982cf4SFabien Sanglard   std::chrono::milliseconds target_playout_delay{};
98*3f982cf4SFabien Sanglard   std::vector<FrameId> received_frames;
99*3f982cf4SFabien Sanglard   std::vector<PacketNack> packet_nacks;
100*3f982cf4SFabien Sanglard   bool picture_loss_indicator = false;
101*3f982cf4SFabien Sanglard 
102*3f982cf4SFabien Sanglard   // The data contained in |buffer| can be a "compound packet," which means that
103*3f982cf4SFabien Sanglard   // it can be the concatenation of multiple RTCP packets. The loop here
104*3f982cf4SFabien Sanglard   // processes each one-by-one.
105*3f982cf4SFabien Sanglard   while (!buffer.empty()) {
106*3f982cf4SFabien Sanglard     const auto header = RtcpCommonHeader::Parse(buffer);
107*3f982cf4SFabien Sanglard     if (!header) {
108*3f982cf4SFabien Sanglard       return false;
109*3f982cf4SFabien Sanglard     }
110*3f982cf4SFabien Sanglard     buffer.remove_prefix(kRtcpCommonHeaderSize);
111*3f982cf4SFabien Sanglard     if (static_cast<int>(buffer.size()) < header->payload_size) {
112*3f982cf4SFabien Sanglard       return false;
113*3f982cf4SFabien Sanglard     }
114*3f982cf4SFabien Sanglard     const absl::Span<const uint8_t> payload =
115*3f982cf4SFabien Sanglard         buffer.subspan(0, header->payload_size);
116*3f982cf4SFabien Sanglard     buffer.remove_prefix(header->payload_size);
117*3f982cf4SFabien Sanglard 
118*3f982cf4SFabien Sanglard     switch (header->packet_type) {
119*3f982cf4SFabien Sanglard       case RtcpPacketType::kReceiverReport:
120*3f982cf4SFabien Sanglard         if (!ParseReceiverReport(payload, header->with.report_count,
121*3f982cf4SFabien Sanglard                                  &receiver_report)) {
122*3f982cf4SFabien Sanglard           return false;
123*3f982cf4SFabien Sanglard         }
124*3f982cf4SFabien Sanglard         break;
125*3f982cf4SFabien Sanglard 
126*3f982cf4SFabien Sanglard       case RtcpPacketType::kPayloadSpecific:
127*3f982cf4SFabien Sanglard         switch (header->with.subtype) {
128*3f982cf4SFabien Sanglard           case RtcpSubtype::kPictureLossIndicator:
129*3f982cf4SFabien Sanglard             if (!ParsePictureLossIndicator(payload, &picture_loss_indicator)) {
130*3f982cf4SFabien Sanglard               return false;
131*3f982cf4SFabien Sanglard             }
132*3f982cf4SFabien Sanglard             break;
133*3f982cf4SFabien Sanglard           case RtcpSubtype::kFeedback:
134*3f982cf4SFabien Sanglard             if (!ParseFeedback(payload, max_feedback_frame_id,
135*3f982cf4SFabien Sanglard                                &checkpoint_frame_id, &target_playout_delay,
136*3f982cf4SFabien Sanglard                                &received_frames, &packet_nacks)) {
137*3f982cf4SFabien Sanglard               return false;
138*3f982cf4SFabien Sanglard             }
139*3f982cf4SFabien Sanglard             break;
140*3f982cf4SFabien Sanglard           default:
141*3f982cf4SFabien Sanglard             // Ignore: Unimplemented or not part of the Cast Streaming spec.
142*3f982cf4SFabien Sanglard             break;
143*3f982cf4SFabien Sanglard         }
144*3f982cf4SFabien Sanglard         break;
145*3f982cf4SFabien Sanglard 
146*3f982cf4SFabien Sanglard       case RtcpPacketType::kExtendedReports:
147*3f982cf4SFabien Sanglard         if (!ParseExtendedReports(payload, &receiver_reference_time)) {
148*3f982cf4SFabien Sanglard           return false;
149*3f982cf4SFabien Sanglard         }
150*3f982cf4SFabien Sanglard         break;
151*3f982cf4SFabien Sanglard 
152*3f982cf4SFabien Sanglard       default:
153*3f982cf4SFabien Sanglard         // Ignored, unimplemented or not part of the Cast Streaming spec.
154*3f982cf4SFabien Sanglard         break;
155*3f982cf4SFabien Sanglard     }
156*3f982cf4SFabien Sanglard   }
157*3f982cf4SFabien Sanglard 
158*3f982cf4SFabien Sanglard   // A well-behaved Cast Streaming Receiver will always include a reference time
159*3f982cf4SFabien Sanglard   // report. This essentially "timestamps" the RTCP packets just parsed.
160*3f982cf4SFabien Sanglard   // However, the spec does not explicitly require this be included. When it is
161*3f982cf4SFabien Sanglard   // present, improve the stability of the system by ignoring stale/out-of-order
162*3f982cf4SFabien Sanglard   // RTCP packets.
163*3f982cf4SFabien Sanglard   if (receiver_reference_time != kNullTimePoint) {
164*3f982cf4SFabien Sanglard     // If the packet is out-of-order (e.g., it got delayed/shuffled when going
165*3f982cf4SFabien Sanglard     // through the network), just ignore it. Since RTCP packets always include
166*3f982cf4SFabien Sanglard     // all the necessary current state from the peer, dropping them does not
167*3f982cf4SFabien Sanglard     // mean important signals will be lost. In fact, it can actually be harmful
168*3f982cf4SFabien Sanglard     // to process compound RTCP packets out-of-order.
169*3f982cf4SFabien Sanglard     if (latest_receiver_timestamp_ != kNullTimePoint &&
170*3f982cf4SFabien Sanglard         receiver_reference_time < latest_receiver_timestamp_) {
171*3f982cf4SFabien Sanglard       return true;
172*3f982cf4SFabien Sanglard     }
173*3f982cf4SFabien Sanglard     latest_receiver_timestamp_ = receiver_reference_time;
174*3f982cf4SFabien Sanglard     client_->OnReceiverReferenceTimeAdvanced(latest_receiver_timestamp_);
175*3f982cf4SFabien Sanglard   }
176*3f982cf4SFabien Sanglard 
177*3f982cf4SFabien Sanglard   // At this point, the packet is known to be well-formed. Dispatch events of
178*3f982cf4SFabien Sanglard   // interest to the Client.
179*3f982cf4SFabien Sanglard   if (receiver_report) {
180*3f982cf4SFabien Sanglard     client_->OnReceiverReport(*receiver_report);
181*3f982cf4SFabien Sanglard   }
182*3f982cf4SFabien Sanglard   if (!checkpoint_frame_id.is_null()) {
183*3f982cf4SFabien Sanglard     client_->OnReceiverCheckpoint(checkpoint_frame_id, target_playout_delay);
184*3f982cf4SFabien Sanglard   }
185*3f982cf4SFabien Sanglard   if (!received_frames.empty()) {
186*3f982cf4SFabien Sanglard     OSP_DCHECK(AreElementsSortedAndUnique(received_frames));
187*3f982cf4SFabien Sanglard     client_->OnReceiverHasFrames(std::move(received_frames));
188*3f982cf4SFabien Sanglard   }
189*3f982cf4SFabien Sanglard   CanonicalizePacketNackVector(&packet_nacks);
190*3f982cf4SFabien Sanglard   if (!packet_nacks.empty()) {
191*3f982cf4SFabien Sanglard     client_->OnReceiverIsMissingPackets(std::move(packet_nacks));
192*3f982cf4SFabien Sanglard   }
193*3f982cf4SFabien Sanglard   if (picture_loss_indicator) {
194*3f982cf4SFabien Sanglard     client_->OnReceiverIndicatesPictureLoss();
195*3f982cf4SFabien Sanglard   }
196*3f982cf4SFabien Sanglard 
197*3f982cf4SFabien Sanglard   return true;
198*3f982cf4SFabien Sanglard }
199*3f982cf4SFabien Sanglard 
ParseReceiverReport(absl::Span<const uint8_t> in,int num_report_blocks,absl::optional<RtcpReportBlock> * receiver_report)200*3f982cf4SFabien Sanglard bool CompoundRtcpParser::ParseReceiverReport(
201*3f982cf4SFabien Sanglard     absl::Span<const uint8_t> in,
202*3f982cf4SFabien Sanglard     int num_report_blocks,
203*3f982cf4SFabien Sanglard     absl::optional<RtcpReportBlock>* receiver_report) {
204*3f982cf4SFabien Sanglard   if (in.size() < kRtcpReceiverReportSize) {
205*3f982cf4SFabien Sanglard     return false;
206*3f982cf4SFabien Sanglard   }
207*3f982cf4SFabien Sanglard   if (ConsumeField<uint32_t>(&in) == session_->receiver_ssrc()) {
208*3f982cf4SFabien Sanglard     *receiver_report = RtcpReportBlock::ParseOne(in, num_report_blocks,
209*3f982cf4SFabien Sanglard                                                  session_->sender_ssrc());
210*3f982cf4SFabien Sanglard   }
211*3f982cf4SFabien Sanglard   return true;
212*3f982cf4SFabien Sanglard }
213*3f982cf4SFabien Sanglard 
ParseFeedback(absl::Span<const uint8_t> in,FrameId max_feedback_frame_id,FrameId * checkpoint_frame_id,std::chrono::milliseconds * target_playout_delay,std::vector<FrameId> * received_frames,std::vector<PacketNack> * packet_nacks)214*3f982cf4SFabien Sanglard bool CompoundRtcpParser::ParseFeedback(
215*3f982cf4SFabien Sanglard     absl::Span<const uint8_t> in,
216*3f982cf4SFabien Sanglard     FrameId max_feedback_frame_id,
217*3f982cf4SFabien Sanglard     FrameId* checkpoint_frame_id,
218*3f982cf4SFabien Sanglard     std::chrono::milliseconds* target_playout_delay,
219*3f982cf4SFabien Sanglard     std::vector<FrameId>* received_frames,
220*3f982cf4SFabien Sanglard     std::vector<PacketNack>* packet_nacks) {
221*3f982cf4SFabien Sanglard   OSP_DCHECK(!max_feedback_frame_id.is_null());
222*3f982cf4SFabien Sanglard 
223*3f982cf4SFabien Sanglard   if (static_cast<int>(in.size()) < kRtcpFeedbackHeaderSize) {
224*3f982cf4SFabien Sanglard     return false;
225*3f982cf4SFabien Sanglard   }
226*3f982cf4SFabien Sanglard   if (ConsumeField<uint32_t>(&in) != session_->receiver_ssrc() ||
227*3f982cf4SFabien Sanglard       ConsumeField<uint32_t>(&in) != session_->sender_ssrc()) {
228*3f982cf4SFabien Sanglard     return true;  // Ignore report from mismatched SSRC(s).
229*3f982cf4SFabien Sanglard   }
230*3f982cf4SFabien Sanglard   if (ConsumeField<uint32_t>(&in) != kRtcpCastIdentifierWord) {
231*3f982cf4SFabien Sanglard     return false;
232*3f982cf4SFabien Sanglard   }
233*3f982cf4SFabien Sanglard 
234*3f982cf4SFabien Sanglard   const FrameId feedback_frame_id =
235*3f982cf4SFabien Sanglard       max_feedback_frame_id.ExpandLessThanOrEqual(ConsumeField<uint8_t>(&in));
236*3f982cf4SFabien Sanglard   const int loss_field_count = ConsumeField<uint8_t>(&in);
237*3f982cf4SFabien Sanglard   const auto playout_delay =
238*3f982cf4SFabien Sanglard       std::chrono::milliseconds(ConsumeField<uint16_t>(&in));
239*3f982cf4SFabien Sanglard   // Don't process feedback that would move the checkpoint backwards. The Client
240*3f982cf4SFabien Sanglard   // makes assumptions about what frame data and other tracking state can be
241*3f982cf4SFabien Sanglard   // discarded based on a monotonically non-decreasing checkpoint FrameId.
242*3f982cf4SFabien Sanglard   if (!checkpoint_frame_id->is_null() &&
243*3f982cf4SFabien Sanglard       *checkpoint_frame_id > feedback_frame_id) {
244*3f982cf4SFabien Sanglard     return true;
245*3f982cf4SFabien Sanglard   }
246*3f982cf4SFabien Sanglard   *checkpoint_frame_id = feedback_frame_id;
247*3f982cf4SFabien Sanglard   *target_playout_delay = playout_delay;
248*3f982cf4SFabien Sanglard   received_frames->clear();
249*3f982cf4SFabien Sanglard   packet_nacks->clear();
250*3f982cf4SFabien Sanglard   if (static_cast<int>(in.size()) <
251*3f982cf4SFabien Sanglard       (kRtcpFeedbackLossFieldSize * loss_field_count)) {
252*3f982cf4SFabien Sanglard     return false;
253*3f982cf4SFabien Sanglard   }
254*3f982cf4SFabien Sanglard 
255*3f982cf4SFabien Sanglard   // Parse the NACKs.
256*3f982cf4SFabien Sanglard   for (int i = 0; i < loss_field_count; ++i) {
257*3f982cf4SFabien Sanglard     const FrameId frame_id =
258*3f982cf4SFabien Sanglard         feedback_frame_id.ExpandGreaterThan(ConsumeField<uint8_t>(&in));
259*3f982cf4SFabien Sanglard     FramePacketId packet_id = ConsumeField<uint16_t>(&in);
260*3f982cf4SFabien Sanglard     uint8_t bits = ConsumeField<uint8_t>(&in);
261*3f982cf4SFabien Sanglard     packet_nacks->push_back(PacketNack{frame_id, packet_id});
262*3f982cf4SFabien Sanglard 
263*3f982cf4SFabien Sanglard     if (packet_id != kAllPacketsLost) {
264*3f982cf4SFabien Sanglard       // Translate each set bit in the bit vector into another missing
265*3f982cf4SFabien Sanglard       // FramePacketId.
266*3f982cf4SFabien Sanglard       while (bits) {
267*3f982cf4SFabien Sanglard         ++packet_id;
268*3f982cf4SFabien Sanglard         if (bits & 1) {
269*3f982cf4SFabien Sanglard           packet_nacks->push_back(PacketNack{frame_id, packet_id});
270*3f982cf4SFabien Sanglard         }
271*3f982cf4SFabien Sanglard         bits >>= 1;
272*3f982cf4SFabien Sanglard       }
273*3f982cf4SFabien Sanglard     }
274*3f982cf4SFabien Sanglard   }
275*3f982cf4SFabien Sanglard 
276*3f982cf4SFabien Sanglard   // Parse the optional CST2 feedback (frame-level ACKs).
277*3f982cf4SFabien Sanglard   if (static_cast<int>(in.size()) < kRtcpFeedbackAckHeaderSize ||
278*3f982cf4SFabien Sanglard       ConsumeField<uint32_t>(&in) != kRtcpCst2IdentifierWord) {
279*3f982cf4SFabien Sanglard     // Optional CST2 extended feedback is not present. For backwards-
280*3f982cf4SFabien Sanglard     // compatibility reasons, do not consider any extra "garbage" in the packet
281*3f982cf4SFabien Sanglard     // that doesn't match 'CST2' as corrupted input.
282*3f982cf4SFabien Sanglard     return true;
283*3f982cf4SFabien Sanglard   }
284*3f982cf4SFabien Sanglard   // Skip over the "Feedback Count" field. It's currently unused, though it
285*3f982cf4SFabien Sanglard   // might be useful for event tracing later...
286*3f982cf4SFabien Sanglard   in.remove_prefix(sizeof(uint8_t));
287*3f982cf4SFabien Sanglard   const int ack_bitvector_octet_count = ConsumeField<uint8_t>(&in);
288*3f982cf4SFabien Sanglard   if (static_cast<int>(in.size()) < ack_bitvector_octet_count) {
289*3f982cf4SFabien Sanglard     return false;
290*3f982cf4SFabien Sanglard   }
291*3f982cf4SFabien Sanglard   // Translate each set bit in the bit vector into a FrameId. See the
292*3f982cf4SFabien Sanglard   // explanation of this wire format in rtp_defines.h for where the "plus two"
293*3f982cf4SFabien Sanglard   // comes from.
294*3f982cf4SFabien Sanglard   FrameId starting_frame_id = feedback_frame_id + 2;
295*3f982cf4SFabien Sanglard   for (int i = 0; i < ack_bitvector_octet_count; ++i) {
296*3f982cf4SFabien Sanglard     uint8_t bits = ConsumeField<uint8_t>(&in);
297*3f982cf4SFabien Sanglard     FrameId frame_id = starting_frame_id;
298*3f982cf4SFabien Sanglard     while (bits) {
299*3f982cf4SFabien Sanglard       if (bits & 1) {
300*3f982cf4SFabien Sanglard         received_frames->push_back(frame_id);
301*3f982cf4SFabien Sanglard       }
302*3f982cf4SFabien Sanglard       ++frame_id;
303*3f982cf4SFabien Sanglard       bits >>= 1;
304*3f982cf4SFabien Sanglard     }
305*3f982cf4SFabien Sanglard     constexpr int kBitsPerOctet = 8;
306*3f982cf4SFabien Sanglard     starting_frame_id += kBitsPerOctet;
307*3f982cf4SFabien Sanglard   }
308*3f982cf4SFabien Sanglard 
309*3f982cf4SFabien Sanglard   return true;
310*3f982cf4SFabien Sanglard }
311*3f982cf4SFabien Sanglard 
ParseExtendedReports(absl::Span<const uint8_t> in,Clock::time_point * receiver_reference_time)312*3f982cf4SFabien Sanglard bool CompoundRtcpParser::ParseExtendedReports(
313*3f982cf4SFabien Sanglard     absl::Span<const uint8_t> in,
314*3f982cf4SFabien Sanglard     Clock::time_point* receiver_reference_time) {
315*3f982cf4SFabien Sanglard   if (static_cast<int>(in.size()) < kRtcpExtendedReportHeaderSize) {
316*3f982cf4SFabien Sanglard     return false;
317*3f982cf4SFabien Sanglard   }
318*3f982cf4SFabien Sanglard   if (ConsumeField<uint32_t>(&in) != session_->receiver_ssrc()) {
319*3f982cf4SFabien Sanglard     return true;  // Ignore report from unknown receiver.
320*3f982cf4SFabien Sanglard   }
321*3f982cf4SFabien Sanglard 
322*3f982cf4SFabien Sanglard   while (!in.empty()) {
323*3f982cf4SFabien Sanglard     // All extended report types have the same 4-byte subheader.
324*3f982cf4SFabien Sanglard     if (static_cast<int>(in.size()) < kRtcpExtendedReportBlockHeaderSize) {
325*3f982cf4SFabien Sanglard       return false;
326*3f982cf4SFabien Sanglard     }
327*3f982cf4SFabien Sanglard     const uint8_t block_type = ConsumeField<uint8_t>(&in);
328*3f982cf4SFabien Sanglard     in.remove_prefix(sizeof(uint8_t));  // Skip the "reserved" byte.
329*3f982cf4SFabien Sanglard     const int block_data_size =
330*3f982cf4SFabien Sanglard         static_cast<int>(ConsumeField<uint16_t>(&in)) * 4;
331*3f982cf4SFabien Sanglard     if (static_cast<int>(in.size()) < block_data_size) {
332*3f982cf4SFabien Sanglard       return false;
333*3f982cf4SFabien Sanglard     }
334*3f982cf4SFabien Sanglard     if (block_type == kRtcpReceiverReferenceTimeReportBlockType) {
335*3f982cf4SFabien Sanglard       if (block_data_size != sizeof(uint64_t)) {
336*3f982cf4SFabien Sanglard         return false;  // Length field must always be 2 words.
337*3f982cf4SFabien Sanglard       }
338*3f982cf4SFabien Sanglard       *receiver_reference_time = session_->ntp_converter().ToLocalTime(
339*3f982cf4SFabien Sanglard           ReadBigEndian<uint64_t>(in.data()));
340*3f982cf4SFabien Sanglard     } else {
341*3f982cf4SFabien Sanglard       // Ignore any other type of extended report.
342*3f982cf4SFabien Sanglard     }
343*3f982cf4SFabien Sanglard     in.remove_prefix(block_data_size);
344*3f982cf4SFabien Sanglard   }
345*3f982cf4SFabien Sanglard 
346*3f982cf4SFabien Sanglard   return true;
347*3f982cf4SFabien Sanglard }
348*3f982cf4SFabien Sanglard 
ParsePictureLossIndicator(absl::Span<const uint8_t> in,bool * picture_loss_indicator)349*3f982cf4SFabien Sanglard bool CompoundRtcpParser::ParsePictureLossIndicator(
350*3f982cf4SFabien Sanglard     absl::Span<const uint8_t> in,
351*3f982cf4SFabien Sanglard     bool* picture_loss_indicator) {
352*3f982cf4SFabien Sanglard   if (static_cast<int>(in.size()) < kRtcpPictureLossIndicatorHeaderSize) {
353*3f982cf4SFabien Sanglard     return false;
354*3f982cf4SFabien Sanglard   }
355*3f982cf4SFabien Sanglard   // Only set the flag if the PLI is from the Receiver and to this Sender.
356*3f982cf4SFabien Sanglard   if (ConsumeField<uint32_t>(&in) == session_->receiver_ssrc() &&
357*3f982cf4SFabien Sanglard       ConsumeField<uint32_t>(&in) == session_->sender_ssrc()) {
358*3f982cf4SFabien Sanglard     *picture_loss_indicator = true;
359*3f982cf4SFabien Sanglard   }
360*3f982cf4SFabien Sanglard   return true;
361*3f982cf4SFabien Sanglard }
362*3f982cf4SFabien Sanglard 
363*3f982cf4SFabien Sanglard CompoundRtcpParser::Client::Client() = default;
364*3f982cf4SFabien Sanglard CompoundRtcpParser::Client::~Client() = default;
OnReceiverReferenceTimeAdvanced(Clock::time_point reference_time)365*3f982cf4SFabien Sanglard void CompoundRtcpParser::Client::OnReceiverReferenceTimeAdvanced(
366*3f982cf4SFabien Sanglard     Clock::time_point reference_time) {}
OnReceiverReport(const RtcpReportBlock & receiver_report)367*3f982cf4SFabien Sanglard void CompoundRtcpParser::Client::OnReceiverReport(
368*3f982cf4SFabien Sanglard     const RtcpReportBlock& receiver_report) {}
OnReceiverIndicatesPictureLoss()369*3f982cf4SFabien Sanglard void CompoundRtcpParser::Client::OnReceiverIndicatesPictureLoss() {}
OnReceiverCheckpoint(FrameId frame_id,std::chrono::milliseconds playout_delay)370*3f982cf4SFabien Sanglard void CompoundRtcpParser::Client::OnReceiverCheckpoint(
371*3f982cf4SFabien Sanglard     FrameId frame_id,
372*3f982cf4SFabien Sanglard     std::chrono::milliseconds playout_delay) {}
OnReceiverHasFrames(std::vector<FrameId> acks)373*3f982cf4SFabien Sanglard void CompoundRtcpParser::Client::OnReceiverHasFrames(
374*3f982cf4SFabien Sanglard     std::vector<FrameId> acks) {}
OnReceiverIsMissingPackets(std::vector<PacketNack> nacks)375*3f982cf4SFabien Sanglard void CompoundRtcpParser::Client::OnReceiverIsMissingPackets(
376*3f982cf4SFabien Sanglard     std::vector<PacketNack> nacks) {}
377*3f982cf4SFabien Sanglard 
378*3f982cf4SFabien Sanglard }  // namespace cast
379*3f982cf4SFabien Sanglard }  // namespace openscreen
380