blob: a7fb6509c23a7b521a7106976e5f3ec1f4d5c8e5 [file] [log] [blame]
// Copyright 2014 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/net/rtcp/rtcp_builder.h"
#include <stdint.h>
#include <algorithm>
#include <vector>
#include "base/logging.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_t kMaxWireFormatTimeDeltaMs = INT64_C(0xfff);
uint16_t MergeEventTypeAndTimestampForWireFormat(
const CastLoggingEvent& event,
const base::TimeDelta& time_delta) {
int64_t time_delta_ms = time_delta.InMilliseconds();
DCHECK_GE(time_delta_ms, 0);
DCHECK_LE(time_delta_ms, kMaxWireFormatTimeDeltaMs);
uint16_t time_delta_12_bits =
static_cast<uint16_t>(time_delta_ms & kMaxWireFormatTimeDeltaMs);
uint16_t 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;
}
// A class to build a string representing the NACK list in Cast message.
//
// The string will look like "F23:3-6 F25: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 "F26:65535".
class NackStringBuilder {
public:
NackStringBuilder()
: frame_count_(0),
packet_count_(0),
last_packet_id_(-1),
contiguous_sequence_(false) {}
~NackStringBuilder() = default;
bool Empty() const { return frame_count_ == 0; }
void PushFrame(FrameId frame_id) {
DCHECK(!frame_id.is_null());
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(!last_frame_id_.is_null());
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_;
FrameId last_frame_id_;
int last_packet_id_;
bool contiguous_sequence_;
};
} // namespace
RtcpBuilder::RtcpBuilder(uint32_t sending_ssrc)
: writer_(nullptr, 0), local_ssrc_(sending_ssrc), ptr_of_length_(nullptr) {}
RtcpBuilder::~RtcpBuilder() = default;
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_ = nullptr;
}
}
// 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(nullptr, 0);
PacketRef ret = packet_;
packet_.reset();
return ret;
}
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(local_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(local_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::AddPli(const RtcpPliMessage& pli_message) {
AddRtcpHeader(kPacketTypePayloadSpecific, 1);
writer_.WriteU32(local_ssrc_);
writer_.WriteU32(pli_message.remote_ssrc);
}
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(local_ssrc_); // Add our own SSRC.
writer_.WriteU32(cast.remote_ssrc); // Remote SSRC.
writer_.WriteU32(kCast);
writer_.WriteU8(cast.ack_frame_id.lower_8_bits());
uint8_t* cast_loss_field_pos = reinterpret_cast<uint8_t*>(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());
size_t number_of_loss_fields = 0;
size_t max_number_of_loss_fields = std::min<size_t>(
kRtcpMaxCastLossFields, writer_.remaining() / 4);
auto 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(frame_it->first.lower_8_bits());
writer_.WriteU16(kRtcpCastAllPacketsLost);
writer_.WriteU8(0);
nack_string_builder.PushPacket(kRtcpCastAllPacketsLost);
++number_of_loss_fields;
} else {
auto packet_it = frame_it->second.begin();
while (packet_it != frame_it->second.end()) {
uint16_t packet_id = *packet_it;
// Write frame and packet id to buffer before calculating bitmask.
writer_.WriteU8(frame_it->first.lower_8_bits());
writer_.WriteU16(packet_id);
nack_string_builder.PushPacket(packet_id);
uint8_t bitmask = 0;
++packet_it;
while (packet_it != frame_it->second.end()) {
int shift = static_cast<uint8_t>(*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.remote_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_t>(number_of_loss_fields);
}
void RtcpBuilder::AddSR(const RtcpSenderInfo& sender_info) {
AddRtcpHeader(kPacketTypeSenderReport, 0);
writer_.WriteU32(local_ssrc_);
writer_.WriteU32(sender_info.ntp_seconds);
writer_.WriteU32(sender_info.ntp_fraction);
writer_.WriteU32(sender_info.rtp_timestamp.lower_32_bits());
writer_.WriteU32(sender_info.send_packet_count);
writer_.WriteU32(static_cast<uint32_t>(sender_info.send_octet_count));
}
/*
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 |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| SSRC1 (SSRC of first receiver) | sub-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
| last RR (LRR) | 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| delay since last RR (DLRR) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*/
void RtcpBuilder::AddDlrrRb(const RtcpDlrrReportBlock& dlrr) {
AddRtcpHeader(kPacketTypeXr, 0);
writer_.WriteU32(local_ssrc_); // Add our own SSRC.
writer_.WriteU8(5); // Add block type.
writer_.WriteU8(0); // Add reserved.
writer_.WriteU16(3); // Block length.
writer_.WriteU32(local_ssrc_); // Add the media (received RTP) SSRC.
writer_.WriteU32(dlrr.last_rr);
writer_.WriteU32(dlrr.delay_since_last_rr);
}
void RtcpBuilder::AddReceiverLog(
const ReceiverRtcpEventSubscriber::RtcpEvents& 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(local_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_.lower_32_bits());
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_t>(messages_in_frame - 1));
base::TimeTicks event_timestamp_base =
frame_log_messages.event_log_messages_.front().event_timestamp;
uint32_t base_timestamp_ms =
(event_timestamp_base - base::TimeTicks()).InMilliseconds();
writer_.WriteU8(static_cast<uint8_t>(base_timestamp_ms >> 16));
writer_.WriteU8(static_cast<uint8_t>(base_timestamp_ms >> 8));
writer_.WriteU8(static_cast<uint8_t>(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_t 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_t>(
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::RtcpEvents& rtcp_events,
RtcpReceiverLogMessage* receiver_log_message,
size_t* total_number_of_messages_to_send) {
size_t number_of_frames = 0;
size_t remaining_space = writer_.remaining();
if (remaining_space < kRtcpCastLogHeaderSize + kRtcpReceiverFrameLogSize +
kRtcpReceiverEventLogSize) {
return false;
}
// 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;
auto rit = rtcp_events.rbegin();
while (rit != rtcp_events.rend() &&
remaining_space >=
kRtcpReceiverFrameLogSize + kRtcpReceiverEventLogSize) {
const RtpTimeTicks 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.
auto 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);
}
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
} // namespace media