Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / media / cast / net / rtcp / rtcp_builder.cc
index f2626d3..b4e58c3 100644 (file)
 
 #include "media/cast/net/rtcp/rtcp_builder.h"
 
+#include <stdint.h>
+
 #include <algorithm>
-#include <string>
 #include <vector>
 
-#include "base/big_endian.h"
 #include "base/logging.h"
 #include "media/cast/net/cast_transport_defines.h"
-#include "media/cast/net/pacing/paced_sender.h"
+#include "media/cast/net/rtcp/rtcp_defines.h"
+#include "media/cast/net/rtcp/rtcp_utility.h"
 
 namespace media {
 namespace cast {
+namespace {
+
+// Max delta is 4095 milliseconds because we need to be able to encode it in
+// 12 bits.
+const int64 kMaxWireFormatTimeDeltaMs = INT64_C(0xfff);
+
+uint16 MergeEventTypeAndTimestampForWireFormat(
+    const CastLoggingEvent& event,
+    const base::TimeDelta& time_delta) {
+  int64 time_delta_ms = time_delta.InMilliseconds();
+
+  DCHECK_GE(time_delta_ms, 0);
+  DCHECK_LE(time_delta_ms, kMaxWireFormatTimeDeltaMs);
+
+  uint16 time_delta_12_bits =
+      static_cast<uint16>(time_delta_ms & kMaxWireFormatTimeDeltaMs);
 
-RtcpBuilder::RtcpBuilder(PacedSender* const outgoing_transport)
-    : transport_(outgoing_transport),
-      ssrc_(0) {
+  uint16 event_type_4_bits = ConvertEventTypeToWireFormat(event);
+  DCHECK(event_type_4_bits);
+  DCHECK(~(event_type_4_bits & 0xfff0));
+  return (event_type_4_bits << 12) | time_delta_12_bits;
 }
 
-RtcpBuilder::~RtcpBuilder() {}
+bool EventTimestampLessThan(const RtcpReceiverEventLogMessage& lhs,
+                            const RtcpReceiverEventLogMessage& rhs) {
+  return lhs.event_timestamp < rhs.event_timestamp;
+}
+
+void 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) {
+  RtcpReceiverLogMessage::const_iterator it =
+      redundancy_receiver_log_message.begin();
+  while (it != redundancy_receiver_log_message.end() &&
+         *remaining_space >=
+             kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize) {
+    receiver_log_message->push_front(*it);
+    size_t num_event_logs = (*remaining_space - kRtcpReceiverFrameLogSize) /
+                            kRtcpReceiverEventLogSize;
+    RtcpReceiverEventLogMessages& event_log_messages =
+        receiver_log_message->front().event_log_messages_;
+    if (num_event_logs < event_log_messages.size())
+      event_log_messages.resize(num_event_logs);
+
+    *remaining_space -= kRtcpReceiverFrameLogSize +
+                        event_log_messages.size() * kRtcpReceiverEventLogSize;
+    ++number_of_frames;
+    *total_number_of_messages_to_send += event_log_messages.size();
+    ++it;
+  }
+}
+
+// A class to build a string representing the NACK list in Cast message.
+//
+// The string will look like "23:3-6 25:1,5-6", meaning packets 3 to 6 in frame
+// 23 are being NACK'ed (i.e. they are missing from the receiver's point of
+// view) and packets 1, 5 and 6 are missing in frame 25. A frame that is
+// completely missing will show as "26:65535".
+class NackStringBuilder {
+ public:
+  NackStringBuilder()
+      : frame_count_(0),
+        packet_count_(0),
+        last_frame_id_(-1),
+        last_packet_id_(-1),
+        contiguous_sequence_(false) {}
+  ~NackStringBuilder() {}
+
+  bool Empty() const { return frame_count_ == 0; }
 
-void RtcpBuilder::SendRtcpFromRtpSender(
-    uint32 packet_type_flags,
-    const RtcpSenderInfo& sender_info,
-    const RtcpDlrrReportBlock& dlrr,
-    uint32 sending_ssrc) {
-  if (packet_type_flags & kRtcpRr ||
-      packet_type_flags & kRtcpRrtr ||
-      packet_type_flags & kRtcpCast ||
-      packet_type_flags & kRtcpReceiverLog ||
-      packet_type_flags & kRtcpNack) {
-    NOTREACHED() << "Invalid argument";
+  void PushFrame(int frame_id) {
+    DCHECK_GE(frame_id, 0);
+    if (frame_count_ > 0) {
+      if (frame_id == last_frame_id_) {
+        return;
+      }
+      if (contiguous_sequence_) {
+        stream_ << "-" << last_packet_id_;
+      }
+      stream_ << ", ";
+    }
+    stream_ << frame_id;
+    last_frame_id_ = frame_id;
+    packet_count_ = 0;
+    contiguous_sequence_ = false;
+    ++frame_count_;
   }
-  ssrc_ = sending_ssrc;
-  PacketRef packet(new base::RefCountedData<Packet>);
-  packet->data.reserve(kMaxIpPacketSize);
-  if (packet_type_flags & kRtcpSr) {
-    if (!BuildSR(sender_info, &packet->data)) return;
-    if (!BuildSdec(&packet->data)) return;
+
+  void PushPacket(int packet_id) {
+    DCHECK_GE(last_frame_id_, 0);
+    DCHECK_GE(packet_id, 0);
+    if (packet_count_ == 0) {
+      stream_ << ":" << packet_id;
+    } else if (packet_id == last_packet_id_ + 1) {
+      contiguous_sequence_ = true;
+    } else {
+      if (contiguous_sequence_) {
+        stream_ << "-" << last_packet_id_;
+        contiguous_sequence_ = false;
+      }
+      stream_ << "," << packet_id;
+    }
+    ++packet_count_;
+    last_packet_id_ = packet_id;
   }
-  if (packet_type_flags & kRtcpDlrr) {
-    if (!BuildDlrrRb(dlrr, &packet->data)) return;
+
+  std::string GetString() {
+    if (contiguous_sequence_) {
+      stream_ << "-" << last_packet_id_;
+      contiguous_sequence_ = false;
+    }
+    return stream_.str();
   }
-  if (packet->data.empty())
-    return;  // Sanity - don't send empty packets.
 
-  transport_->SendRtcpPacket(ssrc_, packet);
+ private:
+  std::ostringstream stream_;
+  int frame_count_;
+  int packet_count_;
+  int last_frame_id_;
+  int last_packet_id_;
+  bool contiguous_sequence_;
+};
+}  // namespace
+
+RtcpBuilder::RtcpBuilder(uint32 sending_ssrc)
+    : writer_(NULL, 0),
+      ssrc_(sending_ssrc),
+      ptr_of_length_(NULL) {
 }
 
-bool RtcpBuilder::BuildSR(const RtcpSenderInfo& sender_info,
-                          Packet* packet) const {
-  // Sender report.
-  size_t start_size = packet->size();
-  if (start_size + 52 > kMaxIpPacketSize) {
-    DLOG(FATAL) << "Not enough buffer space";
-    return false;
+RtcpBuilder::~RtcpBuilder() {}
+
+void RtcpBuilder::PatchLengthField() {
+  if (ptr_of_length_) {
+    // Back-patch the packet length. The client must have taken
+    // care of proper padding to 32-bit words.
+    int this_packet_length = (writer_.ptr() - ptr_of_length_ - 2);
+    DCHECK_EQ(0, this_packet_length % 4)
+        << "Packets must be a multiple of 32 bits long";
+    *ptr_of_length_ = this_packet_length >> 10;
+    *(ptr_of_length_ + 1) = (this_packet_length >> 2) & 0xFF;
+    ptr_of_length_ = NULL;
+  }
+}
+
+// Set the 5-bit value in the 1st byte of the header
+// and the payload type. Set aside room for the length field,
+// and make provision for back-patching it.
+void RtcpBuilder::AddRtcpHeader(RtcpPacketFields payload, int format_or_count) {
+  PatchLengthField();
+  writer_.WriteU8(0x80 | (format_or_count & 0x1F));
+  writer_.WriteU8(payload);
+  ptr_of_length_ = writer_.ptr();
+
+  // Initialize length to "clearly illegal".
+  writer_.WriteU16(0xDEAD);
+}
+
+void RtcpBuilder::Start() {
+  packet_ = new base::RefCountedData<Packet>;
+  packet_->data.resize(kMaxIpPacketSize);
+  writer_ = base::BigEndianWriter(
+      reinterpret_cast<char*>(&(packet_->data[0])), kMaxIpPacketSize);
+}
+
+PacketRef RtcpBuilder::Finish() {
+  PatchLengthField();
+  packet_->data.resize(kMaxIpPacketSize - writer_.remaining());
+  writer_ = base::BigEndianWriter(NULL, 0);
+  PacketRef ret = packet_;
+  packet_ = NULL;
+  return ret;
+}
+
+PacketRef RtcpBuilder::BuildRtcpFromReceiver(
+    const RtcpReportBlock* report_block,
+    const RtcpReceiverReferenceTimeReport* rrtr,
+    const RtcpCastMessage* cast_message,
+    const ReceiverRtcpEventSubscriber::RtcpEventMultiMap* rtcp_events,
+    base::TimeDelta target_delay) {
+  Start();
+
+  if (report_block)
+    AddRR(report_block);
+  if (rrtr)
+    AddRrtr(rrtr);
+  if (cast_message)
+    AddCast(cast_message, target_delay);
+  if (rtcp_events)
+    AddReceiverLog(*rtcp_events);
+
+  return Finish();
+}
+
+PacketRef RtcpBuilder::BuildRtcpFromSender(const RtcpSenderInfo& sender_info) {
+  Start();
+  AddSR(sender_info);
+  return Finish();
+}
+
+void RtcpBuilder::AddRR(const RtcpReportBlock* report_block) {
+  AddRtcpHeader(kPacketTypeReceiverReport, report_block ? 1 : 0);
+  writer_.WriteU32(ssrc_);
+  if (report_block) {
+    AddReportBlocks(*report_block);  // Adds 24 bytes.
   }
+}
+
+void RtcpBuilder::AddReportBlocks(const RtcpReportBlock& report_block) {
+  writer_.WriteU32(report_block.media_ssrc);
+  writer_.WriteU8(report_block.fraction_lost);
+  writer_.WriteU8(report_block.cumulative_lost >> 16);
+  writer_.WriteU8(report_block.cumulative_lost >> 8);
+  writer_.WriteU8(report_block.cumulative_lost);
+
+  // Extended highest seq_no, contain the highest sequence number received.
+  writer_.WriteU32(report_block.extended_high_sequence_number);
+  writer_.WriteU32(report_block.jitter);
+
+  // Last SR timestamp; our NTP time when we received the last report.
+  // This is the value that we read from the send report packet not when we
+  // received it.
+  writer_.WriteU32(report_block.last_sr);
+
+  // Delay since last received report, time since we received the report.
+  writer_.WriteU32(report_block.delay_since_last_sr);
+}
+
+void RtcpBuilder::AddRrtr(const RtcpReceiverReferenceTimeReport* rrtr) {
+  AddRtcpHeader(kPacketTypeXr, 0);
+  writer_.WriteU32(ssrc_);  // Add our own SSRC.
+  writer_.WriteU8(4);       // Add block type.
+  writer_.WriteU8(0);       // Add reserved.
+  writer_.WriteU16(2);      // Block length.
+
+  // Add the media (received RTP) SSRC.
+  writer_.WriteU32(rrtr->ntp_seconds);
+  writer_.WriteU32(rrtr->ntp_fraction);
+}
+
+void RtcpBuilder::AddCast(const RtcpCastMessage* cast,
+                          base::TimeDelta target_delay) {
+  // See RTC 4585 Section 6.4 for application specific feedback messages.
+  AddRtcpHeader(kPacketTypePayloadSpecific, 15);
+  writer_.WriteU32(ssrc_);              // Add our own SSRC.
+  writer_.WriteU32(cast->media_ssrc);  // Remote SSRC.
+  writer_.WriteU32(kCast);
+  writer_.WriteU8(static_cast<uint8>(cast->ack_frame_id));
+  uint8* cast_loss_field_pos = reinterpret_cast<uint8*>(writer_.ptr());
+  writer_.WriteU8(0);  // Overwritten with number_of_loss_fields.
+  DCHECK_LE(target_delay.InMilliseconds(),
+            std::numeric_limits<uint16_t>::max());
+  writer_.WriteU16(target_delay.InMilliseconds());
 
-  uint16 number_of_rows = 6;
-  packet->resize(start_size + 28);
+  size_t number_of_loss_fields = 0;
+  size_t max_number_of_loss_fields = std::min<size_t>(
+      kRtcpMaxCastLossFields, writer_.remaining() / 4);
+
+  MissingFramesAndPacketsMap::const_iterator frame_it =
+      cast->missing_frames_and_packets.begin();
+
+  NackStringBuilder nack_string_builder;
+  for (; frame_it != cast->missing_frames_and_packets.end() &&
+             number_of_loss_fields < max_number_of_loss_fields;
+       ++frame_it) {
+    nack_string_builder.PushFrame(frame_it->first);
+    // Iterate through all frames with missing packets.
+    if (frame_it->second.empty()) {
+      // Special case all packets in a frame is missing.
+      writer_.WriteU8(static_cast<uint8>(frame_it->first));
+      writer_.WriteU16(kRtcpCastAllPacketsLost);
+      writer_.WriteU8(0);
+      nack_string_builder.PushPacket(kRtcpCastAllPacketsLost);
+      ++number_of_loss_fields;
+    } else {
+      PacketIdSet::const_iterator packet_it = frame_it->second.begin();
+      while (packet_it != frame_it->second.end()) {
+        uint16 packet_id = *packet_it;
+        // Write frame and packet id to buffer before calculating bitmask.
+        writer_.WriteU8(static_cast<uint8>(frame_it->first));
+        writer_.WriteU16(packet_id);
+        nack_string_builder.PushPacket(packet_id);
+
+        uint8 bitmask = 0;
+        ++packet_it;
+        while (packet_it != frame_it->second.end()) {
+          int shift = static_cast<uint8>(*packet_it - packet_id) - 1;
+          if (shift >= 0 && shift <= 7) {
+            nack_string_builder.PushPacket(*packet_it);
+            bitmask |= (1 << shift);
+            ++packet_it;
+          } else {
+            break;
+          }
+        }
+        writer_.WriteU8(bitmask);
+        ++number_of_loss_fields;
+      }
+    }
+  }
+  VLOG_IF(1, !nack_string_builder.Empty())
+      << "SSRC: " << cast->media_ssrc
+      << ", ACK: " << cast->ack_frame_id
+      << ", NACK: " << nack_string_builder.GetString();
+  DCHECK_LE(number_of_loss_fields, kRtcpMaxCastLossFields);
+  *cast_loss_field_pos = static_cast<uint8>(number_of_loss_fields);
+}
 
-  base::BigEndianWriter big_endian_writer(
-      reinterpret_cast<char*>(&((*packet)[start_size])), 28);
-  big_endian_writer.WriteU8(0x80);
-  big_endian_writer.WriteU8(kPacketTypeSenderReport);
-  big_endian_writer.WriteU16(number_of_rows);
-  big_endian_writer.WriteU32(ssrc_);
-  big_endian_writer.WriteU32(sender_info.ntp_seconds);
-  big_endian_writer.WriteU32(sender_info.ntp_fraction);
-  big_endian_writer.WriteU32(sender_info.rtp_timestamp);
-  big_endian_writer.WriteU32(sender_info.send_packet_count);
-  big_endian_writer.WriteU32(static_cast<uint32>(sender_info.send_octet_count));
-  return true;
+void RtcpBuilder::AddSR(const RtcpSenderInfo& sender_info) {
+  AddRtcpHeader(kPacketTypeSenderReport, 0);
+  writer_.WriteU32(ssrc_);
+  writer_.WriteU32(sender_info.ntp_seconds);
+  writer_.WriteU32(sender_info.ntp_fraction);
+  writer_.WriteU32(sender_info.rtp_timestamp);
+  writer_.WriteU32(sender_info.send_packet_count);
+  writer_.WriteU32(static_cast<uint32>(sender_info.send_octet_count));
 }
 
 /*
@@ -87,36 +347,202 @@ bool RtcpBuilder::BuildSR(const RtcpSenderInfo& sender_info,
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     BT=5      |   reserved    |         block length          |
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
-  |                 SSRC_1 (SSRC of first receiver)               | sub-
+  |                 SSRC1 (SSRC of first receiver)               | sub-
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
   |                         last RR (LRR)                         |   1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                   delay since last RR (DLRR)                  |
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 */
-bool RtcpBuilder::BuildDlrrRb(const RtcpDlrrReportBlock& dlrr,
-                              Packet* packet) const {
-  size_t start_size = packet->size();
-  if (start_size + 24 > kMaxIpPacketSize) {
-    DLOG(FATAL) << "Not enough buffer space";
+void RtcpBuilder::AddDlrrRb(const RtcpDlrrReportBlock& dlrr) {
+  AddRtcpHeader(kPacketTypeXr, 0);
+  writer_.WriteU32(ssrc_);  // Add our own SSRC.
+  writer_.WriteU8(5);  // Add block type.
+  writer_.WriteU8(0);  // Add reserved.
+  writer_.WriteU16(3);  // Block length.
+  writer_.WriteU32(ssrc_);  // Add the media (received RTP) SSRC.
+  writer_.WriteU32(dlrr.last_rr);
+  writer_.WriteU32(dlrr.delay_since_last_rr);
+}
+
+void RtcpBuilder::AddReceiverLog(
+    const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events) {
+  size_t total_number_of_messages_to_send = 0;
+  RtcpReceiverLogMessage receiver_log_message;
+
+  if (!GetRtcpReceiverLogMessage(rtcp_events,
+                                 &receiver_log_message,
+                                 &total_number_of_messages_to_send)) {
+    return;
+  }
+
+  AddRtcpHeader(kPacketTypeApplicationDefined, kReceiverLogSubtype);
+  writer_.WriteU32(ssrc_);  // Add our own SSRC.
+  writer_.WriteU32(kCast);
+
+  while (!receiver_log_message.empty() &&
+         total_number_of_messages_to_send > 0) {
+    RtcpReceiverFrameLogMessage& frame_log_messages(
+        receiver_log_message.front());
+
+    // Add our frame header.
+    writer_.WriteU32(frame_log_messages.rtp_timestamp_);
+    size_t messages_in_frame = frame_log_messages.event_log_messages_.size();
+    if (messages_in_frame > total_number_of_messages_to_send) {
+      // We are running out of space.
+      messages_in_frame = total_number_of_messages_to_send;
+    }
+    // Keep track of how many messages we have left to send.
+    total_number_of_messages_to_send -= messages_in_frame;
+
+    // On the wire format is number of messages - 1.
+    writer_.WriteU8(static_cast<uint8>(messages_in_frame - 1));
+
+    base::TimeTicks event_timestamp_base =
+        frame_log_messages.event_log_messages_.front().event_timestamp;
+    uint32 base_timestamp_ms =
+        (event_timestamp_base - base::TimeTicks()).InMilliseconds();
+    writer_.WriteU8(static_cast<uint8>(base_timestamp_ms >> 16));
+    writer_.WriteU8(static_cast<uint8>(base_timestamp_ms >> 8));
+    writer_.WriteU8(static_cast<uint8>(base_timestamp_ms));
+
+    while (!frame_log_messages.event_log_messages_.empty() &&
+           messages_in_frame > 0) {
+      const RtcpReceiverEventLogMessage& event_message =
+          frame_log_messages.event_log_messages_.front();
+      uint16 event_type_and_timestamp_delta =
+          MergeEventTypeAndTimestampForWireFormat(
+              event_message.type,
+              event_message.event_timestamp - event_timestamp_base);
+      switch (event_message.type) {
+        case FRAME_ACK_SENT:
+        case FRAME_PLAYOUT:
+        case FRAME_DECODED:
+          writer_.WriteU16(
+              static_cast<uint16>(event_message.delay_delta.InMilliseconds()));
+          writer_.WriteU16(event_type_and_timestamp_delta);
+          break;
+        case PACKET_RECEIVED:
+          writer_.WriteU16(event_message.packet_id);
+          writer_.WriteU16(event_type_and_timestamp_delta);
+          break;
+        default:
+          NOTREACHED();
+      }
+      messages_in_frame--;
+      frame_log_messages.event_log_messages_.pop_front();
+    }
+    if (frame_log_messages.event_log_messages_.empty()) {
+      // We sent all messages on this frame; pop the frame header.
+      receiver_log_message.pop_front();
+    }
+  }
+  DCHECK_EQ(total_number_of_messages_to_send, 0u);
+}
+
+bool RtcpBuilder::GetRtcpReceiverLogMessage(
+    const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events,
+    RtcpReceiverLogMessage* receiver_log_message,
+    size_t* total_number_of_messages_to_send) {
+  size_t number_of_frames = 0;
+  size_t remaining_space =
+      std::min<size_t>(kMaxReceiverLogBytes, writer_.remaining());
+  if (remaining_space < kRtcpCastLogHeaderSize + kRtcpReceiverFrameLogSize +
+                            kRtcpReceiverEventLogSize) {
     return false;
   }
 
-  packet->resize(start_size + 24);
-
-  base::BigEndianWriter big_endian_writer(
-      reinterpret_cast<char*>(&((*packet)[start_size])), 24);
-  big_endian_writer.WriteU8(0x80);
-  big_endian_writer.WriteU8(kPacketTypeXr);
-  big_endian_writer.WriteU16(5);  // Length.
-  big_endian_writer.WriteU32(ssrc_);  // Add our own SSRC.
-  big_endian_writer.WriteU8(5);  // Add block type.
-  big_endian_writer.WriteU8(0);  // Add reserved.
-  big_endian_writer.WriteU16(3);  // Block length.
-  big_endian_writer.WriteU32(ssrc_);  // Add the media (received RTP) SSRC.
-  big_endian_writer.WriteU32(dlrr.last_rr);
-  big_endian_writer.WriteU32(dlrr.delay_since_last_rr);
-  return true;
+  // We use this to do event timestamp sorting and truncating for events of
+  // a single frame.
+  std::vector<RtcpReceiverEventLogMessage> sorted_log_messages;
+
+  // Account for the RTCP header for an application-defined packet.
+  remaining_space -= kRtcpCastLogHeaderSize;
+
+  ReceiverRtcpEventSubscriber::RtcpEventMultiMap::const_reverse_iterator rit =
+      rtcp_events.rbegin();
+
+  while (rit != rtcp_events.rend() &&
+         remaining_space >=
+             kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize) {
+    const RtpTimestamp rtp_timestamp = rit->first;
+    RtcpReceiverFrameLogMessage frame_log(rtp_timestamp);
+    remaining_space -= kRtcpReceiverFrameLogSize;
+    ++number_of_frames;
+
+    // Get all events of a single frame.
+    sorted_log_messages.clear();
+    do {
+      RtcpReceiverEventLogMessage event_log_message;
+      event_log_message.type = rit->second.type;
+      event_log_message.event_timestamp = rit->second.timestamp;
+      event_log_message.delay_delta = rit->second.delay_delta;
+      event_log_message.packet_id = rit->second.packet_id;
+      sorted_log_messages.push_back(event_log_message);
+      ++rit;
+    } while (rit != rtcp_events.rend() && rit->first == rtp_timestamp);
+
+    std::sort(sorted_log_messages.begin(),
+              sorted_log_messages.end(),
+              &EventTimestampLessThan);
+
+    // From |sorted_log_messages|, only take events that are no greater than
+    // |kMaxWireFormatTimeDeltaMs| seconds away from the latest event. Events
+    // older than that cannot be encoded over the wire.
+    std::vector<RtcpReceiverEventLogMessage>::reverse_iterator sorted_rit =
+        sorted_log_messages.rbegin();
+    base::TimeTicks first_event_timestamp = sorted_rit->event_timestamp;
+    size_t events_in_frame = 0;
+    while (sorted_rit != sorted_log_messages.rend() &&
+           events_in_frame < kRtcpMaxReceiverLogMessages &&
+           remaining_space >= kRtcpReceiverEventLogSize) {
+      base::TimeDelta delta(first_event_timestamp -
+                            sorted_rit->event_timestamp);
+      if (delta.InMilliseconds() > kMaxWireFormatTimeDeltaMs)
+        break;
+      frame_log.event_log_messages_.push_front(*sorted_rit);
+      ++events_in_frame;
+      ++*total_number_of_messages_to_send;
+      remaining_space -= kRtcpReceiverEventLogSize;
+      ++sorted_rit;
+    }
+
+    receiver_log_message->push_front(frame_log);
+  }
+
+  rtcp_events_history_.push_front(*receiver_log_message);
+
+  // We don't try to match RTP timestamps of redundancy frame logs with those
+  // from the newest set (which would save the space of an extra RTP timestamp
+  // over the wire). Unless the redundancy frame logs are very recent, it's
+  // unlikely there will be a match anyway.
+  if (rtcp_events_history_.size() > kFirstRedundancyOffset) {
+    // Add first redundnacy messages, if enough space remaining
+    AddReceiverLogEntries(rtcp_events_history_[kFirstRedundancyOffset],
+                          receiver_log_message,
+                          &remaining_space,
+                          &number_of_frames,
+                          total_number_of_messages_to_send);
+  }
+
+  if (rtcp_events_history_.size() > kSecondRedundancyOffset) {
+    // Add second redundancy messages, if enough space remaining
+    AddReceiverLogEntries(rtcp_events_history_[kSecondRedundancyOffset],
+                          receiver_log_message,
+                          &remaining_space,
+                          &number_of_frames,
+                          total_number_of_messages_to_send);
+  }
+
+  if (rtcp_events_history_.size() > kReceiveLogMessageHistorySize) {
+    rtcp_events_history_.pop_back();
+  }
+
+  DCHECK_LE(rtcp_events_history_.size(), kReceiveLogMessageHistorySize);
+
+  VLOG(3) << "number of frames: " << number_of_frames;
+  VLOG(3) << "total messages to send: " << *total_number_of_messages_to_send;
+  return number_of_frames > 0;
 }
 
 }  // namespace cast