// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/cast/rtcp/rtcp_sender.h"
#include <algorithm>
#include <vector>
#include "base/logging.h"
#include "media/cast/cast_environment.h"
#include "media/cast/net/pacing/paced_sender.h"
#include "media/cast/rtcp/rtcp_utility.h"
#include "net/base/big_endian.h"
static const size_t kRtcpCastLogHeaderSize = 12;
static const size_t kRtcpSenderFrameLogSize = 4;
static const size_t kRtcpReceiverFrameLogSize = 8;
static const size_t kRtcpReceiverEventLogSize = 4;
namespace {
uint16 MergeEventTypeAndTimestampForWireFormat(
const media::cast::CastLoggingEvent& event,
const base::TimeDelta& time_delta) {
int64 time_delta_ms = time_delta.InMilliseconds();
// Max delta is 4096 milliseconds.
DCHECK_GE(GG_INT64_C(0xfff), time_delta_ms);
uint16 event_type_and_timestamp_delta =
static_cast<uint16>(time_delta_ms & 0xfff);
uint16 event_type = 0;
switch (event) {
case media::cast::kAckSent:
event_type = 1;
break;
case media::cast::kAudioPlayoutDelay:
event_type = 2;
break;
case media::cast::kAudioFrameDecoded:
event_type = 3;
break;
case media::cast::kVideoFrameDecoded:
event_type = 4;
break;
case media::cast::kVideoRenderDelay:
event_type = 5;
break;
case media::cast::kPacketReceived:
event_type = 6;
break;
default:
NOTREACHED();
}
DCHECK(!(event_type & 0xfff0));
return (event_type << 12) + event_type_and_timestamp_delta;
}
bool ScanRtcpReceiverLogMessage(
const media::cast::RtcpReceiverLogMessage& receiver_log_message,
size_t start_size,
size_t* number_of_frames,
size_t* total_number_of_messages_to_send,
size_t* rtcp_log_size) {
if (receiver_log_message.empty()) return false;
size_t remaining_space = media::cast::kIpPacketSize - start_size;
// We must have space for at least one message
DCHECK_GE(remaining_space, kRtcpCastLogHeaderSize +
kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize)
<< "Not enough buffer space";
if (remaining_space < kRtcpCastLogHeaderSize + kRtcpReceiverFrameLogSize +
kRtcpReceiverEventLogSize) {
return false;
}
// Account for the RTCP header for an application-defined packet.
remaining_space -= kRtcpCastLogHeaderSize;
media::cast::RtcpReceiverLogMessage::const_iterator frame_it =
receiver_log_message.begin();
for (; frame_it != receiver_log_message.end(); ++frame_it) {
(*number_of_frames)++;
remaining_space -= kRtcpReceiverFrameLogSize;
size_t messages_in_frame = frame_it->event_log_messages_.size();
size_t remaining_space_in_messages =
remaining_space / kRtcpReceiverEventLogSize;
size_t messages_to_send = std::min(messages_in_frame,
remaining_space_in_messages);
if (messages_to_send > media::cast::kRtcpMaxReceiverLogMessages) {
// We can't send more than 256 messages.
remaining_space -= media::cast::kRtcpMaxReceiverLogMessages *
kRtcpReceiverEventLogSize;
*total_number_of_messages_to_send +=
media::cast::kRtcpMaxReceiverLogMessages;
break;
}
remaining_space -= messages_to_send * kRtcpReceiverEventLogSize;
*total_number_of_messages_to_send += messages_to_send;
if (remaining_space <
kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize) {
// Make sure that we have room for at least one more message.
break;
}
}
*rtcp_log_size = kRtcpCastLogHeaderSize +
*number_of_frames * kRtcpReceiverFrameLogSize +
*total_number_of_messages_to_send * kRtcpReceiverEventLogSize;
DCHECK_GE(media::cast::kIpPacketSize,
start_size + *rtcp_log_size) << "Not enough buffer space";
VLOG(1) << "number of frames " << *number_of_frames;
VLOG(1) << "total messages to send " << *total_number_of_messages_to_send;
VLOG(1) << "rtcp log size " << *rtcp_log_size;
return true;
}
} // namespace
namespace media {
namespace cast {
RtcpSender::RtcpSender(scoped_refptr<CastEnvironment> cast_environment,
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::SendRtcpFromRtpSender(uint32 packet_type_flags,
const RtcpSenderInfo* sender_info,
const RtcpDlrrReportBlock* dlrr,
RtcpSenderLogMessage* sender_log) {
if (packet_type_flags & kRtcpRr ||
packet_type_flags & kRtcpPli ||
packet_type_flags & kRtcpRrtr ||
packet_type_flags & kRtcpCast ||
packet_type_flags & kRtcpReceiverLog ||
packet_type_flags & kRtcpRpsi ||
packet_type_flags & kRtcpRemb ||
packet_type_flags & kRtcpNack) {
NOTREACHED() << "Invalid argument";
}
std::vector<uint8> packet;
packet.reserve(kIpPacketSize);
if (packet_type_flags & kRtcpSr) {
DCHECK(sender_info) << "Invalid argument";
BuildSR(*sender_info, NULL, &packet);
BuildSdec(&packet);
}
if (packet_type_flags & kRtcpBye) {
BuildBye(&packet);
}
if (packet_type_flags & kRtcpDlrr) {
DCHECK(dlrr) << "Invalid argument";
BuildDlrrRb(dlrr, &packet);
}
if (packet_type_flags & kRtcpSenderLog) {
DCHECK(sender_log) << "Invalid argument";
BuildSenderLog(sender_log, &packet);
}
if (packet.empty())
return; // Sanity don't send empty packets.
transport_->SendRtcpPacket(packet);
}
void RtcpSender::SendRtcpFromRtpReceiver(
uint32 packet_type_flags,
const RtcpReportBlock* report_block,
const RtcpReceiverReferenceTimeReport* rrtr,
const RtcpCastMessage* cast_message,
RtcpReceiverLogMessage* receiver_log) {
if (packet_type_flags & kRtcpSr ||
packet_type_flags & kRtcpDlrr ||
packet_type_flags & kRtcpSenderLog) {
NOTREACHED() << "Invalid argument";
}
if (packet_type_flags & kRtcpPli ||
packet_type_flags & kRtcpRpsi ||
packet_type_flags & kRtcpRemb ||
packet_type_flags & kRtcpNack) {
// Implement these for webrtc interop.
NOTIMPLEMENTED();
}
std::vector<uint8> packet;
packet.reserve(kIpPacketSize);
if (packet_type_flags & kRtcpRr) {
BuildRR(report_block, &packet);
if (!c_name_.empty()) {
BuildSdec(&packet);
}
}
if (packet_type_flags & kRtcpBye) {
BuildBye(&packet);
}
if (packet_type_flags & kRtcpRrtr) {
DCHECK(rrtr) << "Invalid argument";
BuildRrtr(rrtr, &packet);
}
if (packet_type_flags & kRtcpCast) {
DCHECK(cast_message) << "Invalid argument";
BuildCast(cast_message, &packet);
}
if (packet_type_flags & kRtcpReceiverLog) {
DCHECK(receiver_log) << "Invalid argument";
BuildReceiverLog(receiver_log, &packet);
}
if (packet.empty()) return; // Sanity don't send empty packets.
transport_->SendRtcpPacket(packet);
}
void RtcpSender::BuildSR(const RtcpSenderInfo& sender_info,
const RtcpReportBlock* report_block,
std::vector<uint8>* packet) const {
// Sender report.
size_t start_size = packet->size();
DCHECK_LT(start_size + 52, kIpPacketSize) << "Not enough buffer space";
if (start_size + 52 > kIpPacketSize) return;
uint16 number_of_rows = (report_block) ? 12 : 6;
packet->resize(start_size + 28);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 28);
big_endian_writer.WriteU8(0x80 + (report_block ? 1 : 0));
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));
if (report_block) {
AddReportBlocks(*report_block, packet); // Adds 24 bytes.
}
}
void RtcpSender::BuildRR(const RtcpReportBlock* report_block,
std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 32, kIpPacketSize) << "Not enough buffer space";
if (start_size + 32 > kIpPacketSize) return;
uint16 number_of_rows = (report_block) ? 7 : 1;
packet->resize(start_size + 8);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 8);
big_endian_writer.WriteU8(0x80 + (report_block ? 1 : 0));
big_endian_writer.WriteU8(kPacketTypeReceiverReport);
big_endian_writer.WriteU16(number_of_rows);
big_endian_writer.WriteU32(ssrc_);
if (report_block) {
AddReportBlocks(*report_block, packet); // Adds 24 bytes.
}
}
void RtcpSender::AddReportBlocks(const RtcpReportBlock& report_block,
std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 24, kIpPacketSize) << "Not enough buffer space";
if (start_size + 24 > kIpPacketSize) return;
packet->resize(start_size + 24);
net::BigEndianWriter big_endian_writer(&((*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);
// Extended highest seq_no, contain the highest sequence number received.
big_endian_writer.WriteU32(report_block.extended_high_sequence_number);
big_endian_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.
big_endian_writer.WriteU32(report_block.last_sr);
// Delay since last received report, time since we received the report.
big_endian_writer.WriteU32(report_block.delay_since_last_sr);
}
void RtcpSender::BuildSdec(std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 12 + c_name_.length(), kIpPacketSize)
<< "Not enough buffer space";
if (start_size + 12 > kIpPacketSize) return;
// SDES Source Description.
packet->resize(start_size + 10);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 10);
// We always need to add one SDES CNAME.
big_endian_writer.WriteU8(0x80 + 1);
big_endian_writer.WriteU8(kPacketTypeSdes);
// Handle SDES length later on.
uint32 sdes_length_position = static_cast<uint32>(start_size) + 3;
big_endian_writer.WriteU16(0);
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU8(1); // CNAME = 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;
// We must have a zero field even if we have an even multiple of 4 bytes.
if ((packet->size() % 4) == 0) {
padding++;
packet->push_back(0);
}
while ((packet->size() % 4) != 0) {
padding++;
packet->push_back(0);
}
sdes_length += padding;
// In 32-bit words minus one and we don't count the header.
uint8 buffer_length = static_cast<uint8>((sdes_length / 4) - 1);
(*packet)[sdes_length_position] = buffer_length;
}
void RtcpSender::BuildPli(uint32 remote_ssrc,
std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 12, kIpPacketSize) << "Not enough buffer space";
if (start_size + 12 > kIpPacketSize) return;
packet->resize(start_size + 12);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 12);
uint8 FMT = 1; // Picture loss indicator.
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(kPacketTypePayloadSpecific);
big_endian_writer.WriteU16(2); // Used fixed length of 2.
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU32(remote_ssrc); // Add the remote SSRC.
}
/*
0 1 2 3
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PB |0| Payload Type| Native Rpsi bit string |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| defined per codec ... | Padding (0) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
void RtcpSender::BuildRpsi(const RtcpRpsiMessage* rpsi,
std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 24, kIpPacketSize) << "Not enough buffer space";
if (start_size + 24 > kIpPacketSize) return;
packet->resize(start_size + 24);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 24);
uint8 FMT = 3; // Reference Picture Selection Indication.
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(kPacketTypePayloadSpecific);
// Calculate length.
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;
}
// Add padding length in bits, padding can be 0, 8, 16 or 24.
big_endian_writer.WriteU8(padding_bytes * 8);
big_endian_writer.WriteU8(rpsi->payload_type);
// Add picture ID.
for (int i = bytes_required - 1; i > 0; i--) {
big_endian_writer.WriteU8(
0x80 | static_cast<uint8>(rpsi->picture_id >> (i * 7)));
}
// Add last byte of picture ID.
big_endian_writer.WriteU8(static_cast<uint8>(rpsi->picture_id & 0x7f));
// Add padding.
for (int j = 0; j < padding_bytes; ++j) {
big_endian_writer.WriteU8(0);
}
}
void RtcpSender::BuildRemb(const RtcpRembMessage* remb,
std::vector<uint8>* packet) const {
size_t start_size = packet->size();
size_t remb_size = 20 + 4 * remb->remb_ssrcs.size();
DCHECK_LT(start_size + remb_size, kIpPacketSize)
<< "Not enough buffer space";
if (start_size + remb_size > kIpPacketSize) return;
packet->resize(start_size + remb_size);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), remb_size);
// Add application layer feedback.
uint8 FMT = 15;
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(kPacketTypePayloadSpecific);
big_endian_writer.WriteU8(0);
big_endian_writer.WriteU8(static_cast<uint8>(remb->remb_ssrcs.size() + 4));
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU32(0); // Remote SSRC must be 0.
big_endian_writer.WriteU32(kRemb);
big_endian_writer.WriteU8(static_cast<uint8>(remb->remb_ssrcs.size()));
// 6 bit exponent and a 18 bit mantissa.
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);
}
cast_environment_->Logging()->InsertGenericEvent(kRembBitrate,
remb->remb_bitrate);
}
void RtcpSender::BuildNack(const RtcpNackMessage* nack,
std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 16, kIpPacketSize) << "Not enough buffer space";
if (start_size + 16 > kIpPacketSize) return;
packet->resize(start_size + 16);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 16);
uint8 FMT = 1;
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(kPacketTypeGenericRtpFeedback);
big_endian_writer.WriteU8(0);
size_t nack_size_pos = start_size + 3;
big_endian_writer.WriteU8(3);
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU32(nack->remote_ssrc); // Add the remote SSRC.
// Build NACK bitmasks and write them to the Rtcp message.
// The nack list should be sorted and not contain duplicates.
size_t number_of_nack_fields = 0;
size_t max_number_of_nack_fields = std::min<size_t>(kRtcpMaxNackFields,
(kIpPacketSize - 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;
}
}
// Write the sequence number and the bitmask to the packet.
start_size = packet->size();
DCHECK_LT(start_size + 4, kIpPacketSize) << "Not enough buffer space";
if (start_size + 4 > kIpPacketSize) return;
packet->resize(start_size + 4);
net::BigEndianWriter big_endian_nack_writer(&((*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(std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 8, kIpPacketSize) << "Not enough buffer space";
if (start_size + 8 > kIpPacketSize) return;
packet->resize(start_size + 8);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 8);
big_endian_writer.WriteU8(0x80 + 1);
big_endian_writer.WriteU8(kPacketTypeBye);
big_endian_writer.WriteU16(1); // Length.
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
}
/*
0 1 2 3
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|reserved | PT=XR=207 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=5 | reserved | block length |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| SSRC_1 (SSRC of first receiver) | sub-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
| last RR (LRR) | 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| delay since last RR (DLRR) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*/
void RtcpSender::BuildDlrrRb(const RtcpDlrrReportBlock* dlrr,
std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 24, kIpPacketSize) << "Not enough buffer space";
if (start_size + 24 > kIpPacketSize) return;
packet->resize(start_size + 24);
net::BigEndianWriter big_endian_writer(&((*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);
}
void RtcpSender::BuildRrtr(const RtcpReceiverReferenceTimeReport* rrtr,
std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 20, kIpPacketSize) << "Not enough buffer space";
if (start_size + 20 > kIpPacketSize) return;
packet->resize(start_size + 20);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 20);
big_endian_writer.WriteU8(0x80);
big_endian_writer.WriteU8(kPacketTypeXr);
big_endian_writer.WriteU16(4); // Length.
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU8(4); // Add block type.
big_endian_writer.WriteU8(0); // Add reserved.
big_endian_writer.WriteU16(2); // Block length.
// Add the media (received RTP) SSRC.
big_endian_writer.WriteU32(rrtr->ntp_seconds);
big_endian_writer.WriteU32(rrtr->ntp_fraction);
}
void RtcpSender::BuildCast(const RtcpCastMessage* cast,
std::vector<uint8>* packet) const {
size_t start_size = packet->size();
DCHECK_LT(start_size + 20, kIpPacketSize) << "Not enough buffer space";
if (start_size + 20 > kIpPacketSize) return;
packet->resize(start_size + 20);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 20);
uint8 FMT = 15; // Application layer feedback.
big_endian_writer.WriteU8(0x80 + FMT);
big_endian_writer.WriteU8(kPacketTypePayloadSpecific);
big_endian_writer.WriteU8(0);
size_t cast_size_pos = start_size + 3; // Save length position.
big_endian_writer.WriteU8(4);
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU32(cast->media_ssrc_); // Remote 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; // Save loss field position.
big_endian_writer.WriteU8(0); // Overwritten with number_of_loss_fields.
big_endian_writer.WriteU8(0); // Reserved.
big_endian_writer.WriteU8(0); // Reserved.
size_t number_of_loss_fields = 0;
size_t max_number_of_loss_fields = std::min<size_t>(kRtcpMaxCastLossFields,
(kIpPacketSize - packet->size()) / 4);
MissingFramesAndPacketsMap::const_iterator frame_it =
cast->missing_frames_and_packets_.begin();
for (; frame_it != cast->missing_frames_and_packets_.end() &&
number_of_loss_fields < max_number_of_loss_fields; ++frame_it) {
// Iterate through all frames with missing packets.
if (frame_it->second.empty()) {
// Special case all packets in a frame is missing.
start_size = packet->size();
packet->resize(start_size + 4);
net::BigEndianWriter big_endian_nack_writer(&((*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);
++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);
net::BigEndianWriter big_endian_nack_writer(
&((*packet)[start_size]), 4);
// Write frame and packet id to buffer before calculating bitmask.
big_endian_nack_writer.WriteU8(static_cast<uint8>(frame_it->first));
big_endian_nack_writer.WriteU16(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) {
bitmask |= (1 << shift);
++packet_it;
} else {
break;
}
}
big_endian_nack_writer.WriteU8(bitmask);
++number_of_loss_fields;
}
}
}
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::BuildSenderLog(RtcpSenderLogMessage* sender_log_message,
std::vector<uint8>* packet) const {
DCHECK(sender_log_message);
DCHECK(packet);
size_t start_size = packet->size();
size_t remaining_space = kIpPacketSize - start_size;
DCHECK_GE(remaining_space, kRtcpCastLogHeaderSize + kRtcpSenderFrameLogSize)
<< "Not enough buffer space";
if (remaining_space < kRtcpCastLogHeaderSize + kRtcpSenderFrameLogSize)
return;
size_t space_for_x_messages =
(remaining_space - kRtcpCastLogHeaderSize) / kRtcpSenderFrameLogSize;
size_t number_of_messages = std::min(space_for_x_messages,
sender_log_message->size());
size_t log_size = kRtcpCastLogHeaderSize +
number_of_messages * kRtcpSenderFrameLogSize;
packet->resize(start_size + log_size);
net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), log_size);
big_endian_writer.WriteU8(0x80 + kSenderLogSubtype);
big_endian_writer.WriteU8(kPacketTypeApplicationDefined);
big_endian_writer.WriteU16(static_cast<uint16>(2 + number_of_messages));
big_endian_writer.WriteU32(ssrc_); // Add our own SSRC.
big_endian_writer.WriteU32(kCast);
for (; number_of_messages > 0; --number_of_messages) {
DCHECK(!sender_log_message->empty());
const RtcpSenderFrameLogMessage& message = sender_log_message->front();
big_endian_writer.WriteU8(static_cast<uint8>(message.frame_status));
// We send the 24 east significant bits of the RTP timestamp.
big_endian_writer.WriteU8(static_cast<uint8>(message.rtp_timestamp >> 16));
big_endian_writer.WriteU8(static_cast<uint8>(message.rtp_timestamp >> 8));
big_endian_writer.WriteU8(static_cast<uint8>(message.rtp_timestamp));
sender_log_message->pop_front();
}
}
void RtcpSender::BuildReceiverLog(RtcpReceiverLogMessage* receiver_log_message,
std::vector<uint8>* packet) const {
DCHECK(receiver_log_message);
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;
if (!ScanRtcpReceiverLogMessage(*receiver_log_message,
packet_start_size,
&number_of_frames,
&total_number_of_messages_to_send,
&rtcp_log_size)) {
return;
}
packet->resize(packet_start_size + rtcp_log_size);
net::BigEndianWriter big_endian_writer(&((*packet)[packet_start_size]),
rtcp_log_size);
big_endian_writer.WriteU8(0x80 + kReceiverLogSubtype);
big_endian_writer.WriteU8(kPacketTypeApplicationDefined);
big_endian_writer.WriteU16(static_cast<uint16>(2 + 2 * number_of_frames +
total_number_of_messages_to_send));
big_endian_writer.WriteU32(ssrc_); // Add our own 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();
// Add our frame header.
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) {
// 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.
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 kAckSent:
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 kPacketReceived:
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()) {
// We sent all messages on this frame; pop the frame header.
receiver_log_message->pop_front();
}
}
DCHECK_EQ(total_number_of_messages_to_send, 0);
}
} // namespace cast
} // namespace media