summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authormikhal@google.com <mikhal@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-04 02:28:01 +0000
committermikhal@google.com <mikhal@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-04 02:28:01 +0000
commitc72e1a037f0d3dc4a61cb1a26395064a7fcc2ed4 (patch)
treeed7fee2f4351eddb1a6ea4b5d41a81b113fb36a5 /media
parentd363d80ee017cba160a5aec02a830e6bf39fc9d3 (diff)
downloadchromium_src-c72e1a037f0d3dc4a61cb1a26395064a7fcc2ed4.zip
chromium_src-c72e1a037f0d3dc4a61cb1a26395064a7fcc2ed4.tar.gz
chromium_src-c72e1a037f0d3dc4a61cb1a26395064a7fcc2ed4.tar.bz2
Adding rtp receiver to cast
Review URL: https://chromiumcodereview.appspot.com/23718002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221120 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/cast/cast.gyp3
-rw-r--r--media/cast/cast_receiver.gyp1
-rw-r--r--media/cast/rtp_receiver/receiver_stats.cc120
-rw-r--r--media/cast/rtp_receiver/receiver_stats.h53
-rw-r--r--media/cast/rtp_receiver/receiver_stats_unittest.cc157
-rw-r--r--media/cast/rtp_receiver/rtp_parser/include/mock/mock_rtp_feedback.h37
-rw-r--r--media/cast/rtp_receiver/rtp_parser/rtp_parser.cc107
-rw-r--r--media/cast/rtp_receiver/rtp_parser/rtp_parser.gypi25
-rw-r--r--media/cast/rtp_receiver/rtp_parser/rtp_parser.h53
-rw-r--r--media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc201
-rw-r--r--media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.cc104
-rw-r--r--media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.h51
-rw-r--r--media/cast/rtp_receiver/rtp_receiver.cc57
-rw-r--r--media/cast/rtp_receiver/rtp_receiver.gyp27
-rw-r--r--media/cast/rtp_receiver/rtp_receiver.h53
15 files changed, 1049 insertions, 0 deletions
diff --git a/media/cast/cast.gyp b/media/cast/cast.gyp
index 776bb46..33f6afd 100644
--- a/media/cast/cast.gyp
+++ b/media/cast/cast.gyp
@@ -60,6 +60,9 @@
'framer/cast_message_builder_unittest.cc',
'framer/frame_buffer_unittest.cc',
'framer/framer_unittest.cc',
+ 'rtp_receiver/receiver_stats_unittest.cc',
+ 'rtp_receiver/rtp_parser/test/rtp_packet_builder.cc',
+ 'rtp_receiver/rtp_parser/rtp_parser_unittest.cc',
'rtp_sender/packet_storage/packet_storage_unittest.cc',
'rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc',
'rtp_sender/rtp_packetizer/test/rtp_header_parser.cc',
diff --git a/media/cast/cast_receiver.gyp b/media/cast/cast_receiver.gyp
index 0fb5e38..32bc5c7 100644
--- a/media/cast/cast_receiver.gyp
+++ b/media/cast/cast_receiver.gyp
@@ -17,6 +17,7 @@
# 'cast_receiver_impl.h',
], # source
'dependencies': [
+ 'rtp_receiver/rtp_receiver.gyp:*',
# 'audio_receiver',
# 'video_receiver',
'framer/framer.gyp:cast_framer',
diff --git a/media/cast/rtp_receiver/receiver_stats.cc b/media/cast/rtp_receiver/receiver_stats.cc
new file mode 100644
index 0000000..44a9b81
--- /dev/null
+++ b/media/cast/rtp_receiver/receiver_stats.cc
@@ -0,0 +1,120 @@
+// 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/rtp_receiver/receiver_stats.h"
+
+#include "base/logging.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+static const uint32 kMaxSequenceNumber = 65536;
+
+ReceiverStats::ReceiverStats(uint32 ssrc)
+ : ssrc_(ssrc),
+ min_sequence_number_(0),
+ max_sequence_number_(0),
+ total_number_packets_(0),
+ sequence_number_cycles_(0),
+ interval_min_sequence_number_(0),
+ interval_number_packets_(0),
+ interval_wrap_count_(0),
+ default_tick_clock_(),
+ clock_(&default_tick_clock_) {}
+
+ReceiverStats::~ReceiverStats() {}
+
+void ReceiverStats::GetStatistics(uint8* fraction_lost,
+ uint32* cumulative_lost,
+ uint32* extended_high_sequence_number,
+ uint32* jitter) {
+ // Compute losses.
+ if (interval_number_packets_ == 0) {
+ *fraction_lost = 0;
+ } else {
+ int diff = 0;
+ if (interval_wrap_count_ == 0) {
+ diff = max_sequence_number_ - interval_min_sequence_number_ + 1;
+ } else {
+ diff = kMaxSequenceNumber * (interval_wrap_count_ - 1) +
+ (max_sequence_number_ - interval_min_sequence_number_ +
+ kMaxSequenceNumber + 1);
+ }
+
+ if (diff < 1) {
+ *fraction_lost = 0;
+ } else {
+ *fraction_lost = static_cast<uint8>((256 * (1 -
+ static_cast<float>(interval_number_packets_) / abs(diff))));
+ }
+ }
+
+ int expected_packets_num = max_sequence_number_ - min_sequence_number_ + 1;
+ if (total_number_packets_ == 0) {
+ *cumulative_lost = 0;
+ } else if (sequence_number_cycles_ == 0) {
+ *cumulative_lost = expected_packets_num - total_number_packets_;
+ } else {
+ *cumulative_lost = kMaxSequenceNumber * (sequence_number_cycles_ - 1) +
+ (expected_packets_num - total_number_packets_ + kMaxSequenceNumber);
+ }
+
+ // Extended high sequence number consists of the highest seq number and the
+ // number of cycles (wrap).
+ *extended_high_sequence_number = (sequence_number_cycles_ << 16) +
+ max_sequence_number_;
+
+ *jitter = static_cast<uint32>(abs(jitter_.InMilliseconds()));
+
+ // Reset interval values.
+ interval_min_sequence_number_ = 0;
+ interval_number_packets_ = 0;
+ interval_wrap_count_ = 0;
+}
+
+void ReceiverStats::UpdateStatistics(const RtpCastHeader& header) {
+ if (ssrc_ != header.webrtc.header.ssrc) return;
+
+ uint16 new_seq_num = header.webrtc.header.sequenceNumber;
+
+ if (interval_number_packets_ == 0) {
+ // First packet in the interval.
+ interval_min_sequence_number_ = new_seq_num;
+ }
+ if (total_number_packets_ == 0) {
+ // First incoming packet.
+ min_sequence_number_ = new_seq_num;
+ max_sequence_number_ = new_seq_num;
+ }
+
+ if (IsNewerSequenceNumber(new_seq_num, max_sequence_number_)) {
+ // Check wrap.
+ if (new_seq_num < max_sequence_number_) {
+ ++sequence_number_cycles_;
+ ++interval_wrap_count_;
+ }
+ max_sequence_number_ = new_seq_num;
+ }
+
+ // Compute Jitter.
+ base::TimeTicks now = clock_->NowTicks();
+ base::TimeDelta delta_new_timestamp =
+ base::TimeDelta::FromMilliseconds(header.webrtc.header.timestamp);
+ if (total_number_packets_ > 0) {
+ // Update jitter.
+ base::TimeDelta delta = (now - last_received_packet_time_) -
+ ((delta_new_timestamp - last_received_timestamp_) / 90000);
+ jitter_ += (delta - jitter_) / 16;
+ }
+ last_received_timestamp_ = delta_new_timestamp;
+ last_received_packet_time_ = now;
+
+ // Increment counters.
+ ++total_number_packets_;
+ ++interval_number_packets_;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_receiver/receiver_stats.h b/media/cast/rtp_receiver/receiver_stats.h
new file mode 100644
index 0000000..610f515
--- /dev/null
+++ b/media/cast/rtp_receiver/receiver_stats.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef MEDIA_CAST_RTP_RECEIVER_RECEIVER_STATS_H_
+#define MEDIA_CAST_RTP_RECEIVER_RECEIVER_STATS_H_
+
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+class ReceiverStats {
+ public:
+ explicit ReceiverStats(uint32 ssrc);
+ ~ReceiverStats();
+ void GetStatistics(uint8* fraction_lost,
+ uint32* cumulative_lost, // 24 bits valid.
+ uint32* extended_high_sequence_number,
+ uint32* jitter);
+ void UpdateStatistics(const RtpCastHeader& header);
+
+ void set_clock(base::TickClock* clock) {
+ clock_ = clock;
+ }
+
+ private:
+ const uint32 ssrc_;
+
+ // Global metrics.
+ uint16 min_sequence_number_;
+ uint16 max_sequence_number_;
+ uint32 total_number_packets_;
+ uint16 sequence_number_cycles_;
+ base::TimeDelta last_received_timestamp_;
+ base::TimeTicks last_received_packet_time_;
+ base::TimeDelta jitter_;
+
+ // Intermediate metrics - between RTCP reports.
+ int interval_min_sequence_number_;
+ int interval_number_packets_;
+ int interval_wrap_count_;
+ base::DefaultTickClock default_tick_clock_;
+ base::TickClock* clock_;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_RECEIVER_RECEIVER_STATS_H_
diff --git a/media/cast/rtp_receiver/receiver_stats_unittest.cc b/media/cast/rtp_receiver/receiver_stats_unittest.cc
new file mode 100644
index 0000000..c6cf91a
--- /dev/null
+++ b/media/cast/rtp_receiver/receiver_stats_unittest.cc
@@ -0,0 +1,157 @@
+// 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 <gtest/gtest.h>
+
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "media/cast/rtp_receiver/receiver_stats.h"
+
+namespace media {
+namespace cast {
+
+static const int64 kStartMillisecond = 123456789;
+static const uint32 kStdTimeIncrementMs = 33;
+static const uint32 kSsrc = 0x1234;
+
+class ReceiverStatsTest : public ::testing::Test {
+ protected:
+ ReceiverStatsTest()
+ : stats_(kSsrc),
+ rtp_header_(),
+ fraction_lost_(0),
+ cumulative_lost_(0),
+ extended_high_sequence_number_(0),
+ jitter_(0) {
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ start_time_ = testing_clock_.NowTicks();
+ delta_increments_ = base::TimeDelta::FromMilliseconds(kStdTimeIncrementMs);
+ }
+ ~ReceiverStatsTest() {}
+
+ virtual void SetUp() {
+ rtp_header_.webrtc.header.sequenceNumber = 0;
+ rtp_header_.webrtc.header.timestamp = 0;
+ rtp_header_.webrtc.header.ssrc = kSsrc;
+ }
+
+ uint32 ExpectedJitter(uint32 const_interval, int num_packets) {
+ float jitter = 0;
+ // Assume timestamps have a constant kStdTimeIncrementMs interval.
+ float float_interval =
+ static_cast<float>(const_interval - kStdTimeIncrementMs);
+ for (int i = 0; i < num_packets; ++i) {
+ jitter += (float_interval - jitter) / 16;
+ }
+ return static_cast<uint32>(jitter + 0.5f);
+ }
+
+ uint32 Timestamp() {
+ base::TimeDelta delta = testing_clock_.NowTicks() - start_time_;
+ return static_cast<uint32>(delta.InMilliseconds() * 90);
+ }
+
+ ReceiverStats stats_;
+ RtpCastHeader rtp_header_;
+ uint8 fraction_lost_;
+ uint32 cumulative_lost_;
+ uint32 extended_high_sequence_number_;
+ uint32 jitter_;
+ base::SimpleTestTickClock testing_clock_;
+ base::TimeTicks start_time_;
+ base::TimeDelta delta_increments_;
+};
+
+TEST_F(ReceiverStatsTest, ResetState) {
+ stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
+ &extended_high_sequence_number_, &jitter_);
+ EXPECT_EQ(0u, fraction_lost_);
+ EXPECT_EQ(0u, cumulative_lost_);
+ EXPECT_EQ(0u, extended_high_sequence_number_);
+ EXPECT_EQ(0u, jitter_);
+}
+
+TEST_F(ReceiverStatsTest, LossCount) {
+ for (int i = 0; i < 300; ++i) {
+ if (i % 4)
+ stats_.UpdateStatistics(rtp_header_);
+ if (i % 3) {
+ rtp_header_.webrtc.header.timestamp = Timestamp();
+ }
+ ++rtp_header_.webrtc.header.sequenceNumber;
+ testing_clock_.Advance(delta_increments_);
+ }
+ stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
+ &extended_high_sequence_number_, &jitter_);
+ EXPECT_EQ(63u, fraction_lost_);
+ EXPECT_EQ(74u, cumulative_lost_);
+ // Build extended sequence number.
+ uint32 extended_seq_num = rtp_header_.webrtc.header.sequenceNumber - 1;
+ EXPECT_EQ(extended_seq_num, extended_high_sequence_number_);
+}
+
+TEST_F(ReceiverStatsTest, NoLossWrap) {
+ rtp_header_.webrtc.header.sequenceNumber = 65500;
+ for (int i = 0; i < 300; ++i) {
+ stats_.UpdateStatistics(rtp_header_);
+ if (i % 3) {
+ rtp_header_.webrtc.header.timestamp = Timestamp();
+ }
+ ++rtp_header_.webrtc.header.sequenceNumber;
+ testing_clock_.Advance(delta_increments_);
+ }
+ stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
+ &extended_high_sequence_number_, &jitter_);
+ EXPECT_EQ(0u, fraction_lost_);
+ EXPECT_EQ(0u, cumulative_lost_);
+ // Build extended sequence number (one wrap cycle).
+ uint32 extended_seq_num = (1 << 16) +
+ rtp_header_.webrtc.header.sequenceNumber - 1;
+ EXPECT_EQ(extended_seq_num, extended_high_sequence_number_);
+}
+
+TEST_F(ReceiverStatsTest, LossCountWrap) {
+ const uint32 start_sequence_number = 65500;
+ rtp_header_.webrtc.header.sequenceNumber = start_sequence_number;
+ for (int i = 0; i < 300; ++i) {
+ if (i % 4)
+ stats_.UpdateStatistics(rtp_header_);
+ if (i % 3)
+ // Update timestamp.
+ ++rtp_header_.webrtc.header.timestamp;
+ ++rtp_header_.webrtc.header.sequenceNumber;
+ testing_clock_.Advance(delta_increments_);
+ }
+ stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
+ &extended_high_sequence_number_, &jitter_);
+ EXPECT_EQ(63u, fraction_lost_);
+ EXPECT_EQ(74u, cumulative_lost_);
+ // Build extended sequence number (one wrap cycle).
+ uint32 extended_seq_num = (1 << 16) +
+ rtp_header_.webrtc.header.sequenceNumber - 1;
+ EXPECT_EQ(extended_seq_num, extended_high_sequence_number_);
+}
+
+TEST_F(ReceiverStatsTest, Jitter) {
+ rtp_header_.webrtc.header.timestamp = Timestamp();
+ for (int i = 0; i < 300; ++i) {
+ stats_.UpdateStatistics(rtp_header_);
+ ++rtp_header_.webrtc.header.sequenceNumber;
+ rtp_header_.webrtc.header.timestamp += 33 * 90;
+ testing_clock_.Advance(delta_increments_);
+ }
+ stats_.GetStatistics(&fraction_lost_, &cumulative_lost_,
+ &extended_high_sequence_number_, &jitter_);
+ EXPECT_FALSE(fraction_lost_);
+ EXPECT_FALSE(cumulative_lost_);
+ // Build extended sequence number (one wrap cycle).
+ uint32 extended_seq_num = rtp_header_.webrtc.header.sequenceNumber - 1;
+ EXPECT_EQ(extended_seq_num, extended_high_sequence_number_);
+ EXPECT_EQ(ExpectedJitter(kStdTimeIncrementMs, 300), jitter_);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_receiver/rtp_parser/include/mock/mock_rtp_feedback.h b/media/cast/rtp_receiver/rtp_parser/include/mock/mock_rtp_feedback.h
new file mode 100644
index 0000000..d39bc2a
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_parser/include/mock/mock_rtp_feedback.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef MEDIA_CAST_RTP_INCLUDE_MOCK_RTP_FEEDBACK_H_
+#define MEDIA_CAST_RTP_INCLUDE_MOCK_RTP_FEEDBACK_H_
+
+#include "media/cast/rtp_receiver/rtp_parser/rtp_feedback.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+class MockRtpFeedback : public RtpFeedback {
+ public:
+ MOCK_METHOD4(OnInitializeDecoder,
+ int32(const int8 payloadType,
+ const int frequency,
+ const uint8 channels,
+ const uint32 rate));
+
+ MOCK_METHOD1(OnPacketTimeout,
+ void(const int32 id));
+ MOCK_METHOD2(OnReceivedPacket,
+ void(const int32 id, const RtpRtcpPacketType packet_type));
+ MOCK_METHOD2(OnPeriodicDeadOrAlive,
+ void(const int32 id, const RTPAliveType alive));
+ MOCK_METHOD2(OnIncomingSSRCChanged,
+ void(const int32 id, const uint32 ssrc));
+ MOCK_METHOD3(OnIncomingCSRCChanged,
+ void(const int32 id, const uint32 csrc, const bool added));
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_INCLUDE_MOCK_RTP_FEEDBACK_H_ \ No newline at end of file
diff --git a/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc b/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc
new file mode 100644
index 0000000..0eb691b
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc
@@ -0,0 +1,107 @@
+// 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/rtp_receiver/rtp_parser/rtp_parser.h"
+
+#include "base/logging.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/rtp_receiver/rtp_receiver.h"
+#include "net/base/big_endian.h"
+
+namespace media {
+namespace cast {
+
+static const int kRtpCommonHeaderLength = 12;
+static const int kRtpCastHeaderLength = 7;
+static const uint8 kCastKeyFrameBitMask = 0x80;
+static const uint8 kCastReferenceFrameIdBitMask = 0x40;
+
+RtpParser::RtpParser(RtpData* incoming_payload_callback,
+ const RtpParserConfig parser_config)
+ : data_callback_(incoming_payload_callback),
+ parser_config_(parser_config) {
+}
+
+RtpParser::~RtpParser() {
+}
+
+bool RtpParser::ParsePacket(const uint8* packet, int length,
+ RtpCastHeader* rtp_header) {
+ if (length == 0) return false;
+ // Get RTP general header.
+ if (!ParseCommon(packet, length, rtp_header)) return false;
+ if (rtp_header->webrtc.header.payloadType == parser_config_.payload_type &&
+ rtp_header->webrtc.header.ssrc == parser_config_.ssrc) {
+ return ParseCast(packet + kRtpCommonHeaderLength,
+ length - kRtpCommonHeaderLength, rtp_header);
+ }
+ // Not a valid payload type / ssrc combination.
+ return false;
+}
+
+bool RtpParser::ParseCommon(const uint8* packet,
+ int length,
+ RtpCastHeader* rtp_header) {
+ if (length < kRtpCommonHeaderLength) return false;
+ uint8 version = packet[0] >> 6;
+ if (version != 2) return false;
+ uint8 cc = packet[0] & 0x0f;
+ bool marker = ((packet[1] & 0x80) != 0);
+ int payload_type = packet[1] & 0x7f;
+
+ uint16 sequence_number;
+ uint32 rtp_timestamp, ssrc;
+ net::BigEndianReader big_endian_reader(packet + 2, 80);
+ big_endian_reader.ReadU16(&sequence_number);
+ big_endian_reader.ReadU32(&rtp_timestamp);
+ big_endian_reader.ReadU32(&ssrc);
+
+ rtp_header->webrtc.header.markerBit = marker;
+ rtp_header->webrtc.header.payloadType = payload_type;
+ rtp_header->webrtc.header.sequenceNumber = sequence_number;
+ rtp_header->webrtc.header.timestamp = rtp_timestamp;
+ rtp_header->webrtc.header.ssrc = ssrc;
+ rtp_header->webrtc.header.numCSRCs = cc;
+
+ uint8 csrc_octs = cc * 4;
+ rtp_header->webrtc.type.Audio.numEnergy = rtp_header->webrtc.header.numCSRCs;
+ rtp_header->webrtc.header.headerLength = kRtpCommonHeaderLength + csrc_octs;
+ rtp_header->webrtc.type.Audio.isCNG = false;
+ rtp_header->webrtc.type.Audio.channel = parser_config_.audio_channels;
+ return true;
+}
+
+bool RtpParser::ParseCast(const uint8* packet,
+ int length,
+ RtpCastHeader* rtp_header) {
+ if (length < kRtpCastHeaderLength) return false;
+ // Extract header.
+ const uint8* data_ptr = packet;
+ int data_length = length;
+ rtp_header->is_key_frame = (data_ptr[0] & kCastKeyFrameBitMask);
+ rtp_header->is_reference = (data_ptr[0] & kCastReferenceFrameIdBitMask);
+ rtp_header->frame_id = data_ptr[1];
+
+ net::BigEndianReader big_endian_reader(data_ptr + 2, 32);
+ big_endian_reader.ReadU16(&rtp_header->packet_id);
+ big_endian_reader.ReadU16(&rtp_header->max_packet_id);
+
+ if (rtp_header->is_reference) {
+ rtp_header->reference_frame_id = data_ptr[6];
+ data_ptr += kRtpCastHeaderLength;
+ data_length -= kRtpCastHeaderLength;
+ } else {
+ data_ptr += kRtpCastHeaderLength - 1;
+ data_length -= kRtpCastHeaderLength - 1;
+ }
+
+ if (rtp_header->max_packet_id < rtp_header->packet_id) {
+ return false;
+ }
+ data_callback_->OnReceivedPayloadData(data_ptr, data_length, rtp_header);
+ return true;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_receiver/rtp_parser/rtp_parser.gypi b/media/cast/rtp_receiver/rtp_parser/rtp_parser.gypi
new file mode 100644
index 0000000..0814e55
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_parser/rtp_parser.gypi
@@ -0,0 +1,25 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'cast_rtp_parser',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ '<(DEPTH)/third_party/',
+ ],
+ 'sources': [
+ 'rtp_parser_config.h',
+ 'rtp_parser.cc',
+ 'rtp_parser.h',
+ ], # source
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/base/base.gyp:test_support_base',
+ ],
+ },
+ ],
+}
diff --git a/media/cast/rtp_receiver/rtp_parser/rtp_parser.h b/media/cast/rtp_receiver/rtp_parser/rtp_parser.h
new file mode 100644
index 0000000..7f85609
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_parser/rtp_parser.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef MEDIA_CAST_RTP_RECEIVER_RTP_PARSER_RTP_PARSER_H_
+#define MEDIA_CAST_RTP_RECEIVER_RTP_PARSER_RTP_PARSER_H_
+
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+class RtpData;
+
+struct RtpParserConfig {
+ RtpParserConfig() {
+ ssrc = 0;
+ payload_type = 0;
+ audio_channels = 0;
+ }
+
+ uint32 ssrc;
+ int payload_type;
+ AudioCodec audio_codec;
+ VideoCodec video_codec;
+ int audio_channels;
+};
+
+class RtpParser {
+ public:
+ RtpParser(RtpData* incoming_payload_callback,
+ const RtpParserConfig parser_config);
+
+ ~RtpParser();
+
+ bool ParsePacket(const uint8* packet, int length,
+ RtpCastHeader* rtp_header);
+
+ private:
+ bool ParseCommon(const uint8* packet, int length,
+ RtpCastHeader* rtp_header);
+
+ bool ParseCast(const uint8* packet, int length,
+ RtpCastHeader* rtp_header);
+
+ RtpData* data_callback_;
+ RtpParserConfig parser_config_;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_RECEIVER_RTP_PARSER_RTP_PARSER_H_
diff --git a/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc b/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc
new file mode 100644
index 0000000..71e6f50
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_parser/rtp_parser_unittest.cc
@@ -0,0 +1,201 @@
+// 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 <gtest/gtest.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "media/cast/rtp_receiver/rtp_parser/rtp_parser.h"
+#include "media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.h"
+#include "media/cast/rtp_receiver/rtp_receiver.h"
+
+namespace media {
+namespace cast {
+
+static const int kPacketLength = 1500;
+static const int kCastRtpLength = 7;
+static const int kTestPayloadType = 127;
+static const uint32 kTestSsrc = 1234;
+static const uint32 kTestTimestamp = 111111;
+static const uint16 kTestSeqNum = 4321;
+static const uint8 kRefFrameId = 17;
+
+class RtpDataTest : public RtpData {
+ public:
+ RtpDataTest() {
+ expected_header_.reset(new RtpCastHeader());
+ }
+
+ ~RtpDataTest() {}
+
+ void SetExpectedHeader(const RtpCastHeader& cast_header) {
+ memcpy(expected_header_.get(), &cast_header, sizeof(RtpCastHeader));
+ }
+
+ void OnReceivedPayloadData(const uint8* payloadData,
+ int payloadSize,
+ const RtpCastHeader* rtpHeader) {
+ VerifyCommonHeader(*rtpHeader);
+ VerifyCastHeader(*rtpHeader);
+ // TODO(mikhal): Add data verification.
+ }
+
+ void VerifyCommonHeader(const RtpCastHeader& parsed_header) {
+ EXPECT_EQ(expected_header_->packet_id == expected_header_->max_packet_id,
+ parsed_header.webrtc.header.markerBit);
+ EXPECT_EQ(kTestPayloadType, parsed_header.webrtc.header.payloadType);
+ EXPECT_EQ(kTestSsrc, parsed_header.webrtc.header.ssrc);
+ EXPECT_EQ(0, parsed_header.webrtc.header.numCSRCs);
+ }
+
+ void VerifyCastHeader(const RtpCastHeader& parsed_header) {
+ EXPECT_EQ(expected_header_->is_key_frame, parsed_header.is_key_frame);
+ EXPECT_EQ(expected_header_->frame_id, parsed_header.frame_id);
+ EXPECT_EQ(expected_header_->packet_id, parsed_header.packet_id);
+ EXPECT_EQ(expected_header_->max_packet_id, parsed_header.max_packet_id);
+ EXPECT_EQ(expected_header_->is_reference, parsed_header.is_reference);
+ }
+
+ private:
+ scoped_ptr<RtpCastHeader> expected_header_;
+};
+
+class RtpParserTest : public ::testing::Test {
+ protected:
+ RtpParserTest() {
+ PopulateConfig();
+ rtp_data_.reset(new RtpDataTest());
+ rtp_parser_.reset(new RtpParser(rtp_data_.get(), config_));
+ }
+
+ ~RtpParserTest() {}
+
+ virtual void SetUp() {
+ cast_header_.InitRTPVideoHeaderCast();
+ cast_header_.is_reference = true;
+ cast_header_.reference_frame_id = kRefFrameId;
+ packet_builder_.SetSsrc(kTestSsrc);
+ packet_builder_.SetReferenceFrameId(kRefFrameId, true);
+ packet_builder_.SetSequenceNumber(kTestSeqNum);
+ packet_builder_.SetTimestamp(kTestTimestamp);
+ packet_builder_.SetPayloadType(kTestPayloadType);
+ packet_builder_.SetMarkerBit(true); // Only one packet.
+ }
+
+ void PopulateConfig() {
+ config_.payload_type = kTestPayloadType;
+ config_.ssrc = kTestSsrc;
+ }
+
+ scoped_ptr<RtpDataTest> rtp_data_;
+ RtpPacketBuilder packet_builder_;
+ scoped_ptr<RtpParser> rtp_parser_;
+ RtpParserConfig config_;
+ RtpCastHeader cast_header_;
+};
+
+TEST_F(RtpParserTest, ParseDefaultCastPacket) {
+ // Build generic data packet.
+ uint8 packet[kPacketLength];
+ packet_builder_.BuildHeader(packet, kPacketLength);
+ // Parse packet as is.
+ RtpCastHeader rtp_header;
+ rtp_data_->SetExpectedHeader(cast_header_);
+ EXPECT_TRUE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+}
+
+TEST_F(RtpParserTest, ParseNonDefaultCastPacket) {
+ // Build generic data packet.
+ uint8 packet[kPacketLength];
+ packet_builder_.SetKeyFrame(true);
+ packet_builder_.SetFrameId(10);
+ packet_builder_.SetPacketId(5);
+ packet_builder_.SetMaxPacketId(15);
+ packet_builder_.SetMarkerBit(false);
+ packet_builder_.BuildHeader(packet, kPacketLength);
+ cast_header_.is_key_frame = true;
+ cast_header_.frame_id = 10;
+ cast_header_.packet_id = 5;
+ cast_header_.max_packet_id = 15;
+ rtp_data_->SetExpectedHeader(cast_header_);
+ // Parse packet as is.
+ RtpCastHeader rtp_header;
+ EXPECT_TRUE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+}
+
+TEST_F(RtpParserTest, TooBigPacketId) {
+ // Build generic data packet.
+ uint8 packet[kPacketLength];
+ packet_builder_.SetKeyFrame(true);
+ packet_builder_.SetFrameId(10);
+ packet_builder_.SetPacketId(15);
+ packet_builder_.SetMaxPacketId(5);
+ packet_builder_.BuildHeader(packet, kPacketLength);
+ // Parse packet as is.
+ RtpCastHeader rtp_header;
+ EXPECT_FALSE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+}
+
+TEST_F(RtpParserTest, MaxPacketId) {
+ // Build generic data packet.
+ uint8 packet[kPacketLength];
+ packet_builder_.SetKeyFrame(true);
+ packet_builder_.SetFrameId(10);
+ packet_builder_.SetPacketId(65535);
+ packet_builder_.SetMaxPacketId(65535);
+ packet_builder_.BuildHeader(packet, kPacketLength);
+ cast_header_.is_key_frame = true;
+ cast_header_.frame_id = 10;
+ cast_header_.packet_id = 65535;
+ cast_header_.max_packet_id = 65535;
+ rtp_data_->SetExpectedHeader(cast_header_);
+ // Parse packet as is.
+ RtpCastHeader rtp_header;
+ EXPECT_TRUE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+}
+
+TEST_F(RtpParserTest, InvalidPayloadType) {
+ // Build generic data packet.
+ uint8 packet[kPacketLength];
+ packet_builder_.SetKeyFrame(true);
+ packet_builder_.SetFrameId(10);
+ packet_builder_.SetPacketId(65535);
+ packet_builder_.SetMaxPacketId(65535);
+ packet_builder_.SetPayloadType(kTestPayloadType - 1);
+ packet_builder_.BuildHeader(packet, kPacketLength);
+ // Parse packet as is.
+ RtpCastHeader rtp_header;
+ EXPECT_FALSE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+}
+
+TEST_F(RtpParserTest, InvalidSsrc) {
+ // Build generic data packet.
+ uint8 packet[kPacketLength];
+ packet_builder_.SetKeyFrame(true);
+ packet_builder_.SetFrameId(10);
+ packet_builder_.SetPacketId(65535);
+ packet_builder_.SetMaxPacketId(65535);
+ packet_builder_.SetSsrc(kTestSsrc - 1);
+ packet_builder_.BuildHeader(packet, kPacketLength);
+ // Parse packet as is.
+ RtpCastHeader rtp_header;
+ EXPECT_FALSE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+}
+
+TEST_F(RtpParserTest, ParseCastPacketWithoutReference) {
+ cast_header_.is_reference = false;
+ cast_header_.reference_frame_id = 0;
+ packet_builder_.SetReferenceFrameId(kRefFrameId, false);
+
+ // Build generic data packet.
+ uint8 packet[kPacketLength];
+ packet_builder_.BuildHeader(packet, kPacketLength);
+ // Parse packet as is.
+ RtpCastHeader rtp_header;
+ rtp_data_->SetExpectedHeader(cast_header_);
+ EXPECT_TRUE(rtp_parser_->ParsePacket(packet, kPacketLength, &rtp_header));
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.cc b/media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.cc
new file mode 100644
index 0000000..9f61d9b
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.cc
@@ -0,0 +1,104 @@
+// 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/rtp_receiver/rtp_parser/test/rtp_packet_builder.h"
+
+#include "base/logging.h"
+#include "net/base/big_endian.h"
+
+namespace media {
+namespace cast {
+
+const int kCastRtpHeaderLength = 7;
+const int kGenericRtpHeaderLength = 12;
+
+RtpPacketBuilder::RtpPacketBuilder()
+ : is_key_(false),
+ frame_id_(0),
+ packet_id_(0),
+ max_packet_id_(0),
+ reference_frame_id_(0),
+ is_reference_set_(false),
+ timestamp_(0),
+ sequence_number_(0),
+ marker_(false),
+ payload_type_(0),
+ ssrc_(0) {}
+
+void RtpPacketBuilder::SetKeyFrame(bool is_key) {
+ is_key_ = is_key;
+}
+
+void RtpPacketBuilder::SetFrameId(uint8 frame_id) {
+ frame_id_ = frame_id;
+}
+
+void RtpPacketBuilder::SetPacketId(uint16 packet_id) {
+ packet_id_ = packet_id;
+}
+
+void RtpPacketBuilder::SetMaxPacketId(uint16 max_packet_id) {
+ max_packet_id_ = max_packet_id;
+}
+
+void RtpPacketBuilder::SetReferenceFrameId(uint8 reference_frame_id,
+ bool is_set) {
+ is_reference_set_ = is_set;
+ if (is_set)
+ reference_frame_id_ = reference_frame_id;
+}
+void RtpPacketBuilder::SetTimestamp(uint32 timestamp) {
+ timestamp_ = timestamp;
+}
+
+void RtpPacketBuilder::SetSequenceNumber(uint16 sequence_number) {
+ sequence_number_ = sequence_number;
+}
+
+void RtpPacketBuilder::SetMarkerBit(bool marker) {
+ marker_ = marker;
+}
+
+void RtpPacketBuilder::SetPayloadType(int payload_type) {
+ payload_type_ = payload_type;
+}
+
+void RtpPacketBuilder::SetSsrc(uint32 ssrc) {
+ ssrc_ = ssrc;
+}
+
+void RtpPacketBuilder::BuildHeader(uint8* data, uint32 data_length) {
+ BuildCommonHeader(data, data_length);
+ BuildCastHeader(data + kGenericRtpHeaderLength,
+ data_length - kGenericRtpHeaderLength);
+}
+
+void RtpPacketBuilder::BuildCastHeader(uint8* data, uint32 data_length) {
+ // Build header.
+ DCHECK_LE(kCastRtpHeaderLength, data_length);
+ // Set the first 7 bytes to 0.
+ memset(data, 0, kCastRtpHeaderLength);
+ net::BigEndianWriter big_endian_writer(data, 56);
+ big_endian_writer.WriteU8(
+ (is_key_ ? 0x80 : 0) | (is_reference_set_ ? 0x40 : 0));
+ big_endian_writer.WriteU8(frame_id_);
+ big_endian_writer.WriteU16(packet_id_);
+ big_endian_writer.WriteU16(max_packet_id_);
+ if (is_reference_set_) {
+ big_endian_writer.WriteU8(reference_frame_id_);
+ }
+}
+
+void RtpPacketBuilder::BuildCommonHeader(uint8* data, uint32 data_length) {
+ DCHECK_LE(kGenericRtpHeaderLength, data_length);
+ net::BigEndianWriter big_endian_writer(data, 96);
+ big_endian_writer.WriteU8(0x80);
+ big_endian_writer.WriteU8(payload_type_ | (marker_ ? kRtpMarkerBitMask : 0));
+ big_endian_writer.WriteU16(sequence_number_);
+ big_endian_writer.WriteU32(timestamp_);
+ big_endian_writer.WriteU32(ssrc_);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.h b/media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.h
new file mode 100644
index 0000000..70f520e
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_parser/test/rtp_packet_builder.h
@@ -0,0 +1,51 @@
+// 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.
+
+// Test helper class that builds rtp packets.
+
+#ifndef MEDIA_CAST_RTP_RECEIVER_RTP_PARSER_TEST_RTP_PACKET_BUILDER_H_
+#define MEDIA_CAST_RTP_RECEIVER_RTP_PARSER_TEST_RTP_PACKET_BUILDER_H_
+
+
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+class RtpPacketBuilder {
+ public:
+ RtpPacketBuilder();
+ void SetKeyFrame(bool is_key);
+ void SetFrameId(uint8 frame_id);
+ void SetPacketId(uint16 packet_id);
+ void SetMaxPacketId(uint16 max_packet_id);
+ void SetReferenceFrameId(uint8 reference_frame_id, bool is_set);
+ void SetTimestamp(uint32 timestamp);
+ void SetSequenceNumber(uint16 sequence_number);
+ void SetMarkerBit(bool marker);
+ void SetPayloadType(int payload_type);
+ void SetSsrc(uint32 ssrc);
+ void BuildHeader(uint8* data, uint32 data_length);
+
+ private:
+ bool is_key_;
+ uint8 frame_id_;
+ uint16 packet_id_;
+ uint16 max_packet_id_;
+ uint8 reference_frame_id_;
+ bool is_reference_set_;
+ uint32 timestamp_;
+ uint16 sequence_number_;
+ bool marker_;
+ int payload_type_;
+ uint32 ssrc_;
+
+ void BuildCastHeader(uint8* data, uint32 data_length);
+ void BuildCommonHeader(uint8* data, uint32 data_length);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_RECEIVER_RTP_PARSER_TEST_RTP_PACKET_BUILDER_H_
diff --git a/media/cast/rtp_receiver/rtp_receiver.cc b/media/cast/rtp_receiver/rtp_receiver.cc
new file mode 100644
index 0000000..97e9b03
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_receiver.cc
@@ -0,0 +1,57 @@
+// 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/rtp_receiver/rtp_receiver.h"
+
+#include "base/logging.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "media/cast/rtp_receiver/receiver_stats.h"
+#include "media/cast/rtp_receiver/rtp_parser/rtp_parser.h"
+
+namespace media {
+namespace cast {
+
+RtpReceiver::RtpReceiver(const AudioReceiverConfig* audio_config,
+ const VideoReceiverConfig* video_config,
+ RtpData* incoming_payload_callback) {
+ DCHECK(incoming_payload_callback) << "Invalid argument";
+ DCHECK(audio_config || video_config) << "Invalid argument";
+ // Configure parser.
+ RtpParserConfig config;
+ if (audio_config) {
+ config.ssrc = audio_config->incoming_ssrc;
+ config.payload_type = audio_config->rtp_payload_type;
+ config.audio_codec = audio_config->codec;
+ config.audio_channels = audio_config->channels;
+ } else {
+ config.ssrc = video_config->incoming_ssrc;
+ config.payload_type = video_config->rtp_payload_type;
+ config.video_codec = video_config->codec;
+ }
+ stats_.reset(new ReceiverStats(config.ssrc));
+ parser_.reset(new RtpParser(incoming_payload_callback, config));
+}
+
+RtpReceiver::~RtpReceiver() {}
+
+bool RtpReceiver::ReceivedPacket(const uint8* packet, int length) {
+ RtpCastHeader rtp_header;
+ if (!parser_->ParsePacket(packet, length, &rtp_header)) return false;
+
+ stats_->UpdateStatistics(rtp_header);
+ return true;
+}
+
+void RtpReceiver::GetStatistics(uint8* fraction_lost,
+ uint32* cumulative_lost,
+ uint32* extended_high_sequence_number,
+ uint32* jitter) {
+ stats_->GetStatistics(fraction_lost,
+ cumulative_lost,
+ extended_high_sequence_number,
+ jitter);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_receiver/rtp_receiver.gyp b/media/cast/rtp_receiver/rtp_receiver.gyp
new file mode 100644
index 0000000..c1d4d5a
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_receiver.gyp
@@ -0,0 +1,27 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'cast_rtp_receiver',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ '<(DEPTH)/third_party/',
+ ],
+ 'sources': [
+ 'receiver_stats.cc',
+ 'receiver_stats.h',
+ 'rtp_receiver.cc',
+ 'rtp_receiver.h',
+ ], # source
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/base/base.gyp:test_support_base',
+ 'rtp_parser/rtp_parser.gypi:*',
+ ],
+ },
+ ],
+}
diff --git a/media/cast/rtp_receiver/rtp_receiver.h b/media/cast/rtp_receiver/rtp_receiver.h
new file mode 100644
index 0000000..6cac8ca
--- /dev/null
+++ b/media/cast/rtp_receiver/rtp_receiver.h
@@ -0,0 +1,53 @@
+// 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.
+
+// Interface to the rtp receiver.
+
+#ifndef MEDIA_CAST_RTP_RECEIVER_RTP_RECEIVER_H_
+#define MEDIA_CAST_RTP_RECEIVER_RTP_RECEIVER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+class RtpData {
+ public:
+ virtual void OnReceivedPayloadData(const uint8* payload_data,
+ int payload_size,
+ const RtpCastHeader* rtp_header) = 0;
+
+ protected:
+ virtual ~RtpData() {}
+};
+
+class ReceiverStats;
+class RtpParser;
+
+class RtpReceiver {
+ public:
+ RtpReceiver(const AudioReceiverConfig* audio_config,
+ const VideoReceiverConfig* video_config,
+ RtpData* incoming_payload_callback);
+ ~RtpReceiver();
+
+ bool ReceivedPacket(const uint8* packet, int length);
+
+ void GetStatistics(uint8* fraction_lost,
+ uint32* cumulative_lost, // 24 bits valid.
+ uint32* extended_high_sequence_number,
+ uint32* jitter);
+
+ private:
+ scoped_ptr<ReceiverStats> stats_;
+ scoped_ptr<RtpParser> parser_;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_RECEIVER_RTP_RECEIVER_H_