summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-24 04:40:07 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-24 04:40:07 +0000
commit044687857c9eddcb15f8fb71f13d5d292b7360b1 (patch)
tree4f9ead5c473e4add37745cabd255c171cd727a34
parent342000b5ca015e050f1ee3286fd5c1b5b1c8fbec (diff)
downloadchromium_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.cc39
-rw-r--r--remoting/protocol/rtcp_writer.h43
-rw-r--r--remoting/protocol/rtp_reader.cc37
-rw-r--r--remoting/protocol/rtp_reader.h5
-rw-r--r--remoting/protocol/rtp_utils.cc93
-rw-r--r--remoting/protocol/rtp_utils.h54
-rw-r--r--remoting/protocol/rtp_video_reader.cc20
-rw-r--r--remoting/protocol/rtp_video_reader.h11
-rw-r--r--remoting/protocol/rtp_writer.cc6
-rw-r--r--remoting/protocol/rtp_writer.h4
-rw-r--r--remoting/protocol/session.h1
-rw-r--r--remoting/remoting.gyp2
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',