diff options
author | mikhal@google.com <mikhal@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-29 21:16:13 +0000 |
---|---|---|
committer | mikhal@google.com <mikhal@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-29 21:16:13 +0000 |
commit | b89160a94b276966883a2d4dadd92909382c726e (patch) | |
tree | 2e53277629b2fe6a4eeb5e126477655af54598d4 /media | |
parent | 0c4c10ff318c38b8c66a997316e9153ce78c9e26 (diff) | |
download | chromium_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')
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_ |