summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authormikhal@google.com <mikhal@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-29 21:16:13 +0000
committermikhal@google.com <mikhal@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-29 21:16:13 +0000
commitb89160a94b276966883a2d4dadd92909382c726e (patch)
tree2e53277629b2fe6a4eeb5e126477655af54598d4 /media
parent0c4c10ff318c38b8c66a997316e9153ce78c9e26 (diff)
downloadchromium_src-b89160a94b276966883a2d4dadd92909382c726e.zip
chromium_src-b89160a94b276966883a2d4dadd92909382c726e.tar.gz
chromium_src-b89160a94b276966883a2d4dadd92909382c726e.tar.bz2
Adding Rtp sender and rtp common to cast
Review URL: https://chromiumcodereview.appspot.com/23572006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@220373 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/cast/cast.gyp12
-rw-r--r--media/cast/cast_sender.gyp1
-rw-r--r--media/cast/rtp_common/mock_rtp_payload_feedback.h25
-rw-r--r--media/cast/rtp_common/rtp_defines.h72
-rw-r--r--media/cast/rtp_sender/mock_rtp_sender.h34
-rw-r--r--media/cast/rtp_sender/packet_storage/packet_storage.cc141
-rw-r--r--media/cast/rtp_sender/packet_storage/packet_storage.gypi23
-rw-r--r--media/cast/rtp_sender/packet_storage/packet_storage.h59
-rw-r--r--media/cast/rtp_sender/packet_storage/packet_storage_unittest.cc114
-rw-r--r--media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.cc145
-rw-r--r--media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.gypi25
-rw-r--r--media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h65
-rw-r--r--media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h47
-rw-r--r--media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc139
-rw-r--r--media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.cc70
-rw-r--r--media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h31
-rw-r--r--media/cast/rtp_sender/rtp_sender.cc166
-rw-r--r--media/cast/rtp_sender/rtp_sender.gyp27
-rw-r--r--media/cast/rtp_sender/rtp_sender.h63
19 files changed, 1256 insertions, 3 deletions
diff --git a/media/cast/cast.gyp b/media/cast/cast.gyp
index dec8f53..b8e3961 100644
--- a/media/cast/cast.gyp
+++ b/media/cast/cast.gyp
@@ -44,17 +44,23 @@
'dependencies': [
'cast_sender',
'cast_receiver',
- 'rtcp/rtcp.gyp:cast_rtcp_test',
- '<(DEPTH)/base/base.gyp:run_all_unittests',
- '<(DEPTH)/net/net.gyp:net',
+ '../../base/base.gyp:run_all_unittests',
+ '<(DEPTH)/net/net.gyp:*',
'<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest',
+ 'rtcp/rtcp.gyp:cast_rtcp_test',
],
'include_dirs': [
'<(DEPTH)/',
+ '<(DEPTH)/third_party/',
+ '<(DEPTH)/third_party/webrtc/',
],
'sources': [
'congestion_control/congestion_control_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',
+ 'rtp_sender/rtp_packetizer/test/rtp_header_parser.h',
'pacing/paced_sender_unittest.cc',
'rtcp/rtcp_receiver_unittest.cc',
'rtcp/rtcp_sender_unittest.cc',
diff --git a/media/cast/cast_sender.gyp b/media/cast/cast_sender.gyp
index bfec393..c41bd64 100644
--- a/media/cast/cast_sender.gyp
+++ b/media/cast/cast_sender.gyp
@@ -22,6 +22,7 @@
'congestion_control',
'pacing/paced_sender.gyp:paced_sender',
'rtcp/rtcp.gyp:cast_rtcp',
+ 'rtp_sender/rtp_sender.gyp:cast_rtp_sender',
# 'video_sender',
], # dependencies
},
diff --git a/media/cast/rtp_common/mock_rtp_payload_feedback.h b/media/cast/rtp_common/mock_rtp_payload_feedback.h
new file mode 100644
index 0000000..e6c4c3e
--- /dev/null
+++ b/media/cast/rtp_common/mock_rtp_payload_feedback.h
@@ -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.
+
+#ifndef MEDIA_CAST_RTP_COMMON_MOCK_RTP_PAYLOAD_FEEDBACK_H_
+#define MEDIA_CAST_RTP_COMMON_MOCK_RTP_PAYLOAD_FEEDBACK_H_
+
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namesapce media {
+namespace cast {
+
+class MockRtpPayloadFeedback : public RtpPayloadFeedback {
+ public:
+ MOCK_METHOD1(CastFeedback,
+ void(const RtcpCastMessage& cast_feedback));
+
+ MOCK_METHOD0(RequestKeyFrame, void());
+};
+
+} // namespace cast
+} // namesapce media
+
+#endif // MEDIA_CAST_RTP_COMMON_MOCK_RTP_PAYLOAD_FEEDBACK_H_
diff --git a/media/cast/rtp_common/rtp_defines.h b/media/cast/rtp_common/rtp_defines.h
new file mode 100644
index 0000000..22dba68
--- /dev/null
+++ b/media/cast/rtp_common/rtp_defines.h
@@ -0,0 +1,72 @@
+// 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_COMMON_RTP_DEFINES_H_
+#define MEDIA_CAST_RTP_COMMON_RTP_DEFINES_H_
+
+#include <cstring>
+
+#include "base/basictypes.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+
+#include "third_party/webrtc/modules/interface/module_common_types.h"
+
+namespace media {
+namespace cast {
+
+const uint8 kRtpMarkerBitMask = 0x80;
+
+struct RtpCastHeader {
+ void InitRTPVideoHeaderCast() {
+ is_key_frame = false;
+ frame_id = 0;
+ packet_id = 0;
+ max_packet_id = 0;
+ is_reference = false;
+ reference_frame_id = 0;
+ }
+ webrtc::WebRtcRTPHeader webrtc;
+ bool is_key_frame;
+ uint8 frame_id;
+ uint16 packet_id;
+ uint16 max_packet_id;
+ bool is_reference; // Set to true if the previous frame is not available,
+ // and the reference frame id is available.
+ uint8 reference_frame_id;
+};
+
+class RtpPayloadFeedback {
+ public:
+ virtual void CastFeedback(const RtcpCastMessage& cast_feedback) = 0;
+ virtual void RequestKeyFrame() = 0; // TODO(pwestin): can we remove this?
+
+ protected:
+ virtual ~RtpPayloadFeedback() {}
+};
+
+inline bool IsNewerFrameId(uint8 frame_id, uint8 prev_frame_id) {
+ return (frame_id != prev_frame_id) &&
+ static_cast<uint8>(frame_id - prev_frame_id) < 0x80;
+}
+
+inline bool IsOlderFrameId(uint8 frame_id, uint8 prev_frame_id) {
+ return (frame_id == prev_frame_id) || IsNewerFrameId(prev_frame_id, frame_id);
+}
+
+inline bool IsNewerPacketId(uint16 packet_id,
+ uint16 prev_packet_id) {
+ return (packet_id != prev_packet_id) &&
+ static_cast<uint16>(packet_id - prev_packet_id) < 0x8000;
+}
+
+inline bool IsNewerSequenceNumber(uint16 sequence_number,
+ uint16 prev_sequence_number) {
+ // Same function as IsNewerPacketId just different data and name.
+ return IsNewerPacketId(sequence_number, prev_sequence_number);
+}
+
+} // namespace cast
+} // namespace media
+#endif // MEDIA_CAST_RTP_COMMON_RTP_DEFINES_H_
diff --git a/media/cast/rtp_sender/mock_rtp_sender.h b/media/cast/rtp_sender/mock_rtp_sender.h
new file mode 100644
index 0000000..334bc88
--- /dev/null
+++ b/media/cast/rtp_sender/mock_rtp_sender.h
@@ -0,0 +1,34 @@
+// 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_SENDER_MOCK_RTP_SENDER_H_
+#define MEDIA_CAST_RTP_SENDER_MOCK_RTP_SENDER_H_
+
+#include <vector>
+
+#include "media/cast/rtp_sender/rtp_sender.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+class MockRtpSender : public RtpSender {
+ public:
+ MOCK_METHOD2(IncomingEncodedVideoFrame,
+ bool(const EncodedVideoFrame& frame, int64 capture_time));
+
+ MOCK_METHOD2(IncomingEncodedAudioFrame,
+ bool(const EncodedAudioFrame& frame, int64 recorded_time));
+
+ MOCK_METHOD3(ResendPacket,
+ bool(bool is_audio, uint8 frame_id, uint16 packet_id));
+
+ MOCK_METHOD0(RtpStatistics, void());
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_SENDER_MOCK_RTP_SENDER_H_
+
diff --git a/media/cast/rtp_sender/packet_storage/packet_storage.cc b/media/cast/rtp_sender/packet_storage/packet_storage.cc
new file mode 100644
index 0000000..9c2d7ff
--- /dev/null
+++ b/media/cast/rtp_sender/packet_storage/packet_storage.cc
@@ -0,0 +1,141 @@
+// 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_sender/packet_storage/packet_storage.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_defines.h"
+
+namespace media {
+namespace cast {
+
+// Limit the max time delay to avoid frame id wrap around; 256 / 60 fps.
+const int kMaxAllowedTimeStoredMs = 4000;
+
+typedef PacketMap::iterator PacketMapIterator;
+typedef TimeToPacketMap::iterator TimeToPacketIterator;
+
+class StoredPacket {
+ public:
+ StoredPacket() {
+ packet_.reserve(kIpPacketSize);
+ }
+
+ void Save(const std::vector<uint8>& packet) {
+ DCHECK_LT(packet.size(), kIpPacketSize) << "Invalid argument";
+ packet_.clear();
+ packet_.insert(packet_.begin(), packet.begin(), packet.end());
+ }
+
+ void GetCopy(std::vector<uint8>* packet) {
+ packet->insert(packet->begin(), packet_.begin(), packet_.end());
+ }
+
+ private:
+ std::vector<uint8> packet_;
+};
+
+
+PacketStorage::PacketStorage(int max_time_stored_ms)
+ : default_tick_clock_(new base::DefaultTickClock()),
+ clock_(default_tick_clock_.get()) {
+ max_time_stored_ = base::TimeDelta::FromMilliseconds(max_time_stored_ms);
+ DCHECK_LE(max_time_stored_ms, kMaxAllowedTimeStoredMs) << "Invalid argument";
+}
+
+PacketStorage::~PacketStorage() {
+ time_to_packet_map_.clear();
+
+ PacketMapIterator store_it = stored_packets_.begin();
+ for (; store_it != stored_packets_.end();
+ store_it = stored_packets_.begin()) {
+ stored_packets_.erase(store_it);
+ }
+ while (!free_packets_.empty()) {
+ free_packets_.pop_front();
+ }
+}
+
+void PacketStorage::CleanupOldPackets(base::TimeTicks now) {
+ TimeToPacketIterator time_it = time_to_packet_map_.begin();
+
+ // Check max size.
+ while (time_to_packet_map_.size() >= kMaxStoredPackets) {
+ PacketMapIterator store_it = stored_packets_.find(time_it->second);
+
+ // We should always find the packet.
+ DCHECK(store_it != stored_packets_.end()) << "Invalid state";
+ time_to_packet_map_.erase(time_it);
+ // Save the pointer.
+ linked_ptr<StoredPacket> storted_packet = store_it->second;
+ stored_packets_.erase(store_it);
+ // Add this packet to the free list for later re-use.
+ free_packets_.push_back(storted_packet);
+ time_it = time_to_packet_map_.begin();
+ }
+
+ // Time out old packets.
+ while (time_it != time_to_packet_map_.end()) {
+ if (now < time_it->first + max_time_stored_) {
+ break;
+ }
+ // Packet too old.
+ PacketMapIterator store_it = stored_packets_.find(time_it->second);
+
+ // We should always find the packet.
+ DCHECK(store_it != stored_packets_.end()) << "Invalid state";
+ time_to_packet_map_.erase(time_it);
+ // Save the pointer.
+ linked_ptr<StoredPacket> storted_packet = store_it->second;
+ stored_packets_.erase(store_it);
+ // Add this packet to the free list for later re-use.
+ free_packets_.push_back(storted_packet);
+ time_it = time_to_packet_map_.begin();
+ }
+}
+
+void PacketStorage::StorePacket(uint8 frame_id,
+ uint16 packet_id,
+ const std::vector<uint8>& packet) {
+ base::TimeTicks now = clock_->NowTicks();
+ CleanupOldPackets(now);
+
+ uint32 index = (static_cast<uint32>(frame_id) << 16) + packet_id;
+ PacketMapIterator it = stored_packets_.find(index);
+ if (it != stored_packets_.end()) {
+ // We have already saved this.
+ DCHECK(false) << "Invalid state";
+ return;
+ }
+ linked_ptr<StoredPacket> stored_packet;
+ if (free_packets_.empty()) {
+ // No previous allocated packets allocate one.
+ stored_packet.reset(new StoredPacket());
+ } else {
+ // Re-use previous allocated packet.
+ stored_packet = free_packets_.front();
+ free_packets_.pop_front();
+ }
+ stored_packet->Save(packet);
+ stored_packets_[index] = stored_packet;
+ time_to_packet_map_.insert(std::make_pair(now, index));
+}
+
+bool PacketStorage::GetPacket(uint8 frame_id,
+ uint16 packet_id,
+ std::vector<uint8>* packet) {
+ uint32 index = (static_cast<uint32>(frame_id) << 16) + packet_id;
+ PacketMapIterator it = stored_packets_.find(index);
+ if (it == stored_packets_.end()) {
+ return false;
+ }
+ it->second->GetCopy(packet);
+ return true;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_sender/packet_storage/packet_storage.gypi b/media/cast/rtp_sender/packet_storage/packet_storage.gypi
new file mode 100644
index 0000000..f691d9e
--- /dev/null
+++ b/media/cast/rtp_sender/packet_storage/packet_storage.gypi
@@ -0,0 +1,23 @@
+# 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': 'packet_storage',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ ],
+ 'sources': [
+ 'packet_storage.h',
+ 'packet_storage.cc',
+ ], # source
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ ],
+ },
+ ],
+}
+
diff --git a/media/cast/rtp_sender/packet_storage/packet_storage.h b/media/cast/rtp_sender/packet_storage/packet_storage.h
new file mode 100644
index 0000000..e1e3bcb
--- /dev/null
+++ b/media/cast/rtp_sender/packet_storage/packet_storage.h
@@ -0,0 +1,59 @@
+// 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_SENDER_PACKET_STORAGE_INCLUDE_PACKET_STORAGE_H_
+#define MEDIA_CAST_RTP_SENDER_PACKET_STORAGE_INCLUDE_PACKET_STORAGE_H_
+
+#include <list>
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+
+namespace media {
+namespace cast {
+
+class StoredPacket;
+typedef std::map<uint32, linked_ptr<StoredPacket> > PacketMap;
+typedef std::multimap<base::TimeTicks, uint32> TimeToPacketMap;
+
+class PacketStorage {
+ public:
+ static const int kMaxStoredPackets = 1000;
+
+ explicit PacketStorage(int max_time_stored_ms);
+ virtual ~PacketStorage();
+
+ void StorePacket(uint8 frame_id,
+ uint16 packet_id,
+ const std::vector<uint8>& packet);
+
+ // Copies packet into the buffer pointed to by rtp_buffer.
+ bool GetPacket(uint8 frame_id,
+ uint16 packet_id,
+ std::vector<uint8>* packet);
+ void set_clock(base::TickClock* clock) {
+ clock_ = clock;
+ }
+
+ private:
+ void CleanupOldPackets(base::TimeTicks now);
+
+ base::TimeDelta max_time_stored_;
+ PacketMap stored_packets_;
+ TimeToPacketMap time_to_packet_map_;
+ std::list<linked_ptr<StoredPacket> > free_packets_;
+ scoped_ptr<base::TickClock> default_tick_clock_;
+ base::TickClock* clock_;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_SENDER_PACKET_STORAGE_INCLUDE_PACKET_STORAGE_H_
diff --git a/media/cast/rtp_sender/packet_storage/packet_storage_unittest.cc b/media/cast/rtp_sender/packet_storage/packet_storage_unittest.cc
new file mode 100644
index 0000000..d6de08d
--- /dev/null
+++ b/media/cast/rtp_sender/packet_storage/packet_storage_unittest.cc
@@ -0,0 +1,114 @@
+// 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_sender/packet_storage/packet_storage.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/time.h"
+
+namespace media {
+namespace cast {
+
+static const int kMaxDeltaStoredMs = 500;
+static const base::TimeDelta kDeltaBetweenFrames =
+ base::TimeDelta::FromMilliseconds(33);
+
+static const int64 kStartMillisecond = 123456789;
+
+class PacketStorageTest : public ::testing::Test {
+ protected:
+ PacketStorageTest() : packet_storage_(kMaxDeltaStoredMs) {
+ testing_clock_.Advance(
+ base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ packet_storage_.set_clock(&testing_clock_);
+ }
+
+ PacketStorage packet_storage_;
+ base::SimpleTestTickClock testing_clock_;
+};
+
+TEST_F(PacketStorageTest, TimeOut) {
+ std::vector<uint8> test_123(100, 123); // 100 insertions of the value 123.
+
+ for (uint8 frame_id = 0; frame_id < 30; ++frame_id) {
+ for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+ packet_storage_.StorePacket(frame_id, packet_id, test_123);
+ }
+ testing_clock_.Advance(kDeltaBetweenFrames);
+ }
+
+ // All packets belonging to the first 14 frames is expected to be expired.
+ for (uint8 frame_id = 0; frame_id < 14; ++frame_id) {
+ for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+ std::vector<uint8> packet;
+ EXPECT_FALSE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+ }
+ }
+ // All packets belonging to the next 15 frames is expected to be valid.
+ for (uint8 frame_id = 14; frame_id < 30; ++frame_id) {
+ for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+ std::vector<uint8> packet;
+ EXPECT_TRUE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+ EXPECT_TRUE(packet == test_123);
+ }
+ }
+}
+
+TEST_F(PacketStorageTest, MaxNumberOfPackets) {
+ std::vector<uint8> test_123(100, 123); // 100 insertions of the value 123.
+
+ uint8 frame_id = 0;
+ for (uint16 packet_id = 0; packet_id <= PacketStorage::kMaxStoredPackets;
+ ++packet_id) {
+ packet_storage_.StorePacket(frame_id, packet_id, test_123);
+ }
+ std::vector<uint8> packet;
+ uint16 packet_id = 0;
+ EXPECT_FALSE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+
+ ++packet_id;
+ for (; packet_id <= PacketStorage::kMaxStoredPackets; ++packet_id) {
+ std::vector<uint8> packet;
+ EXPECT_TRUE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+ EXPECT_TRUE(packet == test_123);
+ }
+}
+
+TEST_F(PacketStorageTest, PacketContent) {
+ std::vector<uint8> test_123(100, 123); // 100 insertions of the value 123.
+ std::vector<uint8> test_234(200, 234); // 200 insertions of the value 234.
+
+ for (uint8 frame_id = 0; frame_id < 10; ++frame_id) {
+ for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+ // Every other packet.
+ if (packet_id % 2 == 0) {
+ packet_storage_.StorePacket(frame_id, packet_id, test_123);
+ } else {
+ packet_storage_.StorePacket(frame_id, packet_id, test_234);
+ }
+ }
+ testing_clock_.Advance(kDeltaBetweenFrames);
+ }
+ for (uint8 frame_id = 0; frame_id < 10; ++frame_id) {
+ for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+ std::vector<uint8> packet;
+ EXPECT_TRUE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+ // Every other packet.
+ if (packet_id % 2 == 0) {
+ EXPECT_TRUE(packet == test_123);
+ } else {
+ EXPECT_TRUE(packet == test_234);
+ }
+ }
+ }
+}
+
+} // namespace cast
+} // namespace media
+
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.cc b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.cc
new file mode 100644
index 0000000..264701c
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.cc
@@ -0,0 +1,145 @@
+// 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_sender/rtp_packetizer/rtp_packetizer.h"
+
+#include "base/logging.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "net/base/big_endian.h"
+
+namespace media {
+namespace cast {
+
+static const uint16 kCommonRtpHeaderLength = 12;
+static const uint16 kCastRtpHeaderLength = 7;
+static const uint8 kCastKeyFrameBitMask = 0x80;
+static const uint8 kCastReferenceFrameIdBitMask = 0x40;
+
+RtpPacketizer::RtpPacketizer(PacedPacketSender* transport,
+ PacketStorage* packet_storage,
+ RtpPacketizerConfig rtp_packetizer_config)
+ : config_(rtp_packetizer_config),
+ transport_(transport),
+ packet_storage_(packet_storage),
+ time_last_sent_rtp_timestamp_(0),
+ sequence_number_(config_.sequence_number),
+ rtp_timestamp_(config_.rtp_timestamp),
+ frame_id_(0),
+ packet_id_(0),
+ send_packets_count_(0),
+ send_octet_count_(0) {
+ DCHECK(transport) << "Invalid argument";
+}
+
+RtpPacketizer::~RtpPacketizer() {}
+
+void RtpPacketizer::IncomingEncodedVideoFrame(
+ const EncodedVideoFrame& video_frame,
+ int64 capture_time_ms) {
+ DCHECK(!config_.audio) << "Invalid state";
+ if (config_.audio) return;
+
+ // Timestamp is in 90 KHz for video.
+ rtp_timestamp_ = static_cast<uint32>(capture_time_ms * 90);
+ time_last_sent_rtp_timestamp_ = capture_time_ms;
+
+ Cast(video_frame.key_frame,
+ video_frame.last_referenced_frame_id,
+ rtp_timestamp_,
+ video_frame.data);
+}
+
+void RtpPacketizer::IncomingEncodedAudioFrame(
+ const EncodedAudioFrame& audio_frame,
+ int64 recorded_time) {
+ DCHECK(config_.audio) << "Invalid state";
+ if (!config_.audio) return;
+
+ rtp_timestamp_ += audio_frame.samples; // Timestamp is in samples for audio.
+ time_last_sent_rtp_timestamp_ = recorded_time;
+ Cast(true, 0, rtp_timestamp_, audio_frame.data);
+}
+
+uint16 RtpPacketizer::NextSequenceNumber() {
+ ++sequence_number_;
+ return sequence_number_ - 1;
+}
+
+bool RtpPacketizer::LastSentTimestamp(int64* time_sent,
+ uint32* rtp_timestamp) const {
+ if (time_last_sent_rtp_timestamp_ == 0) return false;
+
+ *time_sent = time_last_sent_rtp_timestamp_;
+ *rtp_timestamp = rtp_timestamp_;
+ return true;
+}
+
+void RtpPacketizer::Cast(bool is_key,
+ uint8 reference_frame_id,
+ uint32 timestamp,
+ std::vector<uint8> data) {
+ uint16 rtp_header_length = kCommonRtpHeaderLength + kCastRtpHeaderLength;
+ uint16 max_length = config_.max_payload_length - rtp_header_length - 1;
+ // Split the payload evenly (round number up).
+ uint32 num_packets = (data.size() + max_length) / max_length;
+ uint32 payload_length = (data.size() + num_packets) / num_packets;
+ DCHECK_LE(payload_length, max_length) << "Invalid argument";
+
+ std::vector<uint8> packet;
+ packet.reserve(kIpPacketSize);
+ size_t remaining_size = data.size();
+ uint8* data_ptr = data.data();
+ while (remaining_size > 0) {
+ packet.clear();
+ if (remaining_size < payload_length) {
+ payload_length = remaining_size;
+ }
+ remaining_size -= payload_length;
+ BuildCommonRTPheader(&packet, remaining_size == 0, timestamp);
+ // Build Cast header.
+ packet.push_back(
+ (is_key ? kCastKeyFrameBitMask : 0) | kCastReferenceFrameIdBitMask);
+ packet.push_back(frame_id_);
+ int start_size = packet.size();
+ packet.resize(start_size + 32);
+ net::BigEndianWriter big_endian_writer(&((packet)[start_size]), 32);
+ big_endian_writer.WriteU16(packet_id_);
+ big_endian_writer.WriteU16(num_packets - 1);
+ packet.push_back(reference_frame_id);
+
+ // Copy payload data.
+ packet.insert(packet.end(), data_ptr, data_ptr + payload_length);
+ // Store packet.
+ packet_storage_->StorePacket(frame_id_, packet_id_, packet);
+ // Send to network.
+ transport_->SendPacket(packet, num_packets);
+ ++packet_id_;
+ data_ptr += payload_length;
+ // Update stats.
+ ++send_packets_count_;
+ send_octet_count_ += payload_length;
+ }
+ DCHECK(packet_id_ == num_packets) << "Invalid state";
+ // Prepare for next frame.
+ packet_id_ = 0;
+ frame_id_ = static_cast<uint8>(frame_id_ + 1);
+}
+
+void RtpPacketizer::BuildCommonRTPheader(
+ std::vector<uint8>* packet, bool marker_bit, uint32 time_stamp) {
+ packet->push_back(0x80);
+ packet->push_back(static_cast<uint8>(config_.payload_type) |
+ (marker_bit ? kRtpMarkerBitMask : 0));
+ int start_size = packet->size();
+ packet->resize(start_size + 80);
+ net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 80);
+ big_endian_writer.WriteU16(sequence_number_);
+ big_endian_writer.WriteU32(time_stamp);
+ big_endian_writer.WriteU32(config_.ssrc);
+ ++sequence_number_;
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.gypi b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.gypi
new file mode 100644
index 0000000..09ceb3b
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.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_packetizer',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ '<(DEPTH)/third_party/',
+ '<(DEPTH)/third_party/webrtc/',
+ ],
+ 'sources': [
+ 'rtp_packetizer.cc',
+ 'rtp_packetizer.h',
+ ], # source
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/net/net.gyp:*',
+ ],
+ },
+ ],
+}
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h
new file mode 100644
index 0000000..f1941cd0
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h
@@ -0,0 +1,65 @@
+// 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_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
+#define MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
+
+#include <cmath>
+#include <list>
+#include <map>
+
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "media/cast/rtp_sender/packet_storage/packet_storage.h"
+#include "media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h"
+
+namespace media {
+namespace cast {
+
+class PacedPacketSender;
+
+class RtpPacketizer {
+ public:
+ RtpPacketizer(PacedPacketSender* transport,
+ PacketStorage* packet_storage,
+ RtpPacketizerConfig rtp_packetizer_config);
+ ~RtpPacketizer();
+
+ void IncomingEncodedVideoFrame(const EncodedVideoFrame& video_frame,
+ int64 capture_time);
+
+ void IncomingEncodedAudioFrame(const EncodedAudioFrame& audio_frame,
+ int64 recorded_time);
+
+ bool LastSentTimestamp(int64* time_sent, uint32* rtp_timestamp) const;
+
+ // Return the next sequence number, and increment by one. Enables unique
+ // incremental sequence numbers for every packet (including retransmissions).
+ uint16 NextSequenceNumber();
+
+ uint32 send_packets_count() {return send_packets_count_;}
+ uint32 send_octet_count() {return send_octet_count_;}
+
+ private:
+ void Cast(bool is_key, uint8 reference_frame_id,
+ uint32 timestamp, std::vector<uint8> data);
+ void BuildCommonRTPheader(std::vector<uint8>* packet, bool marker_bit,
+ uint32 time_stamp);
+ RtpPacketizerConfig config_;
+ PacedPacketSender* transport_;
+ PacketStorage* packet_storage_;
+
+ int64 time_last_sent_rtp_timestamp_;
+ uint16 sequence_number_;
+ uint32 rtp_timestamp_;
+ uint8 frame_id_;
+ uint16 packet_id_;
+
+ uint32 send_packets_count_;
+ uint32 send_octet_count_;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h
new file mode 100644
index 0000000..cd005d5
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h
@@ -0,0 +1,47 @@
+// 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 CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_CONFIG_H_
+#define CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_CONFIG_H_
+
+#include "media/cast/cast_config.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+struct RtpPacketizerConfig {
+ RtpPacketizerConfig() {
+ ssrc = 0;
+ max_payload_length = kIpPacketSize - 28; // Default is IP-v4/UDP.
+ audio = false;
+ frequency = 8000;
+ payload_type = -1;
+ sequence_number = 0;
+ rtp_timestamp = 0;
+ }
+
+ // General.
+ bool audio;
+ int payload_type;
+ uint16 max_payload_length;
+ uint16 sequence_number;
+ uint32 rtp_timestamp;
+ int frequency;
+
+ // SSRC.
+ unsigned int ssrc;
+
+ // Video.
+ VideoCodec video_codec;
+
+ // Audio.
+ uint8 channels;
+ AudioCodec audio_codec;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_CONFIG_H_
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
new file mode 100644
index 0000000..7d99a46
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
@@ -0,0 +1,139 @@
+// 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_sender/rtp_packetizer/rtp_packetizer.h"
+
+#include <gtest/gtest.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "media/cast/rtp_sender/packet_storage/packet_storage.h"
+#include "media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h"
+
+namespace media {
+namespace cast {
+
+static const int kPayload = 127;
+static const uint32 kTimestamp = 10;
+static const uint16 kSeqNum = 33;
+static const int kTimeOffset = 22222;
+static const int kMaxPacketLength = 1500;
+static const bool kMarkerBit = true;
+static const int kSsrc = 0x12345;
+static const uint8 kFrameId = 1;
+static const unsigned int kFrameSize = 5000;
+static const int kTotalHeaderLength = 19;
+static const int kMaxPacketStorageTimeMs = 300;
+
+class TestRtpPacketTransport : public PacedPacketSender {
+ public:
+ explicit TestRtpPacketTransport(RtpPacketizerConfig config)
+ : config_(config),
+ sequence_number_(kSeqNum),
+ packets_sent_(0),
+ expected_number_of_packets_(0) {}
+
+ void VerifyRtpHeader(const RtpCastHeader& rtp_header) {
+ VerifyCommonRtpHeader(rtp_header);
+ VerifyCastRtpHeader(rtp_header);
+ }
+
+ void VerifyCommonRtpHeader(const RtpCastHeader& rtp_header) {
+ EXPECT_EQ(expected_number_of_packets_ == packets_sent_,
+ rtp_header.webrtc.header.markerBit);
+ EXPECT_EQ(kPayload, rtp_header.webrtc.header.payloadType);
+ EXPECT_EQ(sequence_number_, rtp_header.webrtc.header.sequenceNumber);
+ EXPECT_EQ(kTimestamp * 90, rtp_header.webrtc.header.timestamp);
+ EXPECT_EQ(config_.ssrc, rtp_header.webrtc.header.ssrc);
+ EXPECT_EQ(0, rtp_header.webrtc.header.numCSRCs);
+ }
+
+ void VerifyCastRtpHeader(const RtpCastHeader& rtp_header) {
+ // TODO(mikhal)
+ }
+
+ virtual bool SendPacket(const std::vector<uint8>& packet,
+ int num_packets) OVERRIDE {
+ EXPECT_EQ(expected_number_of_packets_, num_packets);
+ ++packets_sent_;
+ RtpHeaderParser parser(packet.data(), packet.size());
+ RtpCastHeader rtp_header;
+ parser.Parse(&rtp_header);
+ VerifyRtpHeader(rtp_header);
+ ++sequence_number_;
+ return true;
+ }
+
+ virtual bool ResendPacket(const std::vector<uint8>& packet,
+ int num_of_packets) OVERRIDE {
+ EXPECT_TRUE(false);
+ return false;
+ }
+
+ virtual bool SendRtcpPacket(const std::vector<uint8>& packet) OVERRIDE {
+ EXPECT_TRUE(false);
+ return false;
+ }
+
+ void SetExpectedNumberOfPackets(int num) {
+ expected_number_of_packets_ = num;
+ }
+
+ RtpPacketizerConfig config_;
+ uint32 sequence_number_;
+ int packets_sent_;
+ int expected_number_of_packets_;
+};
+
+class RtpPacketizerTest : public ::testing::Test {
+ protected:
+ RtpPacketizerTest()
+ :video_frame_(),
+ packet_storage_(kMaxPacketStorageTimeMs) {
+ config_.sequence_number = kSeqNum;
+ config_.ssrc = kSsrc;
+ config_.payload_type = kPayload;
+ config_.max_payload_length = kMaxPacketLength;
+ transport_.reset(new TestRtpPacketTransport(config_));
+ rtp_packetizer_.reset(
+ new RtpPacketizer(transport_.get(), &packet_storage_, config_));
+ }
+
+ ~RtpPacketizerTest() {}
+
+ void SetUp() {
+ video_frame_.key_frame = false;
+ video_frame_.frame_id = kFrameId;
+ video_frame_.last_referenced_frame_id = kFrameId - 1;
+ video_frame_.data.assign(kFrameSize, 123);
+ }
+
+ scoped_ptr<RtpPacketizer> rtp_packetizer_;
+ RtpPacketizerConfig config_;
+ scoped_ptr<TestRtpPacketTransport> transport_;
+ EncodedVideoFrame video_frame_;
+ PacketStorage packet_storage_;
+};
+
+TEST_F(RtpPacketizerTest, SendStandardPackets) {
+ int expected_num_of_packets = kFrameSize / kMaxPacketLength + 1;
+ transport_->SetExpectedNumberOfPackets(expected_num_of_packets);
+ rtp_packetizer_->IncomingEncodedVideoFrame(video_frame_, kTimestamp);
+}
+
+TEST_F(RtpPacketizerTest, Stats) {
+ EXPECT_FALSE(rtp_packetizer_->send_packets_count());
+ EXPECT_FALSE(rtp_packetizer_->send_octet_count());
+ // Insert packets at varying lengths.
+ unsigned int expected_num_of_packets = kFrameSize / kMaxPacketLength + 1;
+ transport_->SetExpectedNumberOfPackets(expected_num_of_packets);
+ rtp_packetizer_->IncomingEncodedVideoFrame(video_frame_, kTimestamp);
+ EXPECT_EQ(expected_num_of_packets, rtp_packetizer_->send_packets_count());
+ EXPECT_EQ(kFrameSize, rtp_packetizer_->send_octet_count());
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.cc b/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.cc
new file mode 100644
index 0000000..b4a3021
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.cc
@@ -0,0 +1,70 @@
+// 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_sender/rtp_packetizer/test/rtp_header_parser.h"
+
+#include <cstddef>
+
+#include "net/base/big_endian.h"
+
+namespace media {
+namespace cast {
+
+RtpHeaderParser::RtpHeaderParser(const uint8* rtp_data,
+ const uint32 rtp_data_length)
+ : rtp_data_begin_(rtp_data),
+ rtp_data_end_(rtp_data ? (rtp_data + rtp_data_length) : NULL) {
+}
+
+RtpHeaderParser::~RtpHeaderParser() {}
+
+bool RtpHeaderParser::Parse(RtpCastHeader* parsed_packet) const {
+ const ptrdiff_t length = rtp_data_end_ - rtp_data_begin_;
+
+ if (length < 12) {
+ return false;
+ }
+
+ const uint8 version = rtp_data_begin_[0] >> 6;
+ if (version != 2) {
+ return false;
+ }
+
+ const uint8 num_csrcs = rtp_data_begin_[0] & 0x0f;
+ const bool marker = ((rtp_data_begin_[1] & 0x80) == 0) ? false : true;
+
+ const uint8 payload_type = rtp_data_begin_[1] & 0x7f;
+
+ const uint16 sequence_number = (rtp_data_begin_[2] << 8) +
+ rtp_data_begin_[3];
+
+ const uint8* ptr = &rtp_data_begin_[4];
+
+ net::BigEndianReader big_endian_reader(ptr, 64);
+ uint32 rtp_timestamp, ssrc;
+ big_endian_reader.ReadU32(&rtp_timestamp);
+ big_endian_reader.ReadU32(&ssrc);
+
+ const uint8 csrc_octs = num_csrcs * 4;
+
+ if ((ptr + csrc_octs) > rtp_data_end_) {
+ return false;
+ }
+
+ parsed_packet->webrtc.header.markerBit = marker;
+ parsed_packet->webrtc.header.payloadType = payload_type;
+ parsed_packet->webrtc.header.sequenceNumber = sequence_number;
+ parsed_packet->webrtc.header.timestamp = rtp_timestamp;
+ parsed_packet->webrtc.header.ssrc = ssrc;
+ parsed_packet->webrtc.header.numCSRCs = num_csrcs;
+
+ parsed_packet->webrtc.type.Audio.numEnergy =
+ parsed_packet->webrtc.header.numCSRCs;
+
+ parsed_packet->webrtc.header.headerLength = 12 + csrc_octs;
+ return true;
+}
+
+} // namespace cast
+} // namespace media \ No newline at end of file
diff --git a/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h b/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h
new file mode 100644
index 0000000..e0d84ac
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h
@@ -0,0 +1,31 @@
+// 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.
+
+// Utility parser for rtp packetizer unittests
+#ifndef MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_TEST_RTP_HEADER_PARSER_H_
+#define MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_TEST_RTP_HEADER_PARSER_H_
+
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+class RtpHeaderParser {
+ public:
+ RtpHeaderParser(const uint8* rtpData,
+ const uint32 rtpDataLength);
+ ~RtpHeaderParser();
+
+ bool Parse(RtpCastHeader* parsed_packet) const;
+ private:
+ const uint8* const rtp_data_begin_;
+ const uint8* const rtp_data_end_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtpHeaderParser);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_TEST_RTP_HEADER_PARSER_H_
diff --git a/media/cast/rtp_sender/rtp_sender.cc b/media/cast/rtp_sender/rtp_sender.cc
new file mode 100644
index 0000000..c735dd0e
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_sender.cc
@@ -0,0 +1,166 @@
+// 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_sender/rtp_sender.h"
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+
+namespace media {
+namespace cast {
+namespace {
+
+// January 1970, in milliseconds.
+static const int64 kNtpJan1970 = 2208988800000LL;
+
+// Magic fractional unit.
+static const uint32 kMagicFractionalUnit = 4294967;
+
+void ConvertTimeToFractions(int64 time_ms, uint32* seconds,
+ uint32* fractions) {
+ *seconds = static_cast<uint32>(time_ms / 1000);
+ *fractions = static_cast<uint32>((time_ms % 1000) * kMagicFractionalUnit);
+}
+
+void ConvertTimeToNtp(int64 time_ms, uint32* ntp_seconds,
+ uint32* ntp_fractions) {
+ ConvertTimeToFractions(time_ms + kNtpJan1970, ntp_seconds, ntp_fractions);
+}
+} // namespace
+
+RtpSender::RtpSender(const AudioSenderConfig* audio_config,
+ const VideoSenderConfig* video_config,
+ PacedPacketSender* transport)
+ : config_(),
+ transport_(transport),
+ default_tick_clock_(new base::DefaultTickClock()),
+ clock_(default_tick_clock_.get()) {
+ // Store generic cast config and create packetizer config.
+ DCHECK(audio_config || video_config) << "Invalid argument";
+ if (audio_config) {
+ storage_.reset(new PacketStorage(audio_config->rtp_history_ms));
+ config_.audio = true;
+ config_.ssrc = audio_config->sender_ssrc;
+ config_.payload_type = audio_config->rtp_payload_type;
+ config_.frequency = audio_config->frequency;
+ config_.audio_codec = audio_config->codec;
+ } else {
+ storage_.reset(new PacketStorage(video_config->rtp_history_ms));
+ config_.audio = false;
+ config_.ssrc = video_config->sender_ssrc;
+ config_.payload_type = video_config->rtp_payload_type;
+ config_.frequency = kVideoFrequency;
+ config_.video_codec = video_config->codec;
+ }
+ // Randomly set start values.
+ config_.sequence_number = base::RandInt(0, 65535);
+ config_.rtp_timestamp = base::RandInt(0, 65535);
+ config_.rtp_timestamp += base::RandInt(0, 65535) << 16;
+ packetizer_.reset(new RtpPacketizer(transport, storage_.get(), config_));
+}
+
+RtpSender::~RtpSender() {}
+
+void RtpSender::IncomingEncodedVideoFrame(const EncodedVideoFrame& video_frame,
+ int64 capture_time) {
+ packetizer_->IncomingEncodedVideoFrame(video_frame, capture_time);
+}
+
+void RtpSender::IncomingEncodedAudioFrame(const EncodedAudioFrame& audio_frame,
+ int64 recorded_time) {
+ packetizer_->IncomingEncodedAudioFrame(audio_frame, recorded_time);
+}
+
+void RtpSender::ResendPackets(
+ const MissingFramesAndPackets& missing_frames_and_packets) {
+ std::vector<uint8> packet;
+ // Iterate over all frames in the list.
+ for (std::map<uint8, std::set<uint16> >::const_iterator it =
+ missing_frames_and_packets.begin();
+ it != missing_frames_and_packets.end(); ++it) {
+ uint8 frame_id = it->first;
+ // Iterate over all of the packets in the frame.
+ const std::set<uint16>& packets = it->second;
+ if (packets.empty()) {
+ VLOG(1) << "Missing all packets in frame " << static_cast<int>(frame_id);
+
+ bool success = false;
+ uint16 packet_id = 0;
+ do {
+ // Get packet from storage.
+ packet.clear();
+ success = storage_->GetPacket(frame_id, packet_id, &packet);
+
+ // Resend packet to the network.
+ if (success) {
+ VLOG(1) << "Resend " << static_cast<int>(frame_id) << ":"
+ << packet_id << " size: " << packets.size();
+ // Set a unique incremental sequence number for every packet.
+ UpdateSequenceNumber(&packet);
+ // Set the size as correspond to each frame.
+ transport_->ResendPacket(packet, packets.size());
+ ++packet_id;
+ }
+ } while (success);
+
+
+ } else {
+ for (std::set<uint16>::const_iterator set_it = packets.begin();
+ set_it != packets.end(); ++set_it) {
+ uint16 packet_id = *set_it;
+ // Get packet from storage.
+ packet.clear();
+ bool success = storage_->GetPacket(frame_id, packet_id, &packet);
+ // Resend packet to the network.
+ if (success) {
+ VLOG(1) << "Resend " << static_cast<int>(frame_id) << ":"
+ << packet_id << " size: " << packet.size();
+ UpdateSequenceNumber(&packet);
+ // Set the size as correspond to each frame.
+ transport_->ResendPacket(packet, packets.size());
+ } else {
+ VLOG(1) << "Failed to resend " << static_cast<int>(frame_id) << ":"
+ << packet_id;
+ }
+ }
+ }
+ }
+}
+
+void RtpSender::UpdateSequenceNumber(std::vector<uint8>* packet) {
+ uint16 new_sequence_number = packetizer_->NextSequenceNumber();
+ int index = 2;
+ (*packet)[index] = (static_cast<uint8>(new_sequence_number));
+ (*packet)[index + 1] =(static_cast<uint8>(new_sequence_number >> 8));
+}
+
+void RtpSender::RtpStatistics(int64 now_ms, RtcpSenderInfo* sender_info) {
+ // The timestamp of this Rtcp packet should be estimated as the timestamp of
+ // the frame being captured at this moment. We are calculating that
+ // timestamp as the last frame's timestamp + the time since the last frame
+ // was captured.
+ uint32 ntp_seconds = 0;
+ uint32 ntp_fraction = 0;
+ ConvertTimeToNtp(now_ms, &ntp_seconds, &ntp_fraction);
+ // sender_info->ntp_seconds = ntp_seconds;
+ sender_info->ntp_fraction = ntp_fraction;
+
+ int64 time_sent_ms;
+ uint32 rtp_timestamp;
+ if (packetizer_->LastSentTimestamp(&time_sent_ms, &rtp_timestamp)) {
+ int64 time_since_last_send_ms = now_ms - time_sent_ms;
+ sender_info->rtp_timestamp = rtp_timestamp +
+ time_since_last_send_ms * (config_.frequency / 1000);
+ } else {
+ sender_info->rtp_timestamp = 0;
+ }
+ sender_info->send_packet_count = packetizer_->send_packets_count();
+ sender_info->send_octet_count = packetizer_->send_octet_count();
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/rtp_sender/rtp_sender.gyp b/media/cast/rtp_sender/rtp_sender.gyp
new file mode 100644
index 0000000..77722c9
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_sender.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_sender',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '<(DEPTH)/',
+ '<(DEPTH)/third_party/',
+ '<(DEPTH)/third_party/webrtc/',
+ ],
+ 'sources': [
+ 'rtp_sender.cc',
+ 'rtp_sender.h',
+ ], # source
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/base/base.gyp:test_support_base',
+ 'packet_storage/packet_storage.gypi:*',
+ 'rtp_packetizer/rtp_packetizer.gypi:*',
+ ],
+ },
+ ],
+}
diff --git a/media/cast/rtp_sender/rtp_sender.h b/media/cast/rtp_sender/rtp_sender.h
new file mode 100644
index 0000000..9352fd3
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_sender.h
@@ -0,0 +1,63 @@
+// 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.
+
+// This file contains the interface to the cast RTP sender.
+
+#ifndef MEDIA_CAST_RTP_SENDER_RTP_SENDER_H_
+#define MEDIA_CAST_RTP_SENDER_RTP_SENDER_H_
+
+#include <map>
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/rtp_sender/packet_storage/packet_storage.h"
+#include "media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h"
+#include "media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h"
+
+namespace media {
+namespace cast {
+
+class PacedPacketSender;
+struct RtcpSenderInfo;
+
+typedef std::map<uint8, std::set<uint16> > MissingFramesAndPackets;
+
+class RtpSender {
+ public:
+ RtpSender(const AudioSenderConfig* audio_config,
+ const VideoSenderConfig* video_config,
+ PacedPacketSender* transport);
+
+ ~RtpSender();
+
+ void IncomingEncodedVideoFrame(const EncodedVideoFrame& video_frame,
+ int64 capture_time);
+
+ void IncomingEncodedAudioFrame(const EncodedAudioFrame& audio_frame,
+ int64 recorded_time);
+
+ void ResendPackets(
+ const MissingFramesAndPackets& missing_frames_and_packets);
+
+ void RtpStatistics(int64 now_ms, RtcpSenderInfo* sender_info);
+
+ private:
+ void UpdateSequenceNumber(std::vector<uint8>* packet);
+
+ RtpPacketizerConfig config_;
+ scoped_ptr<RtpPacketizer> packetizer_;
+ scoped_ptr<PacketStorage> storage_;
+ PacedPacketSender* transport_;
+ scoped_ptr<base::TickClock> default_tick_clock_;
+ base::TickClock* clock_;
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_RTP_SENDER_RTP_SENDER_H_