This source file includes following definitions.
- ConvertEventTypeToWireFormat
- MergeEventTypeAndTimestampForWireFormat
- EventTimestampLessThan
- AddReceiverLog
- contiguous_sequence_
- Empty
- PushFrame
- PushPacket
- GetString
- cast_environment_
- SendRtcpFromRtpReceiver
- BuildRR
- AddReportBlocks
- BuildSdec
- BuildPli
- BuildRpsi
- BuildRemb
- BuildNack
- BuildBye
- BuildRrtr
- BuildCast
- BuildReceiverLog
- BuildRtcpReceiverLogMessage
#include "media/cast/rtcp/rtcp_sender.h"
#include <stdint.h>
#include <algorithm>
#include <vector>
#include "base/big_endian.h"
#include "base/logging.h"
#include "media/cast/cast_environment.h"
#include "media/cast/rtcp/rtcp_defines.h"
#include "media/cast/rtcp/rtcp_utility.h"
#include "media/cast/transport/cast_transport_defines.h"
#include "media/cast/transport/pacing/paced_sender.h"
namespace media {
namespace cast {
namespace {
const int64 kMaxWireFormatTimeDeltaMs = INT64_C(0xfff);
int ConvertEventTypeToWireFormat(const CastLoggingEvent& event) {
switch (event) {
case kAudioAckSent:
return 1;
case kAudioPlayoutDelay:
return 2;
case kAudioFrameDecoded:
return 3;
case kAudioPacketReceived:
return 4;
case kVideoAckSent:
return 5;
case kVideoFrameDecoded:
return 6;
case kVideoRenderDelay:
return 7;
case kVideoPacketReceived:
return 8;
case kDuplicateAudioPacketReceived:
return 9;
case kDuplicateVideoPacketReceived:
return 10;
default:
return 0;
}
}
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);
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;
}
bool EventTimestampLessThan(const RtcpReceiverEventLogMessage& lhs,
const RtcpReceiverEventLogMessage& rhs) {
return lhs.event_timestamp < rhs.event_timestamp;
}
void AddReceiverLog(
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;
}
}
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 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_;
}
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;
}
std::string GetString() {
if (contiguous_sequence_) {
stream_ << "-" << last_packet_id_;
contiguous_sequence_ = false;
}
return stream_.str();
}
private:
std::ostringstream stream_;
int frame_count_;
int packet_count_;
int last_frame_id_;
int last_packet_id_;
bool contiguous_sequence_;
};
}
RtcpSender::RtcpSender(scoped_refptr<CastEnvironment> cast_environment,
transport::PacedPacketSender* outgoing_transport,
uint32 sending_ssrc,
const std::string& c_name)
: ssrc_(sending_ssrc),
c_name_(c_name),
transport_(outgoing_transport),
cast_environment_(cast_environment) {
DCHECK_LT(c_name_.length(), kRtcpCnameSize) << "Invalid config";
}
RtcpSender::~RtcpSender() {}
void RtcpSender::SendRtcpFromRtpReceiver(
uint32 packet_type_flags,
const transport::RtcpReportBlock* report_block,
const RtcpReceiverReferenceTimeReport* rrtr,
const RtcpCastMessage* cast_message,
const ReceiverRtcpEventSubscriber::RtcpEventMultiMap* rtcp_events,
uint16 target_delay_ms) {
if (packet_type_flags & transport::kRtcpSr ||
packet_type_flags & transport::kRtcpDlrr ||
packet_type_flags & transport::kRtcpSenderLog) {
NOTREACHED() << "Invalid argument";
}
if (packet_type_flags & transport::kRtcpPli ||
packet_type_flags & transport::kRtcpRpsi ||
packet_type_flags & transport::kRtcpRemb ||
packet_type_flags & transport::kRtcpNack) {
NOTIMPLEMENTED();
}
Packet packet;
packet.reserve(kMaxIpPacketSize);
if (packet_type_flags & transport::kRtcpRr) {
BuildRR(report_block, &packet);
if (!c_name_.empty()) {
BuildSdec(&packet);
}
}
if (packet_type_flags & transport::kRtcpBye) {
BuildBye(&packet);
}
if (packet_type_flags & transport::kRtcpRrtr) {
DCHECK(rrtr) << "Invalid argument";
BuildRrtr(rrtr, &packet);
}
if (packet_type_flags & transport::kRtcpCast) {
DCHECK(cast_message) << "Invalid argument";
BuildCast(cast_message, target_delay_ms, &packet);
}
if (packet_type_flags & transport::kRtcpReceiverLog) {
DCHECK(rtcp_events) << "Invalid argument";
BuildReceiverLog(*rtcp_events, &packet);
}
if (packet.empty())
return;
transport_->SendRtcpPacket(packet);
}
void RtcpSender::BuildRR(const transport::RtcpReportBlock* report_block,
Packet* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 32, kMaxIpPacketSize) << "Not enough buffer space";
if (start_size + 32 > kMaxIpPacketSize)
return;
uint16 number_of_rows = (report_block) ? 7 : 1;
packet->resize(start_size + 8);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 8);
big_endian_writer.WriteU8(0x80 + (report_block ? 1 : 0));
big_endian_writer.WriteU8(transport::kPacketTypeReceiverReport);
big_endian_writer.WriteU16(number_of_rows);
big_endian_writer.WriteU32(ssrc_);
if (report_block) {
AddReportBlocks(*report_block, packet);
}
}
void RtcpSender::AddReportBlocks(const transport::RtcpReportBlock& report_block,
Packet* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 24, kMaxIpPacketSize) << "Not enough buffer space";
if (start_size + 24 > kMaxIpPacketSize)
return;
packet->resize(start_size + 24);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 24);
big_endian_writer.WriteU32(report_block.media_ssrc);
big_endian_writer.WriteU8(report_block.fraction_lost);
big_endian_writer.WriteU8(report_block.cumulative_lost >> 16);
big_endian_writer.WriteU8(report_block.cumulative_lost >> 8);
big_endian_writer.WriteU8(report_block.cumulative_lost);
big_endian_writer.WriteU32(report_block.extended_high_sequence_number);
big_endian_writer.WriteU32(report_block.jitter);
big_endian_writer.WriteU32(report_block.last_sr);
big_endian_writer.WriteU32(report_block.delay_since_last_sr);
}
void RtcpSender::BuildSdec(Packet* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 12 + c_name_.length(), kMaxIpPacketSize)
<< "Not enough buffer space";
if (start_size + 12 > kMaxIpPacketSize)
return;
packet->resize(start_size + 10);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 10);
big_endian_writer.WriteU8(0x80 + 1);
big_endian_writer.WriteU8(transport::kPacketTypeSdes);
uint32 sdes_length_position = static_cast<uint32>(start_size) + 3;
big_endian_writer.WriteU16(0);
big_endian_writer.WriteU32(ssrc_);
big_endian_writer.WriteU8(1);
big_endian_writer.WriteU8(static_cast<uint8>(c_name_.length()));
size_t sdes_length = 10 + c_name_.length();
packet->insert(
packet->end(), c_name_.c_str(), c_name_.c_str() + c_name_.length());
size_t padding = 0;
if ((packet->size() % 4) == 0) {
padding++;
packet->push_back(0);
}
while ((packet->size() % 4) != 0) {
padding++;
packet->push_back(0);
}
sdes_length += padding;
uint8 buffer_length = static_cast<uint8>((sdes_length / 4) - 1);
(*packet)[sdes_length_position] = buffer_length;
}
void RtcpSender::BuildPli(uint32 remote_ssrc, Packet* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 12, kMaxIpPacketSize) << "Not enough buffer space";
if (start_size + 12 > kMaxIpPacketSize)
return;
packet->resize(start_size + 12);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 12);
uint8 FMT = 1;
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(transport::kPacketTypePayloadSpecific);
big_endian_writer.WriteU16(2);
big_endian_writer.WriteU32(ssrc_);
big_endian_writer.WriteU32(remote_ssrc);
}
void RtcpSender::BuildRpsi(const RtcpRpsiMessage* rpsi, Packet* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 24, kMaxIpPacketSize) << "Not enough buffer space";
if (start_size + 24 > kMaxIpPacketSize)
return;
packet->resize(start_size + 24);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 24);
uint8 FMT = 3;
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(transport::kPacketTypePayloadSpecific);
uint32 bits_required = 7;
uint8 bytes_required = 1;
while ((rpsi->picture_id >> bits_required) > 0) {
bits_required += 7;
bytes_required++;
}
uint8 size = 3;
if (bytes_required > 6) {
size = 5;
} else if (bytes_required > 2) {
size = 4;
}
big_endian_writer.WriteU8(0);
big_endian_writer.WriteU8(size);
big_endian_writer.WriteU32(ssrc_);
big_endian_writer.WriteU32(rpsi->remote_ssrc);
uint8 padding_bytes = 4 - ((2 + bytes_required) % 4);
if (padding_bytes == 4) {
padding_bytes = 0;
}
big_endian_writer.WriteU8(padding_bytes * 8);
big_endian_writer.WriteU8(rpsi->payload_type);
for (int i = bytes_required - 1; i > 0; i--) {
big_endian_writer.WriteU8(0x80 |
static_cast<uint8>(rpsi->picture_id >> (i * 7)));
}
big_endian_writer.WriteU8(static_cast<uint8>(rpsi->picture_id & 0x7f));
for (int j = 0; j < padding_bytes; ++j) {
big_endian_writer.WriteU8(0);
}
}
void RtcpSender::BuildRemb(const RtcpRembMessage* remb, Packet* packet) const {
size_t start_size = packet->size();
size_t remb_size = 20 + 4 * remb->remb_ssrcs.size();
DCHECK_LT(start_size + remb_size, kMaxIpPacketSize)
<< "Not enough buffer space";
if (start_size + remb_size > kMaxIpPacketSize)
return;
packet->resize(start_size + remb_size);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), remb_size);
uint8 FMT = 15;
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(transport::kPacketTypePayloadSpecific);
big_endian_writer.WriteU8(0);
big_endian_writer.WriteU8(static_cast<uint8>(remb->remb_ssrcs.size() + 4));
big_endian_writer.WriteU32(ssrc_);
big_endian_writer.WriteU32(0);
big_endian_writer.WriteU32(kRemb);
big_endian_writer.WriteU8(static_cast<uint8>(remb->remb_ssrcs.size()));
uint8 bitrate_exponent;
uint32 bitrate_mantissa;
BitrateToRembExponentBitrate(
remb->remb_bitrate, &bitrate_exponent, &bitrate_mantissa);
big_endian_writer.WriteU8(static_cast<uint8>(
(bitrate_exponent << 2) + ((bitrate_mantissa >> 16) & 0x03)));
big_endian_writer.WriteU8(static_cast<uint8>(bitrate_mantissa >> 8));
big_endian_writer.WriteU8(static_cast<uint8>(bitrate_mantissa));
std::list<uint32>::const_iterator it = remb->remb_ssrcs.begin();
for (; it != remb->remb_ssrcs.end(); ++it) {
big_endian_writer.WriteU32(*it);
}
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
cast_environment_->Logging()->InsertGenericEvent(
now, kRembBitrate, remb->remb_bitrate);
}
void RtcpSender::BuildNack(const RtcpNackMessage* nack, Packet* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 16, kMaxIpPacketSize) << "Not enough buffer space";
if (start_size + 16 > kMaxIpPacketSize)
return;
packet->resize(start_size + 16);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 16);
uint8 FMT = 1;
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(transport::kPacketTypeGenericRtpFeedback);
big_endian_writer.WriteU8(0);
size_t nack_size_pos = start_size + 3;
big_endian_writer.WriteU8(3);
big_endian_writer.WriteU32(ssrc_);
big_endian_writer.WriteU32(nack->remote_ssrc);
size_t number_of_nack_fields = 0;
size_t max_number_of_nack_fields = std::min<size_t>(
kRtcpMaxNackFields, (kMaxIpPacketSize - packet->size()) / 4);
std::list<uint16>::const_iterator it = nack->nack_list.begin();
while (it != nack->nack_list.end() &&
number_of_nack_fields < max_number_of_nack_fields) {
uint16 nack_sequence_number = *it;
uint16 bitmask = 0;
++it;
while (it != nack->nack_list.end()) {
int shift = static_cast<uint16>(*it - nack_sequence_number) - 1;
if (shift >= 0 && shift <= 15) {
bitmask |= (1 << shift);
++it;
} else {
break;
}
}
start_size = packet->size();
DCHECK_LT(start_size + 4, kMaxIpPacketSize) << "Not enough buffer space";
if (start_size + 4 > kMaxIpPacketSize)
return;
packet->resize(start_size + 4);
base::BigEndianWriter big_endian_nack_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 4);
big_endian_nack_writer.WriteU16(nack_sequence_number);
big_endian_nack_writer.WriteU16(bitmask);
number_of_nack_fields++;
}
DCHECK_GE(kRtcpMaxNackFields, number_of_nack_fields);
(*packet)[nack_size_pos] = static_cast<uint8>(2 + number_of_nack_fields);
}
void RtcpSender::BuildBye(Packet* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 8, kMaxIpPacketSize) << "Not enough buffer space";
if (start_size + 8 > kMaxIpPacketSize)
return;
packet->resize(start_size + 8);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 8);
big_endian_writer.WriteU8(0x80 + 1);
big_endian_writer.WriteU8(transport::kPacketTypeBye);
big_endian_writer.WriteU16(1);
big_endian_writer.WriteU32(ssrc_);
}
void RtcpSender::BuildRrtr(const RtcpReceiverReferenceTimeReport* rrtr,
Packet* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 20, kMaxIpPacketSize) << "Not enough buffer space";
if (start_size + 20 > kMaxIpPacketSize)
return;
packet->resize(start_size + 20);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 20);
big_endian_writer.WriteU8(0x80);
big_endian_writer.WriteU8(transport::kPacketTypeXr);
big_endian_writer.WriteU16(4);
big_endian_writer.WriteU32(ssrc_);
big_endian_writer.WriteU8(4);
big_endian_writer.WriteU8(0);
big_endian_writer.WriteU16(2);
big_endian_writer.WriteU32(rrtr->ntp_seconds);
big_endian_writer.WriteU32(rrtr->ntp_fraction);
}
void RtcpSender::BuildCast(const RtcpCastMessage* cast,
uint16 target_delay_ms,
Packet* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 20, kMaxIpPacketSize) << "Not enough buffer space";
if (start_size + 20 > kMaxIpPacketSize)
return;
packet->resize(start_size + 20);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 20);
uint8 FMT = 15;
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(transport::kPacketTypePayloadSpecific);
big_endian_writer.WriteU8(0);
size_t cast_size_pos = start_size + 3;
big_endian_writer.WriteU8(4);
big_endian_writer.WriteU32(ssrc_);
big_endian_writer.WriteU32(cast->media_ssrc_);
big_endian_writer.WriteU32(kCast);
big_endian_writer.WriteU8(static_cast<uint8>(cast->ack_frame_id_));
size_t cast_loss_field_pos = start_size + 17;
big_endian_writer.WriteU8(0);
big_endian_writer.WriteU16(target_delay_ms);
size_t number_of_loss_fields = 0;
size_t max_number_of_loss_fields = std::min<size_t>(
kRtcpMaxCastLossFields, (kMaxIpPacketSize - packet->size()) / 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);
if (frame_it->second.empty()) {
start_size = packet->size();
packet->resize(start_size + 4);
base::BigEndianWriter big_endian_nack_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 4);
big_endian_nack_writer.WriteU8(static_cast<uint8>(frame_it->first));
big_endian_nack_writer.WriteU16(kRtcpCastAllPacketsLost);
big_endian_nack_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;
start_size = packet->size();
packet->resize(start_size + 4);
base::BigEndianWriter big_endian_nack_writer(
reinterpret_cast<char*>(&((*packet)[start_size])), 4);
big_endian_nack_writer.WriteU8(static_cast<uint8>(frame_it->first));
big_endian_nack_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;
}
}
big_endian_nack_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);
(*packet)[cast_size_pos] = static_cast<uint8>(4 + number_of_loss_fields);
(*packet)[cast_loss_field_pos] = static_cast<uint8>(number_of_loss_fields);
}
void RtcpSender::BuildReceiverLog(
const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events,
Packet* packet) {
const size_t packet_start_size = packet->size();
size_t number_of_frames = 0;
size_t total_number_of_messages_to_send = 0;
size_t rtcp_log_size = 0;
RtcpReceiverLogMessage receiver_log_message;
if (!BuildRtcpReceiverLogMessage(rtcp_events,
packet_start_size,
&receiver_log_message,
&number_of_frames,
&total_number_of_messages_to_send,
&rtcp_log_size)) {
return;
}
packet->resize(packet_start_size + rtcp_log_size);
base::BigEndianWriter big_endian_writer(
reinterpret_cast<char*>(&((*packet)[packet_start_size])), rtcp_log_size);
big_endian_writer.WriteU8(0x80 + kReceiverLogSubtype);
big_endian_writer.WriteU8(transport::kPacketTypeApplicationDefined);
big_endian_writer.WriteU16(static_cast<uint16>(
2 + 2 * number_of_frames + total_number_of_messages_to_send));
big_endian_writer.WriteU32(ssrc_);
big_endian_writer.WriteU32(kCast);
while (!receiver_log_message.empty() &&
total_number_of_messages_to_send > 0) {
RtcpReceiverFrameLogMessage& frame_log_messages(
receiver_log_message.front());
big_endian_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) {
messages_in_frame = total_number_of_messages_to_send;
}
total_number_of_messages_to_send -= messages_in_frame;
big_endian_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();
big_endian_writer.WriteU8(static_cast<uint8>(base_timestamp_ms >> 16));
big_endian_writer.WriteU8(static_cast<uint8>(base_timestamp_ms >> 8));
big_endian_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 kAudioAckSent:
case kVideoAckSent:
case kAudioPlayoutDelay:
case kAudioFrameDecoded:
case kVideoFrameDecoded:
case kVideoRenderDelay:
big_endian_writer.WriteU16(
static_cast<uint16>(event_message.delay_delta.InMilliseconds()));
big_endian_writer.WriteU16(event_type_and_timestamp_delta);
break;
case kAudioPacketReceived:
case kVideoPacketReceived:
case kDuplicateAudioPacketReceived:
case kDuplicateVideoPacketReceived:
big_endian_writer.WriteU16(event_message.packet_id);
big_endian_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()) {
receiver_log_message.pop_front();
}
}
DCHECK_EQ(total_number_of_messages_to_send, 0u);
}
bool RtcpSender::BuildRtcpReceiverLogMessage(
const ReceiverRtcpEventSubscriber::RtcpEventMultiMap& rtcp_events,
size_t start_size,
RtcpReceiverLogMessage* receiver_log_message,
size_t* number_of_frames,
size_t* total_number_of_messages_to_send,
size_t* rtcp_log_size) {
size_t remaining_space =
std::min(kMaxReceiverLogBytes, kMaxIpPacketSize - start_size);
if (remaining_space < kRtcpCastLogHeaderSize + kRtcpReceiverFrameLogSize +
kRtcpReceiverEventLogSize) {
return false;
}
std::vector<RtcpReceiverEventLogMessage> sorted_log_messages;
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;
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);
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);
if (rtcp_events_history_.size() > kFirstRedundancyOffset) {
AddReceiverLog(rtcp_events_history_[kFirstRedundancyOffset],
receiver_log_message,
&remaining_space,
number_of_frames,
total_number_of_messages_to_send);
}
if (rtcp_events_history_.size() > kSecondRedundancyOffset) {
AddReceiverLog(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);
*rtcp_log_size =
kRtcpCastLogHeaderSize + *number_of_frames * kRtcpReceiverFrameLogSize +
*total_number_of_messages_to_send * kRtcpReceiverEventLogSize;
DCHECK_GE(kMaxIpPacketSize, start_size + *rtcp_log_size)
<< "Not enough buffer space.";
VLOG(3) << "number of frames: " << *number_of_frames;
VLOG(3) << "total messages to send: " << *total_number_of_messages_to_send;
VLOG(3) << "rtcp log size: " << *rtcp_log_size;
return *number_of_frames > 0;
}
}
}