diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-24 04:40:07 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-24 04:40:07 +0000 |
commit | 044687857c9eddcb15f8fb71f13d5d292b7360b1 (patch) | |
tree | 4f9ead5c473e4add37745cabd255c171cd727a34 | |
parent | 342000b5ca015e050f1ee3286fd5c1b5b1c8fbec (diff) | |
download | chromium_src-044687857c9eddcb15f8fb71f13d5d292b7360b1.zip chromium_src-044687857c9eddcb15f8fb71f13d5d292b7360b1.tar.gz chromium_src-044687857c9eddcb15f8fb71f13d5d292b7360b1.tar.bz2 |
Implemented RtcpWriter for sending RTCP Receiver Reports.
BUG=None
TEST=Unittests
Review URL: http://codereview.chromium.org/5122008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67215 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | remoting/protocol/rtcp_writer.cc | 39 | ||||
-rw-r--r-- | remoting/protocol/rtcp_writer.h | 43 | ||||
-rw-r--r-- | remoting/protocol/rtp_reader.cc | 37 | ||||
-rw-r--r-- | remoting/protocol/rtp_reader.h | 5 | ||||
-rw-r--r-- | remoting/protocol/rtp_utils.cc | 93 | ||||
-rw-r--r-- | remoting/protocol/rtp_utils.h | 54 | ||||
-rw-r--r-- | remoting/protocol/rtp_video_reader.cc | 20 | ||||
-rw-r--r-- | remoting/protocol/rtp_video_reader.h | 11 | ||||
-rw-r--r-- | remoting/protocol/rtp_writer.cc | 6 | ||||
-rw-r--r-- | remoting/protocol/rtp_writer.h | 4 | ||||
-rw-r--r-- | remoting/protocol/session.h | 1 | ||||
-rw-r--r-- | remoting/remoting.gyp | 2 |
12 files changed, 298 insertions, 17 deletions
diff --git a/remoting/protocol/rtcp_writer.cc b/remoting/protocol/rtcp_writer.cc new file mode 100644 index 0000000..c7d53f0 --- /dev/null +++ b/remoting/protocol/rtcp_writer.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2010 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 "remoting/protocol/rtcp_writer.h" + +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "remoting/base/compound_buffer.h" +#include "remoting/protocol/buffered_socket_writer.h" +#include "remoting/protocol/rtp_utils.h" + +namespace remoting { +namespace protocol { + +RtcpWriter::RtcpWriter() { +} + +RtcpWriter::~RtcpWriter() { } + +// Initializes the writer. Must be called on the thread the sockets +// belong to. +void RtcpWriter::Init(net::Socket* socket) { + buffered_rtcp_writer_ = new BufferedDatagramWriter(); + buffered_rtcp_writer_->Init(socket, NULL); +} + +void RtcpWriter::SendReport(const RtcpReceiverReport& report) { + int size = GetRtcpReceiverReportSize(report); + net::IOBufferWithSize* buffer = new net::IOBufferWithSize(size); + + PackRtcpReceiverReport(report, reinterpret_cast<uint8*>(buffer->data()), + size); + + buffered_rtcp_writer_->Write(buffer); +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/rtcp_writer.h b/remoting/protocol/rtcp_writer.h new file mode 100644 index 0000000..1e16658 --- /dev/null +++ b/remoting/protocol/rtcp_writer.h @@ -0,0 +1,43 @@ +// Copyright (c) 2010 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. + +#ifndef REMOTING_PROTOCOL_RTCP_WRITER_H_ +#define REMOTING_PROTOCOL_RTCP_WRITER_H_ + +#include "net/socket/socket.h" + +namespace remoting { + +class CompoundBuffer; + +namespace protocol { + +class BufferedDatagramWriter; +struct RtcpReceiverReport; + +class RtcpWriter { + public: + RtcpWriter(); + virtual ~RtcpWriter(); + + // Initializes the writer. Must be called on the thread the socket + // belongs to. + void Init(net::Socket* socket); + + // Sends next packet. The packet is mutated by + void SendReport(const RtcpReceiverReport& report); + + // Returns number of packets queued in the buffer. + int GetPendingPackets(); + + private: + scoped_refptr<BufferedDatagramWriter> buffered_rtcp_writer_; + + DISALLOW_COPY_AND_ASSIGN(RtcpWriter); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_RTCP_WRITER_H_ diff --git a/remoting/protocol/rtp_reader.cc b/remoting/protocol/rtp_reader.cc index f36dc8b..bee2b98 100644 --- a/remoting/protocol/rtp_reader.cc +++ b/remoting/protocol/rtp_reader.cc @@ -11,6 +11,8 @@ namespace remoting { namespace protocol { namespace { +const int kInitialSequenceNumber = -1; + // Recomended values from RTP spec. const int kMaxDropout = 3000; const int kMaxMisorder = 100; @@ -24,7 +26,8 @@ RtpPacket::~RtpPacket() { } RtpReader::RtpReader() : max_sequence_number_(0), wrap_around_count_(0), - started_(false) { + start_sequence_number_(kInitialSequenceNumber), + total_packets_received_(0) { } RtpReader::~RtpReader() { @@ -59,9 +62,9 @@ void RtpReader::OnDataReceived(net::IOBuffer* buffer, int data_size) { uint16 sequence_number = packet->header().sequence_number; - // Reset |max_sequence_number_| after we've received first packet. - if (!started_) { - started_ = true; + // Reset |start_sequence_number_| after we've received first packet. + if (start_sequence_number_ == kInitialSequenceNumber) { + start_sequence_number_ = sequence_number; max_sequence_number_ = sequence_number; } @@ -83,8 +86,34 @@ void RtpReader::OnDataReceived(net::IOBuffer* buffer, int data_size) { max_sequence_number_ = sequence_number; } + ++total_packets_received_; + on_message_callback_->Run(packet); } +void RtpReader::GetReceiverReport(RtcpReceiverReport* report) { + int expected_packets = start_sequence_number_ >= 0 ? + 1 + max_sequence_number_ - start_sequence_number_ : 0; + if (expected_packets > total_packets_received_) { + report->total_lost_packets = expected_packets - total_packets_received_; + } else { + report->total_lost_packets = 0; + } + + double loss_fraction = expected_packets > 0 ? + report->total_lost_packets / expected_packets : 0.0L; + DCHECK_GE(loss_fraction, 0.0); + DCHECK_LE(loss_fraction, 1.0); + report->loss_fraction = static_cast<uint8>(255 * loss_fraction); + + report->last_sequence_number = max_sequence_number_; + + // TODO(sergeyu): Implement jitter calculation. + // + // TODO(sergeyu): Set last_sender_report_timestamp and + // last_sender_report_delay fields when sender reports are + // implemented. +} + } // namespace protocol } // namespace remoting diff --git a/remoting/protocol/rtp_reader.h b/remoting/protocol/rtp_reader.h index 1af52b6..92ee2ee 100644 --- a/remoting/protocol/rtp_reader.h +++ b/remoting/protocol/rtp_reader.h @@ -60,6 +60,8 @@ class RtpReader : public SocketReaderBase { // |socket|. void Init(net::Socket* socket, OnMessageCallback* on_message_callback); + void GetReceiverReport(RtcpReceiverReport* report); + protected: friend class RtpVideoReaderTest; @@ -70,7 +72,8 @@ class RtpReader : public SocketReaderBase { uint16 max_sequence_number_; uint16 wrap_around_count_; - bool started_; + int start_sequence_number_; + int total_packets_received_; DISALLOW_COPY_AND_ASSIGN(RtpReader); }; diff --git a/remoting/protocol/rtp_utils.cc b/remoting/protocol/rtp_utils.cc index 4075c7b..3a6a9340 100644 --- a/remoting/protocol/rtp_utils.cc +++ b/remoting/protocol/rtp_utils.cc @@ -20,6 +20,11 @@ const int kRtpBaseHeaderSize = 12; const uint8 kRtpVersionNumber = 2; const int kRtpMaxSources = 16; const int kBytesPerCSRC = 4; +const int kRtcpBaseHeaderSize = 4; +const int kRtcpReceiverReportSize = 28; +const int kRtcpReceiverReportTotalSize = + kRtcpBaseHeaderSize + kRtcpReceiverReportSize; +const int kRtcpReceiverReportPacketType = 201; } // namespace static inline uint8 ExtractBits(uint8 byte, int bits_count, int shift) { @@ -105,6 +110,94 @@ int UnpackRtpHeader(const uint8* buffer, int buffer_size, RtpHeader* header) { return GetRtpHeaderSize(*header); } +// RTCP receiver report: +// +// 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| RC=1 | PT=201 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of the reportee | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | fraction lost | cumulative number of packets lost | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | extended highest sequence number received | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | interarrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | last SR (LSR) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last SR (DLSR) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +int GetRtcpReceiverReportSize(const RtcpReceiverReport& report) { + return kRtcpReceiverReportTotalSize; +} + +void PackRtcpReceiverReport(const RtcpReceiverReport& report, + uint8* buffer, int buffer_size) { + CHECK_GE(buffer_size, GetRtcpReceiverReportSize(report)); + + buffer[0] = (kRtpVersionNumber << 6) | + 1 /* RC=1 */; + buffer[1] = kRtcpReceiverReportPacketType; + SetBE16(buffer + 2, kRtcpReceiverReportSize); + + SetBE32(buffer + 4, report.receiver_ssrc); + SetBE32(buffer + 8, report.sender_ssrc); + SetBE32(buffer + 12, report.total_lost_packets & 0xFFFFFF); + buffer[12] = report.loss_fraction; + SetBE32(buffer + 16, report.last_sequence_number); + SetBE32(buffer + 20, report.jitter); + SetBE32(buffer + 24, report.last_sender_report_timestamp); + SetBE32(buffer + 28, report.last_sender_report_delay); +} + +int UnpackRtcpReceiverReport(const uint8* buffer, int buffer_size, + RtcpReceiverReport* report) { + if (buffer_size < kRtcpReceiverReportTotalSize) { + return -1; + } + + int version = ExtractBits(buffer[0], 2, 6); + if (version != kRtpVersionNumber) { + return -1; + } + + int report_count = ExtractBits(buffer[0], 5, 0); + if (report_count != 1) { + // Received RTCP packet with more than one report. This isn't + // supported in the current implementation. + return -1; + } + + int packet_type = buffer[1]; + if (packet_type != kRtcpReceiverReportPacketType) { + // The packet isn't receiver report. + return -1; + } + + int report_size = GetBE16(buffer + 2); + if (report_size != kRtcpReceiverReportSize) { + // Invalid size of the report. + return -1; + } + + report->receiver_ssrc = GetBE32(buffer + 4); + report->sender_ssrc = GetBE32(buffer + 8); + report->loss_fraction = buffer[12]; + report->total_lost_packets = GetBE32(buffer + 12) & 0xFFFFFF; + report->last_sequence_number = GetBE32(buffer + 16); + report->jitter = GetBE32(buffer + 20); + report->last_sender_report_timestamp = GetBE32(buffer + 24); + report->last_sender_report_delay = GetBE32(buffer + 28); + + return kRtcpReceiverReportTotalSize; +} + // VP8 Payload Descriptor format: // // 0 1 2 3 diff --git a/remoting/protocol/rtp_utils.h b/remoting/protocol/rtp_utils.h index 1c2fa4e..599ffaf 100644 --- a/remoting/protocol/rtp_utils.h +++ b/remoting/protocol/rtp_utils.h @@ -35,6 +35,28 @@ struct RtpHeader { uint32 source_id[15]; }; +struct RtcpReceiverReport { + RtcpReceiverReport() + : receiver_ssrc(0), + sender_ssrc(0), + loss_fraction(0), + total_lost_packets(0), + last_sequence_number(0), + jitter(0), + last_sender_report_timestamp(0), + last_sender_report_delay(0) { + } + + uint32 receiver_ssrc; + uint32 sender_ssrc; + uint8 loss_fraction; // 8bit fixed point value in the interval [0..1]. + uint32 total_lost_packets; + uint32 last_sequence_number; + uint32 jitter; + uint32 last_sender_report_timestamp; + uint32 last_sender_report_delay; +}; + // Vp8Descriptor struct used to store values of the VP8 RTP descriptor // fields. Meaning of each field is documented in the RTP Payload // Format for VP8 spec: http://www.webmproject.org/code/specs/rtp/ . @@ -67,15 +89,43 @@ int GetRtpHeaderSize(const RtpHeader& header); // Packs RTP header into the buffer. void PackRtpHeader(const RtpHeader& header, uint8* buffer, int buffer_size); -// Unpacks RTP header and stores unpacked values in |header|. If the header -// is not valid returns -1, otherwise returns size of the header. +// Unpacks RTP header and stores unpacked values in |header|. int UnpackRtpHeader(const uint8* buffer, int buffer_size, RtpHeader* header); +// Three following functions below are used to pack and unpack RTCP +// Receiver Report packets. They implement only subset of RTCP that is +// useful for chromoting. Particularly there are following +// limitations: +// +// 1. Only one report per packet. There is always only one sender and +// only one receiver in chromotocol session, so we never need to +// have more than one report per packet. +// 2. No RTCP Sender Report. Sender Reports are useful for streams +// synchronization (e.g. audio/video syncronization), but it is +// not needed for screencasts. + +// Returns size of RTCP Receiver Report packet. +int GetRtcpReceiverReportSize(const RtcpReceiverReport& report); + +// Packs RTCP Receiver Report into the |buffer|. +void PackRtcpReceiverReport(const RtcpReceiverReport& report, + uint8* buffer, int buffer_size); + +// Unpack RTCP Receiver Report packet. If the packet is invalid +// returns -1, othewise returns size of the data that was read from +// the packet. +int UnpackRtcpReceiverReport(const uint8* buffer, int buffer_size, + RtcpReceiverReport* report); + +// Returns size of VP8 Payload Descriptor. int GetVp8DescriptorSize(const Vp8Descriptor& descriptor); +// Packs VP8 Payload Descriptor into the |buffer|. void PackVp8Descriptor(const Vp8Descriptor& descriptor, uint8* buffer, int buffer_size); +// Unpacks VP8 Payload Descriptor. If the descriptor is not valid +// returns -1, otherwise returns size of the descriptor. int UnpackVp8Descriptor(const uint8* buffer, int buffer_size, Vp8Descriptor* descriptor); diff --git a/remoting/protocol/rtp_video_reader.cc b/remoting/protocol/rtp_video_reader.cc index 6a3ddd9..d7e9e52 100644 --- a/remoting/protocol/rtp_video_reader.cc +++ b/remoting/protocol/rtp_video_reader.cc @@ -13,6 +13,7 @@ namespace protocol { namespace { const int kMaxPacketsInQueue = 1024; +const int kReceiverReportsIntervalMs = 1000; } // namespace RtpVideoReader::PacketsQueueEntry::PacketsQueueEntry() @@ -31,6 +32,7 @@ RtpVideoReader::~RtpVideoReader() { void RtpVideoReader::Init(protocol::Session* session, VideoStub* video_stub) { rtp_reader_.Init(session->video_rtp_channel(), NewCallback(this, &RtpVideoReader::OnRtpPacket)); + rtcp_writer_.Init(session->video_rtcp_channel()); video_stub_ = video_stub; } @@ -167,6 +169,24 @@ void RtpVideoReader::RebuildVideoPacket(PacketsQueue::iterator first, packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); video_stub_->ProcessVideoPacket(packet, new DeleteTask<VideoPacket>(packet)); + + SendReceiverReportIf(); +} + +void RtpVideoReader::SendReceiverReportIf() { + base::Time now = base::Time::Now(); + + // Send receiver report only if we haven't sent any bofore, or + // enough time has passed since the last report. + if (last_receiver_report_.is_null() || + (now - last_receiver_report_).InMilliseconds() > + kReceiverReportsIntervalMs) { + RtcpReceiverReport report; + rtp_reader_.GetReceiverReport(&report); + rtcp_writer_.SendReport(report); + + last_receiver_report_ = now; + } } } // namespace protocol diff --git a/remoting/protocol/rtp_video_reader.h b/remoting/protocol/rtp_video_reader.h index 6d63714..d8d89f7 100644 --- a/remoting/protocol/rtp_video_reader.h +++ b/remoting/protocol/rtp_video_reader.h @@ -5,6 +5,8 @@ #ifndef REMOTING_PROTOCOL_RTP_VIDEO_READER_H_ #define REMOTING_PROTOCOL_RTP_VIDEO_READER_H_ +#include "base/time.h" +#include "remoting/protocol/rtcp_writer.h" #include "remoting/protocol/rtp_reader.h" #include "remoting/protocol/video_reader.h" @@ -49,11 +51,20 @@ class RtpVideoReader : public VideoReader { PacketsQueue::iterator to); void ResetQueue(); + // Helper method that sends RTCP receiver reports if enough time has + // passed since the last report. It is called from + // OnRtpPacket(). Interval between reports is defined by + // |kReceiverReportsIntervalMs|. + void SendReceiverReportIf(); + RtpReader rtp_reader_; + RtcpWriter rtcp_writer_; PacketsQueue packets_queue_; uint32 last_sequence_number_; + base::Time last_receiver_report_; + // The stub that processes all received packets. VideoStub* video_stub_; diff --git a/remoting/protocol/rtp_writer.cc b/remoting/protocol/rtp_writer.cc index 4ee700e..7341741 100644 --- a/remoting/protocol/rtp_writer.cc +++ b/remoting/protocol/rtp_writer.cc @@ -76,11 +76,5 @@ int RtpWriter::GetPendingPackets() { return buffered_rtp_writer_->GetBufferChunks(); } -// Stop writing and drop pending data. Must be called from the same thread as -// Init(). -void RtpWriter::Close() { - buffered_rtp_writer_->Close(); -} - } // namespace protocol } // namespace remoting diff --git a/remoting/protocol/rtp_writer.h b/remoting/protocol/rtp_writer.h index 60c6f0c..cd81bcf 100644 --- a/remoting/protocol/rtp_writer.h +++ b/remoting/protocol/rtp_writer.h @@ -32,10 +32,6 @@ class RtpWriter { // Returns number of packets queued in the buffer. int GetPendingPackets(); - // Stop writing and drop pending data. Must be called from the same thread as - // Init(). - void Close(); - private: uint32 last_packet_number_; diff --git a/remoting/protocol/session.h b/remoting/protocol/session.h index fd100e0..cbd857b 100644 --- a/remoting/protocol/session.h +++ b/remoting/protocol/session.h @@ -8,6 +8,7 @@ #include <string> #include "base/callback.h" +#include "remoting/protocol/buffered_socket_writer.h" #include "remoting/protocol/session_config.h" class MessageLoop; diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index e2c3ece..91dd1c0 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -386,6 +386,8 @@ 'protocol/protobuf_video_reader.h', 'protocol/protobuf_video_writer.cc', 'protocol/protobuf_video_writer.h', + 'protocol/rtcp_writer.cc', + 'protocol/rtcp_writer.h', 'protocol/rtp_reader.cc', 'protocol/rtp_reader.h', 'protocol/rtp_utils.cc', |