1 // Copyright 2014 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 "media/cast/net/rtcp/rtcp_builder.h"
6
7 #include <stdint.h>
8
9 #include <algorithm>
10 #include <vector>
11
12 #include "base/logging.h"
13 #include "media/cast/net/cast_transport_defines.h"
14 #include "media/cast/net/rtcp/rtcp_defines.h"
15 #include "media/cast/net/rtcp/rtcp_utility.h"
16
17 namespace media {
18 namespace cast {
19 namespace {
20
21 // Max delta is 4095 milliseconds because we need to be able to encode it in
22 // 12 bits.
23 const int64 kMaxWireFormatTimeDeltaMs = INT64_C(0xfff);
24
MergeEventTypeAndTimestampForWireFormat(const CastLoggingEvent & event,const base::TimeDelta & time_delta)25 uint16 MergeEventTypeAndTimestampForWireFormat(
26 const CastLoggingEvent& event,
27 const base::TimeDelta& time_delta) {
28 int64 time_delta_ms = time_delta.InMilliseconds();
29
30 DCHECK_GE(time_delta_ms, 0);
31 DCHECK_LE(time_delta_ms, kMaxWireFormatTimeDeltaMs);
32
33 uint16 time_delta_12_bits =
34 static_cast<uint16>(time_delta_ms & kMaxWireFormatTimeDeltaMs);
35
36 uint16 event_type_4_bits = ConvertEventTypeToWireFormat(event);
37 DCHECK(event_type_4_bits);
38 DCHECK(~(event_type_4_bits & 0xfff0));
39 return (event_type_4_bits << 12) | time_delta_12_bits;
40 }
41
EventTimestampLessThan(const RtcpReceiverEventLogMessage & lhs,const RtcpReceiverEventLogMessage & rhs)42 bool EventTimestampLessThan(const RtcpReceiverEventLogMessage& lhs,
43 const RtcpReceiverEventLogMessage& rhs) {
44 return lhs.event_timestamp < rhs.event_timestamp;
45 }
46
AddReceiverLogEntries(const RtcpReceiverLogMessage & redundancy_receiver_log_message,RtcpReceiverLogMessage * receiver_log_message,size_t * remaining_space,size_t * number_of_frames,size_t * total_number_of_messages_to_send)47 void AddReceiverLogEntries(
48 const RtcpReceiverLogMessage& redundancy_receiver_log_message,
49 RtcpReceiverLogMessage* receiver_log_message,
50 size_t* remaining_space,
51 size_t* number_of_frames,
52 size_t* total_number_of_messages_to_send) {
53 RtcpReceiverLogMessage::const_iterator it =
54 redundancy_receiver_log_message.begin();
55 while (it != redundancy_receiver_log_message.end() &&
56 *remaining_space >=
57 kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize) {
58 receiver_log_message->push_front(*it);
59 size_t num_event_logs = (*remaining_space - kRtcpReceiverFrameLogSize) /
60 kRtcpReceiverEventLogSize;
61 RtcpReceiverEventLogMessages& event_log_messages =
62 receiver_log_message->front().event_log_messages_;
63 if (num_event_logs < event_log_messages.size())
64 event_log_messages.resize(num_event_logs);
65
66 *remaining_space -= kRtcpReceiverFrameLogSize +
67 event_log_messages.size() * kRtcpReceiverEventLogSize;
68 ++number_of_frames;
69 *total_number_of_messages_to_send += event_log_messages.size();
70 ++it;
71 }
72 }
73
74 // A class to build a string representing the NACK list in Cast message.
75 //
76 // The string will look like "23:3-6 25:1,5-6", meaning packets 3 to 6 in frame
77 // 23 are being NACK'ed (i.e. they are missing from the receiver's point of
78 // view) and packets 1, 5 and 6 are missing in frame 25. A frame that is
79 // completely missing will show as "26:65535".
80 class NackStringBuilder {
81 public:
NackStringBuilder()82 NackStringBuilder()
83 : frame_count_(0),
84 packet_count_(0),
85 last_frame_id_(-1),
86 last_packet_id_(-1),
87 contiguous_sequence_(false) {}
~NackStringBuilder()88 ~NackStringBuilder() {}
89
Empty() const90 bool Empty() const { return frame_count_ == 0; }
91
PushFrame(int frame_id)92 void PushFrame(int frame_id) {
93 DCHECK_GE(frame_id, 0);
94 if (frame_count_ > 0) {
95 if (frame_id == last_frame_id_) {
96 return;
97 }
98 if (contiguous_sequence_) {
99 stream_ << "-" << last_packet_id_;
100 }
101 stream_ << ", ";
102 }
103 stream_ << frame_id;
104 last_frame_id_ = frame_id;
105 packet_count_ = 0;
106 contiguous_sequence_ = false;
107 ++frame_count_;
108 }
109
PushPacket(int packet_id)110 void PushPacket(int packet_id) {
111 DCHECK_GE(last_frame_id_, 0);
112 DCHECK_GE(packet_id, 0);
113 if (packet_count_ == 0) {
114 stream_ << ":" << packet_id;
115 } else if (packet_id == last_packet_id_ + 1) {
116 contiguous_sequence_ = true;
117 } else {
118 if (contiguous_sequence_) {
119 stream_ << "-" << last_packet_id_;
120 contiguous_sequence_ = false;
121 }
122 stream_ << "," << packet_id;
123 }
124 ++packet_count_;
125 last_packet_id_ = packet_id;
126 }
127
GetString()128 std::string GetString() {
129 if (contiguous_sequence_) {
130 stream_ << "-" << last_packet_id_;
131 contiguous_sequence_ = false;
132 }
133 return stream_.str();
134 }
135
136 private:
137 std::ostringstream stream_;
138 int frame_count_;
139 int packet_count_;
140 int last_frame_id_;
141 int last_packet_id_;
142 bool contiguous_sequence_;
143 };
144 } // namespace
145
RtcpBuilder(uint32 sending_ssrc)146 RtcpBuilder::RtcpBuilder(uint32 sending_ssrc)
147 : writer_(NULL, 0),
148 ssrc_(sending_ssrc),
149 ptr_of_length_(NULL) {
150 }
151
~RtcpBuilder()152 RtcpBuilder::~RtcpBuilder() {}
153
PatchLengthField()154 void RtcpBuilder::PatchLengthField() {
155 if (ptr_of_length_) {
156 // Back-patch the packet length. The client must have taken
157 // care of proper padding to 32-bit words.
158 int this_packet_length = (writer_.ptr() - ptr_of_length_ - 2);
159 DCHECK_EQ(0, this_packet_length % 4)
160 << "Packets must be a multiple of 32 bits long";
161 *ptr_of_length_ = this_packet_length >> 10;
162 *(ptr_of_length_ + 1) = (this_packet_length >> 2) & 0xFF;
163 ptr_of_length_ = NULL;
164 }
165 }
166
167 // Set the 5-bit value in the 1st byte of the header
168 // and the payload type. Set aside room for the length field,
169 // and make provision for back-patching it.
AddRtcpHeader(RtcpPacketFields payload,int format_or_count)170 void RtcpBuilder::AddRtcpHeader(RtcpPacketFields payload, int format_or_count) {
171 PatchLengthField();
172 writer_.WriteU8(0x80 | (format_or_count & 0x1F));
173 writer_.WriteU8(payload);
174 ptr_of_length_ = writer_.ptr();
175
176 // Initialize length to "clearly illegal".
177 writer_.WriteU16(0xDEAD);
178 }
179
Start()180 void RtcpBuilder::Start() {
181 packet_ = new base::RefCountedData<Packet>;
182 packet_->data.resize(kMaxIpPacketSize);
183 writer_ = base::BigEndianWriter(
184 reinterpret_cast<char*>(&(packet_->data[0])), kMaxIpPacketSize);
185 }
186
Finish()187 PacketRef RtcpBuilder::Finish() {
188 PatchLengthField();
189 packet_->data.resize(kMaxIpPacketSize - writer_.remaining());
190 writer_ = base::BigEndianWriter(NULL, 0);
191 PacketRef ret = packet_;
192 packet_ = NULL;
193 return ret;
194 }
195
BuildRtcpFromReceiver(const RtcpReportBlock * report_block,const RtcpReceiverReferenceTimeReport * rrtr,const RtcpCastMessage * cast_message,const ReceiverRtcpEventSubscriber::RtcpEventMultiMap * rtcp_events,base::TimeDelta target_delay)196 PacketRef RtcpBuilder::BuildRtcpFromReceiver(
197 const RtcpReportBlock* report_block,
198 const RtcpReceiverReferenceTimeReport* rrtr,
199 const RtcpCastMessage* cast_message,
200 const ReceiverRtcpEventSubscriber::RtcpEventMultiMap* rtcp_events,
201 base::TimeDelta target_delay) {
202 Start();
203
204 if (report_block)
205 AddRR(report_block);
206 if (rrtr)
207 AddRrtr(rrtr);
208 if (cast_message)
209 AddCast(cast_message, target_delay);
210 if (rtcp_events)
211 AddReceiverLog(*rtcp_events);
212
213 return Finish();
214 }
215
BuildRtcpFromSender(const RtcpSenderInfo & sender_info)216 PacketRef RtcpBuilder::BuildRtcpFromSender(const RtcpSenderInfo& sender_info) {
217 Start();
218 AddSR(sender_info);
219 return Finish();
220 }
221
AddRR(const RtcpReportBlock * report_block)222 void RtcpBuilder::AddRR(const RtcpReportBlock* report_block) {
223 AddRtcpHeader(kPacketTypeReceiverReport, report_block ? 1 : 0);
224 writer_.WriteU32(ssrc_);
225 if (report_block) {
226 AddReportBlocks(*report_block); // Adds 24 bytes.
227 }
228 }
229
AddReportBlocks(const RtcpReportBlock & report_block)230 void RtcpBuilder::AddReportBlocks(const RtcpReportBlock& report_block) {
231 writer_.WriteU32(report_block.media_ssrc);
232 writer_.WriteU8(report_block.fraction_lost);
233 writer_.WriteU8(report_block.cumulative_lost >> 16);
234 writer_.WriteU8(report_block.cumulative_lost >> 8);
235 writer_.WriteU8(report_block.cumulative_lost);
236
237 // Extended highest seq_no, contain the highest sequence number received.
238 writer_.WriteU32(report_block.extended_high_sequence_number);
239 writer_.WriteU32(report_block.jitter);
240
241 // Last SR timestamp; our NTP time when we received the last report.
242 // This is the value that we read from the send report packet not when we
243 // received it.
244 writer_.WriteU32(report_block.last_sr);
245
246 // Delay since last received report, time since we received the report.
247 writer_.WriteU32(report_block.delay_since_last_sr);
248 }
249
AddRrtr(const RtcpReceiverReferenceTimeReport * rrtr)250 void RtcpBuilder::AddRrtr(const RtcpReceiverReferenceTimeReport* rrtr) {
251 AddRtcpHeader(kPacketTypeXr, 0);
252 writer_.WriteU32(ssrc_); // Add our own SSRC.
253 writer_.WriteU8(4); // Add block type.
254 writer_.WriteU8(0); // Add reserved.
255 writer_.WriteU16(2); // Block length.
256
257 // Add the media (received RTP) SSRC.
258 writer_.WriteU32(rrtr->ntp_seconds);
259 writer_.WriteU32(rrtr->ntp_fraction);
260 }
261
AddCast(const RtcpCastMessage * cast,base::TimeDelta target_delay)262 void RtcpBuilder::AddCast(const RtcpCastMessage* cast,
263 base::TimeDelta target_delay) {
264 // See RTC 4585 Section 6.4 for application specific feedback messages.
265 AddRtcpHeader(kPacketTypePayloadSpecific, 15);
266 writer_.WriteU32(ssrc_); // Add our own SSRC.
267 writer_.WriteU32(cast->media_ssrc); // Remote SSRC.
268 writer_.WriteU32(kCast);
269 writer_.WriteU8(static_cast<uint8>(cast->ack_frame_id));
270 uint8* cast_loss_field_pos = reinterpret_cast<uint8*>(writer_.ptr());
271 writer_.WriteU8(0); // Overwritten with number_of_loss_fields.
272 DCHECK_LE(target_delay.InMilliseconds(),
273 std::numeric_limits<uint16_t>::max());
274 writer_.WriteU16(target_delay.InMilliseconds());
275
276 size_t number_of_loss_fields = 0;
277 size_t max_number_of_loss_fields = std::min<size_t>(
278 kRtcpMaxCastLossFields, writer_.remaining() / 4);
279
280 MissingFramesAndPacketsMap::const_iterator frame_it =
281 cast->missing_frames_and_packets.begin();
282
283 NackStringBuilder nack_string_builder;
284 for (; frame_it != cast->missing_frames_and_packets.end() &&
285 number_of_loss_fields < max_number_of_loss_fields;
286 ++frame_it) {
287 nack_string_builder.PushFrame(frame_it->first);
288 // Iterate through all frames with missing packets.
289 if (frame_it->second.empty()) {
290 // Special case all packets in a frame is missing.
291 writer_.WriteU8(static_cast<uint8>(frame_it->first));
292 writer_.WriteU16(kRtcpCastAllPacketsLost);
293 writer_.WriteU8(0);
294 nack_string_builder.PushPacket(kRtcpCastAllPacketsLost);
295 ++number_of_loss_fields;
296 } else {
297 PacketIdSet::const_iterator packet_it = frame_it->second.begin();
298 while (packet_it != frame_it->second.end()) {
299 uint16 packet_id = *packet_it;
300 // Write frame and packet id to buffer before calculating bitmask.
301 writer_.WriteU8(static_cast<uint8>(frame_it->first));
302 writer_.WriteU16(packet_id);
303 nack_string_builder.PushPacket(packet_id);
304
305 uint8 bitmask = 0;
306 ++packet_it;
307 while (packet_it != frame_it->second.end()) {
308 int shift = static_cast<uint8>(*packet_it - packet_id) - 1;
309 if (shift >= 0 && shift <= 7) {
310 nack_string_builder.PushPacket(*packet_it);
311 bitmask |= (1 << shift);
312 ++packet_it;
313 } else {
314 break;
315 }
316 }
317 writer_.WriteU8(bitmask);
318 ++number_of_loss_fields;
319 }
320 }
321 }
322 VLOG_IF(1, !nack_string_builder.Empty())
323 << "SSRC: " << cast->media_ssrc
324 << ", ACK: " << cast->ack_frame_id
325 << ", NACK: " << nack_string_builder.GetString();
326 DCHECK_LE(number_of_loss_fields, kRtcpMaxCastLossFields);
327 *cast_loss_field_pos = static_cast<uint8>(number_of_loss_fields);
328 }
329
AddSR(const RtcpSenderInfo & sender_info)330 void RtcpBuilder::AddSR(const RtcpSenderInfo& sender_info) {
331 AddRtcpHeader(kPacketTypeSenderReport, 0);
332 writer_.WriteU32(ssrc_);
333 writer_.WriteU32(sender_info.ntp_seconds);
334 writer_.WriteU32(sender_info.ntp_fraction);
335 writer_.WriteU32(sender_info.rtp_timestamp);
336 writer_.WriteU32(sender_info.send_packet_count);
337 writer_.WriteU32(static_cast<uint32>(sender_info.send_octet_count));
338 }
339
340 /*
341 0 1 2 3
342 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
343 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
344 |V=2|P|reserved | PT=XR=207 | length |
345 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
346 | SSRC |
347 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
348 | BT=5 | reserved | block length |
349 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
350 | SSRC1 (SSRC of first receiver) | sub-
351 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
352 | last RR (LRR) | 1
353 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
354 | delay since last RR (DLRR) |
355 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
356 */
AddDlrrRb(const RtcpDlrrReportBlock & dlrr)357 void RtcpBuilder::AddDlrrRb(const RtcpDlrrReportBlock& dlrr) {
358 AddRtcpHeader(kPacketTypeXr, 0);
359 writer_.WriteU32(ssrc_); // Add our own SSRC.
360 writer_.WriteU8(5); // Add block type.
361 writer_.WriteU8(0); // Add reserved.
362 writer_.WriteU16(3); // Block length.
363 writer_.WriteU32(ssrc_); // Add the media (received RTP) SSRC.
364 writer_.WriteU32(dlrr.last_rr);
365 writer_.WriteU32(dlrr.delay_since_last_rr);
366 }
367
AddReceiverLog(const ReceiverRtcpEventSubscriber::RtcpEventMultiMap & rtcp_events)368 void RtcpBuilder::AddReceiverLog(
369 const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events) {
370 size_t total_number_of_messages_to_send = 0;
371 RtcpReceiverLogMessage receiver_log_message;
372
373 if (!GetRtcpReceiverLogMessage(rtcp_events,
374 &receiver_log_message,
375 &total_number_of_messages_to_send)) {
376 return;
377 }
378
379 AddRtcpHeader(kPacketTypeApplicationDefined, kReceiverLogSubtype);
380 writer_.WriteU32(ssrc_); // Add our own SSRC.
381 writer_.WriteU32(kCast);
382
383 while (!receiver_log_message.empty() &&
384 total_number_of_messages_to_send > 0) {
385 RtcpReceiverFrameLogMessage& frame_log_messages(
386 receiver_log_message.front());
387
388 // Add our frame header.
389 writer_.WriteU32(frame_log_messages.rtp_timestamp_);
390 size_t messages_in_frame = frame_log_messages.event_log_messages_.size();
391 if (messages_in_frame > total_number_of_messages_to_send) {
392 // We are running out of space.
393 messages_in_frame = total_number_of_messages_to_send;
394 }
395 // Keep track of how many messages we have left to send.
396 total_number_of_messages_to_send -= messages_in_frame;
397
398 // On the wire format is number of messages - 1.
399 writer_.WriteU8(static_cast<uint8>(messages_in_frame - 1));
400
401 base::TimeTicks event_timestamp_base =
402 frame_log_messages.event_log_messages_.front().event_timestamp;
403 uint32 base_timestamp_ms =
404 (event_timestamp_base - base::TimeTicks()).InMilliseconds();
405 writer_.WriteU8(static_cast<uint8>(base_timestamp_ms >> 16));
406 writer_.WriteU8(static_cast<uint8>(base_timestamp_ms >> 8));
407 writer_.WriteU8(static_cast<uint8>(base_timestamp_ms));
408
409 while (!frame_log_messages.event_log_messages_.empty() &&
410 messages_in_frame > 0) {
411 const RtcpReceiverEventLogMessage& event_message =
412 frame_log_messages.event_log_messages_.front();
413 uint16 event_type_and_timestamp_delta =
414 MergeEventTypeAndTimestampForWireFormat(
415 event_message.type,
416 event_message.event_timestamp - event_timestamp_base);
417 switch (event_message.type) {
418 case FRAME_ACK_SENT:
419 case FRAME_PLAYOUT:
420 case FRAME_DECODED:
421 writer_.WriteU16(
422 static_cast<uint16>(event_message.delay_delta.InMilliseconds()));
423 writer_.WriteU16(event_type_and_timestamp_delta);
424 break;
425 case PACKET_RECEIVED:
426 writer_.WriteU16(event_message.packet_id);
427 writer_.WriteU16(event_type_and_timestamp_delta);
428 break;
429 default:
430 NOTREACHED();
431 }
432 messages_in_frame--;
433 frame_log_messages.event_log_messages_.pop_front();
434 }
435 if (frame_log_messages.event_log_messages_.empty()) {
436 // We sent all messages on this frame; pop the frame header.
437 receiver_log_message.pop_front();
438 }
439 }
440 DCHECK_EQ(total_number_of_messages_to_send, 0u);
441 }
442
GetRtcpReceiverLogMessage(const ReceiverRtcpEventSubscriber::RtcpEventMultiMap & rtcp_events,RtcpReceiverLogMessage * receiver_log_message,size_t * total_number_of_messages_to_send)443 bool RtcpBuilder::GetRtcpReceiverLogMessage(
444 const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events,
445 RtcpReceiverLogMessage* receiver_log_message,
446 size_t* total_number_of_messages_to_send) {
447 size_t number_of_frames = 0;
448 size_t remaining_space =
449 std::min<size_t>(kMaxReceiverLogBytes, writer_.remaining());
450 if (remaining_space < kRtcpCastLogHeaderSize + kRtcpReceiverFrameLogSize +
451 kRtcpReceiverEventLogSize) {
452 return false;
453 }
454
455 // We use this to do event timestamp sorting and truncating for events of
456 // a single frame.
457 std::vector<RtcpReceiverEventLogMessage> sorted_log_messages;
458
459 // Account for the RTCP header for an application-defined packet.
460 remaining_space -= kRtcpCastLogHeaderSize;
461
462 ReceiverRtcpEventSubscriber::RtcpEventMultiMap::const_reverse_iterator rit =
463 rtcp_events.rbegin();
464
465 while (rit != rtcp_events.rend() &&
466 remaining_space >=
467 kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize) {
468 const RtpTimestamp rtp_timestamp = rit->first;
469 RtcpReceiverFrameLogMessage frame_log(rtp_timestamp);
470 remaining_space -= kRtcpReceiverFrameLogSize;
471 ++number_of_frames;
472
473 // Get all events of a single frame.
474 sorted_log_messages.clear();
475 do {
476 RtcpReceiverEventLogMessage event_log_message;
477 event_log_message.type = rit->second.type;
478 event_log_message.event_timestamp = rit->second.timestamp;
479 event_log_message.delay_delta = rit->second.delay_delta;
480 event_log_message.packet_id = rit->second.packet_id;
481 sorted_log_messages.push_back(event_log_message);
482 ++rit;
483 } while (rit != rtcp_events.rend() && rit->first == rtp_timestamp);
484
485 std::sort(sorted_log_messages.begin(),
486 sorted_log_messages.end(),
487 &EventTimestampLessThan);
488
489 // From |sorted_log_messages|, only take events that are no greater than
490 // |kMaxWireFormatTimeDeltaMs| seconds away from the latest event. Events
491 // older than that cannot be encoded over the wire.
492 std::vector<RtcpReceiverEventLogMessage>::reverse_iterator sorted_rit =
493 sorted_log_messages.rbegin();
494 base::TimeTicks first_event_timestamp = sorted_rit->event_timestamp;
495 size_t events_in_frame = 0;
496 while (sorted_rit != sorted_log_messages.rend() &&
497 events_in_frame < kRtcpMaxReceiverLogMessages &&
498 remaining_space >= kRtcpReceiverEventLogSize) {
499 base::TimeDelta delta(first_event_timestamp -
500 sorted_rit->event_timestamp);
501 if (delta.InMilliseconds() > kMaxWireFormatTimeDeltaMs)
502 break;
503 frame_log.event_log_messages_.push_front(*sorted_rit);
504 ++events_in_frame;
505 ++*total_number_of_messages_to_send;
506 remaining_space -= kRtcpReceiverEventLogSize;
507 ++sorted_rit;
508 }
509
510 receiver_log_message->push_front(frame_log);
511 }
512
513 rtcp_events_history_.push_front(*receiver_log_message);
514
515 // We don't try to match RTP timestamps of redundancy frame logs with those
516 // from the newest set (which would save the space of an extra RTP timestamp
517 // over the wire). Unless the redundancy frame logs are very recent, it's
518 // unlikely there will be a match anyway.
519 if (rtcp_events_history_.size() > kFirstRedundancyOffset) {
520 // Add first redundnacy messages, if enough space remaining
521 AddReceiverLogEntries(rtcp_events_history_[kFirstRedundancyOffset],
522 receiver_log_message,
523 &remaining_space,
524 &number_of_frames,
525 total_number_of_messages_to_send);
526 }
527
528 if (rtcp_events_history_.size() > kSecondRedundancyOffset) {
529 // Add second redundancy messages, if enough space remaining
530 AddReceiverLogEntries(rtcp_events_history_[kSecondRedundancyOffset],
531 receiver_log_message,
532 &remaining_space,
533 &number_of_frames,
534 total_number_of_messages_to_send);
535 }
536
537 if (rtcp_events_history_.size() > kReceiveLogMessageHistorySize) {
538 rtcp_events_history_.pop_back();
539 }
540
541 DCHECK_LE(rtcp_events_history_.size(), kReceiveLogMessageHistorySize);
542
543 VLOG(3) << "number of frames: " << number_of_frames;
544 VLOG(3) << "total messages to send: " << *total_number_of_messages_to_send;
545 return number_of_frames > 0;
546 }
547
548 } // namespace cast
549 } // namespace media
550