Chromium Code Reviews| Index: media/cast/rtcp/rtcp_sender.cc |
| diff --git a/media/cast/rtcp/rtcp_sender.cc b/media/cast/rtcp/rtcp_sender.cc |
| index 465e9c8c48b8771e491ab8fadab97ef7e79a9910..b5999331a7bbccb70426a82a245d29b055094be2 100644 |
| --- a/media/cast/rtcp/rtcp_sender.cc |
| +++ b/media/cast/rtcp/rtcp_sender.cc |
| @@ -13,12 +13,102 @@ |
| #include "media/cast/rtcp/rtcp_utility.h" |
| #include "net/base/big_endian.h" |
| +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(); |
|
mikhal
2013/11/21 16:43:20
The receiver also includes jitter and packet loss
pwestin
2013/11/22 18:50:47
The difference is that this is intended for timing
|
| + } |
| + 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, 16) << "Not enough buffer space"; |
| + if (remaining_space < 16) return false; |
| + |
| + // Account for the RTCP header for an application-defined packet. |
| + remaining_space -= 12; |
|
mikhal
2013/11/21 16:43:20
use consts, here and elsewhere.
pwestin
2013/11/22 18:50:47
Done.
|
| + |
| + 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 -= 8; |
| + |
| + size_t messages_in_frame = frame_it->event_log_messages.size(); |
| + size_t remaining_space_in_messages = remaining_space / 4; |
| + 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 * 4; |
| + *total_number_of_messages_to_send += |
| + media::cast::kRtcpMaxReceiverLogMessages; |
| + break; |
| + } |
| + remaining_space -= messages_to_send * 4; |
| + *total_number_of_messages_to_send += messages_to_send; |
| + |
| + if (remaining_space < 12) { |
| + // Make sure that we have room for at least one more message. |
| + break; |
| + } |
| + } |
| + *rtcp_log_size = 12 + *number_of_frames * 8 + |
| + *total_number_of_messages_to_send * 4; |
| + 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 { |
| -static const size_t kRtcpMaxNackFields = 253; |
| -static const size_t kRtcpMaxCastLossFields = 100; |
| - |
| RtcpSender::RtcpSender(PacedPacketSender* outgoing_transport, |
| uint32 sending_ssrc, |
| const std::string& c_name) |
| @@ -33,7 +123,7 @@ RtcpSender::~RtcpSender() {} |
| void RtcpSender::SendRtcpFromRtpSender(uint32 packet_type_flags, |
| const RtcpSenderInfo* sender_info, |
| const RtcpDlrrReportBlock* dlrr, |
| - const RtcpSenderLogMessage* sender_log) { |
| + RtcpSenderLogMessage* sender_log) { |
| if (packet_type_flags & kRtcpRr || |
| packet_type_flags & kRtcpPli || |
| packet_type_flags & kRtcpRrtr || |
| @@ -74,7 +164,7 @@ void RtcpSender::SendRtcpFromRtpReceiver( |
| const RtcpReportBlock* report_block, |
| const RtcpReceiverReferenceTimeReport* rrtr, |
| const RtcpCastMessage* cast_message, |
| - const RtcpReceiverLogMessage* receiver_log) { |
| + RtcpReceiverLogMessage* receiver_log) { |
| if (packet_type_flags & kRtcpSr || |
| packet_type_flags & kRtcpDlrr || |
| packet_type_flags & kRtcpSenderLog) { |
| @@ -129,7 +219,7 @@ void RtcpSender::BuildSR(const RtcpSenderInfo& sender_info, |
| net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 28); |
| big_endian_writer.WriteU8(0x80 + (report_block ? 1 : 0)); |
| - big_endian_writer.WriteU8(200); |
| + 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); |
| @@ -154,7 +244,7 @@ void RtcpSender::BuildRR(const RtcpReportBlock* report_block, |
| net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 8); |
| big_endian_writer.WriteU8(0x80 + (report_block ? 1 : 0)); |
| - big_endian_writer.WriteU8(201); |
| + big_endian_writer.WriteU8(kPacketTypeReceiverReport); |
| big_endian_writer.WriteU16(number_of_rows); |
| big_endian_writer.WriteU32(ssrc_); |
| @@ -203,7 +293,7 @@ void RtcpSender::BuildSdec(std::vector<uint8>* packet) const { |
| 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(202); |
| + big_endian_writer.WriteU8(kPacketTypeSdes); |
| // Handle SDES length later on. |
| uint32 sdes_length_position = static_cast<uint32>(start_size) + 3; |
| @@ -245,7 +335,7 @@ void RtcpSender::BuildPli(uint32 remote_ssrc, |
| 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(206); |
| + 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. |
| @@ -274,7 +364,7 @@ void RtcpSender::BuildRpsi(const RtcpRpsiMessage* rpsi, |
| 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(206); |
| + big_endian_writer.WriteU8(kPacketTypePayloadSpecific); |
| // Calculate length. |
| uint32 bits_required = 7; |
| @@ -331,7 +421,7 @@ void RtcpSender::BuildRemb(const RtcpRembMessage* remb, |
| // Add application layer feedback. |
| uint8 FMT = 15; |
| big_endian_writer.WriteU8(0x80 + FMT); |
| - big_endian_writer.WriteU8(206); |
| + 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. |
| @@ -371,7 +461,7 @@ void RtcpSender::BuildNack(const RtcpNackMessage* nack, |
| uint8 FMT = 1; |
| big_endian_writer.WriteU8(0x80 + FMT); |
| - big_endian_writer.WriteU8(205); |
| + big_endian_writer.WriteU8(kPacketTypeGenericRtpFeedback); |
| big_endian_writer.WriteU8(0); |
| size_t nack_size_pos = start_size + 3; |
| big_endian_writer.WriteU8(3); |
| @@ -425,7 +515,7 @@ void RtcpSender::BuildBye(std::vector<uint8>* packet) const { |
| net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 8); |
| big_endian_writer.WriteU8(0x80 + 1); |
| - big_endian_writer.WriteU8(203); |
| + big_endian_writer.WriteU8(kPacketTypeBye); |
| big_endian_writer.WriteU16(1); // Length. |
| big_endian_writer.WriteU32(ssrc_); // Add our own SSRC. |
| } |
| @@ -457,7 +547,7 @@ void RtcpSender::BuildDlrrRb(const RtcpDlrrReportBlock* dlrr, |
| net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 24); |
| big_endian_writer.WriteU8(0x80); |
| - big_endian_writer.WriteU8(207); |
| + 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. |
| @@ -479,7 +569,7 @@ void RtcpSender::BuildRrtr(const RtcpReceiverReferenceTimeReport* rrtr, |
| net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 20); |
| big_endian_writer.WriteU8(0x80); |
| - big_endian_writer.WriteU8(207); |
| + 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. |
| @@ -502,7 +592,7 @@ void RtcpSender::BuildCast(const RtcpCastMessage* cast, |
| 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(206); |
| + 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); |
| @@ -573,17 +663,126 @@ void RtcpSender::BuildCast(const RtcpCastMessage* cast, |
| cast->missing_frames_and_packets_.size()); |
| } |
| -void RtcpSender::BuildSenderLog(const RtcpSenderLogMessage* sender_log_message, |
| +void RtcpSender::BuildSenderLog(RtcpSenderLogMessage* sender_log_message, |
| std::vector<uint8>* packet) const { |
| - // TODO(pwestin): Implement. |
| - NOTIMPLEMENTED(); |
| + DCHECK(sender_log_message); |
| + DCHECK(packet); |
| + static const size_t kRtcpMinSendLogSize = 16; |
| + size_t start_size = packet->size(); |
| + size_t remaining_space = kIpPacketSize - start_size; |
| + DCHECK_GE(remaining_space, kRtcpMinSendLogSize) << "Not enough buffer space"; |
| + if (remaining_space < kRtcpMinSendLogSize) return; |
| + |
| + size_t space_for_x_messages = (remaining_space - 12) / 4; |
| + size_t number_of_messages = std::min(space_for_x_messages, |
| + sender_log_message->size()); |
| + |
| + size_t log_size = 12 + number_of_messages * 4; |
| + 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(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( |
| - const RtcpReceiverLogMessage* receiver_log_message, |
| - std::vector<uint8>* packet) const { |
| - // TODO(pwestin): Implement. |
| - NOTIMPLEMENTED(); |
| +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(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(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 |