diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/third_party/libjingle/source | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/third_party/libjingle/source')
488 files changed, 28815 insertions, 14317 deletions
diff --git a/chromium/third_party/libjingle/source/talk/OWNERS b/chromium/third_party/libjingle/source/talk/OWNERS index ddb55cf3daa..570953a6872 100644 --- a/chromium/third_party/libjingle/source/talk/OWNERS +++ b/chromium/third_party/libjingle/source/talk/OWNERS @@ -5,8 +5,14 @@ hta@webrtc.org juberti@webrtc.org mallinath@webrtc.org perkj@webrtc.org +pthatcher@webrtc.org sergeyu@chromium.org tommi@webrtc.org wu@webrtc.org per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff --git a/chromium/third_party/libjingle/source/talk/PRESUBMIT.py b/chromium/third_party/libjingle/source/talk/PRESUBMIT.py index 4118e107234..8e1a2e3945e 100644 --- a/chromium/third_party/libjingle/source/talk/PRESUBMIT.py +++ b/chromium/third_party/libjingle/source/talk/PRESUBMIT.py @@ -25,16 +25,7 @@ # List of files that should not be committed to DO_NOT_SUBMIT_FILES = [ - "talk/app/webrtc/mediaconstraintsinterface.h", - "talk/app/webrtc/webrtcsdp_unittest.cc", - "talk/base/linux.cc", - "talk/base/linux.h", - "talk/base/linux_unittest.cc", - "talk/main.scons", - "talk/media/base/hybridvideoengine.cc", - "talk/media/base/mediaengine.cc", "talk/media/base/mutedvideocapturer.cc", - "talk/media/base/streamparams.h", "talk/media/base/videocapturer.cc", "talk/media/base/videocapturer.h", "talk/media/base/videocapturer_unittest.cc", @@ -50,9 +41,7 @@ DO_NOT_SUBMIT_FILES = [ "talk/media/webrtc/webrtcvoiceengine.cc", "talk/media/webrtc/webrtcvoiceengine.h", "talk/media/webrtc/webrtcvoiceengine_unittest.cc", - "talk/p2p/base/session.cc", - "talk/session/media/channel.cc", - "talk/session/media/mediasession_unittest.cc"] + "talk/session/media/channel.cc"] def _LicenseHeader(input_api): """Returns the license header regexp.""" @@ -60,6 +49,7 @@ def _LicenseHeader(input_api): current_year = int(input_api.time.strftime('%Y')) allowed_years = (str(s) for s in reversed(xrange(2004, current_year + 1))) years_re = '(' + '|'.join(allowed_years) + ')' + years_re = '%s(--%s)?' % (years_re, years_re) license_header = ( r'.*? libjingle\n' r'.*? Copyright %(year)s,? Google Inc\.\n' diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/OWNERS b/chromium/third_party/libjingle/source/talk/app/webrtc/OWNERS index 40c5bdc1408..967930cf41c 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/OWNERS +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/OWNERS @@ -1,6 +1,7 @@ +glaznev@webrtc.org hellner@google.com juberti@google.com mallinath@google.com perkj@google.com ronghuawu@google.com -tommi@google.com
\ No newline at end of file +tommi@google.com diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/audiotrack.h b/chromium/third_party/libjingle/source/talk/app/webrtc/audiotrack.h index fc09d2b9ef1..2f96527f64a 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/audiotrack.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/audiotrack.h @@ -41,17 +41,22 @@ class AudioTrack : public MediaStreamTrack<AudioTrackInterface> { static talk_base::scoped_refptr<AudioTrack> Create( const std::string& id, AudioSourceInterface* source); - virtual AudioSourceInterface* GetSource() const { + // AudioTrackInterface implementation. + virtual AudioSourceInterface* GetSource() const OVERRIDE { return audio_source_.get(); } - - // This method is used for supporting multiple sources/sinks for AudioTracks. - virtual cricket::AudioRenderer* GetRenderer() { + // TODO(xians): Implement these methods. + virtual void AddSink(AudioTrackSinkInterface* sink) OVERRIDE {} + virtual void RemoveSink(AudioTrackSinkInterface* sink) OVERRIDE {} + virtual bool GetSignalLevel(int* level) OVERRIDE { return false; } + virtual talk_base::scoped_refptr<AudioProcessorInterface> GetAudioProcessor() + OVERRIDE { return NULL; } + virtual cricket::AudioRenderer* GetRenderer() OVERRIDE { return NULL; } - // Implement MediaStreamTrack - virtual std::string kind() const; + // MediaStreamTrack implementation. + virtual std::string kind() const OVERRIDE; protected: AudioTrack(const std::string& label, AudioSourceInterface* audio_source); diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.cc index 6c9e0bc43ff..14caa416b48 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.cc @@ -29,9 +29,9 @@ #include <string> #include "talk/app/webrtc/mediastreamprovider.h" +#include "talk/app/webrtc/sctputils.h" #include "talk/base/logging.h" #include "talk/base/refcount.h" -#include "talk/media/sctp/sctputils.h" namespace webrtc { @@ -46,7 +46,7 @@ talk_base::scoped_refptr<DataChannel> DataChannel::Create( DataChannelProviderInterface* provider, cricket::DataChannelType dct, const std::string& label, - const DataChannelInit* config) { + const InternalDataChannelInit& config) { talk_base::scoped_refptr<DataChannel> channel( new talk_base::RefCountedObject<DataChannel>(provider, dct, label)); if (!channel->Init(config)) { @@ -62,39 +62,40 @@ DataChannel::DataChannel( : label_(label), observer_(NULL), state_(kConnecting), - was_ever_writable_(false), - connected_to_provider_(false), data_channel_type_(dct), provider_(provider), + waiting_for_open_ack_(false), + was_ever_writable_(false), + connected_to_provider_(false), send_ssrc_set_(false), - send_ssrc_(0), receive_ssrc_set_(false), + send_ssrc_(0), receive_ssrc_(0) { } -bool DataChannel::Init(const DataChannelInit* config) { +bool DataChannel::Init(const InternalDataChannelInit& config) { if (data_channel_type_ == cricket::DCT_RTP && - (config->reliable || - config->id != -1 || - config->maxRetransmits != -1 || - config->maxRetransmitTime != -1)) { + (config.reliable || + config.id != -1 || + config.maxRetransmits != -1 || + config.maxRetransmitTime != -1)) { LOG(LS_ERROR) << "Failed to initialize the RTP data channel due to " << "invalid DataChannelInit."; return false; } else if (data_channel_type_ == cricket::DCT_SCTP) { - if (config->id < -1 || - config->maxRetransmits < -1 || - config->maxRetransmitTime < -1) { + if (config.id < -1 || + config.maxRetransmits < -1 || + config.maxRetransmitTime < -1) { LOG(LS_ERROR) << "Failed to initialize the SCTP data channel due to " << "invalid DataChannelInit."; return false; } - if (config->maxRetransmits != -1 && config->maxRetransmitTime != -1) { + if (config.maxRetransmits != -1 && config.maxRetransmitTime != -1) { LOG(LS_ERROR) << "maxRetransmits and maxRetransmitTime should not be both set."; return false; } - config_ = *config; + config_ = config; // Try to connect to the transport in case the transport channel already // exists. @@ -163,17 +164,23 @@ bool DataChannel::Send(const DataBuffer& buffer) { // If the queue is non-empty, we're waiting for SignalReadyToSend, // so just add to the end of the queue and keep waiting. if (!queued_send_data_.empty()) { - return QueueSendData(buffer); + if (!QueueSendData(buffer)) { + if (data_channel_type_ == cricket::DCT_RTP) { + return false; + } + Close(); + } + return true; } cricket::SendDataResult send_result; if (!InternalSendWithoutQueueing(buffer, &send_result)) { - if (send_result == cricket::SDR_BLOCK) { - return QueueSendData(buffer); + if (data_channel_type_ == cricket::DCT_RTP) { + return false; + } + if (send_result != cricket::SDR_BLOCK || !QueueSendData(buffer)) { + Close(); } - // Fail for other results. - // TODO(jiayl): We should close the data channel in this case. - return false; } return true; } @@ -197,9 +204,44 @@ bool DataChannel::SendOpenMessage(const talk_base::Buffer* raw_buffer) { cricket::SendDataResult send_result; bool retval = provider_->SendData(send_params, *buffer, &send_result); - if (!retval && send_result == cricket::SDR_BLOCK) { + if (retval) { + LOG(LS_INFO) << "Sent OPEN message on channel " << config_.id; + // Send data as ordered before we receive any mesage from the remote peer + // to make sure the remote peer will not receive any data before it receives + // the OPEN message. + waiting_for_open_ack_ = true; + } else if (send_result == cricket::SDR_BLOCK) { // Link is congested. Queue for later. QueueControl(buffer.release()); + } else { + LOG(LS_ERROR) << "Failed to send OPEN message with result " + << send_result << " on channel " << config_.id; + } + return retval; +} + +bool DataChannel::SendOpenAckMessage(const talk_base::Buffer* raw_buffer) { + ASSERT(data_channel_type_ == cricket::DCT_SCTP && + was_ever_writable_ && + config_.id >= 0); + + talk_base::scoped_ptr<const talk_base::Buffer> buffer(raw_buffer); + + cricket::SendDataParams send_params; + send_params.ssrc = config_.id; + send_params.ordered = config_.ordered; + send_params.type = cricket::DMT_CONTROL; + + cricket::SendDataResult send_result; + bool retval = provider_->SendData(send_params, *buffer, &send_result); + if (retval) { + LOG(LS_INFO) << "Sent OPEN_ACK message on channel " << config_.id; + } else if (send_result == cricket::SDR_BLOCK) { + // Link is congested. Queue for later. + QueueControl(buffer.release()); + } else { + LOG(LS_ERROR) << "Failed to send OPEN_ACK message with result " + << send_result << " on channel " << config_.id; } return retval; } @@ -254,15 +296,43 @@ void DataChannel::OnDataReceived(cricket::DataChannel* channel, return; } + if (params.type == cricket::DMT_CONTROL) { + ASSERT(data_channel_type_ == cricket::DCT_SCTP); + if (!waiting_for_open_ack_) { + // Ignore it if we are not expecting an ACK message. + LOG(LS_WARNING) << "DataChannel received unexpected CONTROL message, " + << "sid = " << params.ssrc; + return; + } + if (ParseDataChannelOpenAckMessage(payload)) { + // We can send unordered as soon as we receive the ACK message. + waiting_for_open_ack_ = false; + LOG(LS_INFO) << "DataChannel received OPEN_ACK message, sid = " + << params.ssrc; + } else { + LOG(LS_WARNING) << "DataChannel failed to parse OPEN_ACK message, sid = " + << params.ssrc; + } + return; + } + + ASSERT(params.type == cricket::DMT_BINARY || + params.type == cricket::DMT_TEXT); + + LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.ssrc; + // We can send unordered as soon as we receive any DATA message since the + // remote side must have received the OPEN (and old clients do not send + // OPEN_ACK). + waiting_for_open_ack_ = false; + bool binary = (params.type == cricket::DMT_BINARY); talk_base::scoped_ptr<DataBuffer> buffer(new DataBuffer(payload, binary)); if (was_ever_writable_ && observer_) { observer_->OnMessage(*buffer.get()); } else { if (queued_received_data_.size() > kMaxQueuedReceivedDataPackets) { - // TODO(jiayl): We should close the data channel in this case. LOG(LS_ERROR) - << "Queued received data exceeds the max number of packes."; + << "Queued received data exceeds the max number of packets."; ClearQueuedReceivedData(); } queued_received_data_.push(buffer.release()); @@ -279,14 +349,17 @@ void DataChannel::OnChannelReady(bool writable) { if (!was_ever_writable_) { was_ever_writable_ = true; - if (data_channel_type_ == cricket::DCT_SCTP && !config_.negotiated) { - talk_base::Buffer* payload = new talk_base::Buffer; - if (!cricket::WriteDataChannelOpenMessage(label_, config_, payload)) { - // TODO(jiayl): close the data channel on this error. - LOG(LS_ERROR) << "Could not write data channel OPEN message"; - return; + if (data_channel_type_ == cricket::DCT_SCTP) { + if (config_.open_handshake_role == InternalDataChannelInit::kOpener) { + talk_base::Buffer* payload = new talk_base::Buffer; + WriteDataChannelOpenMessage(label_, config_, payload); + SendOpenMessage(payload); + } else if (config_.open_handshake_role == + InternalDataChannelInit::kAcker) { + talk_base::Buffer* payload = new talk_base::Buffer; + WriteDataChannelOpenAckMessage(payload); + SendOpenAckMessage(payload); } - SendOpenMessage(payload); } UpdateState(); @@ -297,6 +370,9 @@ void DataChannel::OnChannelReady(bool writable) { } void DataChannel::DoClose() { + if (state_ == kClosed) + return; + receive_ssrc_set_ = false; send_ssrc_set_ = false; SetState(kClosing); @@ -339,6 +415,9 @@ void DataChannel::UpdateState() { } void DataChannel::SetState(DataState state) { + if (state_ == state) + return; + state_ = state; if (observer_) { observer_->OnStateChange(); @@ -412,7 +491,12 @@ void DataChannel::DeliverQueuedControlData() { while (!queued_control_data_.empty()) { const talk_base::Buffer* buf = queued_control_data_.front(); queued_control_data_.pop(); - SendOpenMessage(buf); + if (config_.open_handshake_role == InternalDataChannelInit::kOpener) { + SendOpenMessage(buf); + } else { + ASSERT(config_.open_handshake_role == InternalDataChannelInit::kAcker); + SendOpenAckMessage(buf); + } } } @@ -430,6 +514,13 @@ bool DataChannel::InternalSendWithoutQueueing( if (data_channel_type_ == cricket::DCT_SCTP) { send_params.ordered = config_.ordered; + // Send as ordered if it is waiting for the OPEN_ACK message. + if (waiting_for_open_ack_ && !config_.ordered) { + send_params.ordered = true; + LOG(LS_VERBOSE) << "Sending data as ordered for unordered DataChannel " + << "because the OPEN_ACK message has not been received."; + } + send_params.max_rtx_count = config_.maxRetransmits; send_params.max_rtx_ms = config_.maxRetransmitTime; send_params.ssrc = config_.id; @@ -442,8 +533,8 @@ bool DataChannel::InternalSendWithoutQueueing( } bool DataChannel::QueueSendData(const DataBuffer& buffer) { - if (queued_send_data_.size() > kMaxQueuedSendDataPackets) { - LOG(LS_ERROR) << "Can't buffer any more data in the data channel."; + if (queued_send_data_.size() >= kMaxQueuedSendDataPackets) { + LOG(LS_ERROR) << "Can't buffer any more data for the data channel."; return false; } queued_send_data_.push_back(new DataBuffer(buffer)); diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.h b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.h index bf31aed5d53..9256e0e071f 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.h @@ -64,6 +64,25 @@ class DataChannelProviderInterface { virtual ~DataChannelProviderInterface() {} }; +struct InternalDataChannelInit : public DataChannelInit { + enum OpenHandshakeRole { + kOpener, + kAcker, + kNone + }; + // The default role is kOpener because the default |negotiated| is false. + InternalDataChannelInit() : open_handshake_role(kOpener) {} + explicit InternalDataChannelInit(const DataChannelInit& base) + : DataChannelInit(base), open_handshake_role(kOpener) { + // If the channel is externally negotiated, do not send the OPEN message. + if (base.negotiated) { + open_handshake_role = kNone; + } + } + + OpenHandshakeRole open_handshake_role; +}; + // DataChannel is a an implementation of the DataChannelInterface based on // libjingle's data engine. It provides an implementation of unreliable or // reliabledata channels. Currently this class is specifically designed to use @@ -87,7 +106,7 @@ class DataChannel : public DataChannelInterface, DataChannelProviderInterface* provider, cricket::DataChannelType dct, const std::string& label, - const DataChannelInit* config); + const InternalDataChannelInit& config); virtual void RegisterObserver(DataChannelObserver* observer); virtual void UnregisterObserver(); @@ -156,7 +175,7 @@ class DataChannel : public DataChannelInterface, virtual ~DataChannel(); private: - bool Init(const DataChannelInit* config); + bool Init(const InternalDataChannelInit& config); void DoClose(); void UpdateState(); void SetState(DataState state); @@ -172,19 +191,20 @@ class DataChannel : public DataChannelInterface, cricket::SendDataResult* send_result); bool QueueSendData(const DataBuffer& buffer); bool SendOpenMessage(const talk_base::Buffer* buffer); - + bool SendOpenAckMessage(const talk_base::Buffer* buffer); std::string label_; - DataChannelInit config_; + InternalDataChannelInit config_; DataChannelObserver* observer_; DataState state_; - bool was_ever_writable_; - bool connected_to_provider_; cricket::DataChannelType data_channel_type_; DataChannelProviderInterface* provider_; + bool waiting_for_open_ack_; + bool was_ever_writable_; + bool connected_to_provider_; bool send_ssrc_set_; - uint32 send_ssrc_; bool receive_ssrc_set_; + uint32 send_ssrc_; uint32 receive_ssrc_; // Control messages that always have to get sent out before any queued // data. @@ -197,7 +217,7 @@ class DataChannelFactory { public: virtual talk_base::scoped_refptr<DataChannel> CreateDataChannel( const std::string& label, - const DataChannelInit* config) = 0; + const InternalDataChannelInit* config) = 0; protected: virtual ~DataChannelFactory() {} diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel_unittest.cc index fdcd2f2cd11..991ae0cae7a 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel_unittest.cc @@ -26,23 +26,48 @@ */ #include "talk/app/webrtc/datachannel.h" +#include "talk/app/webrtc/sctputils.h" #include "talk/app/webrtc/test/fakedatachannelprovider.h" #include "talk/base/gunit.h" -#include "testing/base/public/gmock.h" using webrtc::DataChannel; class FakeDataChannelObserver : public webrtc::DataChannelObserver { public: - MOCK_METHOD0(OnStateChange, void()); - MOCK_METHOD1(OnMessage, void(const webrtc::DataBuffer& buffer)); + FakeDataChannelObserver() + : messages_received_(0), on_state_change_count_(0) {} + + void OnStateChange() { + ++on_state_change_count_; + } + + void OnMessage(const webrtc::DataBuffer& buffer) { + ++messages_received_; + } + + size_t messages_received() const { + return messages_received_; + } + + void ResetOnStateChangeCount() { + on_state_change_count_ = 0; + } + + size_t on_state_change_count() const { + return on_state_change_count_; + } + + private: + size_t messages_received_; + size_t on_state_change_count_; }; class SctpDataChannelTest : public testing::Test { protected: SctpDataChannelTest() : webrtc_data_channel_( - DataChannel::Create(&provider_, cricket::DCT_SCTP, "test", &init_)) { + DataChannel::Create( + &provider_, cricket::DCT_SCTP, "test", init_)) { } void SetChannelReady() { @@ -59,7 +84,7 @@ class SctpDataChannelTest : public testing::Test { webrtc_data_channel_->RegisterObserver(observer_.get()); } - webrtc::DataChannelInit init_; + webrtc::InternalDataChannelInit init_; FakeDataChannelProvider provider_; talk_base::scoped_ptr<FakeDataChannelObserver> observer_; talk_base::scoped_refptr<DataChannel> webrtc_data_channel_; @@ -69,7 +94,7 @@ class SctpDataChannelTest : public testing::Test { TEST_F(SctpDataChannelTest, ConnectedToTransportOnCreated) { provider_.set_transport_available(true); talk_base::scoped_refptr<DataChannel> dc = DataChannel::Create( - &provider_, cricket::DCT_SCTP, "test1", &init_); + &provider_, cricket::DCT_SCTP, "test1", init_); EXPECT_TRUE(provider_.IsConnected(dc.get())); // The sid is not set yet, so it should not have added the streams. @@ -153,15 +178,69 @@ TEST_F(SctpDataChannelTest, OpenMessageSent) { // state. TEST_F(SctpDataChannelTest, LateCreatedChannelTransitionToOpen) { SetChannelReady(); - webrtc::DataChannelInit init; + webrtc::InternalDataChannelInit init; init.id = 1; - talk_base::scoped_refptr<DataChannel> dc = - DataChannel::Create(&provider_, cricket::DCT_SCTP, "test1", &init); + talk_base::scoped_refptr<DataChannel> dc = DataChannel::Create( + &provider_, cricket::DCT_SCTP, "test1", init); EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, dc->state()); EXPECT_TRUE_WAIT(webrtc::DataChannelInterface::kOpen == dc->state(), 1000); } +// Tests that an unordered DataChannel sends data as ordered until the OPEN_ACK +// message is received. +TEST_F(SctpDataChannelTest, SendUnorderedAfterReceivesOpenAck) { + SetChannelReady(); + webrtc::InternalDataChannelInit init; + init.id = 1; + init.ordered = false; + talk_base::scoped_refptr<DataChannel> dc = DataChannel::Create( + &provider_, cricket::DCT_SCTP, "test1", init); + + EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, dc->state(), 1000); + + // Sends a message and verifies it's ordered. + webrtc::DataBuffer buffer("some data"); + ASSERT_TRUE(dc->Send(buffer)); + EXPECT_TRUE(provider_.last_send_data_params().ordered); + + // Emulates receiving an OPEN_ACK message. + cricket::ReceiveDataParams params; + params.ssrc = init.id; + params.type = cricket::DMT_CONTROL; + talk_base::Buffer payload; + webrtc::WriteDataChannelOpenAckMessage(&payload); + dc->OnDataReceived(NULL, params, payload); + + // Sends another message and verifies it's unordered. + ASSERT_TRUE(dc->Send(buffer)); + EXPECT_FALSE(provider_.last_send_data_params().ordered); +} + +// Tests that an unordered DataChannel sends unordered data after any DATA +// message is received. +TEST_F(SctpDataChannelTest, SendUnorderedAfterReceiveData) { + SetChannelReady(); + webrtc::InternalDataChannelInit init; + init.id = 1; + init.ordered = false; + talk_base::scoped_refptr<DataChannel> dc = DataChannel::Create( + &provider_, cricket::DCT_SCTP, "test1", init); + + EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, dc->state(), 1000); + + // Emulates receiving a DATA message. + cricket::ReceiveDataParams params; + params.ssrc = init.id; + params.type = cricket::DMT_TEXT; + webrtc::DataBuffer buffer("data"); + dc->OnDataReceived(NULL, params, buffer.data); + + // Sends a message and verifies it's unordered. + ASSERT_TRUE(dc->Send(buffer)); + EXPECT_FALSE(provider_.last_send_data_params().ordered); +} + // Tests that messages are sent with the right ssrc. TEST_F(SctpDataChannelTest, SendDataSsrc) { webrtc_data_channel_->SetSctpSid(1); @@ -177,12 +256,13 @@ TEST_F(SctpDataChannelTest, ReceiveDataWithInvalidSsrc) { SetChannelReady(); AddObserver(); - EXPECT_CALL(*(observer_.get()), OnMessage(testing::_)).Times(0); cricket::ReceiveDataParams params; params.ssrc = 0; webrtc::DataBuffer buffer("abcd"); webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data); + + EXPECT_EQ(0U, observer_->messages_received()); } // Tests that the incoming messages with right ssrcs are acceted. @@ -191,11 +271,108 @@ TEST_F(SctpDataChannelTest, ReceiveDataWithValidSsrc) { SetChannelReady(); AddObserver(); - EXPECT_CALL(*(observer_.get()), OnMessage(testing::_)).Times(1); cricket::ReceiveDataParams params; params.ssrc = 1; webrtc::DataBuffer buffer("abcd"); webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data); + EXPECT_EQ(1U, observer_->messages_received()); +} + +// Tests that no CONTROL message is sent if the datachannel is negotiated and +// not created from an OPEN message. +TEST_F(SctpDataChannelTest, NoMsgSentIfNegotiatedAndNotFromOpenMsg) { + webrtc::InternalDataChannelInit config; + config.id = 1; + config.negotiated = true; + config.open_handshake_role = webrtc::InternalDataChannelInit::kNone; + + SetChannelReady(); + talk_base::scoped_refptr<DataChannel> dc = DataChannel::Create( + &provider_, cricket::DCT_SCTP, "test1", config); + + EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, dc->state(), 1000); + EXPECT_EQ(0U, provider_.last_send_data_params().ssrc); +} + +// Tests that OPEN_ACK message is sent if the datachannel is created from an +// OPEN message. +TEST_F(SctpDataChannelTest, OpenAckSentIfCreatedFromOpenMessage) { + webrtc::InternalDataChannelInit config; + config.id = 1; + config.negotiated = true; + config.open_handshake_role = webrtc::InternalDataChannelInit::kAcker; + + SetChannelReady(); + talk_base::scoped_refptr<DataChannel> dc = DataChannel::Create( + &provider_, cricket::DCT_SCTP, "test1", config); + + EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, dc->state(), 1000); + + EXPECT_EQ(static_cast<unsigned int>(config.id), + provider_.last_send_data_params().ssrc); + EXPECT_EQ(cricket::DMT_CONTROL, provider_.last_send_data_params().type); } + +// Tests the OPEN_ACK role assigned by InternalDataChannelInit. +TEST_F(SctpDataChannelTest, OpenAckRoleInitialization) { + webrtc::InternalDataChannelInit init; + EXPECT_EQ(webrtc::InternalDataChannelInit::kOpener, init.open_handshake_role); + EXPECT_FALSE(init.negotiated); + + webrtc::DataChannelInit base; + base.negotiated = true; + webrtc::InternalDataChannelInit init2(base); + EXPECT_EQ(webrtc::InternalDataChannelInit::kNone, init2.open_handshake_role); +} + +// Tests that the DataChannel is closed if the sending buffer is full. +TEST_F(SctpDataChannelTest, ClosedWhenSendBufferFull) { + SetChannelReady(); + webrtc::DataBuffer buffer("abcd"); + provider_.set_send_blocked(true); + + for (size_t i = 0; i < 101; ++i) { + EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); + } + + EXPECT_EQ(webrtc::DataChannelInterface::kClosed, + webrtc_data_channel_->state()); +} + +// Tests that the DataChannel is closed on transport errors. +TEST_F(SctpDataChannelTest, ClosedOnTransportError) { + SetChannelReady(); + webrtc::DataBuffer buffer("abcd"); + provider_.set_transport_error(); + + EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); + + EXPECT_EQ(webrtc::DataChannelInterface::kClosed, + webrtc_data_channel_->state()); +} + +// Tests that a already closed DataChannel does not fire onStateChange again. +TEST_F(SctpDataChannelTest, ClosedDataChannelDoesNotFireOnStateChange) { + AddObserver(); + webrtc_data_channel_->Close(); + // OnStateChange called for kClosing and kClosed. + EXPECT_EQ(2U, observer_->on_state_change_count()); + + observer_->ResetOnStateChangeCount(); + webrtc_data_channel_->RemotePeerRequestClose(); + EXPECT_EQ(0U, observer_->on_state_change_count()); +} + +// Tests that RemotePeerRequestClose closes the local DataChannel. +TEST_F(SctpDataChannelTest, RemotePeerRequestClose) { + AddObserver(); + webrtc_data_channel_->RemotePeerRequestClose(); + + // OnStateChange called for kClosing and kClosed. + EXPECT_EQ(2U, observer_->on_state_change_count()); + EXPECT_EQ(webrtc::DataChannelInterface::kClosed, + webrtc_data_channel_->state()); +} + diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannelinterface.h b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannelinterface.h index 7be8a50f5d4..57fe200cfe6 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannelinterface.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannelinterface.h @@ -97,7 +97,9 @@ class DataChannelObserver { class DataChannelInterface : public talk_base::RefCountInterface { public: - enum DataState { // Keep in sync with DataChannel.java:State. + // Keep in sync with DataChannel.java:State and + // RTCDataChannel.h:RTCDataChannelState. + enum DataState { kConnecting, kOpen, // The DataChannel is ready to send data. kClosing, diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/jsepsessiondescription.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/jsepsessiondescription.cc index 8ec145808aa..d7b37b5d761 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/jsepsessiondescription.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/jsepsessiondescription.cc @@ -118,9 +118,6 @@ bool JsepSessionDescription::AddCandidate( } if (mediasection_index >= number_of_mediasections()) return false; - if (candidate_collection_[mediasection_index].HasCandidate(candidate)) { - return true; // Silently ignore this candidate if we already have it. - } const std::string content_name = description_->contents()[mediasection_index].name; const cricket::TransportInfo* transport_info = @@ -137,10 +134,15 @@ bool JsepSessionDescription::AddCandidate( updated_candidate.set_password(transport_info->description.ice_pwd); } - candidate_collection_[mediasection_index].add( - new JsepIceCandidate(candidate->sdp_mid(), - static_cast<int>(mediasection_index), - updated_candidate)); + scoped_ptr<JsepIceCandidate> updated_candidate_wrapper( + new JsepIceCandidate(candidate->sdp_mid(), + static_cast<int>(mediasection_index), + updated_candidate)); + if (!candidate_collection_[mediasection_index].HasCandidate( + updated_candidate_wrapper.get())) + candidate_collection_[mediasection_index].add( + updated_candidate_wrapper.release()); + return true; } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/jsepsessiondescription_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/jsepsessiondescription_unittest.cc index e2b59fba205..55eb3d53925 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/jsepsessiondescription_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/jsepsessiondescription_unittest.cc @@ -32,6 +32,7 @@ #include "talk/base/gunit.h" #include "talk/base/helpers.h" #include "talk/base/scoped_ptr.h" +#include "talk/base/ssladapter.h" #include "talk/base/stringencode.h" #include "talk/p2p/base/candidate.h" #include "talk/p2p/base/constants.h" @@ -96,6 +97,14 @@ static cricket::SessionDescription* CreateCricketSessionDescription() { class JsepSessionDescriptionTest : public testing::Test { protected: + static void SetUpTestCase() { + talk_base::InitializeSSL(); + } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } + virtual void SetUp() { int port = 1234; talk_base::SocketAddress address("127.0.0.1", port++); @@ -195,6 +204,28 @@ TEST_F(JsepSessionDescriptionTest, AddBadCandidate) { EXPECT_FALSE(jsep_desc_->AddCandidate(&bad_candidate2)); } +// Tests that repeatedly adding the same candidate, with or without credentials, +// does not increase the number of candidates in the description. +TEST_F(JsepSessionDescriptionTest, AddCandidateDuplicates) { + JsepIceCandidate jsep_candidate("", 0, candidate_); + EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); + EXPECT_EQ(1u, jsep_desc_->candidates(0)->count()); + + // Add the same candidate again. It should be ignored. + EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); + EXPECT_EQ(1u, jsep_desc_->candidates(0)->count()); + + // Create a new candidate, identical except that the ufrag and pwd are now + // populated. + candidate_.set_username(kCandidateUfragVoice); + candidate_.set_password(kCandidatePwdVoice); + JsepIceCandidate jsep_candidate_with_credentials("", 0, candidate_); + + // This should also be identified as redundant and ignored. + EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate_with_credentials)); + EXPECT_EQ(1u, jsep_desc_->candidates(0)->count()); +} + // Test that we can serialize a JsepSessionDescription and deserialize it again. TEST_F(JsepSessionDescriptionTest, SerializeDeserialize) { std::string sdp = Serialize(jsep_desc_.get()); diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource.cc index 2cd472a5a01..ab9ae4fa99c 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource.cc @@ -37,26 +37,6 @@ using webrtc::MediaSourceInterface; namespace webrtc { -// Constraint keys. -// They are declared as static members in mediaconstraintsinterface.h -const char MediaConstraintsInterface::kEchoCancellation[] = - "googEchoCancellation"; -const char MediaConstraintsInterface::kExperimentalEchoCancellation[] = - "googEchoCancellation2"; -const char MediaConstraintsInterface::kAutoGainControl[] = - "googAutoGainControl"; -const char MediaConstraintsInterface::kExperimentalAutoGainControl[] = - "googAutoGainControl2"; -const char MediaConstraintsInterface::kNoiseSuppression[] = - "googNoiseSuppression"; -const char MediaConstraintsInterface::kHighpassFilter[] = - "googHighpassFilter"; -const char MediaConstraintsInterface::kTypingNoiseDetection[] = - "googTypingNoiseDetection"; -const char MediaConstraintsInterface::kAudioMirroring[] = "googAudioMirroring"; -// TODO(perkj): Remove kInternalAecDump once its not used by Chrome. -const char MediaConstraintsInterface::kInternalAecDump[] = "deprecatedAecDump"; - namespace { // Convert constraints to audio options. Return false if constraints are @@ -90,6 +70,9 @@ bool FromConstraints(const MediaConstraintsInterface::Constraints& constraints, options->experimental_agc.Set(value); else if (iter->key == MediaConstraintsInterface::kNoiseSuppression) options->noise_suppression.Set(value); + else if (iter->key == + MediaConstraintsInterface::kExperimentalNoiseSuppression) + options->experimental_ns.Set(value); else if (iter->key == MediaConstraintsInterface::kHighpassFilter) options->highpass_filter.Set(value); else if (iter->key == MediaConstraintsInterface::kTypingNoiseDetection) @@ -129,8 +112,6 @@ void LocalAudioSource::Initialize( return; } options_.SetAll(audio_options); - if (options.enable_aec_dump) - options_.aec_dump.Set(true); source_state_ = kLive; } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.cc index 2e6af7728fd..0ecadd6913e 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.cc @@ -34,6 +34,94 @@ namespace webrtc { const char MediaConstraintsInterface::kValueTrue[] = "true"; const char MediaConstraintsInterface::kValueFalse[] = "false"; +// Constraints declared as static members in mediastreaminterface.h +// Specified by draft-alvestrand-constraints-resolution-00b +const char MediaConstraintsInterface::kMinAspectRatio[] = "minAspectRatio"; +const char MediaConstraintsInterface::kMaxAspectRatio[] = "maxAspectRatio"; +const char MediaConstraintsInterface::kMaxWidth[] = "maxWidth"; +const char MediaConstraintsInterface::kMinWidth[] = "minWidth"; +const char MediaConstraintsInterface::kMaxHeight[] = "maxHeight"; +const char MediaConstraintsInterface::kMinHeight[] = "minHeight"; +const char MediaConstraintsInterface::kMaxFrameRate[] = "maxFrameRate"; +const char MediaConstraintsInterface::kMinFrameRate[] = "minFrameRate"; + +// Audio constraints. +const char MediaConstraintsInterface::kEchoCancellation[] = + "googEchoCancellation"; +const char MediaConstraintsInterface::kExperimentalEchoCancellation[] = + "googEchoCancellation2"; +const char MediaConstraintsInterface::kAutoGainControl[] = + "googAutoGainControl"; +const char MediaConstraintsInterface::kExperimentalAutoGainControl[] = + "googAutoGainControl2"; +const char MediaConstraintsInterface::kNoiseSuppression[] = + "googNoiseSuppression"; +const char MediaConstraintsInterface::kExperimentalNoiseSuppression[] = + "googNoiseSuppression2"; +const char MediaConstraintsInterface::kHighpassFilter[] = + "googHighpassFilter"; +const char MediaConstraintsInterface::kTypingNoiseDetection[] = + "googTypingNoiseDetection"; +const char MediaConstraintsInterface::kAudioMirroring[] = "googAudioMirroring"; + +// Google-specific constraint keys for a local video source (getUserMedia). +const char MediaConstraintsInterface::kNoiseReduction[] = "googNoiseReduction"; +const char MediaConstraintsInterface::kLeakyBucket[] = "googLeakyBucket"; +const char MediaConstraintsInterface::kTemporalLayeredScreencast[] = + "googTemporalLayeredScreencast"; + +// Constraint keys for CreateOffer / CreateAnswer defined in W3C specification. +const char MediaConstraintsInterface::kOfferToReceiveAudio[] = + "OfferToReceiveAudio"; +const char MediaConstraintsInterface::kOfferToReceiveVideo[] = + "OfferToReceiveVideo"; +const char MediaConstraintsInterface::kVoiceActivityDetection[] = + "VoiceActivityDetection"; +const char MediaConstraintsInterface::kIceRestart[] = + "IceRestart"; +// Google specific constraint for BUNDLE enable/disable. +const char MediaConstraintsInterface::kUseRtpMux[] = + "googUseRtpMUX"; + +// Below constraints should be used during PeerConnection construction. +const char MediaConstraintsInterface::kEnableDtlsSrtp[] = + "DtlsSrtpKeyAgreement"; +const char MediaConstraintsInterface::kEnableRtpDataChannels[] = + "RtpDataChannels"; +// Google-specific constraint keys. +const char MediaConstraintsInterface::kEnableDscp[] = "googDscp"; +const char MediaConstraintsInterface::kEnableIPv6[] = "googIPv6"; +const char MediaConstraintsInterface::kEnableVideoSuspendBelowMinBitrate[] = + "googSuspendBelowMinBitrate"; +const char MediaConstraintsInterface::kImprovedWifiBwe[] = + "googImprovedWifiBwe"; +const char MediaConstraintsInterface::kScreencastMinBitrate[] = + "googScreencastMinBitrate"; +const char MediaConstraintsInterface::kSkipEncodingUnusedStreams[] = + "googSkipEncodingUnusedStreams"; +// TODO(ronghuawu): Remove once cpu overuse detection is stable. +const char MediaConstraintsInterface::kCpuOveruseDetection[] = + "googCpuOveruseDetection"; +const char MediaConstraintsInterface::kCpuUnderuseThreshold[] = + "googCpuUnderuseThreshold"; +const char MediaConstraintsInterface::kCpuOveruseThreshold[] = + "googCpuOveruseThreshold"; +const char MediaConstraintsInterface::kCpuUnderuseEncodeRsdThreshold[] = + "googCpuUnderuseEncodeRsdThreshold"; +const char MediaConstraintsInterface::kCpuOveruseEncodeRsdThreshold[] = + "googCpuOveruseEncodeRsdThreshold"; +const char MediaConstraintsInterface::kCpuOveruseEncodeUsage[] = + "googCpuOveruseEncodeUsage"; +const char MediaConstraintsInterface::kHighStartBitrate[] = + "googHighStartBitrate"; +const char MediaConstraintsInterface::kHighBitrate[] = + "googHighBitrate"; +const char MediaConstraintsInterface::kVeryHighBitrate[] = + "googVeryHighBitrate"; +const char MediaConstraintsInterface::kPayloadPadding[] = "googPayloadPadding"; +const char MediaConstraintsInterface::kOpusFec[] = "googOpusFec"; + + // Set |value| to the value associated with the first appearance of |key|, or // return false if |key| is not found. bool MediaConstraintsInterface::Constraints::FindFirst( diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.h b/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.h index ba6b09be910..36257db7157 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.h @@ -60,7 +60,6 @@ class MediaConstraintsInterface { virtual const Constraints& GetMandatory() const = 0; virtual const Constraints& GetOptional() const = 0; - // Constraint keys used by a local video source. // Specified by draft-alvestrand-constraints-resolution-00b static const char kMinAspectRatio[]; // minAspectRatio @@ -79,6 +78,7 @@ class MediaConstraintsInterface { static const char kAutoGainControl[]; // googAutoGainControl static const char kExperimentalAutoGainControl[]; // googAutoGainControl2 static const char kNoiseSuppression[]; // googNoiseSuppression + static const char kExperimentalNoiseSuppression[]; // googNoiseSuppression2 static const char kHighpassFilter[]; // googHighpassFilter static const char kTypingNoiseDetection[]; // googTypingNoiseDetection static const char kAudioMirroring[]; // googAudioMirroring @@ -86,9 +86,8 @@ class MediaConstraintsInterface { // Google-specific constraint keys for a local video source static const char kNoiseReduction[]; // googNoiseReduction static const char kLeakyBucket[]; // googLeakyBucket - // googTemporalLayeredScreencast static const char kTemporalLayeredScreencast[]; - static const char kCpuOveruseDetection[]; + // googTemporalLayeredScreencast // Constraint keys for CreateOffer / CreateAnswer // Specified by the W3C PeerConnection spec @@ -103,27 +102,47 @@ class MediaConstraintsInterface { static const char kValueTrue[]; // true static const char kValueFalse[]; // false + // PeerConnection constraint keys. // Temporary pseudo-constraints used to enable DTLS-SRTP static const char kEnableDtlsSrtp[]; // Enable DTLS-SRTP // Temporary pseudo-constraints used to enable DataChannels static const char kEnableRtpDataChannels[]; // Enable RTP DataChannels - // TODO(perkj): Remove kEnableSctpDataChannels once Chrome use - // PeerConnectionFactory::SetOptions. - static const char kEnableSctpDataChannels[]; // Enable SCTP DataChannels + // Google-specific constraint keys. // Temporary pseudo-constraint for enabling DSCP through JS. - static const char kEnableDscp[]; + static const char kEnableDscp[]; // googDscp + // Constraint to enable IPv6 through JS. + static const char kEnableIPv6[]; // googIPv6 + // Temporary constraint to enable suspend below min bitrate feature. + static const char kEnableVideoSuspendBelowMinBitrate[]; + // googSuspendBelowMinBitrate + static const char kImprovedWifiBwe[]; // googImprovedWifiBwe + static const char kScreencastMinBitrate[]; // googScreencastMinBitrate + static const char kSkipEncodingUnusedStreams[]; + // googSkipEncodingUnusedStreams + static const char kCpuOveruseDetection[]; // googCpuOveruseDetection + static const char kCpuUnderuseThreshold[]; // googCpuUnderuseThreshold + static const char kCpuOveruseThreshold[]; // googCpuOveruseThreshold + // Low cpu adaptation threshold for relative standard deviation of encode + // time. + static const char kCpuUnderuseEncodeRsdThreshold[]; + // High cpu adaptation threshold for relative standard deviation of encode + // time. + static const char kCpuOveruseEncodeRsdThreshold[]; + static const char kCpuOveruseEncodeUsage[]; // googCpuOveruseEncodeUsage + static const char kHighStartBitrate[]; // googHighStartBitrate + static const char kHighBitrate[]; // googHighBitrate + static const char kVeryHighBitrate[]; // googVeryHighBitrate + static const char kPayloadPadding[]; // googPayloadPadding + + // PeerConnection codec constraint keys. This should be combined with the + // values above. + // kOpusFec controls whether we ask the other side to turn on FEC for Opus. + static const char kOpusFec[]; // googOpusFec // The prefix of internal-only constraints whose JS set values should be // stripped by Chrome before passed down to Libjingle. static const char kInternalConstraintPrefix[]; - // These constraints are for internal use only, representing Chrome command - // line flags. So they are prefixed with "internal" so JS values will be - // removed. - // Used by a local audio source. - // TODO(perkj): Remove once Chrome use PeerConnectionFactory::SetOptions. - static const char kInternalAecDump[]; // internalAecDump - protected: // Dtor protected as objects shouldn't be deleted via this interface virtual ~MediaConstraintsInterface() {} diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastream_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastream_unittest.cc index bb2d50e6ae6..113242faf50 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastream_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastream_unittest.cc @@ -33,7 +33,8 @@ #include "talk/base/refcount.h" #include "talk/base/scoped_ptr.h" #include "talk/base/gunit.h" -#include "testing/base/public/gmock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" static const char kStreamLabel1[] = "local_stream_1"; static const char kVideoTrackId[] = "dummy_video_cam_1"; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler.cc index b09af7892fb..ca28cf49736 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler.cc @@ -56,14 +56,42 @@ void TrackHandler::OnChanged() { } } +LocalAudioSinkAdapter::LocalAudioSinkAdapter() : sink_(NULL) {} + +LocalAudioSinkAdapter::~LocalAudioSinkAdapter() { + talk_base::CritScope lock(&lock_); + if (sink_) + sink_->OnClose(); +} + +void LocalAudioSinkAdapter::OnData(const void* audio_data, + int bits_per_sample, + int sample_rate, + int number_of_channels, + int number_of_frames) { + talk_base::CritScope lock(&lock_); + if (sink_) { + sink_->OnData(audio_data, bits_per_sample, sample_rate, + number_of_channels, number_of_frames); + } +} + +void LocalAudioSinkAdapter::SetSink(cricket::AudioRenderer::Sink* sink) { + talk_base::CritScope lock(&lock_); + ASSERT(!sink || !sink_); + sink_ = sink; +} + LocalAudioTrackHandler::LocalAudioTrackHandler( AudioTrackInterface* track, uint32 ssrc, AudioProviderInterface* provider) : TrackHandler(track, ssrc), audio_track_(track), - provider_(provider) { + provider_(provider), + sink_adapter_(new LocalAudioSinkAdapter()) { OnEnabledChanged(); + track->AddSink(sink_adapter_.get()); } LocalAudioTrackHandler::~LocalAudioTrackHandler() { @@ -74,6 +102,7 @@ void LocalAudioTrackHandler::OnStateChanged() { } void LocalAudioTrackHandler::Stop() { + audio_track_->RemoveSink(sink_adapter_.get()); cricket::AudioOptions options; provider_->SetAudioSend(ssrc(), false, options, NULL); } @@ -81,11 +110,18 @@ void LocalAudioTrackHandler::Stop() { void LocalAudioTrackHandler::OnEnabledChanged() { cricket::AudioOptions options; if (audio_track_->enabled() && audio_track_->GetSource()) { + // TODO(xians): Remove this static_cast since we should be able to connect + // a remote audio track to peer connection. options = static_cast<LocalAudioSource*>( audio_track_->GetSource())->options(); } - provider_->SetAudioSend(ssrc(), audio_track_->enabled(), options, - audio_track_->GetRenderer()); + + // Use the renderer if the audio track has one, otherwise use the sink + // adapter owned by this class. + cricket::AudioRenderer* renderer = audio_track_->GetRenderer() ? + audio_track_->GetRenderer() : sink_adapter_.get(); + ASSERT(renderer != NULL); + provider_->SetAudioSend(ssrc(), audio_track_->enabled(), options, renderer); } RemoteAudioTrackHandler::RemoteAudioTrackHandler( @@ -95,10 +131,12 @@ RemoteAudioTrackHandler::RemoteAudioTrackHandler( : TrackHandler(track, ssrc), audio_track_(track), provider_(provider) { + track->GetSource()->RegisterAudioObserver(this); OnEnabledChanged(); } RemoteAudioTrackHandler::~RemoteAudioTrackHandler() { + audio_track_->GetSource()->UnregisterAudioObserver(this); } void RemoteAudioTrackHandler::Stop() { @@ -113,6 +151,14 @@ void RemoteAudioTrackHandler::OnEnabledChanged() { audio_track_->GetRenderer()); } +void RemoteAudioTrackHandler::OnSetVolume(double volume) { + // When the track is disabled, the volume of the source, which is the + // corresponding WebRtc Voice Engine channel will be 0. So we do not allow + // setting the volume to the source when the track is disabled. + if (audio_track_->enabled()) + provider_->SetAudioPlayoutVolume(ssrc(), volume); +} + LocalVideoTrackHandler::LocalVideoTrackHandler( VideoTrackInterface* track, uint32 ssrc, diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler.h b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler.h index 0cd34d615a4..53afd556285 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler.h @@ -40,6 +40,7 @@ #include "talk/app/webrtc/mediastreamprovider.h" #include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/base/thread.h" +#include "talk/media/base/audiorenderer.h" namespace webrtc { @@ -67,6 +68,28 @@ class TrackHandler : public ObserverInterface { bool enabled_; }; +// LocalAudioSinkAdapter receives data callback as a sink to the local +// AudioTrack, and passes the data to the sink of AudioRenderer. +class LocalAudioSinkAdapter : public AudioTrackSinkInterface, + public cricket::AudioRenderer { + public: + LocalAudioSinkAdapter(); + virtual ~LocalAudioSinkAdapter(); + + private: + // AudioSinkInterface implementation. + virtual void OnData(const void* audio_data, int bits_per_sample, + int sample_rate, int number_of_channels, + int number_of_frames) OVERRIDE; + + // cricket::AudioRenderer implementation. + virtual void SetSink(cricket::AudioRenderer::Sink* sink) OVERRIDE; + + cricket::AudioRenderer::Sink* sink_; + // Critical section protecting |sink_|. + talk_base::CriticalSection lock_; +}; + // LocalAudioTrackHandler listen to events on a local AudioTrack instance // connected to a PeerConnection and orders the |provider| to executes the // requested change. @@ -86,12 +109,17 @@ class LocalAudioTrackHandler : public TrackHandler { private: AudioTrackInterface* audio_track_; AudioProviderInterface* provider_; + + // Used to pass the data callback from the |audio_track_| to the other + // end of cricket::AudioRenderer. + talk_base::scoped_ptr<LocalAudioSinkAdapter> sink_adapter_; }; // RemoteAudioTrackHandler listen to events on a remote AudioTrack instance // connected to a PeerConnection and orders the |provider| to executes the // requested change. -class RemoteAudioTrackHandler : public TrackHandler { +class RemoteAudioTrackHandler : public AudioSourceInterface::AudioObserver, + public TrackHandler { public: RemoteAudioTrackHandler(AudioTrackInterface* track, uint32 ssrc, @@ -104,6 +132,9 @@ class RemoteAudioTrackHandler : public TrackHandler { virtual void OnEnabledChanged() OVERRIDE; private: + // AudioSourceInterface::AudioObserver implementation. + virtual void OnSetVolume(double volume) OVERRIDE; + AudioTrackInterface* audio_track_; AudioProviderInterface* provider_; }; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler_unittest.cc index 475258e9b98..9a53f355619 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamhandler_unittest.cc @@ -31,13 +31,15 @@ #include "talk/app/webrtc/audiotrack.h" #include "talk/app/webrtc/mediastream.h" +#include "talk/app/webrtc/remoteaudiosource.h" #include "talk/app/webrtc/streamcollection.h" #include "talk/app/webrtc/videosource.h" #include "talk/app/webrtc/videotrack.h" #include "talk/base/gunit.h" #include "talk/media/base/fakevideocapturer.h" #include "talk/media/base/mediachannel.h" -#include "testing/base/public/gmock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" using ::testing::_; using ::testing::Exactly; @@ -59,6 +61,7 @@ class MockAudioProvider : public AudioProviderInterface { MOCK_METHOD4(SetAudioSend, void(uint32 ssrc, bool enable, const cricket::AudioOptions& options, cricket::AudioRenderer* renderer)); + MOCK_METHOD2(SetAudioPlayoutVolume, void(uint32 ssrc, double volume)); }; // Helper class to test MediaStreamHandler. @@ -110,12 +113,11 @@ class MediaStreamHandlerTest : public testing::Test { FakeVideoSource::Create()); video_track_ = VideoTrack::Create(kVideoTrackId, source); EXPECT_TRUE(stream_->AddTrack(video_track_)); - audio_track_ = AudioTrack::Create(kAudioTrackId, - NULL); - EXPECT_TRUE(stream_->AddTrack(audio_track_)); } void AddLocalAudioTrack() { + audio_track_ = AudioTrack::Create(kAudioTrackId, NULL); + EXPECT_TRUE(stream_->AddTrack(audio_track_)); EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _)); handlers_.AddLocalAudioTrack(stream_, stream_->GetAudioTracks()[0], kAudioSsrc); @@ -144,6 +146,9 @@ class MediaStreamHandlerTest : public testing::Test { } void AddRemoteAudioTrack() { + audio_track_ = AudioTrack::Create(kAudioTrackId, + RemoteAudioSource::Create().get()); + EXPECT_TRUE(stream_->AddTrack(audio_track_)); EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, true, _)); handlers_.AddRemoteAudioTrack(stream_, stream_->GetAudioTracks()[0], kAudioSsrc); @@ -292,4 +297,27 @@ TEST_F(MediaStreamHandlerTest, RemoteVideoTrackDisable) { handlers_.TearDown(); } +TEST_F(MediaStreamHandlerTest, RemoteAudioTrackSetVolume) { + AddRemoteAudioTrack(); + + double volume = 0.5; + EXPECT_CALL(audio_provider_, SetAudioPlayoutVolume(kAudioSsrc, volume)); + audio_track_->GetSource()->SetVolume(volume); + + // Disable the audio track, this should prevent setting the volume. + EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, false, _)); + audio_track_->set_enabled(false); + audio_track_->GetSource()->SetVolume(1.0); + + EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, true, _)); + audio_track_->set_enabled(true); + + double new_volume = 0.8; + EXPECT_CALL(audio_provider_, SetAudioPlayoutVolume(kAudioSsrc, new_volume)); + audio_track_->GetSource()->SetVolume(new_volume); + + RemoveRemoteAudioTrack(); + handlers_.TearDown(); +} + } // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h index b2c4468fb99..a3439c59c43 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h @@ -142,9 +142,64 @@ class VideoTrackInterface : public MediaStreamTrackInterface { // AudioSourceInterface is a reference counted source used for AudioTracks. // The same source can be used in multiple AudioTracks. -// TODO(perkj): Extend this class with necessary methods to allow separate -// sources for each audio track. class AudioSourceInterface : public MediaSourceInterface { + public: + class AudioObserver { + public: + virtual void OnSetVolume(double volume) = 0; + + protected: + virtual ~AudioObserver() {} + }; + + // TODO(xians): Makes all the interface pure virtual after Chrome has their + // implementations. + // Sets the volume to the source. |volume| is in the range of [0, 10]. + virtual void SetVolume(double volume) {} + + // Registers/unregisters observer to the audio source. + virtual void RegisterAudioObserver(AudioObserver* observer) {} + virtual void UnregisterAudioObserver(AudioObserver* observer) {} +}; + +// Interface for receiving audio data from a AudioTrack. +class AudioTrackSinkInterface { + public: + virtual void OnData(const void* audio_data, + int bits_per_sample, + int sample_rate, + int number_of_channels, + int number_of_frames) = 0; + protected: + virtual ~AudioTrackSinkInterface() {} +}; + +// Interface of the audio processor used by the audio track to collect +// statistics. +class AudioProcessorInterface : public talk_base::RefCountInterface { + public: + struct AudioProcessorStats { + AudioProcessorStats() : typing_noise_detected(false), + echo_return_loss(0), + echo_return_loss_enhancement(0), + echo_delay_median_ms(0), + aec_quality_min(0.0), + echo_delay_std_ms(0) {} + ~AudioProcessorStats() {} + + bool typing_noise_detected; + int echo_return_loss; + int echo_return_loss_enhancement; + int echo_delay_median_ms; + float aec_quality_min; + int echo_delay_std_ms; + }; + + // Get audio processor statistics. + virtual void GetStats(AudioProcessorStats* stats) = 0; + + protected: + virtual ~AudioProcessorInterface() {} }; class AudioTrackInterface : public MediaStreamTrackInterface { @@ -152,10 +207,26 @@ class AudioTrackInterface : public MediaStreamTrackInterface { // TODO(xians): Figure out if the following interface should be const or not. virtual AudioSourceInterface* GetSource() const = 0; - // Gets a pointer to the audio renderer of this AudioTrack. + // Add/Remove a sink that will receive the audio data from the track. + virtual void AddSink(AudioTrackSinkInterface* sink) = 0; + virtual void RemoveSink(AudioTrackSinkInterface* sink) = 0; + + // Get the signal level from the audio track. + // Return true on success, otherwise false. + // TODO(xians): Change the interface to int GetSignalLevel() and pure virtual + // after Chrome has the correct implementation of the interface. + virtual bool GetSignalLevel(int* level) { return false; } + + // Get the audio processor used by the audio track. Return NULL if the track + // does not have any processor. + // TODO(xians): Make the interface pure virtual. + virtual talk_base::scoped_refptr<AudioProcessorInterface> + GetAudioProcessor() { return NULL; } + + // Get a pointer to the audio renderer of this AudioTrack. // The pointer is valid for the lifetime of this AudioTrack. - // TODO(xians): Make the following interface pure virtual once Chrome has its - // implementation. + // TODO(xians): Remove the following interface after Chrome switches to + // AddSink() and RemoveSink() interfaces. virtual cricket::AudioRenderer* GetRenderer() { return NULL; } protected: diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamprovider.h b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamprovider.h index ae00b1de751..5cf0e271695 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamprovider.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamprovider.h @@ -53,6 +53,10 @@ class AudioProviderInterface { const cricket::AudioOptions& options, cricket::AudioRenderer* renderer) = 0; + // Sets the audio playout volume of a remote audio track with |ssrc|. + // |volume| is in the range of [0, 10]. + virtual void SetAudioPlayoutVolume(uint32 ssrc, double volume) = 0; + protected: virtual ~AudioProviderInterface() {} }; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.cc index 7586938ce60..99f627a1d39 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.cc @@ -33,7 +33,9 @@ #include "talk/app/webrtc/mediastreamproxy.h" #include "talk/app/webrtc/mediaconstraintsinterface.h" #include "talk/app/webrtc/mediastreamtrackproxy.h" +#include "talk/app/webrtc/remoteaudiosource.h" #include "talk/app/webrtc/remotevideocapturer.h" +#include "talk/app/webrtc/sctputils.h" #include "talk/app/webrtc/videosource.h" #include "talk/app/webrtc/videotrack.h" #include "talk/base/bytebuffer.h" @@ -49,18 +51,6 @@ namespace webrtc { using talk_base::scoped_ptr; using talk_base::scoped_refptr; -// Supported MediaConstraints. -const char MediaConstraintsInterface::kOfferToReceiveAudio[] = - "OfferToReceiveAudio"; -const char MediaConstraintsInterface::kOfferToReceiveVideo[] = - "OfferToReceiveVideo"; -const char MediaConstraintsInterface::kIceRestart[] = - "IceRestart"; -const char MediaConstraintsInterface::kUseRtpMux[] = - "googUseRtpMUX"; -const char MediaConstraintsInterface::kVoiceActivityDetection[] = - "VoiceActivityDetection"; - static bool ParseConstraints( const MediaConstraintsInterface* constraints, cricket::MediaSessionOptions* options, bool is_answer) { @@ -151,7 +141,7 @@ class RemoteMediaStreamFactory { AudioTrackInterface* AddAudioTrack(webrtc::MediaStreamInterface* stream, const std::string& track_id) { return AddTrack<AudioTrackInterface, AudioTrack, AudioTrackProxy>( - stream, track_id, static_cast<AudioSourceInterface*>(NULL)); + stream, track_id, RemoteAudioSource::Create().get()); } VideoTrackInterface* AddVideoTrack(webrtc::MediaStreamInterface* stream, @@ -208,14 +198,8 @@ void MediaStreamSignaling::TearDown() { bool MediaStreamSignaling::IsSctpSidAvailable(int sid) const { if (sid < 0 || sid > static_cast<int>(cricket::kMaxSctpSid)) return false; - for (SctpDataChannels::const_iterator iter = sctp_data_channels_.begin(); - iter != sctp_data_channels_.end(); - ++iter) { - if ((*iter)->id() == sid) { - return false; - } - } - return true; + + return FindDataChannelBySid(sid) < 0; } // Gets the first unused odd/even id based on the DTLS role. If |role| is @@ -261,13 +245,24 @@ bool MediaStreamSignaling::AddDataChannel(DataChannel* data_channel) { } bool MediaStreamSignaling::AddDataChannelFromOpenMessage( - const std::string& label, - const DataChannelInit& config) { + const cricket::ReceiveDataParams& params, + const talk_base::Buffer& payload) { if (!data_channel_factory_) { LOG(LS_WARNING) << "Remote peer requested a DataChannel but DataChannels " << "are not supported."; return false; } + + std::string label; + InternalDataChannelInit config; + config.id = params.ssrc; + if (!ParseDataChannelOpenMessage(payload, &label, &config)) { + LOG(LS_WARNING) << "Failed to parse the OPEN message for sid " + << params.ssrc; + return false; + } + config.open_handshake_role = InternalDataChannelInit::kAcker; + scoped_refptr<DataChannel> channel( data_channel_factory_->CreateDataChannel(label, &config)); if (!channel.get()) { @@ -279,6 +274,23 @@ bool MediaStreamSignaling::AddDataChannelFromOpenMessage( return true; } +void MediaStreamSignaling::RemoveSctpDataChannel(int sid) { + for (SctpDataChannels::iterator iter = sctp_data_channels_.begin(); + iter != sctp_data_channels_.end(); + ++iter) { + if ((*iter)->id() == sid) { + sctp_data_channels_.erase(iter); + + if (talk_base::IsEven(sid) && sid <= last_allocated_sctp_even_sid_) { + last_allocated_sctp_even_sid_ = sid - 2; + } else if (talk_base::IsOdd(sid) && sid <= last_allocated_sctp_odd_sid_) { + last_allocated_sctp_odd_sid_ = sid - 2; + } + return; + } + } +} + bool MediaStreamSignaling::AddLocalStream(MediaStreamInterface* local_stream) { if (local_streams_->find(local_stream->label()) != NULL) { LOG(LS_WARNING) << "MediaStream with label " << local_stream->label() @@ -295,24 +307,24 @@ bool MediaStreamSignaling::AddLocalStream(MediaStreamInterface* local_stream) { AudioTrackVector audio_tracks = local_stream->GetAudioTracks(); for (AudioTrackVector::const_iterator it = audio_tracks.begin(); it != audio_tracks.end(); ++it) { - TrackInfos::const_iterator track_info_it = - local_audio_tracks_.find((*it)->id()); - if (track_info_it != local_audio_tracks_.end()) { - const TrackInfo& info = track_info_it->second; - OnLocalTrackSeen(info.stream_label, info.track_id, info.ssrc, - cricket::MEDIA_TYPE_AUDIO); + const TrackInfo* track_info = FindTrackInfo(local_audio_tracks_, + local_stream->label(), + (*it)->id()); + if (track_info) { + OnLocalTrackSeen(track_info->stream_label, track_info->track_id, + track_info->ssrc, cricket::MEDIA_TYPE_AUDIO); } } VideoTrackVector video_tracks = local_stream->GetVideoTracks(); for (VideoTrackVector::const_iterator it = video_tracks.begin(); it != video_tracks.end(); ++it) { - TrackInfos::const_iterator track_info_it = - local_video_tracks_.find((*it)->id()); - if (track_info_it != local_video_tracks_.end()) { - const TrackInfo& info = track_info_it->second; - OnLocalTrackSeen(info.stream_label, info.track_id, info.ssrc, - cricket::MEDIA_TYPE_VIDEO); + const TrackInfo* track_info = FindTrackInfo(local_video_tracks_, + local_stream->label(), + (*it)->id()); + if (track_info) { + OnLocalTrackSeen(track_info->stream_label, track_info->track_id, + track_info->ssrc, cricket::MEDIA_TYPE_VIDEO); } } return true; @@ -320,6 +332,28 @@ bool MediaStreamSignaling::AddLocalStream(MediaStreamInterface* local_stream) { void MediaStreamSignaling::RemoveLocalStream( MediaStreamInterface* local_stream) { + AudioTrackVector audio_tracks = local_stream->GetAudioTracks(); + for (AudioTrackVector::const_iterator it = audio_tracks.begin(); + it != audio_tracks.end(); ++it) { + const TrackInfo* track_info = FindTrackInfo(local_audio_tracks_, + local_stream->label(), + (*it)->id()); + if (track_info) { + stream_observer_->OnRemoveLocalAudioTrack(local_stream, *it, + track_info->ssrc); + } + } + VideoTrackVector video_tracks = local_stream->GetVideoTracks(); + for (VideoTrackVector::const_iterator it = video_tracks.begin(); + it != video_tracks.end(); ++it) { + const TrackInfo* track_info = FindTrackInfo(local_video_tracks_, + local_stream->label(), + (*it)->id()); + if (track_info) { + stream_observer_->OnRemoveLocalVideoTrack(local_stream, *it); + } + } + local_streams_->RemoveStream(local_stream); stream_observer_->OnRemoveLocalStream(local_stream); } @@ -464,36 +498,21 @@ void MediaStreamSignaling::OnVideoChannelClose() { } void MediaStreamSignaling::OnDataChannelClose() { - RtpDataChannels::iterator it1 = rtp_data_channels_.begin(); - for (; it1 != rtp_data_channels_.end(); ++it1) { + // Use a temporary copy of the RTP/SCTP DataChannel list because the + // DataChannel may callback to us and try to modify the list. + RtpDataChannels temp_rtp_dcs; + temp_rtp_dcs.swap(rtp_data_channels_); + RtpDataChannels::iterator it1 = temp_rtp_dcs.begin(); + for (; it1 != temp_rtp_dcs.end(); ++it1) { it1->second->OnDataEngineClose(); } - SctpDataChannels::iterator it2 = sctp_data_channels_.begin(); - for (; it2 != sctp_data_channels_.end(); ++it2) { - (*it2)->OnDataEngineClose(); - } -} -bool MediaStreamSignaling::GetRemoteAudioTrackSsrc( - const std::string& track_id, uint32* ssrc) const { - TrackInfos::const_iterator it = remote_audio_tracks_.find(track_id); - if (it == remote_audio_tracks_.end()) { - return false; - } - - *ssrc = it->second.ssrc; - return true; -} - -bool MediaStreamSignaling::GetRemoteVideoTrackSsrc( - const std::string& track_id, uint32* ssrc) const { - TrackInfos::const_iterator it = remote_video_tracks_.find(track_id); - if (it == remote_video_tracks_.end()) { - return false; + SctpDataChannels temp_sctp_dcs; + temp_sctp_dcs.swap(sctp_data_channels_); + SctpDataChannels::iterator it2 = temp_sctp_dcs.begin(); + for (; it2 != temp_sctp_dcs.end(); ++it2) { + (*it2)->OnDataEngineClose(); } - - *ssrc = it->second.ssrc; - return true; } void MediaStreamSignaling::UpdateSessionOptions() { @@ -554,12 +573,12 @@ void MediaStreamSignaling::UpdateRemoteStreamsList( // new StreamParam. TrackInfos::iterator track_it = current_tracks->begin(); while (track_it != current_tracks->end()) { - TrackInfo info = track_it->second; + const TrackInfo& info = *track_it; cricket::StreamParams params; if (!cricket::GetStreamBySsrc(streams, info.ssrc, ¶ms) || params.id != info.track_id) { OnRemoteTrackRemoved(info.stream_label, info.track_id, media_type); - current_tracks->erase(track_it++); + track_it = current_tracks->erase(track_it); } else { ++track_it; } @@ -583,10 +602,10 @@ void MediaStreamSignaling::UpdateRemoteStreamsList( new_streams->AddStream(stream); } - TrackInfos::iterator track_it = current_tracks->find(track_id); - if (track_it == current_tracks->end()) { - (*current_tracks)[track_id] = - TrackInfo(stream_label, track_id, ssrc); + const TrackInfo* track_info = FindTrackInfo(*current_tracks, stream_label, + track_id); + if (!track_info) { + current_tracks->push_back(TrackInfo(stream_label, track_id, ssrc)); OnRemoteTrackSeen(stream_label, track_id, it->first_ssrc(), media_type); } } @@ -642,7 +661,7 @@ void MediaStreamSignaling::RejectRemoteTracks(cricket::MediaType media_type) { TrackInfos* current_tracks = GetRemoteTracks(media_type); for (TrackInfos::iterator track_it = current_tracks->begin(); track_it != current_tracks->end(); ++track_it) { - TrackInfo info = track_it->second; + const TrackInfo& info = *track_it; MediaStreamInterface* stream = remote_streams_->find(info.stream_label); if (media_type == cricket::MEDIA_TYPE_AUDIO) { AudioTrackInterface* track = stream->FindAudioTrack(info.track_id); @@ -695,15 +714,16 @@ void MediaStreamSignaling::MaybeCreateDefaultStream() { } if (remote_info_.default_audio_track_needed && default_remote_stream->GetAudioTracks().size() == 0) { - remote_audio_tracks_[kDefaultAudioTrackLabel] = - TrackInfo(kDefaultStreamLabel, kDefaultAudioTrackLabel, 0); + remote_audio_tracks_.push_back(TrackInfo(kDefaultStreamLabel, + kDefaultAudioTrackLabel, 0)); + OnRemoteTrackSeen(kDefaultStreamLabel, kDefaultAudioTrackLabel, 0, cricket::MEDIA_TYPE_AUDIO); } if (remote_info_.default_video_track_needed && default_remote_stream->GetVideoTracks().size() == 0) { - remote_video_tracks_[kDefaultVideoTrackLabel] = - TrackInfo(kDefaultStreamLabel, kDefaultVideoTrackLabel, 0); + remote_video_tracks_.push_back(TrackInfo(kDefaultStreamLabel, + kDefaultVideoTrackLabel, 0)); OnRemoteTrackSeen(kDefaultStreamLabel, kDefaultVideoTrackLabel, 0, cricket::MEDIA_TYPE_VIDEO); } @@ -736,16 +756,17 @@ void MediaStreamSignaling::UpdateLocalTracks( cricket::MediaType media_type) { TrackInfos* current_tracks = GetLocalTracks(media_type); - // Find removed tracks. Ie tracks where the track id or ssrc don't match the - // new StreamParam. + // Find removed tracks. Ie tracks where the track id, stream label or ssrc + // don't match the new StreamParam. TrackInfos::iterator track_it = current_tracks->begin(); while (track_it != current_tracks->end()) { - TrackInfo info = track_it->second; + const TrackInfo& info = *track_it; cricket::StreamParams params; if (!cricket::GetStreamBySsrc(streams, info.ssrc, ¶ms) || - params.id != info.track_id) { - OnLocalTrackRemoved(info.stream_label, info.track_id, media_type); - current_tracks->erase(track_it++); + params.id != info.track_id || params.sync_label != info.stream_label) { + OnLocalTrackRemoved(info.stream_label, info.track_id, info.ssrc, + media_type); + track_it = current_tracks->erase(track_it); } else { ++track_it; } @@ -759,10 +780,11 @@ void MediaStreamSignaling::UpdateLocalTracks( const std::string& stream_label = it->sync_label; const std::string& track_id = it->id; uint32 ssrc = it->first_ssrc(); - TrackInfos::iterator track_it = current_tracks->find(track_id); - if (track_it == current_tracks->end()) { - (*current_tracks)[track_id] = - TrackInfo(stream_label, track_id, ssrc); + const TrackInfo* track_info = FindTrackInfo(*current_tracks, + stream_label, + track_id); + if (!track_info) { + current_tracks->push_back(TrackInfo(stream_label, track_id, ssrc)); OnLocalTrackSeen(stream_label, track_id, it->first_ssrc(), media_type); } @@ -805,6 +827,7 @@ void MediaStreamSignaling::OnLocalTrackSeen( void MediaStreamSignaling::OnLocalTrackRemoved( const std::string& stream_label, const std::string& track_id, + uint32 ssrc, cricket::MediaType media_type) { MediaStreamInterface* stream = local_streams_->find(stream_label); if (!stream) { @@ -821,7 +844,7 @@ void MediaStreamSignaling::OnLocalTrackRemoved( if (!audio_track) { return; } - stream_observer_->OnRemoveLocalAudioTrack(stream, audio_track); + stream_observer_->OnRemoveLocalAudioTrack(stream, audio_track, ssrc); } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { VideoTrackInterface* video_track = stream->FindVideoTrack(track_id); if (!video_track) { @@ -948,4 +971,38 @@ void MediaStreamSignaling::OnDtlsRoleReadyForSctp(talk_base::SSLRole role) { } } + +void MediaStreamSignaling::OnRemoteSctpDataChannelClosed(uint32 sid) { + int index = FindDataChannelBySid(sid); + if (index < 0) { + LOG(LS_WARNING) << "Unexpected sid " << sid + << " of the remotely closed DataChannel."; + return; + } + sctp_data_channels_[index]->Close(); +} + +const MediaStreamSignaling::TrackInfo* +MediaStreamSignaling::FindTrackInfo( + const MediaStreamSignaling::TrackInfos& infos, + const std::string& stream_label, + const std::string track_id) const { + + for (TrackInfos::const_iterator it = infos.begin(); + it != infos.end(); ++it) { + if (it->stream_label == stream_label && it->track_id == track_id) + return &*it; + } + return NULL; +} + +int MediaStreamSignaling::FindDataChannelBySid(int sid) const { + for (size_t i = 0; i < sctp_data_channels_.size(); ++i) { + if (sctp_data_channels_[i]->id() == sid) { + return static_cast<int>(i); + } + } + return -1; +} + } // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.h b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.h index c600f066285..73781661603 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.h @@ -37,6 +37,7 @@ #include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/app/webrtc/streamcollection.h" #include "talk/base/scoped_ref_ptr.h" +#include "talk/base/sigslot.h" #include "talk/session/media/mediasession.h" namespace talk_base { @@ -92,7 +93,8 @@ class MediaStreamSignalingObserver { // Triggered when the local SessionDescription has removed an audio track. virtual void OnRemoveLocalAudioTrack(MediaStreamInterface* stream, - AudioTrackInterface* audio_track) = 0; + AudioTrackInterface* audio_track, + uint32 ssrc) = 0; // Triggered when the local SessionDescription has removed a video track. virtual void OnRemoveLocalVideoTrack(MediaStreamInterface* stream, @@ -156,7 +158,7 @@ class MediaStreamSignalingObserver { // DataChannel label or SSRC. The DataChannel SSRC is updated with SSRC=0. // The DataChannel change state to kClosed. -class MediaStreamSignaling { +class MediaStreamSignaling : public sigslot::has_slots<> { public: MediaStreamSignaling(talk_base::Thread* signaling_thread, MediaStreamSignalingObserver* stream_observer, @@ -194,8 +196,9 @@ class MediaStreamSignaling { // be offered in a SessionDescription. bool AddDataChannel(DataChannel* data_channel); // After we receive an OPEN message, create a data channel and add it. - bool AddDataChannelFromOpenMessage( - const std::string& label, const DataChannelInit& config); + bool AddDataChannelFromOpenMessage(const cricket::ReceiveDataParams& params, + const talk_base::Buffer& payload); + void RemoveSctpDataChannel(int sid); // Returns a MediaSessionOptions struct with options decided by |constraints|, // the local MediaStreams and DataChannels. @@ -238,10 +241,6 @@ class MediaStreamSignaling { // Called when the data channel closes. void OnDataChannelClose(); - // Returns the SSRC for a given track. - bool GetRemoteAudioTrackSsrc(const std::string& track_id, uint32* ssrc) const; - bool GetRemoteVideoTrackSsrc(const std::string& track_id, uint32* ssrc) const; - // Returns all current known local MediaStreams. StreamCollectionInterface* local_streams() const { return local_streams_;} @@ -251,6 +250,7 @@ class MediaStreamSignaling { } void OnDataTransportCreatedForSctp(); void OnDtlsRoleReadyForSctp(talk_base::SSLRole role); + void OnRemoteSctpDataChannelClosed(uint32 sid); private: struct RemotePeerInfo { @@ -287,7 +287,7 @@ class MediaStreamSignaling { std::string track_id; uint32 ssrc; }; - typedef std::map<std::string, TrackInfo> TrackInfos; + typedef std::vector<TrackInfo> TrackInfos; void UpdateSessionOptions(); @@ -358,6 +358,7 @@ class MediaStreamSignaling { // MediaStreamTrack in a MediaStream in |local_streams_|. void OnLocalTrackRemoved(const std::string& stream_label, const std::string& track_id, + uint32 ssrc, cricket::MediaType media_type); void UpdateLocalRtpDataChannels(const cricket::StreamParamsVec& streams); @@ -366,6 +367,14 @@ class MediaStreamSignaling { const std::vector<std::string>& active_channels, bool is_local_update); void CreateRemoteDataChannel(const std::string& label, uint32 remote_ssrc); + const TrackInfo* FindTrackInfo(const TrackInfos& infos, + const std::string& stream_label, + const std::string track_id) const; + + // Returns the index of the specified SCTP DataChannel in sctp_data_channels_, + // or -1 if not found. + int FindDataChannelBySid(int sid) const; + RemotePeerInfo remote_info_; talk_base::Thread* signaling_thread_; DataChannelFactory* data_channel_factory_; @@ -386,6 +395,7 @@ class MediaStreamSignaling { typedef std::map<std::string, talk_base::scoped_refptr<DataChannel> > RtpDataChannels; typedef std::vector<talk_base::scoped_refptr<DataChannel> > SctpDataChannels; + RtpDataChannels rtp_data_channels_; SctpDataChannels sctp_data_channels_; }; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling_unittest.cc index 5b88aa0c53f..150058eea06 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling_unittest.cc @@ -26,10 +26,12 @@ */ #include <string> +#include <vector> #include "talk/app/webrtc/audiotrack.h" #include "talk/app/webrtc/mediastream.h" #include "talk/app/webrtc/mediastreamsignaling.h" +#include "talk/app/webrtc/sctputils.h" #include "talk/app/webrtc/streamcollection.h" #include "talk/app/webrtc/test/fakeconstraints.h" #include "talk/app/webrtc/test/fakedatachannelprovider.h" @@ -246,13 +248,19 @@ class FakeDataChannelFactory : public webrtc::DataChannelFactory { virtual talk_base::scoped_refptr<webrtc::DataChannel> CreateDataChannel( const std::string& label, - const webrtc::DataChannelInit* config) { - return webrtc::DataChannel::Create(provider_, type_, label, config); + const webrtc::InternalDataChannelInit* config) { + last_init_ = *config; + return webrtc::DataChannel::Create(provider_, type_, label, *config); + } + + const webrtc::InternalDataChannelInit& last_init() const { + return last_init_; } private: FakeDataChannelProvider* provider_; cricket::DataChannelType type_; + webrtc::InternalDataChannelInit last_init_; }; class MockSignalingObserver : public webrtc::MediaStreamSignalingObserver { @@ -290,7 +298,8 @@ class MockSignalingObserver : public webrtc::MediaStreamSignalingObserver { } virtual void OnRemoveLocalAudioTrack(MediaStreamInterface* stream, - AudioTrackInterface* audio_track) { + AudioTrackInterface* audio_track, + uint32 ssrc) { RemoveTrack(&local_audio_tracks_, stream, audio_track); } @@ -376,31 +385,48 @@ class MockSignalingObserver : public webrtc::MediaStreamSignalingObserver { std::string track_id; uint32 ssrc; }; - typedef std::map<std::string, TrackInfo> TrackInfos; + typedef std::vector<TrackInfo> TrackInfos; void AddTrack(TrackInfos* track_infos, MediaStreamInterface* stream, MediaStreamTrackInterface* track, uint32 ssrc) { - (*track_infos)[track->id()] = TrackInfo(stream->label(), track->id(), - ssrc); + (*track_infos).push_back(TrackInfo(stream->label(), track->id(), + ssrc)); } void RemoveTrack(TrackInfos* track_infos, MediaStreamInterface* stream, MediaStreamTrackInterface* track) { - TrackInfos::iterator it = track_infos->find(track->id()); - ASSERT_TRUE(it != track_infos->end()); - ASSERT_EQ(it->second.stream_label, stream->label()); - track_infos->erase(it); + for (TrackInfos::iterator it = track_infos->begin(); + it != track_infos->end(); ++it) { + if (it->stream_label == stream->label() && it->track_id == track->id()) { + track_infos->erase(it); + return; + } + } + ADD_FAILURE(); } + const TrackInfo* FindTrackInfo(const TrackInfos& infos, + const std::string& stream_label, + const std::string track_id) const { + for (TrackInfos::const_iterator it = infos.begin(); + it != infos.end(); ++it) { + if (it->stream_label == stream_label && it->track_id == track_id) + return &*it; + } + return NULL; + } + + void VerifyTrack(const TrackInfos& track_infos, const std::string& stream_label, const std::string& track_id, uint32 ssrc) { - TrackInfos::const_iterator it = track_infos.find(track_id); - ASSERT_TRUE(it != track_infos.end()); - EXPECT_EQ(stream_label, it->second.stream_label); - EXPECT_EQ(ssrc, it->second.ssrc); + const TrackInfo* track_info = FindTrackInfo(track_infos, + stream_label, + track_id); + ASSERT_TRUE(track_info != NULL); + EXPECT_EQ(ssrc, track_info->ssrc); } TrackInfos remote_audio_tracks_; @@ -528,11 +554,11 @@ class MediaStreamSignalingTest: public testing::Test { talk_base::scoped_refptr<webrtc::DataChannel> AddDataChannel( cricket::DataChannelType type, const std::string& label, int id) { - webrtc::DataChannelInit config; + webrtc::InternalDataChannelInit config; config.id = id; talk_base::scoped_refptr<webrtc::DataChannel> data_channel( webrtc::DataChannel::Create( - data_channel_provider_.get(), type, label, &config)); + data_channel_provider_.get(), type, label, config)); EXPECT_TRUE(data_channel.get() != NULL); EXPECT_TRUE(signaling_->AddDataChannel(data_channel.get())); return data_channel; @@ -1044,6 +1070,47 @@ TEST_F(MediaStreamSignalingTest, ChangeSsrcOnTrackInLocalSessionDescription) { observer_->VerifyLocalVideoTrack(kStreams[0], kVideoTracks[0], 98); } +// This test that the correct MediaStreamSignalingObserver methods are called +// if a new session description is set with the same tracks but they are now +// sent on a another MediaStream. +TEST_F(MediaStreamSignalingTest, SignalSameTracksInSeparateMediaStream) { + talk_base::scoped_ptr<SessionDescriptionInterface> desc; + CreateSessionDescriptionAndReference(1, 1, desc.use()); + + signaling_->AddLocalStream(reference_collection_->at(0)); + signaling_->OnLocalDescriptionChanged(desc.get()); + EXPECT_EQ(1u, observer_->NumberOfLocalAudioTracks()); + EXPECT_EQ(1u, observer_->NumberOfLocalVideoTracks()); + + std::string stream_label_0 = kStreams[0]; + observer_->VerifyLocalAudioTrack(stream_label_0, kAudioTracks[0], 1); + observer_->VerifyLocalVideoTrack(stream_label_0, kVideoTracks[0], 2); + + // Add a new MediaStream but with the same tracks as in the first stream. + std::string stream_label_1 = kStreams[1]; + talk_base::scoped_refptr<webrtc::MediaStreamInterface> stream_1( + webrtc::MediaStream::Create(kStreams[1])); + stream_1->AddTrack(reference_collection_->at(0)->GetVideoTracks()[0]); + stream_1->AddTrack(reference_collection_->at(0)->GetAudioTracks()[0]); + signaling_->AddLocalStream(stream_1); + + // Replace msid in the original SDP. + std::string sdp; + desc->ToString(&sdp); + talk_base::replace_substrs( + kStreams[0], strlen(kStreams[0]), kStreams[1], strlen(kStreams[1]), &sdp); + + talk_base::scoped_ptr<SessionDescriptionInterface> updated_desc( + webrtc::CreateSessionDescription(SessionDescriptionInterface::kOffer, + sdp, NULL)); + + signaling_->OnLocalDescriptionChanged(updated_desc.get()); + observer_->VerifyLocalAudioTrack(kStreams[1], kAudioTracks[0], 1); + observer_->VerifyLocalVideoTrack(kStreams[1], kVideoTracks[0], 2); + EXPECT_EQ(1u, observer_->NumberOfLocalAudioTracks()); + EXPECT_EQ(1u, observer_->NumberOfLocalVideoTracks()); +} + // Verifies that an even SCTP id is allocated for SSL_CLIENT and an odd id for // SSL_SERVER. TEST_F(MediaStreamSignalingTest, SctpIdAllocationBasedOnRole) { @@ -1074,14 +1141,55 @@ TEST_F(MediaStreamSignalingTest, SctpIdAllocationNoReuse) { EXPECT_NE(old_id, new_id); } +// Verifies that SCTP ids of removed DataChannels can be reused. +TEST_F(MediaStreamSignalingTest, SctpIdReusedForRemovedDataChannel) { + int odd_id = 1; + int even_id = 0; + AddDataChannel(cricket::DCT_SCTP, "a", odd_id); + AddDataChannel(cricket::DCT_SCTP, "a", even_id); + + int allocated_id = -1; + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_SERVER, + &allocated_id)); + EXPECT_EQ(odd_id + 2, allocated_id); + AddDataChannel(cricket::DCT_SCTP, "a", allocated_id); + + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_CLIENT, + &allocated_id)); + EXPECT_EQ(even_id + 2, allocated_id); + AddDataChannel(cricket::DCT_SCTP, "a", allocated_id); + + signaling_->RemoveSctpDataChannel(odd_id); + signaling_->RemoveSctpDataChannel(even_id); + + // Verifies that removed DataChannel ids are reused. + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_SERVER, + &allocated_id)); + EXPECT_EQ(odd_id, allocated_id); + + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_CLIENT, + &allocated_id)); + EXPECT_EQ(even_id, allocated_id); + + // Verifies that used higher DataChannel ids are not reused. + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_SERVER, + &allocated_id)); + EXPECT_NE(odd_id + 2, allocated_id); + + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_CLIENT, + &allocated_id)); + EXPECT_NE(even_id + 2, allocated_id); + +} + // Verifies that duplicated label is not allowed for RTP data channel. TEST_F(MediaStreamSignalingTest, RtpDuplicatedLabelNotAllowed) { AddDataChannel(cricket::DCT_RTP, "a", -1); - webrtc::DataChannelInit config; + webrtc::InternalDataChannelInit config; talk_base::scoped_refptr<webrtc::DataChannel> data_channel = webrtc::DataChannel::Create( - data_channel_provider_.get(), cricket::DCT_RTP, "a", &config); + data_channel_provider_.get(), cricket::DCT_RTP, "a", config); ASSERT_TRUE(data_channel.get() != NULL); EXPECT_FALSE(signaling_->AddDataChannel(data_channel.get())); } @@ -1092,6 +1200,25 @@ TEST_F(MediaStreamSignalingTest, SctpDuplicatedLabelAllowed) { AddDataChannel(cricket::DCT_SCTP, "a", -1); } +// Verifies the correct configuration is used to create DataChannel from an OPEN +// message. +TEST_F(MediaStreamSignalingTest, CreateDataChannelFromOpenMessage) { + FakeDataChannelFactory fake_factory(data_channel_provider_.get(), + cricket::DCT_SCTP); + signaling_->SetDataChannelFactory(&fake_factory); + webrtc::DataChannelInit config; + config.id = 1; + talk_base::Buffer payload; + webrtc::WriteDataChannelOpenMessage("a", config, &payload); + cricket::ReceiveDataParams params; + params.ssrc = config.id; + EXPECT_TRUE(signaling_->AddDataChannelFromOpenMessage(params, payload)); + EXPECT_EQ(config.id, fake_factory.last_init().id); + EXPECT_FALSE(fake_factory.last_init().negotiated); + EXPECT_EQ(webrtc::InternalDataChannelInit::kAcker, + fake_factory.last_init().open_handshake_role); +} + // Verifies that duplicated label from OPEN message is allowed. TEST_F(MediaStreamSignalingTest, DuplicatedLabelFromOpenMessageAllowed) { AddDataChannel(cricket::DCT_SCTP, "a", -1); @@ -1101,5 +1228,28 @@ TEST_F(MediaStreamSignalingTest, DuplicatedLabelFromOpenMessageAllowed) { signaling_->SetDataChannelFactory(&fake_factory); webrtc::DataChannelInit config; config.id = 0; - EXPECT_TRUE(signaling_->AddDataChannelFromOpenMessage("a", config)); + talk_base::Buffer payload; + webrtc::WriteDataChannelOpenMessage("a", config, &payload); + cricket::ReceiveDataParams params; + params.ssrc = config.id; + EXPECT_TRUE(signaling_->AddDataChannelFromOpenMessage(params, payload)); +} + +// Verifies that a DataChannel closed remotely is closed locally. +TEST_F(MediaStreamSignalingTest, + SctpDataChannelClosedLocallyWhenClosedRemotely) { + webrtc::InternalDataChannelInit config; + config.id = 0; + + talk_base::scoped_refptr<webrtc::DataChannel> data_channel = + webrtc::DataChannel::Create( + data_channel_provider_.get(), cricket::DCT_SCTP, "a", config); + ASSERT_TRUE(data_channel.get() != NULL); + EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, + data_channel->state()); + + EXPECT_TRUE(signaling_->AddDataChannel(data_channel.get())); + + signaling_->OnRemoteSctpDataChannelClosed(config.id); + EXPECT_EQ(webrtc::DataChannelInterface::kClosed, data_channel->state()); } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamtrackproxy.h b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamtrackproxy.h index 7c622e73332..19750b08acc 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamtrackproxy.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamtrackproxy.h @@ -42,6 +42,11 @@ BEGIN_PROXY_MAP(AudioTrack) PROXY_CONSTMETHOD0(TrackState, state) PROXY_CONSTMETHOD0(bool, enabled) PROXY_CONSTMETHOD0(AudioSourceInterface*, GetSource) + PROXY_METHOD1(void, AddSink, AudioTrackSinkInterface*) + PROXY_METHOD1(void, RemoveSink, AudioTrackSinkInterface*) + PROXY_METHOD1(bool, GetSignalLevel, int*) + PROXY_METHOD0(talk_base::scoped_refptr<AudioProcessorInterface>, + GetAudioProcessor) PROXY_METHOD0(cricket::AudioRenderer*, GetRenderer) PROXY_METHOD1(bool, set_enabled, bool) diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/README b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/README index 761338fc9c4..692fbbc5643 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/README +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/README @@ -7,8 +7,6 @@ Prerequisites: .gclient file should contain a line like: target_os = ['ios', 'mac'] Make sure to re-run gclient sync after adding this to download the tools. - Note that until http://crbug.com/248168 is fixed one needs to do a gclient - sync with just 'mac' and then follow that with a sync with both. - Set up webrtc-related $GYP_DEFINES; example shell functions that set up for building for iOS-device, iOS-simulator, and Mac (resp) are: @@ -47,6 +45,10 @@ Example of building & using the unittest & app: ninja -C out_mac/Debug libjingle_peerconnection_objc_test && \ ./out_mac/Debug/libjingle_peerconnection_objc_test.app/Contents/MacOS/libjingle_peerconnection_objc_test +- To build & launch the sample app on OSX: + wrmac && gclient runhooks && ninja -C out_mac/Debug AppRTCDemo && \ + ./out_mac/Debug/AppRTCDemo.app/Contents/MacOS/AppRTCDemo + - To build & launch the sample app on the iOS simulator: wrsim && gclient runhooks && ninja -C out_sim/Debug iossim AppRTCDemo && \ ./out_sim/Debug/iossim out_sim/Debug/AppRTCDemo.app @@ -68,14 +70,11 @@ Example of building & using the unittest & app: the Info.plist file to ensure that the Bundle Identifier matches your phone provisioning profile, or use a development wildcard provisioning profile.) +- Alternately, use ios-deploy: + ios-deploy -d -b out_ios/Debug-iphoneos/AppRTCDemo.app - Once installed: - Tap AppRTCDemo on the iOS device's home screen (might have to scroll to find it). - In desktop chrome, navigate to http://apprtc.appspot.com and note the r=<NNN> room number in the resulting URL; enter that number into the text field on the phone. - - Alternatively, background the app and launch Safari. In Safari, - open the url apprtc://apprtc.appspot.com/?r=<NNN> where <NNN> is - the room name. Other options are to put the link in an email/chat - and send it to yourself. Clicking on it will launch AppRTCDemo - and navigate to the room. diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCAudioTrack.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCAudioTrack.mm index 8a56986536b..2364c2942c4 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCAudioTrack.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCAudioTrack.mm @@ -29,9 +29,9 @@ #error "This file requires ARC support." #endif -#import "RTCAudioTrack+internal.h" +#import "RTCAudioTrack+Internal.h" -#import "RTCMediaStreamTrack+internal.h" +#import "RTCMediaStreamTrack+Internal.h" @implementation RTCAudioTrack @end @@ -39,7 +39,7 @@ @implementation RTCAudioTrack (Internal) - (talk_base::scoped_refptr<webrtc::AudioTrackInterface>)audioTrack { - return static_cast<webrtc::AudioTrackInterface *>(self.mediaTrack.get()); + return static_cast<webrtc::AudioTrackInterface*>(self.mediaTrack.get()); } @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCDataChannel+Internal.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCDataChannel+Internal.h new file mode 100644 index 00000000000..a55089193b0 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCDataChannel+Internal.h @@ -0,0 +1,55 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "RTCDataChannel.h" + +#include "talk/app/webrtc/datachannelinterface.h" +#include "talk/base/scoped_ref_ptr.h" + +@interface RTCDataBuffer (Internal) + +@property(nonatomic, readonly) const webrtc::DataBuffer* dataBuffer; + +- (instancetype)initWithDataBuffer:(const webrtc::DataBuffer&)buffer; + +@end + +@interface RTCDataChannelInit (Internal) + +@property(nonatomic, readonly) const webrtc::DataChannelInit* dataChannelInit; + +@end + +@interface RTCDataChannel (Internal) + +@property(nonatomic, readonly) + talk_base::scoped_refptr<webrtc::DataChannelInterface> dataChannel; + +- (instancetype)initWithDataChannel: + (talk_base::scoped_refptr<webrtc::DataChannelInterface>)dataChannel; + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCDataChannel.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCDataChannel.mm new file mode 100644 index 00000000000..08379403758 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCDataChannel.mm @@ -0,0 +1,273 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "RTCDataChannel+Internal.h" + +#include "talk/app/webrtc/datachannelinterface.h" + +namespace webrtc { + +class RTCDataChannelObserver : public DataChannelObserver { + public: + RTCDataChannelObserver(RTCDataChannel* channel) { _channel = channel; } + + virtual void OnStateChange() OVERRIDE { + [_channel.delegate channelDidChangeState:_channel]; + } + + virtual void OnMessage(const DataBuffer& buffer) OVERRIDE { + if (!_channel.delegate) { + return; + } + RTCDataBuffer* dataBuffer = + [[RTCDataBuffer alloc] initWithDataBuffer:buffer]; + [_channel.delegate channel:_channel didReceiveMessageWithBuffer:dataBuffer]; + } + + private: + __weak RTCDataChannel* _channel; +}; +} + +// TODO(tkchin): move to shared location +NSString* NSStringFromStdString(const std::string& stdString) { + // std::string may contain null termination character so we construct + // using length. + return [[NSString alloc] initWithBytes:stdString.data() + length:stdString.length() + encoding:NSUTF8StringEncoding]; +} + +std::string StdStringFromNSString(NSString* nsString) { + NSData* charData = [nsString dataUsingEncoding:NSUTF8StringEncoding]; + return std::string(reinterpret_cast<const char*>([charData bytes]), + [charData length]); +} + +@implementation RTCDataChannelInit { + webrtc::DataChannelInit _dataChannelInit; +} + +- (BOOL)isOrdered { + return _dataChannelInit.ordered; +} + +- (void)setIsOrdered:(BOOL)isOrdered { + _dataChannelInit.ordered = isOrdered; +} + +- (NSInteger)maxRetransmitTime { + return _dataChannelInit.maxRetransmitTime; +} + +- (void)setMaxRetransmitTime:(NSInteger)maxRetransmitTime { + _dataChannelInit.maxRetransmitTime = maxRetransmitTime; +} + +- (NSInteger)maxRetransmits { + return _dataChannelInit.maxRetransmits; +} + +- (void)setMaxRetransmits:(NSInteger)maxRetransmits { + _dataChannelInit.maxRetransmits = maxRetransmits; +} + +- (NSString*)protocol { + return NSStringFromStdString(_dataChannelInit.protocol); +} + +- (void)setProtocol:(NSString*)protocol { + _dataChannelInit.protocol = StdStringFromNSString(protocol); +} + +- (BOOL)isNegotiated { + return _dataChannelInit.negotiated; +} + +- (void)setIsNegotiated:(BOOL)isNegotiated { + _dataChannelInit.negotiated = isNegotiated; +} + +- (NSInteger)streamId { + return _dataChannelInit.id; +} + +- (void)setStreamId:(NSInteger)streamId { + _dataChannelInit.id = streamId; +} + +@end + +@implementation RTCDataChannelInit (Internal) + +- (const webrtc::DataChannelInit*)dataChannelInit { + return &_dataChannelInit; +} + +@end + +@implementation RTCDataBuffer { + talk_base::scoped_ptr<webrtc::DataBuffer> _dataBuffer; +} + +- (instancetype)initWithData:(NSData*)data isBinary:(BOOL)isBinary { + NSAssert(data, @"data cannot be nil"); + if (self = [super init]) { + talk_base::Buffer buffer([data bytes], [data length]); + _dataBuffer.reset(new webrtc::DataBuffer(buffer, isBinary)); + } + return self; +} + +- (NSData*)data { + return [NSData dataWithBytes:_dataBuffer->data.data() + length:_dataBuffer->data.length()]; +} + +- (BOOL)isBinary { + return _dataBuffer->binary; +} + +@end + +@implementation RTCDataBuffer (Internal) + +- (instancetype)initWithDataBuffer:(const webrtc::DataBuffer&)buffer { + if (self = [super init]) { + _dataBuffer.reset(new webrtc::DataBuffer(buffer)); + } + return self; +} + +- (const webrtc::DataBuffer*)dataBuffer { + return _dataBuffer.get(); +} + +@end + +@implementation RTCDataChannel { + talk_base::scoped_refptr<webrtc::DataChannelInterface> _dataChannel; + talk_base::scoped_ptr<webrtc::RTCDataChannelObserver> _observer; + BOOL _isObserverRegistered; +} + +- (NSString*)label { + return NSStringFromStdString(_dataChannel->label()); +} + +- (BOOL)isReliable { + return _dataChannel->reliable(); +} + +- (BOOL)isOrdered { + return _dataChannel->ordered(); +} + +- (NSUInteger)maxRetransmitTimeMs { + return _dataChannel->maxRetransmitTime(); +} + +- (NSUInteger)maxRetransmits { + return _dataChannel->maxRetransmits(); +} + +- (NSString*)protocol { + return NSStringFromStdString(_dataChannel->protocol()); +} + +- (BOOL)isNegotiated { + return _dataChannel->negotiated(); +} + +- (NSInteger)streamId { + return _dataChannel->id(); +} + +- (RTCDataChannelState)state { + switch (_dataChannel->state()) { + case webrtc::DataChannelInterface::DataState::kConnecting: + return kRTCDataChannelStateConnecting; + case webrtc::DataChannelInterface::DataState::kOpen: + return kRTCDataChannelStateOpen; + case webrtc::DataChannelInterface::DataState::kClosing: + return kRTCDataChannelStateClosing; + case webrtc::DataChannelInterface::DataState::kClosed: + return kRTCDataChannelStateClosed; + } +} + +- (NSUInteger)bufferedAmount { + return _dataChannel->buffered_amount(); +} + +- (void)setDelegate:(id<RTCDataChannelDelegate>)delegate { + if (_delegate == delegate) { + return; + } + if (_isObserverRegistered) { + _dataChannel->UnregisterObserver(); + _isObserverRegistered = NO; + } + _delegate = delegate; + if (_delegate) { + _dataChannel->RegisterObserver(_observer.get()); + _isObserverRegistered = YES; + } +} + +- (void)close { + _dataChannel->Close(); +} + +- (BOOL)sendData:(RTCDataBuffer*)data { + return _dataChannel->Send(*data.dataBuffer); +} + +@end + +@implementation RTCDataChannel (Internal) + +- (instancetype)initWithDataChannel: + (talk_base::scoped_refptr<webrtc::DataChannelInterface>) + dataChannel { + NSAssert(dataChannel != NULL, @"dataChannel cannot be NULL"); + if (self = [super init]) { + _dataChannel = dataChannel; + _observer.reset(new webrtc::RTCDataChannelObserver(self)); + } + return self; +} + +- (talk_base::scoped_refptr<webrtc::DataChannelInterface>)dataChannel { + return _dataChannel; +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEAGLVideoView+Internal.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEAGLVideoView+Internal.h new file mode 100644 index 00000000000..10df2e33594 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEAGLVideoView+Internal.h @@ -0,0 +1,36 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> + +#import "RTCEAGLVideoView.h" +#import "RTCVideoRenderer.h" + +// TODO(tkchin): Move declaration to implementation file. Exposed here in order +// to support deprecated methods in RTCVideoRenderer. +@interface RTCEAGLVideoView (Internal) <RTCVideoRendererDelegate> +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEAGLVideoView.m b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEAGLVideoView.m new file mode 100644 index 00000000000..5365d9821df --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEAGLVideoView.m @@ -0,0 +1,244 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "RTCEAGLVideoView+Internal.h" + +#import <GLKit/GLKit.h> + +#import "RTCOpenGLVideoRenderer.h" +#import "RTCVideoRenderer.h" +#import "RTCVideoTrack.h" + +// RTCDisplayLinkTimer wraps a CADisplayLink and is set to fire every two screen +// refreshes, which should be 30fps. We wrap the display link in order to avoid +// a retain cycle since CADisplayLink takes a strong reference onto its target. +// The timer is paused by default. +@interface RTCDisplayLinkTimer : NSObject + +@property(nonatomic) BOOL isPaused; + +- (instancetype)initWithTimerHandler:(void (^)(void))timerHandler; +- (void)invalidate; + +@end + +@implementation RTCDisplayLinkTimer { + CADisplayLink* _displayLink; + void (^_timerHandler)(void); +} + +- (instancetype)initWithTimerHandler:(void (^)(void))timerHandler { + NSParameterAssert(timerHandler); + if (self = [super init]) { + _timerHandler = timerHandler; + _displayLink = + [CADisplayLink displayLinkWithTarget:self + selector:@selector(displayLinkDidFire:)]; + _displayLink.paused = YES; + // Set to half of screen refresh, which should be 30fps. + [_displayLink setFrameInterval:2]; + [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] + forMode:NSRunLoopCommonModes]; + } + return self; +} + +- (void)dealloc { + [self invalidate]; +} + +- (BOOL)isPaused { + return _displayLink.paused; +} + +- (void)setIsPaused:(BOOL)isPaused { + _displayLink.paused = isPaused; +} + +- (void)invalidate { + [_displayLink invalidate]; +} + +- (void)displayLinkDidFire:(CADisplayLink*)displayLink { + _timerHandler(); +} + +@end + +@interface RTCEAGLVideoView () <GLKViewDelegate> +// |i420Frame| is set when we receive a frame from a worker thread and is read +// from the display link callback so atomicity is required. +@property(atomic, strong) RTCI420Frame* i420Frame; +@property(nonatomic, readonly) GLKView* glkView; +@property(nonatomic, readonly) RTCOpenGLVideoRenderer* glRenderer; +@end + +@implementation RTCEAGLVideoView { + RTCDisplayLinkTimer* _timer; + GLKView* _glkView; + RTCOpenGLVideoRenderer* _glRenderer; + RTCVideoRenderer* _videoRenderer; +} + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + EAGLContext* glContext = + [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + _glRenderer = [[RTCOpenGLVideoRenderer alloc] initWithContext:glContext]; + + // GLKView manages a framebuffer for us. + _glkView = [[GLKView alloc] initWithFrame:CGRectZero + context:glContext]; + _glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; + _glkView.drawableDepthFormat = GLKViewDrawableDepthFormatNone; + _glkView.drawableStencilFormat = GLKViewDrawableStencilFormatNone; + _glkView.drawableMultisample = GLKViewDrawableMultisampleNone; + _glkView.delegate = self; + _glkView.layer.masksToBounds = YES; + [self addSubview:_glkView]; + + // Listen to application state in order to clean up OpenGL before app goes + // away. + NSNotificationCenter* notificationCenter = + [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self + selector:@selector(willResignActive) + name:UIApplicationWillResignActiveNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(didBecomeActive) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + // Frames are received on a separate thread, so we poll for current frame + // using a refresh rate proportional to screen refresh frequency. This + // occurs on the main thread. + __weak RTCEAGLVideoView* weakSelf = self; + _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{ + RTCEAGLVideoView* strongSelf = weakSelf; + // Don't render if frame hasn't changed. + if (strongSelf.glRenderer.lastDrawnFrame == strongSelf.i420Frame) { + return; + } + // This tells the GLKView that it's dirty, which will then call the + // GLKViewDelegate method implemented below. + [strongSelf.glkView setNeedsDisplay]; + }]; + _videoRenderer = [[RTCVideoRenderer alloc] initWithDelegate:self]; + [self setupGL]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + UIApplicationState appState = + [UIApplication sharedApplication].applicationState; + if (appState == UIApplicationStateActive) { + [self teardownGL]; + } + [_timer invalidate]; +} + +- (void)setVideoTrack:(RTCVideoTrack*)videoTrack { + if (_videoTrack == videoTrack) { + return; + } + [_videoTrack removeRenderer:_videoRenderer]; + _videoTrack = videoTrack; + [_videoTrack addRenderer:_videoRenderer]; + // TODO(tkchin): potentially handle changes in track state - e.g. render + // black if track fails. +} + +#pragma mark - UIView + +- (void)layoutSubviews { + [super layoutSubviews]; + _glkView.frame = self.bounds; +} + +#pragma mark - GLKViewDelegate + +// This method is called when the GLKView's content is dirty and needs to be +// redrawn. This occurs on main thread. +- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect { + if (self.i420Frame) { + // The renderer will draw the frame to the framebuffer corresponding to the + // one used by |view|. + [_glRenderer drawFrame:self.i420Frame]; + } +} + +#pragma mark - Private + +- (void)setupGL { + [_glRenderer setupGL]; + _timer.isPaused = NO; +} + +- (void)teardownGL { + _timer.isPaused = YES; + [_glkView deleteDrawable]; + [_glRenderer teardownGL]; +} + +- (void)didBecomeActive { + [self setupGL]; +} + +- (void)willResignActive { + [self teardownGL]; +} + +@end + +@implementation RTCEAGLVideoView (Internal) + +#pragma mark - RTCVideoRendererDelegate + +// These methods are called when the video track has frame information to +// provide. This occurs on non-main thread. +- (void)renderer:(RTCVideoRenderer*)renderer + didSetSize:(CGSize)size { + __weak RTCEAGLVideoView* weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + RTCEAGLVideoView* strongSelf = weakSelf; + [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size]; + }); +} + +- (void)renderer:(RTCVideoRenderer*)renderer + didReceiveFrame:(RTCI420Frame*)frame { + self.i420Frame = frame; +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEnumConverter.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEnumConverter.h index 0e83719d56b..d33709d3006 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEnumConverter.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEnumConverter.h @@ -42,6 +42,9 @@ + (RTCSignalingState)convertSignalingStateToObjC: (webrtc::PeerConnectionInterface::SignalingState)nativeState; ++ (webrtc::PeerConnectionInterface::StatsOutputLevel) + convertStatsOutputLevelToNative:(RTCStatsOutputLevel)statsOutputLevel; + + (RTCSourceState)convertSourceStateToObjC: (webrtc::MediaSourceInterface::SourceState)nativeState; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEnumConverter.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEnumConverter.mm index 7c81c8d854d..9d019419c6e 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEnumConverter.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCEnumConverter.mm @@ -81,6 +81,16 @@ } } ++ (webrtc::PeerConnectionInterface::StatsOutputLevel) + convertStatsOutputLevelToNative:(RTCStatsOutputLevel)statsOutputLevel { + switch (statsOutputLevel) { + case RTCStatsOutputLevelStandard: + return webrtc::PeerConnectionInterface::kStatsOutputLevelStandard; + case RTCStatsOutputLevelDebug: + return webrtc::PeerConnectionInterface::kStatsOutputLevelDebug; + } +} + + (RTCSourceState)convertSourceStateToObjC: (webrtc::MediaSourceInterface::SourceState)nativeState { switch (nativeState) { diff --git a/chromium/third_party/libjingle/source/talk/media/devices/iosdeviceinfo.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCI420Frame+Internal.h index 8b65c14283c..622c0b31efe 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/iosdeviceinfo.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCI420Frame+Internal.h @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2012 Google Inc. + * Copyright 2014, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,16 +25,12 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "talk/media/devices/deviceinfo.h" +#import "RTCI420Frame.h" -namespace cricket { +#include "talk/media/base/videoframe.h" -bool GetUsbId(const Device& device, std::string* usb_id) { - return false; -} +@interface RTCI420Frame (Internal) -bool GetUsbVersion(const Device& device, std::string* usb_version) { - return false; -} +- (instancetype)initWithVideoFrame:(const cricket::VideoFrame*)videoFrame; -} // namespace cricket +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCI420Frame.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCI420Frame.mm index df84fc15e1e..eff3102e262 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCI420Frame.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCI420Frame.mm @@ -27,8 +27,68 @@ #import "RTCI420Frame.h" -@implementation RTCI420Frame +#include "talk/base/scoped_ptr.h" +#include "talk/media/base/videoframe.h" -// TODO(hughv): Should this just be a cricket::VideoFrame wrapper object? +@implementation RTCI420Frame { + talk_base::scoped_ptr<cricket::VideoFrame> _videoFrame; +} + +- (NSUInteger)width { + return _videoFrame->GetWidth(); +} + +- (NSUInteger)height { + return _videoFrame->GetHeight(); +} + +- (NSUInteger)chromaWidth { + return _videoFrame->GetChromaWidth(); +} + +- (NSUInteger)chromaHeight { + return _videoFrame->GetChromaHeight(); +} + +- (NSUInteger)chromaSize { + return _videoFrame->GetChromaSize(); +} + +- (const uint8_t*)yPlane { + return _videoFrame->GetYPlane(); +} + +- (const uint8_t*)uPlane { + return _videoFrame->GetUPlane(); +} + +- (const uint8_t*)vPlane { + return _videoFrame->GetVPlane(); +} + +- (NSInteger)yPitch { + return _videoFrame->GetYPitch(); +} + +- (NSInteger)uPitch { + return _videoFrame->GetUPitch(); +} + +- (NSInteger)vPitch { + return _videoFrame->GetVPitch(); +} + +@end + +@implementation RTCI420Frame (Internal) + +- (instancetype)initWithVideoFrame:(cricket::VideoFrame*)videoFrame { + if (self = [super init]) { + // Keep a shallow copy of the video frame. The underlying frame buffer is + // not copied. + _videoFrame.reset(videoFrame->Copy()); + } + return self; +} @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCICECandidate.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCICECandidate.mm index 07a29ee4565..4b5b0ed4457 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCICECandidate.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCICECandidate.mm @@ -29,7 +29,7 @@ #error "This file requires ARC support." #endif -#import "RTCICECandidate+internal.h" +#import "RTCICECandidate+Internal.h" @implementation RTCICECandidate @@ -37,9 +37,9 @@ @synthesize sdpMLineIndex = _sdpMLineIndex; @synthesize sdp = _sdp; -- (id)initWithMid:(NSString *)sdpMid +- (id)initWithMid:(NSString*)sdpMid index:(NSInteger)sdpMLineIndex - sdp:(NSString *)sdp { + sdp:(NSString*)sdp { if (!sdpMid || !sdp) { NSAssert(NO, @"nil arguments not allowed"); return nil; @@ -52,18 +52,18 @@ return self; } -- (NSString *)description { +- (NSString*)description { return [NSString stringWithFormat:@"%@:%ld:%@", - self.sdpMid, - (long)self.sdpMLineIndex, - self.sdp]; + self.sdpMid, + (long)self.sdpMLineIndex, + self.sdp]; } @end @implementation RTCICECandidate (Internal) -- (id)initWithCandidate:(const webrtc::IceCandidateInterface *)candidate { +- (id)initWithCandidate:(const webrtc::IceCandidateInterface*)candidate { if ((self = [super init])) { std::string sdp; if (candidate->ToString(&sdp)) { diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCICEServer.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCICEServer.mm index f01ed32358e..9f6ecb618f5 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCICEServer.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCICEServer.mm @@ -29,7 +29,7 @@ #error "This file requires ARC support." #endif -#import "RTCICEServer+internal.h" +#import "RTCICEServer+Internal.h" @implementation RTCICEServer @@ -37,9 +37,9 @@ @synthesize username = _username; @synthesize password = _password; -- (id)initWithURI:(NSURL *)URI - username:(NSString *)username - password:(NSString *)password { +- (id)initWithURI:(NSURL*)URI + username:(NSString*)username + password:(NSString*)password { if (!URI || !username || !password) { NSAssert(NO, @"nil arguments not allowed"); self = nil; @@ -53,9 +53,11 @@ return self; } -- (NSString *)description { +- (NSString*)description { return [NSString stringWithFormat:@"RTCICEServer: [%@:%@:%@]", - [self.URI absoluteString], self.username, self.password]; + [self.URI absoluteString], + self.username, + self.password]; } @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaConstraints.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaConstraints.mm index fcb3b52dcfe..a1cc5a564e2 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaConstraints.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaConstraints.mm @@ -29,7 +29,7 @@ #error "This file requires ARC support." #endif -#import "RTCMediaConstraints+internal.h" +#import "RTCMediaConstraints+Internal.h" #import "RTCPair.h" @@ -44,8 +44,8 @@ webrtc::MediaConstraintsInterface::Constraints _optional; } -- (id)initWithMandatoryConstraints:(NSArray *)mandatory - optionalConstraints:(NSArray *)optional { +- (id)initWithMandatoryConstraints:(NSArray*)mandatory + optionalConstraints:(NSArray*)optional { if ((self = [super init])) { _mandatory = [[self class] constraintsFromArray:mandatory]; _optional = [[self class] constraintsFromArray:optional]; @@ -55,10 +55,10 @@ return self; } -+ (webrtc::MediaConstraintsInterface::Constraints) - constraintsFromArray:(NSArray *)array { ++ (webrtc::MediaConstraintsInterface::Constraints)constraintsFromArray: + (NSArray*)array { webrtc::MediaConstraintsInterface::Constraints constraints; - for (RTCPair *pair in array) { + for (RTCPair* pair in array) { constraints.push_back(webrtc::MediaConstraintsInterface::Constraint( [pair.key UTF8String], [pair.value UTF8String])); } @@ -69,7 +69,7 @@ @implementation RTCMediaConstraints (internal) -- (const webrtc::RTCMediaConstraintsNative *)constraints { +- (const webrtc::RTCMediaConstraintsNative*)constraints { return _constraints.get(); } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaSource.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaSource.mm index 9331fd7290c..28af3ad2e3f 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaSource.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaSource.mm @@ -29,7 +29,7 @@ #error "This file requires ARC support." #endif -#import "RTCMediaSource+internal.h" +#import "RTCMediaSource+Internal.h" #import "RTCEnumConverter.h" diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaStream.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaStream.mm index dd4aab690d3..94e14fc57ca 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaStream.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaStream.mm @@ -29,40 +29,40 @@ #error "This file requires ARC support." #endif -#import "RTCMediaStream+internal.h" +#import "RTCMediaStream+Internal.h" -#import "RTCAudioTrack+internal.h" -#import "RTCMediaStreamTrack+internal.h" -#import "RTCVideoTrack+internal.h" +#import "RTCAudioTrack+Internal.h" +#import "RTCMediaStreamTrack+Internal.h" +#import "RTCVideoTrack+Internal.h" #include "talk/app/webrtc/mediastreaminterface.h" @implementation RTCMediaStream { - NSMutableArray *_audioTracks; - NSMutableArray *_videoTracks; + NSMutableArray* _audioTracks; + NSMutableArray* _videoTracks; talk_base::scoped_refptr<webrtc::MediaStreamInterface> _mediaStream; } -- (NSString *)description { +- (NSString*)description { return [NSString stringWithFormat:@"[%@:A=%lu:V=%lu]", - [self label], - (unsigned long)[self.audioTracks count], - (unsigned long)[self.videoTracks count]]; + [self label], + (unsigned long)[self.audioTracks count], + (unsigned long)[self.videoTracks count]]; } -- (NSArray *)audioTracks { +- (NSArray*)audioTracks { return [_audioTracks copy]; } -- (NSArray *)videoTracks { +- (NSArray*)videoTracks { return [_videoTracks copy]; } -- (NSString *)label { +- (NSString*)label { return @(self.mediaStream->label().c_str()); } -- (BOOL)addAudioTrack:(RTCAudioTrack *)track { +- (BOOL)addAudioTrack:(RTCAudioTrack*)track { if (self.mediaStream->AddTrack(track.audioTrack)) { [_audioTracks addObject:track]; return YES; @@ -70,7 +70,7 @@ return NO; } -- (BOOL)addVideoTrack:(RTCVideoTrack *)track { +- (BOOL)addVideoTrack:(RTCVideoTrack*)track { if (self.mediaStream->AddTrack(track.videoTrack)) { [_videoTracks addObject:track]; return YES; @@ -78,7 +78,7 @@ return NO; } -- (BOOL)removeAudioTrack:(RTCAudioTrack *)track { +- (BOOL)removeAudioTrack:(RTCAudioTrack*)track { NSUInteger index = [_audioTracks indexOfObjectIdenticalTo:track]; NSAssert(index != NSNotFound, @"|removeAudioTrack| called on unexpected RTCAudioTrack"); @@ -89,7 +89,7 @@ return NO; } -- (BOOL)removeVideoTrack:(RTCVideoTrack *)track { +- (BOOL)removeVideoTrack:(RTCVideoTrack*)track { NSUInteger index = [_videoTracks indexOfObjectIdenticalTo:track]; NSAssert(index != NSNotFound, @"|removeAudioTrack| called on unexpected RTCVideoTrack"); @@ -105,7 +105,7 @@ @implementation RTCMediaStream (Internal) - (id)initWithMediaStream: - (talk_base::scoped_refptr<webrtc::MediaStreamInterface>)mediaStream { + (talk_base::scoped_refptr<webrtc::MediaStreamInterface>)mediaStream { if (!mediaStream) { NSAssert(NO, @"nil arguments not allowed"); self = nil; @@ -122,18 +122,18 @@ for (size_t i = 0; i < audio_tracks.size(); ++i) { talk_base::scoped_refptr<webrtc::AudioTrackInterface> track = audio_tracks[i]; - RTCAudioTrack *audioTrack = + RTCAudioTrack* audioTrack = [[RTCAudioTrack alloc] initWithMediaTrack:track]; [_audioTracks addObject:audioTrack]; } - // TODO(hughv): Add video. -// for (size_t i = 0; i < video_tracks.size(); ++i) { -// talk_base::scoped_refptr<webrtc::VideoTrackInterface> track = -// video_tracks[i]; -// RTCVideoTrack *videoTrack = -// [[RTCVideoTrack alloc] initWithMediaTrack:track]; -// [_videoTracks addObject:videoTrack]; -// } + + for (size_t i = 0; i < video_tracks.size(); ++i) { + talk_base::scoped_refptr<webrtc::VideoTrackInterface> track = + video_tracks[i]; + RTCVideoTrack* videoTrack = + [[RTCVideoTrack alloc] initWithMediaTrack:track]; + [_videoTracks addObject:videoTrack]; + } } return self; } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaStreamTrack.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaStreamTrack.mm index 6c8f7154292..59313120029 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaStreamTrack.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCMediaStreamTrack.mm @@ -29,23 +29,40 @@ #error "This file requires ARC support." #endif -#import "RTCMediaStreamTrack+internal.h" +#import "RTCMediaStreamTrack+Internal.h" #import "RTCEnumConverter.h" +namespace webrtc { + +class RTCMediaStreamTrackObserver : public ObserverInterface { + public: + RTCMediaStreamTrackObserver(RTCMediaStreamTrack* track) { _track = track; } + + virtual void OnChanged() OVERRIDE { + [_track.delegate mediaStreamTrackDidChange:_track]; + } + + private: + __weak RTCMediaStreamTrack* _track; +}; +} + @implementation RTCMediaStreamTrack { talk_base::scoped_refptr<webrtc::MediaStreamTrackInterface> _mediaTrack; + talk_base::scoped_ptr<webrtc::RTCMediaStreamTrackObserver> _observer; } @synthesize label; - (BOOL)isEqual:(id)other { // Equality is purely based on the label just like the C++ implementation. - if (self == other) return YES; + if (self == other) + return YES; if (![other isKindOfClass:[self class]] || ![self isKindOfClass:[other class]]) { return NO; } - RTCMediaStreamTrack *otherMediaStream = (RTCMediaStreamTrack *)other; + RTCMediaStreamTrack* otherMediaStream = (RTCMediaStreamTrack*)other; return [self.label isEqual:otherMediaStream.label]; } @@ -53,11 +70,11 @@ return [self.label hash]; } -- (NSString *)kind { +- (NSString*)kind { return @(self.mediaTrack->kind().c_str()); } -- (NSString *)label { +- (NSString*)label { return @(self.mediaTrack->id().c_str()); } @@ -82,20 +99,27 @@ @implementation RTCMediaStreamTrack (Internal) -- (id)initWithMediaTrack:( - talk_base::scoped_refptr<webrtc::MediaStreamTrackInterface>)mediaTrack { +- (id)initWithMediaTrack: + (talk_base::scoped_refptr<webrtc::MediaStreamTrackInterface>) + mediaTrack { if (!mediaTrack) { NSAssert(NO, @"nil arguments not allowed"); self = nil; return nil; } - if ((self = [super init])) { + if (self = [super init]) { _mediaTrack = mediaTrack; label = @(mediaTrack->id().c_str()); + _observer.reset(new webrtc::RTCMediaStreamTrackObserver(self)); + _mediaTrack->RegisterObserver(_observer.get()); } return self; } +- (void)dealloc { + _mediaTrack->UnregisterObserver(_observer.get()); +} + - (talk_base::scoped_refptr<webrtc::MediaStreamTrackInterface>)mediaTrack { return _mediaTrack; } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCNSGLVideoView.m b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCNSGLVideoView.m new file mode 100644 index 00000000000..39f3678bfad --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCNSGLVideoView.m @@ -0,0 +1,187 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "RTCNSGLVideoView.h" + +#import <CoreVideo/CVDisplayLink.h> +#import <OpenGL/gl3.h> +#import "RTCOpenGLVideoRenderer.h" +#import "RTCVideoRenderer.h" + +@interface RTCNSGLVideoView () <RTCVideoRendererDelegate> +// |i420Frame| is set when we receive a frame from a worker thread and is read +// from the display link callback so atomicity is required. +@property(atomic, strong) RTCI420Frame* i420Frame; +@property(atomic, strong) RTCOpenGLVideoRenderer* glRenderer; +- (void)drawFrame; +@end + +static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink, + const CVTimeStamp* now, + const CVTimeStamp* outputTime, + CVOptionFlags flagsIn, + CVOptionFlags* flagsOut, + void* displayLinkContext) { + RTCNSGLVideoView* view = (__bridge RTCNSGLVideoView*)displayLinkContext; + [view drawFrame]; + return kCVReturnSuccess; +} + +@implementation RTCNSGLVideoView { + CVDisplayLinkRef _displayLink; + RTCVideoRenderer* _videoRenderer; +} + +- (instancetype)initWithFrame:(NSRect)frame + pixelFormat:(NSOpenGLPixelFormat*)format { + if (self = [super initWithFrame:frame pixelFormat:format]) { + _videoRenderer = [[RTCVideoRenderer alloc] initWithDelegate:self]; + } + return self; +} + +- (void)dealloc { + [self teardownDisplayLink]; +} + +- (void)drawRect:(NSRect)rect { + [self drawFrame]; +} + +- (void)reshape { + [super reshape]; + NSRect frame = [self frame]; + CGLLockContext([[self openGLContext] CGLContextObj]); + glViewport(0, 0, frame.size.width, frame.size.height); + CGLUnlockContext([[self openGLContext] CGLContextObj]); +} + +- (void)lockFocus { + NSOpenGLContext* context = [self openGLContext]; + [super lockFocus]; + if ([context view] != self) { + [context setView:self]; + } + [context makeCurrentContext]; +} + +- (void)prepareOpenGL { + [super prepareOpenGL]; + if (!self.glRenderer) { + self.glRenderer = + [[RTCOpenGLVideoRenderer alloc] initWithContext:[self openGLContext]]; + } + [self.glRenderer setupGL]; + [self setupDisplayLink]; +} + +- (void)clearGLContext { + [self.glRenderer teardownGL]; + self.glRenderer = nil; + [super clearGLContext]; +} + +- (void)setVideoTrack:(RTCVideoTrack*)videoTrack { + if (_videoTrack == videoTrack) { + return; + } + if (_videoTrack) { + [_videoTrack removeRenderer:_videoRenderer]; + CVDisplayLinkStop(_displayLink); + } + _videoTrack = videoTrack; + if (_videoTrack) { + [_videoTrack addRenderer:_videoRenderer]; + CVDisplayLinkStart(_displayLink); + } +} + +#pragma mark - RTCVideoRendererDelegate + +// These methods are called when the video track has frame information to +// provide. This occurs on non-main thread. +- (void)renderer:(RTCVideoRenderer*)renderer + didSetSize:(CGSize)size { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate videoView:self didChangeVideoSize:size]; + }); +} + +- (void)renderer:(RTCVideoRenderer*)renderer + didReceiveFrame:(RTCI420Frame*)frame { + self.i420Frame = frame; +} + +#pragma mark - Private + +- (void)drawFrame { + RTCI420Frame* i420Frame = self.i420Frame; + if (i420Frame && self.glRenderer.lastDrawnFrame != i420Frame) { + // This method may be called from CVDisplayLink callback which isn't on the + // main thread so we have to lock the GL context before drawing. + CGLLockContext([[self openGLContext] CGLContextObj]); + [self.glRenderer drawFrame:i420Frame]; + CGLUnlockContext([[self openGLContext] CGLContextObj]); + } +} + +- (void)setupDisplayLink { + if (_displayLink) { + return; + } + // Synchronize buffer swaps with vertical refresh rate. + GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + + // Create display link. + CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); + CVDisplayLinkSetOutputCallback(_displayLink, + &OnDisplayLinkFired, + (__bridge void*)self); + // Set the display link for the current renderer. + CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; + CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; + CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext( + _displayLink, cglContext, cglPixelFormat); + if (_videoTrack) { + CVDisplayLinkStart(_displayLink); + } +} + +- (void)teardownDisplayLink { + if (!_displayLink) { + return; + } + CVDisplayLinkRelease(_displayLink); + _displayLink = NULL; +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCOpenGLVideoRenderer.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCOpenGLVideoRenderer.mm new file mode 100644 index 00000000000..9ee0216cb85 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCOpenGLVideoRenderer.mm @@ -0,0 +1,457 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "RTCOpenGLVideoRenderer.h" + +#if TARGET_OS_IPHONE +#import <OpenGLES/ES2/gl.h> +#else +#import <OpenGL/gl3.h> +#endif + +#import "RTCI420Frame.h" + +// TODO(tkchin): check and log openGL errors. Methods here return BOOLs in +// anticipation of that happening in the future. + +#if TARGET_OS_IPHONE +#define RTC_PIXEL_FORMAT GL_LUMINANCE +#define SHADER_VERSION +#define VERTEX_SHADER_IN "attribute" +#define VERTEX_SHADER_OUT "varying" +#define FRAGMENT_SHADER_IN "varying" +#define FRAGMENT_SHADER_OUT +#define FRAGMENT_SHADER_COLOR "gl_FragColor" +#define FRAGMENT_SHADER_TEXTURE "texture2D" +#else +#define RTC_PIXEL_FORMAT GL_RED +#define SHADER_VERSION "#version 150\n" +#define VERTEX_SHADER_IN "in" +#define VERTEX_SHADER_OUT "out" +#define FRAGMENT_SHADER_IN "in" +#define FRAGMENT_SHADER_OUT "out vec4 fragColor;\n" +#define FRAGMENT_SHADER_COLOR "fragColor" +#define FRAGMENT_SHADER_TEXTURE "texture" +#endif + +// Vertex shader doesn't do anything except pass coordinates through. +static const char kVertexShaderSource[] = + SHADER_VERSION + VERTEX_SHADER_IN " vec2 position;\n" + VERTEX_SHADER_IN " vec2 texcoord;\n" + VERTEX_SHADER_OUT " vec2 v_texcoord;\n" + "void main() {\n" + " gl_Position = vec4(position.x, position.y, 0.0, 1.0);\n" + " v_texcoord = texcoord;\n" + "}\n"; + +// Fragment shader converts YUV values from input textures into a final RGB +// pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php. +static const char kFragmentShaderSource[] = + SHADER_VERSION + "precision highp float;" + FRAGMENT_SHADER_IN " vec2 v_texcoord;\n" + "uniform lowp sampler2D s_textureY;\n" + "uniform lowp sampler2D s_textureU;\n" + "uniform lowp sampler2D s_textureV;\n" + FRAGMENT_SHADER_OUT + "void main() {\n" + " float y, u, v, r, g, b;\n" + " y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n" + " u = " FRAGMENT_SHADER_TEXTURE "(s_textureU, v_texcoord).r;\n" + " v = " FRAGMENT_SHADER_TEXTURE "(s_textureV, v_texcoord).r;\n" + " u = u - 0.5;\n" + " v = v - 0.5;\n" + " r = y + 1.403 * v;\n" + " g = y - 0.344 * u - 0.714 * v;\n" + " b = y + 1.770 * u;\n" + " " FRAGMENT_SHADER_COLOR " = vec4(r, g, b, 1.0);\n" + " }\n"; + +// Compiles a shader of the given |type| with GLSL source |source| and returns +// the shader handle or 0 on error. +GLuint CreateShader(GLenum type, const GLchar* source) { + GLuint shader = glCreateShader(type); + if (!shader) { + return 0; + } + glShaderSource(shader, 1, &source, NULL); + glCompileShader(shader); + GLint compileStatus = GL_FALSE; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); + if (compileStatus == GL_FALSE) { + glDeleteShader(shader); + shader = 0; + } + return shader; +} + +// Links a shader program with the given vertex and fragment shaders and +// returns the program handle or 0 on error. +GLuint CreateProgram(GLuint vertexShader, GLuint fragmentShader) { + if (vertexShader == 0 || fragmentShader == 0) { + return 0; + } + GLuint program = glCreateProgram(); + if (!program) { + return 0; + } + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if (linkStatus == GL_FALSE) { + glDeleteProgram(program); + program = 0; + } + return program; +} + +// When modelview and projection matrices are identity (default) the world is +// contained in the square around origin with unit size 2. Drawing to these +// coordinates is equivalent to drawing to the entire screen. The texture is +// stretched over that square using texture coordinates (u, v) that range +// from (0, 0) to (1, 1) inclusive. Texture coordinates are flipped vertically +// here because the incoming frame has origin in upper left hand corner but +// OpenGL expects origin in bottom left corner. +const GLfloat gVertices[] = { + // X, Y, U, V. + -1, -1, 0, 1, // Bottom left. + 1, -1, 1, 1, // Bottom right. + 1, 1, 1, 0, // Top right. + -1, 1, 0, 0, // Top left. +}; + +// |kNumTextures| must not exceed 8, which is the limit in OpenGLES2. Two sets +// of 3 textures are used here, one for each of the Y, U and V planes. Having +// two sets alleviates CPU blockage in the event that the GPU is asked to render +// to a texture that is already in use. +static const GLsizei kNumTextureSets = 2; +static const GLsizei kNumTextures = 3 * kNumTextureSets; + +@implementation RTCOpenGLVideoRenderer { +#if TARGET_OS_IPHONE + EAGLContext* _context; +#else + NSOpenGLContext* _context; +#endif + BOOL _isInitialized; + NSUInteger _currentTextureSet; + // Handles for OpenGL constructs. + GLuint _textures[kNumTextures]; + GLuint _program; +#if !TARGET_OS_IPHONE + GLuint _vertexArray; +#endif + GLuint _vertexBuffer; + GLint _position; + GLint _texcoord; + GLint _ySampler; + GLint _uSampler; + GLint _vSampler; +} + ++ (void)initialize { + // Disable dithering for performance. + glDisable(GL_DITHER); +} + +#if TARGET_OS_IPHONE +- (instancetype)initWithContext:(EAGLContext*)context { +#else +- (instancetype)initWithContext:(NSOpenGLContext*)context { +#endif + NSAssert(context != nil, @"context cannot be nil"); + if (self = [super init]) { + _context = context; + } + return self; +} + +- (BOOL)drawFrame:(RTCI420Frame*)frame { + if (!_isInitialized) { + return NO; + } + if (_lastDrawnFrame == frame) { + return NO; + } + [self ensureGLContext]; + if (![self updateTextureSizesForFrame:frame] || + ![self updateTextureDataForFrame:frame]) { + return NO; + } + glClear(GL_COLOR_BUFFER_BIT); +#if !TARGET_OS_IPHONE + glBindVertexArray(_vertexArray); +#endif + glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +#if !TARGET_OS_IPHONE + [_context flushBuffer]; +#endif + _lastDrawnFrame = frame; + return YES; +} + +- (void)setupGL { + if (_isInitialized) { + return; + } + [self ensureGLContext]; + if (![self setupProgram]) { + return; + } + if (![self setupTextures]) { + return; + } + if (![self setupVertices]) { + return; + } + glUseProgram(_program); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glClearColor(0, 0, 0, 1); + _isInitialized = YES; +} + +- (void)teardownGL { + if (!_isInitialized) { + return; + } + [self ensureGLContext]; + glDeleteProgram(_program); + _program = 0; + glDeleteTextures(kNumTextures, _textures); + glDeleteBuffers(1, &_vertexBuffer); + _vertexBuffer = 0; +#if !TARGET_OS_IPHONE + glDeleteVertexArrays(1, &_vertexArray); +#endif + _isInitialized = NO; +} + +#pragma mark - Private + +- (void)ensureGLContext { + NSAssert(_context, @"context shouldn't be nil"); +#if TARGET_OS_IPHONE + if ([EAGLContext currentContext] != _context) { + [EAGLContext setCurrentContext:_context]; + } +#else + if ([NSOpenGLContext currentContext] != _context) { + [_context makeCurrentContext]; + } +#endif +} + +- (BOOL)setupProgram { + NSAssert(!_program, @"program already set up"); + GLuint vertexShader = CreateShader(GL_VERTEX_SHADER, kVertexShaderSource); + NSAssert(vertexShader, @"failed to create vertex shader"); + GLuint fragmentShader = + CreateShader(GL_FRAGMENT_SHADER, kFragmentShaderSource); + NSAssert(fragmentShader, @"failed to create fragment shader"); + _program = CreateProgram(vertexShader, fragmentShader); + // Shaders are created only to generate program. + if (vertexShader) { + glDeleteShader(vertexShader); + } + if (fragmentShader) { + glDeleteShader(fragmentShader); + } + if (!_program) { + return NO; + } + _position = glGetAttribLocation(_program, "position"); + _texcoord = glGetAttribLocation(_program, "texcoord"); + _ySampler = glGetUniformLocation(_program, "s_textureY"); + _uSampler = glGetUniformLocation(_program, "s_textureU"); + _vSampler = glGetUniformLocation(_program, "s_textureV"); + if (_position < 0 || _texcoord < 0 || _ySampler < 0 || _uSampler < 0 || + _vSampler < 0) { + return NO; + } + return YES; +} + +- (BOOL)setupTextures { + glGenTextures(kNumTextures, _textures); + // Set parameters for each of the textures we created. + for (GLsizei i = 0; i < kNumTextures; i++) { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, _textures[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + return YES; +} + +- (BOOL)updateTextureSizesForFrame:(RTCI420Frame*)frame { + if (frame.height == _lastDrawnFrame.height && + frame.width == _lastDrawnFrame.width && + frame.chromaWidth == _lastDrawnFrame.chromaWidth && + frame.chromaHeight == _lastDrawnFrame.chromaHeight) { + return YES; + } + GLsizei lumaWidth = frame.width; + GLsizei lumaHeight = frame.height; + GLsizei chromaWidth = frame.chromaWidth; + GLsizei chromaHeight = frame.chromaHeight; + for (GLint i = 0; i < kNumTextureSets; i++) { + glActiveTexture(GL_TEXTURE0 + i * 3); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + lumaWidth, + lumaHeight, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + 0); + glActiveTexture(GL_TEXTURE0 + i * 3 + 1); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + chromaWidth, + chromaHeight, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + 0); + glActiveTexture(GL_TEXTURE0 + i * 3 + 2); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + chromaWidth, + chromaHeight, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + 0); + } + return YES; +} + +- (BOOL)updateTextureDataForFrame:(RTCI420Frame*)frame { + NSUInteger textureOffset = _currentTextureSet * 3; + NSAssert(textureOffset + 3 <= kNumTextures, @"invalid offset"); + NSParameterAssert(frame.yPitch == frame.width); + NSParameterAssert(frame.uPitch == frame.chromaWidth); + NSParameterAssert(frame.vPitch == frame.chromaWidth); + + glActiveTexture(GL_TEXTURE0 + textureOffset); + // When setting texture sampler uniforms, the texture index is used not + // the texture handle. + glUniform1i(_ySampler, textureOffset); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + frame.width, + frame.height, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + frame.yPlane); + + glActiveTexture(GL_TEXTURE0 + textureOffset + 1); + glUniform1i(_uSampler, textureOffset + 1); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + frame.chromaWidth, + frame.chromaHeight, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + frame.uPlane); + + glActiveTexture(GL_TEXTURE0 + textureOffset + 2); + glUniform1i(_vSampler, textureOffset + 2); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + frame.chromaWidth, + frame.chromaHeight, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + frame.vPlane); + + _currentTextureSet = (_currentTextureSet + 1) % kNumTextureSets; + return YES; +} + +- (BOOL)setupVertices { +#if !TARGET_OS_IPHONE + NSAssert(!_vertexArray, @"vertex array already set up"); + glGenVertexArrays(1, &_vertexArray); + if (!_vertexArray) { + return NO; + } + glBindVertexArray(_vertexArray); +#endif + NSAssert(!_vertexBuffer, @"vertex buffer already set up"); + glGenBuffers(1, &_vertexBuffer); + if (!_vertexBuffer) { +#if !TARGET_OS_IPHONE + glDeleteVertexArrays(1, &_vertexArray); + _vertexArray = 0; +#endif + return NO; + } + glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(gVertices), gVertices, GL_DYNAMIC_DRAW); + + // Read position attribute from |gVertices| with size of 2 and stride of 4 + // beginning at the start of the array. The last argument indicates offset + // of data within |gVertices| as supplied to the vertex buffer. + glVertexAttribPointer( + _position, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0); + glEnableVertexAttribArray(_position); + + // Read texcoord attribute from |gVertices| with size of 2 and stride of 4 + // beginning at the first texcoord in the array. The last argument indicates + // offset of data within |gVertices| as supplied to the vertex buffer. + glVertexAttribPointer(_texcoord, + 2, + GL_FLOAT, + GL_FALSE, + 4 * sizeof(GLfloat), + (void*)(2 * sizeof(GLfloat))); + glEnableVertexAttribArray(_texcoord); + + return YES; +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPair.m b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPair.m index 31ac53ac105..2b289f5ccfd 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPair.m +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPair.m @@ -32,7 +32,7 @@ @synthesize key = _key; @synthesize value = _value; -- (id)initWithKey:(NSString *)key value:(NSString *)value { +- (id)initWithKey:(NSString*)key value:(NSString*)value { if ((self = [super init])) { _key = [key copy]; _value = [value copy]; @@ -40,4 +40,8 @@ return self; } +- (NSString*)description { + return [NSString stringWithFormat:@"%@: %@", _key, _value]; +} + @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnection+Internal.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnection+Internal.h index d1b4639f8af..ad1c334a2a2 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnection+Internal.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnection+Internal.h @@ -28,7 +28,6 @@ #import "RTCPeerConnection.h" #import "RTCPeerConnectionDelegate.h" -#import "RTCPeerConnectionObserver.h" #include "talk/app/webrtc/peerconnectioninterface.h" @@ -37,8 +36,8 @@ @property(nonatomic, assign, readonly) talk_base::scoped_refptr<webrtc::PeerConnectionInterface> peerConnection; -- (id)initWithPeerConnection:( - talk_base::scoped_refptr<webrtc::PeerConnectionInterface>)peerConnection - observer:(webrtc::RTCPeerConnectionObserver *)observer; +- (instancetype)initWithFactory:(webrtc::PeerConnectionFactoryInterface*)factory + iceServers:(const webrtc::PeerConnectionInterface::IceServers&)iceServers + constraints:(const webrtc::MediaConstraintsInterface*)constraints; @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnection.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnection.mm index ae9d1583dc7..738fb313f48 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnection.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnection.mm @@ -29,16 +29,21 @@ #error "This file requires ARC support." #endif -#import "RTCPeerConnection+internal.h" +#import "RTCPeerConnection+Internal.h" +#import "RTCDataChannel+Internal.h" #import "RTCEnumConverter.h" -#import "RTCICECandidate+internal.h" -#import "RTCICEServer+internal.h" -#import "RTCMediaConstraints+internal.h" -#import "RTCMediaStream+internal.h" -#import "RTCSessionDescription+internal.h" -#import "RTCSessionDescriptonDelegate.h" +#import "RTCICECandidate+Internal.h" +#import "RTCICEServer+Internal.h" +#import "RTCMediaConstraints+Internal.h" +#import "RTCMediaStream+Internal.h" +#import "RTCMediaStreamTrack+Internal.h" +#import "RTCPeerConnectionObserver.h" +#import "RTCSessionDescription+Internal.h" +#import "RTCSessionDescriptionDelegate.h" #import "RTCSessionDescription.h" +#import "RTCStatsDelegate.h" +#import "RTCStatsReport+Internal.h" #include "talk/app/webrtc/jsep.h" @@ -50,40 +55,41 @@ namespace webrtc { class RTCCreateSessionDescriptionObserver : public CreateSessionDescriptionObserver { public: - RTCCreateSessionDescriptionObserver(id<RTCSessionDescriptonDelegate> delegate, - RTCPeerConnection *peerConnection) { + RTCCreateSessionDescriptionObserver( + id<RTCSessionDescriptionDelegate> delegate, + RTCPeerConnection* peerConnection) { _delegate = delegate; _peerConnection = peerConnection; } - virtual void OnSuccess(SessionDescriptionInterface *desc) OVERRIDE { - RTCSessionDescription *session = + virtual void OnSuccess(SessionDescriptionInterface* desc) OVERRIDE { + RTCSessionDescription* session = [[RTCSessionDescription alloc] initWithSessionDescription:desc]; [_delegate peerConnection:_peerConnection didCreateSessionDescription:session - error:nil]; + error:nil]; } - virtual void OnFailure(const std::string &error) OVERRIDE { - NSString *str = @(error.c_str()); - NSError *err = + virtual void OnFailure(const std::string& error) OVERRIDE { + NSString* str = @(error.c_str()); + NSError* err = [NSError errorWithDomain:kRTCSessionDescriptionDelegateErrorDomain code:kRTCSessionDescriptionDelegateErrorCode - userInfo:@{ @"error" : str }]; + userInfo:@{@"error" : str}]; [_delegate peerConnection:_peerConnection didCreateSessionDescription:nil - error:err]; + error:err]; } private: - id<RTCSessionDescriptonDelegate> _delegate; - RTCPeerConnection *_peerConnection; + id<RTCSessionDescriptionDelegate> _delegate; + RTCPeerConnection* _peerConnection; }; class RTCSetSessionDescriptionObserver : public SetSessionDescriptionObserver { public: - RTCSetSessionDescriptionObserver(id<RTCSessionDescriptonDelegate> delegate, - RTCPeerConnection *peerConnection) { + RTCSetSessionDescriptionObserver(id<RTCSessionDescriptionDelegate> delegate, + RTCPeerConnection* peerConnection) { _delegate = delegate; _peerConnection = peerConnection; } @@ -93,37 +99,60 @@ class RTCSetSessionDescriptionObserver : public SetSessionDescriptionObserver { didSetSessionDescriptionWithError:nil]; } - virtual void OnFailure(const std::string &error) OVERRIDE { - NSString *str = @(error.c_str()); - NSError *err = + virtual void OnFailure(const std::string& error) OVERRIDE { + NSString* str = @(error.c_str()); + NSError* err = [NSError errorWithDomain:kRTCSessionDescriptionDelegateErrorDomain code:kRTCSessionDescriptionDelegateErrorCode - userInfo:@{ @"error" : str }]; + userInfo:@{@"error" : str}]; [_delegate peerConnection:_peerConnection didSetSessionDescriptionWithError:err]; } private: - id<RTCSessionDescriptonDelegate> _delegate; - RTCPeerConnection *_peerConnection; + id<RTCSessionDescriptionDelegate> _delegate; + RTCPeerConnection* _peerConnection; }; +class RTCStatsObserver : public StatsObserver { + public: + RTCStatsObserver(id<RTCStatsDelegate> delegate, + RTCPeerConnection* peerConnection) { + _delegate = delegate; + _peerConnection = peerConnection; + } + + virtual void OnComplete(const std::vector<StatsReport>& reports) OVERRIDE { + NSMutableArray* stats = [NSMutableArray arrayWithCapacity:reports.size()]; + std::vector<StatsReport>::const_iterator it = reports.begin(); + for (; it != reports.end(); ++it) { + RTCStatsReport* statsReport = + [[RTCStatsReport alloc] initWithStatsReport:*it]; + [stats addObject:statsReport]; + } + [_delegate peerConnection:_peerConnection didGetStats:stats]; + } + + private: + id<RTCStatsDelegate> _delegate; + RTCPeerConnection* _peerConnection; +}; } @implementation RTCPeerConnection { - NSMutableArray *_localStreams; - talk_base::scoped_ptr<webrtc::RTCPeerConnectionObserver>_observer; + NSMutableArray* _localStreams; + talk_base::scoped_ptr<webrtc::RTCPeerConnectionObserver> _observer; talk_base::scoped_refptr<webrtc::PeerConnectionInterface> _peerConnection; } -- (BOOL)addICECandidate:(RTCICECandidate *)candidate { +- (BOOL)addICECandidate:(RTCICECandidate*)candidate { talk_base::scoped_ptr<const webrtc::IceCandidateInterface> iceCandidate( candidate.candidate); return self.peerConnection->AddIceCandidate(iceCandidate.get()); } -- (BOOL)addStream:(RTCMediaStream *)stream - constraints:(RTCMediaConstraints *)constraints { +- (BOOL)addStream:(RTCMediaStream*)stream + constraints:(RTCMediaConstraints*)constraints { BOOL ret = self.peerConnection->AddStream(stream.mediaStream, constraints.constraints); if (!ret) { @@ -133,82 +162,91 @@ class RTCSetSessionDescriptionObserver : public SetSessionDescriptionObserver { return YES; } -- (void)createAnswerWithDelegate:(id<RTCSessionDescriptonDelegate>)delegate - constraints:(RTCMediaConstraints *)constraints { +- (RTCDataChannel*)createDataChannelWithLabel:(NSString*)label + config:(RTCDataChannelInit*)config { + std::string labelString([label UTF8String]); + talk_base::scoped_refptr<webrtc::DataChannelInterface> dataChannel = + self.peerConnection->CreateDataChannel(labelString, + config.dataChannelInit); + return [[RTCDataChannel alloc] initWithDataChannel:dataChannel]; +} + +- (void)createAnswerWithDelegate:(id<RTCSessionDescriptionDelegate>)delegate + constraints:(RTCMediaConstraints*)constraints { talk_base::scoped_refptr<webrtc::RTCCreateSessionDescriptionObserver> observer(new talk_base::RefCountedObject< webrtc::RTCCreateSessionDescriptionObserver>(delegate, self)); self.peerConnection->CreateAnswer(observer, constraints.constraints); } -- (void)createOfferWithDelegate:(id<RTCSessionDescriptonDelegate>)delegate - constraints:(RTCMediaConstraints *)constraints { +- (void)createOfferWithDelegate:(id<RTCSessionDescriptionDelegate>)delegate + constraints:(RTCMediaConstraints*)constraints { talk_base::scoped_refptr<webrtc::RTCCreateSessionDescriptionObserver> observer(new talk_base::RefCountedObject< webrtc::RTCCreateSessionDescriptionObserver>(delegate, self)); self.peerConnection->CreateOffer(observer, constraints.constraints); } -- (void)removeStream:(RTCMediaStream *)stream { +- (void)removeStream:(RTCMediaStream*)stream { self.peerConnection->RemoveStream(stream.mediaStream); [_localStreams removeObject:stream]; } -- (void) - setLocalDescriptionWithDelegate:(id<RTCSessionDescriptonDelegate>)delegate - sessionDescription:(RTCSessionDescription *)sdp { +- (void)setLocalDescriptionWithDelegate: + (id<RTCSessionDescriptionDelegate>)delegate + sessionDescription:(RTCSessionDescription*)sdp { talk_base::scoped_refptr<webrtc::RTCSetSessionDescriptionObserver> observer( new talk_base::RefCountedObject<webrtc::RTCSetSessionDescriptionObserver>( delegate, self)); self.peerConnection->SetLocalDescription(observer, sdp.sessionDescription); } -- (void) - setRemoteDescriptionWithDelegate:(id<RTCSessionDescriptonDelegate>)delegate - sessionDescription:(RTCSessionDescription *)sdp { +- (void)setRemoteDescriptionWithDelegate: + (id<RTCSessionDescriptionDelegate>)delegate + sessionDescription:(RTCSessionDescription*)sdp { talk_base::scoped_refptr<webrtc::RTCSetSessionDescriptionObserver> observer( new talk_base::RefCountedObject<webrtc::RTCSetSessionDescriptionObserver>( delegate, self)); self.peerConnection->SetRemoteDescription(observer, sdp.sessionDescription); } -- (BOOL)updateICEServers:(NSArray *)servers - constraints:(RTCMediaConstraints *)constraints { +- (BOOL)updateICEServers:(NSArray*)servers + constraints:(RTCMediaConstraints*)constraints { webrtc::PeerConnectionInterface::IceServers iceServers; - for (RTCICEServer *server in servers) { + for (RTCICEServer* server in servers) { iceServers.push_back(server.iceServer); } return self.peerConnection->UpdateIce(iceServers, constraints.constraints); } -- (RTCSessionDescription *)localDescription { - const webrtc::SessionDescriptionInterface *sdi = +- (RTCSessionDescription*)localDescription { + const webrtc::SessionDescriptionInterface* sdi = self.peerConnection->local_description(); - return sdi ? - [[RTCSessionDescription alloc] initWithSessionDescription:sdi] : - nil; + return sdi ? [[RTCSessionDescription alloc] initWithSessionDescription:sdi] + : nil; } -- (NSArray *)localStreams { +- (NSArray*)localStreams { return [_localStreams copy]; } -- (RTCSessionDescription *)remoteDescription { - const webrtc::SessionDescriptionInterface *sdi = +- (RTCSessionDescription*)remoteDescription { + const webrtc::SessionDescriptionInterface* sdi = self.peerConnection->remote_description(); - return sdi ? - [[RTCSessionDescription alloc] initWithSessionDescription:sdi] : - nil; + return sdi ? [[RTCSessionDescription alloc] initWithSessionDescription:sdi] + : nil; } - (RTCICEConnectionState)iceConnectionState { - return [RTCEnumConverter convertIceConnectionStateToObjC: - self.peerConnection->ice_connection_state()]; + return [RTCEnumConverter + convertIceConnectionStateToObjC:self.peerConnection + ->ice_connection_state()]; } - (RTCICEGatheringState)iceGatheringState { - return [RTCEnumConverter convertIceGatheringStateToObjC: - self.peerConnection->ice_gathering_state()]; + return [RTCEnumConverter + convertIceGatheringStateToObjC:self.peerConnection + ->ice_gathering_state()]; } - (RTCSignalingState)signalingState { @@ -220,22 +258,31 @@ class RTCSetSessionDescriptionObserver : public SetSessionDescriptionObserver { self.peerConnection->Close(); } +- (BOOL)getStatsWithDelegate:(id<RTCStatsDelegate>)delegate + mediaStreamTrack:(RTCMediaStreamTrack*)mediaStreamTrack + statsOutputLevel:(RTCStatsOutputLevel)statsOutputLevel { + talk_base::scoped_refptr<webrtc::RTCStatsObserver> observer( + new talk_base::RefCountedObject<webrtc::RTCStatsObserver>(delegate, + self)); + webrtc::PeerConnectionInterface::StatsOutputLevel nativeOutputLevel = + [RTCEnumConverter convertStatsOutputLevelToNative:statsOutputLevel]; + return self.peerConnection->GetStats( + observer, mediaStreamTrack.mediaTrack, nativeOutputLevel); +} + @end @implementation RTCPeerConnection (Internal) -- (id)initWithPeerConnection:( - talk_base::scoped_refptr<webrtc::PeerConnectionInterface>)peerConnection - observer:(webrtc::RTCPeerConnectionObserver *)observer { - if (!peerConnection || !observer) { - NSAssert(NO, @"nil arguments not allowed"); - self = nil; - return nil; - } - if ((self = [super init])) { - _peerConnection = peerConnection; +- (instancetype)initWithFactory:(webrtc::PeerConnectionFactoryInterface*)factory + iceServers:(const webrtc::PeerConnectionInterface::IceServers&)iceServers + constraints:(const webrtc::MediaConstraintsInterface*)constraints { + NSParameterAssert(factory != NULL); + if (self = [super init]) { + _observer.reset(new webrtc::RTCPeerConnectionObserver(self)); + _peerConnection = factory->CreatePeerConnection( + iceServers, constraints, NULL, NULL, _observer.get()); _localStreams = [[NSMutableArray alloc] init]; - _observer.reset(observer); } return self; } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionFactory.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionFactory.mm index 325110fb36a..8ada166d6be 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionFactory.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionFactory.mm @@ -33,18 +33,17 @@ #include <vector> -#import "RTCAudioTrack+internal.h" -#import "RTCICEServer+internal.h" -#import "RTCMediaConstraints+internal.h" -#import "RTCMediaSource+internal.h" -#import "RTCMediaStream+internal.h" -#import "RTCMediaStreamTrack+internal.h" -#import "RTCPeerConnection+internal.h" +#import "RTCAudioTrack+Internal.h" +#import "RTCICEServer+Internal.h" +#import "RTCMediaConstraints+Internal.h" +#import "RTCMediaSource+Internal.h" +#import "RTCMediaStream+Internal.h" +#import "RTCMediaStreamTrack+Internal.h" +#import "RTCPeerConnection+Internal.h" #import "RTCPeerConnectionDelegate.h" -#import "RTCPeerConnectionObserver.h" -#import "RTCVideoCapturer+internal.h" -#import "RTCVideoSource+internal.h" -#import "RTCVideoTrack+internal.h" +#import "RTCVideoCapturer+Internal.h" +#import "RTCVideoSource+Internal.h" +#import "RTCVideoTrack+Internal.h" #include "talk/app/webrtc/audiotrack.h" #include "talk/app/webrtc/mediastreaminterface.h" @@ -86,54 +85,48 @@ return self; } -- (RTCPeerConnection *) - peerConnectionWithICEServers:(NSArray *)servers - constraints:(RTCMediaConstraints *)constraints +- (RTCPeerConnection*) + peerConnectionWithICEServers:(NSArray*)servers + constraints:(RTCMediaConstraints*)constraints delegate:(id<RTCPeerConnectionDelegate>)delegate { webrtc::PeerConnectionInterface::IceServers iceServers; - for (RTCICEServer *server in servers) { + for (RTCICEServer* server in servers) { iceServers.push_back(server.iceServer); } - webrtc::RTCPeerConnectionObserver *observer = - new webrtc::RTCPeerConnectionObserver(delegate); - webrtc::DTLSIdentityServiceInterface* dummy_dtls_identity_service = NULL; - talk_base::scoped_refptr<webrtc::PeerConnectionInterface> peerConnection = - self.nativeFactory->CreatePeerConnection( - iceServers, constraints.constraints, dummy_dtls_identity_service, - observer); - RTCPeerConnection *pc = - [[RTCPeerConnection alloc] initWithPeerConnection:peerConnection - observer:observer]; - observer->SetPeerConnection(pc); + RTCPeerConnection* pc = + [[RTCPeerConnection alloc] initWithFactory:self.nativeFactory.get() + iceServers:iceServers + constraints:constraints.constraints]; + pc.delegate = delegate; return pc; } -- (RTCMediaStream *)mediaStreamWithLabel:(NSString *)label { +- (RTCMediaStream*)mediaStreamWithLabel:(NSString*)label { talk_base::scoped_refptr<webrtc::MediaStreamInterface> nativeMediaStream = self.nativeFactory->CreateLocalMediaStream([label UTF8String]); return [[RTCMediaStream alloc] initWithMediaStream:nativeMediaStream]; } -- (RTCVideoSource *)videoSourceWithCapturer:(RTCVideoCapturer *)capturer - constraints:(RTCMediaConstraints *)constraints { +- (RTCVideoSource*)videoSourceWithCapturer:(RTCVideoCapturer*)capturer + constraints:(RTCMediaConstraints*)constraints { if (!capturer) { return nil; } talk_base::scoped_refptr<webrtc::VideoSourceInterface> source = - self.nativeFactory->CreateVideoSource(capturer.capturer.get(), + self.nativeFactory->CreateVideoSource([capturer takeNativeCapturer], constraints.constraints); return [[RTCVideoSource alloc] initWithMediaSource:source]; } -- (RTCVideoTrack *)videoTrackWithID:(NSString *)videoId - source:(RTCVideoSource *)source { +- (RTCVideoTrack*)videoTrackWithID:(NSString*)videoId + source:(RTCVideoSource*)source { talk_base::scoped_refptr<webrtc::VideoTrackInterface> track = self.nativeFactory->CreateVideoTrack([videoId UTF8String], source.videoSource); return [[RTCVideoTrack alloc] initWithMediaTrack:track]; } -- (RTCAudioTrack *)audioTrackWithID:(NSString *)audioId { +- (RTCAudioTrack*)audioTrackWithID:(NSString*)audioId { talk_base::scoped_refptr<webrtc::AudioTrackInterface> track = self.nativeFactory->CreateAudioTrack([audioId UTF8String], NULL); return [[RTCAudioTrack alloc] initWithMediaTrack:track]; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionObserver.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionObserver.h index c7d1ef8b8c4..f66b5672ef7 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionObserver.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionObserver.h @@ -38,9 +38,8 @@ namespace webrtc { class RTCPeerConnectionObserver : public PeerConnectionObserver { public: - explicit RTCPeerConnectionObserver(id<RTCPeerConnectionDelegate> delegate); - - void SetPeerConnection(RTCPeerConnection *peerConnection); + RTCPeerConnectionObserver(RTCPeerConnection* peerConnection); + virtual ~RTCPeerConnectionObserver(); virtual void OnError() OVERRIDE; @@ -57,7 +56,7 @@ class RTCPeerConnectionObserver : public PeerConnectionObserver { // Triggered when a remote peer open a data channel. virtual void OnDataChannel(DataChannelInterface* data_channel) OVERRIDE; - // Triggered when renegotation is needed, for example the ICE has restarted. + // Triggered when renegotiation is needed, for example the ICE has restarted. virtual void OnRenegotiationNeeded() OVERRIDE; // Called any time the ICEConnectionState changes @@ -72,8 +71,7 @@ class RTCPeerConnectionObserver : public PeerConnectionObserver { virtual void OnIceCandidate(const IceCandidateInterface* candidate) OVERRIDE; private: - id<RTCPeerConnectionDelegate> _delegate; - RTCPeerConnection *_peerConnection; + __weak RTCPeerConnection* _peerConnection; }; } // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionObserver.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionObserver.mm index e102bb974fb..061ccf0a92d 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionObserver.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCPeerConnectionObserver.mm @@ -31,73 +31,82 @@ #import "RTCPeerConnectionObserver.h" -#import "RTCICECandidate+internal.h" -#import "RTCMediaStream+internal.h" +#import "RTCDataChannel+Internal.h" +#import "RTCICECandidate+Internal.h" +#import "RTCMediaStream+Internal.h" #import "RTCEnumConverter.h" namespace webrtc { RTCPeerConnectionObserver::RTCPeerConnectionObserver( - id<RTCPeerConnectionDelegate> delegate) { - _delegate = delegate; + RTCPeerConnection* peerConnection) { + _peerConnection = peerConnection; } -void RTCPeerConnectionObserver::SetPeerConnection( - RTCPeerConnection *peerConnection) { - _peerConnection = peerConnection; +RTCPeerConnectionObserver::~RTCPeerConnectionObserver() { } void RTCPeerConnectionObserver::OnError() { - [_delegate peerConnectionOnError:_peerConnection]; + [_peerConnection.delegate peerConnectionOnError:_peerConnection]; } void RTCPeerConnectionObserver::OnSignalingChange( PeerConnectionInterface::SignalingState new_state) { - [_delegate peerConnection:_peerConnection - signalingStateChanged: - [RTCEnumConverter convertSignalingStateToObjC:new_state]]; + RTCSignalingState state = + [RTCEnumConverter convertSignalingStateToObjC:new_state]; + [_peerConnection.delegate peerConnection:_peerConnection + signalingStateChanged:state]; } void RTCPeerConnectionObserver::OnAddStream(MediaStreamInterface* stream) { RTCMediaStream* mediaStream = [[RTCMediaStream alloc] initWithMediaStream:stream]; - [_delegate peerConnection:_peerConnection addedStream:mediaStream]; + [_peerConnection.delegate peerConnection:_peerConnection + addedStream:mediaStream]; } void RTCPeerConnectionObserver::OnRemoveStream(MediaStreamInterface* stream) { RTCMediaStream* mediaStream = [[RTCMediaStream alloc] initWithMediaStream:stream]; - [_delegate peerConnection:_peerConnection removedStream:mediaStream]; + [_peerConnection.delegate peerConnection:_peerConnection + removedStream:mediaStream]; } void RTCPeerConnectionObserver::OnDataChannel( DataChannelInterface* data_channel) { - // TODO(hughv): Implement for future version. + RTCDataChannel* dataChannel = + [[RTCDataChannel alloc] initWithDataChannel:data_channel]; + [_peerConnection.delegate peerConnection:_peerConnection + didOpenDataChannel:dataChannel]; } void RTCPeerConnectionObserver::OnRenegotiationNeeded() { - [_delegate peerConnectionOnRenegotiationNeeded:_peerConnection]; + id<RTCPeerConnectionDelegate> delegate = _peerConnection.delegate; + [delegate peerConnectionOnRenegotiationNeeded:_peerConnection]; } void RTCPeerConnectionObserver::OnIceConnectionChange( PeerConnectionInterface::IceConnectionState new_state) { - [_delegate peerConnection:_peerConnection - iceConnectionChanged: - [RTCEnumConverter convertIceConnectionStateToObjC:new_state]]; + RTCICEConnectionState state = + [RTCEnumConverter convertIceConnectionStateToObjC:new_state]; + [_peerConnection.delegate peerConnection:_peerConnection + iceConnectionChanged:state]; } void RTCPeerConnectionObserver::OnIceGatheringChange( PeerConnectionInterface::IceGatheringState new_state) { - [_delegate peerConnection:_peerConnection - iceGatheringChanged: - [RTCEnumConverter convertIceGatheringStateToObjC:new_state]]; + RTCICEGatheringState state = + [RTCEnumConverter convertIceGatheringStateToObjC:new_state]; + [_peerConnection.delegate peerConnection:_peerConnection + iceGatheringChanged:state]; } void RTCPeerConnectionObserver::OnIceCandidate( const IceCandidateInterface* candidate) { RTCICECandidate* iceCandidate = [[RTCICECandidate alloc] initWithCandidate:candidate]; - [_delegate peerConnection:_peerConnection gotICECandidate:iceCandidate]; + [_peerConnection.delegate peerConnection:_peerConnection + gotICECandidate:iceCandidate]; } -} // namespace webrtc +} // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCSessionDescription.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCSessionDescription.mm index dd2bbdc1410..49dfa2d9cb5 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCSessionDescription.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCSessionDescription.mm @@ -29,14 +29,14 @@ #error "This file requires ARC support." #endif -#import "RTCSessionDescription+internal.h" +#import "RTCSessionDescription+Internal.h" @implementation RTCSessionDescription @synthesize description = _description; @synthesize type = _type; -- (id)initWithType:(NSString *)type sdp:(NSString *)sdp { +- (id)initWithType:(NSString*)type sdp:(NSString*)sdp { if (!type || !sdp) { NSAssert(NO, @"nil arguments not allowed"); return nil; @@ -53,14 +53,14 @@ @implementation RTCSessionDescription (Internal) - (id)initWithSessionDescription: - (const webrtc::SessionDescriptionInterface *)sessionDescription { + (const webrtc::SessionDescriptionInterface*)sessionDescription { if (!sessionDescription) { NSAssert(NO, @"nil arguments not allowed"); self = nil; return nil; } if ((self = [super init])) { - const std::string &type = sessionDescription->type(); + const std::string& type = sessionDescription->type(); std::string sdp; if (!sessionDescription->ToString(&sdp)) { NSAssert(NO, @"Invalid SessionDescriptionInterface."); @@ -73,7 +73,7 @@ return self; } -- (webrtc::SessionDescriptionInterface *)sessionDescription { +- (webrtc::SessionDescriptionInterface*)sessionDescription { return webrtc::CreateSessionDescription( [self.type UTF8String], [self.description UTF8String], NULL); } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCStatsReport+Internal.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCStatsReport+Internal.h new file mode 100644 index 00000000000..b17b01a2fbb --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCStatsReport+Internal.h @@ -0,0 +1,36 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "RTCStatsReport.h" + +#include "talk/app/webrtc/statstypes.h" + +@interface RTCStatsReport (Internal) + +- (instancetype)initWithStatsReport:(const webrtc::StatsReport&)statsReport; + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCStatsReport.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCStatsReport.mm new file mode 100644 index 00000000000..8da4b469a0f --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCStatsReport.mm @@ -0,0 +1,69 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "RTCStatsReport+Internal.h" + +#import "RTCPair.h" + +@implementation RTCStatsReport + +- (NSString*)description { + NSString* format = @"id: %@, type: %@, timestamp: %f, values: %@"; + return [NSString stringWithFormat:format, + self.reportId, + self.type, + self.timestamp, + self.values]; +} + +@end + +@implementation RTCStatsReport (Internal) + +- (instancetype)initWithStatsReport:(const webrtc::StatsReport&)statsReport { + if (self = [super init]) { + _reportId = @(statsReport.id.c_str()); + _type = @(statsReport.type.c_str()); + _timestamp = statsReport.timestamp; + NSMutableArray* values = + [NSMutableArray arrayWithCapacity:statsReport.values.size()]; + webrtc::StatsReport::Values::const_iterator it = statsReport.values.begin(); + for (; it != statsReport.values.end(); ++it) { + RTCPair* pair = [[RTCPair alloc] initWithKey:@(it->name.c_str()) + value:@(it->value.c_str())]; + [values addObject:pair]; + } + _values = values; + } + return self; +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoCapturer+Internal.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoCapturer+Internal.h index d0d685b2c9f..4a4810b471a 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoCapturer+Internal.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoCapturer+Internal.h @@ -31,7 +31,7 @@ @interface RTCVideoCapturer (Internal) -@property(nonatomic, assign, readonly) const talk_base::scoped_ptr<cricket::VideoCapturer> &capturer; +- (cricket::VideoCapturer*)takeNativeCapturer; - (id)initWithCapturer:(cricket::VideoCapturer*)capturer; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoCapturer.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoCapturer.mm index f7282c55d87..d947f02adec 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoCapturer.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoCapturer.mm @@ -29,17 +29,17 @@ #error "This file requires ARC support." #endif -#import "RTCVideoCapturer+internal.h" +#import "RTCVideoCapturer+Internal.h" #include "talk/media/base/videocapturer.h" #include "talk/media/devices/devicemanager.h" @implementation RTCVideoCapturer { - talk_base::scoped_ptr<cricket::VideoCapturer>_capturer; + talk_base::scoped_ptr<cricket::VideoCapturer> _capturer; } -+ (RTCVideoCapturer *)capturerWithDeviceName:(NSString *)deviceName { - const std::string &device_name = std::string([deviceName UTF8String]); ++ (RTCVideoCapturer*)capturerWithDeviceName:(NSString*)deviceName { + const std::string& device_name = std::string([deviceName UTF8String]); talk_base::scoped_ptr<cricket::DeviceManagerInterface> device_manager( cricket::DeviceManagerFactory::Create()); bool initialized = device_manager->Init(); @@ -51,7 +51,7 @@ } talk_base::scoped_ptr<cricket::VideoCapturer> capturer( device_manager->CreateVideoCapturer(device)); - RTCVideoCapturer *rtcCapturer = + RTCVideoCapturer* rtcCapturer = [[RTCVideoCapturer alloc] initWithCapturer:capturer.release()]; return rtcCapturer; } @@ -60,17 +60,15 @@ @implementation RTCVideoCapturer (Internal) -- (id)initWithCapturer:(cricket::VideoCapturer *)capturer { +- (id)initWithCapturer:(cricket::VideoCapturer*)capturer { if ((self = [super init])) { _capturer.reset(capturer); } return self; } -// TODO(hughv): When capturer is implemented, this needs to return -// _capturer.release() instead. For now, this isn't used. -- (const talk_base::scoped_ptr<cricket::VideoCapturer> &)capturer { - return _capturer; +- (cricket::VideoCapturer*)takeNativeCapturer { + return _capturer.release(); } @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoRenderer+Internal.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoRenderer+Internal.h index 8854ed71ff4..22e445ccc9d 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoRenderer+Internal.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoRenderer+Internal.h @@ -31,10 +31,6 @@ @interface RTCVideoRenderer (Internal) -// TODO(hughv): Use smart pointer. -@property(nonatomic, assign, readonly) - webrtc::VideoRendererInterface *videoRenderer; - -- (id)initWithVideoRenderer:(webrtc::VideoRendererInterface *)videoRenderer; +@property(nonatomic, readonly) webrtc::VideoRendererInterface* videoRenderer; @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoRenderer.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoRenderer.mm index 23136152109..07041819f65 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoRenderer.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoRenderer.mm @@ -29,46 +29,74 @@ #error "This file requires ARC support." #endif -#import "RTCVideoRenderer+internal.h" +#import "RTCVideoRenderer+Internal.h" #if TARGET_OS_IPHONE -#import <UIKit/UIKit.h> +#import "RTCEAGLVideoView+Internal.h" #endif +#import "RTCI420Frame+Internal.h" -#import "RTCI420Frame.h" -#import "RTCVideoRendererDelegate.h" +namespace webrtc { -@implementation RTCVideoRenderer +class RTCVideoRendererAdapter : public VideoRendererInterface { + public: + RTCVideoRendererAdapter(RTCVideoRenderer* renderer) { _renderer = renderer; } -@synthesize delegate = _delegate; + virtual void SetSize(int width, int height) OVERRIDE { + [_renderer.delegate renderer:_renderer + didSetSize:CGSizeMake(width, height)]; + } + + virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE { + if (!_renderer.delegate) { + return; + } + RTCI420Frame* i420Frame = [[RTCI420Frame alloc] initWithVideoFrame:frame]; + [_renderer.delegate renderer:_renderer didReceiveFrame:i420Frame]; + } -+ (RTCVideoRenderer *)videoRenderGUIWithFrame:(CGRect)frame { - // TODO (hughv): Implement. - return nil; + private: + __weak RTCVideoRenderer* _renderer; +}; } -- (id)initWithDelegate:(id<RTCVideoRendererDelegate>)delegate { - if ((self = [super init])) { +@implementation RTCVideoRenderer { + talk_base::scoped_ptr<webrtc::RTCVideoRendererAdapter> _adapter; +#if TARGET_OS_IPHONE + RTCEAGLVideoView* _videoView; +#endif +} + +- (instancetype)initWithDelegate:(id<RTCVideoRendererDelegate>)delegate { + if (self = [super init]) { _delegate = delegate; - // TODO (hughv): Create video renderer. + _adapter.reset(new webrtc::RTCVideoRendererAdapter(self)); } return self; } -@end - -@implementation RTCVideoRenderer (Internal) - -- (id)initWithVideoRenderer:(webrtc::VideoRendererInterface *)videoRenderer { - if ((self = [super init])) { - // TODO (hughv): Implement. +#if TARGET_OS_IPHONE +// TODO(tkchin): remove shim for deprecated method. +- (instancetype)initWithView:(UIView*)view { + if (self = [super init]) { + _videoView = [[RTCEAGLVideoView alloc] initWithFrame:view.bounds]; + _videoView.autoresizingMask = + UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + _videoView.translatesAutoresizingMaskIntoConstraints = YES; + [view addSubview:_videoView]; + self.delegate = _videoView; + _adapter.reset(new webrtc::RTCVideoRendererAdapter(self)); } return self; } +#endif + +@end + +@implementation RTCVideoRenderer (Internal) -- (webrtc::VideoRendererInterface *)videoRenderer { - // TODO (hughv): Implement. - return NULL; +- (webrtc::VideoRendererInterface*)videoRenderer { + return _adapter.get(); } @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoSource.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoSource.mm index c28fa9bcfcd..b4554e08d22 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoSource.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoSource.mm @@ -29,8 +29,8 @@ #error "This file requires ARC support." #endif -#import "RTCVideoSource+internal.h" -#import "RTCMediaSource+internal.h" +#import "RTCVideoSource+Internal.h" +#import "RTCMediaSource+Internal.h" @implementation RTCVideoSource @end @@ -38,7 +38,7 @@ @implementation RTCVideoSource (Internal) - (talk_base::scoped_refptr<webrtc::VideoSourceInterface>)videoSource { - return static_cast<webrtc::VideoSourceInterface *>(self.mediaSource.get()); + return static_cast<webrtc::VideoSourceInterface*>(self.mediaSource.get()); } @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoTrack.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoTrack.mm index 88f7226a11e..d6c8ed8a4f5 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoTrack.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCVideoTrack.mm @@ -29,24 +29,25 @@ #error "This file requires ARC support." #endif -#import "RTCVideoTrack+internal.h" +#import "RTCVideoTrack+Internal.h" -#import "RTCMediaStreamTrack+internal.h" -#import "RTCVideoRenderer+internal.h" +#import "RTCMediaStreamTrack+Internal.h" +#import "RTCVideoRenderer+Internal.h" @implementation RTCVideoTrack { - NSMutableArray *_rendererArray; + NSMutableArray* _rendererArray; } -- (id)initWithMediaTrack:( - talk_base::scoped_refptr<webrtc::MediaStreamTrackInterface>)mediaTrack { +- (id)initWithMediaTrack: + (talk_base::scoped_refptr<webrtc::MediaStreamTrackInterface>) + mediaTrack { if (self = [super initWithMediaTrack:mediaTrack]) { _rendererArray = [NSMutableArray array]; } return self; } -- (void)addRenderer:(RTCVideoRenderer *)renderer { +- (void)addRenderer:(RTCVideoRenderer*)renderer { NSAssert1(![self.renderers containsObject:renderer], @"renderers already contains object [%@]", [renderer description]); @@ -54,7 +55,7 @@ self.videoTrack->AddRenderer(renderer.videoRenderer); } -- (void)removeRenderer:(RTCVideoRenderer *)renderer { +- (void)removeRenderer:(RTCVideoRenderer*)renderer { NSUInteger index = [self.renderers indexOfObjectIdenticalTo:renderer]; if (index != NSNotFound) { [_rendererArray removeObjectAtIndex:index]; @@ -62,7 +63,7 @@ } } -- (NSArray *)renderers { +- (NSArray*)renderers { return [_rendererArray copy]; } @@ -71,7 +72,7 @@ @implementation RTCVideoTrack (Internal) - (talk_base::scoped_refptr<webrtc::VideoTrackInterface>)videoTrack { - return static_cast<webrtc::VideoTrackInterface *>(self.mediaTrack.get()); + return static_cast<webrtc::VideoTrackInterface*>(self.mediaTrack.get()); } @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCDataChannel.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCDataChannel.h new file mode 100644 index 00000000000..762bd662e8a --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCDataChannel.h @@ -0,0 +1,112 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> + +// ObjectiveC wrapper for a DataChannelInit object. +@interface RTCDataChannelInit : NSObject + +// Set to YES if ordered delivery is required +@property(nonatomic) BOOL isOrdered; +// Max period in milliseconds in which retransmissions will be sent. After this +// time, no more retransmissions will be sent. -1 if unset. +@property(nonatomic) NSInteger maxRetransmitTimeMs; +// The max number of retransmissions. -1 if unset. +@property(nonatomic) NSInteger maxRetransmits; +// Set to YES if the channel has been externally negotiated and we do not send +// an in-band signalling in the form of an "open" message +@property(nonatomic) BOOL isNegotiated; +// The stream id, or SID, for SCTP data channels. -1 if unset. +@property(nonatomic) NSInteger streamId; +// Set by the application and opaque to the WebRTC implementation. +@property(nonatomic) NSString* protocol; + +@end + +// ObjectiveC wrapper for a DataBuffer object. +@interface RTCDataBuffer : NSObject + +@property(nonatomic, readonly) NSData* data; +@property(nonatomic, readonly) BOOL isBinary; + +- (instancetype)initWithData:(NSData*)data isBinary:(BOOL)isBinary; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Disallow init and don't add to documentation +- (id)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +@end + +// Keep in sync with webrtc::DataChannelInterface::DataState +typedef enum { + kRTCDataChannelStateConnecting, + kRTCDataChannelStateOpen, + kRTCDataChannelStateClosing, + kRTCDataChannelStateClosed +} RTCDataChannelState; + +@class RTCDataChannel; +// Protocol for receving data channel state and message events. +@protocol RTCDataChannelDelegate<NSObject> + +// Called when the data channel state has changed. +- (void)channelDidChangeState:(RTCDataChannel*)channel; + +// Called when a data buffer was successfully received. +- (void)channel:(RTCDataChannel*)channel + didReceiveMessageWithBuffer:(RTCDataBuffer*)buffer; + +@end + +// ObjectiveC wrapper for a DataChannel object. +// See talk/app/webrtc/datachannelinterface.h +@interface RTCDataChannel : NSObject + +@property(nonatomic, readonly) NSString* label; +@property(nonatomic, readonly) BOOL isReliable; +@property(nonatomic, readonly) BOOL isOrdered; +@property(nonatomic, readonly) NSUInteger maxRetransmitTime; +@property(nonatomic, readonly) NSUInteger maxRetransmits; +@property(nonatomic, readonly) NSString* protocol; +@property(nonatomic, readonly) BOOL isNegotiated; +@property(nonatomic, readonly) NSInteger streamId; +@property(nonatomic, readonly) RTCDataChannelState state; +@property(nonatomic, readonly) NSUInteger bufferedAmount; +@property(nonatomic, weak) id<RTCDataChannelDelegate> delegate; + +- (void)close; +- (BOOL)sendData:(RTCDataBuffer*)data; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Disallow init and don't add to documentation +- (id)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCVideoRendererDelegate.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCEAGLVideoView.h index af72bdeb9a1..c38799e86f9 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCVideoRendererDelegate.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCEAGLVideoView.h @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2013, Google Inc. + * Copyright 2014, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,19 +26,22 @@ */ #import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> -@class RTCI420Frame; -@class RTCVideoRenderer; +#import "RTCVideoRenderer.h" -// RTCVideoRendererDelegate is a protocol for an object that must be -// implemented to get messages when rendering. -@protocol RTCVideoRendererDelegate<NSObject> +@class RTCEAGLVideoView; +@protocol RTCEAGLVideoViewDelegate -// The size of the frame. -- (void)videoRenderer:(RTCVideoRenderer *)videoRenderer setSize:(CGSize)size; +- (void)videoView:(RTCEAGLVideoView*)videoView didChangeVideoSize:(CGSize)size; -// The frame to be displayed. -- (void)videoRenderer:(RTCVideoRenderer *)videoRenderer - renderFrame:(RTCI420Frame *)frame; +@end + +@class RTCVideoTrack; +// RTCEAGLVideoView renders |videoTrack| onto itself using OpenGLES. +@interface RTCEAGLVideoView : UIView + +@property(nonatomic, strong) RTCVideoTrack* videoTrack; +@property(nonatomic, weak) id<RTCEAGLVideoViewDelegate> delegate; @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCI420Frame.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCI420Frame.h index bf58085da06..737968c123d 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCI420Frame.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCI420Frame.h @@ -30,7 +30,24 @@ // RTCI420Frame is an ObjectiveC version of cricket::VideoFrame. @interface RTCI420Frame : NSObject -// TODO(hughv): Implement this when iOS VP8 is ready. +@property(nonatomic, readonly) NSUInteger width; +@property(nonatomic, readonly) NSUInteger height; +@property(nonatomic, readonly) NSUInteger chromaWidth; +@property(nonatomic, readonly) NSUInteger chromaHeight; +@property(nonatomic, readonly) NSUInteger chromaSize; +// These can return NULL if the object is not backed by a buffer. +@property(nonatomic, readonly) const uint8_t* yPlane; +@property(nonatomic, readonly) const uint8_t* uPlane; +@property(nonatomic, readonly) const uint8_t* vPlane; +@property(nonatomic, readonly) NSInteger yPitch; +@property(nonatomic, readonly) NSInteger uPitch; +@property(nonatomic, readonly) NSInteger vPitch; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Disallow init and don't add to documentation +- (id)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCICEServer.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCICEServer.h index 63c14efaaf4..b0adbf1ed08 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCICEServer.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCICEServer.h @@ -35,8 +35,8 @@ @property(nonatomic, copy, readonly) NSString* username; @property(nonatomic, copy, readonly) NSString* password; -// Initializer for RTCICEServer taking uri and password. -- (id)initWithURI:(NSString*)URI +// Initializer for RTCICEServer taking uri, username, and password. +- (id)initWithURI:(NSURL*)URI username:(NSString*)username password:(NSString*)password; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCMediaSource.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCMediaSource.h index be3ad329186..e31817f3f7f 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCMediaSource.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCMediaSource.h @@ -33,12 +33,12 @@ @interface RTCMediaSource : NSObject // The current state of the RTCMediaSource. -@property (nonatomic, assign, readonly)RTCSourceState state; +@property(nonatomic, assign, readonly) RTCSourceState state; #ifndef DOXYGEN_SHOULD_SKIP_THIS // Disallow init and don't add to documentation -- (id)init __attribute__( - (unavailable("init is not a supported initializer for this class."))); +- (id)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); #endif /* DOXYGEN_SHOULD_SKIP_THIS */ @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCMediaStreamTrack.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCMediaStreamTrack.h index f8f9369b5ca..1b9339d76b2 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCMediaStreamTrack.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCMediaStreamTrack.h @@ -29,13 +29,21 @@ #import "RTCTypes.h" +@class RTCMediaStreamTrack; +@protocol RTCMediaStreamTrackDelegate<NSObject> + +- (void)mediaStreamTrackDidChange:(RTCMediaStreamTrack*)mediaStreamTrack; + +@end + // RTCMediaStreamTrack implements the interface common to RTCAudioTrack and // RTCVideoTrack. Do not create an instance of this class, rather create one // of the derived classes. @interface RTCMediaStreamTrack : NSObject -@property(nonatomic, assign, readonly) NSString *kind; -@property(nonatomic, assign, readonly) NSString *label; +@property(nonatomic, readonly) NSString* kind; +@property(nonatomic, readonly) NSString* label; +@property(nonatomic, weak) id<RTCMediaStreamTrackDelegate> delegate; - (BOOL)isEnabled; - (BOOL)setEnabled:(BOOL)enabled; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCNSGLVideoView.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCNSGLVideoView.h new file mode 100644 index 00000000000..fd757cb43c5 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCNSGLVideoView.h @@ -0,0 +1,48 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if TARGET_OS_IPHONE +#error "This file targets OSX." +#endif + +#import <AppKit/NSOpenGLView.h> + +#import "RTCVideoTrack.h" + +@class RTCNSGLVideoView; +@protocol RTCNSGLVideoViewDelegate + +- (void)videoView:(RTCNSGLVideoView*)videoView didChangeVideoSize:(CGSize)size; + +@end + +@interface RTCNSGLVideoView : NSOpenGLView + +@property(nonatomic, strong) RTCVideoTrack* videoTrack; +@property(nonatomic, weak) id<RTCNSGLVideoViewDelegate> delegate; + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCOpenGLVideoRenderer.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCOpenGLVideoRenderer.h new file mode 100644 index 00000000000..d6744b23fa9 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCOpenGLVideoRenderer.h @@ -0,0 +1,73 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> +#if TARGET_OS_IPHONE +#import <GLKit/GLKit.h> +#else +#import <AppKit/NSOpenGL.h> +#endif + +@class RTCI420Frame; + +// RTCOpenGLVideoRenderer issues appropriate OpenGL commands to draw a frame to +// the currently bound framebuffer. Supports OpenGL 3.2 and OpenGLES 2.0. OpenGL +// framebuffer creation and management should be handled elsewhere using the +// same context used to initialize this class. +@interface RTCOpenGLVideoRenderer : NSObject + +// The last successfully drawn frame. Used to avoid drawing frames unnecessarily +// hence saving battery life by reducing load. +@property(nonatomic, readonly) RTCI420Frame* lastDrawnFrame; + +#if TARGET_OS_IPHONE +- (instancetype)initWithContext:(EAGLContext*)context; +#else +- (instancetype)initWithContext:(NSOpenGLContext*)context; +#endif + +// Draws |frame| onto the currently bound OpenGL framebuffer. |setupGL| must be +// called before this function will succeed. +- (BOOL)drawFrame:(RTCI420Frame*)frame; + +// The following methods are used to manage OpenGL resources. On iOS +// applications should release resources when placed in background for use in +// the foreground application. In fact, attempting to call OpenGLES commands +// while in background will result in application termination. + +// Sets up the OpenGL state needed for rendering. +- (void)setupGL; +// Tears down the OpenGL state created by |setupGL|. +- (void)teardownGL; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Disallow init and don't add to documentation +- (id)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCPeerConnection.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCPeerConnection.h index c66bac8b4cb..32a98306ef6 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCPeerConnection.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCPeerConnection.h @@ -27,12 +27,16 @@ #import "RTCPeerConnectionDelegate.h" +@class RTCDataChannel; +@class RTCDataChannelInit; @class RTCICECandidate; @class RTCICEServers; @class RTCMediaConstraints; @class RTCMediaStream; +@class RTCMediaStreamTrack; @class RTCSessionDescription; -@protocol RTCSessionDescriptonDelegate; +@protocol RTCSessionDescriptionDelegate; +@protocol RTCStatsDelegate; // RTCPeerConnection is an ObjectiveC friendly wrapper around a PeerConnection // object. See the documentation in talk/app/webrtc/peerconnectioninterface.h. @@ -41,6 +45,8 @@ // http://www.w3.org/TR/mediacapture-streams/ @interface RTCPeerConnection : NSObject +@property(nonatomic, weak) id<RTCPeerConnectionDelegate> delegate; + // Accessor methods to active local streams. @property(nonatomic, strong, readonly) NSArray *localStreams; @@ -66,26 +72,30 @@ // remote peer is notified. - (void)removeStream:(RTCMediaStream *)stream; +// Create a data channel. +- (RTCDataChannel*)createDataChannelWithLabel:(NSString*)label + config:(RTCDataChannelInit*)config; + // Create a new offer. -// Success or failure will be reported via RTCSessionDescriptonDelegate. -- (void)createOfferWithDelegate:(id<RTCSessionDescriptonDelegate>)delegate +// Success or failure will be reported via RTCSessionDescriptionDelegate. +- (void)createOfferWithDelegate:(id<RTCSessionDescriptionDelegate>)delegate constraints:(RTCMediaConstraints *)constraints; // Create an answer to an offer. -// Success or failure will be reported via RTCSessionDescriptonDelegate. -- (void)createAnswerWithDelegate:(id<RTCSessionDescriptonDelegate>)delegate +// Success or failure will be reported via RTCSessionDescriptionDelegate. +- (void)createAnswerWithDelegate:(id<RTCSessionDescriptionDelegate>)delegate constraints:(RTCMediaConstraints *)constraints; // Sets the local session description. -// Success or failure will be reported via RTCSessionDescriptonDelegate. +// Success or failure will be reported via RTCSessionDescriptionDelegate. - (void) - setLocalDescriptionWithDelegate:(id<RTCSessionDescriptonDelegate>)delegate + setLocalDescriptionWithDelegate:(id<RTCSessionDescriptionDelegate>)delegate sessionDescription:(RTCSessionDescription *)sdp; // Sets the remote session description. -// Success or failure will be reported via RTCSessionDescriptonDelegate. +// Success or failure will be reported via RTCSessionDescriptionDelegate. - (void) - setRemoteDescriptionWithDelegate:(id<RTCSessionDescriptonDelegate>)delegate + setRemoteDescriptionWithDelegate:(id<RTCSessionDescriptionDelegate>)delegate sessionDescription:(RTCSessionDescription *)sdp; // Restarts or updates the ICE Agent process of gathering local candidates @@ -99,7 +109,12 @@ // Terminates all media and closes the transport. - (void)close; -// TODO(hughv): Implement GetStats. +// Gets statistics for the media track. If |mediaStreamTrack| is nil statistics +// are gathered for all tracks. +// Statistics information will be reported via RTCStatsDelegate. +- (BOOL)getStatsWithDelegate:(id<RTCStatsDelegate>)delegate + mediaStreamTrack:(RTCMediaStreamTrack*)mediaStreamTrack + statsOutputLevel:(RTCStatsOutputLevel)statsOutputLevel; #ifndef DOXYGEN_SHOULD_SKIP_THIS // Disallow init and don't add to documentation diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCPeerConnectionDelegate.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCPeerConnectionDelegate.h index b3bb881da68..4b177d504fb 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCPeerConnectionDelegate.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCPeerConnectionDelegate.h @@ -29,6 +29,7 @@ #import "RTCTypes.h" +@class RTCDataChannel; @class RTCICECandidate; @class RTCMediaStream; @class RTCPeerConnection; @@ -52,7 +53,7 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection removedStream:(RTCMediaStream *)stream; -// Triggered when renegotation is needed, for example the ICE has restarted. +// Triggered when renegotiation is needed, for example the ICE has restarted. - (void)peerConnectionOnRenegotiationNeeded:(RTCPeerConnection *)peerConnection; // Called any time the ICEConnectionState changes. @@ -67,4 +68,8 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection gotICECandidate:(RTCICECandidate *)candidate; +// New data channel has been opened. +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didOpenDataChannel:(RTCDataChannel*)dataChannel; + @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCSessionDescriptonDelegate.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCSessionDescriptionDelegate.h index 409aaee5d63..ecdc581f5e4 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCSessionDescriptonDelegate.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCSessionDescriptionDelegate.h @@ -33,9 +33,9 @@ extern NSString* const kRTCSessionDescriptionDelegateErrorDomain; extern int const kRTCSessionDescriptionDelegateErrorCode; -// RTCSessionDescriptonDelegate is a protocol for listening to callback messages -// when RTCSessionDescriptions are created or set. -@protocol RTCSessionDescriptonDelegate<NSObject> +// RTCSessionDescriptionDelegate is a protocol for listening to callback +// messages when RTCSessionDescriptions are created or set. +@protocol RTCSessionDescriptionDelegate<NSObject> // Called when creating a session. - (void)peerConnection:(RTCPeerConnection *)peerConnection diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCStatsDelegate.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCStatsDelegate.h new file mode 100644 index 00000000000..3de3dfe9618 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCStatsDelegate.h @@ -0,0 +1,39 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> + +@class RTCPeerConnection; + +// RTCSessionDescriptionDelegate is a protocol for receiving statistic +// reports from RTCPeerConnection. +@protocol RTCStatsDelegate<NSObject> + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didGetStats:(NSArray*)stats; // NSArray of RTCStatsReport*. + +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/plus/presencepushtask.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCStatsReport.h index b9c8038b36a..784ce67410a 100644 --- a/chromium/third_party/libjingle/source/talk/examples/plus/presencepushtask.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCStatsReport.h @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2005, Google Inc. + * Copyright 2014, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,29 +25,21 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _PRESENCEPUSHTASK_H_ -#define _PRESENCEPUSHTASK_H_ +#import <Foundation/Foundation.h> -#include "talk/xmpp/xmppengine.h" -#include "talk/xmpp/xmpptask.h" -#include "talk/base/sigslot.h" -#include "talk/app/status.h" +// ObjectiveC friendly wrapper around a StatsReport object. +// See talk/app/webrtc/statsypes.h +@interface RTCStatsReport : NSObject -namespace buzz { +@property(nonatomic, readonly) NSString* reportId; +@property(nonatomic, readonly) NSString* type; +@property(nonatomic, readonly) CFTimeInterval timestamp; +@property(nonatomic, readonly) NSArray* values; // NSArray of RTCPair*. -class PresencePushTask : public XmppTask { +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Disallow init and don't add to documentation +- (id)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ -public: - PresencePushTask(Task * parent) : XmppTask(parent, XmppEngine::HL_TYPE) {} - virtual int ProcessStart(); - sigslot::signal1<const Status &>SignalStatusUpdate; - sigslot::signal1<const XmlElement &> SignalStatusError; - -protected: - virtual bool HandleStanza(const XmlElement * stanza); -}; - - -} - -#endif +@end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCTypes.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCTypes.h index 8ff8bf48593..2a55200a6ed 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCTypes.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCTypes.h @@ -55,6 +55,12 @@ typedef enum { RTCSignalingClosed, } RTCSignalingState; +// RTCStatsOutputLevel correspond to webrtc::StatsOutputLevel +typedef enum { + RTCStatsOutputLevelStandard, + RTCStatsOutputLevelDebug, +} RTCStatsOutputLevel; + // RTCSourceState corresponds to the states in webrtc::SourceState. typedef enum { RTCSourceStateInitializing, diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCVideoRenderer.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCVideoRenderer.h index cc7ba718476..f78746c2fe8 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCVideoRenderer.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/public/RTCVideoRenderer.h @@ -26,27 +26,45 @@ */ #import <Foundation/Foundation.h> +#if TARGET_OS_IPHONE +#import <UIKit/UIKit.h> +#endif -@protocol RTCVideoRendererDelegate; -struct CGRect; +@class RTCI420Frame; +@class RTCVideoRenderer; + +// RTCVideoRendererDelegate is a protocol for an object that must be +// implemented to get messages when rendering. +@protocol RTCVideoRendererDelegate<NSObject> + +// The size of the frame. +- (void)renderer:(RTCVideoRenderer*)renderer didSetSize:(CGSize)size; + +// The frame to be displayed. +- (void)renderer:(RTCVideoRenderer*)renderer + didReceiveFrame:(RTCI420Frame*)frame; + +@end // Interface for rendering VideoFrames from a VideoTrack @interface RTCVideoRenderer : NSObject -@property(nonatomic, strong) id<RTCVideoRendererDelegate> delegate; - -// A convenience method to create a renderer and window and render frames into -// that window. -+ (RTCVideoRenderer *)videoRenderGUIWithFrame:(CGRect)frame; +@property(nonatomic, weak) id<RTCVideoRendererDelegate> delegate; // Initialize the renderer. Requires a delegate which does the actual drawing // of frames. -- (id)initWithDelegate:(id<RTCVideoRendererDelegate>)delegate; +- (instancetype)initWithDelegate:(id<RTCVideoRendererDelegate>)delegate; + +#if TARGET_OS_IPHONE +// DEPRECATED. See https://code.google.com/p/webrtc/issues/detail?id=3341 for +// details. +- (instancetype)initWithView:(UIView*)view; +#endif #ifndef DOXYGEN_SHOULD_SKIP_THIS // Disallow init and don't add to documentation -- (id)init __attribute__( - (unavailable("init is not a supported initializer for this class."))); +- (id)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); #endif /* DOXYGEN_SHOULD_SKIP_THIS */ @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/Info.plist b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/Info.plist index 0b1583e6ea5..c2fb0617f33 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/Info.plist +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/Info.plist @@ -18,21 +18,7 @@ <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> - <key>CFBundleSignature</key> - <string>????</string> <key>CFBundleVersion</key> <string>1.0</string> - <key>LSRequiresIPhoneOS</key> - <true/> - <key>UIRequiredDeviceCapabilities</key> - <array> - <string>armv7</string> - </array> - <key>UISupportedInterfaceOrientations</key> - <array> - <string>UIInterfaceOrientationPortrait</string> - <string>UIInterfaceOrientationLandscapeLeft</string> - <string>UIInterfaceOrientationLandscapeRight</string> - </array> </dict> </plist> diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.h index db97816fc90..8bbf9825115 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.h @@ -27,11 +27,14 @@ #import <Foundation/Foundation.h> +#import "RTCDataChannel.h" #import "RTCPeerConnectionDelegate.h" // Observer of PeerConnection events, used by RTCPeerConnectionTest to check // expectations. -@interface RTCPeerConnectionSyncObserver : NSObject<RTCPeerConnectionDelegate> +@interface RTCPeerConnectionSyncObserver + : NSObject<RTCPeerConnectionDelegate, RTCDataChannelDelegate> +@property(nonatomic) RTCDataChannel* dataChannel; // TODO(hughv): Add support for RTCVideoRendererDelegate when Video is enabled. // Transfer received ICE candidates to the caller. @@ -46,6 +49,9 @@ - (void)expectICECandidates:(int)count; - (void)expectICEConnectionChange:(RTCICEConnectionState)state; - (void)expectICEGatheringChange:(RTCICEGatheringState)state; +- (void)expectDataChannel:(NSString*)label; +- (void)expectStateChange:(RTCDataChannelState)state; +- (void)expectMessage:(NSData*)message isBinary:(BOOL)isBinary; // Wait until all registered expectations above have been observed. - (void)waitForAllExpectationsToBeSatisfied; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m index 0f33bac5cc6..c3f898a2945 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m @@ -35,13 +35,16 @@ @implementation RTCPeerConnectionSyncObserver { int _expectedErrors; - NSMutableArray *_expectedSignalingChanges; - NSMutableArray *_expectedAddStreamLabels; - NSMutableArray *_expectedRemoveStreamLabels; + NSMutableArray* _expectedSignalingChanges; + NSMutableArray* _expectedAddStreamLabels; + NSMutableArray* _expectedRemoveStreamLabels; int _expectedICECandidates; - NSMutableArray *_receivedICECandidates; - NSMutableArray *_expectedICEConnectionChanges; - NSMutableArray *_expectedICEGatheringChanges; + NSMutableArray* _receivedICECandidates; + NSMutableArray* _expectedICEConnectionChanges; + NSMutableArray* _expectedICEGatheringChanges; + NSMutableArray* _expectedDataChannels; + NSMutableArray* _expectedStateChanges; + NSMutableArray* _expectedMessages; } - (id)init { @@ -54,36 +57,41 @@ _receivedICECandidates = [NSMutableArray array]; _expectedICEConnectionChanges = [NSMutableArray array]; _expectedICEGatheringChanges = [NSMutableArray array]; + _expectedDataChannels = [NSMutableArray array]; + _expectedMessages = [NSMutableArray array]; + _expectedStateChanges = [NSMutableArray array]; } return self; } -- (int)popFirstElementAsInt:(NSMutableArray *)array { +- (int)popFirstElementAsInt:(NSMutableArray*)array { NSAssert([array count] > 0, @"Empty array"); - NSNumber *boxedState = [array objectAtIndex:0]; + NSNumber* boxedState = [array objectAtIndex:0]; [array removeObjectAtIndex:0]; return [boxedState intValue]; } -- (NSString *)popFirstElementAsNSString:(NSMutableArray *)array { +- (NSString*)popFirstElementAsNSString:(NSMutableArray*)array { NSAssert([array count] > 0, @"Empty expectation array"); - NSString *string = [array objectAtIndex:0]; + NSString* string = [array objectAtIndex:0]; [array removeObjectAtIndex:0]; return string; } - (BOOL)areAllExpectationsSatisfied { return _expectedICECandidates <= 0 && // See comment in gotICECandidate. - _expectedErrors == 0 && - [_expectedSignalingChanges count] == 0 && + _expectedErrors == 0 && [_expectedSignalingChanges count] == 0 && [_expectedICEConnectionChanges count] == 0 && [_expectedICEGatheringChanges count] == 0 && [_expectedAddStreamLabels count] == 0 && - [_expectedRemoveStreamLabels count] == 0; + [_expectedRemoveStreamLabels count] == 0 && + [_expectedDataChannels count] == 0 && + [_expectedStateChanges count] == 0 && + [_expectedMessages count] == 0; // TODO(hughv): Test video state here too. } -- (NSArray *)releaseReceivedICECandidates { +- (NSArray*)releaseReceivedICECandidates { NSArray* ret = _receivedICECandidates; _receivedICECandidates = [NSMutableArray array]; return ret; @@ -97,11 +105,11 @@ [_expectedSignalingChanges addObject:@((int)state)]; } -- (void)expectAddStream:(NSString *)label { +- (void)expectAddStream:(NSString*)label { [_expectedAddStreamLabels addObject:label]; } -- (void)expectRemoveStream:(NSString *)label { +- (void)expectRemoveStream:(NSString*)label { [_expectedRemoveStreamLabels addObject:label]; } @@ -117,6 +125,20 @@ [_expectedICEGatheringChanges addObject:@((int)state)]; } +- (void)expectDataChannel:(NSString*)label { + [_expectedDataChannels addObject:label]; +} + +- (void)expectStateChange:(RTCDataChannelState)state { + [_expectedStateChanges addObject:@(state)]; +} + +- (void)expectMessage:(NSData*)message isBinary:(BOOL)isBinary { + RTCDataBuffer* buffer = [[RTCDataBuffer alloc] initWithData:message + isBinary:isBinary]; + [_expectedMessages addObject:buffer]; +} + - (void)waitForAllExpectationsToBeSatisfied { // TODO (fischman): Revisit. Keeping in sync with the Java version, but // polling is not optimal. @@ -129,39 +151,41 @@ #pragma mark - RTCPeerConnectionDelegate methods -- (void)peerConnectionOnError:(RTCPeerConnection *)peerConnection { +- (void)peerConnectionOnError:(RTCPeerConnection*)peerConnection { NSLog(@"RTCPeerConnectionDelegate::onError"); NSAssert(--_expectedErrors >= 0, @"Unexpected error"); } -- (void)peerConnection:(RTCPeerConnection *)peerConnection +- (void)peerConnection:(RTCPeerConnection*)peerConnection signalingStateChanged:(RTCSignalingState)stateChanged { int expectedState = [self popFirstElementAsInt:_expectedSignalingChanges]; - NSString *message = [NSString stringWithFormat: @"RTCPeerConnectionDelegate::" - @"onSignalingStateChange [%d] expected[%d]", stateChanged, expectedState]; - NSAssert(expectedState == (int) stateChanged, message); + NSString* message = + [NSString stringWithFormat:@"RTCPeerConnectionDelegate::" + @"onSignalingStateChange [%d] expected[%d]", + stateChanged, + expectedState]; + NSAssert(expectedState == (int)stateChanged, message); } -- (void)peerConnection:(RTCPeerConnection *)peerConnection - addedStream:(RTCMediaStream *)stream { - NSString *expectedLabel = +- (void)peerConnection:(RTCPeerConnection*)peerConnection + addedStream:(RTCMediaStream*)stream { + NSString* expectedLabel = [self popFirstElementAsNSString:_expectedAddStreamLabels]; NSAssert([expectedLabel isEqual:stream.label], @"Stream not expected"); } -- (void)peerConnection:(RTCPeerConnection *)peerConnection - removedStream:(RTCMediaStream *)stream { - NSString *expectedLabel = +- (void)peerConnection:(RTCPeerConnection*)peerConnection + removedStream:(RTCMediaStream*)stream { + NSString* expectedLabel = [self popFirstElementAsNSString:_expectedRemoveStreamLabels]; NSAssert([expectedLabel isEqual:stream.label], @"Stream not expected"); } -- (void)peerConnectionOnRenegotiationNeeded: - (RTCPeerConnection *)peerConnection { +- (void)peerConnectionOnRenegotiationNeeded:(RTCPeerConnection*)peerConnection { } -- (void)peerConnection:(RTCPeerConnection *)peerConnection - gotICECandidate:(RTCICECandidate *)candidate { +- (void)peerConnection:(RTCPeerConnection*)peerConnection + gotICECandidate:(RTCICECandidate*)candidate { --_expectedICECandidates; // We don't assert expectedICECandidates >= 0 because it's hard to know // how many to expect, in general. We only use expectICECandidates to @@ -169,7 +193,7 @@ [_receivedICECandidates addObject:candidate]; } -- (void)peerConnection:(RTCPeerConnection *)peerConnection +- (void)peerConnection:(RTCPeerConnection*)peerConnection iceGatheringChanged:(RTCICEGatheringState)newState { // It's fine to get a variable number of GATHERING messages before // COMPLETE fires (depending on how long the test runs) so we don't assert @@ -181,10 +205,46 @@ NSAssert(expectedState == (int)newState, @"Empty expectation array"); } -- (void)peerConnection:(RTCPeerConnection *)peerConnection - iceConnectionChanged:(RTCICEConnectionState)newState { +- (void)peerConnection:(RTCPeerConnection*)peerConnection + iceConnectionChanged:(RTCICEConnectionState)newState { + // See TODO(fischman) in RTCPeerConnectionTest.mm about Completed. + if (newState == RTCICEConnectionCompleted) + return; int expectedState = [self popFirstElementAsInt:_expectedICEConnectionChanges]; NSAssert(expectedState == (int)newState, @"Empty expectation array"); } +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didOpenDataChannel:(RTCDataChannel*)dataChannel { + NSString* expectedLabel = + [self popFirstElementAsNSString:_expectedDataChannels]; + NSAssert([expectedLabel isEqual:dataChannel.label], + @"Data channel not expected"); + self.dataChannel = dataChannel; + dataChannel.delegate = self; + NSAssert(kRTCDataChannelStateConnecting == dataChannel.state, + @"Unexpected state"); +} + +#pragma mark - RTCDataChannelDelegate + +- (void)channelDidChangeState:(RTCDataChannel*)channel { + NSAssert([_expectedStateChanges count] > 0, + @"Unexpected state change"); + int expectedState = [self popFirstElementAsInt:_expectedStateChanges]; + NSAssert(expectedState == channel.state, @"Channel state should match"); +} + +- (void)channel:(RTCDataChannel*)channel + didReceiveMessageWithBuffer:(RTCDataBuffer*)buffer { + NSAssert([_expectedMessages count] > 0, + @"Unexpected message received"); + RTCDataBuffer* expectedBuffer = [_expectedMessages objectAtIndex:0]; + NSAssert(expectedBuffer.isBinary == buffer.isBinary, + @"Buffer isBinary should match"); + NSAssert([expectedBuffer.data isEqual:buffer.data], + @"Buffer data should match"); + [_expectedMessages removeObjectAtIndex:0]; +} + @end diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm index 0ce8822fd1e..7a178f39fcd 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm @@ -30,6 +30,7 @@ #import "RTCICEServer.h" #import "RTCMediaConstraints.h" #import "RTCMediaStream.h" +#import "RTCPair.h" #import "RTCPeerConnection.h" #import "RTCPeerConnectionFactory.h" #import "RTCPeerConnectionSyncObserver.h" @@ -39,6 +40,7 @@ #import "RTCVideoTrack.h" #include "talk/base/gunit.h" +#include "talk/base/ssladapter.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -47,123 +49,126 @@ @interface RTCPeerConnectionTest : NSObject // Returns whether the two sessions are of the same type. -+ (BOOL)isSession:(RTCSessionDescription *)session1 - ofSameTypeAsSession:(RTCSessionDescription *)session2; ++ (BOOL)isSession:(RTCSessionDescription*)session1 + ofSameTypeAsSession:(RTCSessionDescription*)session2; // Create and add tracks to pc, with the given source, label, and IDs -- (RTCMediaStream *) - addTracksToPeerConnection:(RTCPeerConnection *)pc - withFactory:(RTCPeerConnectionFactory *)factory - videoSource:(RTCVideoSource *)videoSource - streamLabel:(NSString *)streamLabel - videoTrackID:(NSString *)videoTrackID - audioTrackID:(NSString *)audioTrackID; +- (RTCMediaStream*)addTracksToPeerConnection:(RTCPeerConnection*)pc + withFactory:(RTCPeerConnectionFactory*)factory + videoSource:(RTCVideoSource*)videoSource + streamLabel:(NSString*)streamLabel + videoTrackID:(NSString*)videoTrackID + audioTrackID:(NSString*)audioTrackID; -- (void)testCompleteSession; +- (void)testCompleteSessionWithFactory:(RTCPeerConnectionFactory*)factory; @end @implementation RTCPeerConnectionTest -+ (BOOL)isSession:(RTCSessionDescription *)session1 - ofSameTypeAsSession:(RTCSessionDescription *)session2 { ++ (BOOL)isSession:(RTCSessionDescription*)session1 + ofSameTypeAsSession:(RTCSessionDescription*)session2 { return [session1.type isEqual:session2.type]; } -- (RTCMediaStream *) - addTracksToPeerConnection:(RTCPeerConnection *)pc - withFactory:(RTCPeerConnectionFactory *)factory - videoSource:(RTCVideoSource *)videoSource - streamLabel:(NSString *)streamLabel - videoTrackID:(NSString *)videoTrackID - audioTrackID:(NSString *)audioTrackID { - RTCMediaStream *localMediaStream = [factory mediaStreamWithLabel:streamLabel]; - RTCVideoTrack *videoTrack = +- (RTCMediaStream*)addTracksToPeerConnection:(RTCPeerConnection*)pc + withFactory:(RTCPeerConnectionFactory*)factory + videoSource:(RTCVideoSource*)videoSource + streamLabel:(NSString*)streamLabel + videoTrackID:(NSString*)videoTrackID + audioTrackID:(NSString*)audioTrackID { + RTCMediaStream* localMediaStream = [factory mediaStreamWithLabel:streamLabel]; + RTCVideoTrack* videoTrack = [factory videoTrackWithID:videoTrackID source:videoSource]; - RTCVideoRenderer *videoRenderer = + RTCVideoRenderer* videoRenderer = [[RTCVideoRenderer alloc] initWithDelegate:nil]; [videoTrack addRenderer:videoRenderer]; [localMediaStream addVideoTrack:videoTrack]; // Test that removal/re-add works. [localMediaStream removeVideoTrack:videoTrack]; [localMediaStream addVideoTrack:videoTrack]; - RTCAudioTrack *audioTrack = [factory audioTrackWithID:audioTrackID]; + RTCAudioTrack* audioTrack = [factory audioTrackWithID:audioTrackID]; [localMediaStream addAudioTrack:audioTrack]; - RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] init]; + RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc] init]; [pc addStream:localMediaStream constraints:constraints]; return localMediaStream; } -- (void)testCompleteSession { - RTCPeerConnectionFactory *factory = [[RTCPeerConnectionFactory alloc] init]; - NSString *stunURL = @"stun:stun.l.google.com:19302"; - RTCICEServer *stunServer = - [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:stunURL] - username:@"" - password:@""]; - NSArray *iceServers = @[stunServer]; - - RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] init]; - RTCPeerConnectionSyncObserver *offeringExpectations = +- (void)testCompleteSessionWithFactory:(RTCPeerConnectionFactory*)factory { + NSArray* mandatory = @[ + [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"], + [[RTCPair alloc] initWithKey:@"internalSctpDataChannels" value:@"true"], + ]; + RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc] init]; + RTCMediaConstraints* pcConstraints = + [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory + optionalConstraints:nil]; + + RTCPeerConnectionSyncObserver* offeringExpectations = [[RTCPeerConnectionSyncObserver alloc] init]; - RTCPeerConnection *pcOffer = - [factory peerConnectionWithICEServers:iceServers - constraints:constraints + RTCPeerConnection* pcOffer = + [factory peerConnectionWithICEServers:nil + constraints:pcConstraints delegate:offeringExpectations]; - RTCPeerConnectionSyncObserver *answeringExpectations = + RTCPeerConnectionSyncObserver* answeringExpectations = [[RTCPeerConnectionSyncObserver alloc] init]; - RTCPeerConnection *pcAnswer = - [factory peerConnectionWithICEServers:iceServers - constraints:constraints - delegate:answeringExpectations]; + RTCPeerConnection* pcAnswer = + [factory peerConnectionWithICEServers:nil + constraints:pcConstraints + delegate:answeringExpectations]; // TODO(hughv): Create video capturer - RTCVideoCapturer *capturer = nil; - RTCVideoSource *videoSource = + RTCVideoCapturer* capturer = nil; + RTCVideoSource* videoSource = [factory videoSourceWithCapturer:capturer constraints:constraints]; // Here and below, "oLMS" refers to offerer's local media stream, and "aLMS" // refers to the answerer's local media stream, with suffixes of "a0" and "v0" // for audio and video tracks, resp. These mirror chrome historical naming. - RTCMediaStream *oLMSUnused = - [self addTracksToPeerConnection:pcOffer - withFactory:factory - videoSource:videoSource - streamLabel:@"oLMS" - videoTrackID:@"oLMSv0" - audioTrackID:@"oLMSa0"]; - RTCSessionDescriptionSyncObserver *sdpObserver = + RTCMediaStream* oLMSUnused = [self addTracksToPeerConnection:pcOffer + withFactory:factory + videoSource:videoSource + streamLabel:@"oLMS" + videoTrackID:@"oLMSv0" + audioTrackID:@"oLMSa0"]; + + RTCDataChannel* offerDC = + [pcOffer createDataChannelWithLabel:@"offerDC" + config:[[RTCDataChannelInit alloc] init]]; + EXPECT_TRUE([offerDC.label isEqual:@"offerDC"]); + offerDC.delegate = offeringExpectations; + offeringExpectations.dataChannel = offerDC; + + RTCSessionDescriptionSyncObserver* sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init]; [pcOffer createOfferWithDelegate:sdpObserver constraints:constraints]; [sdpObserver wait]; EXPECT_TRUE(sdpObserver.success); - RTCSessionDescription *offerSDP = sdpObserver.sessionDescription; + RTCSessionDescription* offerSDP = sdpObserver.sessionDescription; EXPECT_EQ([@"offer" compare:offerSDP.type options:NSCaseInsensitiveSearch], NSOrderedSame); EXPECT_GT([offerSDP.description length], 0); sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init]; - [answeringExpectations - expectSignalingChange:RTCSignalingHaveRemoteOffer]; + [answeringExpectations expectSignalingChange:RTCSignalingHaveRemoteOffer]; [answeringExpectations expectAddStream:@"oLMS"]; [pcAnswer setRemoteDescriptionWithDelegate:sdpObserver sessionDescription:offerSDP]; [sdpObserver wait]; - RTCMediaStream *aLMSUnused = - [self addTracksToPeerConnection:pcAnswer - withFactory:factory - videoSource:videoSource - streamLabel:@"aLMS" - videoTrackID:@"aLMSv0" - audioTrackID:@"aLMSa0"]; + RTCMediaStream* aLMSUnused = [self addTracksToPeerConnection:pcAnswer + withFactory:factory + videoSource:videoSource + streamLabel:@"aLMS" + videoTrackID:@"aLMSv0" + audioTrackID:@"aLMSa0"]; sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init]; [pcAnswer createAnswerWithDelegate:sdpObserver constraints:constraints]; [sdpObserver wait]; EXPECT_TRUE(sdpObserver.success); - RTCSessionDescription *answerSDP = sdpObserver.sessionDescription; + RTCSessionDescription* answerSDP = sdpObserver.sessionDescription; EXPECT_EQ([@"answer" compare:answerSDP.type options:NSCaseInsensitiveSearch], NSOrderedSame); EXPECT_GT([answerSDP.description length], 0); @@ -187,9 +192,16 @@ [offeringExpectations expectICEConnectionChange:RTCICEConnectionChecking]; [offeringExpectations expectICEConnectionChange:RTCICEConnectionConnected]; + // TODO(fischman): figure out why this is flaky and re-introduce (and remove + // special-casing from the observer!). + // [offeringExpectations expectICEConnectionChange:RTCICEConnectionCompleted]; [answeringExpectations expectICEConnectionChange:RTCICEConnectionChecking]; [answeringExpectations expectICEConnectionChange:RTCICEConnectionConnected]; + [offeringExpectations expectStateChange:kRTCDataChannelStateOpen]; + [answeringExpectations expectDataChannel:@"offerDC"]; + [answeringExpectations expectStateChange:kRTCDataChannelStateOpen]; + [offeringExpectations expectICEGatheringChange:RTCICEGatheringComplete]; [answeringExpectations expectICEGatheringChange:RTCICEGatheringComplete]; @@ -206,31 +218,100 @@ EXPECT_TRUE([offerSDP.type isEqual:pcAnswer.remoteDescription.type]); EXPECT_TRUE([answerSDP.type isEqual:pcAnswer.localDescription.type]); - for (RTCICECandidate *candidate in - offeringExpectations.releaseReceivedICECandidates) { + for (RTCICECandidate* candidate in offeringExpectations + .releaseReceivedICECandidates) { [pcAnswer addICECandidate:candidate]; } - for (RTCICECandidate *candidate in - answeringExpectations.releaseReceivedICECandidates) { + for (RTCICECandidate* candidate in answeringExpectations + .releaseReceivedICECandidates) { [pcOffer addICECandidate:candidate]; } [offeringExpectations waitForAllExpectationsToBeSatisfied]; [answeringExpectations waitForAllExpectationsToBeSatisfied]; - // Let the audio feedback run for 10s to allow human testing and to ensure + EXPECT_EQ(pcOffer.signalingState, RTCSignalingStable); + EXPECT_EQ(pcAnswer.signalingState, RTCSignalingStable); + + // Test send and receive UTF-8 text + NSString* text = @"ä½ å¥½"; + NSData* textData = [text dataUsingEncoding:NSUTF8StringEncoding]; + RTCDataBuffer* buffer = + [[RTCDataBuffer alloc] initWithData:textData isBinary:NO]; + [answeringExpectations expectMessage:[textData copy] isBinary:NO]; + EXPECT_TRUE([offeringExpectations.dataChannel sendData:buffer]); + [answeringExpectations waitForAllExpectationsToBeSatisfied]; + + // Test send and receive binary data + const size_t byteLength = 5; + char bytes[byteLength] = {1, 2, 3, 4, 5}; + NSData* byteData = [NSData dataWithBytes:bytes length:byteLength]; + buffer = [[RTCDataBuffer alloc] initWithData:byteData isBinary:YES]; + [answeringExpectations expectMessage:[byteData copy] isBinary:YES]; + EXPECT_TRUE([offeringExpectations.dataChannel sendData:buffer]); + [answeringExpectations waitForAllExpectationsToBeSatisfied]; + + [offeringExpectations expectStateChange:kRTCDataChannelStateClosing]; + [answeringExpectations expectStateChange:kRTCDataChannelStateClosing]; + [offeringExpectations expectStateChange:kRTCDataChannelStateClosed]; + [answeringExpectations expectStateChange:kRTCDataChannelStateClosed]; + + [answeringExpectations.dataChannel close]; + [offeringExpectations.dataChannel close]; + + [offeringExpectations waitForAllExpectationsToBeSatisfied]; + [answeringExpectations waitForAllExpectationsToBeSatisfied]; + // Don't need to listen to further state changes. + // TODO(tkchin): figure out why Closed->Closing without this. + offeringExpectations.dataChannel.delegate = nil; + answeringExpectations.dataChannel.delegate = nil; + + // Let the audio feedback run for 2s to allow human testing and to ensure // things stabilize. TODO(fischman): replace seconds with # of video frames, // when we have video flowing. [[NSRunLoop currentRunLoop] - runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]; + runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; + + [offeringExpectations expectICEConnectionChange:RTCICEConnectionClosed]; + [answeringExpectations expectICEConnectionChange:RTCICEConnectionClosed]; + [offeringExpectations expectSignalingChange:RTCSignalingClosed]; + [answeringExpectations expectSignalingChange:RTCSignalingClosed]; + + [pcOffer close]; + [pcAnswer close]; - // TODO(hughv): Implement orderly shutdown. + [offeringExpectations waitForAllExpectationsToBeSatisfied]; + [answeringExpectations waitForAllExpectationsToBeSatisfied]; + + capturer = nil; + videoSource = nil; + pcOffer = nil; + pcAnswer = nil; + // TODO(fischman): be stricter about shutdown checks; ensure thread + // counts return to where they were before the test kicked off, and + // that all objects have in fact shut down. } @end - +// TODO(fischman): move {Initialize,Cleanup}SSL into alloc/dealloc of +// RTCPeerConnectionTest and avoid the appearance of RTCPeerConnectionTest being +// a TestBase since it's not. TEST(RTCPeerConnectionTest, SessionTest) { - RTCPeerConnectionTest *pcTest = [[RTCPeerConnectionTest alloc] init]; - [pcTest testCompleteSession]; + @autoreleasepool { + talk_base::InitializeSSL(); + // Since |factory| will own the signaling & worker threads, it's important + // that it outlive the created PeerConnections since they self-delete on the + // signaling thread, and if |factory| is freed first then a last refcount on + // the factory will expire during this teardown, causing the signaling + // thread to try to Join() with itself. This is a hack to ensure that the + // factory outlives RTCPeerConnection:dealloc. + // See https://code.google.com/p/webrtc/issues/detail?id=3100. + RTCPeerConnectionFactory* factory = [[RTCPeerConnectionFactory alloc] init]; + @autoreleasepool { + RTCPeerConnectionTest* pcTest = [[RTCPeerConnectionTest alloc] init]; + [pcTest testCompleteSessionWithFactory:factory]; + } + talk_base::CleanupSSL(); + } } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCSessionDescriptionSyncObserver.h b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCSessionDescriptionSyncObserver.h index 18d7902886b..8631f017047 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCSessionDescriptionSyncObserver.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCSessionDescriptionSyncObserver.h @@ -27,14 +27,14 @@ #import <Foundation/Foundation.h> -#import "RTCSessionDescriptonDelegate.h" +#import "RTCSessionDescriptionDelegate.h" @class RTCSessionDescription; // Observer of SDP-related events, used by RTCPeerConnectionTest to check // expectations. @interface RTCSessionDescriptionSyncObserver : NSObject< - RTCSessionDescriptonDelegate> + RTCSessionDescriptionDelegate> // Error string. May be nil. @property(atomic, copy) NSString *error; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCSessionDescriptionSyncObserver.m b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCSessionDescriptionSyncObserver.m index 85a4482b89e..6d782536c6c 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCSessionDescriptionSyncObserver.m +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/RTCSessionDescriptionSyncObserver.m @@ -33,10 +33,10 @@ #import "RTCSessionDescription.h" -@interface RTCSessionDescriptionSyncObserver() +@interface RTCSessionDescriptionSyncObserver () // CondVar used to wait for, and signal arrival of, an SDP-related callback. -@property(nonatomic, strong) NSCondition *condition; +@property(nonatomic, strong) NSCondition* condition; // Whether an SDP-related callback has fired; cleared before wait returns. @property(atomic, assign) BOOL signaled; @@ -71,10 +71,10 @@ [self.condition unlock]; } -#pragma mark - RTCSessionDescriptonDelegate methods -- (void)peerConnection:(RTCPeerConnection *)peerConnection - didCreateSessionDescription:(RTCSessionDescription *)sdp - error:(NSError *)error { +#pragma mark - RTCSessionDescriptionDelegate methods +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didCreateSessionDescription:(RTCSessionDescription*)sdp + error:(NSError*)error { [self.condition lock]; if (error) { self.success = NO; @@ -87,8 +87,8 @@ [self.condition unlock]; } -- (void)peerConnection:(RTCPeerConnection *)peerConnection - didSetSessionDescriptionWithError:(NSError *)error { +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didSetSessionDescriptionWithError:(NSError*)error { [self.condition lock]; if (error) { self.success = NO; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/mac/main.mm b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/mac/main.mm index 3fb24f37b23..4995b7f3ac2 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/mac/main.mm +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objctests/mac/main.mm @@ -27,7 +27,11 @@ #include "talk/base/gunit.h" -int main(int argc, char *argv[]) { +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.cc index e10e8fc953a..0839977f17a 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.cc @@ -32,10 +32,12 @@ #include "talk/app/webrtc/dtmfsender.h" #include "talk/app/webrtc/jsepicecandidate.h" #include "talk/app/webrtc/jsepsessiondescription.h" +#include "talk/app/webrtc/mediaconstraintsinterface.h" #include "talk/app/webrtc/mediastreamhandler.h" #include "talk/app/webrtc/streamcollection.h" #include "talk/base/logging.h" #include "talk/base/stringencode.h" +#include "talk/p2p/client/basicportallocator.h" #include "talk/session/media/channelmanager.h" namespace { @@ -70,17 +72,6 @@ enum { MSG_SET_SESSIONDESCRIPTION_SUCCESS = 0, MSG_SET_SESSIONDESCRIPTION_FAILED, MSG_GETSTATS, - MSG_ICECONNECTIONCHANGE, - MSG_ICEGATHERINGCHANGE, - MSG_ICECANDIDATE, - MSG_ICECOMPLETE, -}; - -struct CandidateMsg : public talk_base::MessageData { - explicit CandidateMsg(const webrtc::JsepIceCandidate* candidate) - : candidate(candidate) { - } - talk_base::scoped_ptr<const webrtc::JsepIceCandidate> candidate; }; struct SetSessionDescriptionMsg : public talk_base::MessageData { @@ -276,13 +267,6 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration, server.password, turn_transport_type, secure)); - // STUN functionality is part of TURN. - // Note: If there is only TURNS is supplied as part of configuration, - // we will have problem in fetching server reflexive candidate, as - // currently we don't have support of TCP/TLS in stunport.cc. - // In that case we should fetch server reflexive addess from - // TURN allocate response. - stun_config->push_back(StunConfiguration(address, port)); break; } case INVALID: @@ -317,6 +301,7 @@ namespace webrtc { PeerConnection::PeerConnection(PeerConnectionFactory* factory) : factory_(factory), observer_(NULL), + uma_observer_(NULL), signaling_state_(kStable), ice_state_(kIceNew), ice_connection_state_(kIceConnectionNew), @@ -331,22 +316,23 @@ PeerConnection::~PeerConnection() { } bool PeerConnection::Initialize( - const PeerConnectionInterface::IceServers& configuration, + const PeerConnectionInterface::RTCConfiguration& configuration, const MediaConstraintsInterface* constraints, PortAllocatorFactoryInterface* allocator_factory, DTLSIdentityServiceInterface* dtls_identity_service, PeerConnectionObserver* observer) { std::vector<PortAllocatorFactoryInterface::StunConfiguration> stun_config; std::vector<PortAllocatorFactoryInterface::TurnConfiguration> turn_config; - if (!ParseIceServers(configuration, &stun_config, &turn_config)) { + if (!ParseIceServers(configuration.servers, &stun_config, &turn_config)) { return false; } - return DoInitialize(stun_config, turn_config, constraints, + return DoInitialize(configuration.type, stun_config, turn_config, constraints, allocator_factory, dtls_identity_service, observer); } bool PeerConnection::DoInitialize( + IceTransportsType type, const StunConfigurations& stun_config, const TurnConfigurations& turn_config, const MediaConstraintsInterface* constraints, @@ -359,11 +345,21 @@ bool PeerConnection::DoInitialize( observer_ = observer; port_allocator_.reset( allocator_factory->CreatePortAllocator(stun_config, turn_config)); + // To handle both internal and externally created port allocator, we will - // enable BUNDLE here. Also enabling TURN and disable legacy relay service. - port_allocator_->set_flags(cricket::PORTALLOCATOR_ENABLE_BUNDLE | - cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | - cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET); + // enable BUNDLE here. + int portallocator_flags = cricket::PORTALLOCATOR_ENABLE_BUNDLE | + cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | + cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET; + bool value; + if (FindConstraint( + constraints, + MediaConstraintsInterface::kEnableIPv6, + &value, NULL) && value) { + portallocator_flags |= cricket::PORTALLOCATOR_ENABLE_IPV6; + } + + port_allocator_->set_flags(portallocator_flags); // No step delay is used while allocating ports. port_allocator_->set_step_delay(cricket::kMinimumStepDelay); @@ -381,7 +377,7 @@ bool PeerConnection::DoInitialize( // Initialize the WebRtcSession. It creates transport channels etc. if (!session_->Initialize(factory_->options(), constraints, - dtls_identity_service)) + dtls_identity_service, type)) return false; // Register PeerConnection as receiver of local ice candidates. @@ -448,13 +444,14 @@ talk_base::scoped_refptr<DtmfSenderInterface> PeerConnection::CreateDtmfSender( } bool PeerConnection::GetStats(StatsObserver* observer, - MediaStreamTrackInterface* track) { + MediaStreamTrackInterface* track, + StatsOutputLevel level) { if (!VERIFY(observer != NULL)) { LOG(LS_ERROR) << "GetStats - observer is NULL."; return false; } - stats_.UpdateStats(); + stats_.UpdateStats(level); talk_base::scoped_ptr<GetStatsMsg> msg(new GetStatsMsg(observer)); if (!stats_.GetStats(track, &(msg->reports))) { return false; @@ -485,12 +482,22 @@ talk_base::scoped_refptr<DataChannelInterface> PeerConnection::CreateDataChannel( const std::string& label, const DataChannelInit* config) { + bool first_datachannel = !mediastream_signaling_->HasDataChannels(); + + talk_base::scoped_ptr<InternalDataChannelInit> internal_config; + if (config) { + internal_config.reset(new InternalDataChannelInit(*config)); + } talk_base::scoped_refptr<DataChannelInterface> channel( - session_->CreateDataChannel(label, config)); + session_->CreateDataChannel(label, internal_config.get())); if (!channel.get()) return NULL; - observer_->OnRenegotiationNeeded(); + // Trigger the onRenegotiationNeeded event for every new RTP DataChannel, or + // the first SCTP DataChannel. + if (session_->data_channel_type() == cricket::DCT_RTP || first_datachannel) { + observer_->OnRenegotiationNeeded(); + } return DataChannelProxy::Create(signaling_thread(), channel.get()); } @@ -527,7 +534,7 @@ void PeerConnection::SetLocalDescription( } // Update stats here so that we have the most recent stats for tracks and // streams that might be removed by updating the session description. - stats_.UpdateStats(); + stats_.UpdateStats(kStatsOutputLevelStandard); std::string error; if (!session_->SetLocalDescription(desc, &error)) { PostSetSessionDescriptionFailure(observer, error); @@ -550,7 +557,7 @@ void PeerConnection::SetRemoteDescription( } // Update stats here so that we have the most recent stats for tracks and // streams that might be removed by updating the session description. - stats_.UpdateStats(); + stats_.UpdateStats(kStatsOutputLevelStandard); std::string error; if (!session_->SetRemoteDescription(desc, &error)) { PostSetSessionDescriptionFailure(observer, error); @@ -570,16 +577,67 @@ void PeerConnection::PostSetSessionDescriptionFailure( bool PeerConnection::UpdateIce(const IceServers& configuration, const MediaConstraintsInterface* constraints) { - // TODO(ronghuawu): Implement UpdateIce. - LOG(LS_ERROR) << "UpdateIce is not implemented."; return false; } +bool PeerConnection::UpdateIce(const RTCConfiguration& config) { + if (port_allocator_) { + std::vector<PortAllocatorFactoryInterface::StunConfiguration> stuns; + std::vector<PortAllocatorFactoryInterface::TurnConfiguration> turns; + if (!ParseIceServers(config.servers, &stuns, &turns)) { + return false; + } + + std::vector<talk_base::SocketAddress> stun_hosts; + typedef std::vector<StunConfiguration>::const_iterator StunIt; + for (StunIt stun_it = stuns.begin(); stun_it != stuns.end(); ++stun_it) { + stun_hosts.push_back(stun_it->server); + } + + talk_base::SocketAddress stun_addr; + if (!stun_hosts.empty()) { + stun_addr = stun_hosts.front(); + LOG(LS_INFO) << "UpdateIce: StunServer Address: " << stun_addr.ToString(); + } + + for (size_t i = 0; i < turns.size(); ++i) { + cricket::RelayCredentials credentials(turns[i].username, + turns[i].password); + cricket::RelayServerConfig relay_server(cricket::RELAY_TURN); + cricket::ProtocolType protocol; + if (cricket::StringToProto(turns[i].transport_type.c_str(), &protocol)) { + relay_server.ports.push_back(cricket::ProtocolAddress( + turns[i].server, protocol, turns[i].secure)); + relay_server.credentials = credentials; + LOG(LS_INFO) << "UpdateIce: TurnServer Address: " + << turns[i].server.ToString(); + } else { + LOG(LS_WARNING) << "Ignoring TURN server " << turns[i].server << ". " + << "Reason= Incorrect " << turns[i].transport_type + << " transport parameter."; + } + } + } + return session_->UpdateIce(config.type); +} + bool PeerConnection::AddIceCandidate( const IceCandidateInterface* ice_candidate) { return session_->ProcessIceMessage(ice_candidate); } +void PeerConnection::RegisterUMAObserver(UMAObserver* observer) { + uma_observer_ = observer; + // Send information about IPv4/IPv6 status. + if (uma_observer_ && port_allocator_) { + if (port_allocator_->flags() & cricket::PORTALLOCATOR_ENABLE_IPV6) { + uma_observer_->IncrementCounter(kPeerConnection_IPv6); + } else { + uma_observer_->IncrementCounter(kPeerConnection_IPv4); + } + } +} + const SessionDescriptionInterface* PeerConnection::local_description() const { return session_->local_description(); } @@ -591,7 +649,7 @@ const SessionDescriptionInterface* PeerConnection::remote_description() const { void PeerConnection::Close() { // Update stats here so that we have the most recent stats for tracks and // streams before the channels are closed. - stats_.UpdateStats(); + stats_.UpdateStats(kStatsOutputLevelStandard); session_->Terminate(); } @@ -648,24 +706,6 @@ void PeerConnection::OnMessage(talk_base::Message* msg) { delete param; break; } - case MSG_ICECONNECTIONCHANGE: { - observer_->OnIceConnectionChange(ice_connection_state_); - break; - } - case MSG_ICEGATHERINGCHANGE: { - observer_->OnIceGatheringChange(ice_gathering_state_); - break; - } - case MSG_ICECANDIDATE: { - CandidateMsg* data = static_cast<CandidateMsg*>(msg->pdata); - observer_->OnIceCandidate(data->candidate.get()); - delete data; - break; - } - case MSG_ICECOMPLETE: { - observer_->OnIceComplete(); - break; - } default: ASSERT(false && "Not implemented"); break; @@ -714,6 +754,7 @@ void PeerConnection::OnAddLocalAudioTrack(MediaStreamInterface* stream, AudioTrackInterface* audio_track, uint32 ssrc) { stream_handler_container_->AddLocalAudioTrack(stream, audio_track, ssrc); + stats_.AddLocalAudioTrack(audio_track, ssrc); } void PeerConnection::OnAddLocalVideoTrack(MediaStreamInterface* stream, VideoTrackInterface* video_track, @@ -722,8 +763,10 @@ void PeerConnection::OnAddLocalVideoTrack(MediaStreamInterface* stream, } void PeerConnection::OnRemoveLocalAudioTrack(MediaStreamInterface* stream, - AudioTrackInterface* audio_track) { + AudioTrackInterface* audio_track, + uint32 ssrc) { stream_handler_container_->RemoveLocalTrack(stream, audio_track); + stats_.RemoveLocalAudioTrack(audio_track, ssrc); } void PeerConnection::OnRemoveLocalVideoTrack(MediaStreamInterface* stream, @@ -737,35 +780,29 @@ void PeerConnection::OnRemoveLocalStream(MediaStreamInterface* stream) { void PeerConnection::OnIceConnectionChange( PeerConnectionInterface::IceConnectionState new_state) { + ASSERT(signaling_thread()->IsCurrent()); ice_connection_state_ = new_state; - signaling_thread()->Post(this, MSG_ICECONNECTIONCHANGE); + observer_->OnIceConnectionChange(ice_connection_state_); } void PeerConnection::OnIceGatheringChange( PeerConnectionInterface::IceGatheringState new_state) { + ASSERT(signaling_thread()->IsCurrent()); if (IsClosed()) { return; } ice_gathering_state_ = new_state; - signaling_thread()->Post(this, MSG_ICEGATHERINGCHANGE); + observer_->OnIceGatheringChange(ice_gathering_state_); } void PeerConnection::OnIceCandidate(const IceCandidateInterface* candidate) { - JsepIceCandidate* candidate_copy = NULL; - if (candidate) { - // TODO(ronghuawu): Make IceCandidateInterface reference counted instead - // of making a copy. - candidate_copy = new JsepIceCandidate(candidate->sdp_mid(), - candidate->sdp_mline_index(), - candidate->candidate()); - } - // The Post takes the ownership of the |candidate_copy|. - signaling_thread()->Post(this, MSG_ICECANDIDATE, - new CandidateMsg(candidate_copy)); + ASSERT(signaling_thread()->IsCurrent()); + observer_->OnIceCandidate(candidate); } void PeerConnection::OnIceComplete() { - signaling_thread()->Post(this, MSG_ICECOMPLETE); + ASSERT(signaling_thread()->IsCurrent()); + observer_->OnIceComplete(); } void PeerConnection::ChangeSignalingState( diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.h b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.h index 9cc9f3868de..4a428efeed5 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.h @@ -57,11 +57,12 @@ class PeerConnection : public PeerConnectionInterface, public: explicit PeerConnection(PeerConnectionFactory* factory); - bool Initialize(const PeerConnectionInterface::IceServers& configuration, - const MediaConstraintsInterface* constraints, - PortAllocatorFactoryInterface* allocator_factory, - DTLSIdentityServiceInterface* dtls_identity_service, - PeerConnectionObserver* observer); + bool Initialize( + const PeerConnectionInterface::RTCConfiguration& configuration, + const MediaConstraintsInterface* constraints, + PortAllocatorFactoryInterface* allocator_factory, + DTLSIdentityServiceInterface* dtls_identity_service, + PeerConnectionObserver* observer); virtual talk_base::scoped_refptr<StreamCollectionInterface> local_streams(); virtual talk_base::scoped_refptr<StreamCollectionInterface> remote_streams(); virtual bool AddStream(MediaStreamInterface* local_stream, @@ -75,7 +76,8 @@ class PeerConnection : public PeerConnectionInterface, const std::string& label, const DataChannelInit* config); virtual bool GetStats(StatsObserver* observer, - webrtc::MediaStreamTrackInterface* track); + webrtc::MediaStreamTrackInterface* track, + StatsOutputLevel level); virtual SignalingState signaling_state(); @@ -96,10 +98,15 @@ class PeerConnection : public PeerConnectionInterface, SessionDescriptionInterface* desc); virtual void SetRemoteDescription(SetSessionDescriptionObserver* observer, SessionDescriptionInterface* desc); + // TODO(mallinath) : Deprecated version, remove after all clients are updated. virtual bool UpdateIce(const IceServers& configuration, const MediaConstraintsInterface* constraints); + virtual bool UpdateIce( + const PeerConnectionInterface::RTCConfiguration& config); virtual bool AddIceCandidate(const IceCandidateInterface* candidate); + virtual void RegisterUMAObserver(UMAObserver* observer); + virtual void Close(); protected: @@ -133,7 +140,8 @@ class PeerConnection : public PeerConnectionInterface, uint32 ssrc) OVERRIDE; virtual void OnRemoveLocalAudioTrack( MediaStreamInterface* stream, - AudioTrackInterface* audio_track) OVERRIDE; + AudioTrackInterface* audio_track, + uint32 ssrc) OVERRIDE; virtual void OnRemoveLocalVideoTrack( MediaStreamInterface* stream, VideoTrackInterface* video_track) OVERRIDE; @@ -150,7 +158,8 @@ class PeerConnection : public PeerConnectionInterface, cricket::BaseSession::State state); void ChangeSignalingState(SignalingState signaling_state); - bool DoInitialize(const StunConfigurations& stun_config, + bool DoInitialize(IceTransportsType type, + const StunConfigurations& stun_config, const TurnConfigurations& turn_config, const MediaConstraintsInterface* constraints, PortAllocatorFactoryInterface* allocator_factory, @@ -176,6 +185,7 @@ class PeerConnection : public PeerConnectionInterface, // will refer to the same reference count. talk_base::scoped_refptr<PeerConnectionFactory> factory_; PeerConnectionObserver* observer_; + UMAObserver* uma_observer_; SignalingState signaling_state_; // TODO(bemasc): Remove ice_state_. IceState ice_state_; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection_unittest.cc index 76d9cd73cf0..0c39297ab23 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection_unittest.cc @@ -40,6 +40,7 @@ #include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/app/webrtc/test/fakeaudiocapturemodule.h" #include "talk/app/webrtc/test/fakeconstraints.h" +#include "talk/app/webrtc/test/fakedtlsidentityservice.h" #include "talk/app/webrtc/test/fakevideotrackrenderer.h" #include "talk/app/webrtc/test/fakeperiodicvideocapturer.h" #include "talk/app/webrtc/test/mockpeerconnectionobservers.h" @@ -78,12 +79,19 @@ using webrtc::MockCreateSessionDescriptionObserver; using webrtc::MockDataChannelObserver; using webrtc::MockSetSessionDescriptionObserver; using webrtc::MockStatsObserver; +using webrtc::PeerConnectionInterface; using webrtc::SessionDescriptionInterface; using webrtc::StreamCollectionInterface; -static const int kMaxWaitMs = 1000; +static const int kMaxWaitMs = 2000; +// Disable for TSan v2, see +// https://code.google.com/p/webrtc/issues/detail?id=1205 for details. +// This declaration is also #ifdef'd as it causes uninitialized-variable +// warnings. +#if !defined(THREAD_SANITIZER) static const int kMaxWaitForStatsMs = 3000; -static const int kMaxWaitForFramesMs = 5000; +#endif +static const int kMaxWaitForFramesMs = 10000; static const int kEndAudioFrameCount = 3; static const int kEndVideoFrameCount = 3; @@ -327,7 +335,8 @@ class PeerConnectionTestClientBase int GetAudioOutputLevelStats(webrtc::MediaStreamTrackInterface* track) { talk_base::scoped_refptr<MockStatsObserver> observer(new talk_base::RefCountedObject<MockStatsObserver>()); - EXPECT_TRUE(peer_connection_->GetStats(observer, track)); + EXPECT_TRUE(peer_connection_->GetStats( + observer, track, PeerConnectionInterface::kStatsOutputLevelStandard)); EXPECT_TRUE_WAIT(observer->called(), kMaxWaitMs); return observer->AudioOutputLevel(); } @@ -335,7 +344,8 @@ class PeerConnectionTestClientBase int GetAudioInputLevelStats() { talk_base::scoped_refptr<MockStatsObserver> observer(new talk_base::RefCountedObject<MockStatsObserver>()); - EXPECT_TRUE(peer_connection_->GetStats(observer, NULL)); + EXPECT_TRUE(peer_connection_->GetStats( + observer, NULL, PeerConnectionInterface::kStatsOutputLevelStandard)); EXPECT_TRUE_WAIT(observer->called(), kMaxWaitMs); return observer->AudioInputLevel(); } @@ -343,7 +353,8 @@ class PeerConnectionTestClientBase int GetBytesReceivedStats(webrtc::MediaStreamTrackInterface* track) { talk_base::scoped_refptr<MockStatsObserver> observer(new talk_base::RefCountedObject<MockStatsObserver>()); - EXPECT_TRUE(peer_connection_->GetStats(observer, track)); + EXPECT_TRUE(peer_connection_->GetStats( + observer, track, PeerConnectionInterface::kStatsOutputLevelStandard)); EXPECT_TRUE_WAIT(observer->called(), kMaxWaitMs); return observer->BytesReceived(); } @@ -351,7 +362,8 @@ class PeerConnectionTestClientBase int GetBytesSentStats(webrtc::MediaStreamTrackInterface* track) { talk_base::scoped_refptr<MockStatsObserver> observer(new talk_base::RefCountedObject<MockStatsObserver>()); - EXPECT_TRUE(peer_connection_->GetStats(observer, track)); + EXPECT_TRUE(peer_connection_->GetStats( + observer, track, PeerConnectionInterface::kStatsOutputLevelStandard)); EXPECT_TRUE_WAIT(observer->called(), kMaxWaitMs); return observer->BytesSent(); } @@ -719,8 +731,12 @@ class JsepTestClient webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = "stun:stun.l.google.com:19302"; ice_servers.push_back(ice_server); + + FakeIdentityService* dtls_service = + talk_base::SSLStreamAdapter::HaveDtlsSrtp() ? + new FakeIdentityService() : NULL; return peer_connection_factory()->CreatePeerConnection( - ice_servers, constraints, factory, NULL, this); + ice_servers, constraints, factory, dtls_service, this); } void HandleIncomingOffer(const std::string& msg) { @@ -957,12 +973,13 @@ class P2PTestConductor : public testing::Test { } if (audio_frame_count != -1 || video_frame_count != -1) { - // Audio or video is expected to flow, so both sides should get to the - // Connected state. + // Audio or video is expected to flow, so both clients should reach the + // Connected state, and the offerer (ICE controller) should proceed to + // Completed. // Note: These tests have been observed to fail under heavy load at // shorter timeouts, so they may be flaky. EXPECT_EQ_WAIT( - webrtc::PeerConnectionInterface::kIceConnectionConnected, + webrtc::PeerConnectionInterface::kIceConnectionCompleted, initiating_client_->ice_connection_state(), kMaxWaitForFramesMs); EXPECT_EQ_WAIT( @@ -992,6 +1009,16 @@ class P2PTestConductor : public testing::Test { kMaxWaitForFramesMs); } + void SendRtpData(webrtc::DataChannelInterface* dc, const std::string& data) { + // Messages may get lost on the unreliable DataChannel, so we send multiple + // times to avoid test flakiness. + static const size_t kSendAttempts = 5; + + for (size_t i = 0; i < kSendAttempts; ++i) { + dc->Send(DataBuffer(data)); + } + } + SignalingClass* initializing_client() { return initiating_client_.get(); } SignalingClass* receiving_client() { return receiving_client_.get(); } @@ -1077,32 +1104,6 @@ TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestDtlsRenegotiate) { receiving_client()->Negotiate(); } -// This test sets up a call between an endpoint configured to use either SDES or -// DTLS (the offerer) and just SDES (the answerer). As a result, SDES is used -// instead of DTLS. -TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestOfferDtlsToSdes) { - MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - FakeConstraints setup_constraints; - setup_constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, - true); - ASSERT_TRUE(CreateTestClients(&setup_constraints, NULL)); - LocalP2PTest(); - VerifyRenderedSize(640, 480); -} - -// This test sets up a call between an endpoint configured to use SDES -// (the offerer) and either SDES or DTLS (the answerer). As a result, SDES is -// used instead of DTLS. -TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestOfferSdesToDtls) { - MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - FakeConstraints setup_constraints; - setup_constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, - true); - ASSERT_TRUE(CreateTestClients(NULL, &setup_constraints)); - LocalP2PTest(); - VerifyRenderedSize(640, 480); -} - // This test sets up a call between two endpoints that are configured to use // DTLS key agreement. The offerer don't support SDES. As a result, DTLS is // negotiated and used for transport. @@ -1146,7 +1147,9 @@ TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestAnswerNone) { // runs for a while (10 frames), the caller sends an update offer with video // being rejected. Once the re-negotiation is done, the video flow should stop // and the audio flow should continue. -TEST_F(JsepPeerConnectionP2PTestClient, UpdateOfferWithRejectedContent) { +// Disabled due to b/14955157. +TEST_F(JsepPeerConnectionP2PTestClient, + DISABLED_UpdateOfferWithRejectedContent) { ASSERT_TRUE(CreateTestClients()); LocalP2PTest(); TestUpdateOfferWithRejectedContent(); @@ -1154,7 +1157,8 @@ TEST_F(JsepPeerConnectionP2PTestClient, UpdateOfferWithRejectedContent) { // This test sets up a Jsep call between two parties. The MSID is removed from // the SDP strings from the caller. -TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestWithoutMsid) { +// Disabled due to b/14955157. +TEST_F(JsepPeerConnectionP2PTestClient, DISABLED_LocalP2PTestWithoutMsid) { ASSERT_TRUE(CreateTestClients()); receiving_client()->RemoveMsidFromReceivedSdp(true); // TODO(perkj): Currently there is a bug that cause audio to stop playing if @@ -1271,10 +1275,12 @@ TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestDataChannel) { kMaxWaitMs); std::string data = "hello world"; - initializing_client()->data_channel()->Send(DataBuffer(data)); + + SendRtpData(initializing_client()->data_channel(), data); EXPECT_EQ_WAIT(data, receiving_client()->data_observer()->last_message(), kMaxWaitMs); - receiving_client()->data_channel()->Send(DataBuffer(data)); + + SendRtpData(receiving_client()->data_channel(), data); EXPECT_EQ_WAIT(data, initializing_client()->data_observer()->last_message(), kMaxWaitMs); @@ -1308,8 +1314,10 @@ TEST_F(JsepPeerConnectionP2PTestClient, RegisterDataChannelObserver) { // Unregister the existing observer. receiving_client()->data_channel()->UnregisterObserver(); + std::string data = "hello world"; - initializing_client()->data_channel()->Send(DataBuffer(data)); + SendRtpData(initializing_client()->data_channel(), data); + // Wait a while to allow the sent data to arrive before an observer is // registered.. talk_base::Thread::Current()->ProcessMessages(100); @@ -1321,9 +1329,15 @@ TEST_F(JsepPeerConnectionP2PTestClient, RegisterDataChannelObserver) { // This test sets up a call between two parties with audio, video and but only // the initiating client support data. TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestReceiverDoesntSupportData) { - FakeConstraints setup_constraints; - setup_constraints.SetAllowRtpDataChannels(); - ASSERT_TRUE(CreateTestClients(&setup_constraints, NULL)); + FakeConstraints setup_constraints_1; + setup_constraints_1.SetAllowRtpDataChannels(); + // Must disable DTLS to make negotiation succeed. + setup_constraints_1.SetMandatory( + MediaConstraintsInterface::kEnableDtlsSrtp, false); + FakeConstraints setup_constraints_2; + setup_constraints_2.SetMandatory( + MediaConstraintsInterface::kEnableDtlsSrtp, false); + ASSERT_TRUE(CreateTestClients(&setup_constraints_1, &setup_constraints_2)); initializing_client()->CreateDataChannel(); LocalP2PTest(); EXPECT_TRUE(initializing_client()->data_channel() != NULL); @@ -1349,6 +1363,20 @@ TEST_F(JsepPeerConnectionP2PTestClient, AddDataChannelAfterRenegotiation) { kMaxWaitMs); } +// This test sets up a Jsep call with SCTP DataChannel and verifies the +// negotiation is completed without error. +#ifdef HAVE_SCTP +TEST_F(JsepPeerConnectionP2PTestClient, CreateOfferWithSctpDataChannel) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + FakeConstraints constraints; + constraints.SetMandatory( + MediaConstraintsInterface::kEnableDtlsSrtp, true); + ASSERT_TRUE(CreateTestClients(&constraints, &constraints)); + initializing_client()->CreateDataChannel(); + initializing_client()->Negotiate(false, false); +} +#endif + // This test sets up a call between two parties with audio, and video. // During the call, the initializing side restart ice and the test verifies that // new ice candidates are generated and audio and video still can flow. @@ -1410,6 +1438,4 @@ TEST_F(JsepPeerConnectionP2PTestClient, EnableVideoDecoderFactory(); LocalP2PTest(); } - #endif // if !defined(THREAD_SANITIZER) - diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionendtoend_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionendtoend_unittest.cc index da3c03da4eb..f701e0635f0 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionendtoend_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionendtoend_unittest.cc @@ -26,6 +26,7 @@ */ #include "talk/app/webrtc/test/peerconnectiontestwrapper.h" +#include "talk/app/webrtc/test/mockpeerconnectionobservers.h" #include "talk/base/gunit.h" #include "talk/base/logging.h" #include "talk/base/ssladapter.h" @@ -33,6 +34,13 @@ #include "talk/base/stringencode.h" #include "talk/base/stringutils.h" +#define MAYBE_SKIP_TEST(feature) \ + if (!(feature())) { \ + LOG(LS_INFO) << "Feature disabled... skipping"; \ + return; \ + } + +using webrtc::DataChannelInterface; using webrtc::FakeConstraints; using webrtc::MediaConstraintsInterface; using webrtc::MediaStreamInterface; @@ -42,6 +50,7 @@ namespace { const char kExternalGiceUfrag[] = "1234567890123456"; const char kExternalGicePwd[] = "123456789012345678901234"; +const size_t kMaxWait = 10000; void RemoveLinesFromSdp(const std::string& line_start, std::string* sdp) { @@ -117,6 +126,9 @@ class PeerConnectionEndToEndTest : public sigslot::has_slots<>, public testing::Test { public: + typedef std::vector<talk_base::scoped_refptr<DataChannelInterface> > + DataChannelList; + PeerConnectionEndToEndTest() : caller_(new talk_base::RefCountedObject<PeerConnectionTestWrapper>( "caller")), @@ -133,6 +145,11 @@ class PeerConnectionEndToEndTest EXPECT_TRUE(caller_->CreatePc(pc_constraints)); EXPECT_TRUE(callee_->CreatePc(pc_constraints)); PeerConnectionTestWrapper::Connect(caller_.get(), callee_.get()); + + caller_->SignalOnDataChannel.connect( + this, &PeerConnectionEndToEndTest::OnCallerAddedDataChanel); + callee_->SignalOnDataChannel.connect( + this, &PeerConnectionEndToEndTest::OnCalleeAddedDataChannel); } void GetAndAddUserMedia() { @@ -158,6 +175,11 @@ class PeerConnectionEndToEndTest callee_->WaitForCallEstablished(); } + void WaitForConnection() { + caller_->WaitForConnection(); + callee_->WaitForConnection(); + } + void SetupLegacySdpConverter() { caller_->SignalOnSdpCreated.connect( this, &PeerConnectionEndToEndTest::ConvertToLegacySdp); @@ -189,6 +211,57 @@ class PeerConnectionEndToEndTest LOG(LS_INFO) << "AddGiceCredsToCandidate: " << *sdp; } + void OnCallerAddedDataChanel(DataChannelInterface* dc) { + caller_signaled_data_channels_.push_back(dc); + } + + void OnCalleeAddedDataChannel(DataChannelInterface* dc) { + callee_signaled_data_channels_.push_back(dc); + } + + // Tests that |dc1| and |dc2| can send to and receive from each other. + void TestDataChannelSendAndReceive( + DataChannelInterface* dc1, DataChannelInterface* dc2) { + talk_base::scoped_ptr<webrtc::MockDataChannelObserver> dc1_observer( + new webrtc::MockDataChannelObserver(dc1)); + + talk_base::scoped_ptr<webrtc::MockDataChannelObserver> dc2_observer( + new webrtc::MockDataChannelObserver(dc2)); + + static const std::string kDummyData = "abcdefg"; + webrtc::DataBuffer buffer(kDummyData); + EXPECT_TRUE(dc1->Send(buffer)); + EXPECT_EQ_WAIT(kDummyData, dc2_observer->last_message(), kMaxWait); + + EXPECT_TRUE(dc2->Send(buffer)); + EXPECT_EQ_WAIT(kDummyData, dc1_observer->last_message(), kMaxWait); + + EXPECT_EQ(1U, dc1_observer->received_message_count()); + EXPECT_EQ(1U, dc2_observer->received_message_count()); + } + + void WaitForDataChannelsToOpen(DataChannelInterface* local_dc, + const DataChannelList& remote_dc_list, + size_t remote_dc_index) { + EXPECT_EQ_WAIT(DataChannelInterface::kOpen, local_dc->state(), kMaxWait); + + EXPECT_TRUE_WAIT(remote_dc_list.size() > remote_dc_index, kMaxWait); + EXPECT_EQ_WAIT(DataChannelInterface::kOpen, + remote_dc_list[remote_dc_index]->state(), + kMaxWait); + EXPECT_EQ(local_dc->id(), remote_dc_list[remote_dc_index]->id()); + } + + void CloseDataChannels(DataChannelInterface* local_dc, + const DataChannelList& remote_dc_list, + size_t remote_dc_index) { + local_dc->Close(); + EXPECT_EQ_WAIT(DataChannelInterface::kClosed, local_dc->state(), kMaxWait); + EXPECT_EQ_WAIT(DataChannelInterface::kClosed, + remote_dc_list[remote_dc_index]->state(), + kMaxWait); + } + ~PeerConnectionEndToEndTest() { talk_base::CleanupSSL(); } @@ -196,6 +269,8 @@ class PeerConnectionEndToEndTest protected: talk_base::scoped_refptr<PeerConnectionTestWrapper> caller_; talk_base::scoped_refptr<PeerConnectionTestWrapper> callee_; + DataChannelList caller_signaled_data_channels_; + DataChannelList callee_signaled_data_channels_; }; // Disable for TSan v2, see @@ -209,7 +284,8 @@ TEST_F(PeerConnectionEndToEndTest, Call) { WaitForCallEstablished(); } -TEST_F(PeerConnectionEndToEndTest, CallWithLegacySdp) { +// Disabled per b/14899892 +TEST_F(PeerConnectionEndToEndTest, DISABLED_CallWithLegacySdp) { FakeConstraints pc_constraints; pc_constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, false); @@ -221,4 +297,126 @@ TEST_F(PeerConnectionEndToEndTest, CallWithLegacySdp) { WaitForCallEstablished(); } +// Verifies that a DataChannel created before the negotiation can transition to +// "OPEN" and transfer data. +TEST_F(PeerConnectionEndToEndTest, CreateDataChannelBeforeNegotiate) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + + CreatePcs(); + + webrtc::DataChannelInit init; + talk_base::scoped_refptr<DataChannelInterface> caller_dc( + caller_->CreateDataChannel("data", init)); + talk_base::scoped_refptr<DataChannelInterface> callee_dc( + callee_->CreateDataChannel("data", init)); + + Negotiate(); + WaitForConnection(); + + WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(callee_dc, caller_signaled_data_channels_, 0); + + TestDataChannelSendAndReceive(caller_dc, callee_signaled_data_channels_[0]); + TestDataChannelSendAndReceive(callee_dc, caller_signaled_data_channels_[0]); + + CloseDataChannels(caller_dc, callee_signaled_data_channels_, 0); + CloseDataChannels(callee_dc, caller_signaled_data_channels_, 0); +} + +// Verifies that a DataChannel created after the negotiation can transition to +// "OPEN" and transfer data. +TEST_F(PeerConnectionEndToEndTest, CreateDataChannelAfterNegotiate) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + + CreatePcs(); + + webrtc::DataChannelInit init; + + // This DataChannel is for creating the data content in the negotiation. + talk_base::scoped_refptr<DataChannelInterface> dummy( + caller_->CreateDataChannel("data", init)); + Negotiate(); + WaitForConnection(); + + // Creates new DataChannels after the negotiation and verifies their states. + talk_base::scoped_refptr<DataChannelInterface> caller_dc( + caller_->CreateDataChannel("hello", init)); + talk_base::scoped_refptr<DataChannelInterface> callee_dc( + callee_->CreateDataChannel("hello", init)); + + WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 1); + WaitForDataChannelsToOpen(callee_dc, caller_signaled_data_channels_, 0); + + TestDataChannelSendAndReceive(caller_dc, callee_signaled_data_channels_[1]); + TestDataChannelSendAndReceive(callee_dc, caller_signaled_data_channels_[0]); + + CloseDataChannels(caller_dc, callee_signaled_data_channels_, 1); + CloseDataChannels(callee_dc, caller_signaled_data_channels_, 0); +} + +// Verifies that DataChannel IDs are even/odd based on the DTLS roles. +TEST_F(PeerConnectionEndToEndTest, DataChannelIdAssignment) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + + CreatePcs(); + + webrtc::DataChannelInit init; + talk_base::scoped_refptr<DataChannelInterface> caller_dc_1( + caller_->CreateDataChannel("data", init)); + talk_base::scoped_refptr<DataChannelInterface> callee_dc_1( + callee_->CreateDataChannel("data", init)); + + Negotiate(); + WaitForConnection(); + + EXPECT_EQ(1U, caller_dc_1->id() % 2); + EXPECT_EQ(0U, callee_dc_1->id() % 2); + + talk_base::scoped_refptr<DataChannelInterface> caller_dc_2( + caller_->CreateDataChannel("data", init)); + talk_base::scoped_refptr<DataChannelInterface> callee_dc_2( + callee_->CreateDataChannel("data", init)); + + EXPECT_EQ(1U, caller_dc_2->id() % 2); + EXPECT_EQ(0U, callee_dc_2->id() % 2); +} + +// Verifies that the message is received by the right remote DataChannel when +// there are multiple DataChannels. +TEST_F(PeerConnectionEndToEndTest, + MessageTransferBetweenTwoPairsOfDataChannels) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + + CreatePcs(); + + webrtc::DataChannelInit init; + + talk_base::scoped_refptr<DataChannelInterface> caller_dc_1( + caller_->CreateDataChannel("data", init)); + talk_base::scoped_refptr<DataChannelInterface> caller_dc_2( + caller_->CreateDataChannel("data", init)); + + Negotiate(); + WaitForConnection(); + WaitForDataChannelsToOpen(caller_dc_1, callee_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(caller_dc_2, callee_signaled_data_channels_, 1); + + talk_base::scoped_ptr<webrtc::MockDataChannelObserver> dc_1_observer( + new webrtc::MockDataChannelObserver(callee_signaled_data_channels_[0])); + + talk_base::scoped_ptr<webrtc::MockDataChannelObserver> dc_2_observer( + new webrtc::MockDataChannelObserver(callee_signaled_data_channels_[1])); + + const std::string message_1 = "hello 1"; + const std::string message_2 = "hello 2"; + + caller_dc_1->Send(webrtc::DataBuffer(message_1)); + EXPECT_EQ_WAIT(message_1, dc_1_observer->last_message(), kMaxWait); + + caller_dc_2->Send(webrtc::DataBuffer(message_2)); + EXPECT_EQ_WAIT(message_2, dc_2_observer->last_message(), kMaxWait); + + EXPECT_EQ(1U, dc_1_observer->received_message_count()); + EXPECT_EQ(1U, dc_2_observer->received_message_count()); +} #endif // if !defined(THREAD_SANITIZER) diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.cc index e8b8f63169c..3628c590535 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.cc @@ -51,7 +51,7 @@ typedef talk_base::TypedMessageData<bool> InitMessageData; struct CreatePeerConnectionParams : public talk_base::MessageData { CreatePeerConnectionParams( - const webrtc::PeerConnectionInterface::IceServers& configuration, + const webrtc::PeerConnectionInterface::RTCConfiguration& configuration, const webrtc::MediaConstraintsInterface* constraints, webrtc::PortAllocatorFactoryInterface* allocator_factory, webrtc::DTLSIdentityServiceInterface* dtls_identity_service, @@ -63,28 +63,13 @@ struct CreatePeerConnectionParams : public talk_base::MessageData { observer(observer) { } scoped_refptr<webrtc::PeerConnectionInterface> peerconnection; - const webrtc::PeerConnectionInterface::IceServers& configuration; + const webrtc::PeerConnectionInterface::RTCConfiguration& configuration; const webrtc::MediaConstraintsInterface* constraints; scoped_refptr<webrtc::PortAllocatorFactoryInterface> allocator_factory; webrtc::DTLSIdentityServiceInterface* dtls_identity_service; webrtc::PeerConnectionObserver* observer; }; -struct CreatePeerConnectionParamsDeprecated : public talk_base::MessageData { - CreatePeerConnectionParamsDeprecated( - const std::string& configuration, - webrtc::PortAllocatorFactoryInterface* allocator_factory, - webrtc::PeerConnectionObserver* observer) - : configuration(configuration), - allocator_factory(allocator_factory), - observer(observer) { - } - scoped_refptr<webrtc::PeerConnectionInterface> peerconnection; - const std::string& configuration; - scoped_refptr<webrtc::PortAllocatorFactoryInterface> allocator_factory; - webrtc::PeerConnectionObserver* observer; -}; - struct CreateAudioSourceParams : public talk_base::MessageData { explicit CreateAudioSourceParams( const webrtc::MediaConstraintsInterface* constraints) @@ -105,21 +90,30 @@ struct CreateVideoSourceParams : public talk_base::MessageData { scoped_refptr<webrtc::VideoSourceInterface> source; }; +struct StartAecDumpParams : public talk_base::MessageData { + explicit StartAecDumpParams(talk_base::PlatformFile aec_dump_file) + : aec_dump_file(aec_dump_file) { + } + talk_base::PlatformFile aec_dump_file; + bool result; +}; + enum { MSG_INIT_FACTORY = 1, MSG_TERMINATE_FACTORY, MSG_CREATE_PEERCONNECTION, MSG_CREATE_AUDIOSOURCE, MSG_CREATE_VIDEOSOURCE, + MSG_START_AEC_DUMP, }; } // namespace namespace webrtc { -scoped_refptr<PeerConnectionFactoryInterface> +talk_base::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory() { - scoped_refptr<PeerConnectionFactory> pc_factory( + talk_base::scoped_refptr<PeerConnectionFactory> pc_factory( new talk_base::RefCountedObject<PeerConnectionFactory>()); if (!pc_factory->Initialize()) { @@ -128,17 +122,19 @@ CreatePeerConnectionFactory() { return pc_factory; } -scoped_refptr<PeerConnectionFactoryInterface> +talk_base::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory( talk_base::Thread* worker_thread, talk_base::Thread* signaling_thread, AudioDeviceModule* default_adm, cricket::WebRtcVideoEncoderFactory* encoder_factory, cricket::WebRtcVideoDecoderFactory* decoder_factory) { - scoped_refptr<PeerConnectionFactory> pc_factory( - new talk_base::RefCountedObject<PeerConnectionFactory>( - worker_thread, signaling_thread, default_adm, - encoder_factory, decoder_factory)); + talk_base::scoped_refptr<PeerConnectionFactory> pc_factory( + new talk_base::RefCountedObject<PeerConnectionFactory>(worker_thread, + signaling_thread, + default_adm, + encoder_factory, + decoder_factory)); if (!pc_factory->Initialize()) { return NULL; } @@ -223,6 +219,12 @@ void PeerConnectionFactory::OnMessage(talk_base::Message* msg) { pdata->source = CreateVideoSource_s(pdata->capturer, pdata->constraints); break; } + case MSG_START_AEC_DUMP: { + StartAecDumpParams* pdata = + static_cast<StartAecDumpParams*>(msg->pdata); + pdata->result = StartAecDump_s(pdata->aec_dump_file); + break; + } } } @@ -245,6 +247,7 @@ bool PeerConnectionFactory::Initialize_s() { channel_manager_.reset(new cricket::ChannelManager( webrtc_media_engine, device_manager, worker_thread_)); + channel_manager_->SetVideoRtxEnabled(true); if (!channel_manager_->Init()) { return false; } @@ -274,9 +277,13 @@ PeerConnectionFactory::CreateVideoSource_s( return VideoSourceProxy::Create(signaling_thread_, source); } -scoped_refptr<PeerConnectionInterface> +bool PeerConnectionFactory::StartAecDump_s(talk_base::PlatformFile file) { + return channel_manager_->StartAecDump(file); +} + +talk_base::scoped_refptr<PeerConnectionInterface> PeerConnectionFactory::CreatePeerConnection( - const PeerConnectionInterface::IceServers& configuration, + const PeerConnectionInterface::RTCConfiguration& configuration, const MediaConstraintsInterface* constraints, PortAllocatorFactoryInterface* allocator_factory, DTLSIdentityServiceInterface* dtls_identity_service, @@ -284,23 +291,14 @@ PeerConnectionFactory::CreatePeerConnection( CreatePeerConnectionParams params(configuration, constraints, allocator_factory, dtls_identity_service, observer); - signaling_thread_->Send(this, MSG_CREATE_PEERCONNECTION, ¶ms); + signaling_thread_->Send( + this, MSG_CREATE_PEERCONNECTION, ¶ms); return params.peerconnection; } -scoped_refptr<PeerConnectionInterface> -PeerConnectionFactory::CreatePeerConnection( - const PeerConnectionInterface::IceServers& configuration, - const MediaConstraintsInterface* constraints, - DTLSIdentityServiceInterface* dtls_identity_service, - PeerConnectionObserver* observer) { - return CreatePeerConnection( - configuration, constraints, NULL, dtls_identity_service, observer); -} - talk_base::scoped_refptr<PeerConnectionInterface> PeerConnectionFactory::CreatePeerConnection_s( - const PeerConnectionInterface::IceServers& configuration, + const PeerConnectionInterface::RTCConfiguration& configuration, const MediaConstraintsInterface* constraints, PortAllocatorFactoryInterface* allocator_factory, DTLSIdentityServiceInterface* dtls_identity_service, @@ -319,7 +317,7 @@ PeerConnectionFactory::CreatePeerConnection_s( return PeerConnectionProxy::Create(signaling_thread(), pc); } -scoped_refptr<MediaStreamInterface> +talk_base::scoped_refptr<MediaStreamInterface> PeerConnectionFactory::CreateLocalMediaStream(const std::string& label) { return MediaStreamProxy::Create(signaling_thread_, MediaStream::Create(label)); @@ -353,14 +351,20 @@ PeerConnectionFactory::CreateVideoTrack( return VideoTrackProxy::Create(signaling_thread_, track); } -scoped_refptr<AudioTrackInterface> PeerConnectionFactory::CreateAudioTrack( - const std::string& id, - AudioSourceInterface* source) { +talk_base::scoped_refptr<AudioTrackInterface> +PeerConnectionFactory::CreateAudioTrack(const std::string& id, + AudioSourceInterface* source) { talk_base::scoped_refptr<AudioTrackInterface> track( AudioTrack::Create(id, source)); return AudioTrackProxy::Create(signaling_thread_, track); } +bool PeerConnectionFactory::StartAecDump(talk_base::PlatformFile file) { + StartAecDumpParams params(file); + signaling_thread_->Send(this, MSG_START_AEC_DUMP, ¶ms); + return params.result; +} + cricket::ChannelManager* PeerConnectionFactory::channel_manager() { return channel_manager_.get(); } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.h b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.h index dff885dfe95..633d2811406 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.h @@ -46,18 +46,12 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface, virtual talk_base::scoped_refptr<PeerConnectionInterface> CreatePeerConnection( - const PeerConnectionInterface::IceServers& configuration, - const MediaConstraintsInterface* constraints, - DTLSIdentityServiceInterface* dtls_identity_service, - PeerConnectionObserver* observer); - - virtual talk_base::scoped_refptr<PeerConnectionInterface> - CreatePeerConnection( - const PeerConnectionInterface::IceServers& configuration, + const PeerConnectionInterface::RTCConfiguration& configuration, const MediaConstraintsInterface* constraints, PortAllocatorFactoryInterface* allocator_factory, DTLSIdentityServiceInterface* dtls_identity_service, PeerConnectionObserver* observer); + bool Initialize(); virtual talk_base::scoped_refptr<MediaStreamInterface> @@ -78,6 +72,8 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface, CreateAudioTrack(const std::string& id, AudioSourceInterface* audio_source); + virtual bool StartAecDump(talk_base::PlatformFile file); + virtual cricket::ChannelManager* channel_manager(); virtual talk_base::Thread* signaling_thread(); virtual talk_base::Thread* worker_thread(); @@ -93,7 +89,6 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface, cricket::WebRtcVideoDecoderFactory* video_decoder_factory); virtual ~PeerConnectionFactory(); - private: bool Initialize_s(); void Terminate_s(); @@ -102,12 +97,16 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface, talk_base::scoped_refptr<VideoSourceInterface> CreateVideoSource_s( cricket::VideoCapturer* capturer, const MediaConstraintsInterface* constraints); + talk_base::scoped_refptr<PeerConnectionInterface> CreatePeerConnection_s( - const PeerConnectionInterface::IceServers& configuration, + const PeerConnectionInterface::RTCConfiguration& configuration, const MediaConstraintsInterface* constraints, PortAllocatorFactoryInterface* allocator_factory, DTLSIdentityServiceInterface* dtls_identity_service, PeerConnectionObserver* observer); + + bool StartAecDump_s(talk_base::PlatformFile file); + // Implements talk_base::MessageHandler. void OnMessage(talk_base::Message* msg); diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory_unittest.cc index 4ab9e35c891..01f35d940dc 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory_unittest.cc @@ -156,7 +156,7 @@ TEST(PeerConnectionFactoryTestInternal, CreatePCUsingInternalModules) { webrtc::PeerConnectionInterface::IceServers servers; talk_base::scoped_refptr<PeerConnectionInterface> pc( - factory->CreatePeerConnection(servers, NULL, NULL, &observer)); + factory->CreatePeerConnection(servers, NULL, NULL, NULL, &observer)); EXPECT_TRUE(pc.get() != NULL); } @@ -164,6 +164,42 @@ TEST(PeerConnectionFactoryTestInternal, CreatePCUsingInternalModules) { // This test verifies creation of PeerConnection with valid STUN and TURN // configuration. Also verifies the URL's parsed correctly as expected. TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) { + PeerConnectionInterface::RTCConfiguration config; + webrtc::PeerConnectionInterface::IceServer ice_server; + ice_server.uri = kStunIceServer; + config.servers.push_back(ice_server); + ice_server.uri = kTurnIceServer; + ice_server.password = kTurnPassword; + config.servers.push_back(ice_server); + ice_server.uri = kTurnIceServerWithTransport; + ice_server.password = kTurnPassword; + config.servers.push_back(ice_server); + talk_base::scoped_refptr<PeerConnectionInterface> pc( + factory_->CreatePeerConnection(config, NULL, + allocator_factory_.get(), + NULL, + &observer_)); + EXPECT_TRUE(pc.get() != NULL); + StunConfigurations stun_configs; + webrtc::PortAllocatorFactoryInterface::StunConfiguration stun1( + "stun.l.google.com", 19302); + stun_configs.push_back(stun1); + VerifyStunConfigurations(stun_configs); + TurnConfigurations turn_configs; + webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1( + "test.com", 1234, "test@hello.com", kTurnPassword, "udp", false); + turn_configs.push_back(turn1); + webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2( + "hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false); + turn_configs.push_back(turn2); + VerifyTurnConfigurations(turn_configs); +} + +// This test verifies creation of PeerConnection with valid STUN and TURN +// configuration. Also verifies the URL's parsed correctly as expected. +// This version doesn't use RTCConfiguration. +// TODO(mallinath) - Remove this method after clients start using RTCConfig. +TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServersOldSignature) { webrtc::PeerConnectionInterface::IceServers ice_servers; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kStunIceServer; @@ -184,12 +220,6 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) { webrtc::PortAllocatorFactoryInterface::StunConfiguration stun1( "stun.l.google.com", 19302); stun_configs.push_back(stun1); - webrtc::PortAllocatorFactoryInterface::StunConfiguration stun2( - "test.com", 1234); - stun_configs.push_back(stun2); - webrtc::PortAllocatorFactoryInterface::StunConfiguration stun3( - "hello.com", kDefaultStunPort); - stun_configs.push_back(stun3); VerifyStunConfigurations(stun_configs); TurnConfigurations turn_configs; webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1( @@ -202,16 +232,16 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) { } TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) { - webrtc::PeerConnectionInterface::IceServers ice_servers; + PeerConnectionInterface::RTCConfiguration config; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kStunIceServer; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); ice_server.uri = kTurnIceServerWithNoUsernameInUri; ice_server.username = kTurnUsername; ice_server.password = kTurnPassword; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); talk_base::scoped_refptr<PeerConnectionInterface> pc( - factory_->CreatePeerConnection(ice_servers, NULL, + factory_->CreatePeerConnection(config, NULL, allocator_factory_.get(), NULL, &observer_)); @@ -226,13 +256,13 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) { // This test verifies the PeerConnection created properly with TURN url which // has transport parameter in it. TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) { - webrtc::PeerConnectionInterface::IceServers ice_servers; + PeerConnectionInterface::RTCConfiguration config; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kTurnIceServerWithTransport; ice_server.password = kTurnPassword; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); talk_base::scoped_refptr<PeerConnectionInterface> pc( - factory_->CreatePeerConnection(ice_servers, NULL, + factory_->CreatePeerConnection(config, NULL, allocator_factory_.get(), NULL, &observer_)); @@ -242,27 +272,22 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) { "hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false); turn_configs.push_back(turn); VerifyTurnConfigurations(turn_configs); - StunConfigurations stun_configs; - webrtc::PortAllocatorFactoryInterface::StunConfiguration stun( - "hello.com", kDefaultStunPort); - stun_configs.push_back(stun); - VerifyStunConfigurations(stun_configs); } TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) { - webrtc::PeerConnectionInterface::IceServers ice_servers; + PeerConnectionInterface::RTCConfiguration config; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kSecureTurnIceServer; ice_server.password = kTurnPassword; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); ice_server.uri = kSecureTurnIceServerWithoutTransportParam; ice_server.password = kTurnPassword; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); ice_server.uri = kSecureTurnIceServerWithoutTransportAndPortParam; ice_server.password = kTurnPassword; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); talk_base::scoped_refptr<PeerConnectionInterface> pc( - factory_->CreatePeerConnection(ice_servers, NULL, + factory_->CreatePeerConnection(config, NULL, allocator_factory_.get(), NULL, &observer_)); @@ -283,23 +308,23 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) { } TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) { - webrtc::PeerConnectionInterface::IceServers ice_servers; + PeerConnectionInterface::RTCConfiguration config; webrtc::PeerConnectionInterface::IceServer ice_server; ice_server.uri = kStunIceServerWithIPv4Address; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); ice_server.uri = kStunIceServerWithIPv4AddressWithoutPort; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); ice_server.uri = kStunIceServerWithIPv6Address; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); ice_server.uri = kStunIceServerWithIPv6AddressWithoutPort; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); ice_server.uri = kStunIceServerWithInvalidIPv6Address; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); ice_server.uri = kTurnIceServerWithIPv6Address; ice_server.password = kTurnPassword; - ice_servers.push_back(ice_server); + config.servers.push_back(ice_server); talk_base::scoped_refptr<PeerConnectionInterface> pc( - factory_->CreatePeerConnection(ice_servers, NULL, + factory_->CreatePeerConnection(config, NULL, allocator_factory_.get(), NULL, &observer_)); @@ -317,9 +342,8 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) { webrtc::PortAllocatorFactoryInterface::StunConfiguration stun4( "2401:fa00:4::", 3478); stun_configs.push_back(stun4); // Default port - // Turn Address has the same host information as |stun3|. - stun_configs.push_back(stun3); VerifyStunConfigurations(stun_configs); + TurnConfigurations turn_configs; webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1( "2401:fa00:4::", 1234, "test", kTurnPassword, "udp", false); diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h index a127dad3a9d..ed4033c17d6 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h @@ -76,6 +76,8 @@ #include "talk/app/webrtc/jsep.h" #include "talk/app/webrtc/mediastreaminterface.h" #include "talk/app/webrtc/statstypes.h" +#include "talk/app/webrtc/umametrics.h" +#include "talk/base/fileutils.h" #include "talk/base/socketaddress.h" namespace talk_base { @@ -117,6 +119,16 @@ class StatsObserver : public talk_base::RefCountInterface { virtual ~StatsObserver() {} }; +class UMAObserver : public talk_base::RefCountInterface { + public: + virtual void IncrementCounter(PeerConnectionUMAMetricsCounter type) = 0; + virtual void AddHistogramSample(PeerConnectionUMAMetricsName type, + int value) = 0; + + protected: + virtual ~UMAObserver() {} +}; + class PeerConnectionInterface : public talk_base::RefCountInterface { public: // See http://dev.w3.org/2011/webrtc/editor/webrtc.html#state-definitions . @@ -165,6 +177,30 @@ class PeerConnectionInterface : public talk_base::RefCountInterface { }; typedef std::vector<IceServer> IceServers; + enum IceTransportsType { + kNone, + kRelay, + kNoHost, + kAll + }; + + struct RTCConfiguration { + IceTransportsType type; + IceServers servers; + + RTCConfiguration() : type(kAll) {} + explicit RTCConfiguration(IceTransportsType type) : type(type) {} + }; + + // Used by GetStats to decide which stats to include in the stats reports. + // |kStatsOutputLevelStandard| includes the standard stats for Javascript API; + // |kStatsOutputLevelDebug| includes both the standard stats and additional + // stats for debugging purposes. + enum StatsOutputLevel { + kStatsOutputLevelStandard, + kStatsOutputLevelDebug, + }; + // Accessor methods to active local streams. virtual talk_base::scoped_refptr<StreamCollectionInterface> local_streams() = 0; @@ -190,7 +226,8 @@ class PeerConnectionInterface : public talk_base::RefCountInterface { AudioTrackInterface* track) = 0; virtual bool GetStats(StatsObserver* observer, - MediaStreamTrackInterface* track) = 0; + MediaStreamTrackInterface* track, + StatsOutputLevel level) = 0; virtual talk_base::scoped_refptr<DataChannelInterface> CreateDataChannel( const std::string& label, @@ -229,6 +266,8 @@ class PeerConnectionInterface : public talk_base::RefCountInterface { // take the ownership of the |candidate|. virtual bool AddIceCandidate(const IceCandidateInterface* candidate) = 0; + virtual void RegisterUMAObserver(UMAObserver* observer) = 0; + // Returns the current SignalingState. virtual SignalingState signaling_state() = 0; @@ -276,8 +315,8 @@ class PeerConnectionObserver { // TODO(perkj): Make pure virtual. virtual void OnDataChannel(DataChannelInterface* data_channel) {} - // Triggered when renegotation is needed, for example the ICE has restarted. - virtual void OnRenegotiationNeeded() {} + // Triggered when renegotiation is needed, for example the ICE has restarted. + virtual void OnRenegotiationNeeded() = 0; // Called any time the IceConnectionState changes virtual void OnIceConnectionChange( @@ -393,29 +432,42 @@ class PeerConnectionFactoryInterface : public talk_base::RefCountInterface { class Options { public: Options() : - enable_aec_dump(false), disable_encryption(false), disable_sctp_data_channels(false) { } - bool enable_aec_dump; bool disable_encryption; bool disable_sctp_data_channels; }; virtual void SetOptions(const Options& options) = 0; - virtual talk_base::scoped_refptr<PeerConnectionInterface> - CreatePeerConnection( - const PeerConnectionInterface::IceServers& configuration, - const MediaConstraintsInterface* constraints, - DTLSIdentityServiceInterface* dtls_identity_service, - PeerConnectionObserver* observer) = 0; + virtual talk_base::scoped_refptr<PeerConnectionInterface> CreatePeerConnection( - const PeerConnectionInterface::IceServers& configuration, + const PeerConnectionInterface::RTCConfiguration& configuration, const MediaConstraintsInterface* constraints, PortAllocatorFactoryInterface* allocator_factory, DTLSIdentityServiceInterface* dtls_identity_service, PeerConnectionObserver* observer) = 0; + + // TODO(mallinath) : Remove below versions after clients are updated + // to above method. + // In latest W3C WebRTC draft, PC constructor will take RTCConfiguration, + // and not IceServers. RTCConfiguration is made up of ice servers and + // ice transport type. + // http://dev.w3.org/2011/webrtc/editor/webrtc.html + inline talk_base::scoped_refptr<PeerConnectionInterface> + CreatePeerConnection( + const PeerConnectionInterface::IceServers& configuration, + const MediaConstraintsInterface* constraints, + PortAllocatorFactoryInterface* allocator_factory, + DTLSIdentityServiceInterface* dtls_identity_service, + PeerConnectionObserver* observer) { + PeerConnectionInterface::RTCConfiguration rtc_config; + rtc_config.servers = configuration; + return CreatePeerConnection(rtc_config, constraints, allocator_factory, + dtls_identity_service, observer); + } + virtual talk_base::scoped_refptr<MediaStreamInterface> CreateLocalMediaStream(const std::string& label) = 0; @@ -442,6 +494,13 @@ class PeerConnectionFactoryInterface : public talk_base::RefCountInterface { CreateAudioTrack(const std::string& label, AudioSourceInterface* source) = 0; + // Starts AEC dump using existing file. Takes ownership of |file| and passes + // it on to VoiceEngine (via other objects) immediately, which will take + // the ownerhip. If the operation fails, the file will be closed. + // TODO(grunell): Remove when Chromium has started to use AEC in each source. + // http://crbug.com/264611. + virtual bool StartAecDump(talk_base::PlatformFile file) = 0; + protected: // Dtor and ctor protected as objects shouldn't be created or deleted via // this interface. diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface_unittest.cc index 093b8426856..5c9b826dac0 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface_unittest.cc @@ -32,6 +32,7 @@ #include "talk/app/webrtc/mediastreaminterface.h" #include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/app/webrtc/test/fakeconstraints.h" +#include "talk/app/webrtc/test/fakedtlsidentityservice.h" #include "talk/app/webrtc/test/mockpeerconnectionobservers.h" #include "talk/app/webrtc/test/testsdpstrings.h" #include "talk/app/webrtc/videosource.h" @@ -257,9 +258,21 @@ class PeerConnectionInterfaceTest : public testing::Test { servers.push_back(server); port_allocator_factory_ = FakePortAllocatorFactory::Create(); + + // DTLS does not work in a loopback call, so is disabled for most of the + // tests in this file. We only create a FakeIdentityService if the test + // explicitly sets the constraint. + FakeIdentityService* dtls_service = NULL; + bool dtls; + if (FindConstraint(constraints, + webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, + &dtls, + NULL) && dtls) { + dtls_service = new FakeIdentityService(); + } pc_ = pc_factory_->CreatePeerConnection(servers, constraints, port_allocator_factory_.get(), - NULL, + dtls_service, &observer_); ASSERT_TRUE(pc_.get() != NULL); observer_.SetPeerConnectionInterface(pc_.get()); @@ -288,7 +301,7 @@ class PeerConnectionInterfaceTest : public testing::Test { EXPECT_EQ(0u, port_allocator_factory_->turn_configs().size()); CreatePeerConnection(kTurnIceServerUri, kTurnPassword, NULL); - EXPECT_EQ(1u, port_allocator_factory_->stun_configs().size()); + EXPECT_EQ(0u, port_allocator_factory_->stun_configs().size()); EXPECT_EQ(1u, port_allocator_factory_->turn_configs().size()); EXPECT_EQ(kTurnUsername, port_allocator_factory_->turn_configs()[0].username); @@ -296,8 +309,6 @@ class PeerConnectionInterfaceTest : public testing::Test { port_allocator_factory_->turn_configs()[0].password); EXPECT_EQ(kTurnHostname, port_allocator_factory_->turn_configs()[0].server.hostname()); - EXPECT_EQ(kTurnHostname, - port_allocator_factory_->stun_configs()[0].server.hostname()); } void ReleasePeerConnection() { @@ -398,7 +409,8 @@ class PeerConnectionInterfaceTest : public testing::Test { bool DoGetStats(MediaStreamTrackInterface* track) { talk_base::scoped_refptr<MockStatsObserver> observer( new talk_base::RefCountedObject<MockStatsObserver>()); - if (!pc_->GetStats(observer, track)) + if (!pc_->GetStats( + observer, track, PeerConnectionInterface::kStatsOutputLevelStandard)) return false; EXPECT_TRUE_WAIT(observer->called(), kTimeout); return observer->called(); @@ -496,6 +508,8 @@ class PeerConnectionInterfaceTest : public testing::Test { EXPECT_TRUE(DoSetLocalDescription(new_offer)); EXPECT_EQ(PeerConnectionInterface::kHaveLocalOffer, observer_.state_); + // Wait for the ice_complete message, so that SDP will have candidates. + EXPECT_TRUE_WAIT(observer_.ice_complete_, kTimeout); } void CreateAnswerAsRemoteDescription(const std::string& offer) { @@ -766,13 +780,7 @@ TEST_F(PeerConnectionInterfaceTest, GetStatsForInvalidTrack) { } // This test setup two RTP data channels in loop back. -#ifdef WIN32 -// TODO(perkj): Investigate why the transport channel sometimes don't become -// writable on Windows when we try to connect in loop back. -TEST_F(PeerConnectionInterfaceTest, DISABLED_TestDataChannel) { -#else TEST_F(PeerConnectionInterfaceTest, TestDataChannel) { -#endif FakeConstraints constraints; constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); @@ -819,13 +827,7 @@ TEST_F(PeerConnectionInterfaceTest, TestDataChannel) { // This test verifies that sendnig binary data over RTP data channels should // fail. -#ifdef WIN32 -// TODO(perkj): Investigate why the transport channel sometimes don't become -// writable on Windows when we try to connect in loop back. -TEST_F(PeerConnectionInterfaceTest, DISABLED_TestSendBinaryOnRtpDataChannel) { -#else TEST_F(PeerConnectionInterfaceTest, TestSendBinaryOnRtpDataChannel) { -#endif FakeConstraints constraints; constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); @@ -855,13 +857,7 @@ TEST_F(PeerConnectionInterfaceTest, TestSendBinaryOnRtpDataChannel) { // This test setup a RTP data channels in loop back and test that a channel is // opened even if the remote end answer with a zero SSRC. -#ifdef WIN32 -// TODO(perkj): Investigate why the transport channel sometimes don't become -// writable on Windows when we try to connect in loop back. -TEST_F(PeerConnectionInterfaceTest, DISABLED_TestSendOnlyDataChannel) { -#else TEST_F(PeerConnectionInterfaceTest, TestSendOnlyDataChannel) { -#endif FakeConstraints constraints; constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); @@ -950,23 +946,28 @@ TEST_F(PeerConnectionInterfaceTest, CreateSctpDataChannel) { pc_->CreateDataChannel("1", &config); EXPECT_TRUE(channel != NULL); EXPECT_TRUE(channel->reliable()); + EXPECT_TRUE(observer_.renegotiation_needed_); + observer_.renegotiation_needed_ = false; config.ordered = false; channel = pc_->CreateDataChannel("2", &config); EXPECT_TRUE(channel != NULL); EXPECT_TRUE(channel->reliable()); + EXPECT_FALSE(observer_.renegotiation_needed_); config.ordered = true; config.maxRetransmits = 0; channel = pc_->CreateDataChannel("3", &config); EXPECT_TRUE(channel != NULL); EXPECT_FALSE(channel->reliable()); + EXPECT_FALSE(observer_.renegotiation_needed_); config.maxRetransmits = -1; config.maxRetransmitTime = 0; channel = pc_->CreateDataChannel("4", &config); EXPECT_TRUE(channel != NULL); EXPECT_FALSE(channel->reliable()); + EXPECT_FALSE(observer_.renegotiation_needed_); } // This tests that no data channel is returned if both maxRetransmits and @@ -1016,15 +1017,25 @@ TEST_F(PeerConnectionInterfaceTest, EXPECT_TRUE(channel == NULL); } +// This test verifies that OnRenegotiationNeeded is fired for every new RTP +// DataChannel. +TEST_F(PeerConnectionInterfaceTest, RenegotiationNeededForNewRtpDataChannel) { + FakeConstraints constraints; + constraints.SetAllowRtpDataChannels(); + CreatePeerConnection(&constraints); + + scoped_refptr<DataChannelInterface> dc1 = + pc_->CreateDataChannel("test1", NULL); + EXPECT_TRUE(observer_.renegotiation_needed_); + observer_.renegotiation_needed_ = false; + + scoped_refptr<DataChannelInterface> dc2 = + pc_->CreateDataChannel("test2", NULL); + EXPECT_TRUE(observer_.renegotiation_needed_); +} + // This test that a data channel closes when a PeerConnection is deleted/closed. -#ifdef WIN32 -// TODO(perkj): Investigate why the transport channel sometimes don't become -// writable on Windows when we try to connect in loop back. -TEST_F(PeerConnectionInterfaceTest, - DISABLED_DataChannelCloseWhenPeerConnectionClose) { -#else TEST_F(PeerConnectionInterfaceTest, DataChannelCloseWhenPeerConnectionClose) { -#endif FakeConstraints constraints; constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionproxy.h b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionproxy.h index f07416d65c4..74e5012bb14 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionproxy.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionproxy.h @@ -44,7 +44,9 @@ BEGIN_PROXY_MAP(PeerConnection) PROXY_METHOD1(void, RemoveStream, MediaStreamInterface*) PROXY_METHOD1(talk_base::scoped_refptr<DtmfSenderInterface>, CreateDtmfSender, AudioTrackInterface*) - PROXY_METHOD2(bool, GetStats, StatsObserver*, MediaStreamTrackInterface*) + PROXY_METHOD3(bool, GetStats, StatsObserver*, + MediaStreamTrackInterface*, + StatsOutputLevel) PROXY_METHOD2(talk_base::scoped_refptr<DataChannelInterface>, CreateDataChannel, const std::string&, const DataChannelInit*) PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, local_description) @@ -60,6 +62,7 @@ BEGIN_PROXY_MAP(PeerConnection) PROXY_METHOD2(bool, UpdateIce, const IceServers&, const MediaConstraintsInterface*) PROXY_METHOD1(bool, AddIceCandidate, const IceCandidateInterface*) + PROXY_METHOD1(void, RegisterUMAObserver, UMAObserver*) PROXY_METHOD0(SignalingState, signaling_state) PROXY_METHOD0(IceState, ice_state) PROXY_METHOD0(IceConnectionState, ice_connection_state) diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/portallocatorfactory.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/portallocatorfactory.cc index 32008f6b5b0..decd33c7b6e 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/portallocatorfactory.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/portallocatorfactory.cc @@ -33,8 +33,6 @@ #include "talk/p2p/base/basicpacketsocketfactory.h" #include "talk/p2p/client/basicportallocator.h" -static const char kUserAgent[] = "PeerConnection User Agent"; - namespace webrtc { using talk_base::scoped_ptr; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/remoteaudiosource.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/remoteaudiosource.cc new file mode 100644 index 00000000000..1c275c74c67 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/remoteaudiosource.cc @@ -0,0 +1,72 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/app/webrtc/remoteaudiosource.h" + +#include <algorithm> +#include <functional> + +#include "talk/base/logging.h" + +namespace webrtc { + +talk_base::scoped_refptr<RemoteAudioSource> RemoteAudioSource::Create() { + return new talk_base::RefCountedObject<RemoteAudioSource>(); +} + +RemoteAudioSource::RemoteAudioSource() { +} + +RemoteAudioSource::~RemoteAudioSource() { + ASSERT(audio_observers_.empty()); +} + +MediaSourceInterface::SourceState RemoteAudioSource::state() const { + return MediaSourceInterface::kLive; +} + +void RemoteAudioSource::SetVolume(double volume) { + ASSERT(volume >= 0 && volume <= 10); + for (AudioObserverList::iterator it = audio_observers_.begin(); + it != audio_observers_.end(); ++it) { + (*it)->OnSetVolume(volume); + } +} + +void RemoteAudioSource::RegisterAudioObserver(AudioObserver* observer) { + ASSERT(observer != NULL); + ASSERT(std::find(audio_observers_.begin(), audio_observers_.end(), + observer) == audio_observers_.end()); + audio_observers_.push_back(observer); +} + +void RemoteAudioSource::UnregisterAudioObserver(AudioObserver* observer) { + ASSERT(observer != NULL); + audio_observers_.remove(observer); +} + +} // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/textchatreceivetask.h b/chromium/third_party/libjingle/source/talk/app/webrtc/remoteaudiosource.h index e2776927a72..ed2421449af 100644 --- a/chromium/third_party/libjingle/source/talk/examples/chat/textchatreceivetask.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/remoteaudiosource.h @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2013, Google Inc. + * Copyright 2014, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,39 +25,42 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_EXAMPLES_CHAT_TEXTCHATRECEIVETASK_H_ -#define TALK_EXAMPLES_CHAT_TEXTCHATRECEIVETASK_H_ +#ifndef TALK_APP_WEBRTC_REMOTEAUDIOSOURCE_H_ +#define TALK_APP_WEBRTC_REMOTEAUDIOSOURCE_H_ -#include "talk/base/sigslot.h" -#include "talk/xmpp/xmpptask.h" +#include <list> -namespace buzz { +#include "talk/app/webrtc/mediastreaminterface.h" +#include "talk/app/webrtc/notifier.h" -// A class to receive chat messages from the XMPP server. -class TextChatReceiveTask : public XmppTask { +namespace webrtc { + +using webrtc::AudioSourceInterface; + +// This class implements the audio source used by the remote audio track. +class RemoteAudioSource : public Notifier<AudioSourceInterface> { public: - // Arguments: - // parent a reference to task interface associated withe the XMPP client. - explicit TextChatReceiveTask(XmppTaskParentInterface* parent); + // Creates an instance of RemoteAudioSource. + static talk_base::scoped_refptr<RemoteAudioSource> Create(); + + protected: + RemoteAudioSource(); + virtual ~RemoteAudioSource(); - // Shuts down the thread associated with this task. - virtual ~TextChatReceiveTask(); + private: + typedef std::list<AudioObserver*> AudioObserverList; - // Starts pulling queued status messages and dispatching them to the - // PresenceUpdate() callback. - virtual int ProcessStart(); + // MediaSourceInterface implementation. + virtual MediaSourceInterface::SourceState state() const OVERRIDE; - // Slot for chat message callbacks - sigslot::signal3<const Jid&, const Jid&, const std::string&> - SignalTextChatReceived; + // AudioSourceInterface implementation. + virtual void SetVolume(double volume) OVERRIDE; + virtual void RegisterAudioObserver(AudioObserver* observer) OVERRIDE; + virtual void UnregisterAudioObserver(AudioObserver* observer) OVERRIDE; - protected: - // Called by the XMPP client when chat stanzas arrive. We pull out the - // interesting parts and send them to the SignalTextCharReceived() slot. - virtual bool HandleStanza(const XmlElement* stanza); + AudioObserverList audio_observers_; }; -} // namespace buzz - -#endif // TALK_EXAMPLES_CHAT_TEXTCHATRECEIVETASK_H_ +} // namespace webrtc +#endif // TALK_APP_WEBRTC_REMOTEAUDIOSOURCE_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/sctputils.cc index 4073905de7e..dcc6ba6eac3 100644 --- a/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/sctputils.cc @@ -25,19 +25,19 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "talk/media/sctp/sctputils.h" +#include "talk/app/webrtc/sctputils.h" -#include "talk/app/webrtc/datachannelinterface.h" #include "talk/base/buffer.h" #include "talk/base/bytebuffer.h" #include "talk/base/logging.h" -namespace cricket { +namespace webrtc { // Format defined at -// http://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-00#section-6.1 +// http://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-01#section static const uint8 DATA_CHANNEL_OPEN_MESSAGE_TYPE = 0x03; +static const uint8 DATA_CHANNEL_OPEN_ACK_MESSAGE_TYPE = 0x02; enum DataChannelOpenMessageChannelType { DCOMCT_ORDERED_RELIABLE = 0x00, @@ -48,10 +48,9 @@ enum DataChannelOpenMessageChannelType { DCOMCT_UNORDERED_PARTIAL_TIME = 0x82, }; -bool ParseDataChannelOpenMessage( - const talk_base::Buffer& payload, - std::string* label, - webrtc::DataChannelInit* config) { +bool ParseDataChannelOpenMessage(const talk_base::Buffer& payload, + std::string* label, + DataChannelInit* config) { // Format defined at // http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04 @@ -123,14 +122,28 @@ bool ParseDataChannelOpenMessage( config->maxRetransmitTime = reliability_param; break; } + return true; +} + +bool ParseDataChannelOpenAckMessage(const talk_base::Buffer& payload) { + talk_base::ByteBuffer buffer(payload.data(), payload.length()); + uint8 message_type; + if (!buffer.ReadUInt8(&message_type)) { + LOG(LS_WARNING) << "Could not read OPEN_ACK message type."; + return false; + } + if (message_type != DATA_CHANNEL_OPEN_ACK_MESSAGE_TYPE) { + LOG(LS_WARNING) << "Data Channel OPEN_ACK message of unexpected type: " + << message_type; + return false; + } return true; } -bool WriteDataChannelOpenMessage( - const std::string& label, - const webrtc::DataChannelInit& config, - talk_base::Buffer* payload) { +bool WriteDataChannelOpenMessage(const std::string& label, + const DataChannelInit& config, + talk_base::Buffer* payload) { // Format defined at // http://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-00#section-6.1 uint8 channel_type = 0; @@ -173,4 +186,9 @@ bool WriteDataChannelOpenMessage( return true; } -} // namespace cricket +void WriteDataChannelOpenAckMessage(talk_base::Buffer* payload) { + talk_base::ByteBuffer buffer(talk_base::ByteBuffer::ORDER_NETWORK); + buffer.WriteUInt8(DATA_CHANNEL_OPEN_ACK_MESSAGE_TYPE); + payload->SetData(buffer.Data(), buffer.Length()); +} +} // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.h b/chromium/third_party/libjingle/source/talk/app/webrtc/sctputils.h index d349274a9a6..d0b4e9c36b2 100644 --- a/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/sctputils.h @@ -25,29 +25,31 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_MEDIA_BASE_SCTPUTILS_H_ -#define TALK_MEDIA_BASE_SCTPUTILS_H_ +#ifndef TALK_APP_WEBRTC_SCTPUTILS_H_ +#define TALK_APP_WEBRTC_SCTPUTILS_H_ #include <string> +#include "talk/app/webrtc/datachannelinterface.h" + namespace talk_base { class Buffer; } // namespace talk_base namespace webrtc { struct DataChannelInit; -} // namespace webrtc - -namespace cricket { bool ParseDataChannelOpenMessage(const talk_base::Buffer& payload, std::string* label, - webrtc::DataChannelInit* config); + DataChannelInit* config); + +bool ParseDataChannelOpenAckMessage(const talk_base::Buffer& payload); bool WriteDataChannelOpenMessage(const std::string& label, - const webrtc::DataChannelInit& config, + const DataChannelInit& config, talk_base::Buffer* payload); -} // namespace cricket +void WriteDataChannelOpenAckMessage(talk_base::Buffer* payload); +} // namespace webrtc -#endif // TALK_MEDIA_BASE_SCTPUTILS_H_ +#endif // TALK_APP_WEBRTC_SCTPUTILS_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/sctp/sctputils_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/sctputils_unittest.cc index 70f67b8b581..6a139a04c20 100644 --- a/chromium/third_party/libjingle/source/talk/media/sctp/sctputils_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/sctputils_unittest.cc @@ -25,10 +25,9 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "talk/app/webrtc/datachannelinterface.h" #include "talk/base/bytebuffer.h" #include "talk/base/gunit.h" -#include "talk/media/sctp/sctputils.h" +#include "talk/app/webrtc/sctputils.h" class SctpUtilsTest : public testing::Test { public: @@ -80,23 +79,22 @@ class SctpUtilsTest : public testing::Test { } }; -TEST_F(SctpUtilsTest, WriteParseMessageWithOrderedReliable) { - std::string input_label = "abc"; +TEST_F(SctpUtilsTest, WriteParseOpenMessageWithOrderedReliable) { webrtc::DataChannelInit config; + std::string label = "abc"; config.protocol = "y"; talk_base::Buffer packet; - ASSERT_TRUE( - cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); + ASSERT_TRUE(webrtc::WriteDataChannelOpenMessage(label, config, &packet)); - VerifyOpenMessageFormat(packet, input_label, config); + VerifyOpenMessageFormat(packet, label, config); std::string output_label; webrtc::DataChannelInit output_config; - ASSERT_TRUE(cricket::ParseDataChannelOpenMessage( + ASSERT_TRUE(webrtc::ParseDataChannelOpenMessage( packet, &output_label, &output_config)); - EXPECT_EQ(input_label, output_label); + EXPECT_EQ(label, output_label); EXPECT_EQ(config.protocol, output_config.protocol); EXPECT_EQ(config.ordered, output_config.ordered); EXPECT_EQ(config.maxRetransmitTime, output_config.maxRetransmitTime); @@ -104,24 +102,23 @@ TEST_F(SctpUtilsTest, WriteParseMessageWithOrderedReliable) { } TEST_F(SctpUtilsTest, WriteParseOpenMessageWithMaxRetransmitTime) { - std::string input_label = "abc"; webrtc::DataChannelInit config; + std::string label = "abc"; config.ordered = false; config.maxRetransmitTime = 10; config.protocol = "y"; talk_base::Buffer packet; - ASSERT_TRUE( - cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); + ASSERT_TRUE(webrtc::WriteDataChannelOpenMessage(label, config, &packet)); - VerifyOpenMessageFormat(packet, input_label, config); + VerifyOpenMessageFormat(packet, label, config); std::string output_label; webrtc::DataChannelInit output_config; - ASSERT_TRUE(cricket::ParseDataChannelOpenMessage( + ASSERT_TRUE(webrtc::ParseDataChannelOpenMessage( packet, &output_label, &output_config)); - EXPECT_EQ(input_label, output_label); + EXPECT_EQ(label, output_label); EXPECT_EQ(config.protocol, output_config.protocol); EXPECT_EQ(config.ordered, output_config.ordered); EXPECT_EQ(config.maxRetransmitTime, output_config.maxRetransmitTime); @@ -129,25 +126,36 @@ TEST_F(SctpUtilsTest, WriteParseOpenMessageWithMaxRetransmitTime) { } TEST_F(SctpUtilsTest, WriteParseOpenMessageWithMaxRetransmits) { - std::string input_label = "abc"; webrtc::DataChannelInit config; + std::string label = "abc"; config.maxRetransmits = 10; config.protocol = "y"; talk_base::Buffer packet; - ASSERT_TRUE( - cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); + ASSERT_TRUE(webrtc::WriteDataChannelOpenMessage(label, config, &packet)); - VerifyOpenMessageFormat(packet, input_label, config); + VerifyOpenMessageFormat(packet, label, config); std::string output_label; webrtc::DataChannelInit output_config; - ASSERT_TRUE(cricket::ParseDataChannelOpenMessage( + ASSERT_TRUE(webrtc::ParseDataChannelOpenMessage( packet, &output_label, &output_config)); - EXPECT_EQ(input_label, output_label); + EXPECT_EQ(label, output_label); EXPECT_EQ(config.protocol, output_config.protocol); EXPECT_EQ(config.ordered, output_config.ordered); EXPECT_EQ(config.maxRetransmits, output_config.maxRetransmits); EXPECT_EQ(-1, output_config.maxRetransmitTime); } + +TEST_F(SctpUtilsTest, WriteParseAckMessage) { + talk_base::Buffer packet; + webrtc::WriteDataChannelOpenAckMessage(&packet); + + uint8 message_type; + talk_base::ByteBuffer buffer(packet.data(), packet.length()); + ASSERT_TRUE(buffer.ReadUInt8(&message_type)); + EXPECT_EQ(0x02, message_type); + + EXPECT_TRUE(webrtc::ParseDataChannelOpenAckMessage(packet)); +} diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.cc index 7e1e7eec662..67d64fbe8db 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.cc @@ -63,6 +63,18 @@ const char StatsReport::kStatsValueNameComponent[] = "googComponent"; const char StatsReport::kStatsValueNameContentName[] = "googContentName"; const char StatsReport::kStatsValueNameCpuLimitedResolution[] = "googCpuLimitedResolution"; +const char StatsReport::kStatsValueNameDecodingCTSG[] = + "googDecodingCTSG"; +const char StatsReport::kStatsValueNameDecodingCTN[] = + "googDecodingCTN"; +const char StatsReport::kStatsValueNameDecodingNormal[] = + "googDecodingNormal"; +const char StatsReport::kStatsValueNameDecodingPLC[] = + "googDecodingPLC"; +const char StatsReport::kStatsValueNameDecodingCNG[] = + "googDecodingCNG"; +const char StatsReport::kStatsValueNameDecodingPLCCNG[] = + "googDecodingPLCCNG"; const char StatsReport::kStatsValueNameDer[] = "googDerBase64"; // Echo metrics from the audio processing module. const char StatsReport::kStatsValueNameEchoCancellationQualityMin[] = @@ -76,13 +88,18 @@ const char StatsReport::kStatsValueNameEchoReturnLoss[] = const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] = "googEchoCancellationReturnLossEnhancement"; +const char StatsReport::kStatsValueNameEncodeRelStdDev[] = + "googEncodeRelStdDev"; const char StatsReport::kStatsValueNameEncodeUsagePercent[] = "googEncodeUsagePercent"; +const char StatsReport::kStatsValueNameExpandRate[] = "googExpandRate"; const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint"; const char StatsReport::kStatsValueNameFingerprintAlgorithm[] = "googFingerprintAlgorithm"; const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived"; const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent"; +const char StatsReport::kStatsValueNameFrameHeightInput[] = + "googFrameHeightInput"; const char StatsReport::kStatsValueNameFrameHeightReceived[] = "googFrameHeightReceived"; const char StatsReport::kStatsValueNameFrameHeightSent[] = @@ -102,8 +119,13 @@ const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] = "googMinPlayoutDelayMs"; const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs"; +const char StatsReport::kStatsValueNameCaptureStartNtpTimeMs[] = + "googCaptureStartNtpTimeMs"; + const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput"; const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent"; +const char StatsReport::kStatsValueNameFrameWidthInput[] = + "googFrameWidthInput"; const char StatsReport::kStatsValueNameFrameWidthReceived[] = "googFrameWidthReceived"; const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent"; @@ -117,10 +139,21 @@ const char StatsReport::kStatsValueNameLocalCertificateId[] = "googLocalCertificateId"; const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived"; const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent"; +const char StatsReport::kStatsValueNamePlisReceived[] = "googPlisReceived"; +const char StatsReport::kStatsValueNamePlisSent[] = "googPlisSent"; const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived"; const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent"; const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost"; +const char StatsReport::kStatsValueNamePreferredJitterBufferMs[] = + "googPreferredJitterBufferMs"; const char StatsReport::kStatsValueNameReadable[] = "googReadable"; +const char StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug[] = + "googReceivedPacketGroupArrivalTimeDebug"; +const char StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug[] = + "googReceivedPacketGroupPropagationDeltaDebug"; +const char +StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[] = + "googReceivedPacketGroupPropagationDeltaSumDebug"; const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress"; const char StatsReport::kStatsValueNameRemoteCandidateType[] = "googRemoteCandidateType"; @@ -156,7 +189,6 @@ const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate"; const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo"; - // Implementations of functions in statstypes.h void StatsReport::AddValue(const std::string& name, const std::string& value) { Value temp; @@ -169,10 +201,37 @@ void StatsReport::AddValue(const std::string& name, int64 value) { AddValue(name, talk_base::ToString<int64>(value)); } +template <typename T> +void StatsReport::AddValue(const std::string& name, + const std::vector<T>& value) { + std::ostringstream oss; + oss << "["; + for (size_t i = 0; i < value.size(); ++i) { + oss << talk_base::ToString<T>(value[i]); + if (i != value.size() - 1) + oss << ", "; + } + oss << "]"; + AddValue(name, oss.str()); +} + void StatsReport::AddBoolean(const std::string& name, bool value) { AddValue(name, value ? "true" : "false"); } +void StatsReport::ReplaceValue(const std::string& name, + const std::string& value) { + for (Values::iterator it = values.begin(); it != values.end(); ++it) { + if ((*it).name == name) { + it->value = value; + return; + } + } + // It is not reachable here, add an ASSERT to make sure the overwriting is + // always a success. + ASSERT(false); +} + namespace { typedef std::map<std::string, StatsReport> StatsMap; @@ -180,6 +239,20 @@ std::string StatsId(const std::string& type, const std::string& id) { return type + "_" + id; } +std::string StatsId(const std::string& type, const std::string& id, + StatsCollector::TrackDirection direction) { + ASSERT(direction == StatsCollector::kSending || + direction == StatsCollector::kReceiving); + + // Strings for the direction of the track. + const char kSendDirection[] = "send"; + const char kRecvDirection[] = "recv"; + + const std::string direction_id = (direction == StatsCollector::kSending) ? + kSendDirection : kRecvDirection; + return type + "_" + id + "_" + direction_id; +} + bool ExtractValueFromReport( const StatsReport& report, const std::string& name, @@ -215,10 +288,33 @@ void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) { info.bytes_rcvd); report->AddValue(StatsReport::kStatsValueNameJitterReceived, info.jitter_ms); + report->AddValue(StatsReport::kStatsValueNameJitterBufferMs, + info.jitter_buffer_ms); + report->AddValue(StatsReport::kStatsValueNamePreferredJitterBufferMs, + info.jitter_buffer_preferred_ms); + report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs, + info.delay_estimate_ms); + report->AddValue(StatsReport::kStatsValueNameExpandRate, + talk_base::ToString<float>(info.expand_rate)); report->AddValue(StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd); report->AddValue(StatsReport::kStatsValueNamePacketsLost, info.packets_lost); + report->AddValue(StatsReport::kStatsValueNameDecodingCTSG, + info.decoding_calls_to_silence_generator); + report->AddValue(StatsReport::kStatsValueNameDecodingCTN, + info.decoding_calls_to_neteq); + report->AddValue(StatsReport::kStatsValueNameDecodingNormal, + info.decoding_normal); + report->AddValue(StatsReport::kStatsValueNameDecodingPLC, + info.decoding_plc); + report->AddValue(StatsReport::kStatsValueNameDecodingCNG, + info.decoding_cng); + report->AddValue(StatsReport::kStatsValueNameDecodingPLCCNG, + info.decoding_plc_cng); + report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs, + info.capture_start_ntp_time_ms); + report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name); } void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) { @@ -228,6 +324,8 @@ void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) { info.bytes_sent); report->AddValue(StatsReport::kStatsValueNamePacketsSent, info.packets_sent); + report->AddValue(StatsReport::kStatsValueNamePacketsLost, + info.packets_lost); report->AddValue(StatsReport::kStatsValueNameJitterReceived, info.jitter_ms); report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms); @@ -256,6 +354,8 @@ void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) { report->AddValue(StatsReport::kStatsValueNameFirsSent, info.firs_sent); + report->AddValue(StatsReport::kStatsValueNamePlisSent, + info.plis_sent); report->AddValue(StatsReport::kStatsValueNameNacksSent, info.nacks_sent); report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived, @@ -283,6 +383,9 @@ void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) { info.min_playout_delay_ms); report->AddValue(StatsReport::kStatsValueNameRenderDelayMs, info.render_delay_ms); + + report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs, + info.capture_start_ntp_time_ms); } void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { @@ -290,15 +393,23 @@ void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { info.bytes_sent); report->AddValue(StatsReport::kStatsValueNamePacketsSent, info.packets_sent); + report->AddValue(StatsReport::kStatsValueNamePacketsLost, + info.packets_lost); report->AddValue(StatsReport::kStatsValueNameFirsReceived, info.firs_rcvd); + report->AddValue(StatsReport::kStatsValueNamePlisReceived, + info.plis_rcvd); report->AddValue(StatsReport::kStatsValueNameNacksReceived, info.nacks_rcvd); + report->AddValue(StatsReport::kStatsValueNameFrameWidthInput, + info.input_frame_width); + report->AddValue(StatsReport::kStatsValueNameFrameHeightInput, + info.input_frame_height); report->AddValue(StatsReport::kStatsValueNameFrameWidthSent, - info.frame_width); + info.send_frame_width); report->AddValue(StatsReport::kStatsValueNameFrameHeightSent, - info.frame_height); + info.send_frame_height); report->AddValue(StatsReport::kStatsValueNameFrameRateInput, info.framerate_input); report->AddValue(StatsReport::kStatsValueNameFrameRateSent, @@ -318,10 +429,13 @@ void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { info.capture_queue_delay_ms_per_s); report->AddValue(StatsReport::kStatsValueNameEncodeUsagePercent, info.encode_usage_percent); + report->AddValue(StatsReport::kStatsValueNameEncodeRelStdDev, + info.encode_rsd); } void ExtractStats(const cricket::BandwidthEstimationInfo& info, double stats_gathering_started, + PeerConnectionInterface::StatsOutputLevel level, StatsReport* report) { report->id = StatsReport::kStatsReportVideoBweId; report->type = StatsReport::kStatsReportTypeBwe; @@ -346,6 +460,19 @@ void ExtractStats(const cricket::BandwidthEstimationInfo& info, info.transmit_bitrate); report->AddValue(StatsReport::kStatsValueNameBucketDelay, info.bucket_delay); + if (level >= PeerConnectionInterface::kStatsOutputLevelDebug) { + report->AddValue( + StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug, + info.total_received_propagation_delta_ms); + if (info.recent_received_propagation_delta_ms.size() > 0) { + report->AddValue( + StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug, + info.recent_received_propagation_delta_ms); + report->AddValue( + StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug, + info.recent_received_packet_group_arrival_time_ms); + } + } } void ExtractRemoteStats(const cricket::MediaSenderInfo& info, @@ -367,27 +494,29 @@ void ExtractRemoteStats(const cricket::MediaReceiverInfo& info, template<typename T> void ExtractStatsFromList(const std::vector<T>& data, const std::string& transport_id, - StatsCollector* collector) { + StatsCollector* collector, + StatsCollector::TrackDirection direction) { typename std::vector<T>::const_iterator it = data.begin(); for (; it != data.end(); ++it) { std::string id; uint32 ssrc = it->ssrc(); - // Each object can result in 2 objects, a local and a remote object. + // Each track can have stats for both local and remote objects. // TODO(hta): Handle the case of multiple SSRCs per object. - StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id); - if (!report) { - continue; - } - ExtractStats(*it, report); + StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id, + direction); + if (report) + ExtractStats(*it, report); + if (it->remote_stats.size() > 0) { - report = collector->PrepareRemoteReport(ssrc, transport_id); + report = collector->PrepareRemoteReport(ssrc, transport_id, + direction); if (!report) { continue; } ExtractRemoteStats(*it, report); } } -}; +} } // namespace @@ -406,6 +535,32 @@ void StatsCollector::AddStream(MediaStreamInterface* stream) { &reports_); } +void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track, + uint32 ssrc) { + ASSERT(audio_track != NULL); +#ifdef _DEBUG + for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin(); + it != local_audio_tracks_.end(); ++it) { + ASSERT(it->first != audio_track || it->second != ssrc); + } +#endif + local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc)); +} + +void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track, + uint32 ssrc) { + ASSERT(audio_track != NULL); + for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin(); + it != local_audio_tracks_.end(); ++it) { + if (it->first == audio_track && it->second == ssrc) { + local_audio_tracks_.erase(it); + return; + } + } + + ASSERT(false); +} + bool StatsCollector::GetStats(MediaStreamTrackInterface* track, StatsReports* reports) { ASSERT(reports != NULL); @@ -451,7 +606,8 @@ bool StatsCollector::GetStats(MediaStreamTrackInterface* track, return true; } -void StatsCollector::UpdateStats() { +void +StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) { double time_now = GetTimeNow(); // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of // ms apart will be ignored. @@ -464,24 +620,22 @@ void StatsCollector::UpdateStats() { if (session_) { ExtractSessionInfo(); ExtractVoiceInfo(); - ExtractVideoInfo(); + ExtractVideoInfo(level); } } StatsReport* StatsCollector::PrepareLocalReport( uint32 ssrc, - const std::string& transport_id) { - std::string ssrc_id = talk_base::ToString<uint32>(ssrc); + const std::string& transport_id, + TrackDirection direction) { + const std::string ssrc_id = talk_base::ToString<uint32>(ssrc); StatsMap::iterator it = reports_.find(StatsId( - StatsReport::kStatsReportTypeSsrc, ssrc_id)); + StatsReport::kStatsReportTypeSsrc, ssrc_id, direction)); std::string track_id; if (it == reports_.end()) { - if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) { - LOG(LS_WARNING) << "The SSRC " << ssrc - << " is not associated with a track"; + if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) return NULL; - } } else { // Keeps the old track id since we want to report the stats for inactive // tracks. @@ -491,7 +645,7 @@ StatsReport* StatsCollector::PrepareLocalReport( } StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc, - ssrc_id); + ssrc_id, direction); // Clear out stats from previous GatherStats calls if any. if (report->timestamp != stats_gathering_started_) { @@ -509,18 +663,16 @@ StatsReport* StatsCollector::PrepareLocalReport( StatsReport* StatsCollector::PrepareRemoteReport( uint32 ssrc, - const std::string& transport_id) { - std::string ssrc_id = talk_base::ToString<uint32>(ssrc); + const std::string& transport_id, + TrackDirection direction) { + const std::string ssrc_id = talk_base::ToString<uint32>(ssrc); StatsMap::iterator it = reports_.find(StatsId( - StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id)); + StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction)); std::string track_id; if (it == reports_.end()) { - if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) { - LOG(LS_WARNING) << "The SSRC " << ssrc - << " is not associated with a track"; + if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) return NULL; - } } else { // Keeps the old track id since we want to report the stats for inactive // tracks. @@ -530,7 +682,7 @@ StatsReport* StatsCollector::PrepareRemoteReport( } StatsReport* report = GetOrCreateReport( - StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id); + StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction); // Clear out stats from previous GatherStats calls if any. // The timestamp will be added later. Zero it for debugging. @@ -557,6 +709,14 @@ std::string StatsCollector::AddOneCertificateReport( talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint( talk_base::SSLFingerprint::Create(digest_algorithm, cert)); + + // SSLFingerprint::Create can fail if the algorithm returned by + // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the + // implementation of SSLCertificate::ComputeDigest. This currently happens + // with MD5- and SHA-224-signed certificates when linked to libNSS. + if (!ssl_fingerprint) + return std::string(); + std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint(); talk_base::Buffer der_buffer; @@ -721,16 +881,23 @@ void StatsCollector::ExtractVoiceInfo() { << session_->voice_channel()->content_name(); return; } - ExtractStatsFromList(voice_info.receivers, transport_id, this); - ExtractStatsFromList(voice_info.senders, transport_id, this); + ExtractStatsFromList(voice_info.receivers, transport_id, this, kReceiving); + ExtractStatsFromList(voice_info.senders, transport_id, this, kSending); + + UpdateStatsFromExistingLocalAudioTracks(); } -void StatsCollector::ExtractVideoInfo() { +void StatsCollector::ExtractVideoInfo( + PeerConnectionInterface::StatsOutputLevel level) { if (!session_->video_channel()) { return; } + cricket::StatsOptions options; + options.include_received_propagation_stats = + (level >= PeerConnectionInterface::kStatsOutputLevelDebug) ? + true : false; cricket::VideoMediaInfo video_info; - if (!session_->video_channel()->GetStats(&video_info)) { + if (!session_->video_channel()->GetStats(options, &video_info)) { LOG(LS_ERROR) << "Failed to get video channel stats."; return; } @@ -741,14 +908,14 @@ void StatsCollector::ExtractVideoInfo() { << session_->video_channel()->content_name(); return; } - ExtractStatsFromList(video_info.receivers, transport_id, this); - ExtractStatsFromList(video_info.senders, transport_id, this); + ExtractStatsFromList(video_info.receivers, transport_id, this, kReceiving); + ExtractStatsFromList(video_info.senders, transport_id, this, kSending); if (video_info.bw_estimations.size() != 1) { LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size(); } else { StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId]; ExtractStats( - video_info.bw_estimations[0], stats_gathering_started_, report); + video_info.bw_estimations[0], stats_gathering_started_, level, report); } } @@ -774,19 +941,118 @@ bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy, return true; } -StatsReport* StatsCollector::GetOrCreateReport(const std::string& type, - const std::string& id) { - std::string statsid = StatsId(type, id); +StatsReport* StatsCollector::GetReport(const std::string& type, + const std::string& id, + TrackDirection direction) { + ASSERT(type == StatsReport::kStatsReportTypeSsrc || + type == StatsReport::kStatsReportTypeRemoteSsrc); + std::string statsid = StatsId(type, id, direction); StatsReport* report = NULL; std::map<std::string, StatsReport>::iterator it = reports_.find(statsid); - if (it == reports_.end()) { + if (it != reports_.end()) + report = &(it->second); + + return report; +} + +StatsReport* StatsCollector::GetOrCreateReport(const std::string& type, + const std::string& id, + TrackDirection direction) { + ASSERT(type == StatsReport::kStatsReportTypeSsrc || + type == StatsReport::kStatsReportTypeRemoteSsrc); + StatsReport* report = GetReport(type, id, direction); + if (report == NULL) { + std::string statsid = StatsId(type, id, direction); report = &reports_[statsid]; // Create new element. report->id = statsid; report->type = type; - } else { - report = &(it->second); } + return report; } +void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() { + // Loop through the existing local audio tracks. + for (LocalAudioTrackVector::const_iterator it = local_audio_tracks_.begin(); + it != local_audio_tracks_.end(); ++it) { + AudioTrackInterface* track = it->first; + uint32 ssrc = it->second; + std::string ssrc_id = talk_base::ToString<uint32>(ssrc); + StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc, + ssrc_id, + kSending); + if (report == NULL) { + // This can happen if a local audio track is added to a stream on the + // fly and the report has not been set up yet. Do nothing in this case. + LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc; + continue; + } + + // The same ssrc can be used by both local and remote audio tracks. + std::string track_id; + if (!ExtractValueFromReport(*report, + StatsReport::kStatsValueNameTrackId, + &track_id) || + track_id != track->id()) { + continue; + } + + UpdateReportFromAudioTrack(track, report); + } +} + +void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track, + StatsReport* report) { + ASSERT(track != NULL); + if (report == NULL) + return; + + int signal_level = 0; + if (track->GetSignalLevel(&signal_level)) { + report->ReplaceValue(StatsReport::kStatsValueNameAudioInputLevel, + talk_base::ToString<int>(signal_level)); + } + + talk_base::scoped_refptr<AudioProcessorInterface> audio_processor( + track->GetAudioProcessor()); + if (audio_processor.get() == NULL) + return; + + AudioProcessorInterface::AudioProcessorStats stats; + audio_processor->GetStats(&stats); + report->ReplaceValue(StatsReport::kStatsValueNameTypingNoiseState, + stats.typing_noise_detected ? "true" : "false"); + report->ReplaceValue(StatsReport::kStatsValueNameEchoReturnLoss, + talk_base::ToString<int>(stats.echo_return_loss)); + report->ReplaceValue( + StatsReport::kStatsValueNameEchoReturnLossEnhancement, + talk_base::ToString<int>(stats.echo_return_loss_enhancement)); + report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayMedian, + talk_base::ToString<int>(stats.echo_delay_median_ms)); + report->ReplaceValue(StatsReport::kStatsValueNameEchoCancellationQualityMin, + talk_base::ToString<float>(stats.aec_quality_min)); + report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayStdDev, + talk_base::ToString<int>(stats.echo_delay_std_ms)); +} + +bool StatsCollector::GetTrackIdBySsrc(uint32 ssrc, std::string* track_id, + TrackDirection direction) { + if (direction == kSending) { + if (!session()->GetLocalTrackIdBySsrc(ssrc, track_id)) { + LOG(LS_WARNING) << "The SSRC " << ssrc + << " is not associated with a sending track"; + return false; + } + } else { + ASSERT(direction == kReceiving); + if (!session()->GetRemoteTrackIdBySsrc(ssrc, track_id)) { + LOG(LS_WARNING) << "The SSRC " << ssrc + << " is not associated with a receiving track"; + return false; + } + } + + return true; +} + } // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.h b/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.h index 01da059b510..77a1ba086df 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.h @@ -31,10 +31,12 @@ #ifndef TALK_APP_WEBRTC_STATSCOLLECTOR_H_ #define TALK_APP_WEBRTC_STATSCOLLECTOR_H_ -#include <string> #include <map> +#include <string> +#include <vector> #include "talk/app/webrtc/mediastreaminterface.h" +#include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/app/webrtc/statstypes.h" #include "talk/app/webrtc/webrtcsession.h" @@ -44,6 +46,11 @@ namespace webrtc { class StatsCollector { public: + enum TrackDirection { + kSending = 0, + kReceiving, + }; + StatsCollector(); // Register the session Stats should operate on. @@ -56,20 +63,30 @@ class StatsCollector { // to GetStats. void AddStream(MediaStreamInterface* stream); + // Adds a local audio track that is used for getting some voice statistics. + void AddLocalAudioTrack(AudioTrackInterface* audio_track, uint32 ssrc); + + // Removes a local audio tracks that is used for getting some voice + // statistics. + void RemoveLocalAudioTrack(AudioTrackInterface* audio_track, uint32 ssrc); + // Gather statistics from the session and store them for future use. - void UpdateStats(); + void UpdateStats(PeerConnectionInterface::StatsOutputLevel level); // Gets a StatsReports of the last collected stats. Note that UpdateStats must // be called before this function to get the most recent stats. |selector| is // a track label or empty string. The most recent reports are stored in // |reports|. - bool GetStats(MediaStreamTrackInterface* track, StatsReports* reports); + bool GetStats(MediaStreamTrackInterface* track, + StatsReports* reports); // Prepare an SSRC report for the given ssrc. Used internally // in the ExtractStatsFromList template. - StatsReport* PrepareLocalReport(uint32 ssrc, const std::string& transport); + StatsReport* PrepareLocalReport(uint32 ssrc, const std::string& transport, + TrackDirection direction); // Prepare an SSRC report for the given remote ssrc. Used internally. - StatsReport* PrepareRemoteReport(uint32 ssrc, const std::string& transport); + StatsReport* PrepareRemoteReport(uint32 ssrc, const std::string& transport, + TrackDirection direction); // Extracts the ID of a Transport belonging to an SSRC. Used internally. bool GetTransportIdFromProxy(const std::string& proxy, std::string* transport_id); @@ -87,12 +104,26 @@ class StatsCollector { void ExtractSessionInfo(); void ExtractVoiceInfo(); - void ExtractVideoInfo(); + void ExtractVideoInfo(PeerConnectionInterface::StatsOutputLevel level); double GetTimeNow(); void BuildSsrcToTransportId(); WebRtcSession* session() { return session_; } webrtc::StatsReport* GetOrCreateReport(const std::string& type, - const std::string& id); + const std::string& id, + TrackDirection direction); + webrtc::StatsReport* GetReport(const std::string& type, + const std::string& id, + TrackDirection direction); + + // Helper method to get stats from the local audio tracks. + void UpdateStatsFromExistingLocalAudioTracks(); + void UpdateReportFromAudioTrack(AudioTrackInterface* track, + StatsReport* report); + + // Helper method to get the id for the track identified by ssrc. + // |direction| tells if the track is for sending or receiving. + bool GetTrackIdBySsrc(uint32 ssrc, std::string* track_id, + TrackDirection direction); // A map from the report id to the report. std::map<std::string, StatsReport> reports_; @@ -101,6 +132,10 @@ class StatsCollector { double stats_gathering_started_; talk_base::Timing timing_; cricket::ProxyTransportMap proxy_to_transport_; + + typedef std::vector<std::pair<AudioTrackInterface*, uint32> > + LocalAudioTrackVector; + LocalAudioTrackVector local_audio_tracks_; }; } // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector_unittest.cc index 1adcb0e20aa..2cd716e0232 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector_unittest.cc @@ -1,5 +1,6 @@ /* * libjingle + * Copyright 2014, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -29,6 +30,8 @@ #include "talk/app/webrtc/statscollector.h" #include "talk/app/webrtc/mediastream.h" +#include "talk/app/webrtc/mediastreaminterface.h" +#include "talk/app/webrtc/mediastreamtrack.h" #include "talk/app/webrtc/videotrack.h" #include "talk/base/base64.h" #include "talk/base/fakesslidentity.h" @@ -37,13 +40,19 @@ #include "talk/media/devices/fakedevicemanager.h" #include "talk/p2p/base/fakesession.h" #include "talk/session/media/channelmanager.h" -#include "testing/base/public/gmock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +using cricket::StatsOptions; using testing::_; using testing::DoAll; +using testing::Field; using testing::Return; using testing::ReturnNull; using testing::SetArgPointee; +using webrtc::PeerConnectionInterface; +using webrtc::StatsReport; +using webrtc::StatsReports; namespace cricket { @@ -59,7 +68,8 @@ const char kNotFound[] = "NOT FOUND"; const char kNoReports[] = "NO REPORTS"; // Constant names for track identification. -const char kTrackId[] = "somename"; +const char kLocalTrackId[] = "local_track_id"; +const char kRemoteTrackId[] = "remote_track_id"; const uint32 kSsrcOfTrack = 1234; class MockWebRtcSession : public webrtc::WebRtcSession { @@ -68,8 +78,12 @@ class MockWebRtcSession : public webrtc::WebRtcSession { : WebRtcSession(channel_manager, talk_base::Thread::Current(), talk_base::Thread::Current(), NULL, NULL) { } + MOCK_METHOD0(voice_channel, cricket::VoiceChannel*()); MOCK_METHOD0(video_channel, cricket::VideoChannel*()); - MOCK_METHOD2(GetTrackIdBySsrc, bool(uint32, std::string*)); + // Libjingle uses "local" for a outgoing track, and "remote" for a incoming + // track. + MOCK_METHOD2(GetLocalTrackIdBySsrc, bool(uint32, std::string*)); + MOCK_METHOD2(GetRemoteTrackIdBySsrc, bool(uint32, std::string*)); MOCK_METHOD1(GetStats, bool(cricket::SessionStats*)); MOCK_METHOD1(GetTransport, cricket::Transport*(const std::string&)); }; @@ -80,13 +94,64 @@ class MockVideoMediaChannel : public cricket::FakeVideoMediaChannel { : cricket::FakeVideoMediaChannel(NULL) { } // MOCK_METHOD0(transport_channel, cricket::TransportChannel*()); - MOCK_METHOD1(GetStats, bool(cricket::VideoMediaInfo*)); + MOCK_METHOD2(GetStats, bool(const StatsOptions&, cricket::VideoMediaInfo*)); }; -bool GetValue(const webrtc::StatsReport* report, +class MockVoiceMediaChannel : public cricket::FakeVoiceMediaChannel { + public: + MockVoiceMediaChannel() : cricket::FakeVoiceMediaChannel(NULL) { + } + MOCK_METHOD1(GetStats, bool(cricket::VoiceMediaInfo*)); +}; + +class FakeAudioProcessor : public webrtc::AudioProcessorInterface { + public: + FakeAudioProcessor() {} + ~FakeAudioProcessor() {} + + private: + virtual void GetStats( + AudioProcessorInterface::AudioProcessorStats* stats) OVERRIDE { + stats->typing_noise_detected = true; + stats->echo_return_loss = 2; + stats->echo_return_loss_enhancement = 3; + stats->echo_delay_median_ms = 4; + stats->aec_quality_min = 5.1f; + stats->echo_delay_std_ms = 6; + } +}; + +class FakeAudioTrack + : public webrtc::MediaStreamTrack<webrtc::AudioTrackInterface> { + public: + explicit FakeAudioTrack(const std::string& id) + : webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>(id), + processor_(new talk_base::RefCountedObject<FakeAudioProcessor>()) {} + std::string kind() const OVERRIDE { + return "audio"; + } + virtual webrtc::AudioSourceInterface* GetSource() const OVERRIDE { + return NULL; + } + virtual void AddSink(webrtc::AudioTrackSinkInterface* sink) OVERRIDE {} + virtual void RemoveSink(webrtc::AudioTrackSinkInterface* sink) OVERRIDE {} + virtual bool GetSignalLevel(int* level) OVERRIDE { + *level = 1; + return true; + } + virtual talk_base::scoped_refptr<webrtc::AudioProcessorInterface> + GetAudioProcessor() OVERRIDE { + return processor_; + } + + private: + talk_base::scoped_refptr<FakeAudioProcessor> processor_; +}; + +bool GetValue(const StatsReport* report, const std::string& name, std::string* value) { - webrtc::StatsReport::Values::const_iterator it = report->values.begin(); + StatsReport::Values::const_iterator it = report->values.begin(); for (; it != report->values.end(); ++it) { if (it->name == name) { *value = it->value; @@ -97,7 +162,7 @@ bool GetValue(const webrtc::StatsReport* report, } std::string ExtractStatsValue(const std::string& type, - const webrtc::StatsReports& reports, + const StatsReports& reports, const std::string name) { if (reports.empty()) { return kNoReports; @@ -116,8 +181,8 @@ std::string ExtractStatsValue(const std::string& type, // Finds the |n|-th report of type |type| in |reports|. // |n| starts from 1 for finding the first report. -const webrtc::StatsReport* FindNthReportByType( - const webrtc::StatsReports& reports, const std::string& type, int n) { +const StatsReport* FindNthReportByType( + const StatsReports& reports, const std::string& type, int n) { for (size_t i = 0; i < reports.size(); ++i) { if (reports[i].type == type) { n--; @@ -128,8 +193,8 @@ const webrtc::StatsReport* FindNthReportByType( return NULL; } -const webrtc::StatsReport* FindReportById(const webrtc::StatsReports& reports, - const std::string& id) { +const StatsReport* FindReportById(const StatsReports& reports, + const std::string& id) { for (size_t i = 0; i < reports.size(); ++i) { if (reports[i].id == id) { return &reports[i]; @@ -138,16 +203,16 @@ const webrtc::StatsReport* FindReportById(const webrtc::StatsReports& reports, return NULL; } -std::string ExtractSsrcStatsValue(webrtc::StatsReports reports, +std::string ExtractSsrcStatsValue(StatsReports reports, const std::string& name) { return ExtractStatsValue( - webrtc::StatsReport::kStatsReportTypeSsrc, reports, name); + StatsReport::kStatsReportTypeSsrc, reports, name); } -std::string ExtractBweStatsValue(webrtc::StatsReports reports, +std::string ExtractBweStatsValue(StatsReports reports, const std::string& name) { return ExtractStatsValue( - webrtc::StatsReport::kStatsReportTypeBwe, reports, name); + StatsReport::kStatsReportTypeBwe, reports, name); } std::string DerToPem(const std::string& der) { @@ -164,18 +229,18 @@ std::vector<std::string> DersToPems( return pems; } -void CheckCertChainReports(const webrtc::StatsReports& reports, +void CheckCertChainReports(const StatsReports& reports, const std::vector<std::string>& ders, const std::string& start_id) { std::string certificate_id = start_id; size_t i = 0; while (true) { - const webrtc::StatsReport* report = FindReportById(reports, certificate_id); + const StatsReport* report = FindReportById(reports, certificate_id); ASSERT_TRUE(report != NULL); std::string der_base64; EXPECT_TRUE(GetValue( - report, webrtc::StatsReport::kStatsValueNameDer, &der_base64)); + report, StatsReport::kStatsValueNameDer, &der_base64)); std::string der = talk_base::Base64::Decode(der_base64, talk_base::Base64::DO_STRICT); EXPECT_EQ(ders[i], der); @@ -183,7 +248,7 @@ void CheckCertChainReports(const webrtc::StatsReports& reports, std::string fingerprint_algorithm; EXPECT_TRUE(GetValue( report, - webrtc::StatsReport::kStatsValueNameFingerprintAlgorithm, + StatsReport::kStatsValueNameFingerprintAlgorithm, &fingerprint_algorithm)); // The digest algorithm for a FakeSSLCertificate is always SHA-1. std::string sha_1_str = talk_base::DIGEST_SHA_1; @@ -192,17 +257,179 @@ void CheckCertChainReports(const webrtc::StatsReports& reports, std::string dummy_fingerprint; // Value is not checked. EXPECT_TRUE(GetValue( report, - webrtc::StatsReport::kStatsValueNameFingerprint, + StatsReport::kStatsValueNameFingerprint, &dummy_fingerprint)); ++i; if (!GetValue( - report, webrtc::StatsReport::kStatsValueNameIssuerId, &certificate_id)) + report, StatsReport::kStatsValueNameIssuerId, &certificate_id)) break; } EXPECT_EQ(ders.size(), i); } +void VerifyVoiceReceiverInfoReport( + const StatsReport* report, + const cricket::VoiceReceiverInfo& info) { + std::string value_in_report; + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameAudioOutputLevel, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.audio_level), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameBytesReceived, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int64>(info.bytes_rcvd), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameJitterReceived, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.jitter_ms), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameJitterBufferMs, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.jitter_buffer_ms), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNamePreferredJitterBufferMs, + &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.jitter_buffer_preferred_ms), + value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameCurrentDelayMs, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.delay_estimate_ms), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameExpandRate, &value_in_report)); + EXPECT_EQ(talk_base::ToString<float>(info.expand_rate), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNamePacketsReceived, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.packets_rcvd), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameDecodingCTSG, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.decoding_calls_to_silence_generator), + value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameDecodingCTN, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.decoding_calls_to_neteq), + value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameDecodingNormal, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.decoding_normal), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameDecodingPLC, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.decoding_plc), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameDecodingCNG, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.decoding_cng), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameDecodingPLCCNG, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(info.decoding_plc_cng), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameCodecName, &value_in_report)); +} + + +void VerifyVoiceSenderInfoReport(const StatsReport* report, + const cricket::VoiceSenderInfo& sinfo) { + std::string value_in_report; + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameCodecName, &value_in_report)); + EXPECT_EQ(sinfo.codec_name, value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameBytesSent, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int64>(sinfo.bytes_sent), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNamePacketsSent, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.packets_sent), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNamePacketsLost, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.packets_lost), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameRtt, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.rtt_ms), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameRtt, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.rtt_ms), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameJitterReceived, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.jitter_ms), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameEchoCancellationQualityMin, + &value_in_report)); + EXPECT_EQ(talk_base::ToString<float>(sinfo.aec_quality_min), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameEchoDelayMedian, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.echo_delay_median_ms), + value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameEchoDelayStdDev, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.echo_delay_std_ms), + value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameEchoReturnLoss, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.echo_return_loss), + value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameEchoReturnLossEnhancement, + &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.echo_return_loss_enhancement), + value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameAudioInputLevel, &value_in_report)); + EXPECT_EQ(talk_base::ToString<int>(sinfo.audio_level), value_in_report); + EXPECT_TRUE(GetValue( + report, StatsReport::kStatsValueNameTypingNoiseState, &value_in_report)); + std::string typing_detected = sinfo.typing_noise_detected ? "true" : "false"; + EXPECT_EQ(typing_detected, value_in_report); +} + +// Helper methods to avoid duplication of code. +void InitVoiceSenderInfo(cricket::VoiceSenderInfo* voice_sender_info) { + voice_sender_info->add_ssrc(kSsrcOfTrack); + voice_sender_info->codec_name = "fake_codec"; + voice_sender_info->bytes_sent = 100; + voice_sender_info->packets_sent = 101; + voice_sender_info->rtt_ms = 102; + voice_sender_info->fraction_lost = 103; + voice_sender_info->jitter_ms = 104; + voice_sender_info->packets_lost = 105; + voice_sender_info->ext_seqnum = 106; + voice_sender_info->audio_level = 107; + voice_sender_info->echo_return_loss = 108; + voice_sender_info->echo_return_loss_enhancement = 109; + voice_sender_info->echo_delay_median_ms = 110; + voice_sender_info->echo_delay_std_ms = 111; + voice_sender_info->aec_quality_min = 112.0f; + voice_sender_info->typing_noise_detected = false; +} + +void UpdateVoiceSenderInfoFromAudioTrack( + FakeAudioTrack* audio_track, cricket::VoiceSenderInfo* voice_sender_info) { + audio_track->GetSignalLevel(&voice_sender_info->audio_level); + webrtc::AudioProcessorInterface::AudioProcessorStats audio_processor_stats; + audio_track->GetAudioProcessor()->GetStats(&audio_processor_stats); + voice_sender_info->typing_noise_detected = + audio_processor_stats.typing_noise_detected; + voice_sender_info->echo_return_loss = audio_processor_stats.echo_return_loss; + voice_sender_info->echo_return_loss_enhancement = + audio_processor_stats.echo_return_loss_enhancement; + voice_sender_info->echo_delay_median_ms = + audio_processor_stats.echo_delay_median_ms; + voice_sender_info->aec_quality_min = audio_processor_stats.aec_quality_min; + voice_sender_info->echo_delay_std_ms = + audio_processor_stats.echo_delay_std_ms; +} + +void InitVoiceReceiverInfo(cricket::VoiceReceiverInfo* voice_receiver_info) { + voice_receiver_info->add_ssrc(kSsrcOfTrack); + voice_receiver_info->bytes_rcvd = 110; + voice_receiver_info->packets_rcvd = 111; + voice_receiver_info->packets_lost = 112; + voice_receiver_info->fraction_lost = 113; + voice_receiver_info->packets_lost = 114; + voice_receiver_info->ext_seqnum = 115; + voice_receiver_info->jitter_ms = 116; + voice_receiver_info->jitter_buffer_ms = 117; + voice_receiver_info->jitter_buffer_preferred_ms = 118; + voice_receiver_info->delay_estimate_ms = 119; + voice_receiver_info->audio_level = 120; + voice_receiver_info->expand_rate = 121; +} + class StatsCollectorTest : public testing::Test { protected: StatsCollectorTest() @@ -211,8 +438,7 @@ class StatsCollectorTest : public testing::Test { new cricket::ChannelManager(media_engine_, new cricket::FakeDeviceManager(), talk_base::Thread::Current())), - session_(channel_manager_.get()), - track_id_(kTrackId) { + session_(channel_manager_.get()) { // By default, we ignore session GetStats calls. EXPECT_CALL(session_, GetStats(_)).WillRepeatedly(Return(false)); } @@ -232,14 +458,56 @@ class StatsCollectorTest : public testing::Test { session_stats_.proxy_to_transport[vc_name] = kTransportName; } - // Adds a track with a given SSRC into the stats. - void AddVideoTrackStats() { + // Adds a outgoing video track with a given SSRC into the stats. + void AddOutgoingVideoTrackStats() { stream_ = webrtc::MediaStream::Create("streamlabel"); - track_= webrtc::VideoTrack::Create(kTrackId, NULL); + track_= webrtc::VideoTrack::Create(kLocalTrackId, NULL); stream_->AddTrack(track_); - EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(track_id_), - Return(true))); + EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(kLocalTrackId), Return(true))); + EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(Return(false)); + } + + // Adds a incoming video track with a given SSRC into the stats. + void AddIncomingVideoTrackStats() { + stream_ = webrtc::MediaStream::Create("streamlabel"); + track_= webrtc::VideoTrack::Create(kRemoteTrackId, NULL); + stream_->AddTrack(track_); + EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(kRemoteTrackId), + Return(true))); + } + + // Adds a outgoing audio track with a given SSRC into the stats. + void AddOutgoingAudioTrackStats() { + if (stream_ == NULL) + stream_ = webrtc::MediaStream::Create("streamlabel"); + + audio_track_ = new talk_base::RefCountedObject<FakeAudioTrack>( + kLocalTrackId); + stream_->AddTrack(audio_track_); + EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(kLocalTrackId), + Return(true))); + EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(Return(false)); + } + + // Adds a incoming audio track with a given SSRC into the stats. + void AddIncomingAudioTrackStats() { + if (stream_ == NULL) + stream_ = webrtc::MediaStream::Create("streamlabel"); + + audio_track_ = new talk_base::RefCountedObject<FakeAudioTrack>( + kRemoteTrackId); + stream_->AddTrack(audio_track_); + EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(kRemoteTrackId), Return(true))); } void TestCertificateReports(const talk_base::FakeSSLCertificate& local_cert, @@ -247,7 +515,7 @@ class StatsCollectorTest : public testing::Test { const talk_base::FakeSSLCertificate& remote_cert, const std::vector<std::string>& remote_ders) { webrtc::StatsCollector stats; // Implementation under test. - webrtc::StatsReports reports; // returned values. + StatsReports reports; // returned values. stats.set_session(&session_); // Fake stats to process. @@ -286,32 +554,40 @@ class StatsCollectorTest : public testing::Test { EXPECT_CALL(session_, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(session_stats), Return(true))); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); - const webrtc::StatsReport* channel_report = FindNthReportByType( - reports, webrtc::StatsReport::kStatsReportTypeComponent, 1); + const StatsReport* channel_report = FindNthReportByType( + reports, StatsReport::kStatsReportTypeComponent, 1); EXPECT_TRUE(channel_report != NULL); // Check local certificate chain. std::string local_certificate_id = ExtractStatsValue( - webrtc::StatsReport::kStatsReportTypeComponent, + StatsReport::kStatsReportTypeComponent, reports, - webrtc::StatsReport::kStatsValueNameLocalCertificateId); - EXPECT_NE(kNotFound, local_certificate_id); - CheckCertChainReports(reports, local_ders, local_certificate_id); + StatsReport::kStatsValueNameLocalCertificateId); + if (local_ders.size() > 0) { + EXPECT_NE(kNotFound, local_certificate_id); + CheckCertChainReports(reports, local_ders, local_certificate_id); + } else { + EXPECT_EQ(kNotFound, local_certificate_id); + } // Check remote certificate chain. std::string remote_certificate_id = ExtractStatsValue( - webrtc::StatsReport::kStatsReportTypeComponent, + StatsReport::kStatsReportTypeComponent, reports, - webrtc::StatsReport::kStatsValueNameRemoteCertificateId); - EXPECT_NE(kNotFound, remote_certificate_id); - CheckCertChainReports(reports, remote_ders, remote_certificate_id); + StatsReport::kStatsValueNameRemoteCertificateId); + if (remote_ders.size() > 0) { + EXPECT_NE(kNotFound, remote_certificate_id); + CheckCertChainReports(reports, remote_ders, remote_certificate_id); + } else { + EXPECT_EQ(kNotFound, remote_certificate_id); + } } cricket::FakeMediaEngine* media_engine_; @@ -320,16 +596,16 @@ class StatsCollectorTest : public testing::Test { cricket::SessionStats session_stats_; talk_base::scoped_refptr<webrtc::MediaStream> stream_; talk_base::scoped_refptr<webrtc::VideoTrack> track_; - std::string track_id_; + talk_base::scoped_refptr<FakeAudioTrack> audio_track_; }; // This test verifies that 64-bit counters are passed successfully. TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { webrtc::StatsCollector stats; // Implementation under test. - MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, "", false, NULL); - webrtc::StatsReports reports; // returned values. + StatsReports reports; // returned values. cricket::VideoSenderInfo video_sender_info; cricket::VideoMediaInfo stats_read; // The number of bytes must be larger than 0xFFFFFFFF for this test. @@ -337,7 +613,7 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { const std::string kBytesSentString("12345678901234"); stats.set_session(&session_); - AddVideoTrackStats(); + AddOutgoingVideoTrackStats(); stats.AddStream(stream_); // Construct a stats value to read. @@ -345,12 +621,12 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillOnce(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillOnce(DoAll(SetArgPointee<1>(stats_read), Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); std::string result = ExtractSsrcStatsValue(reports, "bytesSent"); EXPECT_EQ(kBytesSentString, result); @@ -359,10 +635,10 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { // Test that BWE information is reported via stats. TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { webrtc::StatsCollector stats; // Implementation under test. - MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, "", false, NULL); - webrtc::StatsReports reports; // returned values. + StatsReports reports; // returned values. cricket::VideoSenderInfo video_sender_info; cricket::VideoMediaInfo stats_read; // Set up an SSRC just to test that we get both kinds of stats back: SSRC and @@ -371,7 +647,7 @@ TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { const std::string kBytesSentString("12345678901234"); stats.set_session(&session_); - AddVideoTrackStats(); + AddOutgoingVideoTrackStats(); stats.AddStream(stream_); // Construct a stats value to read. @@ -384,13 +660,13 @@ TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { bwe.target_enc_bitrate = kTargetEncBitrate; stats_read.bw_estimations.push_back(bwe); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillOnce(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillOnce(DoAll(SetArgPointee<1>(stats_read), Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); std::string result = ExtractSsrcStatsValue(reports, "bytesSent"); EXPECT_EQ(kBytesSentString, result); @@ -402,14 +678,14 @@ TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { // exists in the returned stats. TEST_F(StatsCollectorTest, SessionObjectExists) { webrtc::StatsCollector stats; // Implementation under test. - webrtc::StatsReports reports; // returned values. + StatsReports reports; // returned values. stats.set_session(&session_); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(ReturnNull()); - stats.UpdateStats(); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); - const webrtc::StatsReport* session_report = FindNthReportByType( - reports, webrtc::StatsReport::kStatsReportTypeSession, 1); + const StatsReport* session_report = FindNthReportByType( + reports, StatsReport::kStatsReportTypeSession, 1); EXPECT_FALSE(session_report == NULL); } @@ -417,18 +693,18 @@ TEST_F(StatsCollectorTest, SessionObjectExists) { // in the returned stats. TEST_F(StatsCollectorTest, OnlyOneSessionObjectExists) { webrtc::StatsCollector stats; // Implementation under test. - webrtc::StatsReports reports; // returned values. + StatsReports reports; // returned values. stats.set_session(&session_); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(ReturnNull()); - stats.UpdateStats(); - stats.UpdateStats(); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); - const webrtc::StatsReport* session_report = FindNthReportByType( - reports, webrtc::StatsReport::kStatsReportTypeSession, 1); + const StatsReport* session_report = FindNthReportByType( + reports, StatsReport::kStatsReportTypeSession, 1); EXPECT_FALSE(session_report == NULL); session_report = FindNthReportByType( - reports, webrtc::StatsReport::kStatsReportTypeSession, 2); + reports, StatsReport::kStatsReportTypeSession, 2); EXPECT_EQ(NULL, session_report); } @@ -436,42 +712,42 @@ TEST_F(StatsCollectorTest, OnlyOneSessionObjectExists) { // without calling StatsCollector::UpdateStats. TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) { webrtc::StatsCollector stats; // Implementation under test. - MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, "", false, NULL); - AddVideoTrackStats(); + AddOutgoingVideoTrackStats(); stats.AddStream(stream_); stats.set_session(&session_); - webrtc::StatsReports reports; + StatsReports reports; // Verfies the existence of the track report. stats.GetStats(NULL, &reports); EXPECT_EQ((size_t)1, reports.size()); - EXPECT_EQ(std::string(webrtc::StatsReport::kStatsReportTypeTrack), + EXPECT_EQ(std::string(StatsReport::kStatsReportTypeTrack), reports[0].type); std::string trackValue = - ExtractStatsValue(webrtc::StatsReport::kStatsReportTypeTrack, + ExtractStatsValue(StatsReport::kStatsReportTypeTrack, reports, - webrtc::StatsReport::kStatsValueNameTrackId); - EXPECT_EQ(kTrackId, trackValue); + StatsReport::kStatsValueNameTrackId); + EXPECT_EQ(kLocalTrackId, trackValue); } // This test verifies that the empty track report exists in the returned stats // when StatsCollector::UpdateStats is called with ssrc stats. TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { webrtc::StatsCollector stats; // Implementation under test. - MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, "", false, NULL); - AddVideoTrackStats(); + AddOutgoingVideoTrackStats(); stats.AddStream(stream_); stats.set_session(&session_); - webrtc::StatsReports reports; + StatsReports reports; // Constructs an ssrc stats update. cricket::VideoSenderInfo video_sender_info; @@ -483,53 +759,54 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillOnce(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillOnce(DoAll(SetArgPointee<1>(stats_read), Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); // |reports| should contain at least one session report, one track report, // and one ssrc report. EXPECT_LE((size_t)3, reports.size()); - const webrtc::StatsReport* track_report = FindNthReportByType( - reports, webrtc::StatsReport::kStatsReportTypeTrack, 1); - EXPECT_FALSE(track_report == NULL); + const StatsReport* track_report = FindNthReportByType( + reports, StatsReport::kStatsReportTypeTrack, 1); + EXPECT_TRUE(track_report); + // Get report for the specific |track|. stats.GetStats(track_, &reports); // |reports| should contain at least one session report, one track report, // and one ssrc report. EXPECT_LE((size_t)3, reports.size()); track_report = FindNthReportByType( - reports, webrtc::StatsReport::kStatsReportTypeTrack, 1); - EXPECT_FALSE(track_report == NULL); + reports, StatsReport::kStatsReportTypeTrack, 1); + EXPECT_TRUE(track_report); std::string ssrc_id = ExtractSsrcStatsValue( - reports, webrtc::StatsReport::kStatsValueNameSsrc); + reports, StatsReport::kStatsValueNameSsrc); EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id); std::string track_id = ExtractSsrcStatsValue( - reports, webrtc::StatsReport::kStatsValueNameTrackId); - EXPECT_EQ(kTrackId, track_id); + reports, StatsReport::kStatsValueNameTrackId); + EXPECT_EQ(kLocalTrackId, track_id); } // This test verifies that an SSRC object has the identifier of a Transport // stats object, and that this transport stats object exists in stats. TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { webrtc::StatsCollector stats; // Implementation under test. - MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); // The content_name known by the video channel. const std::string kVcName("vcname"); cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, kVcName, false, NULL); - AddVideoTrackStats(); + AddOutgoingVideoTrackStats(); stats.AddStream(stream_); stats.set_session(&session_); - webrtc::StatsReports reports; + StatsReports reports; // Constructs an ssrc stats update. cricket::VideoSenderInfo video_sender_info; @@ -541,26 +818,26 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(stats_read), Return(true))); InitSessionStats(kVcName); EXPECT_CALL(session_, GetStats(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), - Return(true))); + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), + Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); std::string transport_id = ExtractStatsValue( - webrtc::StatsReport::kStatsReportTypeSsrc, + StatsReport::kStatsReportTypeSsrc, reports, - webrtc::StatsReport::kStatsValueNameTransportId); + StatsReport::kStatsValueNameTransportId); ASSERT_NE(kNotFound, transport_id); - const webrtc::StatsReport* transport_report = FindReportById(reports, - transport_id); + const StatsReport* transport_report = FindReportById(reports, + transport_id); ASSERT_FALSE(transport_report == NULL); } @@ -568,24 +845,24 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { // an outgoing SSRC where remote stats are not returned. TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { webrtc::StatsCollector stats; // Implementation under test. - MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); // The content_name known by the video channel. const std::string kVcName("vcname"); cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, kVcName, false, NULL); - AddVideoTrackStats(); + AddOutgoingVideoTrackStats(); stats.AddStream(stream_); stats.set_session(&session_); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); - stats.UpdateStats(); - webrtc::StatsReports reports; + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); + StatsReports reports; stats.GetStats(NULL, &reports); - const webrtc::StatsReport* remote_report = FindNthReportByType(reports, - webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1); + const StatsReport* remote_report = FindNthReportByType(reports, + StatsReport::kStatsReportTypeRemoteSsrc, 1); EXPECT_TRUE(remote_report == NULL); } @@ -593,23 +870,23 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { // an outgoing SSRC where stats are returned. TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { webrtc::StatsCollector stats; // Implementation under test. - MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); // The content_name known by the video channel. const std::string kVcName("vcname"); cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, kVcName, false, NULL); - AddVideoTrackStats(); + AddOutgoingVideoTrackStats(); stats.AddStream(stream_); stats.set_session(&session_); - webrtc::StatsReports reports; + StatsReports reports; // Instruct the session to return stats containing the transport channel. InitSessionStats(kVcName); EXPECT_CALL(session_, GetStats(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), - Return(true))); + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), + Return(true))); // Constructs an ssrc stats update. cricket::VideoMediaInfo stats_read; @@ -622,20 +899,69 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { video_sender_info.remote_stats.push_back(remote_ssrc_stats); stats_read.senders.push_back(video_sender_info); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(Return(&video_channel)); - EXPECT_CALL(*media_channel, GetStats(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), + EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(stats_read), Return(true))); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); - const webrtc::StatsReport* remote_report = FindNthReportByType(reports, - webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1); + + const StatsReport* remote_report = FindNthReportByType(reports, + StatsReport::kStatsReportTypeRemoteSsrc, 1); EXPECT_FALSE(remote_report == NULL); EXPECT_NE(0, remote_report->timestamp); } +// This test verifies that the empty track report exists in the returned stats +// when StatsCollector::UpdateStats is called with ssrc stats. +TEST_F(StatsCollectorTest, ReportsFromRemoteTrack) { + webrtc::StatsCollector stats; // Implementation under test. + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); + cricket::VideoChannel video_channel(talk_base::Thread::Current(), + media_engine_, media_channel, &session_, "", false, NULL); + AddIncomingVideoTrackStats(); + stats.AddStream(stream_); + + stats.set_session(&session_); + + StatsReports reports; + + // Constructs an ssrc stats update. + cricket::VideoReceiverInfo video_receiver_info; + cricket::VideoMediaInfo stats_read; + const int64 kNumOfPacketsConcealed = 54321; + + // Construct a stats value to read. + video_receiver_info.add_ssrc(1234); + video_receiver_info.packets_concealed = kNumOfPacketsConcealed; + stats_read.receivers.push_back(video_receiver_info); + + EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_, _)) + .WillOnce(DoAll(SetArgPointee<1>(stats_read), + Return(true))); + + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); + stats.GetStats(NULL, &reports); + // |reports| should contain at least one session report, one track report, + // and one ssrc report. + EXPECT_LE(static_cast<size_t>(3), reports.size()); + const StatsReport* track_report = FindNthReportByType( + reports, StatsReport::kStatsReportTypeTrack, 1); + EXPECT_TRUE(track_report); + + std::string ssrc_id = ExtractSsrcStatsValue( + reports, StatsReport::kStatsValueNameSsrc); + EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id); + + std::string track_id = ExtractSsrcStatsValue( + reports, StatsReport::kStatsValueNameTrackId); + EXPECT_EQ(kRemoteTrackId, track_id); +} + // This test verifies that all chained certificates are correctly // reported TEST_F(StatsCollectorTest, ChainedCertificateReportsCreated) { @@ -678,7 +1004,7 @@ TEST_F(StatsCollectorTest, ChainlessCertificateReportsCreated) { // transport is present. TEST_F(StatsCollectorTest, NoTransport) { webrtc::StatsCollector stats; // Implementation under test. - webrtc::StatsReports reports; // returned values. + StatsReports reports; // returned values. stats.set_session(&session_); // Fake stats to process. @@ -700,24 +1026,24 @@ TEST_F(StatsCollectorTest, NoTransport) { .WillOnce(DoAll(SetArgPointee<0>(session_stats), Return(true))); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); // Check that the local certificate is absent. std::string local_certificate_id = ExtractStatsValue( - webrtc::StatsReport::kStatsReportTypeComponent, + StatsReport::kStatsReportTypeComponent, reports, - webrtc::StatsReport::kStatsValueNameLocalCertificateId); + StatsReport::kStatsValueNameLocalCertificateId); ASSERT_EQ(kNotFound, local_certificate_id); // Check that the remote certificate is absent. std::string remote_certificate_id = ExtractStatsValue( - webrtc::StatsReport::kStatsReportTypeComponent, + StatsReport::kStatsReportTypeComponent, reports, - webrtc::StatsReport::kStatsValueNameRemoteCertificateId); + StatsReport::kStatsValueNameRemoteCertificateId); ASSERT_EQ(kNotFound, remote_certificate_id); } @@ -725,7 +1051,7 @@ TEST_F(StatsCollectorTest, NoTransport) { // does not have any certificates. TEST_F(StatsCollectorTest, NoCertificates) { webrtc::StatsCollector stats; // Implementation under test. - webrtc::StatsReports reports; // returned values. + StatsReports reports; // returned values. stats.set_session(&session_); // Fake stats to process. @@ -753,25 +1079,343 @@ TEST_F(StatsCollectorTest, NoCertificates) { EXPECT_CALL(session_, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(session_stats), Return(true))); - EXPECT_CALL(session_, video_channel()) - .WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); - stats.UpdateStats(); + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats.GetStats(NULL, &reports); // Check that the local certificate is absent. std::string local_certificate_id = ExtractStatsValue( - webrtc::StatsReport::kStatsReportTypeComponent, + StatsReport::kStatsReportTypeComponent, reports, - webrtc::StatsReport::kStatsValueNameLocalCertificateId); + StatsReport::kStatsValueNameLocalCertificateId); ASSERT_EQ(kNotFound, local_certificate_id); // Check that the remote certificate is absent. std::string remote_certificate_id = ExtractStatsValue( - webrtc::StatsReport::kStatsReportTypeComponent, + StatsReport::kStatsReportTypeComponent, reports, - webrtc::StatsReport::kStatsValueNameRemoteCertificateId); + StatsReport::kStatsValueNameRemoteCertificateId); ASSERT_EQ(kNotFound, remote_certificate_id); } +// This test verifies that a remote certificate with an unsupported digest +// algorithm is correctly ignored. +TEST_F(StatsCollectorTest, UnsupportedDigestIgnored) { + // Build a local certificate. + std::string local_der = "This is the local der."; + talk_base::FakeSSLCertificate local_cert(DerToPem(local_der)); + + // Build a remote certificate with an unsupported digest algorithm. + std::string remote_der = "This is somebody else's der."; + talk_base::FakeSSLCertificate remote_cert(DerToPem(remote_der)); + remote_cert.set_digest_algorithm("foobar"); + + TestCertificateReports(local_cert, std::vector<std::string>(1, local_der), + remote_cert, std::vector<std::string>()); +} + +// Verifies the correct optons are passed to the VideoMediaChannel when using +// verbose output level. +TEST_F(StatsCollectorTest, StatsOutputLevelVerbose) { + webrtc::StatsCollector stats; // Implementation under test. + MockVideoMediaChannel* media_channel = new MockVideoMediaChannel(); + cricket::VideoChannel video_channel(talk_base::Thread::Current(), + media_engine_, media_channel, &session_, "", false, NULL); + stats.set_session(&session_); + + StatsReports reports; // returned values. + cricket::VideoMediaInfo stats_read; + cricket::BandwidthEstimationInfo bwe; + bwe.total_received_propagation_delta_ms = 10; + bwe.recent_received_propagation_delta_ms.push_back(100); + bwe.recent_received_propagation_delta_ms.push_back(200); + bwe.recent_received_packet_group_arrival_time_ms.push_back(1000); + bwe.recent_received_packet_group_arrival_time_ms.push_back(2000); + stats_read.bw_estimations.push_back(bwe); + + EXPECT_CALL(session_, video_channel()) + .WillRepeatedly(Return(&video_channel)); + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull()); + + StatsOptions options; + options.include_received_propagation_stats = true; + EXPECT_CALL(*media_channel, GetStats( + Field(&StatsOptions::include_received_propagation_stats, true), + _)) + .WillOnce(DoAll(SetArgPointee<1>(stats_read), + Return(true))); + + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelDebug); + stats.GetStats(NULL, &reports); + std::string result = ExtractBweStatsValue( + reports, "googReceivedPacketGroupPropagationDeltaSumDebug"); + EXPECT_EQ("10", result); + result = ExtractBweStatsValue( + reports, "googReceivedPacketGroupPropagationDeltaDebug"); + EXPECT_EQ("[100, 200]", result); + result = ExtractBweStatsValue( + reports, "googReceivedPacketGroupArrivalTimeDebug"); + EXPECT_EQ("[1000, 2000]", result); +} + +// This test verifies that a local stats object can get statistics via +// AudioTrackInterface::GetStats() method. +TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) { + webrtc::StatsCollector stats; // Implementation under test. + MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); + // The content_name known by the voice channel. + const std::string kVcName("vcname"); + cricket::VoiceChannel voice_channel(talk_base::Thread::Current(), + media_engine_, media_channel, &session_, kVcName, false); + AddOutgoingAudioTrackStats(); + stats.AddStream(stream_); + stats.AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); + + stats.set_session(&session_); + + // Instruct the session to return stats containing the transport channel. + InitSessionStats(kVcName); + EXPECT_CALL(session_, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), + Return(true))); + + cricket::VoiceSenderInfo voice_sender_info; + InitVoiceSenderInfo(&voice_sender_info); + + // Constructs an ssrc stats update. + cricket::VoiceMediaInfo stats_read; + stats_read.senders.push_back(voice_sender_info); + + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(Return(&voice_channel)); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), + Return(true))); + + StatsReports reports; // returned values. + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); + stats.GetStats(NULL, &reports); + + // Verfy the existence of the track report. + const StatsReport* report = FindNthReportByType( + reports, StatsReport::kStatsReportTypeSsrc, 1); + EXPECT_FALSE(report == NULL); + std::string track_id = ExtractSsrcStatsValue( + reports, StatsReport::kStatsValueNameTrackId); + EXPECT_EQ(kLocalTrackId, track_id); + std::string ssrc_id = ExtractSsrcStatsValue( + reports, StatsReport::kStatsValueNameSsrc); + EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id); + + // Verifies the values in the track report. + UpdateVoiceSenderInfoFromAudioTrack(audio_track_.get(), &voice_sender_info); + VerifyVoiceSenderInfoReport(report, voice_sender_info); + + // Verify we get the same result by passing a track to GetStats(). + StatsReports track_reports; // returned values. + stats.GetStats(audio_track_.get(), &track_reports); + const StatsReport* track_report = FindNthReportByType( + track_reports, StatsReport::kStatsReportTypeSsrc, 1); + EXPECT_TRUE(track_report); + track_id = ExtractSsrcStatsValue(track_reports, + StatsReport::kStatsValueNameTrackId); + EXPECT_EQ(kLocalTrackId, track_id); + ssrc_id = ExtractSsrcStatsValue(track_reports, + StatsReport::kStatsValueNameSsrc); + EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id); + VerifyVoiceSenderInfoReport(track_report, voice_sender_info); + + // Verify that there is no remote report for the local audio track because + // we did not set it up. + const StatsReport* remote_report = FindNthReportByType(reports, + StatsReport::kStatsReportTypeRemoteSsrc, 1); + EXPECT_TRUE(remote_report == NULL); +} + +// This test verifies that audio receive streams populate stats reports +// correctly. +TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) { + webrtc::StatsCollector stats; // Implementation under test. + MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); + // The content_name known by the voice channel. + const std::string kVcName("vcname"); + cricket::VoiceChannel voice_channel(talk_base::Thread::Current(), + media_engine_, media_channel, &session_, kVcName, false); + AddIncomingAudioTrackStats(); + stats.AddStream(stream_); + + stats.set_session(&session_); + + // Instruct the session to return stats containing the transport channel. + InitSessionStats(kVcName); + EXPECT_CALL(session_, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), + Return(true))); + + cricket::VoiceReceiverInfo voice_receiver_info; + InitVoiceReceiverInfo(&voice_receiver_info); + voice_receiver_info.codec_name = "fake_codec"; + + // Constructs an ssrc stats update. + cricket::VoiceMediaInfo stats_read; + stats_read.receivers.push_back(voice_receiver_info); + + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(Return(&voice_channel)); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), + Return(true))); + + StatsReports reports; // returned values. + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); + stats.GetStats(NULL, &reports); + + // Verify the track id is |kRemoteTrackId|. + const std::string track_id = ExtractSsrcStatsValue( + reports, StatsReport::kStatsValueNameTrackId); + EXPECT_EQ(kRemoteTrackId, track_id); + + // Verify the report for this remote track. + const StatsReport* report = FindNthReportByType( + reports, StatsReport::kStatsReportTypeSsrc, 1); + EXPECT_FALSE(report == NULL); + VerifyVoiceReceiverInfoReport(report, voice_receiver_info); +} + +// This test verifies that a local stats object won't update its statistics +// after a RemoveLocalAudioTrack() call. +TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) { + webrtc::StatsCollector stats; // Implementation under test. + MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); + // The content_name known by the voice channel. + const std::string kVcName("vcname"); + cricket::VoiceChannel voice_channel(talk_base::Thread::Current(), + media_engine_, media_channel, &session_, kVcName, false); + AddOutgoingAudioTrackStats(); + stats.AddStream(stream_); + stats.AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); + + stats.set_session(&session_); + + // Instruct the session to return stats containing the transport channel. + InitSessionStats(kVcName); + EXPECT_CALL(session_, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), + Return(true))); + + stats.RemoveLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); + cricket::VoiceSenderInfo voice_sender_info; + InitVoiceSenderInfo(&voice_sender_info); + + // Constructs an ssrc stats update. + cricket::VoiceMediaInfo stats_read; + stats_read.senders.push_back(voice_sender_info); + + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(Return(&voice_channel)); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), + Return(true))); + + StatsReports reports; // returned values. + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); + stats.GetStats(NULL, &reports); + + // The report will exist since we don't remove them in RemoveStream(). + const StatsReport* report = FindNthReportByType( + reports, StatsReport::kStatsReportTypeSsrc, 1); + EXPECT_FALSE(report == NULL); + std::string track_id = ExtractSsrcStatsValue( + reports, StatsReport::kStatsValueNameTrackId); + EXPECT_EQ(kLocalTrackId, track_id); + std::string ssrc_id = ExtractSsrcStatsValue( + reports, StatsReport::kStatsValueNameSsrc); + EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id); + + // Verifies the values in the track report, no value will be changed by the + // AudioTrackInterface::GetSignalValue() and + // AudioProcessorInterface::AudioProcessorStats::GetStats(); + VerifyVoiceSenderInfoReport(report, voice_sender_info); +} + +// This test verifies that when ongoing and incoming audio tracks are using +// the same ssrc, they populate stats reports correctly. +TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) { + webrtc::StatsCollector stats; // Implementation under test. + MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel(); + // The content_name known by the voice channel. + const std::string kVcName("vcname"); + cricket::VoiceChannel voice_channel(talk_base::Thread::Current(), + media_engine_, media_channel, &session_, kVcName, false); + + // Create a local stream with a local audio track and adds it to the stats. + AddOutgoingAudioTrackStats(); + stats.AddStream(stream_); + stats.AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); + + // Create a remote stream with a remote audio track and adds it to the stats. + talk_base::scoped_refptr<webrtc::MediaStream> remote_stream( + webrtc::MediaStream::Create("remotestreamlabel")); + talk_base::scoped_refptr<FakeAudioTrack> remote_track( + new talk_base::RefCountedObject<FakeAudioTrack>(kRemoteTrackId)); + EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(kRemoteTrackId), Return(true))); + remote_stream->AddTrack(remote_track); + stats.AddStream(remote_stream); + + stats.set_session(&session_); + + // Instruct the session to return stats containing the transport channel. + InitSessionStats(kVcName); + EXPECT_CALL(session_, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), + Return(true))); + + cricket::VoiceSenderInfo voice_sender_info; + InitVoiceSenderInfo(&voice_sender_info); + + // Some of the contents in |voice_sender_info| needs to be updated from the + // |audio_track_|. + UpdateVoiceSenderInfoFromAudioTrack(audio_track_.get(), &voice_sender_info); + + cricket::VoiceReceiverInfo voice_receiver_info; + InitVoiceReceiverInfo(&voice_receiver_info); + + // Constructs an ssrc stats update. + cricket::VoiceMediaInfo stats_read; + stats_read.senders.push_back(voice_sender_info); + stats_read.receivers.push_back(voice_receiver_info); + + EXPECT_CALL(session_, voice_channel()).WillRepeatedly(Return(&voice_channel)); + EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull()); + EXPECT_CALL(*media_channel, GetStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), + Return(true))); + + StatsReports reports; // returned values. + stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); + + // Get stats for the local track. + stats.GetStats(audio_track_.get(), &reports); + const StatsReport* track_report = FindNthReportByType( + reports, StatsReport::kStatsReportTypeSsrc, 1); + EXPECT_TRUE(track_report); + std::string track_id = ExtractSsrcStatsValue( + reports, StatsReport::kStatsValueNameTrackId); + EXPECT_EQ(kLocalTrackId, track_id); + VerifyVoiceSenderInfoReport(track_report, voice_sender_info); + + // Get stats for the remote track. + stats.GetStats(remote_track.get(), &reports); + track_report = FindNthReportByType(reports, + StatsReport::kStatsReportTypeSsrc, 1); + EXPECT_TRUE(track_report); + track_id = ExtractSsrcStatsValue(reports, + StatsReport::kStatsValueNameTrackId); + EXPECT_EQ(kRemoteTrackId, track_id); + VerifyVoiceReceiverInfoReport(track_report, voice_receiver_info); +} + } // namespace diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/statstypes.h b/chromium/third_party/libjingle/source/talk/app/webrtc/statstypes.h index 6890f9e017a..22e281cf71c 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/statstypes.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/statstypes.h @@ -53,8 +53,12 @@ class StatsReport { void AddValue(const std::string& name, const std::string& value); void AddValue(const std::string& name, int64 value); + template <typename T> + void AddValue(const std::string& name, const std::vector<T>& value); void AddBoolean(const std::string& name, bool value); + void ReplaceValue(const std::string& name, const std::string& value); + double timestamp; // Time since 1970-01-01T00:00:00Z in milliseconds. typedef std::vector<Value> Values; Values values; @@ -129,6 +133,7 @@ class StatsReport { // Internal StatsValue names static const char kStatsValueNameAvgEncodeMs[]; + static const char kStatsValueNameEncodeRelStdDev[]; static const char kStatsValueNameEncodeUsagePercent[]; static const char kStatsValueNameCaptureJitterMs[]; static const char kStatsValueNameCaptureQueueDelayMsPerS[]; @@ -141,8 +146,10 @@ class StatsReport { static const char kStatsValueNameEchoDelayStdDev[]; static const char kStatsValueNameEchoReturnLoss[]; static const char kStatsValueNameEchoReturnLossEnhancement[]; + static const char kStatsValueNameExpandRate[]; static const char kStatsValueNameFirsReceived[]; static const char kStatsValueNameFirsSent[]; + static const char kStatsValueNameFrameHeightInput[]; static const char kStatsValueNameFrameHeightReceived[]; static const char kStatsValueNameFrameHeightSent[]; static const char kStatsValueNameFrameRateReceived[]; @@ -155,13 +162,18 @@ class StatsReport { static const char kStatsValueNameJitterBufferMs[]; static const char kStatsValueNameMinPlayoutDelayMs[]; static const char kStatsValueNameRenderDelayMs[]; + static const char kStatsValueNameCaptureStartNtpTimeMs[]; static const char kStatsValueNameFrameRateInput[]; static const char kStatsValueNameFrameRateSent[]; + static const char kStatsValueNameFrameWidthInput[]; static const char kStatsValueNameFrameWidthReceived[]; static const char kStatsValueNameFrameWidthSent[]; static const char kStatsValueNameJitterReceived[]; static const char kStatsValueNameNacksReceived[]; static const char kStatsValueNameNacksSent[]; + static const char kStatsValueNamePlisReceived[]; + static const char kStatsValueNamePlisSent[]; + static const char kStatsValueNamePreferredJitterBufferMs[]; static const char kStatsValueNameRtt[]; static const char kStatsValueNameAvailableSendBandwidth[]; static const char kStatsValueNameAvailableReceiveBandwidth[]; @@ -186,6 +198,15 @@ class StatsReport { static const char kStatsValueNameRemoteCertificateId[]; static const char kStatsValueNameLocalCandidateType[]; static const char kStatsValueNameRemoteCandidateType[]; + static const char kStatsValueNameRecvPacketGroupArrivalTimeDebug[]; + static const char kStatsValueNameRecvPacketGroupPropagationDeltaDebug[]; + static const char kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[]; + static const char kStatsValueNameDecodingCTSG[]; + static const char kStatsValueNameDecodingCTN[]; + static const char kStatsValueNameDecodingNormal[]; + static const char kStatsValueNameDecodingPLC[]; + static const char kStatsValueNameDecodingCNG[]; + static const char kStatsValueNameDecodingPLCCNG[]; }; typedef std::vector<StatsReport> StatsReports; diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/textchatsendtask.h b/chromium/third_party/libjingle/source/talk/app/webrtc/umametrics.h index 9b18923d2bf..81f7bac400d 100644..100755 --- a/chromium/third_party/libjingle/source/talk/examples/chat/textchatsendtask.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/umametrics.h @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2013, Google Inc. + * Copyright 2014, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,32 +25,35 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_EXAMPLES_CHAT_TEXTCHATSENDTASK_H_ -#define TALK_EXAMPLES_CHAT_TEXTCHATSENDTASK_H_ +// This file contains enums related to IPv4/IPv6 metrics. -#include "talk/xmpp/xmpptask.h" +#ifndef TALK_APP_WEBRTC_UMAMETRICS_H_ +#define TALK_APP_WEBRTC_UMAMETRICS_H_ -namespace buzz { +namespace webrtc { -// A class to send chat messages to the XMPP server. -class TextChatSendTask : public XmppTask { - public: - // Arguments: - // parent a reference to task interface associated withe the XMPP client. - explicit TextChatSendTask(XmppTaskParentInterface* parent); +// Currently this contains information related to WebRTC network/transport +// information. - // Shuts down the thread associated with this task. - virtual ~TextChatSendTask(); - - // Forms the XMPP "chat" stanza with the specified receipient and message - // and queues it up. - XmppReturnStatus Send(const Jid& to, const std::string& message); - - // Picks up any "chat" stanzas from our queue and sends them to the server. - virtual int ProcessStart(); +// This enum is backed by Chromium's histograms.xml, +// chromium/src/tools/metrics/histograms/histograms.xml +// Existing values cannot be re-ordered and new enums must be added +// before kBoundary. +enum PeerConnectionUMAMetricsCounter { + kPeerConnection_IPv4, + kPeerConnection_IPv6, + kBestConnections_IPv4, + kBestConnections_IPv6, + kBoundary, }; -} // namespace buzz +// This enum defines types for UMA samples, which will have a range. +enum PeerConnectionUMAMetricsName { + kNetworkInterfaces_IPv4, // Number of IPv4 interfaces. + kNetworkInterfaces_IPv6, // Number of IPv6 interfaces. + kTimeToConnect, // In milliseconds. +}; -#endif // TALK_EXAMPLES_CHAT_TEXTCHATSENDTASK_H_ +} // namespace webrtc +#endif // TALK_APP_WEBRTC_UMA6METRICS_H_ diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/videosource.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/videosource.cc index f0182f4fb18..eb4ab97ea93 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/videosource.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/videosource.cc @@ -36,30 +36,6 @@ using cricket::CaptureState; using webrtc::MediaConstraintsInterface; using webrtc::MediaSourceInterface; -namespace webrtc { - -// Constraint keys. Specified by draft-alvestrand-constraints-resolution-00b -// They are declared as static members in mediastreaminterface.h -const char MediaConstraintsInterface::kMinAspectRatio[] = "minAspectRatio"; -const char MediaConstraintsInterface::kMaxAspectRatio[] = "maxAspectRatio"; -const char MediaConstraintsInterface::kMaxWidth[] = "maxWidth"; -const char MediaConstraintsInterface::kMinWidth[] = "minWidth"; -const char MediaConstraintsInterface::kMaxHeight[] = "maxHeight"; -const char MediaConstraintsInterface::kMinHeight[] = "minHeight"; -const char MediaConstraintsInterface::kMaxFrameRate[] = "maxFrameRate"; -const char MediaConstraintsInterface::kMinFrameRate[] = "minFrameRate"; - -// Google-specific keys -const char MediaConstraintsInterface::kNoiseReduction[] = "googNoiseReduction"; -const char MediaConstraintsInterface::kLeakyBucket[] = "googLeakyBucket"; -const char MediaConstraintsInterface::kTemporalLayeredScreencast[] = - "googTemporalLayeredScreencast"; -// TODO(ronghuawu): Remove once cpu overuse detection is stable. -const char MediaConstraintsInterface::kCpuOveruseDetection[] = - "googCpuOveruseDetection"; - -} // namespace webrtc - namespace { const double kRoundingTruncation = 0.0005; @@ -205,9 +181,7 @@ bool NewFormatWithConstraints( } else if (constraint.key == MediaConstraintsInterface::kNoiseReduction || constraint.key == MediaConstraintsInterface::kLeakyBucket || constraint.key == - MediaConstraintsInterface::kTemporalLayeredScreencast || - constraint.key == - MediaConstraintsInterface::kCpuOveruseDetection) { + MediaConstraintsInterface::kTemporalLayeredScreencast) { // These are actually options, not constraints, so they can be satisfied // regardless of the format. return true; @@ -321,9 +295,6 @@ bool ExtractVideoOptions(const MediaConstraintsInterface* all_constraints, all_valid &= ExtractOption(all_constraints, MediaConstraintsInterface::kTemporalLayeredScreencast, &(options->video_temporal_layer_screencast)); - all_valid &= ExtractOption(all_constraints, - MediaConstraintsInterface::kCpuOveruseDetection, - &(options->cpu_overuse_detection)); return all_valid; } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/videosource_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/videosource_unittest.cc index 69e9b3f0f74..43811760c43 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/videosource_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/videosource_unittest.cc @@ -238,7 +238,7 @@ TEST_F(VideoSourceTest, MandatoryConstraintCif5Fps) { ASSERT_TRUE(format != NULL); EXPECT_EQ(352, format->width); EXPECT_EQ(288, format->height); - EXPECT_EQ(5, format->framerate()); + EXPECT_EQ(30, format->framerate()); } // Test that the capture output is 720P if the camera support it and the @@ -370,8 +370,6 @@ TEST_F(VideoSourceTest, SetValidOptionValues) { MediaConstraintsInterface::kTemporalLayeredScreencast, "false"); constraints.AddOptional( MediaConstraintsInterface::kLeakyBucket, "true"); - constraints.AddOptional( - MediaConstraintsInterface::kCpuOveruseDetection, "true"); CreateVideoSource(&constraints); @@ -383,8 +381,6 @@ TEST_F(VideoSourceTest, SetValidOptionValues) { EXPECT_FALSE(value); EXPECT_TRUE(source_->options()->video_leaky_bucket.Get(&value)); EXPECT_TRUE(value); - EXPECT_TRUE(source_->options()-> - cpu_overuse_detection.GetWithDefaultIfUnset(false)); } TEST_F(VideoSourceTest, OptionNotSet) { @@ -392,7 +388,6 @@ TEST_F(VideoSourceTest, OptionNotSet) { CreateVideoSource(&constraints); bool value; EXPECT_FALSE(source_->options()->video_noise_reduction.Get(&value)); - EXPECT_FALSE(source_->options()->cpu_overuse_detection.Get(&value)); } TEST_F(VideoSourceTest, MandatoryOptionOverridesOptional) { @@ -491,7 +486,7 @@ TEST_F(VideoSourceTest, MixedOptionsAndConstraints) { ASSERT_TRUE(format != NULL); EXPECT_EQ(352, format->width); EXPECT_EQ(288, format->height); - EXPECT_EQ(5, format->framerate()); + EXPECT_EQ(30, format->framerate()); bool value = true; EXPECT_TRUE(source_->options()->video_noise_reduction.Get(&value)); @@ -554,6 +549,6 @@ TEST_F(VideoSourceTest, OptionalSubOneFpsConstraints) { kMaxWaitMs); const cricket::VideoFormat* format = capturer_->GetCaptureFormat(); ASSERT_TRUE(format != NULL); - EXPECT_EQ(1, format->framerate()); + EXPECT_EQ(30, format->framerate()); } diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtc.scons b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtc.scons deleted file mode 100644 index 9b1af3cead8..00000000000 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtc.scons +++ /dev/null @@ -1,89 +0,0 @@ -# -*- Python -*- -import talk - -Import('env') - -# For peerconnection, we need additional flags only for GCC 4.6+. -peerconnection_lin_ccflags = [] - -if env.Bit('linux'): - # Detect the GCC version and update peerconnection flags. - (major, minor, rev) = env.GetGccVersion() - if major > 4 or (major == 4 and minor >= 6): - peerconnection_lin_ccflags = ['-Wno-error=unused-but-set-variable'] - - -if env.Bit('have_webrtc_voice') and env.Bit('have_webrtc_video'): - # local sources - talk.Library( - env, - name = 'peerconnection', - srcs = [ - 'audiotrack.cc', - 'jsepicecandidate.cc', - 'jsepsessiondescription.cc', - 'mediaconstraintsinterface.cc', - 'mediastream.cc', - 'mediastreamhandler.cc', - 'mediastreamproxy.cc', - 'mediastreamsignaling.cc', - 'mediastreamtrackproxy.cc', - 'peerconnectionfactory.cc', - 'peerconnection.cc', - 'portallocatorfactory.cc', - 'roapmessages.cc', - 'roapsession.cc', - 'roapsignaling.cc', - 'videorendererimpl.cc', - 'videotrack.cc', - 'webrtcsdp.cc', - 'webrtcsession.cc', - 'webrtcsessiondescriptionfactory.cc', - ], - lin_ccflags = peerconnection_lin_ccflags - ) - - talk.Unittest( - env, - name = 'peerconnection', - srcs = [ - 'test/fakeaudiocapturemodule.cc', - 'test/fakeaudiocapturemodule_unittest.cc', - 'test/fakevideocapturemodule.cc', - 'test/fileframesource.cc', - 'test/i420framesource.cc', - 'test/staticframesource.cc', - 'jsepsessiondescription_unittest.cc', - 'mediastream_unittest.cc', - 'mediastreamhandler_unittest.cc', - 'mediastreamsignaling_unittest.cc', - 'peerconnectioninterface_unittest.cc', - 'peerconnection_unittest.cc', - 'peerconnectionfactory_unittest.cc', - 'roapmessages_unittest.cc', - 'roapsession_unittest.cc', - 'roapsignaling_unittest.cc', - 'webrtcsdp_unittest.cc', - 'webrtcsession_unittest.cc', - ], - libs = [ - 'base', - 'expat', - 'json', - 'p2p', - 'phone', - 'srtp', - 'xmllite', - 'xmpp', - 'yuvscaler', - 'peerconnection', - ], - win_link_flags = [('', '/nodefaultlib:libcmt')[env.Bit('debug')]], - lin_libs = [ - 'sound', - ], - mac_libs = [ - 'crypto', - 'ssl', - ], - ) diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp.cc index 79f94fe3b4e..b2d8a30ec52 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp.cc @@ -65,11 +65,13 @@ using cricket::kCodecParamMinBitrate; using cricket::kCodecParamMinPTime; using cricket::kCodecParamPTime; using cricket::kCodecParamSPropStereo; +using cricket::kCodecParamStartBitrate; using cricket::kCodecParamStereo; using cricket::kCodecParamUseInbandFec; using cricket::kCodecParamSctpProtocol; using cricket::kCodecParamSctpStreams; using cricket::kCodecParamMaxAverageBitrate; +using cricket::kCodecParamAssociatedPayloadType; using cricket::kWildcardPayloadType; using cricket::MediaContentDescription; using cricket::MediaType; @@ -127,9 +129,6 @@ static const char kAttributeMsidSemantics[] = "msid-semantic"; static const char kMediaStreamSemantic[] = "WMS"; static const char kSsrcAttributeMsid[] = "msid"; static const char kDefaultMsid[] = "default"; -static const char kMsidAppdataAudio[] = "a"; -static const char kMsidAppdataVideo[] = "v"; -static const char kMsidAppdataData[] = "d"; static const char kSsrcAttributeMslabel[] = "mslabel"; static const char kSSrcAttributeLabel[] = "label"; static const char kAttributeSsrcGroup[] = "ssrc-group"; @@ -210,7 +209,6 @@ static const char kIsacCodecName[] = "ISAC"; // From webrtcvoiceengine.cc static const int kIsacWbDefaultRate = 32000; // From acm_common_defs.h static const int kIsacSwbDefaultRate = 56000; // From acm_common_defs.h -static const int kDefaultSctpFmt = 5000; static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel"; struct SsrcInfo { @@ -243,7 +241,7 @@ static void BuildMediaDescription(const ContentInfo* content_info, const TransportInfo* transport_info, const MediaType media_type, std::string* message); -static void BuildSctpContentAttributes(std::string* message); +static void BuildSctpContentAttributes(std::string* message, int sctp_port); static void BuildRtpContentAttributes( const MediaContentDescription* media_desc, const MediaType media_type, @@ -591,6 +589,19 @@ static bool CaseInsensitiveFind(std::string str1, std::string str2) { return str1.find(str2) != std::string::npos; } +template <class T> +static bool GetValueFromString(const std::string& line, + const std::string& s, + T* t, + SdpParseError* error) { + if (!talk_base::FromString(s, t)) { + std::ostringstream description; + description << "Invalid value: " << s << "."; + return ParseFailed(line, description.str(), error); + } + return true; +} + void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos, StreamParamsVec* tracks) { ASSERT(tracks != NULL); @@ -1006,11 +1017,20 @@ bool ParseCandidate(const std::string& message, Candidate* candidate, if (!GetValue(fields[0], kAttributeCandidate, &foundation, error)) { return false; } - const int component_id = talk_base::FromString<int>(fields[1]); + int component_id = 0; + if (!GetValueFromString(first_line, fields[1], &component_id, error)) { + return false; + } const std::string transport = fields[2]; - const uint32 priority = talk_base::FromString<uint32>(fields[3]); + uint32 priority = 0; + if (!GetValueFromString(first_line, fields[3], &priority, error)) { + return false; + } const std::string connection_address = fields[4]; - const int port = talk_base::FromString<int>(fields[5]); + int port = 0; + if (!GetValueFromString(first_line, fields[5], &port, error)) { + return false; + } SocketAddress address(connection_address, port); cricket::ProtocolType protocol; @@ -1041,8 +1061,12 @@ bool ParseCandidate(const std::string& message, Candidate* candidate, } if (fields.size() >= (current_position + 2) && fields[current_position] == kAttributeCandidateRport) { - related_address.SetPort( - talk_base::FromString<int>(fields[++current_position])); + int port = 0; + if (!GetValueFromString( + first_line, fields[++current_position], &port, error)) { + return false; + } + related_address.SetPort(port); ++current_position; } @@ -1058,7 +1082,9 @@ bool ParseCandidate(const std::string& message, Candidate* candidate, // RFC 5245 // *(SP extension-att-name SP extension-att-value) if (fields[i] == kAttributeCandidateGeneration) { - generation = talk_base::FromString<uint32>(fields[++i]); + if (!GetValueFromString(first_line, fields[++i], &generation, error)) { + return false; + } } else if (fields[i] == kAttributeCandidateUsername) { username = fields[++i]; } else if (fields[i] == kAttributeCandidatePassword) { @@ -1113,7 +1139,10 @@ bool ParseExtmap(const std::string& line, RtpHeaderExtension* extmap, } std::vector<std::string> sub_fields; talk_base::split(value_direction, kSdpDelimiterSlash, &sub_fields); - int value = talk_base::FromString<int>(sub_fields[0]); + int value = 0; + if (!GetValueFromString(line, sub_fields[0], &value, error)) { + return false; + } *extmap = RtpHeaderExtension(uri, value); return true; @@ -1138,6 +1167,7 @@ void BuildMediaDescription(const ContentInfo* content_info, ASSERT(media_desc != NULL); bool is_sctp = (media_desc->protocol() == cricket::kMediaProtocolDtlsSctp); + int sctp_port = cricket::kSctpDefaultPort; // RFC 4566 // m=<media> <port> <proto> <fmt> @@ -1172,14 +1202,22 @@ void BuildMediaDescription(const ContentInfo* content_info, fmt.append(talk_base::ToString<int>(it->id)); } } else if (media_type == cricket::MEDIA_TYPE_DATA) { + const DataContentDescription* data_desc = + static_cast<const DataContentDescription*>(media_desc); if (is_sctp) { fmt.append(" "); - // TODO(jiayl): Replace the hard-coded string with the fmt read out of the - // ContentDescription. - fmt.append(talk_base::ToString<int>(kDefaultSctpFmt)); + + for (std::vector<cricket::DataCodec>::const_iterator it = + data_desc->codecs().begin(); + it != data_desc->codecs().end(); ++it) { + if (it->id == cricket::kGoogleSctpDataCodecId && + it->GetParam(cricket::kCodecParamPort, &sctp_port)) { + break; + } + } + + fmt.append(talk_base::ToString<int>(sctp_port)); } else { - const DataContentDescription* data_desc = - static_cast<const DataContentDescription*>(media_desc); for (std::vector<cricket::DataCodec>::const_iterator it = data_desc->codecs().begin(); it != data_desc->codecs().end(); ++it) { @@ -1261,18 +1299,18 @@ void BuildMediaDescription(const ContentInfo* content_info, AddLine(os.str(), message); if (is_sctp) { - BuildSctpContentAttributes(message); + BuildSctpContentAttributes(message, sctp_port); } else { BuildRtpContentAttributes(media_desc, media_type, message); } } -void BuildSctpContentAttributes(std::string* message) { +void BuildSctpContentAttributes(std::string* message, int sctp_port) { // draft-ietf-mmusic-sctp-sdp-04 // a=sctpmap:sctpmap-number protocol [streams] std::ostringstream os; InitAttrLine(kAttributeSctpmap, &os); - os << kSdpDelimiterColon << kDefaultSctpFmt << kSdpDelimiterSpace + os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace << kDefaultSctpmapProtocol << kSdpDelimiterSpace << (cricket::kMaxSctpSid + 1); AddLine(os.str(), message); @@ -1472,10 +1510,10 @@ void WriteFmtpParameters(const cricket::CodecParameterMap& parameters, bool IsFmtpParam(const std::string& name) { const char* kFmtpParams[] = { kCodecParamMinPTime, kCodecParamSPropStereo, - kCodecParamStereo, kCodecParamUseInbandFec, + kCodecParamStereo, kCodecParamUseInbandFec, kCodecParamStartBitrate, kCodecParamMaxBitrate, kCodecParamMinBitrate, kCodecParamMaxQuantization, kCodecParamSctpProtocol, kCodecParamSctpStreams, - kCodecParamMaxAverageBitrate + kCodecParamMaxAverageBitrate, kCodecParamAssociatedPayloadType }; for (size_t i = 0; i < ARRAY_SIZE(kFmtpParams); ++i) { if (_stricmp(name.c_str(), kFmtpParams[i]) == 0) { @@ -1544,7 +1582,9 @@ bool GetParameter(const std::string& name, if (found == params.end()) { return false; } - *value = talk_base::FromString<int>(found->second); + if (!talk_base::FromString(found->second, value)) { + return false; + } return true; } @@ -2085,7 +2125,17 @@ bool ParseMediaDescription(const std::string& message, // <fmt> std::vector<int> codec_preference; for (size_t j = 3 ; j < fields.size(); ++j) { - codec_preference.push_back(talk_base::FromString<int>(fields[j])); + // TODO(wu): Remove when below bug is fixed. + // https://bugzilla.mozilla.org/show_bug.cgi?id=996329 + if (fields[j] == "" && j == fields.size() - 1) { + continue; + } + + int pl = 0; + if (!GetValueFromString(line, fields[j], &pl, error)) { + return false; + } + codec_preference.push_back(pl); } // Make a temporary TransportDescription based on |session_td|. @@ -2111,9 +2161,6 @@ bool ParseMediaDescription(const std::string& message, message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol, codec_preference, pos, &content_name, &transport, candidates, error)); - MaybeCreateStaticPayloadAudioCodecs( - codec_preference, - static_cast<AudioContentDescription*>(content.get())); } else if (HasAttribute(line, kMediaTypeData)) { DataContentDescription* desc = ParseContentDescription<DataContentDescription>( @@ -2121,7 +2168,7 @@ bool ParseMediaDescription(const std::string& message, codec_preference, pos, &content_name, &transport, candidates, error); - if (protocol == cricket::kMediaProtocolDtlsSctp) { + if (desc && protocol == cricket::kMediaProtocolDtlsSctp) { // Add the SCTP Port number as a pseudo-codec "port" parameter cricket::DataCodec codec_port( cricket::kGoogleSctpDataCodecId, cricket::kGoogleSctpDataCodecName, @@ -2129,6 +2176,7 @@ bool ParseMediaDescription(const std::string& message, codec_port.SetParam(cricket::kCodecParamPort, fields[3]); LOG(INFO) << "ParseMediaDescription: Got SCTP Port Number " << fields[3]; + ASSERT(!desc->HasCodec(cricket::kGoogleSctpDataCodecId)); desc->AddCodec(codec_port); } @@ -2366,6 +2414,11 @@ bool ParseContent(const std::string& message, ASSERT(content_name != NULL); ASSERT(transport != NULL); + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + MaybeCreateStaticPayloadAudioCodecs( + codec_preference, static_cast<AudioContentDescription*>(media_desc)); + } + // The media level "ice-ufrag" and "ice-pwd". // The candidates before update the media level "ice-pwd" and "ice-ufrag". Candidates candidates_orig; @@ -2393,19 +2446,6 @@ bool ParseContent(const std::string& message, } } - if (IsLineType(line, kLineTypeSessionBandwidth)) { - std::string bandwidth; - if (HasAttribute(line, kApplicationSpecificMaximum)) { - if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) { - return false; - } else { - media_desc->set_bandwidth( - talk_base::FromString<int>(bandwidth) * 1000); - } - } - continue; - } - // RFC 4566 // b=* (zero or more bandwidth information lines) if (IsLineType(line, kLineTypeSessionBandwidth)) { @@ -2414,8 +2454,11 @@ bool ParseContent(const std::string& message, if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) { return false; } else { - media_desc->set_bandwidth( - talk_base::FromString<int>(bandwidth) * 1000); + int b = 0; + if (!GetValueFromString(line, bandwidth, &b, error)) { + return false; + } + media_desc->set_bandwidth(b * 1000); } } continue; @@ -2538,9 +2581,11 @@ bool ParseContent(const std::string& message, return false; } int buffer_latency = 0; - if (!talk_base::FromString(flag_value, &buffer_latency) || - buffer_latency < 0) { - return ParseFailed(message, "Invalid buffer latency.", error); + if (!GetValueFromString(line, flag_value, &buffer_latency, error)) { + return false; + } + if (buffer_latency < 0) { + return ParseFailed(line, "Buffer latency less than 0.", error); } media_desc->set_buffered_mode_latency(buffer_latency); } @@ -2632,7 +2677,10 @@ bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos, if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) { return false; } - uint32 ssrc_id = talk_base::FromString<uint32>(ssrc_id_s); + uint32 ssrc_id = 0; + if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) { + return false; + } std::string attribute; std::string value; @@ -2709,7 +2757,10 @@ bool ParseSsrcGroupAttribute(const std::string& line, } std::vector<uint32> ssrcs; for (size_t i = 1; i < fields.size(); ++i) { - uint32 ssrc = talk_base::FromString<uint32>(fields[i]); + uint32 ssrc = 0; + if (!GetValueFromString(line, fields[i], &ssrc, error)) { + return false; + } ssrcs.push_back(ssrc); } ssrc_groups->push_back(SsrcGroup(semantics, ssrcs)); @@ -2732,7 +2783,10 @@ bool ParseCryptoAttribute(const std::string& line, if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) { return false; } - int tag = talk_base::FromString<int>(tag_value); + int tag = 0; + if (!GetValueFromString(line, tag_value, &tag, error)) { + return false; + } const std::string crypto_suite = fields[1]; const std::string key_params = fields[2]; std::string session_params; @@ -2796,7 +2850,10 @@ bool ParseRtpmapAttribute(const std::string& line, if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) { return false; } - const int payload_type = talk_base::FromString<int>(payload_type_value); + int payload_type = 0; + if (!GetValueFromString(line, payload_type_value, &payload_type, error)) { + return false; + } // Set the preference order depending on the order of the pl type in the // <fmt> of the m-line. @@ -2820,7 +2877,10 @@ bool ParseRtpmapAttribute(const std::string& line, error); } const std::string encoding_name = codec_params[0]; - const int clock_rate = talk_base::FromString<int>(codec_params[1]); + int clock_rate = 0; + if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) { + return false; + } if (media_type == cricket::MEDIA_TYPE_VIDEO) { VideoContentDescription* video_desc = static_cast<VideoContentDescription*>(media_desc); @@ -2839,7 +2899,9 @@ bool ParseRtpmapAttribute(const std::string& line, // additional parameters are needed. int channels = 1; if (codec_params.size() == 3) { - channels = talk_base::FromString<int>(codec_params[2]); + if (!GetValueFromString(line, codec_params[2], &channels, error)) { + return false; + } } int bitrate = 0; // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc @@ -2926,7 +2988,10 @@ bool ParseFmtpAttributes(const std::string& line, const MediaType media_type, codec_params[name] = value; } - int int_payload_type = talk_base::FromString<int>(payload_type); + int int_payload_type = 0; + if (!GetValueFromString(line, payload_type, &int_payload_type, error)) { + return false; + } if (media_type == cricket::MEDIA_TYPE_AUDIO) { UpdateCodec<AudioContentDescription, cricket::AudioCodec>( media_desc, int_payload_type, codec_params); @@ -2954,8 +3019,12 @@ bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type, error)) { return false; } - int payload_type = (payload_type_string == "*") ? - kWildcardPayloadType : talk_base::FromString<int>(payload_type_string); + int payload_type = kWildcardPayloadType; + if (payload_type_string != "*") { + if (!GetValueFromString(line, payload_type_string, &payload_type, error)) { + return false; + } + } std::string id = rtcp_fb_fields[1]; std::string param = ""; for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp_unittest.cc index 541868314c7..64776e29e1c 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp_unittest.cc @@ -299,6 +299,19 @@ static const char kSdpSctpDataChannelWithCandidatesString[] = "a=mid:data_content_name\r\n" "a=sctpmap:5000 webrtc-datachannel 1024\r\n"; +static const char kSdpConferenceString[] = + "v=0\r\n" + "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "a=msid-semantic: WMS\r\n" + "m=audio 1 RTP/SAVPF 111 103 104\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=x-google-flag:conference\r\n" + "m=video 1 RTP/SAVPF 120\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=x-google-flag:conference\r\n"; + // One candidate reference string as per W3c spec. // candidate:<blah> not a=candidate:<blah>CRLF @@ -388,14 +401,31 @@ static void Replace(const std::string& line, newlines.c_str(), newlines.length(), message); } -static void ReplaceAndTryToParse(const char* search, const char* replace) { +// Expect fail to parase |bad_sdp| and expect |bad_part| be part of the error +// message. +static void ExpectParseFailure(const std::string& bad_sdp, + const std::string& bad_part) { JsepSessionDescription desc(kDummyString); - std::string sdp = kSdpFullString; - Replace(search, replace, &sdp); SdpParseError error; - bool ret = webrtc::SdpDeserialize(sdp, &desc, &error); + bool ret = webrtc::SdpDeserialize(bad_sdp, &desc, &error); EXPECT_FALSE(ret); - EXPECT_NE(std::string::npos, error.line.find(replace)); + EXPECT_NE(std::string::npos, error.line.find(bad_part.c_str())); +} + +// Expect fail to parse kSdpFullString if replace |good_part| with |bad_part|. +static void ExpectParseFailure(const char* good_part, const char* bad_part) { + std::string bad_sdp = kSdpFullString; + Replace(good_part, bad_part, &bad_sdp); + ExpectParseFailure(bad_sdp, bad_part); +} + +// Expect fail to parse kSdpFullString if add |newlines| after |injectpoint|. +static void ExpectParseFailureWithNewLines(const std::string& injectpoint, + const std::string& newlines, + const std::string& bad_part) { + std::string bad_sdp = kSdpFullString; + InjectAfter(injectpoint, newlines, &bad_sdp); + ExpectParseFailure(bad_sdp, bad_part); } static void ReplaceDirection(cricket::MediaContentDirection direction, @@ -1123,6 +1153,15 @@ class WebRtcSdpTest : public testing::Test { << "a=maxptime:" << params.max_ptime << "\r\n"; sdp += os.str(); + os.clear(); + os.str(""); + // Pl type 100 preferred. + os << "m=video 1 RTP/SAVPF 99 95\r\n" + << "a=rtpmap:99 VP8/90000\r\n" + << "a=rtpmap:95 RTX/90000\r\n" + << "a=fmtp:95 apt=99;rtx-time=1000\r\n"; + sdp += os.str(); + // Deserialize SdpParseError error; EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error)); @@ -1153,6 +1192,19 @@ class WebRtcSdpTest : public testing::Test { } } } + + const ContentInfo* vc = GetFirstVideoContent(jdesc_output->description()); + ASSERT_TRUE(vc != NULL); + const VideoContentDescription* vcd = + static_cast<const VideoContentDescription*>(vc->description); + ASSERT_FALSE(vcd->codecs().empty()); + cricket::VideoCodec vp8 = vcd->codecs()[0]; + EXPECT_EQ("VP8", vp8.name); + EXPECT_EQ(99, vp8.id); + cricket::VideoCodec rtx = vcd->codecs()[1]; + EXPECT_EQ("RTX", rtx.name); + EXPECT_EQ(95, rtx.id); + VerifyCodecParameter(rtx.params, "apt", vp8.id); } void TestDeserializeRtcpFb(JsepSessionDescription* jdesc_output, @@ -1172,6 +1224,7 @@ class WebRtcSdpTest : public testing::Test { "m=video 3457 RTP/SAVPF 101\r\n" "a=rtpmap:101 VP8/90000\r\n" "a=rtcp-fb:101 nack\r\n" + "a=rtcp-fb:101 nack pli\r\n" "a=rtcp-fb:101 goog-remb\r\n" "a=rtcp-fb:101 ccm fir\r\n"; std::ostringstream os; @@ -1204,6 +1257,9 @@ class WebRtcSdpTest : public testing::Test { cricket::FeedbackParam(cricket::kRtcpFbParamNack, cricket::kParamValueEmpty))); EXPECT_TRUE(vp8.HasFeedbackParam( + cricket::FeedbackParam(cricket::kRtcpFbParamNack, + cricket::kRtcpFbNackParamPli))); + EXPECT_TRUE(vp8.HasFeedbackParam( cricket::FeedbackParam(cricket::kRtcpFbParamRemb, cricket::kParamValueEmpty))); EXPECT_TRUE(vp8.HasFeedbackParam( @@ -1432,6 +1488,37 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) { EXPECT_EQ(message, expected_sdp); } +TEST_F(WebRtcSdpTest, SerializeWithSctpDataChannelAndNewPort) { + AddSctpDataChannel(); + JsepSessionDescription jsep_desc(kDummyString); + + ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); + DataContentDescription* dcdesc = static_cast<DataContentDescription*>( + jsep_desc.description()->GetContentDescriptionByName(kDataContentName)); + + const int kNewPort = 1234; + cricket::DataCodec codec( + cricket::kGoogleSctpDataCodecId, cricket::kGoogleSctpDataCodecName, 0); + codec.SetParam(cricket::kCodecParamPort, kNewPort); + dcdesc->AddOrReplaceCodec(codec); + + std::string message = webrtc::SdpSerialize(jsep_desc); + + std::string expected_sdp = kSdpString; + expected_sdp.append(kSdpSctpDataChannelString); + + char default_portstr[16]; + char new_portstr[16]; + talk_base::sprintfn(default_portstr, sizeof(default_portstr), "%d", + kDefaultSctpPort); + talk_base::sprintfn(new_portstr, sizeof(new_portstr), "%d", kNewPort); + talk_base::replace_substrs(default_portstr, strlen(default_portstr), + new_portstr, strlen(new_portstr), + &expected_sdp); + + EXPECT_EQ(expected_sdp, message); +} + TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) { AddRtpDataChannel(); data_desc_->set_bandwidth(100*1000); @@ -1470,6 +1557,21 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) { EXPECT_EQ(sdp_with_extmap, message); } +TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBufferLatency) { + VideoContentDescription* vcd = static_cast<VideoContentDescription*>( + GetFirstVideoContent(&desc_)->description); + vcd->set_buffered_mode_latency(128); + + ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), + jdesc_.session_id(), + jdesc_.session_version())); + std::string message = webrtc::SdpSerialize(jdesc_); + std::string sdp_with_buffer_latency = kSdpFullString; + InjectAfter("a=rtpmap:120 VP8/90000\r\n", + "a=x-google-buffer-latency:128\r\n", + &sdp_with_buffer_latency); + EXPECT_EQ(sdp_with_buffer_latency, message); +} TEST_F(WebRtcSdpTest, SerializeCandidates) { std::string message = webrtc::SdpSerializeCandidate(*jcandidate_); @@ -1543,6 +1645,37 @@ TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmap) { EXPECT_EQ(ref_codecs, audio->codecs()); } +TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmapButWithFmtp) { + static const char kSdpNoRtpmapString[] = + "v=0\r\n" + "o=- 11 22 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "m=audio 49232 RTP/AVP 18 103\r\n" + "a=fmtp:18 annexb=yes\r\n" + "a=rtpmap:103 ISAC/16000\r\n"; + + JsepSessionDescription jdesc(kDummyString); + EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc)); + cricket::AudioContentDescription* audio = + static_cast<AudioContentDescription*>( + jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO)); + + cricket::AudioCodec g729 = audio->codecs()[0]; + EXPECT_EQ("G729", g729.name); + EXPECT_EQ(8000, g729.clockrate); + EXPECT_EQ(18, g729.id); + cricket::CodecParameterMap::iterator found = + g729.params.find("annexb"); + ASSERT_TRUE(found != g729.params.end()); + EXPECT_EQ(found->second, "yes"); + + cricket::AudioCodec isac = audio->codecs()[1]; + EXPECT_EQ("ISAC", isac.name); + EXPECT_EQ(103, isac.id); + EXPECT_EQ(16000, isac.clockrate); +} + // Ensure that we can deserialize SDP with a=fingerprint properly. TEST_F(WebRtcSdpTest, DeserializeJsepSessionDescriptionWithFingerprint) { // Add a DTLS a=fingerprint attribute to our session description. @@ -1650,6 +1783,23 @@ TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithUfragPwd) { EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ufrag_pwd)); } +TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBufferLatency) { + JsepSessionDescription jdesc_with_buffer_latency(kDummyString); + std::string sdp_with_buffer_latency = kSdpFullString; + InjectAfter("a=rtpmap:120 VP8/90000\r\n", + "a=x-google-buffer-latency:128\r\n", + &sdp_with_buffer_latency); + + EXPECT_TRUE( + SdpDeserialize(sdp_with_buffer_latency, &jdesc_with_buffer_latency)); + VideoContentDescription* vcd = static_cast<VideoContentDescription*>( + GetFirstVideoContent(&desc_)->description); + vcd->set_buffered_mode_latency(128); + ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), + jdesc_.session_id(), + jdesc_.session_version())); + EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_buffer_latency)); +} TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRecvOnlyContent) { EXPECT_TRUE(TestDeserializeDirection(cricket::MD_RECVONLY)); @@ -1779,6 +1929,18 @@ TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) { EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output)); } +// For crbug/344475. +TEST_F(WebRtcSdpTest, DeserializeSdpWithCorruptedSctpDataChannels) { + std::string sdp_with_data = kSdpString; + sdp_with_data.append(kSdpSctpDataChannelString); + // Remove the "\n" at the end. + sdp_with_data = sdp_with_data.substr(0, sdp_with_data.size() - 1); + JsepSessionDescription jdesc_output(kDummyString); + + EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output)); + // No crash is a pass. +} + TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndNewPort) { AddSctpDataChannel(); const uint16 kUnusualSctpPort = 9556; @@ -1789,6 +1951,7 @@ TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndNewPort) { talk_base::sprintfn(unusual_portstr, sizeof(unusual_portstr), "%d", kUnusualSctpPort); + // First setup the expected JsepSessionDescription. JsepSessionDescription jdesc(kDummyString); // take our pre-built session description and change the SCTP port. cricket::SessionDescription* mutant = desc_.Copy(); @@ -1798,11 +1961,13 @@ TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndNewPort) { EXPECT_EQ(codecs.size(), 1UL); EXPECT_EQ(codecs[0].id, cricket::kGoogleSctpDataCodecId); codecs[0].SetParam(cricket::kCodecParamPort, kUnusualSctpPort); + dcdesc->set_codecs(codecs); // note: mutant's owned by jdesc now. ASSERT_TRUE(jdesc.Initialize(mutant, kSessionId, kSessionVersion)); mutant = NULL; + // Then get the deserialized JsepSessionDescription. std::string sdp_with_data = kSdpString; sdp_with_data.append(kSdpSctpDataChannelString); talk_base::replace_substrs(default_portstr, strlen(default_portstr), @@ -1888,6 +2053,24 @@ TEST_F(WebRtcSdpTest, DeserializeCandidateOldFormat) { EXPECT_TRUE(jcandidate.candidate().IsEquivalent(ref_candidate)); } +TEST_F(WebRtcSdpTest, DeserializeSdpWithConferenceFlag) { + JsepSessionDescription jdesc(kDummyString); + + // Deserialize + EXPECT_TRUE(SdpDeserialize(kSdpConferenceString, &jdesc)); + + // Verify + cricket::AudioContentDescription* audio = + static_cast<AudioContentDescription*>( + jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO)); + EXPECT_TRUE(audio->conference_mode()); + + cricket::VideoContentDescription* video = + static_cast<VideoContentDescription*>( + jdesc.description()->GetContentDescriptionByName(cricket::CN_VIDEO)); + EXPECT_TRUE(video->conference_mode()); +} + TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) { const char kSdpDestroyer[] = "!@#$%^&"; const char kSdpInvalidLine1[] = " =candidate"; @@ -1902,29 +2085,72 @@ TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) { // Missing space. const char kSdpInvalidLine6[] = "a=fingerprint:sha-1" "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB"; + // MD5 is not allowed in fingerprints. + const char kSdpInvalidLine7[] = "a=fingerprint:md5 " + "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B"; // Broken session description - ReplaceAndTryToParse("v=", kSdpDestroyer); - ReplaceAndTryToParse("o=", kSdpDestroyer); - ReplaceAndTryToParse("s=-", kSdpDestroyer); + ExpectParseFailure("v=", kSdpDestroyer); + ExpectParseFailure("o=", kSdpDestroyer); + ExpectParseFailure("s=-", kSdpDestroyer); // Broken time description - ReplaceAndTryToParse("t=", kSdpDestroyer); + ExpectParseFailure("t=", kSdpDestroyer); // Broken media description - ReplaceAndTryToParse("m=audio", "c=IN IP4 74.125.224.39"); - ReplaceAndTryToParse("m=video", kSdpDestroyer); + ExpectParseFailure("m=audio", "c=IN IP4 74.125.224.39"); + ExpectParseFailure("m=video", kSdpDestroyer); // Invalid lines - ReplaceAndTryToParse("a=candidate", kSdpInvalidLine1); - ReplaceAndTryToParse("a=candidate", kSdpInvalidLine2); - ReplaceAndTryToParse("a=candidate", kSdpInvalidLine3); + ExpectParseFailure("a=candidate", kSdpInvalidLine1); + ExpectParseFailure("a=candidate", kSdpInvalidLine2); + ExpectParseFailure("a=candidate", kSdpInvalidLine3); // Bogus fingerprint replacing a=sendrev. We selected this attribute // because it's orthogonal to what we are replacing and hence // safe. - ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine4); - ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine5); - ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine6); + ExpectParseFailure("a=sendrecv", kSdpInvalidLine4); + ExpectParseFailure("a=sendrecv", kSdpInvalidLine5); + ExpectParseFailure("a=sendrecv", kSdpInvalidLine6); + ExpectParseFailure("a=sendrecv", kSdpInvalidLine7); +} + +TEST_F(WebRtcSdpTest, DeserializeSdpWithInvalidAttributeValue) { + // ssrc + ExpectParseFailure("a=ssrc:1", "a=ssrc:badvalue"); + ExpectParseFailure("a=ssrc-group:FEC 5 6", "a=ssrc-group:FEC badvalue 6"); + // crypto + ExpectParseFailure("a=crypto:1 ", "a=crypto:badvalue "); + // rtpmap + ExpectParseFailure("a=rtpmap:111 ", "a=rtpmap:badvalue "); + ExpectParseFailure("opus/48000/2", "opus/badvalue/2"); + ExpectParseFailure("opus/48000/2", "opus/48000/badvalue"); + // candidate + ExpectParseFailure("1 udp 2130706432", "badvalue udp 2130706432"); + ExpectParseFailure("1 udp 2130706432", "1 udp badvalue"); + ExpectParseFailure("192.168.1.5 1234", "192.168.1.5 badvalue"); + ExpectParseFailure("rport 2346", "rport badvalue"); + ExpectParseFailure("rport 2346 generation 2", + "rport 2346 generation badvalue"); + // m line + ExpectParseFailure("m=audio 2345 RTP/SAVPF 111 103 104", + "m=audio 2345 RTP/SAVPF 111 badvalue 104"); + + // bandwidth + ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n", + "b=AS:badvalue\r\n", + "b=AS:badvalue"); + // rtcp-fb + ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n", + "a=rtcp-fb:badvalue nack\r\n", + "a=rtcp-fb:badvalue nack"); + // extmap + ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n", + "a=extmap:badvalue http://example.com\r\n", + "a=extmap:badvalue http://example.com"); + // x-google-buffer-latency + ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n", + "a=x-google-buffer-latency:badvalue\r\n", + "a=x-google-buffer-latency:badvalue"); } TEST_F(WebRtcSdpTest, DeserializeSdpWithReorderedPltypes) { diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.cc index 7e153b3ffd1..1b1c287817d 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.cc @@ -27,8 +27,9 @@ #include "talk/app/webrtc/webrtcsession.h" +#include <limits.h> + #include <algorithm> -#include <climits> #include <vector> #include "talk/app/webrtc/jsepicecandidate.h" @@ -40,6 +41,7 @@ #include "talk/base/helpers.h" #include "talk/base/logging.h" #include "talk/base/stringencode.h" +#include "talk/base/stringutils.h" #include "talk/media/base/constants.h" #include "talk/media/base/videocapturer.h" #include "talk/session/media/channel.h" @@ -54,47 +56,24 @@ using cricket::TransportInfo; namespace webrtc { -const char MediaConstraintsInterface::kInternalConstraintPrefix[] = "internal"; - -// Supported MediaConstraints. -// DSCP constraints. -const char MediaConstraintsInterface::kEnableDscp[] = "googDscp"; -// DTLS-SRTP pseudo-constraints. -const char MediaConstraintsInterface::kEnableDtlsSrtp[] = - "DtlsSrtpKeyAgreement"; -// DataChannel pseudo constraints. -const char MediaConstraintsInterface::kEnableRtpDataChannels[] = - "RtpDataChannels"; -// This constraint is for internal use only, representing the Chrome command -// line flag. So it is prefixed with kInternalConstraintPrefix so JS values -// will be removed. -const char MediaConstraintsInterface::kEnableSctpDataChannels[] = - "deprecatedSctpDataChannels"; - // Error messages -const char kSetLocalSdpFailed[] = "SetLocalDescription failed: "; -const char kSetRemoteSdpFailed[] = "SetRemoteDescription failed: "; -const char kCreateChannelFailed[] = "Failed to create channels."; const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE " "is enabled."; +const char kCreateChannelFailed[] = "Failed to create channels."; const char kInvalidCandidates[] = "Description contains invalid candidates."; const char kInvalidSdp[] = "Invalid session description."; const char kMlineMismatch[] = - "Offer and answer descriptions m-lines are not matching. " - "Rejecting answer."; -const char kSdpWithoutCrypto[] = "Called with a SDP without crypto enabled."; -const char kSdpWithoutSdesAndDtlsDisabled[] = - "Called with an SDP without SDES crypto and DTLS disabled locally."; + "Offer and answer descriptions m-lines are not matching. Rejecting answer."; +const char kPushDownTDFailed[] = + "Failed to push down transport description:"; +const char kSdpWithoutDtlsFingerprint[] = + "Called with SDP without DTLS fingerprint."; +const char kSdpWithoutSdesCrypto[] = + "Called with SDP without SDES crypto."; const char kSdpWithoutIceUfragPwd[] = - "Called with an SDP without ice-ufrag and ice-pwd."; + "Called with SDP without ice-ufrag and ice-pwd."; const char kSessionError[] = "Session error code: "; -const char kUpdateStateFailed[] = "Failed to update session state: "; -const char kPushDownOfferTDFailed[] = - "Failed to push down offer transport description."; -const char kPushDownPranswerTDFailed[] = - "Failed to push down pranswer transport description."; -const char kPushDownAnswerTDFailed[] = - "Failed to push down answer transport description."; +const char kSessionErrorDesc[] = "Session error description: "; // Compares |answer| against |offer|. Comparision is done // for number of m-lines in answer against offer. If matches true will be @@ -108,6 +87,15 @@ static bool VerifyMediaDescriptions( if ((offer->contents()[i].name) != answer->contents()[i].name) { return false; } + const MediaContentDescription* offer_mdesc = + static_cast<const MediaContentDescription*>( + offer->contents()[i].description); + const MediaContentDescription* answer_mdesc = + static_cast<const MediaContentDescription*>( + answer->contents()[i].description); + if (offer_mdesc->type() != answer_mdesc->type()) { + return false; + } } return true; } @@ -136,17 +124,18 @@ static bool VerifyCrypto(const SessionDescription* desc, *error = kInvalidSdp; return false; } - if (media->cryptos().empty()) { + if (dtls_enabled) { if (!tinfo->description.identity_fingerprint) { - // Crypto must be supplied. - LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP."; - *error = kSdpWithoutCrypto; + LOG(LS_WARNING) << + "Session description must have DTLS fingerprint if DTLS enabled."; + *error = kSdpWithoutDtlsFingerprint; return false; } - if (!dtls_enabled) { + } else { + if (media->cryptos().empty()) { LOG(LS_WARNING) << "Session description must have SDES when DTLS disabled."; - *error = kSdpWithoutSdesAndDtlsDisabled; + *error = kSdpWithoutSdesCrypto; return false; } } @@ -184,9 +173,8 @@ static bool VerifyIceUfragPwdPresent(const SessionDescription* desc) { // current security policy, to ensure a failure occurs if there is an error // in crypto negotiation. // Called when processing the local session description. -static void UpdateSessionDescriptionSecurePolicy( - cricket::SecureMediaPolicy secure_policy, - SessionDescription* sdesc) { +static void UpdateSessionDescriptionSecurePolicy(cricket::CryptoType type, + SessionDescription* sdesc) { if (!sdesc) { return; } @@ -199,7 +187,7 @@ static void UpdateSessionDescriptionSecurePolicy( MediaContentDescription* mdesc = static_cast<MediaContentDescription*> (iter->description); if (mdesc) { - mdesc->set_crypto_required(secure_policy == cricket::SEC_REQUIRED); + mdesc->set_crypto_required(type); } } } @@ -262,39 +250,60 @@ static bool GetTrackIdBySsrc(const SessionDescription* session_description, return false; } -static bool BadSdp(const std::string& desc, std::string* err_desc) { +static bool BadSdp(const std::string& source, + const std::string& type, + const std::string& reason, + std::string* err_desc) { + std::ostringstream desc; + desc << "Failed to set " << source << " " << type << " sdp: " << reason; + if (err_desc) { - *err_desc = desc; + *err_desc = desc.str(); } - LOG(LS_ERROR) << desc; + LOG(LS_ERROR) << desc.str(); return false; } -static bool BadLocalSdp(const std::string& desc, std::string* err_desc) { - std::string set_local_sdp_failed = kSetLocalSdpFailed; - set_local_sdp_failed.append(desc); - return BadSdp(set_local_sdp_failed, err_desc); -} - -static bool BadRemoteSdp(const std::string& desc, std::string* err_desc) { - std::string set_remote_sdp_failed = kSetRemoteSdpFailed; - set_remote_sdp_failed.append(desc); - return BadSdp(set_remote_sdp_failed, err_desc); -} - static bool BadSdp(cricket::ContentSource source, - const std::string& desc, std::string* err_desc) { + const std::string& type, + const std::string& reason, + std::string* err_desc) { if (source == cricket::CS_LOCAL) { - return BadLocalSdp(desc, err_desc); + return BadSdp("local", type, reason, err_desc); } else { - return BadRemoteSdp(desc, err_desc); + return BadSdp("remote", type, reason, err_desc); } } -static std::string SessionErrorMsg(cricket::BaseSession::Error error) { - std::ostringstream desc; - desc << kSessionError << error; - return desc.str(); +static bool BadLocalSdp(const std::string& type, + const std::string& reason, + std::string* err_desc) { + return BadSdp(cricket::CS_LOCAL, type, reason, err_desc); +} + +static bool BadRemoteSdp(const std::string& type, + const std::string& reason, + std::string* err_desc) { + return BadSdp(cricket::CS_REMOTE, type, reason, err_desc); +} + +static bool BadOfferSdp(cricket::ContentSource source, + const std::string& reason, + std::string* err_desc) { + return BadSdp(source, SessionDescriptionInterface::kOffer, reason, err_desc); +} + +static bool BadPranswerSdp(cricket::ContentSource source, + const std::string& reason, + std::string* err_desc) { + return BadSdp(source, SessionDescriptionInterface::kPrAnswer, + reason, err_desc); +} + +static bool BadAnswerSdp(cricket::ContentSource source, + const std::string& reason, + std::string* err_desc) { + return BadSdp(source, SessionDescriptionInterface::kAnswer, reason, err_desc); } #define GET_STRING_OF_STATE(state) \ @@ -328,20 +337,20 @@ static std::string GetStateString(cricket::BaseSession::State state) { return result; } -#define GET_STRING_OF_ERROR(err) \ +#define GET_STRING_OF_ERROR_CODE(err) \ case cricket::BaseSession::err: \ result = #err; \ break; -static std::string GetErrorString(cricket::BaseSession::Error err) { +static std::string GetErrorCodeString(cricket::BaseSession::Error err) { std::string result; switch (err) { - GET_STRING_OF_ERROR(ERROR_NONE) - GET_STRING_OF_ERROR(ERROR_TIME) - GET_STRING_OF_ERROR(ERROR_RESPONSE) - GET_STRING_OF_ERROR(ERROR_NETWORK) - GET_STRING_OF_ERROR(ERROR_CONTENT) - GET_STRING_OF_ERROR(ERROR_TRANSPORT) + GET_STRING_OF_ERROR_CODE(ERROR_NONE) + GET_STRING_OF_ERROR_CODE(ERROR_TIME) + GET_STRING_OF_ERROR_CODE(ERROR_RESPONSE) + GET_STRING_OF_ERROR_CODE(ERROR_NETWORK) + GET_STRING_OF_ERROR_CODE(ERROR_CONTENT) + GET_STRING_OF_ERROR_CODE(ERROR_TRANSPORT) default: ASSERT(false); break; @@ -349,12 +358,33 @@ static std::string GetErrorString(cricket::BaseSession::Error err) { return result; } -static bool SetSessionStateFailed(cricket::ContentSource source, - cricket::BaseSession::Error err, - std::string* err_desc) { - std::string set_state_err = kUpdateStateFailed; - set_state_err.append(GetErrorString(err)); - return BadSdp(source, set_state_err, err_desc); +static std::string MakeErrorString(const std::string& error, + const std::string& desc) { + std::ostringstream ret; + ret << error << " " << desc; + return ret.str(); +} + +static std::string MakeTdErrorString(const std::string& desc) { + return MakeErrorString(kPushDownTDFailed, desc); +} + +// Set |option| to the highest-priority value of |key| in the optional +// constraints if the key is found and has a valid value. +template<typename T> +static void SetOptionFromOptionalConstraint( + const MediaConstraintsInterface* constraints, + const std::string& key, cricket::Settable<T>* option) { + if (!constraints) { + return; + } + std::string string_value; + T value; + if (constraints->GetOptional().FindFirst(key, &string_value)) { + if (talk_base::FromString(string_value, &value)) { + option->Set(value); + } + } } // Help class used to remember if a a remote peer has requested ice restart by @@ -432,7 +462,6 @@ WebRtcSession::WebRtcSession( ice_connection_state_(PeerConnectionInterface::kIceConnectionNew), older_version_remote_peer_(false), dtls_enabled_(false), - dscp_enabled_(false), data_channel_type_(cricket::DCT_NONE), ice_restart_latch_(new IceRestartAnswerLatch) { } @@ -458,21 +487,26 @@ WebRtcSession::~WebRtcSession() { bool WebRtcSession::Initialize( const PeerConnectionFactoryInterface::Options& options, - const MediaConstraintsInterface* constraints, - DTLSIdentityServiceInterface* dtls_identity_service) { + const MediaConstraintsInterface* constraints, + DTLSIdentityServiceInterface* dtls_identity_service, + PeerConnectionInterface::IceTransportsType ice_transport) { // TODO(perkj): Take |constraints| into consideration. Return false if not all // mandatory constraints can be fulfilled. Note that |constraints| // can be null. bool value; - // Enable DTLS by default if |dtls_identity_service| is valid. - dtls_enabled_ = (dtls_identity_service != NULL); - // |constraints| can override the default |dtls_enabled_| value. - if (FindConstraint( - constraints, - MediaConstraintsInterface::kEnableDtlsSrtp, - &value, NULL)) { - dtls_enabled_ = value; + if (options.disable_encryption) { + dtls_enabled_ = false; + } else { + // Enable DTLS by default if |dtls_identity_service| is valid. + dtls_enabled_ = (dtls_identity_service != NULL); + // |constraints| can override the default |dtls_enabled_| value. + if (FindConstraint( + constraints, + MediaConstraintsInterface::kEnableDtlsSrtp, + &value, NULL)) { + dtls_enabled_ = value; + } } // Enable creation of RTP data channels if the kEnableRtpDataChannels is set. @@ -499,9 +533,92 @@ bool WebRtcSession::Initialize( constraints, MediaConstraintsInterface::kEnableDscp, &value, NULL)) { - dscp_enabled_ = value; + audio_options_.dscp.Set(value); + video_options_.dscp.Set(value); + } + + // Find Suspend Below Min Bitrate constraint. + if (FindConstraint( + constraints, + MediaConstraintsInterface::kEnableVideoSuspendBelowMinBitrate, + &value, + NULL)) { + video_options_.suspend_below_min_bitrate.Set(value); } + if (FindConstraint( + constraints, + MediaConstraintsInterface::kSkipEncodingUnusedStreams, + &value, + NULL)) { + video_options_.skip_encoding_unused_streams.Set(value); + } + + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kScreencastMinBitrate, + &video_options_.screencast_min_bitrate); + + // Find constraints for cpu overuse detection. + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kCpuUnderuseThreshold, + &video_options_.cpu_underuse_threshold); + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kCpuOveruseThreshold, + &video_options_.cpu_overuse_threshold); + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kCpuOveruseDetection, + &video_options_.cpu_overuse_detection); + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kCpuOveruseEncodeUsage, + &video_options_.cpu_overuse_encode_usage); + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kCpuUnderuseEncodeRsdThreshold, + &video_options_.cpu_underuse_encode_rsd_threshold); + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kCpuOveruseEncodeRsdThreshold, + &video_options_.cpu_overuse_encode_rsd_threshold); + + // Find payload padding constraint. + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kPayloadPadding, + &video_options_.use_payload_padding); + + // Find improved wifi bwe constraint. + if (FindConstraint( + constraints, + MediaConstraintsInterface::kImprovedWifiBwe, + &value, + NULL)) { + video_options_.use_improved_wifi_bandwidth_estimator.Set(value); + } else { + // Enable by default if the constraint is not set. + video_options_.use_improved_wifi_bandwidth_estimator.Set(true); + } + + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kHighStartBitrate, + &video_options_.video_start_bitrate); + + if (FindConstraint( + constraints, + MediaConstraintsInterface::kVeryHighBitrate, + &value, + NULL)) { + video_options_.video_highest_bitrate.Set( + cricket::VideoOptions::VERY_HIGH); + } else if (FindConstraint( + constraints, + MediaConstraintsInterface::kHighBitrate, + &value, + NULL)) { + video_options_.video_highest_bitrate.Set( + cricket::VideoOptions::HIGH); + } + + SetOptionFromOptionalConstraint(constraints, + MediaConstraintsInterface::kOpusFec, + &audio_options_.opus_fec); + const cricket::VideoCodec default_codec( JsepSessionDescription::kDefaultVideoCodecId, JsepSessionDescription::kDefaultVideoCodecName, @@ -526,7 +643,7 @@ bool WebRtcSession::Initialize( this, &WebRtcSession::OnIdentityReady); if (options.disable_encryption) { - webrtc_session_desc_factory_->SetSecure(cricket::SEC_DISABLED); + webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED); } return true; @@ -554,13 +671,12 @@ bool WebRtcSession::StartCandidatesAllocation() { return true; } -void WebRtcSession::SetSecurePolicy( - cricket::SecureMediaPolicy secure_policy) { - webrtc_session_desc_factory_->SetSecure(secure_policy); +void WebRtcSession::SetSdesPolicy(cricket::SecurePolicy secure_policy) { + webrtc_session_desc_factory_->SetSdesPolicy(secure_policy); } -cricket::SecureMediaPolicy WebRtcSession::SecurePolicy() const { - return webrtc_session_desc_factory_->Secure(); +cricket::SecurePolicy WebRtcSession::SdesPolicy() const { + return webrtc_session_desc_factory_->SdesPolicy(); } bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) { @@ -609,10 +725,13 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, set_initiator(true); } - cricket::SecureMediaPolicy secure_policy = - webrtc_session_desc_factory_->Secure(); + cricket::SecurePolicy sdes_policy = + webrtc_session_desc_factory_->SdesPolicy(); + cricket::CryptoType crypto_required = dtls_enabled_ ? + cricket::CT_DTLS : (sdes_policy == cricket::SEC_REQUIRED ? + cricket::CT_SDES : cricket::CT_NONE); // Update the MediaContentDescription crypto settings as per the policy set. - UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description()); + UpdateSessionDescriptionSecurePolicy(crypto_required, desc->description()); set_local_description(desc->description()->Copy()); local_desc_.reset(desc_temp.release()); @@ -621,15 +740,14 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, if (action == kOffer && !CreateChannels(local_desc_->description())) { // TODO(mallinath) - Handle CreateChannel failure, as new local description // is applied. Restore back to old description. - return BadLocalSdp(kCreateChannelFailed, err_desc); + return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc); } // Remove channel and transport proxies, if MediaContentDescription is // rejected. RemoveUnusedChannelsAndTransports(local_desc_->description()); - if (!UpdateSessionState(action, cricket::CS_LOCAL, - local_desc_->description(), err_desc)) { + if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) { return false; } // Kick starting the ice candidates allocation. @@ -644,7 +762,7 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, mediastream_signaling_->OnDtlsRoleReadyForSctp(role); } if (error() != cricket::BaseSession::ERROR_NONE) { - return BadLocalSdp(SessionErrorMsg(error()), err_desc); + return BadLocalSdp(desc->type(), GetSessionErrorMsg(), err_desc); } return true; } @@ -664,7 +782,7 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, if (action == kOffer && !CreateChannels(desc->description())) { // TODO(mallinath) - Handle CreateChannel failure, as new local description // is applied. Restore back to old description. - return BadRemoteSdp(kCreateChannelFailed, err_desc); + return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc); } // Remove channel and transport proxies, if MediaContentDescription is @@ -674,15 +792,14 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, // NOTE: Candidates allocation will be initiated only when SetLocalDescription // is called. set_remote_description(desc->description()->Copy()); - if (!UpdateSessionState(action, cricket::CS_REMOTE, - desc->description(), err_desc)) { + if (!UpdateSessionState(action, cricket::CS_REMOTE, err_desc)) { return false; } // Update remote MediaStreams. mediastream_signaling_->OnRemoteDescriptionChanged(desc); if (local_description() && !UseCandidatesInSessionDescription(desc)) { - return BadRemoteSdp(kInvalidCandidates, err_desc); + return BadRemoteSdp(desc->type(), kInvalidCandidates, err_desc); } // Copy all saved candidates. @@ -702,47 +819,47 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, } if (error() != cricket::BaseSession::ERROR_NONE) { - return BadRemoteSdp(SessionErrorMsg(error()), err_desc); + return BadRemoteSdp(desc->type(), GetSessionErrorMsg(), err_desc); } return true; } bool WebRtcSession::UpdateSessionState( Action action, cricket::ContentSource source, - const cricket::SessionDescription* desc, std::string* err_desc) { // If there's already a pending error then no state transition should happen. // But all call-sites should be verifying this before calling us! ASSERT(error() == cricket::BaseSession::ERROR_NONE); + std::string td_err; if (action == kOffer) { - if (!PushdownTransportDescription(source, cricket::CA_OFFER)) { - return BadSdp(source, kPushDownOfferTDFailed, err_desc); + if (!PushdownTransportDescription(source, cricket::CA_OFFER, &td_err)) { + return BadOfferSdp(source, MakeTdErrorString(td_err), err_desc); } SetState(source == cricket::CS_LOCAL ? STATE_SENTINITIATE : STATE_RECEIVEDINITIATE); if (error() != cricket::BaseSession::ERROR_NONE) { - return SetSessionStateFailed(source, error(), err_desc); + return BadOfferSdp(source, GetSessionErrorMsg(), err_desc); } } else if (action == kPrAnswer) { - if (!PushdownTransportDescription(source, cricket::CA_PRANSWER)) { - return BadSdp(source, kPushDownPranswerTDFailed, err_desc); + if (!PushdownTransportDescription(source, cricket::CA_PRANSWER, &td_err)) { + return BadPranswerSdp(source, MakeTdErrorString(td_err), err_desc); } EnableChannels(); SetState(source == cricket::CS_LOCAL ? STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT); if (error() != cricket::BaseSession::ERROR_NONE) { - return SetSessionStateFailed(source, error(), err_desc); + return BadPranswerSdp(source, GetSessionErrorMsg(), err_desc); } } else if (action == kAnswer) { - if (!PushdownTransportDescription(source, cricket::CA_ANSWER)) { - return BadSdp(source, kPushDownAnswerTDFailed, err_desc); + if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) { + return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc); } MaybeEnableMuxingSupport(); EnableChannels(); SetState(source == cricket::CS_LOCAL ? STATE_SENTACCEPT : STATE_RECEIVEDACCEPT); if (error() != cricket::BaseSession::ERROR_NONE) { - return SetSessionStateFailed(source, error(), err_desc); + return BadAnswerSdp(source, GetSessionErrorMsg(), err_desc); } } return true; @@ -773,9 +890,32 @@ bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) { return false; } - if (!local_description() || !remote_description()) { - LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, " - << "save the candidate for later use."; + cricket::TransportProxy* transport_proxy = NULL; + if (remote_description()) { + size_t mediacontent_index = + static_cast<size_t>(candidate->sdp_mline_index()); + size_t remote_content_size = + BaseSession::remote_description()->contents().size(); + if (mediacontent_index >= remote_content_size) { + LOG(LS_ERROR) + << "ProcessIceMessage: Invalid candidate media index."; + return false; + } + + cricket::ContentInfo content = + BaseSession::remote_description()->contents()[mediacontent_index]; + transport_proxy = GetTransportProxy(content.name); + } + + // We need to check the local/remote description for the Transport instead of + // the session, because a new Transport added during renegotiation may have + // them unset while the session has them set from the previou negotiation. Not + // doing so may trigger the auto generation of transport description and mess + // up DTLS identity information, ICE credential, etc. + if (!transport_proxy || !(transport_proxy->local_description_set() && + transport_proxy->remote_description_set())) { + LOG(LS_INFO) << "ProcessIceMessage: Local/Remote description not set " + << "on the Transport, save the candidate for later use."; saved_candidates_.push_back( new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); @@ -788,41 +928,30 @@ bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) { return false; } - return UseCandidatesInSessionDescription(remote_desc_.get()); + return UseCandidate(candidate); } -bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) { - if (GetLocalTrackId(ssrc, id)) { - if (GetRemoteTrackId(ssrc, id)) { - LOG(LS_WARNING) << "SSRC " << ssrc - << " exists in both local and remote descriptions"; - return true; // We return the remote track id. - } - return true; - } else { - return GetRemoteTrackId(ssrc, id); - } +bool WebRtcSession::UpdateIce(PeerConnectionInterface::IceTransportsType type) { + return false; } -bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) { +bool WebRtcSession::GetLocalTrackIdBySsrc(uint32 ssrc, std::string* track_id) { if (!BaseSession::local_description()) return false; return webrtc::GetTrackIdBySsrc( - BaseSession::local_description(), ssrc, track_id); + BaseSession::local_description(), ssrc, track_id); } -bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) { +bool WebRtcSession::GetRemoteTrackIdBySsrc(uint32 ssrc, std::string* track_id) { if (!BaseSession::remote_description()) - return false; + return false; return webrtc::GetTrackIdBySsrc( - BaseSession::remote_description(), ssrc, track_id); + BaseSession::remote_description(), ssrc, track_id); } -std::string WebRtcSession::BadStateErrMsg( - const std::string& type, State state) { +std::string WebRtcSession::BadStateErrMsg(State state) { std::ostringstream desc; - desc << "Called with type in wrong state, " - << "type: " << type << " state: " << GetStateString(state); + desc << "Called in wrong state: " << GetStateString(state); return desc.str(); } @@ -870,6 +999,18 @@ void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable, voice_channel_->SetChannelOptions(options); } +void WebRtcSession::SetAudioPlayoutVolume(uint32 ssrc, double volume) { + ASSERT(signaling_thread()->IsCurrent()); + ASSERT(volume >= 0 && volume <= 10); + if (!voice_channel_) { + LOG(LS_ERROR) << "SetAudioPlayoutVolume: No audio channel exists."; + return; + } + + if (!voice_channel_->SetOutputScaling(ssrc, volume, volume)) + ASSERT(false); +} + bool WebRtcSession::SetCaptureDevice(uint32 ssrc, cricket::VideoCapturer* camera) { ASSERT(signaling_thread()->IsCurrent()); @@ -1007,6 +1148,8 @@ void WebRtcSession::AddSctpDataStream(uint32 sid) { } void WebRtcSession::RemoveSctpDataStream(uint32 sid) { + mediastream_signaling_->RemoveSctpDataChannel(static_cast<int>(sid)); + if (!data_channel_.get()) { LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is " << "NULL."; @@ -1022,7 +1165,7 @@ bool WebRtcSession::ReadyToSendData() const { talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel( const std::string& label, - const DataChannelInit* config) { + const InternalDataChannelInit* config) { if (state() == STATE_RECEIVEDTERMINATE) { return NULL; } @@ -1030,8 +1173,8 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel( LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call."; return NULL; } - DataChannelInit new_config = config ? (*config) : DataChannelInit(); - + InternalDataChannelInit new_config = + config ? (*config) : InternalDataChannelInit(); if (data_channel_type_ == cricket::DCT_SCTP) { if (new_config.id < 0) { talk_base::SSLRole role; @@ -1047,8 +1190,8 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel( } } - talk_base::scoped_refptr<DataChannel> channel( - DataChannel::Create(this, data_channel_type_, label, &new_config)); + talk_base::scoped_refptr<DataChannel> channel(DataChannel::Create( + this, data_channel_type_, label, new_config)); if (channel && !mediastream_signaling_->AddDataChannel(channel)) return NULL; @@ -1143,15 +1286,8 @@ void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) { void WebRtcSession::OnTransportWritable(cricket::Transport* transport) { ASSERT(signaling_thread()->IsCurrent()); - // TODO(bemasc): Expose more API from Transport to detect when - // candidate selection starts or stops, due to success or failure. if (transport->all_channels_writable()) { - if (ice_connection_state_ == - PeerConnectionInterface::kIceConnectionChecking || - ice_connection_state_ == - PeerConnectionInterface::kIceConnectionDisconnected) { - SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); - } + SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); } else if (transport->HasChannels()) { // If the current state is Connected or Completed, then there were writable // channels but now there are not, so the next state must be Disconnected. @@ -1165,6 +1301,16 @@ void WebRtcSession::OnTransportWritable(cricket::Transport* transport) { } } +void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) { + ASSERT(signaling_thread()->IsCurrent()); + SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted); +} + +void WebRtcSession::OnTransportFailed(cricket::Transport* transport) { + ASSERT(signaling_thread()->IsCurrent()); + SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed); +} + void WebRtcSession::OnTransportProxyCandidatesReady( cricket::TransportProxy* proxy, const cricket::Candidates& candidates) { ASSERT(signaling_thread()->IsCurrent()); @@ -1284,7 +1430,9 @@ bool WebRtcSession::UseCandidate( } // TODO(bemasc): If state is Completed, go back to Connected. } else { - LOG(LS_WARNING) << error; + if (!error.empty()) { + LOG(LS_WARNING) << error; + } } return true; } @@ -1367,11 +1515,7 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) { if (!voice_channel_.get()) return false; - if (dscp_enabled_) { - cricket::AudioOptions options; - options.dscp.Set(true); - voice_channel_->SetChannelOptions(options); - } + voice_channel_->SetChannelOptions(audio_options_); return true; } @@ -1381,11 +1525,7 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) { if (!video_channel_.get()) return false; - if (dscp_enabled_) { - cricket::VideoOptions options; - options.dscp.Set(true); - video_channel_->SetChannelOptions(options); - } + video_channel_->SetChannelOptions(video_options_); return true; } @@ -1398,8 +1538,11 @@ bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) { } if (sctp) { mediastream_signaling_->OnDataTransportCreatedForSctp(); - data_channel_->SignalNewStreamReceived.connect( - this, &WebRtcSession::OnNewDataChannelReceived); + data_channel_->SignalDataReceived.connect( + this, &WebRtcSession::OnDataChannelMessageReceived); + data_channel_->SignalStreamClosedRemotely.connect( + mediastream_signaling_, + &MediaStreamSignaling::OnRemoteSctpDataChannelClosed); } return true; } @@ -1417,14 +1560,17 @@ void WebRtcSession::CopySavedCandidates( saved_candidates_.clear(); } -void WebRtcSession::OnNewDataChannelReceived( - const std::string& label, const DataChannelInit& init) { +void WebRtcSession::OnDataChannelMessageReceived( + cricket::DataChannel* channel, + const cricket::ReceiveDataParams& params, + const talk_base::Buffer& payload) { ASSERT(data_channel_type_ == cricket::DCT_SCTP); - if (!mediastream_signaling_->AddDataChannelFromOpenMessage( - label, init)) { - LOG(LS_WARNING) << "Failed to create data channel from OPEN message."; - return; + if (params.type == cricket::DMT_CONTROL && + mediastream_signaling_->IsSctpSidAvailable(params.ssrc)) { + // Received CONTROL on unused sid, process as an OPEN message. + mediastream_signaling_->AddDataChannelFromOpenMessage(params, payload); } + // otherwise ignore the message. } // Returns false if bundle is enabled and rtcp_mux is disabled. @@ -1461,40 +1607,41 @@ bool WebRtcSession::HasRtcpMuxEnabled( bool WebRtcSession::ValidateSessionDescription( const SessionDescriptionInterface* sdesc, - cricket::ContentSource source, std::string* error_desc) { - + cricket::ContentSource source, std::string* err_desc) { + std::string type; if (error() != cricket::BaseSession::ERROR_NONE) { - return BadSdp(source, SessionErrorMsg(error()), error_desc); + return BadSdp(source, type, GetSessionErrorMsg(), err_desc); } if (!sdesc || !sdesc->description()) { - return BadSdp(source, kInvalidSdp, error_desc); + return BadSdp(source, type, kInvalidSdp, err_desc); } - std::string type = sdesc->type(); + type = sdesc->type(); Action action = GetAction(sdesc->type()); if (source == cricket::CS_LOCAL) { if (!ExpectSetLocalDescription(action)) - return BadSdp(source, BadStateErrMsg(type, state()), error_desc); + return BadLocalSdp(type, BadStateErrMsg(state()), err_desc); } else { if (!ExpectSetRemoteDescription(action)) - return BadSdp(source, BadStateErrMsg(type, state()), error_desc); + return BadRemoteSdp(type, BadStateErrMsg(state()), err_desc); } // Verify crypto settings. std::string crypto_error; - if (webrtc_session_desc_factory_->Secure() == cricket::SEC_REQUIRED && + if ((webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED || + dtls_enabled_) && !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) { - return BadSdp(source, crypto_error, error_desc); + return BadSdp(source, type, crypto_error, err_desc); } // Verify ice-ufrag and ice-pwd. if (!VerifyIceUfragPwdPresent(sdesc->description())) { - return BadSdp(source, kSdpWithoutIceUfragPwd, error_desc); + return BadSdp(source, type, kSdpWithoutIceUfragPwd, err_desc); } if (!ValidateBundleSettings(sdesc->description())) { - return BadSdp(source, kBundleWithoutRtcpMux, error_desc); + return BadSdp(source, type, kBundleWithoutRtcpMux, err_desc); } // Verify m-lines in Answer when compared against Offer. @@ -1503,7 +1650,7 @@ bool WebRtcSession::ValidateSessionDescription( (source == cricket::CS_LOCAL) ? remote_description()->description() : local_description()->description(); if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) { - return BadSdp(source, kMlineMismatch, error_desc); + return BadAnswerSdp(source, kMlineMismatch, err_desc); } } @@ -1540,4 +1687,11 @@ bool WebRtcSession::ExpectSetRemoteDescription(Action action) { (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT)); } +std::string WebRtcSession::GetSessionErrorMsg() { + std::ostringstream desc; + desc << kSessionError << GetErrorCodeString(error()) << ". "; + desc << kSessionErrorDesc << error_desc() << "."; + return desc.str(); +} + } // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.h b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.h index 4c83906ae98..3ffea8ffac5 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.h @@ -42,6 +42,7 @@ #include "talk/session/media/mediasession.h" namespace cricket { + class BaseChannel; class ChannelManager; class DataChannel; @@ -50,28 +51,28 @@ class Transport; class VideoCapturer; class VideoChannel; class VoiceChannel; + } // namespace cricket namespace webrtc { + class IceRestartAnswerLatch; +class JsepIceCandidate; class MediaStreamSignaling; class WebRtcSessionDescriptionFactory; -extern const char kSetLocalSdpFailed[]; -extern const char kSetRemoteSdpFailed[]; -extern const char kCreateChannelFailed[]; extern const char kBundleWithoutRtcpMux[]; +extern const char kCreateChannelFailed[]; extern const char kInvalidCandidates[]; extern const char kInvalidSdp[]; extern const char kMlineMismatch[]; -extern const char kSdpWithoutCrypto[]; -extern const char kSdpWithoutSdesAndDtlsDisabled[]; +extern const char kPushDownTDFailed[]; +extern const char kSdpWithoutDtlsFingerprint[]; +extern const char kSdpWithoutSdesCrypto[]; extern const char kSdpWithoutIceUfragPwd[]; +extern const char kSdpWithoutSdesAndDtlsDisabled[]; extern const char kSessionError[]; -extern const char kUpdateStateFailed[]; -extern const char kPushDownOfferTDFailed[]; -extern const char kPushDownPranswerTDFailed[]; -extern const char kPushDownAnswerTDFailed[]; +extern const char kSessionErrorDesc[]; // ICE state callback interface. class IceObserver { @@ -113,7 +114,8 @@ class WebRtcSession : public cricket::BaseSession, bool Initialize(const PeerConnectionFactoryInterface::Options& options, const MediaConstraintsInterface* constraints, - DTLSIdentityServiceInterface* dtls_identity_service); + DTLSIdentityServiceInterface* dtls_identity_service, + PeerConnectionInterface::IceTransportsType ice_transport); // Deletes the voice, video and data channel and changes the session state // to STATE_RECEIVEDTERMINATE. void Terminate(); @@ -132,8 +134,8 @@ class WebRtcSession : public cricket::BaseSession, return data_channel_.get(); } - void SetSecurePolicy(cricket::SecureMediaPolicy secure_policy); - cricket::SecureMediaPolicy SecurePolicy() const; + void SetSdesPolicy(cricket::SecurePolicy secure_policy); + cricket::SecurePolicy SdesPolicy() const; // Get current ssl role from transport. bool GetSslRole(talk_base::SSLRole* role); @@ -153,6 +155,9 @@ class WebRtcSession : public cricket::BaseSession, bool SetRemoteDescription(SessionDescriptionInterface* desc, std::string* err_desc); bool ProcessIceMessage(const IceCandidateInterface* ice_candidate); + + bool UpdateIce(PeerConnectionInterface::IceTransportsType type); + const SessionDescriptionInterface* local_description() const { return local_desc_.get(); } @@ -161,7 +166,9 @@ class WebRtcSession : public cricket::BaseSession, } // Get the id used as a media stream track's "id" field from ssrc. - virtual bool GetTrackIdBySsrc(uint32 ssrc, std::string* id); + virtual bool GetLocalTrackIdBySsrc(uint32 ssrc, std::string* track_id); + virtual bool GetRemoteTrackIdBySsrc(uint32 ssrc, std::string* track_id); + // AudioMediaProviderInterface implementation. virtual void SetAudioPlayout(uint32 ssrc, bool enable, @@ -169,6 +176,7 @@ class WebRtcSession : public cricket::BaseSession, virtual void SetAudioSend(uint32 ssrc, bool enable, const cricket::AudioOptions& options, cricket::AudioRenderer* renderer) OVERRIDE; + virtual void SetAudioPlayoutVolume(uint32 ssrc, double volume) OVERRIDE; // Implements VideoMediaProviderInterface. virtual bool SetCaptureDevice(uint32 ssrc, @@ -195,9 +203,10 @@ class WebRtcSession : public cricket::BaseSession, virtual void RemoveSctpDataStream(uint32 sid) OVERRIDE; virtual bool ReadyToSendData() const OVERRIDE; + // Implements DataChannelFactory. talk_base::scoped_refptr<DataChannel> CreateDataChannel( const std::string& label, - const DataChannelInit* config); + const InternalDataChannelInit* config) OVERRIDE; cricket::DataChannelType data_channel_type() const; @@ -225,7 +234,6 @@ class WebRtcSession : public cricket::BaseSession, // candidates allocation. bool StartCandidatesAllocation(); bool UpdateSessionState(Action action, cricket::ContentSource source, - const cricket::SessionDescription* desc, std::string* err_desc); static Action GetAction(const std::string& type); @@ -233,6 +241,8 @@ class WebRtcSession : public cricket::BaseSession, virtual void OnTransportRequestSignaling(cricket::Transport* transport); virtual void OnTransportConnecting(cricket::Transport* transport); virtual void OnTransportWritable(cricket::Transport* transport); + virtual void OnTransportCompleted(cricket::Transport* transport); + virtual void OnTransportFailed(cricket::Transport* transport); virtual void OnTransportProxyCandidatesReady( cricket::TransportProxy* proxy, const cricket::Candidates& candidates); @@ -275,13 +285,13 @@ class WebRtcSession : public cricket::BaseSession, // The |saved_candidates_| will be cleared after this function call. void CopySavedCandidates(SessionDescriptionInterface* dest_desc); - void OnNewDataChannelReceived(const std::string& label, - const DataChannelInit& init); + // Listens to SCTP CONTROL messages on unused SIDs and process them as OPEN + // messages. + void OnDataChannelMessageReceived(cricket::DataChannel* channel, + const cricket::ReceiveDataParams& params, + const talk_base::Buffer& payload); - bool GetLocalTrackId(uint32 ssrc, std::string* track_id); - bool GetRemoteTrackId(uint32 ssrc, std::string* track_id); - - std::string BadStateErrMsg(const std::string& type, State state); + std::string BadStateErrMsg(State state); void SetIceConnectionState(PeerConnectionInterface::IceConnectionState state); bool ValidateBundleSettings(const cricket::SessionDescription* desc); @@ -289,7 +299,7 @@ class WebRtcSession : public cricket::BaseSession, // Below methods are helper methods which verifies SDP. bool ValidateSessionDescription(const SessionDescriptionInterface* sdesc, cricket::ContentSource source, - std::string* error_desc); + std::string* err_desc); // Check if a call to SetLocalDescription is acceptable with |action|. bool ExpectSetLocalDescription(Action action); @@ -299,6 +309,8 @@ class WebRtcSession : public cricket::BaseSession, bool ValidateDtlsSetupAttribute(const cricket::SessionDescription* desc, Action action); + std::string GetSessionErrorMsg(); + talk_base::scoped_ptr<cricket::VoiceChannel> voice_channel_; talk_base::scoped_ptr<cricket::VideoChannel> video_channel_; talk_base::scoped_ptr<cricket::DataChannel> data_channel_; @@ -313,8 +325,6 @@ class WebRtcSession : public cricket::BaseSession, // If the remote peer is using a older version of implementation. bool older_version_remote_peer_; bool dtls_enabled_; - // Flag will be set based on the constraint value. - bool dscp_enabled_; // Specifies which kind of data channel is allowed. This is controlled // by the chrome command-line flag and constraints: // 1. If chrome command-line switch 'enable-sctp-data-channels' is enabled, @@ -332,6 +342,10 @@ class WebRtcSession : public cricket::BaseSession, sigslot::signal0<> SignalVideoChannelDestroyed; sigslot::signal0<> SignalDataChannelDestroyed; + // Member variables for caching global options. + cricket::AudioOptions audio_options_; + cricket::VideoOptions video_options_; + DISALLOW_COPY_AND_ASSIGN(WebRtcSession); }; } // namespace webrtc diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession_unittest.cc index 5ec880a1db2..956f8a66fb2 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession_unittest.cc @@ -53,6 +53,7 @@ #include "talk/media/devices/fakedevicemanager.h" #include "talk/p2p/base/stunserver.h" #include "talk/p2p/base/teststunserver.h" +#include "talk/p2p/base/testturnserver.h" #include "talk/p2p/client/basicportallocator.h" #include "talk/session/media/channelmanager.h" #include "talk/session/media/mediasession.h" @@ -72,6 +73,7 @@ using cricket::NS_JINGLE_ICE_UDP; using cricket::TransportInfo; using talk_base::SocketAddress; using talk_base::scoped_ptr; +using talk_base::Thread; using webrtc::CreateSessionDescription; using webrtc::CreateSessionDescriptionObserver; using webrtc::CreateSessionDescriptionRequest; @@ -87,20 +89,22 @@ using webrtc::SessionDescriptionInterface; using webrtc::StreamCollection; using webrtc::WebRtcSession; using webrtc::kBundleWithoutRtcpMux; +using webrtc::kCreateChannelFailed; +using webrtc::kInvalidSdp; using webrtc::kMlineMismatch; -using webrtc::kPushDownAnswerTDFailed; -using webrtc::kPushDownPranswerTDFailed; -using webrtc::kSdpWithoutCrypto; +using webrtc::kPushDownTDFailed; using webrtc::kSdpWithoutIceUfragPwd; -using webrtc::kSdpWithoutSdesAndDtlsDisabled; +using webrtc::kSdpWithoutDtlsFingerprint; +using webrtc::kSdpWithoutSdesCrypto; using webrtc::kSessionError; -using webrtc::kSetLocalSdpFailed; -using webrtc::kSetRemoteSdpFailed; +using webrtc::kSessionErrorDesc; static const int kClientAddrPort = 0; static const char kClientAddrHost1[] = "11.11.11.11"; static const char kClientAddrHost2[] = "22.22.22.22"; static const char kStunAddrHost[] = "99.99.99.1"; +static const SocketAddress kTurnUdpIntAddr("99.99.99.4", 3478); +static const SocketAddress kTurnUdpExtAddr("99.99.99.6", 0); static const char kSessionVersion[] = "1"; @@ -118,6 +122,12 @@ static const char kFakeDtlsFingerprint[] = "BB:CD:72:F7:2F:D0:BA:43:F3:68:B1:0C:23:72:B6:4A:" "0F:DE:34:06:BC:E0:FE:01:BC:73:C8:6D:F4:65:D5:24"; +static const char kTooLongIceUfragPwd[] = + "IceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfrag" + "IceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfrag" + "IceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfrag" + "IceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfrag"; + // Add some extra |newlines| to the |message| after |line|. static void InjectAfter(const std::string& line, const std::string& newlines, @@ -249,7 +259,11 @@ class WebRtcSessionCreateSDPObserverForTest class FakeAudioRenderer : public cricket::AudioRenderer { public: - FakeAudioRenderer() : channel_id_(-1) {} + FakeAudioRenderer() : channel_id_(-1), sink_(NULL) {} + virtual ~FakeAudioRenderer() { + if (sink_) + sink_->OnClose(); + } virtual void AddChannel(int channel_id) OVERRIDE { ASSERT(channel_id_ == -1); @@ -259,10 +273,15 @@ class FakeAudioRenderer : public cricket::AudioRenderer { ASSERT(channel_id == channel_id_); channel_id_ = -1; } + virtual void SetSink(Sink* sink) OVERRIDE { + sink_ = sink; + } int channel_id() const { return channel_id_; } + cricket::AudioRenderer::Sink* sink() const { return sink_; } private: int channel_id_; + cricket::AudioRenderer::Sink* sink_; }; class WebRtcSessionTest : public testing::Test { @@ -285,16 +304,20 @@ class WebRtcSessionTest : public testing::Test { ss_scope_(fss_.get()), stun_socket_addr_(talk_base::SocketAddress(kStunAddrHost, cricket::STUN_SERVER_PORT)), - stun_server_(talk_base::Thread::Current(), stun_socket_addr_), - allocator_(&network_manager_, stun_socket_addr_, - SocketAddress(), SocketAddress(), SocketAddress()), - mediastream_signaling_(channel_manager_.get()) { + stun_server_(Thread::Current(), stun_socket_addr_), + turn_server_(Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr), + allocator_(new cricket::BasicPortAllocator( + &network_manager_, stun_socket_addr_, + SocketAddress(), SocketAddress(), SocketAddress())), + mediastream_signaling_(channel_manager_.get()), + ice_type_(PeerConnectionInterface::kAll) { tdesc_factory_->set_protocol(cricket::ICEPROTO_HYBRID); - allocator_.set_flags(cricket::PORTALLOCATOR_DISABLE_TCP | + allocator_->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP | cricket::PORTALLOCATOR_DISABLE_RELAY | cricket::PORTALLOCATOR_ENABLE_BUNDLE); EXPECT_TRUE(channel_manager_->Init()); desc_factory_->set_add_legacy_streams(false); + allocator_->set_step_delay(cricket::kMinimumStepDelay); } static void SetUpTestCase() { @@ -309,11 +332,15 @@ class WebRtcSessionTest : public testing::Test { network_manager_.AddInterface(addr); } + void SetIceTransportType(PeerConnectionInterface::IceTransportsType type) { + ice_type_ = type; + } + void Init(DTLSIdentityServiceInterface* identity_service) { ASSERT_TRUE(session_.get() == NULL); session_.reset(new WebRtcSessionForTest( channel_manager_.get(), talk_base::Thread::Current(), - talk_base::Thread::Current(), &allocator_, + talk_base::Thread::Current(), allocator_.get(), &observer_, &mediastream_signaling_)); @@ -323,7 +350,7 @@ class WebRtcSessionTest : public testing::Test { observer_.ice_gathering_state_); EXPECT_TRUE(session_->Initialize(options_, constraints_.get(), - identity_service)); + identity_service, ice_type_)); } void InitWithDtmfCodec() { @@ -449,7 +476,7 @@ class WebRtcSessionTest : public testing::Test { // Set the internal fake description factories to do DTLS-SRTP. void SetFactoryDtlsSrtp() { - desc_factory_->set_secure(cricket::SEC_ENABLED); + desc_factory_->set_secure(cricket::SEC_DISABLED); std::string identity_name = "WebRTC" + talk_base::ToString(talk_base::CreateRandomId()); identity_.reset(talk_base::SSLIdentity::Generate(identity_name)); @@ -475,8 +502,8 @@ class WebRtcSessionTest : public testing::Test { CreateRemoteOffer(options, cricket::SEC_DISABLED)); ASSERT_TRUE(offer != NULL); VerifyNoCryptoParams(offer->description(), false); - SetRemoteDescriptionExpectError("Called with a SDP without crypto enabled", - offer); + SetRemoteDescriptionOfferExpectError(kSdpWithoutSdesCrypto, + offer); const webrtc::SessionDescriptionInterface* answer = CreateAnswer(NULL); // Answer should be NULL as no crypto params in offer. ASSERT_TRUE(answer == NULL); @@ -549,6 +576,35 @@ class WebRtcSessionTest : public testing::Test { } } + void ModifyIceUfragPwdLines(const SessionDescriptionInterface* current_desc, + const std::string& modified_ice_ufrag, + const std::string& modified_ice_pwd, + std::string* sdp) { + const cricket::SessionDescription* desc = current_desc->description(); + EXPECT_TRUE(current_desc->ToString(sdp)); + + const cricket::ContentInfos& contents = desc->contents(); + cricket::ContentInfos::const_iterator it = contents.begin(); + // Replace ufrag and pwd lines with |modified_ice_ufrag| and + // |modified_ice_pwd| strings. + for (; it != contents.end(); ++it) { + const cricket::TransportDescription* transport_desc = + desc->GetTransportDescriptionByName(it->name); + std::string ufrag_line = "a=ice-ufrag:" + transport_desc->ice_ufrag + + "\r\n"; + std::string pwd_line = "a=ice-pwd:" + transport_desc->ice_pwd + + "\r\n"; + std::string mod_ufrag = "a=ice-ufrag:" + modified_ice_ufrag + "\r\n"; + std::string mod_pwd = "a=ice-pwd:" + modified_ice_pwd + "\r\n"; + talk_base::replace_substrs(ufrag_line.c_str(), ufrag_line.length(), + mod_ufrag.c_str(), mod_ufrag.length(), + sdp); + talk_base::replace_substrs(pwd_line.c_str(), pwd_line.length(), + mod_pwd.c_str(), mod_pwd.length(), + sdp); + } + } + // Creates a remote offer and and applies it as a remote description, // creates a local answer and applies is as a local description. // Call mediastream_signaling_.UseOptionsWithStreamX() before this function @@ -567,13 +623,26 @@ class WebRtcSessionTest : public testing::Test { SetLocalDescriptionWithoutError(desc); EXPECT_EQ(expected_state, session_->state()); } - void SetLocalDescriptionExpectError(const std::string& expected_error, + void SetLocalDescriptionExpectError(const std::string& action, + const std::string& expected_error, SessionDescriptionInterface* desc) { std::string error; EXPECT_FALSE(session_->SetLocalDescription(desc, &error)); - EXPECT_NE(std::string::npos, error.find(kSetLocalSdpFailed)); + std::string sdp_type = "local "; + sdp_type.append(action); + EXPECT_NE(std::string::npos, error.find(sdp_type)); EXPECT_NE(std::string::npos, error.find(expected_error)); } + void SetLocalDescriptionOfferExpectError(const std::string& expected_error, + SessionDescriptionInterface* desc) { + SetLocalDescriptionExpectError(SessionDescriptionInterface::kOffer, + expected_error, desc); + } + void SetLocalDescriptionAnswerExpectError(const std::string& expected_error, + SessionDescriptionInterface* desc) { + SetLocalDescriptionExpectError(SessionDescriptionInterface::kAnswer, + expected_error, desc); + } void SetRemoteDescriptionWithoutError(SessionDescriptionInterface* desc) { EXPECT_TRUE(session_->SetRemoteDescription(desc, NULL)); } @@ -582,13 +651,31 @@ class WebRtcSessionTest : public testing::Test { SetRemoteDescriptionWithoutError(desc); EXPECT_EQ(expected_state, session_->state()); } - void SetRemoteDescriptionExpectError(const std::string& expected_error, + void SetRemoteDescriptionExpectError(const std::string& action, + const std::string& expected_error, SessionDescriptionInterface* desc) { std::string error; EXPECT_FALSE(session_->SetRemoteDescription(desc, &error)); - EXPECT_NE(std::string::npos, error.find(kSetRemoteSdpFailed)); + std::string sdp_type = "remote "; + sdp_type.append(action); + EXPECT_NE(std::string::npos, error.find(sdp_type)); EXPECT_NE(std::string::npos, error.find(expected_error)); } + void SetRemoteDescriptionOfferExpectError( + const std::string& expected_error, SessionDescriptionInterface* desc) { + SetRemoteDescriptionExpectError(SessionDescriptionInterface::kOffer, + expected_error, desc); + } + void SetRemoteDescriptionPranswerExpectError( + const std::string& expected_error, SessionDescriptionInterface* desc) { + SetRemoteDescriptionExpectError(SessionDescriptionInterface::kPrAnswer, + expected_error, desc); + } + void SetRemoteDescriptionAnswerExpectError( + const std::string& expected_error, SessionDescriptionInterface* desc) { + SetRemoteDescriptionExpectError(SessionDescriptionInterface::kAnswer, + expected_error, desc); + } void CreateCryptoOfferAndNonCryptoAnswer(SessionDescriptionInterface** offer, SessionDescriptionInterface** nocrypto_answer) { @@ -605,6 +692,28 @@ class WebRtcSessionTest : public testing::Test { EXPECT_TRUE(*nocrypto_answer != NULL); } + void CreateDtlsOfferAndNonDtlsAnswer(SessionDescriptionInterface** offer, + SessionDescriptionInterface** nodtls_answer) { + cricket::MediaSessionOptions options; + options.has_video = true; + options.bundle_enabled = true; + + talk_base::scoped_ptr<SessionDescriptionInterface> temp_offer( + CreateRemoteOffer(options, cricket::SEC_ENABLED)); + + *nodtls_answer = + CreateRemoteAnswer(temp_offer.get(), options, cricket::SEC_ENABLED); + EXPECT_TRUE(*nodtls_answer != NULL); + VerifyFingerprintStatus((*nodtls_answer)->description(), false); + VerifyCryptoParams((*nodtls_answer)->description()); + + SetFactoryDtlsSrtp(); + *offer = CreateRemoteOffer(options, cricket::SEC_ENABLED); + ASSERT_TRUE(*offer != NULL); + VerifyFingerprintStatus((*offer)->description(), true); + VerifyCryptoParams((*offer)->description()); + } + JsepSessionDescription* CreateRemoteOfferWithVersion( cricket::MediaSessionOptions options, cricket::SecurePolicy secure_policy, @@ -633,8 +742,9 @@ class WebRtcSessionTest : public testing::Test { kSessionVersion, NULL); } JsepSessionDescription* CreateRemoteOffer( - cricket::MediaSessionOptions options, cricket::SecurePolicy policy) { - return CreateRemoteOfferWithVersion(options, policy, kSessionVersion, NULL); + cricket::MediaSessionOptions options, cricket::SecurePolicy sdes_policy) { + return CreateRemoteOfferWithVersion( + options, sdes_policy, kSessionVersion, NULL); } JsepSessionDescription* CreateRemoteOffer( cricket::MediaSessionOptions options, @@ -777,13 +887,15 @@ class WebRtcSessionTest : public testing::Test { // The method sets up a call from the session to itself, in a loopback // arrangement. It also uses a firewall rule to create a temporary - // disconnection. This code is placed as a method so that it can be invoked + // disconnection, and then a permanent disconnection. + // This code is placed in a method so that it can be invoked // by multiple tests with different allocators (e.g. with and without BUNDLE). // While running the call, this method also checks if the session goes through // the correct sequence of ICE states when a connection is established, // broken, and re-established. // The Connection state should go: - // New -> Checking -> Connected -> Disconnected -> Connected. + // New -> Checking -> (Connected) -> Completed -> Disconnected -> Completed + // -> Failed. // The Gathering state should go: New -> Gathering -> Completed. void TestLoopbackCall() { AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); @@ -814,10 +926,11 @@ class WebRtcSessionTest : public testing::Test { EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking, observer_.ice_connection_state_, kIceCandidatesTimeout); - EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected, + + // The ice connection state is "Connected" too briefly to catch in a test. + EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted, observer_.ice_connection_state_, kIceCandidatesTimeout); - // TODO(bemasc): EXPECT(Completed) once the details are standardized. // Adding firewall rule to block ping requests, which should cause // transport channel failure. @@ -834,10 +947,21 @@ class WebRtcSessionTest : public testing::Test { // Session is automatically calling OnSignalingReady after creation of // new portallocator session which will allocate new set of candidates. - // TODO(bemasc): Change this to Completed once the details are standardized. - EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected, + EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted, observer_.ice_connection_state_, kIceCandidatesTimeout); + + // Now we block ping requests and wait until the ICE connection transitions + // to the Failed state. This will take at least 30 seconds because it must + // wait for the Port to timeout. + int port_timeout = 30000; + fss_->AddRule(false, + talk_base::FP_ANY, + talk_base::FD_ANY, + talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); + EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed, + observer_.ice_connection_state_, + kIceCandidatesTimeout + port_timeout); } void VerifyTransportType(const std::string& content_name, @@ -874,7 +998,7 @@ class WebRtcSessionTest : public testing::Test { } void SetLocalDescriptionWithDataChannel() { - webrtc::DataChannelInit dci; + webrtc::InternalDataChannelInit dci; dci.reliable = false; session_->CreateDataChannel("datachannel", &dci); SessionDescriptionInterface* offer = CreateOffer(NULL); @@ -884,11 +1008,11 @@ class WebRtcSessionTest : public testing::Test { void VerifyMultipleAsyncCreateDescription( bool success, CreateSessionDescriptionRequest::Type type) { InitWithDtls(!success); - + SetFactoryDtlsSrtp(); if (type == CreateSessionDescriptionRequest::kAnswer) { cricket::MediaSessionOptions options; scoped_ptr<JsepSessionDescription> offer( - CreateRemoteOffer(options, cricket::SEC_REQUIRED)); + CreateRemoteOffer(options, cricket::SEC_DISABLED)); ASSERT_TRUE(offer.get() != NULL); SetRemoteDescriptionWithoutError(offer.release()); } @@ -932,8 +1056,9 @@ class WebRtcSessionTest : public testing::Test { talk_base::SocketServerScope ss_scope_; talk_base::SocketAddress stun_socket_addr_; cricket::TestStunServer stun_server_; + cricket::TestTurnServer turn_server_; talk_base::FakeNetworkManager network_manager_; - cricket::BasicPortAllocator allocator_; + talk_base::scoped_ptr<cricket::BasicPortAllocator> allocator_; PeerConnectionFactoryInterface::Options options_; talk_base::scoped_ptr<FakeConstraints> constraints_; FakeMediaStreamSignaling mediastream_signaling_; @@ -941,20 +1066,19 @@ class WebRtcSessionTest : public testing::Test { MockIceObserver observer_; cricket::FakeVideoMediaChannel* video_channel_; cricket::FakeVoiceMediaChannel* voice_channel_; + PeerConnectionInterface::IceTransportsType ice_type_; }; -TEST_F(WebRtcSessionTest, TestInitialize) { - Init(NULL); -} - TEST_F(WebRtcSessionTest, TestInitializeWithDtls) { InitWithDtls(); + // SDES is disabled when DTLS is on. + EXPECT_EQ(cricket::SEC_DISABLED, session_->SdesPolicy()); } -// Verifies that WebRtcSession uses SEC_REQUIRED by default. -TEST_F(WebRtcSessionTest, TestDefaultSetSecurePolicy) { +TEST_F(WebRtcSessionTest, TestInitializeWithoutDtls) { Init(NULL); - EXPECT_EQ(cricket::SEC_REQUIRED, session_->SecurePolicy()); + // SDES is required if DTLS is off. + EXPECT_EQ(cricket::SEC_REQUIRED, session_->SdesPolicy()); } TEST_F(WebRtcSessionTest, TestSessionCandidates) { @@ -998,9 +1122,18 @@ TEST_F(WebRtcSessionTest, TestStunError) { EXPECT_EQ(6u, observer_.mline_1_candidates_.size()); } +TEST_F(WebRtcSessionTest, SetSdpFailedOnInvalidSdp) { + Init(NULL); + SessionDescriptionInterface* offer = NULL; + // Since |offer| is NULL, there's no way to tell if it's an offer or answer. + std::string unknown_action; + SetLocalDescriptionExpectError(unknown_action, kInvalidSdp, offer); + SetRemoteDescriptionExpectError(unknown_action, kInvalidSdp, offer); +} + // Test creating offers and receive answers and make sure the // media engine creates the expected send and receive streams. -TEST_F(WebRtcSessionTest, TestCreateOfferReceiveAnswer) { +TEST_F(WebRtcSessionTest, TestCreateSdesOfferReceiveSdesAnswer) { Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); SessionDescriptionInterface* offer = CreateOffer(NULL); @@ -1055,14 +1188,16 @@ TEST_F(WebRtcSessionTest, TestCreateOfferReceiveAnswer) { // Test receiving offers and creating answers and make sure the // media engine creates the expected send and receive streams. -TEST_F(WebRtcSessionTest, TestReceiveOfferCreateAnswer) { +TEST_F(WebRtcSessionTest, TestReceiveSdesOfferCreateSdesAnswer) { Init(NULL); mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* offer = CreateOffer(NULL); + VerifyCryptoParams(offer->description()); SetRemoteDescriptionWithoutError(offer); mediastream_signaling_.SendAudioVideoStream1(); SessionDescriptionInterface* answer = CreateAnswer(NULL); + VerifyCryptoParams(answer->description()); SetLocalDescriptionWithoutError(answer); const std::string session_id_orig = answer->session_id(); @@ -1109,9 +1244,53 @@ TEST_F(WebRtcSessionTest, TestReceiveOfferCreateAnswer) { EXPECT_EQ(0u, voice_channel_->send_streams().size()); } -// Test we will return fail when apply an offer that doesn't have -// crypto enabled. -TEST_F(WebRtcSessionTest, SetNonCryptoOffer) { +TEST_F(WebRtcSessionTest, SetLocalSdpFailedOnCreateChannel) { + Init(NULL); + media_engine_->set_fail_create_channel(true); + + SessionDescriptionInterface* offer = CreateOffer(NULL); + ASSERT_TRUE(offer != NULL); + // SetRemoteDescription and SetLocalDescription will take the ownership of + // the offer. + SetRemoteDescriptionOfferExpectError(kCreateChannelFailed, offer); + offer = CreateOffer(NULL); + ASSERT_TRUE(offer != NULL); + SetLocalDescriptionOfferExpectError(kCreateChannelFailed, offer); +} + +// +// Tests for creating/setting SDP under different SDES/DTLS polices: +// +// --DTLS off and SDES on +// TestCreateSdesOfferReceiveSdesAnswer/TestReceiveSdesOfferCreateSdesAnswer: +// set local/remote offer/answer with crypto --> success +// TestSetNonSdesOfferWhenSdesOn: set local/remote offer without crypto ---> +// failure +// TestSetLocalNonSdesAnswerWhenSdesOn: set local answer without crypto --> +// failure +// TestSetRemoteNonSdesAnswerWhenSdesOn: set remote answer without crypto --> +// failure +// +// --DTLS on and SDES off +// TestCreateDtlsOfferReceiveDtlsAnswer/TestReceiveDtlsOfferCreateDtlsAnswer: +// set local/remote offer/answer with DTLS fingerprint --> success +// TestReceiveNonDtlsOfferWhenDtlsOn: set local/remote offer without DTLS +// fingerprint --> failure +// TestSetLocalNonDtlsAnswerWhenDtlsOn: set local answer without fingerprint +// --> failure +// TestSetRemoteNonDtlsAnswerWhenDtlsOn: set remote answer without fingerprint +// --> failure +// +// --Encryption disabled: DTLS off and SDES off +// TestCreateOfferReceiveAnswerWithoutEncryption: set local offer and remote +// answer without SDES or DTLS --> success +// TestCreateAnswerReceiveOfferWithoutEncryption: set remote offer and local +// answer without SDES or DTLS --> success +// + +// Test that we return a failure when applying a remote/local offer that doesn't +// have cryptos enabled when DTLS is off. +TEST_F(WebRtcSessionTest, TestSetNonSdesOfferWhenSdesOn) { Init(NULL); cricket::MediaSessionOptions options; options.has_video = true; @@ -1121,15 +1300,15 @@ TEST_F(WebRtcSessionTest, SetNonCryptoOffer) { VerifyNoCryptoParams(offer->description(), false); // SetRemoteDescription and SetLocalDescription will take the ownership of // the offer. - SetRemoteDescriptionExpectError(kSdpWithoutCrypto, offer); + SetRemoteDescriptionOfferExpectError(kSdpWithoutSdesCrypto, offer); offer = CreateRemoteOffer(options, cricket::SEC_DISABLED); ASSERT_TRUE(offer != NULL); - SetLocalDescriptionExpectError(kSdpWithoutCrypto, offer); + SetLocalDescriptionOfferExpectError(kSdpWithoutSdesCrypto, offer); } -// Test we will return fail when apply an answer that doesn't have -// crypto enabled. -TEST_F(WebRtcSessionTest, SetLocalNonCryptoAnswer) { +// Test that we return a failure when applying a local answer that doesn't have +// cryptos enabled when DTLS is off. +TEST_F(WebRtcSessionTest, TestSetLocalNonSdesAnswerWhenSdesOn) { Init(NULL); SessionDescriptionInterface* offer = NULL; SessionDescriptionInterface* answer = NULL; @@ -1137,12 +1316,12 @@ TEST_F(WebRtcSessionTest, SetLocalNonCryptoAnswer) { // SetRemoteDescription and SetLocalDescription will take the ownership of // the offer. SetRemoteDescriptionWithoutError(offer); - SetLocalDescriptionExpectError(kSdpWithoutCrypto, answer); + SetLocalDescriptionAnswerExpectError(kSdpWithoutSdesCrypto, answer); } -// Test we will return fail when apply an answer that doesn't have -// crypto enabled. -TEST_F(WebRtcSessionTest, SetRemoteNonCryptoAnswer) { +// Test we will return fail when apply an remote answer that doesn't have +// crypto enabled when DTLS is off. +TEST_F(WebRtcSessionTest, TestSetRemoteNonSdesAnswerWhenSdesOn) { Init(NULL); SessionDescriptionInterface* offer = NULL; SessionDescriptionInterface* answer = NULL; @@ -1150,32 +1329,23 @@ TEST_F(WebRtcSessionTest, SetRemoteNonCryptoAnswer) { // SetRemoteDescription and SetLocalDescription will take the ownership of // the offer. SetLocalDescriptionWithoutError(offer); - SetRemoteDescriptionExpectError(kSdpWithoutCrypto, answer); + SetRemoteDescriptionAnswerExpectError(kSdpWithoutSdesCrypto, answer); } -// Test that we can create and set an offer with a DTLS fingerprint. -TEST_F(WebRtcSessionTest, CreateSetDtlsOffer) { +// Test that we accept an offer with a DTLS fingerprint when DTLS is on +// and that we return an answer with a DTLS fingerprint. +TEST_F(WebRtcSessionTest, TestReceiveDtlsOfferCreateDtlsAnswer) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - InitWithDtls(); mediastream_signaling_.SendAudioVideoStream1(); - SessionDescriptionInterface* offer = CreateOffer(NULL); - ASSERT_TRUE(offer != NULL); - VerifyFingerprintStatus(offer->description(), true); - // SetLocalDescription will take the ownership of the offer. - SetLocalDescriptionWithoutError(offer); -} - -// Test that we can process an offer with a DTLS fingerprint -// and that we return an answer with a fingerprint. -TEST_F(WebRtcSessionTest, ReceiveDtlsOfferCreateAnswer) { - MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); InitWithDtls(); SetFactoryDtlsSrtp(); cricket::MediaSessionOptions options; options.has_video = true; - JsepSessionDescription* offer = CreateRemoteOffer(options); + JsepSessionDescription* offer = + CreateRemoteOffer(options, cricket::SEC_DISABLED); ASSERT_TRUE(offer != NULL); VerifyFingerprintStatus(offer->description(), true); + VerifyNoCryptoParams(offer->description(), true); // SetRemoteDescription will take the ownership of the offer. SetRemoteDescriptionWithoutError(offer); @@ -1191,28 +1361,148 @@ TEST_F(WebRtcSessionTest, ReceiveDtlsOfferCreateAnswer) { SetLocalDescriptionWithoutError(answer); } -// Test that even if we support DTLS, if the other side didn't offer a -// fingerprint, we don't either. -TEST_F(WebRtcSessionTest, ReceiveNoDtlsOfferCreateAnswer) { +// Test that we set a local offer with a DTLS fingerprint when DTLS is on +// and then we accept a remote answer with a DTLS fingerprint successfully. +TEST_F(WebRtcSessionTest, TestCreateDtlsOfferReceiveDtlsAnswer) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + mediastream_signaling_.SendAudioVideoStream1(); InitWithDtls(); + SetFactoryDtlsSrtp(); + + // Verify that we get a crypto fingerprint in the answer. + SessionDescriptionInterface* offer = CreateOffer(NULL); + ASSERT_TRUE(offer != NULL); + VerifyFingerprintStatus(offer->description(), true); + // Check that we don't have an a=crypto line in the offer. + VerifyNoCryptoParams(offer->description(), true); + + // Now set the local description, which should work, even without a=crypto. + SetLocalDescriptionWithoutError(offer); + cricket::MediaSessionOptions options; options.has_video = true; + JsepSessionDescription* answer = + CreateRemoteAnswer(offer, options, cricket::SEC_DISABLED); + ASSERT_TRUE(answer != NULL); + VerifyFingerprintStatus(answer->description(), true); + VerifyNoCryptoParams(answer->description(), true); + + // SetRemoteDescription will take the ownership of the answer. + SetRemoteDescriptionWithoutError(answer); +} + +// Test that if we support DTLS and the other side didn't offer a fingerprint, +// we will fail to set the remote description. +TEST_F(WebRtcSessionTest, TestReceiveNonDtlsOfferWhenDtlsOn) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + InitWithDtls(); + cricket::MediaSessionOptions options; + options.has_video = true; + options.bundle_enabled = true; JsepSessionDescription* offer = CreateRemoteOffer( options, cricket::SEC_REQUIRED); ASSERT_TRUE(offer != NULL); VerifyFingerprintStatus(offer->description(), false); + VerifyCryptoParams(offer->description()); - // SetRemoteDescription will take the ownership of - // the offer. + // SetRemoteDescription will take the ownership of the offer. + SetRemoteDescriptionOfferExpectError( + kSdpWithoutDtlsFingerprint, offer); + + offer = CreateRemoteOffer(options, cricket::SEC_REQUIRED); + // SetLocalDescription will take the ownership of the offer. + SetLocalDescriptionOfferExpectError( + kSdpWithoutDtlsFingerprint, offer); +} + +// Test that we return a failure when applying a local answer that doesn't have +// a DTLS fingerprint when DTLS is required. +TEST_F(WebRtcSessionTest, TestSetLocalNonDtlsAnswerWhenDtlsOn) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + InitWithDtls(); + SessionDescriptionInterface* offer = NULL; + SessionDescriptionInterface* answer = NULL; + CreateDtlsOfferAndNonDtlsAnswer(&offer, &answer); + + // SetRemoteDescription and SetLocalDescription will take the ownership of + // the offer and answer. SetRemoteDescriptionWithoutError(offer); + SetLocalDescriptionAnswerExpectError( + kSdpWithoutDtlsFingerprint, answer); +} + +// Test that we return a failure when applying a remote answer that doesn't have +// a DTLS fingerprint when DTLS is required. +TEST_F(WebRtcSessionTest, TestSetRemoteNonDtlsAnswerWhenDtlsOn) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + InitWithDtls(); + SessionDescriptionInterface* offer = CreateOffer(NULL); + cricket::MediaSessionOptions options; + options.has_video = true; + JsepSessionDescription* answer = + CreateRemoteAnswer(offer, options, cricket::SEC_ENABLED); - // Verify that we don't get a crypto fingerprint in the answer. + // SetRemoteDescription and SetLocalDescription will take the ownership of + // the offer and answer. + SetLocalDescriptionWithoutError(offer); + SetRemoteDescriptionAnswerExpectError( + kSdpWithoutDtlsFingerprint, answer); +} + +// Test that we create a local offer without SDES or DTLS and accept a remote +// answer without SDES or DTLS when encryption is disabled. +TEST_F(WebRtcSessionTest, TestCreateOfferReceiveAnswerWithoutEncryption) { + mediastream_signaling_.SendAudioVideoStream1(); + options_.disable_encryption = true; + InitWithDtls(); + + // Verify that we get a crypto fingerprint in the answer. + SessionDescriptionInterface* offer = CreateOffer(NULL); + ASSERT_TRUE(offer != NULL); + VerifyFingerprintStatus(offer->description(), false); + // Check that we don't have an a=crypto line in the offer. + VerifyNoCryptoParams(offer->description(), false); + + // Now set the local description, which should work, even without a=crypto. + SetLocalDescriptionWithoutError(offer); + + cricket::MediaSessionOptions options; + options.has_video = true; + JsepSessionDescription* answer = + CreateRemoteAnswer(offer, options, cricket::SEC_DISABLED); + ASSERT_TRUE(answer != NULL); + VerifyFingerprintStatus(answer->description(), false); + VerifyNoCryptoParams(answer->description(), false); + + // SetRemoteDescription will take the ownership of the answer. + SetRemoteDescriptionWithoutError(answer); +} + +// Test that we create a local answer without SDES or DTLS and accept a remote +// offer without SDES or DTLS when encryption is disabled. +TEST_F(WebRtcSessionTest, TestCreateAnswerReceiveOfferWithoutEncryption) { + options_.disable_encryption = true; + InitWithDtls(); + + cricket::MediaSessionOptions options; + options.has_video = true; + JsepSessionDescription* offer = + CreateRemoteOffer(options, cricket::SEC_DISABLED); + ASSERT_TRUE(offer != NULL); + VerifyFingerprintStatus(offer->description(), false); + VerifyNoCryptoParams(offer->description(), false); + + // SetRemoteDescription will take the ownership of the offer. + SetRemoteDescriptionWithoutError(offer); + + // Verify that we get a crypto fingerprint in the answer. SessionDescriptionInterface* answer = CreateAnswer(NULL); ASSERT_TRUE(answer != NULL); VerifyFingerprintStatus(answer->description(), false); + // Check that we don't have an a=crypto line in the answer. + VerifyNoCryptoParams(answer->description(), false); - // Now set the local description. + // Now set the local description, which should work, even without a=crypto. SetLocalDescriptionWithoutError(answer); } @@ -1245,9 +1535,8 @@ TEST_F(WebRtcSessionTest, TestSetLocalAndRemoteOffer) { SessionDescriptionInterface* offer = CreateOffer(NULL); SetLocalDescriptionWithoutError(offer); offer = CreateOffer(NULL); - SetRemoteDescriptionExpectError( - "Called with type in wrong state, type: offer state: STATE_SENTINITIATE", - offer); + SetRemoteDescriptionOfferExpectError( + "Called in wrong state: STATE_SENTINITIATE", offer); } TEST_F(WebRtcSessionTest, TestSetRemoteAndLocalOffer) { @@ -1256,10 +1545,8 @@ TEST_F(WebRtcSessionTest, TestSetRemoteAndLocalOffer) { SessionDescriptionInterface* offer = CreateOffer(NULL); SetRemoteDescriptionWithoutError(offer); offer = CreateOffer(NULL); - SetLocalDescriptionExpectError( - "Called with type in wrong state, type: " - "offer state: STATE_RECEIVEDINITIATE", - offer); + SetLocalDescriptionOfferExpectError( + "Called in wrong state: STATE_RECEIVEDINITIATE", offer); } TEST_F(WebRtcSessionTest, TestSetLocalPrAnswer) { @@ -1319,9 +1606,8 @@ TEST_F(WebRtcSessionTest, TestSetLocalAnswerWithoutOffer) { CreateOffer(NULL)); SessionDescriptionInterface* answer = CreateRemoteAnswer(offer.get()); - SetLocalDescriptionExpectError( - "Called with type in wrong state, type: answer state: STATE_INIT", - answer); + SetLocalDescriptionAnswerExpectError("Called in wrong state: STATE_INIT", + answer); } TEST_F(WebRtcSessionTest, TestSetRemoteAnswerWithoutOffer) { @@ -1331,9 +1617,8 @@ TEST_F(WebRtcSessionTest, TestSetRemoteAnswerWithoutOffer) { CreateOffer(NULL)); SessionDescriptionInterface* answer = CreateRemoteAnswer(offer.get()); - SetRemoteDescriptionExpectError( - "Called with type in wrong state, type: answer state: STATE_INIT", - answer); + SetRemoteDescriptionAnswerExpectError( + "Called in wrong state: STATE_INIT", answer); } TEST_F(WebRtcSessionTest, TestAddRemoteCandidate) { @@ -1372,10 +1657,12 @@ TEST_F(WebRtcSessionTest, TestAddRemoteCandidate) { EXPECT_EQ(1, candidates->at(0)->candidate().component()); EXPECT_EQ(2, candidates->at(1)->candidate().component()); + // |ice_candidate3| is identical to |ice_candidate2|. It can be added + // successfully, but the total count of candidates will not increase. candidate.set_component(2); JsepIceCandidate ice_candidate3(kMediaContentName0, 0, candidate); EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate3)); - ASSERT_EQ(3u, candidates->count()); + ASSERT_EQ(2u, candidates->count()); JsepIceCandidate bad_ice_candidate("bad content name", 99, candidate); EXPECT_FALSE(session_->ProcessIceMessage(&bad_ice_candidate)); @@ -1979,7 +2266,7 @@ TEST_F(WebRtcSessionTest, TestSetLocalDescriptionWithoutIce) { RemoveIceUfragPwdLines(offer.get(), &sdp); SessionDescriptionInterface* modified_offer = CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL); - SetLocalDescriptionExpectError(kSdpWithoutIceUfragPwd, modified_offer); + SetLocalDescriptionOfferExpectError(kSdpWithoutIceUfragPwd, modified_offer); } // This test verifies that setRemoteDescription fails if @@ -1991,7 +2278,55 @@ TEST_F(WebRtcSessionTest, TestSetRemoteDescriptionWithoutIce) { RemoveIceUfragPwdLines(offer.get(), &sdp); SessionDescriptionInterface* modified_offer = CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL); - SetRemoteDescriptionExpectError(kSdpWithoutIceUfragPwd, modified_offer); + SetRemoteDescriptionOfferExpectError(kSdpWithoutIceUfragPwd, modified_offer); +} + +// This test verifies that setLocalDescription fails if local offer has +// too short ice ufrag and pwd strings. +TEST_F(WebRtcSessionTest, TestSetLocalDescriptionInvalidIceCredentials) { + Init(NULL); + tdesc_factory_->set_protocol(cricket::ICEPROTO_RFC5245); + mediastream_signaling_.SendAudioVideoStream1(); + talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL)); + std::string sdp; + // Modifying ice ufrag and pwd in local offer with strings smaller than the + // recommended values of 4 and 22 bytes respectively. + ModifyIceUfragPwdLines(offer.get(), "ice", "icepwd", &sdp); + SessionDescriptionInterface* modified_offer = + CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL); + std::string error; + EXPECT_FALSE(session_->SetLocalDescription(modified_offer, &error)); + + // Test with string greater than 256. + sdp.clear(); + ModifyIceUfragPwdLines(offer.get(), kTooLongIceUfragPwd, kTooLongIceUfragPwd, + &sdp); + modified_offer = CreateSessionDescription(JsepSessionDescription::kOffer, sdp, + NULL); + EXPECT_FALSE(session_->SetLocalDescription(modified_offer, &error)); +} + +// This test verifies that setRemoteDescription fails if remote offer has +// too short ice ufrag and pwd strings. +TEST_F(WebRtcSessionTest, TestSetRemoteDescriptionInvalidIceCredentials) { + Init(NULL); + tdesc_factory_->set_protocol(cricket::ICEPROTO_RFC5245); + talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateRemoteOffer()); + std::string sdp; + // Modifying ice ufrag and pwd in remote offer with strings smaller than the + // recommended values of 4 and 22 bytes respectively. + ModifyIceUfragPwdLines(offer.get(), "ice", "icepwd", &sdp); + SessionDescriptionInterface* modified_offer = + CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL); + std::string error; + EXPECT_FALSE(session_->SetRemoteDescription(modified_offer, &error)); + + sdp.clear(); + ModifyIceUfragPwdLines(offer.get(), kTooLongIceUfragPwd, kTooLongIceUfragPwd, + &sdp); + modified_offer = CreateSessionDescription(JsepSessionDescription::kOffer, sdp, + NULL); + EXPECT_FALSE(session_->SetRemoteDescription(modified_offer, &error)); } TEST_F(WebRtcSessionTest, VerifyBundleFlagInPA) { @@ -1999,8 +2334,8 @@ TEST_F(WebRtcSessionTest, VerifyBundleFlagInPA) { // local description is removed by the application, BUNDLE flag should be // disabled in PortAllocator. By default BUNDLE is enabled in the WebRtc. Init(NULL); - EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & allocator_.flags()) == - cricket::PORTALLOCATOR_ENABLE_BUNDLE); + EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & + allocator_->flags()) == cricket::PORTALLOCATOR_ENABLE_BUNDLE); talk_base::scoped_ptr<SessionDescriptionInterface> offer( CreateOffer(NULL)); cricket::SessionDescription* offer_copy = @@ -2011,14 +2346,14 @@ TEST_F(WebRtcSessionTest, VerifyBundleFlagInPA) { modified_offer->Initialize(offer_copy, "1", "1"); SetLocalDescriptionWithoutError(modified_offer); - EXPECT_FALSE(allocator_.flags() & cricket::PORTALLOCATOR_ENABLE_BUNDLE); + EXPECT_FALSE(allocator_->flags() & cricket::PORTALLOCATOR_ENABLE_BUNDLE); } TEST_F(WebRtcSessionTest, TestDisabledBundleInAnswer) { Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); - EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & allocator_.flags()) == - cricket::PORTALLOCATOR_ENABLE_BUNDLE); + EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & + allocator_->flags()) == cricket::PORTALLOCATOR_ENABLE_BUNDLE); FakeConstraints constraints; constraints.SetMandatoryUseRtpMux(true); SessionDescriptionInterface* offer = CreateOffer(&constraints); @@ -2032,8 +2367,8 @@ TEST_F(WebRtcSessionTest, TestDisabledBundleInAnswer) { new JsepSessionDescription(JsepSessionDescription::kAnswer); modified_answer->Initialize(answer_copy, "1", "1"); SetRemoteDescriptionWithoutError(modified_answer); - EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & allocator_.flags()) == - cricket::PORTALLOCATOR_ENABLE_BUNDLE); + EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & + allocator_->flags()) == cricket::PORTALLOCATOR_ENABLE_BUNDLE); video_channel_ = media_engine_->GetVideoChannel(0); voice_channel_ = media_engine_->GetVoiceChannel(0); @@ -2055,8 +2390,8 @@ TEST_F(WebRtcSessionTest, TestDisabledBundleInAnswer) { TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) { WebRtcSessionTest::Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); - EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & allocator_.flags()) == - cricket::PORTALLOCATOR_ENABLE_BUNDLE); + EXPECT_TRUE((cricket::PORTALLOCATOR_ENABLE_BUNDLE & + allocator_->flags()) == cricket::PORTALLOCATOR_ENABLE_BUNDLE); FakeConstraints constraints; constraints.SetMandatoryUseRtpMux(true); SessionDescriptionInterface* offer = CreateOffer(&constraints); @@ -2071,11 +2406,11 @@ TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) { JsepSessionDescription *local_offer = new JsepSessionDescription(JsepSessionDescription::kOffer); EXPECT_TRUE((local_offer)->Initialize(offer_str, NULL)); - SetLocalDescriptionExpectError(kBundleWithoutRtcpMux, local_offer); + SetLocalDescriptionOfferExpectError(kBundleWithoutRtcpMux, local_offer); JsepSessionDescription *remote_offer = new JsepSessionDescription(JsepSessionDescription::kOffer); EXPECT_TRUE((remote_offer)->Initialize(offer_str, NULL)); - SetRemoteDescriptionExpectError(kBundleWithoutRtcpMux, remote_offer); + SetRemoteDescriptionOfferExpectError(kBundleWithoutRtcpMux, remote_offer); // Trying unmodified SDP. SetLocalDescriptionWithoutError(offer); } @@ -2123,13 +2458,39 @@ TEST_F(WebRtcSessionTest, SetAudioSend) { EXPECT_TRUE(channel->IsStreamMuted(send_ssrc)); EXPECT_FALSE(channel->options().echo_cancellation.IsSet()); EXPECT_EQ(0, renderer->channel_id()); + EXPECT_TRUE(renderer->sink() != NULL); + // This will trigger SetSink(NULL) to the |renderer|. session_->SetAudioSend(send_ssrc, true, options, NULL); EXPECT_FALSE(channel->IsStreamMuted(send_ssrc)); bool value; EXPECT_TRUE(channel->options().echo_cancellation.Get(&value)); EXPECT_TRUE(value); EXPECT_EQ(-1, renderer->channel_id()); + EXPECT_TRUE(renderer->sink() == NULL); +} + +TEST_F(WebRtcSessionTest, AudioRendererForLocalStream) { + Init(NULL); + mediastream_signaling_.SendAudioVideoStream1(); + CreateAndSetRemoteOfferAndLocalAnswer(); + cricket::FakeVoiceMediaChannel* channel = media_engine_->GetVoiceChannel(0); + ASSERT_TRUE(channel != NULL); + ASSERT_EQ(1u, channel->send_streams().size()); + uint32 send_ssrc = channel->send_streams()[0].first_ssrc(); + + talk_base::scoped_ptr<FakeAudioRenderer> renderer(new FakeAudioRenderer()); + cricket::AudioOptions options; + session_->SetAudioSend(send_ssrc, true, options, renderer.get()); + EXPECT_TRUE(renderer->sink() != NULL); + + // Delete the |renderer| and it will trigger OnClose() to the sink, and this + // will invalidate the |renderer_| pointer in the sink and prevent getting a + // SetSink(NULL) callback afterwards. + renderer.reset(); + + // This will trigger SetSink(NULL) if no OnClose() callback. + session_->SetAudioSend(send_ssrc, true, options, NULL); } TEST_F(WebRtcSessionTest, SetVideoPlayout) { @@ -2319,13 +2680,13 @@ TEST_F(WebRtcSessionTest, TestIceOfferGIceOnlyAnswer) { SessionDescriptionInterface* pranswer_with_gice = CreateSessionDescription(JsepSessionDescription::kPrAnswer, original_offer_sdp, NULL); - SetRemoteDescriptionExpectError(kPushDownPranswerTDFailed, - pranswer_with_gice); + SetRemoteDescriptionPranswerExpectError(kPushDownTDFailed, + pranswer_with_gice); SessionDescriptionInterface* answer_with_gice = CreateSessionDescription(JsepSessionDescription::kAnswer, original_offer_sdp, NULL); - SetRemoteDescriptionExpectError(kPushDownAnswerTDFailed, - answer_with_gice); + SetRemoteDescriptionAnswerExpectError(kPushDownTDFailed, + answer_with_gice); } // Verifing local offer and remote answer have matching m-lines as per RFC 3264. @@ -2345,23 +2706,32 @@ TEST_F(WebRtcSessionTest, TestIncorrectMLinesInRemoteAnswer) { EXPECT_TRUE(modified_answer->Initialize(answer_copy, answer->session_id(), answer->session_version())); - SetRemoteDescriptionExpectError(kMlineMismatch, modified_answer); + SetRemoteDescriptionAnswerExpectError(kMlineMismatch, modified_answer); - // Modifying content names. + // Different content names. std::string sdp; EXPECT_TRUE(answer->ToString(&sdp)); const std::string kAudioMid = "a=mid:audio"; const std::string kAudioMidReplaceStr = "a=mid:audio_content_name"; - - // Replacing |audio| with |audio_content_name|. talk_base::replace_substrs(kAudioMid.c_str(), kAudioMid.length(), kAudioMidReplaceStr.c_str(), kAudioMidReplaceStr.length(), &sdp); - SessionDescriptionInterface* modified_answer1 = CreateSessionDescription(JsepSessionDescription::kAnswer, sdp, NULL); - SetRemoteDescriptionExpectError(kMlineMismatch, modified_answer1); + SetRemoteDescriptionAnswerExpectError(kMlineMismatch, modified_answer1); + + // Different media types. + EXPECT_TRUE(answer->ToString(&sdp)); + const std::string kAudioMline = "m=audio"; + const std::string kAudioMlineReplaceStr = "m=video"; + talk_base::replace_substrs(kAudioMline.c_str(), kAudioMline.length(), + kAudioMlineReplaceStr.c_str(), + kAudioMlineReplaceStr.length(), + &sdp); + SessionDescriptionInterface* modified_answer2 = + CreateSessionDescription(JsepSessionDescription::kAnswer, sdp, NULL); + SetRemoteDescriptionAnswerExpectError(kMlineMismatch, modified_answer2); SetRemoteDescriptionWithoutError(answer.release()); } @@ -2383,7 +2753,7 @@ TEST_F(WebRtcSessionTest, TestIncorrectMLinesInLocalAnswer) { EXPECT_TRUE(modified_answer->Initialize(answer_copy, answer->session_id(), answer->session_version())); - SetLocalDescriptionExpectError(kMlineMismatch, modified_answer); + SetLocalDescriptionAnswerExpectError(kMlineMismatch, modified_answer); SetLocalDescriptionWithoutError(answer); } @@ -2537,33 +2907,49 @@ TEST_F(WebRtcSessionTest, TestSessionContentError) { mediastream_signaling_.SendAudioVideoStream2(); SessionDescriptionInterface* answer = CreateRemoteAnswer(session_->local_description()); - SetRemoteDescriptionExpectError("ERROR_CONTENT", answer); + SetRemoteDescriptionAnswerExpectError("ERROR_CONTENT", answer); } // Runs the loopback call test with BUNDLE and STUN disabled. TEST_F(WebRtcSessionTest, TestIceStatesBasic) { // Lets try with only UDP ports. - allocator_.set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | + allocator_->set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | cricket::PORTALLOCATOR_DISABLE_TCP | cricket::PORTALLOCATOR_DISABLE_STUN | cricket::PORTALLOCATOR_DISABLE_RELAY); TestLoopbackCall(); } -// Regression-test for a crash which should have been an error. -TEST_F(WebRtcSessionTest, TestNoStateTransitionPendingError) { +// Runs the loopback call test with BUNDLE and STUN enabled. +TEST_F(WebRtcSessionTest, TestIceStatesBundle) { + allocator_->set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | + cricket::PORTALLOCATOR_ENABLE_BUNDLE | + cricket::PORTALLOCATOR_DISABLE_TCP | + cricket::PORTALLOCATOR_DISABLE_RELAY); + TestLoopbackCall(); +} + +TEST_F(WebRtcSessionTest, SetSdpFailedOnSessionError) { Init(NULL); cricket::MediaSessionOptions options; options.has_audio = true; options.has_video = true; - session_->SetError(cricket::BaseSession::ERROR_CONTENT); + cricket::BaseSession::Error error_code = cricket::BaseSession::ERROR_CONTENT; + std::string error_code_str = "ERROR_CONTENT"; + std::string error_desc = "Fake session error description."; + session_->SetError(error_code, error_desc); + SessionDescriptionInterface* offer = CreateRemoteOffer(options); SessionDescriptionInterface* answer = CreateRemoteAnswer(offer, options); - SetRemoteDescriptionExpectError(kSessionError, offer); - SetLocalDescriptionExpectError(kSessionError, answer); - // Not crashing is our success. + + std::string action; + std::ostringstream session_error_msg; + session_error_msg << kSessionError << error_code_str << ". "; + session_error_msg << kSessionErrorDesc << error_desc << "."; + SetRemoteDescriptionExpectError(action, session_error_msg.str(), offer); + SetLocalDescriptionExpectError(action, session_error_msg.str(), answer); } TEST_F(WebRtcSessionTest, TestRtpDataChannel) { @@ -2584,7 +2970,7 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { webrtc::MediaConstraintsInterface::kEnableRtpDataChannels, true); options_.disable_sctp_data_channels = false; - InitWithDtls(false); + InitWithDtls(); SetLocalDescriptionWithDataChannel(); EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type()); @@ -2593,7 +2979,7 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { TEST_F(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - InitWithDtls(false); + InitWithDtls(); talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL)); EXPECT_TRUE(offer->description()->GetContentByName("data") == NULL); @@ -2603,13 +2989,13 @@ TEST_F(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) { TEST_F(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); SetFactoryDtlsSrtp(); - InitWithDtls(false); + InitWithDtls(); // Create remote offer with SCTP. cricket::MediaSessionOptions options; options.data_channel_type = cricket::DCT_SCTP; JsepSessionDescription* offer = - CreateRemoteOffer(options, cricket::SEC_ENABLED); + CreateRemoteOffer(options, cricket::SEC_DISABLED); SetRemoteDescriptionWithoutError(offer); // Verifies the answer contains SCTP. @@ -2623,7 +3009,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) { constraints_.reset(new FakeConstraints()); constraints_->AddOptional( webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, false); - InitWithDtls(false); + InitWithDtls(); SetLocalDescriptionWithDataChannel(); EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type()); @@ -2632,7 +3018,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) { TEST_F(WebRtcSessionTest, TestSctpDataChannelWithDtls) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - InitWithDtls(false); + InitWithDtls(); SetLocalDescriptionWithDataChannel(); EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); @@ -2641,7 +3027,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelWithDtls) { TEST_F(WebRtcSessionTest, TestDisableSctpDataChannels) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); options_.disable_sctp_data_channels = true; - InitWithDtls(false); + InitWithDtls(); SetLocalDescriptionWithDataChannel(); EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type()); @@ -2652,7 +3038,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) { const int new_send_port = 9998; const int new_recv_port = 7775; - InitWithDtls(false); + InitWithDtls(); SetFactoryDtlsSrtp(); // By default, don't actually add the codecs to desc_factory_; they don't @@ -2675,7 +3061,7 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) { // TEST PLAN: Set the port number to something new, set it in the SDP, // and pass it all the way down. - webrtc::DataChannelInit dci; + webrtc::InternalDataChannelInit dci; dci.reliable = true; EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); talk_base::scoped_refptr<webrtc::DataChannel> dc = @@ -2705,34 +3091,41 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) { // identity generation is finished. TEST_F(WebRtcSessionTest, TestCreateOfferBeforeIdentityRequestReturnSuccess) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - InitWithDtls(false); + InitWithDtls(); EXPECT_TRUE(session_->waiting_for_identity()); + mediastream_signaling_.SendAudioVideoStream1(); talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL)); EXPECT_TRUE(offer != NULL); + VerifyNoCryptoParams(offer->description(), true); + VerifyFingerprintStatus(offer->description(), true); } // Verifies that CreateAnswer succeeds when CreateOffer is called before async // identity generation is finished. TEST_F(WebRtcSessionTest, TestCreateAnswerBeforeIdentityRequestReturnSuccess) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - InitWithDtls(false); + InitWithDtls(); + SetFactoryDtlsSrtp(); cricket::MediaSessionOptions options; + options.has_video = true; scoped_ptr<JsepSessionDescription> offer( - CreateRemoteOffer(options, cricket::SEC_REQUIRED)); + CreateRemoteOffer(options, cricket::SEC_DISABLED)); ASSERT_TRUE(offer.get() != NULL); SetRemoteDescriptionWithoutError(offer.release()); talk_base::scoped_ptr<SessionDescriptionInterface> answer(CreateAnswer(NULL)); EXPECT_TRUE(answer != NULL); + VerifyNoCryptoParams(answer->description(), true); + VerifyFingerprintStatus(answer->description(), true); } // Verifies that CreateOffer succeeds when CreateOffer is called after async // identity generation is finished. TEST_F(WebRtcSessionTest, TestCreateOfferAfterIdentityRequestReturnSuccess) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - InitWithDtls(false); + InitWithDtls(); EXPECT_TRUE_WAIT(!session_->waiting_for_identity(), 1000); talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL)); @@ -2803,8 +3196,8 @@ TEST_F(WebRtcSessionTest, TestSetRemoteOfferFailIfDtlsDisabledAndNoCrypto) { audio->description.identity_fingerprint.reset( talk_base::SSLFingerprint::CreateFromRfc4572( talk_base::DIGEST_SHA_256, kFakeDtlsFingerprint)); - SetRemoteDescriptionExpectError(kSdpWithoutSdesAndDtlsDisabled, - offer); + SetRemoteDescriptionOfferExpectError(kSdpWithoutSdesCrypto, + offer); } // This test verifies DSCP is properly applied on the media channels. @@ -2833,6 +3226,26 @@ TEST_F(WebRtcSessionTest, TestDscpConstraint) { EXPECT_TRUE(video_options.dscp.GetWithDefaultIfUnset(false)); } +TEST_F(WebRtcSessionTest, TestSuspendBelowMinBitrateConstraint) { + constraints_.reset(new FakeConstraints()); + constraints_->AddOptional( + webrtc::MediaConstraintsInterface::kEnableVideoSuspendBelowMinBitrate, + true); + Init(NULL); + mediastream_signaling_.SendAudioVideoStream1(); + SessionDescriptionInterface* offer = CreateOffer(NULL); + + SetLocalDescriptionWithoutError(offer); + + video_channel_ = media_engine_->GetVideoChannel(0); + + ASSERT_TRUE(video_channel_ != NULL); + cricket::VideoOptions video_options; + EXPECT_TRUE(video_channel_->GetOptions(&video_options)); + EXPECT_TRUE( + video_options.suspend_below_min_bitrate.GetWithDefaultIfUnset(false)); +} + // TODO(bemasc): Add a TestIceStatesBundle with BUNDLE enabled. That test // currently fails because upon disconnection and reconnection OnIceComplete is // called more than once without returning to IceGatheringGathering. diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.cc index b6f523ce0cf..25d8fc9316b 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.cc @@ -127,8 +127,8 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory( identity_request_state_(IDENTITY_NOT_NEEDED) { transport_desc_factory_.set_protocol(cricket::ICEPROTO_HYBRID); session_desc_factory_.set_add_legacy_streams(false); - // By default SRTP-SDES is enabled in WebRtc. - SetSecure(cricket::SEC_REQUIRED); + // SRTP-SDES is disabled if DTLS is on. + SetSdesPolicy(dtls_enabled ? cricket::SEC_DISABLED : cricket::SEC_REQUIRED); if (!dtls_enabled) { return; @@ -261,12 +261,12 @@ void WebRtcSessionDescriptionFactory::CreateAnswer( } } -void WebRtcSessionDescriptionFactory::SetSecure( - cricket::SecureMediaPolicy secure_policy) { +void WebRtcSessionDescriptionFactory::SetSdesPolicy( + cricket::SecurePolicy secure_policy) { session_desc_factory_.set_secure(secure_policy); } -cricket::SecureMediaPolicy WebRtcSessionDescriptionFactory::Secure() const { +cricket::SecurePolicy WebRtcSessionDescriptionFactory::SdesPolicy() const { return session_desc_factory_.secure(); } @@ -318,7 +318,8 @@ void WebRtcSessionDescriptionFactory::InternalCreateOffer( if (!offer->Initialize(desc, session_id_, talk_base::ToString(session_version_++))) { delete offer; - PostCreateSessionDescriptionFailed(request.observer, "CreateOffer failed."); + PostCreateSessionDescriptionFailed(request.observer, + "Failed to initialize the offer."); return; } if (session_->local_description() && @@ -362,7 +363,7 @@ void WebRtcSessionDescriptionFactory::InternalCreateAnswer( talk_base::ToString(session_version_++))) { delete answer; PostCreateSessionDescriptionFailed(request.observer, - "CreateAnswer failed."); + "Failed to initialize the answer."); return; } if (session_->local_description() && @@ -380,6 +381,7 @@ void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed( CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer); msg->error = error; signaling_thread_->Post(this, MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg); + LOG(LS_ERROR) << "Create SDP failed: " << error; } void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded( diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.h b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.h index ca614b4356d..cad0c65da11 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.h @@ -112,8 +112,8 @@ class WebRtcSessionDescriptionFactory : public talk_base::MessageHandler, CreateSessionDescriptionObserver* observer, const MediaConstraintsInterface* constraints); - void SetSecure(cricket::SecureMediaPolicy secure_policy); - cricket::SecureMediaPolicy Secure() const; + void SetSdesPolicy(cricket::SecurePolicy secure_policy); + cricket::SecurePolicy SdesPolicy() const; sigslot::signal1<talk_base::SSLIdentity*> SignalIdentityReady; diff --git a/chromium/third_party/libjingle/source/talk/base/asyncinvoker-inl.h b/chromium/third_party/libjingle/source/talk/base/asyncinvoker-inl.h new file mode 100644 index 00000000000..b6be1750453 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/asyncinvoker-inl.h @@ -0,0 +1,146 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_BASE_ASYNCINVOKER_INL_H_ +#define TALK_BASE_ASYNCINVOKER_INL_H_ + +#include "talk/base/bind.h" +#include "talk/base/callback.h" +#include "talk/base/criticalsection.h" +#include "talk/base/messagehandler.h" +#include "talk/base/refcount.h" +#include "talk/base/scoped_ref_ptr.h" +#include "talk/base/sigslot.h" +#include "talk/base/thread.h" + +namespace talk_base { + +class AsyncInvoker; + +// Helper class for AsyncInvoker. Runs a task and triggers a callback +// on the calling thread if necessary. Instances are ref-counted so their +// lifetime can be independent of AsyncInvoker. +class AsyncClosure : public RefCountInterface { + public: + virtual ~AsyncClosure() {} + // Runs the asynchronous task, and triggers a callback to the calling + // thread if needed. Should be called from the target thread. + virtual void Execute() = 0; +}; + +// Simple closure that doesn't trigger a callback for the calling thread. +template <class FunctorT> +class FireAndForgetAsyncClosure : public AsyncClosure { + public: + explicit FireAndForgetAsyncClosure(const FunctorT& functor) + : functor_(functor) {} + virtual void Execute() { + functor_(); + } + private: + FunctorT functor_; +}; + +// Base class for closures that may trigger a callback for the calling thread. +// Listens for the "destroyed" signals from the calling thread and the invoker, +// and cancels the callback to the calling thread if either is destroyed. +class NotifyingAsyncClosureBase : public AsyncClosure, + public sigslot::has_slots<> { + public: + virtual ~NotifyingAsyncClosureBase() { disconnect_all(); } + + protected: + NotifyingAsyncClosureBase(AsyncInvoker* invoker, Thread* calling_thread); + void TriggerCallback(); + void SetCallback(const Callback0<void>& callback) { + CritScope cs(&crit_); + callback_ = callback; + } + bool CallbackCanceled() const { return calling_thread_ == NULL; } + + private: + Callback0<void> callback_; + CriticalSection crit_; + AsyncInvoker* invoker_; + Thread* calling_thread_; + + void CancelCallback(); +}; + +// Closures that have a non-void return value and require a callback. +template <class ReturnT, class FunctorT, class HostT> +class NotifyingAsyncClosure : public NotifyingAsyncClosureBase { + public: + NotifyingAsyncClosure(AsyncInvoker* invoker, + Thread* calling_thread, + const FunctorT& functor, + void (HostT::*callback)(ReturnT), + HostT* callback_host) + : NotifyingAsyncClosureBase(invoker, calling_thread), + functor_(functor), + callback_(callback), + callback_host_(callback_host) {} + virtual void Execute() { + ReturnT result = functor_(); + if (!CallbackCanceled()) { + SetCallback(Callback0<void>(Bind(callback_, callback_host_, result))); + TriggerCallback(); + } + } + + private: + FunctorT functor_; + void (HostT::*callback_)(ReturnT); + HostT* callback_host_; +}; + +// Closures that have a void return value and require a callback. +template <class FunctorT, class HostT> +class NotifyingAsyncClosure<void, FunctorT, HostT> + : public NotifyingAsyncClosureBase { + public: + NotifyingAsyncClosure(AsyncInvoker* invoker, + Thread* calling_thread, + const FunctorT& functor, + void (HostT::*callback)(), + HostT* callback_host) + : NotifyingAsyncClosureBase(invoker, calling_thread), + functor_(functor) { + SetCallback(Callback0<void>(Bind(callback, callback_host))); + } + virtual void Execute() { + functor_(); + TriggerCallback(); + } + + private: + FunctorT functor_; +}; + +} // namespace talk_base + +#endif // TALK_BASE_ASYNCINVOKER_INL_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/asyncinvoker.cc b/chromium/third_party/libjingle/source/talk/base/asyncinvoker.cc new file mode 100644 index 00000000000..a57eb7b962d --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/asyncinvoker.cc @@ -0,0 +1,108 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/base/asyncinvoker.h" + +namespace talk_base { + +AsyncInvoker::AsyncInvoker() : destroying_(false) {} + +AsyncInvoker::~AsyncInvoker() { + destroying_ = true; + SignalInvokerDestroyed(); + // Messages for this need to be cleared *before* our destructor is complete. + MessageQueueManager::Clear(this); +} + +void AsyncInvoker::OnMessage(Message* msg) { + // Get the AsyncClosure shared ptr from this message's data. + ScopedRefMessageData<AsyncClosure>* data = + static_cast<ScopedRefMessageData<AsyncClosure>*>(msg->pdata); + scoped_refptr<AsyncClosure> closure = data->data(); + delete msg->pdata; + msg->pdata = NULL; + + // Execute the closure and trigger the return message if needed. + closure->Execute(); +} + +void AsyncInvoker::Flush(Thread* thread, uint32 id /*= MQID_ANY*/) { + if (destroying_) return; + + // Run this on |thread| to reduce the number of context switches. + if (Thread::Current() != thread) { + thread->Invoke<void>(Bind(&AsyncInvoker::Flush, this, thread, id)); + return; + } + + MessageList removed; + thread->Clear(this, id, &removed); + for (MessageList::iterator it = removed.begin(); it != removed.end(); ++it) { + // This message was pending on this thread, so run it now. + thread->Send(it->phandler, + it->message_id, + it->pdata); + } +} + +void AsyncInvoker::DoInvoke(Thread* thread, AsyncClosure* closure, + uint32 id) { + if (destroying_) { + LOG(LS_WARNING) << "Tried to invoke while destroying the invoker."; + // Since this call transwers ownership of |closure|, we clean it up here. + delete closure; + return; + } + thread->Post(this, id, new ScopedRefMessageData<AsyncClosure>(closure)); +} + +NotifyingAsyncClosureBase::NotifyingAsyncClosureBase(AsyncInvoker* invoker, + Thread* calling_thread) + : invoker_(invoker), calling_thread_(calling_thread) { + calling_thread->SignalQueueDestroyed.connect( + this, &NotifyingAsyncClosureBase::CancelCallback); + invoker->SignalInvokerDestroyed.connect( + this, &NotifyingAsyncClosureBase::CancelCallback); +} + +void NotifyingAsyncClosureBase::TriggerCallback() { + CritScope cs(&crit_); + if (!CallbackCanceled() && !callback_.empty()) { + invoker_->AsyncInvoke<void>(calling_thread_, callback_); + } +} + +void NotifyingAsyncClosureBase::CancelCallback() { + // If the callback is triggering when this is called, block the + // destructor of the dying object here by waiting until the callback + // is done triggering. + CritScope cs(&crit_); + // calling_thread_ == NULL means do not trigger the callback. + calling_thread_ = NULL; +} + +} // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/asyncinvoker.h b/chromium/third_party/libjingle/source/talk/base/asyncinvoker.h new file mode 100644 index 00000000000..b7dfac98381 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/asyncinvoker.h @@ -0,0 +1,151 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_BASE_ASYNCINVOKER_H_ +#define TALK_BASE_ASYNCINVOKER_H_ + +#include "talk/base/asyncinvoker-inl.h" +#include "talk/base/bind.h" +#include "talk/base/sigslot.h" +#include "talk/base/scopedptrcollection.h" +#include "talk/base/thread.h" + +namespace talk_base { + +// Invokes function objects (aka functors) asynchronously on a Thread, and +// owns the lifetime of calls (ie, when this object is destroyed, calls in +// flight are cancelled). AsyncInvoker can optionally execute a user-specified +// function when the asynchronous call is complete, or operates in +// fire-and-forget mode otherwise. +// +// AsyncInvoker does not own the thread it calls functors on. +// +// A note about async calls and object lifetimes: users should +// be mindful of object lifetimes when calling functions asynchronously and +// ensure objects used by the function _cannot_ be deleted between the +// invocation and execution of the functor. AsyncInvoker is designed to +// help: any calls in flight will be cancelled when the AsyncInvoker used to +// make the call is destructed, and any calls executing will be allowed to +// complete before AsyncInvoker destructs. +// +// The easiest way to ensure lifetimes are handled correctly is to create a +// class that owns the Thread and AsyncInvoker objects, and then call its +// methods asynchronously as needed. +// +// Example: +// class MyClass { +// public: +// void FireAsyncTaskWithResult(Thread* thread, int x) { +// // Specify a callback to get the result upon completion. +// invoker_.AsyncInvoke<int>( +// thread, Bind(&MyClass::AsyncTaskWithResult, this, x), +// &MyClass::OnTaskComplete, this); +// } +// void FireAnotherAsyncTask(Thread* thread) { +// // No callback specified means fire-and-forget. +// invoker_.AsyncInvoke<void>( +// thread, Bind(&MyClass::AnotherAsyncTask, this)); +// +// private: +// int AsyncTaskWithResult(int x) { +// // Some long running process... +// return x * x; +// } +// void AnotherAsyncTask() { +// // Some other long running process... +// } +// void OnTaskComplete(int result) { result_ = result; } +// +// AsyncInvoker invoker_; +// int result_; +// }; +class AsyncInvoker : public MessageHandler { + public: + AsyncInvoker(); + virtual ~AsyncInvoker(); + + // Call |functor| asynchronously on |thread|, with no callback upon + // completion. Returns immediately. + template <class ReturnT, class FunctorT> + void AsyncInvoke(Thread* thread, + const FunctorT& functor, + uint32 id = 0) { + AsyncClosure* closure = + new RefCountedObject<FireAndForgetAsyncClosure<FunctorT> >(functor); + DoInvoke(thread, closure, id); + } + + // Call |functor| asynchronously on |thread|, calling |callback| when done. + template <class ReturnT, class FunctorT, class HostT> + void AsyncInvoke(Thread* thread, + const FunctorT& functor, + void (HostT::*callback)(ReturnT), + HostT* callback_host, + uint32 id = 0) { + AsyncClosure* closure = + new RefCountedObject<NotifyingAsyncClosure<ReturnT, FunctorT, HostT> >( + this, Thread::Current(), functor, callback, callback_host); + DoInvoke(thread, closure, id); + } + + // Call |functor| asynchronously on |thread|, calling |callback| when done. + // Overloaded for void return. + template <class ReturnT, class FunctorT, class HostT> + void AsyncInvoke(Thread* thread, + const FunctorT& functor, + void (HostT::*callback)(), + HostT* callback_host, + uint32 id = 0) { + AsyncClosure* closure = + new RefCountedObject<NotifyingAsyncClosure<void, FunctorT, HostT> >( + this, Thread::Current(), functor, callback, callback_host); + DoInvoke(thread, closure, id); + } + + // Synchronously execute on |thread| all outstanding calls we own + // that are pending on |thread|, and wait for calls to complete + // before returning. Optionally filter by message id. + // The destructor will not wait for outstanding calls, so if that + // behavior is desired, call Flush() before destroying this object. + void Flush(Thread* thread, uint32 id = MQID_ANY); + + // Signaled when this object is destructed. + sigslot::signal0<> SignalInvokerDestroyed; + + private: + virtual void OnMessage(Message* msg); + void DoInvoke(Thread* thread, AsyncClosure* closure, uint32 id); + + bool destroying_; + + DISALLOW_COPY_AND_ASSIGN(AsyncInvoker); +}; + +} // namespace talk_base + + +#endif // TALK_BASE_ASYNCINVOKER_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/asyncpacketsocket.h b/chromium/third_party/libjingle/source/talk/base/asyncpacketsocket.h index 29ab55ffc47..091f1d0fe71 100644 --- a/chromium/third_party/libjingle/source/talk/base/asyncpacketsocket.h +++ b/chromium/third_party/libjingle/source/talk/base/asyncpacketsocket.h @@ -2,26 +2,26 @@ * libjingle * Copyright 2004--2005, Google Inc. * - * Redistribution and use in source and binary forms, with or without + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - * 1. Redistributions of source code must retain the above copyright notice, + * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products + * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -35,6 +35,31 @@ namespace talk_base { +// This structure holds the info needed to update the packet send time header +// extension, including the information needed to update the authentication tag +// after changing the value. +struct PacketTimeUpdateParams { + PacketTimeUpdateParams() + : rtp_sendtime_extension_id(-1), srtp_auth_tag_len(-1), + srtp_packet_index(-1) { + } + + int rtp_sendtime_extension_id; // extension header id present in packet. + std::vector<char> srtp_auth_key; // Authentication key. + int srtp_auth_tag_len; // Authentication tag length. + int64 srtp_packet_index; // Required for Rtp Packet authentication. +}; + +// This structure holds meta information for the packet which is about to send +// over network. +struct PacketOptions { + PacketOptions() : dscp(DSCP_NO_CHANGE) {} + explicit PacketOptions(DiffServCodePoint dscp) : dscp(dscp) {} + + DiffServCodePoint dscp; + PacketTimeUpdateParams packet_time_params; +}; + // This structure will have the information about when packet is actually // received by socket. struct PacketTime { @@ -78,9 +103,9 @@ class AsyncPacketSocket : public sigslot::has_slots<> { virtual SocketAddress GetRemoteAddress() const = 0; // Send a packet. - virtual int Send(const void *pv, size_t cb, DiffServCodePoint dscp) = 0; + virtual int Send(const void *pv, size_t cb, const PacketOptions& options) = 0; virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr, - DiffServCodePoint) = 0; + const PacketOptions& options) = 0; // Close the socket. virtual int Close() = 0; diff --git a/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.cc b/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.cc index d2ae513fd57..781fb0adee4 100644 --- a/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.cc +++ b/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.cc @@ -27,7 +27,7 @@ #include "talk/base/asynctcpsocket.h" -#include <cstring> +#include <string.h> #include "talk/base/byteorder.h" #include "talk/base/common.h" @@ -141,12 +141,11 @@ void AsyncTCPSocketBase::SetError(int error) { return socket_->SetError(error); } -// TODO(mallinath) - Add support of setting DSCP code on AsyncSocket. int AsyncTCPSocketBase::SendTo(const void *pv, size_t cb, const SocketAddress& addr, - DiffServCodePoint dscp) { + const talk_base::PacketOptions& options) { if (addr == GetRemoteAddress()) - return Send(pv, cb, dscp); + return Send(pv, cb, options); ASSERT(false); socket_->SetError(ENOTCONN); @@ -263,8 +262,8 @@ AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket, bool listen) : AsyncTCPSocketBase(socket, listen, kBufSize) { } -// TODO(mallinath) - Add support of setting DSCP code on AsyncSocket. -int AsyncTCPSocket::Send(const void *pv, size_t cb, DiffServCodePoint dscp) { +int AsyncTCPSocket::Send(const void *pv, size_t cb, + const talk_base::PacketOptions& options) { if (cb > kBufSize) { SetError(EMSGSIZE); return -1; diff --git a/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.h b/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.h index a0e7a7e2f41..2b795f64f9e 100644 --- a/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.h +++ b/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.h @@ -43,7 +43,8 @@ class AsyncTCPSocketBase : public AsyncPacketSocket { virtual ~AsyncTCPSocketBase(); // Pure virtual methods to send and recv data. - virtual int Send(const void *pv, size_t cb, DiffServCodePoint dscp) = 0; + virtual int Send(const void *pv, size_t cb, + const talk_base::PacketOptions& options) = 0; virtual void ProcessInput(char* data, size_t* len) = 0; // Signals incoming connection. virtual void HandleIncomingConnection(AsyncSocket* socket) = 0; @@ -51,7 +52,7 @@ class AsyncTCPSocketBase : public AsyncPacketSocket { virtual SocketAddress GetLocalAddress() const; virtual SocketAddress GetRemoteAddress() const; virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr, - DiffServCodePoint dscp); + const talk_base::PacketOptions& options); virtual int Close(); virtual State GetState() const; @@ -102,7 +103,8 @@ class AsyncTCPSocket : public AsyncTCPSocketBase { AsyncTCPSocket(AsyncSocket* socket, bool listen); virtual ~AsyncTCPSocket() {} - virtual int Send(const void* pv, size_t cb, DiffServCodePoint dscp); + virtual int Send(const void* pv, size_t cb, + const talk_base::PacketOptions& options); virtual void ProcessInput(char* data, size_t* len); virtual void HandleIncomingConnection(AsyncSocket* socket); diff --git a/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.cc b/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.cc index 50052630d99..367287f800d 100644 --- a/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.cc +++ b/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.cc @@ -75,14 +75,14 @@ SocketAddress AsyncUDPSocket::GetRemoteAddress() const { return socket_->GetRemoteAddress(); } -// TODO(mallinath) - Add support of setting DSCP code on AsyncSocket. -int AsyncUDPSocket::Send(const void *pv, size_t cb, DiffServCodePoint dscp) { +int AsyncUDPSocket::Send(const void *pv, size_t cb, + const talk_base::PacketOptions& options) { return socket_->Send(pv, cb); } -// TODO(mallinath) - Add support of setting DSCP code on AsyncSocket. int AsyncUDPSocket::SendTo(const void *pv, size_t cb, - const SocketAddress& addr, DiffServCodePoint dscp) { + const SocketAddress& addr, + const talk_base::PacketOptions& options) { return socket_->SendTo(pv, cb, addr); } diff --git a/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.h b/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.h index 17e12a26c31..17fb043a39e 100644 --- a/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.h +++ b/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.h @@ -2,26 +2,26 @@ * libjingle * Copyright 2004--2005, Google Inc. * - * Redistribution and use in source and binary forms, with or without + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - * 1. Redistributions of source code must retain the above copyright notice, + * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products + * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -52,9 +52,10 @@ class AsyncUDPSocket : public AsyncPacketSocket { virtual SocketAddress GetLocalAddress() const; virtual SocketAddress GetRemoteAddress() const; - virtual int Send(const void *pv, size_t cb, DiffServCodePoint dscp); + virtual int Send(const void *pv, size_t cb, + const talk_base::PacketOptions& options); virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr, - DiffServCodePoint dscp); + const talk_base::PacketOptions& options); virtual int Close(); virtual State GetState() const; diff --git a/chromium/third_party/libjingle/source/talk/base/bandwidthsmoother.cc b/chromium/third_party/libjingle/source/talk/base/bandwidthsmoother.cc index 39164884d40..edb4edab743 100644 --- a/chromium/third_party/libjingle/source/talk/base/bandwidthsmoother.cc +++ b/chromium/third_party/libjingle/source/talk/base/bandwidthsmoother.cc @@ -62,7 +62,7 @@ bool BandwidthSmoother::Sample(uint32 sample_time, int bandwidth) { } // Replace bandwidth with the mean of sampled bandwidths. - const int mean_bandwidth = accumulator_.ComputeMean(); + const int mean_bandwidth = static_cast<int>(accumulator_.ComputeMean()); if (mean_bandwidth < bandwidth_estimation_) { time_at_last_change_ = sample_time; diff --git a/chromium/third_party/libjingle/source/talk/base/bind.h b/chromium/third_party/libjingle/source/talk/base/bind.h index 622cc679db1..5b4eaac943b 100644 --- a/chromium/third_party/libjingle/source/talk/base/bind.h +++ b/chromium/third_party/libjingle/source/talk/base/bind.h @@ -84,6 +84,18 @@ class MethodFunctor0 { ObjectT* object_; }; +template <class FunctorT, class R> +class Functor0 { + public: + explicit Functor0(const FunctorT& functor) + : functor_(functor) {} + R operator()() const { + return functor_(); } + private: + FunctorT functor_; +}; + + #define FP_T(x) R (ObjectT::*x)() template <class ObjectT, class R> @@ -104,6 +116,16 @@ Bind(FP_T(method), const ObjectT* object) { } #undef FP_T +#define FP_T(x) R (*x)() + +template <class R> +Functor0<FP_T(NONAME), R> +Bind(FP_T(function)) { + return Functor0<FP_T(NONAME), R>( + function); +} + +#undef FP_T template <class ObjectT, class MethodT, class R, class P1> @@ -121,6 +143,21 @@ class MethodFunctor1 { P1 p1_; }; +template <class FunctorT, class R, + class P1> +class Functor1 { + public: + Functor1(const FunctorT& functor, P1 p1) + : functor_(functor), + p1_(p1) {} + R operator()() const { + return functor_(p1_); } + private: + FunctorT functor_; + P1 p1_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1) template <class ObjectT, class R, @@ -145,6 +182,18 @@ Bind(FP_T(method), const ObjectT* object, } #undef FP_T +#define FP_T(x) R (*x)(P1) + +template <class R, + class P1> +Functor1<FP_T(NONAME), R, P1> +Bind(FP_T(function), + typename detail::identity<P1>::type p1) { + return Functor1<FP_T(NONAME), R, P1>( + function, p1); +} + +#undef FP_T template <class ObjectT, class MethodT, class R, class P1, @@ -166,6 +215,24 @@ class MethodFunctor2 { P2 p2_; }; +template <class FunctorT, class R, + class P1, + class P2> +class Functor2 { + public: + Functor2(const FunctorT& functor, P1 p1, P2 p2) + : functor_(functor), + p1_(p1), + p2_(p2) {} + R operator()() const { + return functor_(p1_, p2_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1, P2) template <class ObjectT, class R, @@ -194,6 +261,20 @@ Bind(FP_T(method), const ObjectT* object, } #undef FP_T +#define FP_T(x) R (*x)(P1, P2) + +template <class R, + class P1, + class P2> +Functor2<FP_T(NONAME), R, P1, P2> +Bind(FP_T(function), + typename detail::identity<P1>::type p1, + typename detail::identity<P2>::type p2) { + return Functor2<FP_T(NONAME), R, P1, P2>( + function, p1, p2); +} + +#undef FP_T template <class ObjectT, class MethodT, class R, class P1, @@ -219,6 +300,27 @@ class MethodFunctor3 { P3 p3_; }; +template <class FunctorT, class R, + class P1, + class P2, + class P3> +class Functor3 { + public: + Functor3(const FunctorT& functor, P1 p1, P2 p2, P3 p3) + : functor_(functor), + p1_(p1), + p2_(p2), + p3_(p3) {} + R operator()() const { + return functor_(p1_, p2_, p3_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; + P3 p3_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1, P2, P3) template <class ObjectT, class R, @@ -251,6 +353,22 @@ Bind(FP_T(method), const ObjectT* object, } #undef FP_T +#define FP_T(x) R (*x)(P1, P2, P3) + +template <class R, + class P1, + class P2, + class P3> +Functor3<FP_T(NONAME), R, P1, P2, P3> +Bind(FP_T(function), + typename detail::identity<P1>::type p1, + typename detail::identity<P2>::type p2, + typename detail::identity<P3>::type p3) { + return Functor3<FP_T(NONAME), R, P1, P2, P3>( + function, p1, p2, p3); +} + +#undef FP_T template <class ObjectT, class MethodT, class R, class P1, @@ -280,6 +398,30 @@ class MethodFunctor4 { P4 p4_; }; +template <class FunctorT, class R, + class P1, + class P2, + class P3, + class P4> +class Functor4 { + public: + Functor4(const FunctorT& functor, P1 p1, P2 p2, P3 p3, P4 p4) + : functor_(functor), + p1_(p1), + p2_(p2), + p3_(p3), + p4_(p4) {} + R operator()() const { + return functor_(p1_, p2_, p3_, p4_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; + P3 p3_; + P4 p4_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1, P2, P3, P4) template <class ObjectT, class R, @@ -316,6 +458,24 @@ Bind(FP_T(method), const ObjectT* object, } #undef FP_T +#define FP_T(x) R (*x)(P1, P2, P3, P4) + +template <class R, + class P1, + class P2, + class P3, + class P4> +Functor4<FP_T(NONAME), R, P1, P2, P3, P4> +Bind(FP_T(function), + typename detail::identity<P1>::type p1, + typename detail::identity<P2>::type p2, + typename detail::identity<P3>::type p3, + typename detail::identity<P4>::type p4) { + return Functor4<FP_T(NONAME), R, P1, P2, P3, P4>( + function, p1, p2, p3, p4); +} + +#undef FP_T template <class ObjectT, class MethodT, class R, class P1, @@ -349,6 +509,33 @@ class MethodFunctor5 { P5 p5_; }; +template <class FunctorT, class R, + class P1, + class P2, + class P3, + class P4, + class P5> +class Functor5 { + public: + Functor5(const FunctorT& functor, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : functor_(functor), + p1_(p1), + p2_(p2), + p3_(p3), + p4_(p4), + p5_(p5) {} + R operator()() const { + return functor_(p1_, p2_, p3_, p4_, p5_); } + private: + FunctorT functor_; + P1 p1_; + P2 p2_; + P3 p3_; + P4 p4_; + P5 p5_; +}; + + #define FP_T(x) R (ObjectT::*x)(P1, P2, P3, P4, P5) template <class ObjectT, class R, @@ -389,6 +576,26 @@ Bind(FP_T(method), const ObjectT* object, } #undef FP_T +#define FP_T(x) R (*x)(P1, P2, P3, P4, P5) + +template <class R, + class P1, + class P2, + class P3, + class P4, + class P5> +Functor5<FP_T(NONAME), R, P1, P2, P3, P4, P5> +Bind(FP_T(function), + typename detail::identity<P1>::type p1, + typename detail::identity<P2>::type p2, + typename detail::identity<P3>::type p3, + typename detail::identity<P4>::type p4, + typename detail::identity<P5>::type p5) { + return Functor5<FP_T(NONAME), R, P1, P2, P3, P4, P5>( + function, p1, p2, p3, p4, p5); +} + +#undef FP_T } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/bind.h.pump b/chromium/third_party/libjingle/source/talk/base/bind.h.pump index 7f4c39e6344..2ebb8955159 100644 --- a/chromium/third_party/libjingle/source/talk/base/bind.h.pump +++ b/chromium/third_party/libjingle/source/talk/base/bind.h.pump @@ -91,6 +91,24 @@ class MethodFunctor$i { }; +template <class FunctorT, class R$for j [[, + class P$j]]> +class Functor$i { + public: + $if i == 0 [[explicit ]] +Functor$i(const FunctorT& functor$for j [[, P$j p$j]]) + : functor_(functor)$for j [[, + p$(j)_(p$j)]] {} + R operator()() const { + return functor_($for j , [[p$(j)_]]); } + private: + FunctorT functor_;$for j [[ + + P$j p$(j)_;]] + +}; + + #define FP_T(x) R (ObjectT::*x)($for j , [[P$j]]) template <class ObjectT, class R$for j [[, @@ -115,6 +133,18 @@ Bind(FP_T(method), const ObjectT* object$for j [[, } #undef FP_T +#define FP_T(x) R (*x)($for j , [[P$j]]) + +template <class R$for j [[, + class P$j]]> +Functor$i<FP_T(NONAME), R$for j [[, P$j]]> +Bind(FP_T(function)$for j [[, + typename detail::identity<P$j>::type p$j]]) { + return Functor$i<FP_T(NONAME), R$for j [[, P$j]]>( + function$for j [[, p$j]]); +} + +#undef FP_T ]] diff --git a/chromium/third_party/libjingle/source/talk/base/bind_unittest.cc b/chromium/third_party/libjingle/source/talk/base/bind_unittest.cc index 81bbddd6b70..78ac278376e 100644 --- a/chromium/third_party/libjingle/source/talk/base/bind_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/bind_unittest.cc @@ -43,6 +43,10 @@ struct MethodBindTester { mutable int call_count; }; +int Return42() { return 42; } +int Negate(int a) { return -a; } +int Multiply(int a, int b) { return a * b; } + } // namespace TEST(BindTest, BindToMethod) { @@ -71,4 +75,10 @@ TEST(BindTest, BindToMethod) { EXPECT_EQ(8, object.call_count); } +TEST(BindTest, BindToFunction) { + EXPECT_EQ(42, Bind(&Return42)()); + EXPECT_EQ(3, Bind(&Negate, -3)()); + EXPECT_EQ(56, Bind(&Multiply, 8, 7)()); +} + } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/buffer.h b/chromium/third_party/libjingle/source/talk/base/buffer.h index 47096332c58..2d589f27ef4 100644 --- a/chromium/third_party/libjingle/source/talk/base/buffer.h +++ b/chromium/third_party/libjingle/source/talk/base/buffer.h @@ -28,7 +28,7 @@ #ifndef TALK_BASE_BUFFER_H_ #define TALK_BASE_BUFFER_H_ -#include <cstring> +#include <string.h> #include "talk/base/scoped_ptr.h" diff --git a/chromium/third_party/libjingle/source/talk/base/bytebuffer.cc b/chromium/third_party/libjingle/source/talk/base/bytebuffer.cc index 523475d82c4..396a1d34282 100644 --- a/chromium/third_party/libjingle/source/talk/base/bytebuffer.cc +++ b/chromium/third_party/libjingle/source/talk/base/bytebuffer.cc @@ -27,9 +27,10 @@ #include "talk/base/bytebuffer.h" +#include <assert.h> +#include <string.h> + #include <algorithm> -#include <cassert> -#include <cstring> #include "talk/base/basictypes.h" #include "talk/base/byteorder.h" diff --git a/chromium/third_party/libjingle/source/talk/base/byteorder.h b/chromium/third_party/libjingle/source/talk/base/byteorder.h index c6d0dbbe0ea..cf26a1292a6 100644 --- a/chromium/third_party/libjingle/source/talk/base/byteorder.h +++ b/chromium/third_party/libjingle/source/talk/base/byteorder.h @@ -28,7 +28,7 @@ #ifndef TALK_BASE_BYTEORDER_H_ #define TALK_BASE_BYTEORDER_H_ -#ifdef POSIX +#if defined(POSIX) && !defined(__native_client__) #include <arpa/inet.h> #endif diff --git a/chromium/third_party/libjingle/source/talk/base/callback.h b/chromium/third_party/libjingle/source/talk/base/callback.h new file mode 100644 index 00000000000..11fbf86badb --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/callback.h @@ -0,0 +1,278 @@ +// This file was GENERATED by command: +// pump.py callback.h.pump +// DO NOT EDIT BY HAND!!! + +/* + * libjingle + * Copyright 2012 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// To generate callback.h from callback.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py callback.h.pump + +// Callbacks are callable object containers. They can hold a function pointer +// or a function object and behave like a value type. Internally, data is +// reference-counted, making copies and pass-by-value inexpensive. +// +// Callbacks are typed using template arguments. The format is: +// CallbackN<ReturnType, ParamType1, ..., ParamTypeN> +// where N is the number of arguments supplied to the callable object. +// Callbacks are invoked using operator(), just like a function or a function +// object. Default-constructed callbacks are "empty," and executing an empty +// callback does nothing. A callback can be made empty by assigning it from +// a default-constructed callback. +// +// Callbacks are similar in purpose to std::function (which isn't available on +// all platforms we support) and a lightweight alternative to sigslots. Since +// they effectively hide the type of the object they call, they're useful in +// breaking dependencies between objects that need to interact with one another. +// Notably, they can hold the results of Bind(), std::bind*, etc, without +// needing +// to know the resulting object type of those calls. +// +// Sigslots, on the other hand, provide a fuller feature set, such as multiple +// subscriptions to a signal, optional thread-safety, and lifetime tracking of +// slots. When these features are needed, choose sigslots. +// +// Example: +// int sqr(int x) { return x * x; } +// struct AddK { +// int k; +// int operator()(int x) const { return x + k; } +// } add_k = {5}; +// +// Callback1<int, int> my_callback; +// cout << my_callback.empty() << endl; // true +// +// my_callback = Callback1<int, int>(&sqr); +// cout << my_callback.empty() << endl; // false +// cout << my_callback(3) << endl; // 9 +// +// my_callback = Callback1<int, int>(add_k); +// cout << my_callback(10) << endl; // 15 +// +// my_callback = Callback1<int, int>(); +// cout << my_callback.empty() << endl; // true + +#ifndef TALK_BASE_CALLBACK_H_ +#define TALK_BASE_CALLBACK_H_ + +#include "talk/base/logging.h" +#include "talk/base/refcount.h" +#include "talk/base/scoped_ref_ptr.h" + +namespace talk_base { + +template <class R> +class Callback0 { + public: + // Default copy operations are appropriate for this class. + Callback0() {} + template <class T> Callback0(const T& functor) + : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {} + R operator()() { + if (empty()) + return R(); + return helper_->Run(); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run() = 0; + }; + template <class T> struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run() { + return functor_(); + } + T functor_; + }; + scoped_refptr<Helper> helper_; +}; + +template <class R, + class P1> +class Callback1 { + public: + // Default copy operations are appropriate for this class. + Callback1() {} + template <class T> Callback1(const T& functor) + : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {} + R operator()(P1 p1) { + if (empty()) + return R(); + return helper_->Run(p1); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1) = 0; + }; + template <class T> struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1) { + return functor_(p1); + } + T functor_; + }; + scoped_refptr<Helper> helper_; +}; + +template <class R, + class P1, + class P2> +class Callback2 { + public: + // Default copy operations are appropriate for this class. + Callback2() {} + template <class T> Callback2(const T& functor) + : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {} + R operator()(P1 p1, P2 p2) { + if (empty()) + return R(); + return helper_->Run(p1, p2); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2) = 0; + }; + template <class T> struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2) { + return functor_(p1, p2); + } + T functor_; + }; + scoped_refptr<Helper> helper_; +}; + +template <class R, + class P1, + class P2, + class P3> +class Callback3 { + public: + // Default copy operations are appropriate for this class. + Callback3() {} + template <class T> Callback3(const T& functor) + : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {} + R operator()(P1 p1, P2 p2, P3 p3) { + if (empty()) + return R(); + return helper_->Run(p1, p2, p3); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2, P3 p3) = 0; + }; + template <class T> struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2, P3 p3) { + return functor_(p1, p2, p3); + } + T functor_; + }; + scoped_refptr<Helper> helper_; +}; + +template <class R, + class P1, + class P2, + class P3, + class P4> +class Callback4 { + public: + // Default copy operations are appropriate for this class. + Callback4() {} + template <class T> Callback4(const T& functor) + : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {} + R operator()(P1 p1, P2 p2, P3 p3, P4 p4) { + if (empty()) + return R(); + return helper_->Run(p1, p2, p3, p4); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) = 0; + }; + template <class T> struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) { + return functor_(p1, p2, p3, p4); + } + T functor_; + }; + scoped_refptr<Helper> helper_; +}; + +template <class R, + class P1, + class P2, + class P3, + class P4, + class P5> +class Callback5 { + public: + // Default copy operations are appropriate for this class. + Callback5() {} + template <class T> Callback5(const T& functor) + : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {} + R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) { + if (empty()) + return R(); + return helper_->Run(p1, p2, p3, p4, p5); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) = 0; + }; + template <class T> struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) { + return functor_(p1, p2, p3, p4, p5); + } + T functor_; + }; + scoped_refptr<Helper> helper_; +}; +} // namespace talk_base + +#endif // TALK_BASE_CALLBACK_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/callback.h.pump b/chromium/third_party/libjingle/source/talk/base/callback.h.pump new file mode 100644 index 00000000000..458eac73bf0 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/callback.h.pump @@ -0,0 +1,120 @@ +/* + * libjingle + * Copyright 2012 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// To generate callback.h from callback.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py callback.h.pump + +// Callbacks are callable object containers. They can hold a function pointer +// or a function object and behave like a value type. Internally, data is +// reference-counted, making copies and pass-by-value inexpensive. +// +// Callbacks are typed using template arguments. The format is: +// CallbackN<ReturnType, ParamType1, ..., ParamTypeN> +// where N is the number of arguments supplied to the callable object. +// Callbacks are invoked using operator(), just like a function or a function +// object. Default-constructed callbacks are "empty," and executing an empty +// callback does nothing. A callback can be made empty by assigning it from +// a default-constructed callback. +// +// Callbacks are similar in purpose to std::function (which isn't available on +// all platforms we support) and a lightweight alternative to sigslots. Since +// they effectively hide the type of the object they call, they're useful in +// breaking dependencies between objects that need to interact with one another. +// Notably, they can hold the results of Bind(), std::bind*, etc, without needing +// to know the resulting object type of those calls. +// +// Sigslots, on the other hand, provide a fuller feature set, such as multiple +// subscriptions to a signal, optional thread-safety, and lifetime tracking of +// slots. When these features are needed, choose sigslots. +// +// Example: +// int sqr(int x) { return x * x; } +// struct AddK { +// int k; +// int operator()(int x) const { return x + k; } +// } add_k = {5}; +// +// Callback1<int, int> my_callback; +// cout << my_callback.empty() << endl; // true +// +// my_callback = Callback1<int, int>(&sqr); +// cout << my_callback.empty() << endl; // false +// cout << my_callback(3) << endl; // 9 +// +// my_callback = Callback1<int, int>(add_k); +// cout << my_callback(10) << endl; // 15 +// +// my_callback = Callback1<int, int>(); +// cout << my_callback.empty() << endl; // true + +#ifndef TALK_BASE_CALLBACK_H_ +#define TALK_BASE_CALLBACK_H_ + +#include "talk/base/refcount.h" +#include "talk/base/scoped_ref_ptr.h" + +namespace talk_base { + +$var n = 5 +$range i 0..n +$for i [[ +$range j 1..i + +template <class R$for j [[, + class P$j]]> +class Callback$i { + public: + // Default copy operations are appropriate for this class. + Callback$i() {} + template <class T> Callback$i(const T& functor) + : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {} + R operator()($for j , [[P$j p$j]]) { + if (empty()) + return R(); + return helper_->Run($for j , [[p$j]]); + } + bool empty() const { return !helper_; } + + private: + struct Helper : RefCountInterface { + virtual ~Helper() {} + virtual R Run($for j , [[P$j p$j]]) = 0; + }; + template <class T> struct HelperImpl : Helper { + explicit HelperImpl(const T& functor) : functor_(functor) {} + virtual R Run($for j , [[P$j p$j]]) { + return functor_($for j , [[p$j]]); + } + T functor_; + }; + scoped_refptr<Helper> helper_; +}; + +]] +} // namespace talk_base + +#endif // TALK_BASE_CALLBACK_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/callback_unittest.cc b/chromium/third_party/libjingle/source/talk/base/callback_unittest.cc new file mode 100644 index 00000000000..c7ca00f0fd2 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/callback_unittest.cc @@ -0,0 +1,98 @@ +/* + * libjingle + * Copyright 2004--2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/base/bind.h" +#include "talk/base/callback.h" +#include "talk/base/gunit.h" + +namespace talk_base { + +namespace { + +void f() {} +int g() { return 42; } +int h(int x) { return x * x; } +void i(int& x) { x *= x; } // NOLINT: Testing refs + +struct BindTester { + int a() { return 24; } + int b(int x) const { return x * x; } +}; + +} // namespace + +TEST(CallbackTest, VoidReturn) { + Callback0<void> cb; + EXPECT_TRUE(cb.empty()); + cb(); // Executing an empty callback should not crash. + cb = Callback0<void>(&f); + EXPECT_FALSE(cb.empty()); + cb(); +} + +TEST(CallbackTest, IntReturn) { + Callback0<int> cb; + EXPECT_TRUE(cb.empty()); + cb = Callback0<int>(&g); + EXPECT_FALSE(cb.empty()); + EXPECT_EQ(42, cb()); + EXPECT_EQ(42, cb()); +} + +TEST(CallbackTest, OneParam) { + Callback1<int, int> cb1(&h); + EXPECT_FALSE(cb1.empty()); + EXPECT_EQ(9, cb1(-3)); + EXPECT_EQ(100, cb1(10)); + + // Try clearing a callback. + cb1 = Callback1<int, int>(); + EXPECT_TRUE(cb1.empty()); + + // Try a callback with a ref parameter. + Callback1<void, int&> cb2(&i); + int x = 3; + cb2(x); + EXPECT_EQ(9, x); + cb2(x); + EXPECT_EQ(81, x); +} + +TEST(CallbackTest, WithBind) { + BindTester t; + Callback0<int> cb1 = Bind(&BindTester::a, &t); + EXPECT_EQ(24, cb1()); + EXPECT_EQ(24, cb1()); + cb1 = Bind(&BindTester::b, &t, 10); + EXPECT_EQ(100, cb1()); + EXPECT_EQ(100, cb1()); + cb1 = Bind(&BindTester::b, &t, 5); + EXPECT_EQ(25, cb1()); + EXPECT_EQ(25, cb1()); +} + +} // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/common.cc b/chromium/third_party/libjingle/source/talk/base/common.cc index 3c0c352a900..9f63aa4da99 100644 --- a/chromium/third_party/libjingle/source/talk/base/common.cc +++ b/chromium/third_party/libjingle/source/talk/base/common.cc @@ -28,7 +28,7 @@ #include <signal.h> #include <stdlib.h> #include <stdio.h> -#include <memory.h> +#include <string.h> #if WIN32 #define WIN32_LEAN_AND_MEAN @@ -78,4 +78,12 @@ void LogAssert(const char* function, const char* file, int line, } } +bool IsOdd(int n) { + return (n & 0x1); +} + +bool IsEven(int n) { + return !IsOdd(n); +} + } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/common.h b/chromium/third_party/libjingle/source/talk/base/common.h index a76748b3905..ed7d59ed676 100644 --- a/chromium/third_party/libjingle/source/talk/base/common.h +++ b/chromium/third_party/libjingle/source/talk/base/common.h @@ -61,8 +61,14 @@ inline void Unused(const void*) {} #endif // UNUSED #ifndef WIN32 + +#ifndef strnicmp #define strnicmp(x, y, n) strncasecmp(x, y, n) +#endif + +#ifndef stricmp #define stricmp(x, y) strcasecmp(x, y) +#endif // TODO(fbarchard): Remove this. std::max should be used everywhere in the code. // NOMINMAX must be defined where we include <windows.h>. @@ -87,6 +93,11 @@ inline void Unused(const void*) {} namespace talk_base { + +// If a debugger is attached, triggers a debugger breakpoint. If a debugger is +// not attached, forces program termination. +void Break(); + // LogAssert writes information about an assertion to the log. It's called by // Assert (and from the ASSERT macro in debug mode) before any other action // is taken (e.g. breaking the debugger, abort()ing, etc.). @@ -105,17 +116,16 @@ typedef void (*AssertLogger)(const char* function, // only by one component. void SetCustomAssertLogger(AssertLogger logger); -} // namespace talk_base +bool IsOdd(int n); +bool IsEven(int n); + +} // namespace talk_base #if ENABLE_DEBUG namespace talk_base { -// If a debugger is attached, triggers a debugger breakpoint. If a debugger is -// not attached, forces program termination. -void Break(); - inline bool Assert(bool result, const char* function, const char* file, int line, const char* expression) { if (!result) { diff --git a/chromium/third_party/libjingle/source/talk/base/cpumonitor.cc b/chromium/third_party/libjingle/source/talk/base/cpumonitor.cc index e9b481fdbd9..aaec77208d4 100644 --- a/chromium/third_party/libjingle/source/talk/base/cpumonitor.cc +++ b/chromium/third_party/libjingle/source/talk/base/cpumonitor.cc @@ -48,6 +48,7 @@ #if defined(IOS) || defined(OSX) #include <mach/mach_host.h> #include <mach/mach_init.h> +#include <mach/mach_port.h> #include <mach/host_info.h> #include <mach/task.h> #endif // defined(IOS) || defined(OSX) @@ -241,11 +242,14 @@ float CpuSampler::GetSystemLoad() { #endif // WIN32 #if defined(IOS) || defined(OSX) + mach_port_t mach_host = mach_host_self(); host_cpu_load_info_data_t cpu_info; mach_msg_type_number_t info_count = HOST_CPU_LOAD_INFO_COUNT; - if (KERN_SUCCESS != host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, - reinterpret_cast<host_info_t>(&cpu_info), - &info_count)) { + kern_return_t kr = host_statistics(mach_host, HOST_CPU_LOAD_INFO, + reinterpret_cast<host_info_t>(&cpu_info), + &info_count); + mach_port_deallocate(mach_task_self(), mach_host); + if (KERN_SUCCESS != kr) { LOG(LS_ERROR) << "::host_statistics() failed"; return 0.f; } diff --git a/chromium/third_party/libjingle/source/talk/base/cpumonitor_unittest.cc b/chromium/third_party/libjingle/source/talk/base/cpumonitor_unittest.cc index b9f5ba33e77..cdc3e6b79b2 100644 --- a/chromium/third_party/libjingle/source/talk/base/cpumonitor_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/cpumonitor_unittest.cc @@ -386,8 +386,8 @@ TEST(CpuMonitorTest, TestCpuMonitor) { CpuLoadListener listener; monitor.SignalUpdate.connect(&listener, &CpuLoadListener::OnCpuLoad); EXPECT_TRUE(monitor.Start(10)); - Thread::Current()->ProcessMessages(50); - EXPECT_GT(listener.count(), 2); // We have checked cpu load more than twice. + // We have checked cpu load more than twice. + EXPECT_TRUE_WAIT(listener.count() > 2, 1000); EXPECT_GT(listener.current_cpus(), 0); EXPECT_GT(listener.cpus(), 0); EXPECT_GE(listener.process_load(), .0f); diff --git a/chromium/third_party/libjingle/source/talk/base/criticalsection_unittest.cc b/chromium/third_party/libjingle/source/talk/base/criticalsection_unittest.cc new file mode 100644 index 00000000000..0bb34b7026c --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/criticalsection_unittest.cc @@ -0,0 +1,163 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <set> +#include <vector> + +#include "talk/base/criticalsection.h" +#include "talk/base/event.h" +#include "talk/base/gunit.h" +#include "talk/base/scopedptrcollection.h" +#include "talk/base/thread.h" + +namespace talk_base { + +namespace { + +const int kLongTime = 10000; // 10 seconds +const int kNumThreads = 16; +const int kOperationsToRun = 1000; + +template <class T> +class AtomicOpRunner : public MessageHandler { + public: + explicit AtomicOpRunner(int initial_value) + : value_(initial_value), + threads_active_(0), + start_event_(true, false), + done_event_(true, false) {} + + int value() const { return value_; } + + bool Run() { + // Signal all threads to start. + start_event_.Set(); + + // Wait for all threads to finish. + return done_event_.Wait(kLongTime); + } + + void SetExpectedThreadCount(int count) { + threads_active_ = count; + } + + virtual void OnMessage(Message* msg) { + std::vector<int> values; + values.reserve(kOperationsToRun); + + // Wait to start. + ASSERT_TRUE(start_event_.Wait(kLongTime)); + + // Generate a bunch of values by updating value_ atomically. + for (int i = 0; i < kOperationsToRun; ++i) { + values.push_back(T::AtomicOp(&value_)); + } + + { // Add them all to the set. + CritScope cs(&all_values_crit_); + for (size_t i = 0; i < values.size(); ++i) { + std::pair<std::set<int>::iterator, bool> result = + all_values_.insert(values[i]); + // Each value should only be taken by one thread, so if this value + // has already been added, something went wrong. + EXPECT_TRUE(result.second) + << "Thread=" << Thread::Current() << " value=" << values[i]; + } + } + + // Signal that we're done. + if (AtomicOps::Decrement(&threads_active_) == 0) { + done_event_.Set(); + } + } + + private: + int value_; + int threads_active_; + CriticalSection all_values_crit_; + std::set<int> all_values_; + Event start_event_; + Event done_event_; +}; + +struct IncrementOp { + static int AtomicOp(int* i) { return AtomicOps::Increment(i); } +}; + +struct DecrementOp { + static int AtomicOp(int* i) { return AtomicOps::Decrement(i); } +}; + +void StartThreads(ScopedPtrCollection<Thread>* threads, + MessageHandler* handler) { + for (int i = 0; i < kNumThreads; ++i) { + Thread* thread = new Thread(); + thread->Start(); + thread->Post(handler); + threads->PushBack(thread); + } +} + +} // namespace + +TEST(AtomicOpsTest, Simple) { + int value = 0; + EXPECT_EQ(1, AtomicOps::Increment(&value)); + EXPECT_EQ(1, value); + EXPECT_EQ(2, AtomicOps::Increment(&value)); + EXPECT_EQ(2, value); + EXPECT_EQ(1, AtomicOps::Decrement(&value)); + EXPECT_EQ(1, value); + EXPECT_EQ(0, AtomicOps::Decrement(&value)); + EXPECT_EQ(0, value); +} + +TEST(AtomicOpsTest, Increment) { + // Create and start lots of threads. + AtomicOpRunner<IncrementOp> runner(0); + ScopedPtrCollection<Thread> threads; + StartThreads(&threads, &runner); + runner.SetExpectedThreadCount(kNumThreads); + + // Release the hounds! + EXPECT_TRUE(runner.Run()); + EXPECT_EQ(kOperationsToRun * kNumThreads, runner.value()); +} + +TEST(AtomicOpsTest, Decrement) { + // Create and start lots of threads. + AtomicOpRunner<DecrementOp> runner(kOperationsToRun * kNumThreads); + ScopedPtrCollection<Thread> threads; + StartThreads(&threads, &runner); + runner.SetExpectedThreadCount(kNumThreads); + + // Release the hounds! + EXPECT_TRUE(runner.Run()); + EXPECT_EQ(0, runner.value()); +} + +} // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/cryptstring.h b/chromium/third_party/libjingle/source/talk/base/cryptstring.h index eb39be229e0..600474f2adb 100644 --- a/chromium/third_party/libjingle/source/talk/base/cryptstring.h +++ b/chromium/third_party/libjingle/source/talk/base/cryptstring.h @@ -28,9 +28,11 @@ #ifndef _TALK_BASE_CRYPTSTRING_H_ #define _TALK_BASE_CRYPTSTRING_H_ -#include <cstring> +#include <string.h> + #include <string> #include <vector> + #include "talk/base/linked_ptr.h" #include "talk/base/scoped_ptr.h" diff --git a/chromium/third_party/libjingle/source/talk/base/event.cc b/chromium/third_party/libjingle/source/talk/base/event.cc index 6089c8c9f94..410c65851f5 100644 --- a/chromium/third_party/libjingle/source/talk/base/event.cc +++ b/chromium/third_party/libjingle/source/talk/base/event.cc @@ -103,10 +103,16 @@ bool Event::Wait(int cms) { // Converting from seconds and microseconds (1e-6) plus // milliseconds (1e-3) to seconds and nanoseconds (1e-9). + struct timespec ts; +#if HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE + // Use relative time version, which tends to be more efficient for + // pthread implementations where provided (like on Android). + ts.tv_sec = cms / 1000; + ts.tv_nsec = (cms % 1000) * 1000000; +#else struct timeval tv; gettimeofday(&tv, NULL); - struct timespec ts; ts.tv_sec = tv.tv_sec + (cms / 1000); ts.tv_nsec = tv.tv_usec * 1000 + (cms % 1000) * 1000000; @@ -115,9 +121,16 @@ bool Event::Wait(int cms) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } +#endif - while (!event_status_ && error == 0) + while (!event_status_ && error == 0) { +#if HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE + error = pthread_cond_timedwait_relative_np( + &event_cond_, &event_mutex_, &ts); +#else error = pthread_cond_timedwait(&event_cond_, &event_mutex_, &ts); +#endif + } } else { while (!event_status_ && error == 0) error = pthread_cond_wait(&event_cond_, &event_mutex_); diff --git a/chromium/third_party/libjingle/source/talk/base/fakesslidentity.h b/chromium/third_party/libjingle/source/talk/base/fakesslidentity.h index 203bb83bf0d..fbe5e648425 100644 --- a/chromium/third_party/libjingle/source/talk/base/fakesslidentity.h +++ b/chromium/third_party/libjingle/source/talk/base/fakesslidentity.h @@ -38,9 +38,12 @@ namespace talk_base { class FakeSSLCertificate : public talk_base::SSLCertificate { public: - explicit FakeSSLCertificate(const std::string& data) : data_(data) {} + // SHA-1 is the default digest algorithm because it is available in all build + // configurations used for unit testing. + explicit FakeSSLCertificate(const std::string& data) + : data_(data), digest_algorithm_(DIGEST_SHA_1) {} explicit FakeSSLCertificate(const std::vector<std::string>& certs) - : data_(certs.front()) { + : data_(certs.front()), digest_algorithm_(DIGEST_SHA_1) { std::vector<std::string>::const_iterator it; // Skip certs[0]. for (it = certs.begin() + 1; it != certs.end(); ++it) { @@ -58,15 +61,17 @@ class FakeSSLCertificate : public talk_base::SSLCertificate { VERIFY(SSLIdentity::PemToDer(kPemTypeCertificate, data_, &der_string)); der_buffer->SetData(der_string.c_str(), der_string.size()); } + void set_digest_algorithm(const std::string& algorithm) { + digest_algorithm_ = algorithm; + } virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const { - // SHA-1 is chosen because it is available in all build configurations - // used for unit testing. - *algorithm = DIGEST_SHA_1; + *algorithm = digest_algorithm_; return true; } - virtual bool ComputeDigest(const std::string &algorithm, - unsigned char *digest, std::size_t size, - std::size_t *length) const { + virtual bool ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const { *length = talk_base::ComputeDigest(algorithm, data_.c_str(), data_.size(), digest, size); return (*length != 0); @@ -86,6 +91,7 @@ class FakeSSLCertificate : public talk_base::SSLCertificate { } std::string data_; std::vector<FakeSSLCertificate> certs_; + std::string digest_algorithm_; }; class FakeSSLIdentity : public talk_base::SSLIdentity { diff --git a/chromium/third_party/libjingle/source/talk/base/fileutils.cc b/chromium/third_party/libjingle/source/talk/base/fileutils.cc index ff34147db76..72797f3d0ff 100644 --- a/chromium/third_party/libjingle/source/talk/base/fileutils.cc +++ b/chromium/third_party/libjingle/source/talk/base/fileutils.cc @@ -25,9 +25,12 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <cassert> +#include <assert.h> #ifdef WIN32 +// TODO(grunell): Remove io.h includes when Chromium has started +// to use AEC in each source. http://crbug.com/264611. +#include <io.h> #include "talk/base/win32.h" #endif @@ -294,4 +297,28 @@ bool CreateUniqueFile(Pathname& path, bool create_empty) { return true; } +// Taken from Chromium's base/platform_file_*.cc. +// TODO(grunell): Remove when Chromium has started to use AEC in each source. +// http://crbug.com/264611. +FILE* FdopenPlatformFileForWriting(PlatformFile file) { +#if defined(WIN32) + if (file == kInvalidPlatformFileValue) + return NULL; + int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0); + if (fd < 0) + return NULL; + return _fdopen(fd, "w"); +#else + return fdopen(file, "w"); +#endif +} + +bool ClosePlatformFile(PlatformFile file) { +#if defined(WIN32) + return CloseHandle(file) != 0; +#else + return close(file); +#endif +} + } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/fileutils.h b/chromium/third_party/libjingle/source/talk/base/fileutils.h index 186c9633226..e58f76c7e9a 100644 --- a/chromium/third_party/libjingle/source/talk/base/fileutils.h +++ b/chromium/third_party/libjingle/source/talk/base/fileutils.h @@ -33,9 +33,10 @@ #ifdef WIN32 #include "talk/base/win32.h" #else -#include <sys/types.h> #include <dirent.h> +#include <stdio.h> #include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> #endif @@ -452,6 +453,24 @@ class FilesystemScope{ // process). bool CreateUniqueFile(Pathname& path, bool create_empty); +// Taken from Chromium's base/platform_file.h. +// Don't use ClosePlatformFile to close a file opened with FdopenPlatformFile. +// Use fclose instead. +// TODO(grunell): Remove when Chromium has started to use AEC in each source. +// http://crbug.com/264611. +#if defined(WIN32) +typedef HANDLE PlatformFile; +const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE; +#elif defined(POSIX) +typedef int PlatformFile; +const PlatformFile kInvalidPlatformFileValue = -1; +#else +#error Unsupported platform +#endif + +FILE* FdopenPlatformFileForWriting(PlatformFile file); +bool ClosePlatformFile(PlatformFile file); + } // namespace talk_base #endif // TALK_BASE_FILEUTILS_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/firewallsocketserver.cc b/chromium/third_party/libjingle/source/talk/base/firewallsocketserver.cc index be8f2e1831c..4a8a66d43e4 100644 --- a/chromium/third_party/libjingle/source/talk/base/firewallsocketserver.cc +++ b/chromium/third_party/libjingle/source/talk/base/firewallsocketserver.cc @@ -27,7 +27,8 @@ #include "talk/base/firewallsocketserver.h" -#include <cassert> +#include <assert.h> + #include <algorithm> #include "talk/base/asyncsocket.h" diff --git a/chromium/third_party/libjingle/source/talk/base/gunit.h b/chromium/third_party/libjingle/source/talk/base/gunit.h index 3a0321468a7..e56dd6f617a 100644 --- a/chromium/third_party/libjingle/source/talk/base/gunit.h +++ b/chromium/third_party/libjingle/source/talk/base/gunit.h @@ -36,11 +36,6 @@ #include "testing/base/public/gunit.h" #endif -// forward declarations -namespace talk_base { -class Pathname; -} - // Wait until "ex" is true, or "timeout" expires. #define WAIT(ex, timeout) \ for (uint32 start = talk_base::Time(); \ @@ -107,6 +102,4 @@ class Pathname; } \ } while (0); -talk_base::Pathname GetTalkDirectory(); - #endif // TALK_BASE_GUNIT_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/helpers.cc b/chromium/third_party/libjingle/source/talk/base/helpers.cc index b10a3f742a7..691a8131f84 100644 --- a/chromium/third_party/libjingle/source/talk/base/helpers.cc +++ b/chromium/third_party/libjingle/source/talk/base/helpers.cc @@ -29,6 +29,7 @@ #include <limits> +#if defined(FEATURE_ENABLE_SSL) #include "talk/base/sslconfig.h" #if defined(SSL_USE_OPENSSL) #include <openssl/rand.h> @@ -40,7 +41,8 @@ #include <windows.h> #include <ntsecapi.h> #endif // WIN32 -#endif +#endif // else +#endif // FEATURE_ENABLED_SSL #include "talk/base/base64.h" #include "talk/base/basictypes.h" @@ -153,6 +155,28 @@ class SecureRandomGenerator : public RandomGenerator { RtlGenRandomProc rtl_gen_random_; }; +#elif !defined(FEATURE_ENABLE_SSL) + +// No SSL implementation -- use rand() +class SecureRandomGenerator : public RandomGenerator { + public: + virtual bool Init(const void* seed, size_t len) { + if (len >= 4) { + srand(*reinterpret_cast<const int*>(seed)); + } else { + srand(*reinterpret_cast<const char*>(seed)); + } + return true; + } + virtual bool Generate(void* buf, size_t len) { + char* bytes = reinterpret_cast<char*>(buf); + for (size_t i = 0; i < len; ++i) { + bytes[i] = static_cast<char>(rand()); + } + return true; + } +}; + #else #error No SSL implementation has been selected! diff --git a/chromium/third_party/libjingle/source/talk/base/helpers_unittest.cc b/chromium/third_party/libjingle/source/talk/base/helpers_unittest.cc index 0fe1d5b36e1..c2cfdb193d3 100644 --- a/chromium/third_party/libjingle/source/talk/base/helpers_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/helpers_unittest.cc @@ -29,14 +29,26 @@ #include "talk/base/gunit.h" #include "talk/base/helpers.h" +#include "talk/base/ssladapter.h" namespace talk_base { -TEST(RandomTest, TestCreateRandomId) { +class RandomTest : public testing::Test { + public: + static void SetUpTestCase() { + talk_base::InitializeSSL(); + } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } +}; + +TEST_F(RandomTest, TestCreateRandomId) { CreateRandomId(); } -TEST(RandomTest, TestCreateRandomDouble) { +TEST_F(RandomTest, TestCreateRandomDouble) { for (int i = 0; i < 100; ++i) { double r = CreateRandomDouble(); EXPECT_GE(r, 0.0); @@ -44,11 +56,11 @@ TEST(RandomTest, TestCreateRandomDouble) { } } -TEST(RandomTest, TestCreateNonZeroRandomId) { +TEST_F(RandomTest, TestCreateNonZeroRandomId) { EXPECT_NE(0U, CreateRandomNonZeroId()); } -TEST(RandomTest, TestCreateRandomString) { +TEST_F(RandomTest, TestCreateRandomString) { std::string random = CreateRandomString(256); EXPECT_EQ(256U, random.size()); std::string random2; @@ -57,7 +69,7 @@ TEST(RandomTest, TestCreateRandomString) { EXPECT_EQ(256U, random2.size()); } -TEST(RandomTest, TestCreateRandomForTest) { +TEST_F(RandomTest, TestCreateRandomForTest) { // Make sure we get the output we expect. SetRandomTestMode(true); EXPECT_EQ(2154761789U, CreateRandomId()); diff --git a/chromium/third_party/libjingle/source/talk/base/httpserver_unittest.cc b/chromium/third_party/libjingle/source/talk/base/httpserver_unittest.cc index d0e0760af89..6e12cf51205 100644 --- a/chromium/third_party/libjingle/source/talk/base/httpserver_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/httpserver_unittest.cc @@ -16,12 +16,6 @@ namespace { "Host: localhost\r\n" "\r\n"; - const char* const kResponse = - "HTTP/1.1 200\r\n" - "Connection: Close\r\n" - "Content-Length: 0\r\n" - "\r\n"; - struct HttpServerMonitor : public sigslot::has_slots<> { HttpServerTransaction* transaction; bool server_closed, connection_closed; diff --git a/chromium/third_party/libjingle/source/talk/base/iosfilesystem.mm b/chromium/third_party/libjingle/source/talk/base/iosfilesystem.mm new file mode 100644 index 00000000000..aaefcb03fb5 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/iosfilesystem.mm @@ -0,0 +1,70 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This file only exists because various iOS system APIs are only +// available from Objective-C. See unixfilesystem.cc for the only use +// (enforced by a lack of a header file). + +#import <Foundation/NSPathUtilities.h> +#import <Foundation/NSProcessInfo.h> +#include <string.h> + +#include "talk/base/common.h" +#include "talk/base/pathutils.h" + +// Return a new[]'d |char*| copy of the UTF8 representation of |s|. +// Caller owns the returned memory and must use delete[] on it. +static char* copyString(NSString* s) { + const char* utf8 = [s UTF8String]; + size_t len = strlen(utf8) + 1; + char* copy = new char[len]; + // This uses a new[] + strcpy (instead of strdup) because the + // receiver expects to be able to delete[] the returned pointer + // (instead of free()ing it). + strcpy(copy, utf8); + return copy; +} + +// Return a (leaked) copy of a directory name suitable for application data. +char* IOSDataDirectory() { + NSArray* paths = NSSearchPathForDirectoriesInDomains( + NSApplicationSupportDirectory, NSUserDomainMask, YES); + ASSERT([paths count] == 1); + return copyString([paths objectAtIndex:0]); +} + +// Return a (leaked) copy of a directory name suitable for use as a $TEMP. +char* IOSTempDirectory() { + return copyString(NSTemporaryDirectory()); +} + +// Return the binary's path. +void IOSAppName(talk_base::Pathname* path) { + NSProcessInfo *pInfo = [NSProcessInfo processInfo]; + NSString* argv0 = [[pInfo arguments] objectAtIndex:0]; + path->SetPathname([argv0 UTF8String]); +} diff --git a/chromium/third_party/libjingle/source/talk/base/ipaddress.cc b/chromium/third_party/libjingle/source/talk/base/ipaddress.cc index 46725908d7d..6c2212bf8b5 100644 --- a/chromium/third_party/libjingle/source/talk/base/ipaddress.cc +++ b/chromium/third_party/libjingle/source/talk/base/ipaddress.cc @@ -32,7 +32,9 @@ #ifdef OPENBSD #include <netinet/in_systm.h> #endif +#ifndef __native_client__ #include <netinet/ip.h> +#endif #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> @@ -49,13 +51,11 @@ namespace talk_base { // Prefixes used for categorizing IPv6 addresses. -static const in6_addr kULAPrefix = {{{0xfc, 0}}}; static const in6_addr kV4MappedPrefix = {{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0}}}; static const in6_addr k6To4Prefix = {{{0x20, 0x02, 0}}}; static const in6_addr kTeredoPrefix = {{{0x20, 0x01, 0x00, 0x00}}}; static const in6_addr kV4CompatibilityPrefix = {{{0}}}; -static const in6_addr kSiteLocalPrefix = {{{0xfe, 0xc0, 0}}}; static const in6_addr k6BonePrefix = {{{0x3f, 0xfe, 0}}}; bool IPAddress::strip_sensitive_ = false; diff --git a/chromium/third_party/libjingle/source/talk/base/ipaddress_unittest.cc b/chromium/third_party/libjingle/source/talk/base/ipaddress_unittest.cc index 424b557cd63..2c03cbdbd06 100644 --- a/chromium/third_party/libjingle/source/talk/base/ipaddress_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/ipaddress_unittest.cc @@ -42,18 +42,10 @@ static const in6_addr kIPv6PublicAddr = {{{0x24, 0x01, 0xfa, 0x00, 0x00, 0x04, 0x10, 0x00, 0xbe, 0x30, 0x5b, 0xff, 0xfe, 0xe5, 0x00, 0xc3}}}; -static const in6_addr kIPv6CompatAddr = {{{0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xfe, 0xe5, 0x00, 0xc3}}}; static const in6_addr kIPv4MappedAnyAddr = {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}}}; -static const in6_addr kIPv4MappedLoopbackAddr = {{{0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xff, - 0x7f, 0x00, 0x00, 0x01}}}; static const in6_addr kIPv4MappedRFC1918Addr = {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, @@ -62,10 +54,6 @@ static const in6_addr kIPv4MappedPublicAddr = {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04}}}; -static const in6_addr kIPv6AllNodes = {{{0xff, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01}}}; static const std::string kIPv4AnyAddrString = "0.0.0.0"; static const std::string kIPv4LoopbackAddrString = "127.0.0.1"; diff --git a/chromium/third_party/libjingle/source/talk/base/json.cc b/chromium/third_party/libjingle/source/talk/base/json.cc index af81e06949c..6b9fce1ae91 100644 --- a/chromium/third_party/libjingle/source/talk/base/json.cc +++ b/chromium/third_party/libjingle/source/talk/base/json.cc @@ -28,9 +28,9 @@ #include "talk/base/json.h" #include <errno.h> +#include <limits.h> +#include <stdlib.h> -#include <climits> -#include <cstdlib> #include <sstream> bool GetStringFromJson(const Json::Value& in, std::string* out) { diff --git a/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.cc.def b/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.cc.def index 1f84f30fc55..64fc0dce2b1 100644 --- a/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.cc.def +++ b/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.cc.def @@ -63,16 +63,17 @@ #error You must define LATE_BINDING_SYMBOL_TABLE_DLL_NAME #endif +#define X(sym) #sym, +const char* const LATE_BINDING_SYMBOL_TABLE_CLASS_NAME::kSymbolNames[] = { + LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +}; +#undef X + const ::talk_base::LateBindingSymbolTable::TableInfo LATE_BINDING_SYMBOL_TABLE_CLASS_NAME::kTableInfo = { LATE_BINDING_SYMBOL_TABLE_DLL_NAME, SYMBOL_TABLE_SIZE, - (const char *const []){ -#define X(sym) \ - #sym, -LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST -#undef X - }, + LATE_BINDING_SYMBOL_TABLE_CLASS_NAME::kSymbolNames }; LATE_BINDING_SYMBOL_TABLE_CLASS_NAME::LATE_BINDING_SYMBOL_TABLE_CLASS_NAME() diff --git a/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.h.def b/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.h.def index cd8c176f36b..3e1cdab599e 100644 --- a/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.h.def +++ b/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.h.def @@ -89,6 +89,7 @@ LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST }; static const ::talk_base::LateBindingSymbolTable::TableInfo kTableInfo; + static const char *const kSymbolNames[]; void *table_[SYMBOL_TABLE_SIZE]; diff --git a/chromium/third_party/libjingle/source/talk/base/linux.cc b/chromium/third_party/libjingle/source/talk/base/linux.cc index 644ec450655..16666f89761 100644 --- a/chromium/third_party/libjingle/source/talk/base/linux.cc +++ b/chromium/third_party/libjingle/source/talk/base/linux.cc @@ -250,6 +250,89 @@ bool ConfigParser::ParseLine(std::string* key, std::string* value) { return true; } +#if !defined(GOOGLE_CHROME_BUILD) && !defined(CHROMIUM_BUILD) +static bool ExpectLineFromStream(FileStream* stream, + std::string* out) { + StreamResult res = stream->ReadLine(out); + if (res != SR_SUCCESS) { + if (res != SR_EOS) { + LOG(LS_ERROR) << "Error when reading from stream"; + } else { + LOG(LS_ERROR) << "Incorrect number of lines in stream"; + } + return false; + } + return true; +} + +static void ExpectEofFromStream(FileStream* stream) { + std::string unused; + StreamResult res = stream->ReadLine(&unused); + if (res == SR_SUCCESS) { + LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream"; + } else if (res != SR_EOS) { + LOG(LS_WARNING) << "Error when checking for extra lines from stream"; + } +} + +// For caching the lsb_release output (reading it invokes a sub-process and +// hence is somewhat expensive). +static std::string lsb_release_string; +static CriticalSection lsb_release_string_critsec; + +std::string ReadLinuxLsbRelease() { + CritScope cs(&lsb_release_string_critsec); + if (!lsb_release_string.empty()) { + // Have cached result from previous call. + return lsb_release_string; + } + // No cached result. Run lsb_release and parse output. + POpenStream lsb_release_output; + if (!lsb_release_output.Open("lsb_release -idrcs", "r", NULL)) { + LOG_ERR(LS_ERROR) << "Can't run lsb_release"; + return lsb_release_string; // empty + } + // Read in the command's output and build the string. + std::ostringstream sstr; + std::string line; + int wait_status; + + if (!ExpectLineFromStream(&lsb_release_output, &line)) { + return lsb_release_string; // empty + } + sstr << "DISTRIB_ID=" << line; + + if (!ExpectLineFromStream(&lsb_release_output, &line)) { + return lsb_release_string; // empty + } + sstr << " DISTRIB_DESCRIPTION=\"" << line << '"'; + + if (!ExpectLineFromStream(&lsb_release_output, &line)) { + return lsb_release_string; // empty + } + sstr << " DISTRIB_RELEASE=" << line; + + if (!ExpectLineFromStream(&lsb_release_output, &line)) { + return lsb_release_string; // empty + } + sstr << " DISTRIB_CODENAME=" << line; + + // Should not be anything left. + ExpectEofFromStream(&lsb_release_output); + + lsb_release_output.Close(); + wait_status = lsb_release_output.GetWaitStatus(); + if (wait_status == -1 || + !WIFEXITED(wait_status) || + WEXITSTATUS(wait_status) != 0) { + LOG(LS_WARNING) << "Unexpected exit status from lsb_release"; + } + + lsb_release_string = sstr.str(); + + return lsb_release_string; +} +#endif std::string ReadLinuxUname() { struct utsname buf; diff --git a/chromium/third_party/libjingle/source/talk/base/linux.h b/chromium/third_party/libjingle/source/talk/base/linux.h index 63e30218140..46fa5ed6b27 100644 --- a/chromium/third_party/libjingle/source/talk/base/linux.h +++ b/chromium/third_party/libjingle/source/talk/base/linux.h @@ -121,6 +121,11 @@ class ProcCpuInfo { ConfigParser::MapVector sections_; }; +#if !defined(GOOGLE_CHROME_BUILD) && !defined(CHROMIUM_BUILD) +// Builds a string containing the info from lsb_release on a single line. +std::string ReadLinuxLsbRelease(); +#endif + // Returns the output of "uname". std::string ReadLinuxUname(); diff --git a/chromium/third_party/libjingle/source/talk/base/linux_unittest.cc b/chromium/third_party/libjingle/source/talk/base/linux_unittest.cc index efc7f87c1e4..f6815141d2b 100644 --- a/chromium/third_party/libjingle/source/talk/base/linux_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/linux_unittest.cc @@ -105,6 +105,14 @@ TEST(ConfigParser, ParseConfig) { EXPECT_EQ(true, parser.Parse(&key_val_pairs)); } +#if !defined(GOOGLE_CHROME_BUILD) && !defined(CHROMIUM_BUILD) +TEST(ReadLinuxLsbRelease, ReturnsSomething) { + std::string str = ReadLinuxLsbRelease(); + // ChromeOS don't have lsb_release + // EXPECT_FALSE(str.empty()); +} +#endif + TEST(ReadLinuxUname, ReturnsSomething) { std::string str = ReadLinuxUname(); EXPECT_FALSE(str.empty()); diff --git a/chromium/third_party/libjingle/source/talk/base/logging.cc b/chromium/third_party/libjingle/source/talk/base/logging.cc index 4c7eae172e7..c1d0a53baaf 100644 --- a/chromium/third_party/libjingle/source/talk/base/logging.cc +++ b/chromium/third_party/libjingle/source/talk/base/logging.cc @@ -349,6 +349,9 @@ void LogMessage::ConfigureLogging(const char* params, const char* filename) { } #endif // WIN32 + LogToDebug(debug_level); + +#if !defined(__native_client__) // No logging to file in NaCl. scoped_ptr<FileStream> stream; if (NO_LOGGING != file_level) { stream.reset(new FileStream); @@ -357,8 +360,8 @@ void LogMessage::ConfigureLogging(const char* params, const char* filename) { } } - LogToDebug(debug_level); LogToStream(stream.release(), file_level); +#endif } int LogMessage::ParseLogSeverity(const std::string& value) { diff --git a/chromium/third_party/libjingle/source/talk/base/logging.h b/chromium/third_party/libjingle/source/talk/base/logging.h index 49e126bab0c..01636e89fd6 100644 --- a/chromium/third_party/libjingle/source/talk/base/logging.h +++ b/chromium/third_party/libjingle/source/talk/base/logging.h @@ -376,6 +376,13 @@ inline bool LogCheckLevel(LoggingSeverity sev) { LOG_GLE(sev) #define LAST_SYSTEM_ERROR \ (::GetLastError()) +#elif __native_client__ +#define LOG_ERR_EX(sev, err) \ + LOG(sev) +#define LOG_ERR(sev) \ + LOG(sev) +#define LAST_SYSTEM_ERROR \ + (0) #elif POSIX #define LOG_ERR_EX(sev, err) \ LOG_ERRNO_EX(sev, err) diff --git a/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver.h b/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver.h index f4aeb339768..51dc749d794 100644 --- a/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver.h +++ b/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver.h @@ -54,6 +54,8 @@ class MacCocoaSocketServer : public MacBaseSocketServer { private: MacCocoaSocketServerHelper* helper_; NSTimer* timer_; // Weak. + // The count of how many times we're inside the NSApplication main loop. + int run_count_; DISALLOW_EVIL_CONSTRUCTORS(MacCocoaSocketServer); }; diff --git a/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver.mm b/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver.mm index bf308e610e0..8257e386626 100644 --- a/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver.mm +++ b/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver.mm @@ -53,6 +53,25 @@ - (void)timerFired:(NSTimer*)timer { socketServer_->WakeUp(); } + +- (void)breakMainloop { + [NSApp stop:self]; + // NSApp stop only exits after finishing processing of the + // current event. Since we're potentially in a timer callback + // and not an NSEvent handler, we need to trigger a dummy one + // and turn the loop over. We may be able to skip this if we're + // on the ss' thread and not inside the app loop already. + NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined + location:NSMakePoint(0,0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0]; + [NSApp postEvent:event atStart:NO]; +} @end namespace talk_base { @@ -60,6 +79,7 @@ namespace talk_base { MacCocoaSocketServer::MacCocoaSocketServer() { helper_ = [[MacCocoaSocketServerHelper alloc] initWithSocketServer:this]; timer_ = nil; + run_count_ = 0; // Initialize the shared NSApplication [NSApplication sharedApplication]; @@ -71,12 +91,19 @@ MacCocoaSocketServer::~MacCocoaSocketServer() { [helper_ release]; } +// ::Wait is reentrant, for example when blocking on another thread while +// responding to I/O. Calls to [NSApp] MUST be made from the main thread +// only! bool MacCocoaSocketServer::Wait(int cms, bool process_io) { talk_base::ScopedAutoreleasePool pool; if (!process_io && cms == 0) { // No op. return true; } + if ([NSApp isRunning]) { + // Only allow reentrant waiting if we're in a blocking send. + ASSERT(!process_io && cms == kForever); + } if (!process_io) { // No way to listen to common modes and not get socket events, unless @@ -96,7 +123,9 @@ bool MacCocoaSocketServer::Wait(int cms, bool process_io) { } // Run until WakeUp is called, which will call stop and exit this loop. + run_count_++; [NSApp run]; + run_count_--; if (!process_io) { // Reenable them. Hopefully this won't cause spurious callbacks or @@ -107,28 +136,22 @@ bool MacCocoaSocketServer::Wait(int cms, bool process_io) { return true; } +// Can be called from any thread. Post a message back to the main thread to +// break out of the NSApp loop. void MacCocoaSocketServer::WakeUp() { - // Timer has either fired or shortcutted. - [timer_ invalidate]; - [timer_ release]; - timer_ = nil; - [NSApp stop:nil]; + if (timer_ != nil) { + [timer_ invalidate]; + [timer_ release]; + timer_ = nil; + } - // NSApp stop only exits after finishing processing of the - // current event. Since we're potentially in a timer callback - // and not an NSEvent handler, we need to trigger a dummy one - // and turn the loop over. We may be able to skip this if we're - // on the ss' thread and not inside the app loop already. - NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:nil - subtype:1 - data1:1 - data2:1]; - [NSApp postEvent:event atStart:YES]; + // [NSApp isRunning] returns unexpected results when called from another + // thread. Maintain our own count of how many times to break the main loop. + if (run_count_ > 0) { + [helper_ performSelectorOnMainThread:@selector(breakMainloop) + withObject:nil + waitUntilDone:false]; + } } } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/messagedigest.cc b/chromium/third_party/libjingle/source/talk/base/messagedigest.cc index d91d0674b5f..975991db7c0 100644 --- a/chromium/third_party/libjingle/source/talk/base/messagedigest.cc +++ b/chromium/third_party/libjingle/source/talk/base/messagedigest.cc @@ -70,6 +70,19 @@ MessageDigest* MessageDigestFactory::Create(const std::string& alg) { #endif } +bool IsFips180DigestAlgorithm(const std::string& alg) { + // These are the FIPS 180 algorithms. According to RFC 4572 Section 5, + // "Self-signed certificates (for which legacy certificates are not a + // consideration) MUST use one of the FIPS 180 algorithms (SHA-1, + // SHA-224, SHA-256, SHA-384, or SHA-512) as their signature algorithm, + // and thus also MUST use it to calculate certificate fingerprints." + return alg == DIGEST_SHA_1 || + alg == DIGEST_SHA_224 || + alg == DIGEST_SHA_256 || + alg == DIGEST_SHA_384 || + alg == DIGEST_SHA_512; +} + size_t ComputeDigest(MessageDigest* digest, const void* input, size_t in_len, void* output, size_t out_len) { digest->Update(input, in_len); diff --git a/chromium/third_party/libjingle/source/talk/base/messagedigest.h b/chromium/third_party/libjingle/source/talk/base/messagedigest.h index 734082b0166..e8f303f2666 100644 --- a/chromium/third_party/libjingle/source/talk/base/messagedigest.h +++ b/chromium/third_party/libjingle/source/talk/base/messagedigest.h @@ -60,6 +60,9 @@ class MessageDigestFactory { static MessageDigest* Create(const std::string& alg); }; +// A whitelist of approved digest algorithms from RFC 4572 (FIPS 180). +bool IsFips180DigestAlgorithm(const std::string& alg); + // Functions to create hashes. // Computes the hash of |in_len| bytes of |input|, using the |digest| hash diff --git a/chromium/third_party/libjingle/source/talk/base/messagehandler.h b/chromium/third_party/libjingle/source/talk/base/messagehandler.h index 913edf8ce26..6494f2b2561 100644 --- a/chromium/third_party/libjingle/source/talk/base/messagehandler.h +++ b/chromium/third_party/libjingle/source/talk/base/messagehandler.h @@ -38,16 +38,48 @@ struct Message; class MessageHandler { public: + virtual ~MessageHandler(); virtual void OnMessage(Message* msg) = 0; protected: MessageHandler() {} - virtual ~MessageHandler(); private: DISALLOW_COPY_AND_ASSIGN(MessageHandler); }; +// Helper class to facilitate executing a functor on a thread. +template <class ReturnT, class FunctorT> +class FunctorMessageHandler : public MessageHandler { + public: + explicit FunctorMessageHandler(const FunctorT& functor) + : functor_(functor) {} + virtual void OnMessage(Message* msg) { + result_ = functor_(); + } + const ReturnT& result() const { return result_; } + + private: + FunctorT functor_; + ReturnT result_; +}; + +// Specialization for ReturnT of void. +template <class FunctorT> +class FunctorMessageHandler<void, FunctorT> : public MessageHandler { + public: + explicit FunctorMessageHandler(const FunctorT& functor) + : functor_(functor) {} + virtual void OnMessage(Message* msg) { + functor_(); + } + void result() const {} + + private: + FunctorT functor_; +}; + + } // namespace talk_base #endif // TALK_BASE_MESSAGEHANDLER_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/messagequeue.cc b/chromium/third_party/libjingle/source/talk/base/messagequeue.cc index 15b700ffe48..7bda92411c4 100644 --- a/chromium/third_party/libjingle/source/talk/base/messagequeue.cc +++ b/chromium/third_party/libjingle/source/talk/base/messagequeue.cc @@ -32,8 +32,13 @@ #include "talk/base/common.h" #include "talk/base/logging.h" #include "talk/base/messagequeue.h" +#if defined(__native_client__) +#include "talk/base/nullsocketserver.h" +typedef talk_base::NullSocketServer DefaultSocketServer; +#else #include "talk/base/physicalsocketserver.h" - +typedef talk_base::PhysicalSocketServer DefaultSocketServer; +#endif namespace talk_base { @@ -121,7 +126,7 @@ void MessageQueueManager::ClearInternal(MessageHandler *handler) { // MessageQueue MessageQueue::MessageQueue(SocketServer* ss) - : ss_(ss), fStop_(false), fPeekKeep_(false), active_(false), + : ss_(ss), fStop_(false), fPeekKeep_(false), dmsgq_next_num_(0) { if (!ss_) { // Currently, MessageQueue holds a socket server, and is the base class for @@ -129,10 +134,11 @@ MessageQueue::MessageQueue(SocketServer* ss) // server, and provide it to the MessageQueue, since the Thread controls // the I/O model, and MQ is agnostic to those details. Anyway, this causes // messagequeue_unittest to depend on network libraries... yuck. - default_ss_.reset(new PhysicalSocketServer()); + default_ss_.reset(new DefaultSocketServer()); ss_ = default_ss_.get(); } ss_->SetMessageQueue(this); + MessageQueueManager::Add(this); } MessageQueue::~MessageQueue() { @@ -140,10 +146,8 @@ MessageQueue::~MessageQueue() { // that it always gets called when the queue // is going away. SignalQueueDestroyed(); - if (active_) { - MessageQueueManager::Remove(this); - Clear(NULL); - } + MessageQueueManager::Remove(this); + Clear(NULL); if (ss_) { ss_->SetMessageQueue(NULL); } @@ -291,7 +295,6 @@ void MessageQueue::Post(MessageHandler *phandler, uint32 id, // Signal for the multiplexer to return CritScope cs(&crit_); - EnsureActive(); Message msg; msg.phandler = phandler; msg.message_id = id; @@ -313,7 +316,6 @@ void MessageQueue::DoDelayPost(int cmsDelay, uint32 tstamp, // Signal for the multiplexer to return. CritScope cs(&crit_); - EnsureActive(); Message msg; msg.phandler = phandler; msg.message_id = id; @@ -396,12 +398,4 @@ void MessageQueue::Dispatch(Message *pmsg) { pmsg->phandler->OnMessage(pmsg); } -void MessageQueue::EnsureActive() { - ASSERT(crit_.CurrentThreadIsOwner()); - if (!active_) { - active_ = true; - MessageQueueManager::Add(this); - } -} - } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/messagequeue.h b/chromium/third_party/libjingle/source/talk/base/messagequeue.h index 7b38ba0082d..dc2b5a89a53 100644 --- a/chromium/third_party/libjingle/source/talk/base/messagequeue.h +++ b/chromium/third_party/libjingle/source/talk/base/messagequeue.h @@ -28,8 +28,9 @@ #ifndef TALK_BASE_MESSAGEQUEUE_H_ #define TALK_BASE_MESSAGEQUEUE_H_ +#include <string.h> + #include <algorithm> -#include <cstring> #include <list> #include <queue> #include <vector> @@ -74,7 +75,7 @@ class MessageQueueManager { void ClearInternal(MessageHandler *handler); static MessageQueueManager* instance_; - // This list contains 'active' MessageQueues. + // This list contains all live MessageQueues. std::vector<MessageQueue *> message_queues_; CriticalSection crit_; }; @@ -246,7 +247,6 @@ class MessageQueue { void reheap() { make_heap(c.begin(), c.end(), comp); } }; - void EnsureActive(); void DoDelayPost(int cmsDelay, uint32 tstamp, MessageHandler *phandler, uint32 id, MessageData* pdata); @@ -257,9 +257,6 @@ class MessageQueue { bool fStop_; bool fPeekKeep_; Message msgPeek_; - // A message queue is active if it has ever had a message posted to it. - // This also corresponds to being in MessageQueueManager's global list. - bool active_; MessageList msgq_; PriorityQueue dmsgq_; uint32 dmsgq_next_num_; diff --git a/chromium/third_party/libjingle/source/talk/base/natserver.cc b/chromium/third_party/libjingle/source/talk/base/natserver.cc index 46980487175..f69baa0c427 100644 --- a/chromium/third_party/libjingle/source/talk/base/natserver.cc +++ b/chromium/third_party/libjingle/source/talk/base/natserver.cc @@ -126,8 +126,8 @@ void NATServer::OnInternalPacket( iter->second->WhitelistInsert(dest_addr); // Send the packet to its intended destination. - iter->second->socket->SendTo(buf + length, size - length, dest_addr, - DSCP_NO_CHANGE); + talk_base::PacketOptions options; + iter->second->socket->SendTo(buf + length, size - length, dest_addr, options); } void NATServer::OnExternalPacket( @@ -154,9 +154,10 @@ void NATServer::OnExternalPacket( size + kNATEncodedIPv6AddressSize, remote_addr); // Copy the data part after the address. - std::memcpy(real_buf.get() + addrlength, buf, size); + talk_base::PacketOptions options; + memcpy(real_buf.get() + addrlength, buf, size); server_socket_->SendTo(real_buf.get(), size + addrlength, - iter->second->route.source(), DSCP_NO_CHANGE); + iter->second->route.source(), options); } void NATServer::Translate(const SocketAddressPair& route) { diff --git a/chromium/third_party/libjingle/source/talk/base/natsocketfactory.cc b/chromium/third_party/libjingle/source/talk/base/natsocketfactory.cc index 395069e6658..6ce09fd251c 100644 --- a/chromium/third_party/libjingle/source/talk/base/natsocketfactory.cc +++ b/chromium/third_party/libjingle/source/talk/base/natsocketfactory.cc @@ -47,12 +47,12 @@ size_t PackAddressForNAT(char* buf, size_t buf_size, if (family == AF_INET) { ASSERT(buf_size >= kNATEncodedIPv4AddressSize); in_addr v4addr = ip.ipv4_address(); - std::memcpy(&buf[4], &v4addr, kNATEncodedIPv4AddressSize - 4); + memcpy(&buf[4], &v4addr, kNATEncodedIPv4AddressSize - 4); return kNATEncodedIPv4AddressSize; } else if (family == AF_INET6) { ASSERT(buf_size >= kNATEncodedIPv6AddressSize); in6_addr v6addr = ip.ipv6_address(); - std::memcpy(&buf[4], &v6addr, kNATEncodedIPv6AddressSize - 4); + memcpy(&buf[4], &v6addr, kNATEncodedIPv6AddressSize - 4); return kNATEncodedIPv6AddressSize; } return 0U; @@ -159,7 +159,7 @@ class NATSocket : public AsyncSocket, public sigslot::has_slots<> { size + kNATEncodedIPv6AddressSize, addr); size_t encoded_size = size + addrlength; - std::memcpy(buf.get() + addrlength, data, size); + memcpy(buf.get() + addrlength, data, size); int result = socket_->SendTo(buf.get(), encoded_size, server_addr_); if (result >= 0) { ASSERT(result == static_cast<int>(encoded_size)); @@ -196,7 +196,7 @@ class NATSocket : public AsyncSocket, public sigslot::has_slots<> { SocketAddress real_remote_addr; size_t addrlength = UnpackAddressFromNAT(buf_, result, &real_remote_addr); - std::memcpy(data, buf_ + addrlength, result - addrlength); + memcpy(data, buf_ + addrlength, result - addrlength); // Make sure this packet should be delivered before returning it. if (!connected_ || (real_remote_addr == remote_addr_)) { diff --git a/chromium/third_party/libjingle/source/talk/base/nattypes.cc b/chromium/third_party/libjingle/source/talk/base/nattypes.cc index 290c3adde77..da1d0116110 100644 --- a/chromium/third_party/libjingle/source/talk/base/nattypes.cc +++ b/chromium/third_party/libjingle/source/talk/base/nattypes.cc @@ -25,7 +25,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <cassert> +#include <assert.h> #include "talk/base/nattypes.h" diff --git a/chromium/third_party/libjingle/source/talk/base/nethelpers.cc b/chromium/third_party/libjingle/source/talk/base/nethelpers.cc index e6310ac45fe..057f34ddc15 100644 --- a/chromium/third_party/libjingle/source/talk/base/nethelpers.cc +++ b/chromium/third_party/libjingle/source/talk/base/nethelpers.cc @@ -34,12 +34,18 @@ #endif #include "talk/base/byteorder.h" +#include "talk/base/logging.h" #include "talk/base/signalthread.h" namespace talk_base { int ResolveHostname(const std::string& hostname, int family, std::vector<IPAddress>* addresses) { +#ifdef __native_client__ + ASSERT(false); + LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl"; + return -1; +#else // __native_client__ if (!addresses) { return -1; } @@ -64,6 +70,7 @@ int ResolveHostname(const std::string& hostname, int family, } freeaddrinfo(result); return 0; +#endif // !__native_client__ } // AsyncResolver @@ -83,7 +90,7 @@ bool AsyncResolver::GetResolvedAddress(int family, SocketAddress* addr) const { *addr = addr_; for (size_t i = 0; i < addresses_.size(); ++i) { if (family == addresses_[i].family()) { - addr->SetIP(addresses_[i]); + addr->SetResolvedIP(addresses_[i]); return true; } } diff --git a/chromium/third_party/libjingle/source/talk/base/nethelpers.h b/chromium/third_party/libjingle/source/talk/base/nethelpers.h index a49f48ac7cd..5b385bbc509 100644 --- a/chromium/third_party/libjingle/source/talk/base/nethelpers.h +++ b/chromium/third_party/libjingle/source/talk/base/nethelpers.h @@ -30,7 +30,7 @@ #ifdef POSIX #include <netdb.h> -#include <cstddef> +#include <stddef.h> #elif WIN32 #include <winsock2.h> // NOLINT #endif diff --git a/chromium/third_party/libjingle/source/talk/base/network.cc b/chromium/third_party/libjingle/source/talk/base/network.cc index d4dda138135..829507aec7d 100644 --- a/chromium/third_party/libjingle/source/talk/base/network.cc +++ b/chromium/third_party/libjingle/source/talk/base/network.cc @@ -38,7 +38,7 @@ #if defined(ANDROID) || defined(LINUX) #include <linux/if.h> #include <linux/route.h> -#else +#elif !defined(__native_client__) #include <net/if.h> #endif #include <sys/socket.h> @@ -46,11 +46,13 @@ #include <sys/ioctl.h> #include <unistd.h> #include <errno.h> + #ifdef ANDROID #include "talk/base/ifaddrs-android.h" -#else +#elif !defined(__native_client__) #include <ifaddrs.h> #endif + #endif // POSIX #ifdef WIN32 @@ -58,8 +60,9 @@ #include <Iphlpapi.h> #endif +#include <stdio.h> + #include <algorithm> -#include <cstdio> #include "talk/base/logging.h" #include "talk/base/scoped_ptr.h" @@ -77,16 +80,7 @@ const uint32 kSignalNetworksMessage = 2; // Fetch list of networks every two seconds. const int kNetworksUpdateIntervalMs = 2000; - -// Makes a string key for this network. Used in the network manager's maps. -// Network objects are keyed on interface name, network prefix and the -// length of that prefix. -std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix, - int prefix_length) { - std::ostringstream ost; - ost << name << "%" << prefix.ToString() << "/" << prefix_length; - return ost.str(); -} +const int kHighestNetworkPreference = 127; bool CompareNetworks(const Network* a, const Network* b) { if (a->prefix_length() == b->prefix_length()) { @@ -97,9 +91,54 @@ bool CompareNetworks(const Network* a, const Network* b) { return a->name() < b->name(); } +bool SortNetworks(const Network* a, const Network* b) { + // Network types will be preferred above everything else while sorting + // Networks. + + // Networks are sorted first by type. + if (a->type() != b->type()) { + return a->type() < b->type(); + } + + // After type, networks are sorted by IP address precedence values + // from RFC 3484-bis + if (IPAddressPrecedence(a->ip()) != IPAddressPrecedence(b->ip())) { + return IPAddressPrecedence(a->ip()) > IPAddressPrecedence(b->ip()); + } + + // TODO(mallinath) - Add VPN and Link speed conditions while sorting. + + // Networks are sorted last by key. + return a->key() > b->key(); +} + +std::string AdapterTypeToString(AdapterType type) { + switch (type) { + case ADAPTER_TYPE_UNKNOWN: + return "Unknown"; + case ADAPTER_TYPE_ETHERNET: + return "Ethernet"; + case ADAPTER_TYPE_WIFI: + return "Wifi"; + case ADAPTER_TYPE_CELLULAR: + return "Cellular"; + case ADAPTER_TYPE_VPN: + return "VPN"; + default: + ASSERT(false); + return std::string(); + } +} } // namespace +std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix, + int prefix_length) { + std::ostringstream ost; + ost << name << "%" << prefix.ToString() << "/" << prefix_length; + return ost.str(); +} + NetworkManager::NetworkManager() { } @@ -178,6 +217,29 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, } } networks_ = merged_list; + + // If the network lists changes, we resort it. + if (changed) { + std::sort(networks_.begin(), networks_.end(), SortNetworks); + // Now network interfaces are sorted, we should set the preference value + // for each of the interfaces we are planning to use. + // Preference order of network interfaces might have changed from previous + // sorting due to addition of higher preference network interface. + // Since we have already sorted the network interfaces based on our + // requirements, we will just assign a preference value starting with 127, + // in decreasing order. + int pref = kHighestNetworkPreference; + for (NetworkList::const_iterator iter = networks_.begin(); + iter != networks_.end(); ++iter) { + (*iter)->set_preference(pref); + if (pref > 0) { + --pref; + } else { + LOG(LS_ERROR) << "Too many network interfaces to handle!"; + break; + } + } + } } BasicNetworkManager::BasicNetworkManager() @@ -188,7 +250,16 @@ BasicNetworkManager::BasicNetworkManager() BasicNetworkManager::~BasicNetworkManager() { } -#if defined(POSIX) +#if defined(__native_client__) + +bool BasicNetworkManager::CreateNetworks(bool include_ignored, + NetworkList* networks) const { + ASSERT(false); + LOG(LS_WARNING) << "BasicNetworkManager doesn't work on NaCl yet"; + return false; +} + +#elif defined(POSIX) void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, bool include_ignored, NetworkList* networks) const { @@ -229,6 +300,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, continue; } } + int prefix_length = CountIPMaskBits(mask); prefix = TruncateIP(ip, prefix_length); std::string key = MakeNetworkKey(std::string(cursor->ifa_name), @@ -375,6 +447,7 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, continue; } } + IPAddress prefix; int prefix_length = GetPrefix(prefixlist, ip, &prefix); std::string key = MakeNetworkKey(name, prefix, prefix_length); @@ -553,9 +626,17 @@ void BasicNetworkManager::DumpNetworks(bool include_ignored) { Network::Network(const std::string& name, const std::string& desc, const IPAddress& prefix, int prefix_length) : name_(name), description_(desc), prefix_(prefix), - prefix_length_(prefix_length), scope_id_(0), ignored_(false), - uniform_numerator_(0), uniform_denominator_(0), exponential_numerator_(0), - exponential_denominator_(0) { + prefix_length_(prefix_length), + key_(MakeNetworkKey(name, prefix, prefix_length)), scope_id_(0), + ignored_(false), type_(ADAPTER_TYPE_UNKNOWN), preference_(0) { +} + +Network::Network(const std::string& name, const std::string& desc, + const IPAddress& prefix, int prefix_length, AdapterType type) + : name_(name), description_(desc), prefix_(prefix), + prefix_length_(prefix_length), + key_(MakeNetworkKey(name, prefix, prefix_length)), scope_id_(0), + ignored_(false), type_(type), preference_(0) { } std::string Network::ToString() const { @@ -563,7 +644,8 @@ std::string Network::ToString() const { // Print out the first space-terminated token of the network desc, plus // the IP address. ss << "Net[" << description_.substr(0, description_.find(' ')) - << ":" << prefix_.ToSensitiveString() << "/" << prefix_length_ << "]"; + << ":" << prefix_.ToSensitiveString() << "/" << prefix_length_ + << ":" << AdapterTypeToString(type_) << "]"; return ss.str(); } @@ -589,4 +671,5 @@ bool Network::SetIPs(const std::vector<IPAddress>& ips, bool changed) { ips_ = ips; return changed; } + } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/network.h b/chromium/third_party/libjingle/source/talk/base/network.h index 63f3e732fd9..2be81bb1c8d 100644 --- a/chromium/third_party/libjingle/source/talk/base/network.h +++ b/chromium/third_party/libjingle/source/talk/base/network.h @@ -45,9 +45,23 @@ struct ifaddrs; namespace talk_base { class Network; -class NetworkSession; class Thread; +enum AdapterType { + // This enum resembles the one in Chromium net::ConnectionType. + ADAPTER_TYPE_UNKNOWN = 0, + ADAPTER_TYPE_ETHERNET = 1, + ADAPTER_TYPE_WIFI = 2, + ADAPTER_TYPE_CELLULAR = 3, + ADAPTER_TYPE_VPN = 4 +}; + +// Makes a string key for this network. Used in the network manager's maps. +// Network objects are keyed on interface name, network prefix and the +// length of that prefix. +std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix, + int prefix_length); + // Generic network manager interface. It provides list of local // networks. class NetworkManager { @@ -168,10 +182,12 @@ class BasicNetworkManager : public NetworkManagerBase, // Represents a Unix-type network interface, with a name and single address. class Network { public: - Network() : prefix_(INADDR_ANY), scope_id_(0) {} Network(const std::string& name, const std::string& description, const IPAddress& prefix, int prefix_length); + Network(const std::string& name, const std::string& description, + const IPAddress& prefix, int prefix_length, AdapterType type); + // Returns the name of the interface this network is associated wtih. const std::string& name() const { return name_; } @@ -184,6 +200,10 @@ class Network { // Returns the length, in bits, of this network's prefix. int prefix_length() const { return prefix_length_; } + // |key_| has unique value per network interface. Used in sorting network + // interfaces. Key is derived from interface name and it's prefix. + std::string key() const { return key_; } + // Returns the Network's current idea of the 'best' IP it has. // 'Best' currently means the first one added. // TODO: We should be preferring temporary addresses. @@ -215,27 +235,28 @@ class Network { bool ignored() const { return ignored_; } void set_ignored(bool ignored) { ignored_ = ignored; } + AdapterType type() const { return type_; } + int preference() const { return preference_; } + void set_preference(int preference) { preference_ = preference; } + // Debugging description of this network std::string ToString() const; private: - typedef std::vector<NetworkSession*> SessionList; - std::string name_; std::string description_; IPAddress prefix_; int prefix_length_; + std::string key_; std::vector<IPAddress> ips_; int scope_id_; bool ignored_; - SessionList sessions_; - double uniform_numerator_; - double uniform_denominator_; - double exponential_numerator_; - double exponential_denominator_; + AdapterType type_; + int preference_; friend class NetworkManager; }; + } // namespace talk_base #endif // TALK_BASE_NETWORK_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/network_unittest.cc b/chromium/third_party/libjingle/source/talk/base/network_unittest.cc index e11e78daa4a..56b11c617aa 100644 --- a/chromium/third_party/libjingle/source/talk/base/network_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/network_unittest.cc @@ -527,6 +527,51 @@ TEST_F(NetworkTest, TestIPv6Toggle) { } } +TEST_F(NetworkTest, TestNetworkListSorting) { + BasicNetworkManager manager; + Network ipv4_network1("test_eth0", "Test Network Adapter 1", + IPAddress(0x12345600U), 24); + ipv4_network1.AddIP(IPAddress(0x12345600U)); + + IPAddress ip; + IPAddress prefix; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:abcd:efab:cdef", &ip)); + prefix = TruncateIP(ip, 64); + Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 2", + prefix, 64); + ipv6_eth1_publicnetwork1_ip1.AddIP(ip); + + NetworkManager::NetworkList list; + list.push_back(new Network(ipv4_network1)); + list.push_back(new Network(ipv6_eth1_publicnetwork1_ip1)); + Network* net1 = list[0]; + Network* net2 = list[1]; + + bool changed = false; + MergeNetworkList(manager, list, &changed); + ASSERT_TRUE(changed); + // After sorting IPv6 network should be higher order than IPv4 networks. + EXPECT_TRUE(net1->preference() < net2->preference()); +} + +TEST_F(NetworkTest, TestNetworkAdapterTypes) { + Network wifi("wlan0", "Wireless Adapter", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_WIFI); + EXPECT_EQ(ADAPTER_TYPE_WIFI, wifi.type()); + Network ethernet("eth0", "Ethernet", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_ETHERNET); + EXPECT_EQ(ADAPTER_TYPE_ETHERNET, ethernet.type()); + Network cellular("test_cell", "Cellular Adapter", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_CELLULAR); + EXPECT_EQ(ADAPTER_TYPE_CELLULAR, cellular.type()); + Network vpn("bridge_test", "VPN Adapter", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_VPN); + EXPECT_EQ(ADAPTER_TYPE_VPN, vpn.type()); + Network unknown("test", "Test Adapter", IPAddress(0x12345600U), 24, + ADAPTER_TYPE_UNKNOWN); + EXPECT_EQ(ADAPTER_TYPE_UNKNOWN, unknown.type()); +} + #if defined(POSIX) // Verify that we correctly handle interfaces with no address. TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) { diff --git a/chromium/third_party/libjingle/source/talk/base/nssidentity.cc b/chromium/third_party/libjingle/source/talk/base/nssidentity.cc index 053035e562e..a0cd8b217bb 100644 --- a/chromium/third_party/libjingle/source/talk/base/nssidentity.cc +++ b/chromium/third_party/libjingle/source/talk/base/nssidentity.cc @@ -48,9 +48,16 @@ #include "talk/base/logging.h" #include "talk/base/helpers.h" #include "talk/base/nssstreamadapter.h" +#include "talk/base/safe_conversions.h" namespace talk_base { +// Certificate validity lifetime in seconds. +static const int CERTIFICATE_LIFETIME = 60*60*24*30; // 30 days, arbitrarily +// Certificate validity window in seconds. +// This is to compensate for slightly incorrect system clocks. +static const int CERTIFICATE_WINDOW = -60*60*24; + NSSKeyPair::~NSSKeyPair() { if (privkey_) SECKEY_DestroyPrivateKey(privkey_); @@ -137,7 +144,7 @@ NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) { SECItem der_cert; der_cert.data = reinterpret_cast<unsigned char *>(const_cast<char *>( der.data())); - der_cert.len = der.size(); + der_cert.len = checked_cast<unsigned int>(der.size()); CERTCertificate *cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE); @@ -163,8 +170,49 @@ void NSSCertificate::ToDER(Buffer* der_buffer) const { der_buffer->SetData(certificate_->derCert.data, certificate_->derCert.len); } -bool NSSCertificate::GetDigestLength(const std::string &algorithm, - std::size_t *length) { +static bool Certifies(CERTCertificate* parent, CERTCertificate* child) { + // TODO(bemasc): Identify stricter validation checks to use here. In the + // context of some future identity standard, it might make sense to check + // the certificates' roles, expiration dates, self-signatures (if + // self-signed), certificate transparency logging, or many other attributes. + // NOTE: Future changes to this validation may reject some previously allowed + // certificate chains. Users should be advised not to deploy chained + // certificates except in controlled environments until the validity + // requirements are finalized. + + // Check that the parent's name is the same as the child's claimed issuer. + SECComparison name_status = + CERT_CompareName(&child->issuer, &parent->subject); + if (name_status != SECEqual) + return false; + + // Extract the parent's public key, or fail if the key could not be read + // (e.g. certificate is corrupted). + SECKEYPublicKey* parent_key = CERT_ExtractPublicKey(parent); + if (!parent_key) + return false; + + // Check that the parent's privkey was actually used to generate the child's + // signature. + SECStatus verified = CERT_VerifySignedDataWithPublicKey( + &child->signatureWrap, parent_key, NULL); + SECKEY_DestroyPublicKey(parent_key); + return verified == SECSuccess; +} + +bool NSSCertificate::IsValidChain(const CERTCertList* cert_list) { + CERTCertListNode* child = CERT_LIST_HEAD(cert_list); + for (CERTCertListNode* parent = CERT_LIST_NEXT(child); + !CERT_LIST_END(parent, cert_list); + child = parent, parent = CERT_LIST_NEXT(parent)) { + if (!Certifies(parent->cert, child->cert)) + return false; + } + return true; +} + +bool NSSCertificate::GetDigestLength(const std::string& algorithm, + size_t* length) { const SECHashObject *ho; if (!GetDigestObject(algorithm, &ho)) @@ -223,9 +271,10 @@ bool NSSCertificate::GetSignatureDigestAlgorithm(std::string* algorithm) const { return true; } -bool NSSCertificate::ComputeDigest(const std::string &algorithm, - unsigned char *digest, std::size_t size, - std::size_t *length) const { +bool NSSCertificate::ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const { const SECHashObject *ho; if (!GetDigestObject(algorithm, &ho)) @@ -299,23 +348,25 @@ bool NSSCertificate::GetDigestObject(const std::string &algorithm, } -NSSIdentity *NSSIdentity::Generate(const std::string &common_name) { - std::string subject_name_string = "CN=" + common_name; +NSSIdentity* NSSIdentity::GenerateInternal(const SSLIdentityParams& params) { + std::string subject_name_string = "CN=" + params.common_name; CERTName *subject_name = CERT_AsciiToName( const_cast<char *>(subject_name_string.c_str())); NSSIdentity *identity = NULL; CERTSubjectPublicKeyInfo *spki = NULL; CERTCertificateRequest *certreq = NULL; - CERTValidity *validity; + CERTValidity *validity = NULL; CERTCertificate *certificate = NULL; NSSKeyPair *keypair = NSSKeyPair::Generate(); SECItem inner_der; SECStatus rv; PLArenaPool* arena; SECItem signed_cert; - PRTime not_before, not_after; PRTime now = PR_Now(); - PRTime one_day; + PRTime not_before = + now + static_cast<PRTime>(params.not_before) * PR_USEC_PER_SEC; + PRTime not_after = + now + static_cast<PRTime>(params.not_after) * PR_USEC_PER_SEC; inner_der.len = 0; inner_der.data = NULL; @@ -342,11 +393,6 @@ NSSIdentity *NSSIdentity::Generate(const std::string &common_name) { goto fail; } - one_day = 86400; - one_day *= PR_USEC_PER_SEC; - not_before = now - one_day; - not_after = now + 30 * one_day; - validity = CERT_CreateValidity(not_before, not_after); if (!validity) { LOG(LS_ERROR) << "Couldn't create validity"; @@ -408,6 +454,18 @@ NSSIdentity *NSSIdentity::Generate(const std::string &common_name) { return identity; } +NSSIdentity* NSSIdentity::Generate(const std::string &common_name) { + SSLIdentityParams params; + params.common_name = common_name; + params.not_before = CERTIFICATE_WINDOW; + params.not_after = CERTIFICATE_LIFETIME; + return GenerateInternal(params); +} + +NSSIdentity* NSSIdentity::GenerateForTest(const SSLIdentityParams& params) { + return GenerateInternal(params); +} + SSLIdentity* NSSIdentity::FromPEMStrings(const std::string& private_key, const std::string& certificate) { std::string private_key_der; @@ -416,10 +474,9 @@ SSLIdentity* NSSIdentity::FromPEMStrings(const std::string& private_key, return NULL; SECItem private_key_item; - private_key_item.data = - reinterpret_cast<unsigned char *>( - const_cast<char *>(private_key_der.c_str())); - private_key_item.len = private_key_der.size(); + private_key_item.data = reinterpret_cast<unsigned char *>( + const_cast<char *>(private_key_der.c_str())); + private_key_item.len = checked_cast<unsigned int>(private_key_der.size()); const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | KU_DIGITAL_SIGNATURE; diff --git a/chromium/third_party/libjingle/source/talk/base/nssidentity.h b/chromium/third_party/libjingle/source/talk/base/nssidentity.h index 3f97ebbb619..b4376d521a6 100644 --- a/chromium/third_party/libjingle/source/talk/base/nssidentity.h +++ b/chromium/third_party/libjingle/source/talk/base/nssidentity.h @@ -84,16 +84,21 @@ class NSSCertificate : public SSLCertificate { virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const; virtual bool ComputeDigest(const std::string& algorithm, - unsigned char* digest, std::size_t size, - std::size_t* length) const; + unsigned char* digest, + size_t size, + size_t* length) const; virtual bool GetChain(SSLCertChain** chain) const; CERTCertificate* certificate() { return certificate_; } + // Performs minimal checks to determine if the list is a valid chain. This + // only checks that each certificate certifies the preceding certificate, + // and ignores many other certificate features such as expiration dates. + static bool IsValidChain(const CERTCertList* cert_list); + // Helper function to get the length of a digest - static bool GetDigestLength(const std::string& algorithm, - std::size_t* length); + static bool GetDigestLength(const std::string& algorithm, size_t* length); // Comparison. Only the certificate itself is considered, not the chain. bool Equals(const NSSCertificate* tocompare) const; @@ -113,6 +118,7 @@ class NSSCertificate : public SSLCertificate { class NSSIdentity : public SSLIdentity { public: static NSSIdentity* Generate(const std::string& common_name); + static NSSIdentity* GenerateForTest(const SSLIdentityParams& params); static SSLIdentity* FromPEMStrings(const std::string& private_key, const std::string& certificate); virtual ~NSSIdentity() { @@ -128,6 +134,8 @@ class NSSIdentity : public SSLIdentity { NSSIdentity(NSSKeyPair* keypair, NSSCertificate* cert) : keypair_(keypair), certificate_(cert) {} + static NSSIdentity* GenerateInternal(const SSLIdentityParams& params); + talk_base::scoped_ptr<NSSKeyPair> keypair_; talk_base::scoped_ptr<NSSCertificate> certificate_; diff --git a/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.cc b/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.cc index 185c243f5e5..60fa738b3c1 100644 --- a/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.cc +++ b/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.cc @@ -53,6 +53,7 @@ #endif #include "talk/base/nssidentity.h" +#include "talk/base/safe_conversions.h" #include "talk/base/thread.h" namespace talk_base { @@ -86,7 +87,8 @@ static const SrtpCipherMapEntry kSrtpCipherMap[] = { // Implementation of NSPR methods static PRStatus StreamClose(PRFileDesc *socket) { - // Noop + ASSERT(!socket->lower); + socket->dtor(socket); return PR_SUCCESS; } @@ -96,7 +98,7 @@ static PRInt32 StreamRead(PRFileDesc *socket, void *buf, PRInt32 length) { int error; StreamResult result = stream->Read(buf, length, &read, &error); if (result == SR_SUCCESS) { - return read; + return checked_cast<PRInt32>(read); } if (result == SR_EOS) { @@ -119,7 +121,7 @@ static PRInt32 StreamWrite(PRFileDesc *socket, const void *buf, int error; StreamResult result = stream->Write(buf, length, &written, &error); if (result == SR_SUCCESS) { - return written; + return checked_cast<PRInt32>(written); } if (result == SR_BLOCK) { @@ -437,7 +439,7 @@ bool NSSStreamAdapter::Init() { LOG(LS_ERROR) << "Error disabling false start"; return false; } - + ssl_fd_ = ssl_fd; return true; @@ -515,7 +517,7 @@ int NSSStreamAdapter::BeginSSL() { SSL_LIBRARY_VERSION_TLS_1_1 : SSL_LIBRARY_VERSION_TLS_1_0; vrange.max = SSL_LIBRARY_VERSION_TLS_1_1; - + rv = SSL_VersionRangeSet(ssl_fd_, &vrange); if (rv != SECSuccess) { Error("BeginSSL", -1, false); @@ -525,7 +527,9 @@ int NSSStreamAdapter::BeginSSL() { // SRTP #ifdef HAVE_DTLS_SRTP if (!srtp_ciphers_.empty()) { - rv = SSL_SetSRTPCiphers(ssl_fd_, &srtp_ciphers_[0], srtp_ciphers_.size()); + rv = SSL_SetSRTPCiphers( + ssl_fd_, &srtp_ciphers_[0], + checked_cast<unsigned int>(srtp_ciphers_.size())); if (rv != SECSuccess) { Error("BeginSSL", -1, false); return -1; @@ -644,7 +648,7 @@ StreamResult NSSStreamAdapter::Read(void* data, size_t data_len, return SR_ERROR; } - PRInt32 rv = PR_Read(ssl_fd_, data, data_len); + PRInt32 rv = PR_Read(ssl_fd_, data, checked_cast<PRInt32>(data_len)); if (rv == 0) { return SR_EOS; @@ -681,7 +685,7 @@ StreamResult NSSStreamAdapter::Write(const void* data, size_t data_len, case SSL_CONNECTED: break; - + case SSL_ERROR: case SSL_CLOSED: default: @@ -690,7 +694,7 @@ StreamResult NSSStreamAdapter::Write(const void* data, size_t data_len, return SR_ERROR; } - PRInt32 rv = PR_Write(ssl_fd_, data, data_len); + PRInt32 rv = PR_Write(ssl_fd_, data, checked_cast<PRInt32>(data_len)); // Error if (rv < 0) { @@ -780,12 +784,33 @@ SECStatus NSSStreamAdapter::AuthCertificateHook(void *arg, PRBool checksig, PRBool isServer) { LOG(LS_INFO) << "NSSStreamAdapter::AuthCertificateHook"; - NSSCertificate peer_cert(SSL_PeerCertificate(fd)); - bool ok = false; + // SSL_PeerCertificate returns a pointer that is owned by the caller, and + // the NSSCertificate constructor copies its argument, so |raw_peer_cert| + // must be destroyed in this function. + CERTCertificate* raw_peer_cert = SSL_PeerCertificate(fd); + NSSCertificate peer_cert(raw_peer_cert); + CERT_DestroyCertificate(raw_peer_cert); - // TODO(ekr@rtfm.com): Should we be enforcing self-signed like - // the OpenSSL version? NSSStreamAdapter *stream = reinterpret_cast<NSSStreamAdapter *>(arg); + stream->cert_ok_ = false; + + // Read the peer's certificate chain. + CERTCertList* cert_list = SSL_PeerCertificateChain(fd); + ASSERT(cert_list != NULL); + + // If the peer provided multiple certificates, check that they form a valid + // chain as defined by RFC 5246 Section 7.4.2: "Each following certificate + // MUST directly certify the one preceding it.". This check does NOT + // verify other requirements, such as whether the chain reaches a trusted + // root, self-signed certificates have valid signatures, certificates are not + // expired, etc. + // Even if the chain is valid, the leaf certificate must still match a + // provided certificate or digest. + if (!NSSCertificate::IsValidChain(cert_list)) { + CERT_DestroyCertList(cert_list); + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } if (stream->peer_certificate_.get()) { LOG(LS_INFO) << "Checking against specified certificate"; @@ -794,13 +819,13 @@ SECStatus NSSStreamAdapter::AuthCertificateHook(void *arg, if (reinterpret_cast<NSSCertificate *>(stream->peer_certificate_.get())-> Equals(&peer_cert)) { LOG(LS_INFO) << "Accepted peer certificate"; - ok = true; + stream->cert_ok_ = true; } } else if (!stream->peer_certificate_digest_algorithm_.empty()) { LOG(LS_INFO) << "Checking against specified digest"; // The peer certificate digest was specified unsigned char digest[64]; // Maximum size - std::size_t digest_length; + size_t digest_length; if (!peer_cert.ComputeDigest( stream->peer_certificate_digest_algorithm_, @@ -810,7 +835,7 @@ SECStatus NSSStreamAdapter::AuthCertificateHook(void *arg, Buffer computed_digest(digest, digest_length); if (computed_digest == stream->peer_certificate_digest_value_) { LOG(LS_INFO) << "Accepted peer certificate"; - ok = true; + stream->cert_ok_ = true; } } } else { @@ -819,23 +844,18 @@ SECStatus NSSStreamAdapter::AuthCertificateHook(void *arg, UNIMPLEMENTED; } - if (ok) { + if (!stream->cert_ok_ && stream->ignore_bad_cert()) { + LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain"; stream->cert_ok_ = true; + } - // Record the peer's certificate chain. - CERTCertList* cert_list = SSL_PeerCertificateChain(fd); - ASSERT(cert_list != NULL); - + if (stream->cert_ok_) stream->peer_certificate_.reset(new NSSCertificate(cert_list)); - CERT_DestroyCertList(cert_list); - return SECSuccess; - } - if (!ok && stream->ignore_bad_cert()) { - LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain"; - stream->cert_ok_ = true; + CERT_DestroyCertList(cert_list); + + if (stream->cert_ok_) return SECSuccess; - } PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); return SECFailure; @@ -869,11 +889,15 @@ bool NSSStreamAdapter::ExportKeyingMaterial(const std::string& label, bool use_context, uint8* result, size_t result_len) { - SECStatus rv = SSL_ExportKeyingMaterial(ssl_fd_, - label.c_str(), label.size(), - use_context, - context, context_len, - result, result_len); + SECStatus rv = SSL_ExportKeyingMaterial( + ssl_fd_, + label.c_str(), + checked_cast<unsigned int>(label.size()), + use_context, + context, + checked_cast<unsigned int>(context_len), + result, + checked_cast<unsigned int>(result_len)); return rv == SECSuccess; } diff --git a/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.h b/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.h index 219f6193f07..3919c5a1571 100644 --- a/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.h +++ b/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.h @@ -103,8 +103,7 @@ class NSSStreamAdapter : public SSLStreamAdapterHelper { // Override SSLStreamAdapterHelper virtual int BeginSSL(); virtual void Cleanup(); - virtual bool GetDigestLength(const std::string &algorithm, - std::size_t *length) { + virtual bool GetDigestLength(const std::string& algorithm, size_t* length) { return NSSCertificate::GetDigestLength(algorithm, length); } diff --git a/chromium/third_party/libjingle/source/talk/base/openssl.h b/chromium/third_party/libjingle/source/talk/base/openssl.h new file mode 100644 index 00000000000..d9628a76368 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/openssl.h @@ -0,0 +1,37 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_BASE_OPENSSL_H_ +#define TALK_BASE_OPENSSL_H_ + +#include <openssl/ssl.h> + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) +#error OpenSSL is older than 1.0.0, which is the minimum supported version. +#endif + +#endif // TALK_BASE_OPENSSL_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/openssladapter.cc b/chromium/third_party/libjingle/source/talk/base/openssladapter.cc index af92f0c4534..9e6fe72c244 100644 --- a/chromium/third_party/libjingle/source/talk/base/openssladapter.cc +++ b/chromium/third_party/libjingle/source/talk/base/openssladapter.cc @@ -41,7 +41,6 @@ #include <openssl/err.h> #include <openssl/opensslv.h> #include <openssl/rand.h> -#include <openssl/ssl.h> #include <openssl/x509v3.h> #if HAVE_CONFIG_H @@ -50,6 +49,7 @@ #include "talk/base/common.h" #include "talk/base/logging.h" +#include "talk/base/openssl.h" #include "talk/base/sslroots.h" #include "talk/base/stringutils.h" @@ -62,9 +62,7 @@ #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE) #define MUTEX_UNLOCK(x) ReleaseMutex(x) #define THREAD_ID GetCurrentThreadId() -#elif defined(_POSIX_THREADS) - // _POSIX_THREADS is normally defined in unistd.h if pthreads are available - // on your platform. +#elif defined(POSIX) #define MUTEX_TYPE pthread_mutex_t #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) @@ -690,11 +688,7 @@ bool OpenSSLAdapter::VerifyServerName(SSL* ssl, const char* host, int extension_nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension)); if (extension_nid == NID_subject_alt_name) { -#if OPENSSL_VERSION_NUMBER >= 0x10000000L const X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension); -#else - X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension); -#endif if (!meth) break; @@ -705,12 +699,8 @@ bool OpenSSLAdapter::VerifyServerName(SSL* ssl, const char* host, // See http://readlist.com/lists/openssl.org/openssl-users/0/4761.html. unsigned char* ext_value_data = extension->value->data; -#if OPENSSL_VERSION_NUMBER >= 0x0090800fL const unsigned char **ext_value_data_ptr = (const_cast<const unsigned char **>(&ext_value_data)); -#else - unsigned char **ext_value_data_ptr = &ext_value_data; -#endif if (meth->it) { ext_str = ASN1_item_d2i(NULL, ext_value_data_ptr, diff --git a/chromium/third_party/libjingle/source/talk/base/openssldigest.cc b/chromium/third_party/libjingle/source/talk/base/openssldigest.cc index 3d9276de844..3d0d227e67c 100644 --- a/chromium/third_party/libjingle/source/talk/base/openssldigest.cc +++ b/chromium/third_party/libjingle/source/talk/base/openssldigest.cc @@ -30,6 +30,7 @@ #include "talk/base/openssldigest.h" #include "talk/base/common.h" +#include "talk/base/openssl.h" namespace talk_base { @@ -78,7 +79,6 @@ bool OpenSSLDigest::GetDigestEVP(const std::string& algorithm, md = EVP_md5(); } else if (algorithm == DIGEST_SHA_1) { md = EVP_sha1(); -#if OPENSSL_VERSION_NUMBER >= 0x00908000L } else if (algorithm == DIGEST_SHA_224) { md = EVP_sha224(); } else if (algorithm == DIGEST_SHA_256) { @@ -87,7 +87,6 @@ bool OpenSSLDigest::GetDigestEVP(const std::string& algorithm, md = EVP_sha384(); } else if (algorithm == DIGEST_SHA_512) { md = EVP_sha512(); -#endif } else { return false; } @@ -108,7 +107,6 @@ bool OpenSSLDigest::GetDigestName(const EVP_MD* md, *algorithm = DIGEST_MD5; } else if (md_type == NID_sha1) { *algorithm = DIGEST_SHA_1; -#if OPENSSL_VERSION_NUMBER >= 0x00908000L } else if (md_type == NID_sha224) { *algorithm = DIGEST_SHA_224; } else if (md_type == NID_sha256) { @@ -117,7 +115,6 @@ bool OpenSSLDigest::GetDigestName(const EVP_MD* md, *algorithm = DIGEST_SHA_384; } else if (md_type == NID_sha512) { *algorithm = DIGEST_SHA_512; -#endif } else { algorithm->clear(); return false; diff --git a/chromium/third_party/libjingle/source/talk/base/opensslidentity.cc b/chromium/third_party/libjingle/source/talk/base/opensslidentity.cc index 4ff76016183..a58f83967e8 100644 --- a/chromium/third_party/libjingle/source/talk/base/opensslidentity.cc +++ b/chromium/third_party/libjingle/source/talk/base/opensslidentity.cc @@ -32,7 +32,6 @@ // Must be included first before openssl headers. #include "talk/base/win32.h" // NOLINT -#include <openssl/ssl.h> #include <openssl/bio.h> #include <openssl/err.h> #include <openssl/pem.h> @@ -43,6 +42,7 @@ #include "talk/base/checks.h" #include "talk/base/helpers.h" #include "talk/base/logging.h" +#include "talk/base/openssl.h" #include "talk/base/openssldigest.h" namespace talk_base { @@ -57,7 +57,7 @@ static const int KEY_LENGTH = 1024; static const int SERIAL_RAND_BITS = 64; // Certificate validity lifetime -static const int CERTIFICATE_LIFETIME = 60*60*24*365; // one year, arbitrarily +static const int CERTIFICATE_LIFETIME = 60*60*24*30; // 30 days, arbitrarily // Certificate validity window. // This is to compensate for slightly incorrect system clocks. static const int CERTIFICATE_WINDOW = -60*60*24; @@ -66,15 +66,6 @@ static const int CERTIFICATE_WINDOW = -60*60*24; static EVP_PKEY* MakeKey() { LOG(LS_INFO) << "Making key pair"; EVP_PKEY* pkey = EVP_PKEY_new(); -#if OPENSSL_VERSION_NUMBER < 0x00908000l - // Only RSA_generate_key is available. Use that. - RSA* rsa = RSA_generate_key(KEY_LENGTH, 0x10001, NULL, NULL); - if (!EVP_PKEY_assign_RSA(pkey, rsa)) { - EVP_PKEY_free(pkey); - RSA_free(rsa); - return NULL; - } -#else // RSA_generate_key is deprecated. Use _ex version. BIGNUM* exponent = BN_new(); RSA* rsa = RSA_new(); @@ -89,15 +80,14 @@ static EVP_PKEY* MakeKey() { } // ownership of rsa struct was assigned, don't free it. BN_free(exponent); -#endif LOG(LS_INFO) << "Returning key pair"; return pkey; } // Generate a self-signed certificate, with the public key from the // given key pair. Caller is responsible for freeing the returned object. -static X509* MakeCertificate(EVP_PKEY* pkey, const char* common_name) { - LOG(LS_INFO) << "Making certificate for " << common_name; +static X509* MakeCertificate(EVP_PKEY* pkey, const SSLIdentityParams& params) { + LOG(LS_INFO) << "Making certificate for " << params.common_name; X509* x509 = NULL; BIGNUM* serial_number = NULL; X509_NAME* name = NULL; @@ -128,14 +118,15 @@ static X509* MakeCertificate(EVP_PKEY* pkey, const char* common_name) { // clear during SSL negotiation, so there may be a privacy issue in // putting anything recognizable here. if ((name = X509_NAME_new()) == NULL || - !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_UTF8, - (unsigned char*)common_name, -1, -1, 0) || + !X509_NAME_add_entry_by_NID( + name, NID_commonName, MBSTRING_UTF8, + (unsigned char*)params.common_name.c_str(), -1, -1, 0) || !X509_set_subject_name(x509, name) || !X509_set_issuer_name(x509, name)) goto error; - if (!X509_gmtime_adj(X509_get_notBefore(x509), CERTIFICATE_WINDOW) || - !X509_gmtime_adj(X509_get_notAfter(x509), CERTIFICATE_LIFETIME)) + if (!X509_gmtime_adj(X509_get_notBefore(x509), params.not_before) || + !X509_gmtime_adj(X509_get_notAfter(x509), params.not_after)) goto error; if (!X509_sign(x509, pkey, EVP_sha1())) @@ -199,12 +190,13 @@ static void PrintCert(X509* x509) { #endif OpenSSLCertificate* OpenSSLCertificate::Generate( - OpenSSLKeyPair* key_pair, const std::string& common_name) { - std::string actual_common_name = common_name; - if (actual_common_name.empty()) + OpenSSLKeyPair* key_pair, const SSLIdentityParams& params) { + SSLIdentityParams actual_params(params); + if (actual_params.common_name.empty()) { // Use a random string, arbitrarily 8chars long. - actual_common_name = CreateRandomString(8); - X509* x509 = MakeCertificate(key_pair->pkey(), actual_common_name.c_str()); + actual_params.common_name = CreateRandomString(8); + } + X509* x509 = MakeCertificate(key_pair->pkey(), actual_params); if (!x509) { LogSSLErrors("Generating certificate"); return NULL; @@ -222,11 +214,11 @@ OpenSSLCertificate* OpenSSLCertificate::FromPEMString( BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1); if (!bio) return NULL; - (void)BIO_set_close(bio, BIO_NOCLOSE); BIO_set_mem_eof_return(bio, 0); X509 *x509 = PEM_read_bio_X509(bio, NULL, NULL, const_cast<char*>("\0")); - BIO_free(bio); + BIO_free(bio); // Frees the BIO, but not the pointed-to string. + if (!x509) return NULL; @@ -243,18 +235,18 @@ bool OpenSSLCertificate::GetSignatureDigestAlgorithm( EVP_get_digestbyobj(x509_->sig_alg->algorithm), algorithm); } -bool OpenSSLCertificate::ComputeDigest(const std::string &algorithm, - unsigned char *digest, - std::size_t size, - std::size_t *length) const { +bool OpenSSLCertificate::ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const { return ComputeDigest(x509_, algorithm, digest, size, length); } -bool OpenSSLCertificate::ComputeDigest(const X509 *x509, - const std::string &algorithm, - unsigned char *digest, - std::size_t size, - std::size_t *length) { +bool OpenSSLCertificate::ComputeDigest(const X509* x509, + const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) { const EVP_MD *md; unsigned int n; @@ -320,11 +312,12 @@ void OpenSSLCertificate::AddReference() const { CRYPTO_add(&x509_->references, 1, CRYPTO_LOCK_X509); } -OpenSSLIdentity* OpenSSLIdentity::Generate(const std::string& common_name) { +OpenSSLIdentity* OpenSSLIdentity::GenerateInternal( + const SSLIdentityParams& params) { OpenSSLKeyPair *key_pair = OpenSSLKeyPair::Generate(); if (key_pair) { - OpenSSLCertificate *certificate = - OpenSSLCertificate::Generate(key_pair, common_name); + OpenSSLCertificate *certificate = OpenSSLCertificate::Generate( + key_pair, params); if (certificate) return new OpenSSLIdentity(key_pair, certificate); delete key_pair; @@ -333,6 +326,19 @@ OpenSSLIdentity* OpenSSLIdentity::Generate(const std::string& common_name) { return NULL; } +OpenSSLIdentity* OpenSSLIdentity::Generate(const std::string& common_name) { + SSLIdentityParams params; + params.common_name = common_name; + params.not_before = CERTIFICATE_WINDOW; + params.not_after = CERTIFICATE_LIFETIME; + return GenerateInternal(params); +} + +OpenSSLIdentity* OpenSSLIdentity::GenerateForTest( + const SSLIdentityParams& params) { + return GenerateInternal(params); +} + SSLIdentity* OpenSSLIdentity::FromPEMStrings( const std::string& private_key, const std::string& certificate) { @@ -348,11 +354,10 @@ SSLIdentity* OpenSSLIdentity::FromPEMStrings( LOG(LS_ERROR) << "Failed to create a new BIO buffer."; return NULL; } - (void)BIO_set_close(bio, BIO_NOCLOSE); BIO_set_mem_eof_return(bio, 0); EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, const_cast<char*>("\0")); - BIO_free(bio); + BIO_free(bio); // Frees the BIO, but not the pointed-to string. if (!pkey) { LOG(LS_ERROR) << "Failed to create the private key from PEM string."; @@ -376,5 +381,3 @@ bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) { } // namespace talk_base #endif // HAVE_OPENSSL_SSL_H - - diff --git a/chromium/third_party/libjingle/source/talk/base/opensslidentity.h b/chromium/third_party/libjingle/source/talk/base/opensslidentity.h index af18c5c4d09..84b28261e4d 100644 --- a/chromium/third_party/libjingle/source/talk/base/opensslidentity.h +++ b/chromium/third_party/libjingle/source/talk/base/opensslidentity.h @@ -78,7 +78,7 @@ class OpenSSLCertificate : public SSLCertificate { } static OpenSSLCertificate* Generate(OpenSSLKeyPair* key_pair, - const std::string& common_name); + const SSLIdentityParams& params); static OpenSSLCertificate* FromPEMString(const std::string& pem_string); virtual ~OpenSSLCertificate(); @@ -94,16 +94,17 @@ class OpenSSLCertificate : public SSLCertificate { virtual void ToDER(Buffer* der_buffer) const; // Compute the digest of the certificate given algorithm - virtual bool ComputeDigest(const std::string &algorithm, - unsigned char *digest, std::size_t size, - std::size_t *length) const; + virtual bool ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const; // Compute the digest of a certificate as an X509 * - static bool ComputeDigest(const X509 *x509, - const std::string &algorithm, - unsigned char *digest, - std::size_t size, - std::size_t *length); + static bool ComputeDigest(const X509* x509, + const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length); virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const; @@ -127,6 +128,7 @@ class OpenSSLCertificate : public SSLCertificate { class OpenSSLIdentity : public SSLIdentity { public: static OpenSSLIdentity* Generate(const std::string& common_name); + static OpenSSLIdentity* GenerateForTest(const SSLIdentityParams& params); static SSLIdentity* FromPEMStrings(const std::string& private_key, const std::string& certificate); virtual ~OpenSSLIdentity() { } @@ -151,6 +153,8 @@ class OpenSSLIdentity : public SSLIdentity { ASSERT(certificate != NULL); } + static OpenSSLIdentity* GenerateInternal(const SSLIdentityParams& params); + scoped_ptr<OpenSSLKeyPair> key_pair_; scoped_ptr<OpenSSLCertificate> certificate_; diff --git a/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.cc b/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.cc index 034dfcf9266..218f656be12 100644 --- a/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.cc +++ b/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.cc @@ -37,7 +37,6 @@ #include <openssl/crypto.h> #include <openssl/err.h> #include <openssl/rand.h> -#include <openssl/ssl.h> #include <openssl/x509v3.h> #include <vector> @@ -45,6 +44,7 @@ #include "talk/base/common.h" #include "talk/base/logging.h" #include "talk/base/stream.h" +#include "talk/base/openssl.h" #include "talk/base/openssladapter.h" #include "talk/base/openssldigest.h" #include "talk/base/opensslidentity.h" @@ -57,10 +57,6 @@ namespace talk_base { #define HAVE_DTLS_SRTP #endif -#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) -#define HAVE_DTLS -#endif - #ifdef HAVE_DTLS_SRTP // SRTP cipher suite table struct SrtpCipherMapEntry { @@ -210,13 +206,6 @@ void OpenSSLStreamAdapter::SetServerRole(SSLRole role) { role_ = role; } -void OpenSSLStreamAdapter::SetPeerCertificate(SSLCertificate* cert) { - ASSERT(!peer_certificate_); - ASSERT(peer_certificate_digest_algorithm_.empty()); - ASSERT(ssl_server_name_.empty()); - peer_certificate_.reset(static_cast<OpenSSLCertificate*>(cert)); -} - bool OpenSSLStreamAdapter::GetPeerCertificate(SSLCertificate** cert) const { if (!peer_certificate_) return false; @@ -274,12 +263,12 @@ bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label, bool OpenSSLStreamAdapter::SetDtlsSrtpCiphers( const std::vector<std::string>& ciphers) { +#ifdef HAVE_DTLS_SRTP std::string internal_ciphers; if (state_ != SSL_NONE) return false; -#ifdef HAVE_DTLS_SRTP for (std::vector<std::string>::const_iterator cipher = ciphers.begin(); cipher != ciphers.end(); ++cipher) { bool found = false; @@ -613,7 +602,6 @@ int OpenSSLStreamAdapter::BeginSSL() { // The underlying stream has open. If we are in peer-to-peer mode // then a peer certificate must have been specified by now. ASSERT(!ssl_server_name_.empty() || - peer_certificate_ || !peer_certificate_digest_algorithm_.empty()); LOG(LS_INFO) << "BeginSSL: " << (!ssl_server_name_.empty() ? ssl_server_name_ : @@ -661,9 +649,7 @@ int OpenSSLStreamAdapter::ContinueSSL() { case SSL_ERROR_NONE: LOG(LS_VERBOSE) << " -- success"; - if (!SSLPostConnectionCheck(ssl_, ssl_server_name_.c_str(), - peer_certificate_ ? - peer_certificate_->x509() : NULL, + if (!SSLPostConnectionCheck(ssl_, ssl_server_name_.c_str(), NULL, peer_certificate_digest_algorithm_)) { LOG(LS_ERROR) << "TLS post connection check failed"; return -1; @@ -675,14 +661,12 @@ int OpenSSLStreamAdapter::ContinueSSL() { case SSL_ERROR_WANT_READ: { LOG(LS_VERBOSE) << " -- error want read"; -#ifdef HAVE_DTLS struct timeval timeout; if (DTLSv1_get_timeout(ssl_, &timeout)) { int delay = timeout.tv_sec * 1000 + timeout.tv_usec/1000; Thread::Current()->PostDelayed(delay, this, MSG_TIMEOUT, 0); } -#endif } break; @@ -737,9 +721,7 @@ void OpenSSLStreamAdapter::OnMessage(Message* msg) { // Process our own messages and then pass others to the superclass if (MSG_TIMEOUT == msg->message_id) { LOG(LS_INFO) << "DTLS timeout expired"; -#ifdef HAVE_DTLS DTLSv1_handle_timeout(ssl_); -#endif ContinueSSL(); } else { StreamInterface::OnMessage(msg); @@ -750,19 +732,11 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { SSL_CTX *ctx = NULL; if (role_ == SSL_CLIENT) { -#ifdef HAVE_DTLS ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLSv1_client_method() : TLSv1_client_method()); -#else - ctx = SSL_CTX_new(TLSv1_client_method()); -#endif } else { -#ifdef HAVE_DTLS ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLSv1_server_method() : TLSv1_server_method()); -#else - ctx = SSL_CTX_new(TLSv1_server_method()); -#endif } if (ctx == NULL) return NULL; @@ -772,18 +746,6 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { return NULL; } - if (!peer_certificate_) { // traditional mode - // Add the root cert to the SSL context - if (!OpenSSLAdapter::ConfigureTrustedRootCertificates(ctx)) { - SSL_CTX_free(ctx); - return NULL; - } - } - - if (peer_certificate_ && role_ == SSL_SERVER) - // we must specify which client cert to ask for - SSL_CTX_add_client_CA(ctx, peer_certificate_->x509()); - #ifdef _DEBUG SSL_CTX_set_info_callback(ctx, OpenSSLAdapter::SSLInfoCallback); #endif @@ -806,88 +768,53 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { } int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) { -#if _DEBUG - if (!ok) { - char data[256]; - X509* cert = X509_STORE_CTX_get_current_cert(store); - int depth = X509_STORE_CTX_get_error_depth(store); - int err = X509_STORE_CTX_get_error(store); - - LOG(LS_INFO) << "Error with certificate at depth: " << depth; - X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data)); - LOG(LS_INFO) << " issuer = " << data; - X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data)); - LOG(LS_INFO) << " subject = " << data; - LOG(LS_INFO) << " err = " << err - << ":" << X509_verify_cert_error_string(err); - } -#endif - // Get our SSL structure from the store SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data( store, SSL_get_ex_data_X509_STORE_CTX_idx())); - OpenSSLStreamAdapter* stream = reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl)); - // In peer-to-peer mode, no root cert / certificate authority was - // specified, so the libraries knows of no certificate to accept, - // and therefore it will necessarily call here on the first cert it - // tries to verify. - if (!ok && stream->peer_certificate_) { - X509* cert = X509_STORE_CTX_get_current_cert(store); - int err = X509_STORE_CTX_get_error(store); - // peer-to-peer mode: allow the certificate to be self-signed, - // assuming it matches the cert that was specified. - if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && - X509_cmp(cert, stream->peer_certificate_->x509()) == 0) { - LOG(LS_INFO) << "Accepted self-signed peer certificate authority"; - ok = 1; - } - } else if (!ok && !stream->peer_certificate_digest_algorithm_.empty()) { - X509* cert = X509_STORE_CTX_get_current_cert(store); - int err = X509_STORE_CTX_get_error(store); - - // peer-to-peer mode: allow the certificate to be self-signed, - // assuming it matches the digest that was specified. - if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) { - unsigned char digest[EVP_MAX_MD_SIZE]; - std::size_t digest_length; - - if (OpenSSLCertificate:: - ComputeDigest(cert, - stream->peer_certificate_digest_algorithm_, - digest, sizeof(digest), - &digest_length)) { - Buffer computed_digest(digest, digest_length); - if (computed_digest == stream->peer_certificate_digest_value_) { - LOG(LS_INFO) << - "Accepted self-signed peer certificate authority"; - ok = 1; - - // Record the peer's certificate. - stream->peer_certificate_.reset(new OpenSSLCertificate(cert)); - } - } - } - } else if (!ok && OpenSSLAdapter::custom_verify_callback_) { - // this applies only in traditional mode - void* cert = - reinterpret_cast<void*>(X509_STORE_CTX_get_current_cert(store)); - if (OpenSSLAdapter::custom_verify_callback_(cert)) { - stream->custom_verification_succeeded_ = true; - LOG(LS_INFO) << "validated certificate using custom callback"; - ok = 1; - } + if (stream->peer_certificate_digest_algorithm_.empty()) { + return 0; + } + X509* cert = X509_STORE_CTX_get_current_cert(store); + int depth = X509_STORE_CTX_get_error_depth(store); + + // For now We ignore the parent certificates and verify the leaf against + // the digest. + // + // TODO(jiayl): Verify the chain is a proper chain and report the chain to + // |stream->peer_certificate_|, like what NSS does. + if (depth > 0) { + LOG(LS_INFO) << "Ignored chained certificate at depth " << depth; + return 1; + } + + unsigned char digest[EVP_MAX_MD_SIZE]; + size_t digest_length; + if (!OpenSSLCertificate::ComputeDigest( + cert, + stream->peer_certificate_digest_algorithm_, + digest, sizeof(digest), + &digest_length)) { + LOG(LS_WARNING) << "Failed to compute peer cert digest."; + return 0; } - if (!ok && stream->ignore_bad_cert()) { - LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain"; - ok = 1; + Buffer computed_digest(digest, digest_length); + if (computed_digest != stream->peer_certificate_digest_value_) { + LOG(LS_WARNING) << "Rejected peer certificate due to mismatched digest."; + return 0; } + // Ignore any verification error if the digest matches, since there is no + // value in checking the validity of a self-signed cert issued by untrusted + // sources. + LOG(LS_INFO) << "Accepted peer certificate."; - return ok; + // Record the peer's certificate. + stream->peer_certificate_.reset(new OpenSSLCertificate(cert)); + return 1; } // This code is taken from the "Network Security with OpenSSL" @@ -923,11 +850,7 @@ bool OpenSSLStreamAdapter::SSLPostConnectionCheck(SSL* ssl, } bool OpenSSLStreamAdapter::HaveDtls() { -#ifdef HAVE_DTLS return true; -#else - return false; -#endif } bool OpenSSLStreamAdapter::HaveDtlsSrtp() { diff --git a/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.h b/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.h index 3c478187fcc..744d29980e1 100644 --- a/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.h +++ b/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.h @@ -2,26 +2,26 @@ * libjingle * Copyright 2004--2008, Google Inc. * - * Redistribution and use in source and binary forms, with or without + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - * 1. Redistributions of source code must retain the above copyright notice, + * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products + * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -81,7 +81,6 @@ class OpenSSLStreamAdapter : public SSLStreamAdapter { // Default argument is for compatibility virtual void SetServerRole(SSLRole role = SSL_SERVER); - virtual void SetPeerCertificate(SSLCertificate* cert); virtual bool SetPeerCertificateDigest(const std::string& digest_alg, const unsigned char* digest_val, size_t digest_len); @@ -175,7 +174,6 @@ class OpenSSLStreamAdapter : public SSLStreamAdapter { // passed. static int SSLVerifyCallback(int ok, X509_STORE_CTX* store); - SSLState state_; SSLRole role_; int ssl_error_code_; // valid when state_ == SSL_ERROR or SSL_CLOSED diff --git a/chromium/third_party/libjingle/source/talk/base/optionsfile_unittest.cc b/chromium/third_party/libjingle/source/talk/base/optionsfile_unittest.cc index 65861ff4982..afb79cf9a7b 100644 --- a/chromium/third_party/libjingle/source/talk/base/optionsfile_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/optionsfile_unittest.cc @@ -25,19 +25,13 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "talk/base/fileutils.h" #include "talk/base/gunit.h" #include "talk/base/optionsfile.h" +#include "talk/base/pathutils.h" namespace talk_base { -#ifdef ANDROID -static const char *kTestFile = "/sdcard/.testfile"; -#elif CHROMEOS -static const char *kTestFile = "/tmp/.testfile"; -#else -static const char *kTestFile = ".testfile"; -#endif - static const std::string kTestOptionA = "test-option-a"; static const std::string kTestOptionB = "test-option-b"; static const std::string kTestString1 = "a string"; @@ -56,122 +50,135 @@ static int kTestInt2 = 67890; static int kNegInt = -634; static int kZero = 0; -TEST(OptionsFile, GetSetString) { - OptionsFile store(kTestFile); +class OptionsFileTest : public testing::Test { + public: + OptionsFileTest() { + Pathname dir; + ASSERT(Filesystem::GetTemporaryFolder(dir, true, NULL)); + test_file_ = Filesystem::TempFilename(dir, ".testfile"); + OpenStore(); + } + + protected: + void OpenStore() { + store_.reset(new OptionsFile(test_file_)); + } + + talk_base::scoped_ptr<OptionsFile> store_; + + private: + std::string test_file_; +}; + +TEST_F(OptionsFileTest, GetSetString) { // Clear contents of the file on disk. - EXPECT_TRUE(store.Save()); + EXPECT_TRUE(store_->Save()); std::string out1, out2; - EXPECT_FALSE(store.GetStringValue(kTestOptionA, &out1)); - EXPECT_FALSE(store.GetStringValue(kTestOptionB, &out2)); - EXPECT_TRUE(store.SetStringValue(kTestOptionA, kTestString1)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.SetStringValue(kTestOptionB, kTestString2)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.GetStringValue(kTestOptionA, &out1)); - EXPECT_TRUE(store.GetStringValue(kTestOptionB, &out2)); + EXPECT_FALSE(store_->GetStringValue(kTestOptionA, &out1)); + EXPECT_FALSE(store_->GetStringValue(kTestOptionB, &out2)); + EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kTestString1)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->SetStringValue(kTestOptionB, kTestString2)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out1)); + EXPECT_TRUE(store_->GetStringValue(kTestOptionB, &out2)); EXPECT_EQ(kTestString1, out1); EXPECT_EQ(kTestString2, out2); - EXPECT_TRUE(store.RemoveValue(kTestOptionA)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.RemoveValue(kTestOptionB)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_FALSE(store.GetStringValue(kTestOptionA, &out1)); - EXPECT_FALSE(store.GetStringValue(kTestOptionB, &out2)); + EXPECT_TRUE(store_->RemoveValue(kTestOptionA)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->RemoveValue(kTestOptionB)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_FALSE(store_->GetStringValue(kTestOptionA, &out1)); + EXPECT_FALSE(store_->GetStringValue(kTestOptionB, &out2)); } -TEST(OptionsFile, GetSetInt) { - OptionsFile store(kTestFile); +TEST_F(OptionsFileTest, GetSetInt) { // Clear contents of the file on disk. - EXPECT_TRUE(store.Save()); + EXPECT_TRUE(store_->Save()); int out1, out2; - EXPECT_FALSE(store.GetIntValue(kTestOptionA, &out1)); - EXPECT_FALSE(store.GetIntValue(kTestOptionB, &out2)); - EXPECT_TRUE(store.SetIntValue(kTestOptionA, kTestInt1)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.SetIntValue(kTestOptionB, kTestInt2)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.GetIntValue(kTestOptionA, &out1)); - EXPECT_TRUE(store.GetIntValue(kTestOptionB, &out2)); + EXPECT_FALSE(store_->GetIntValue(kTestOptionA, &out1)); + EXPECT_FALSE(store_->GetIntValue(kTestOptionB, &out2)); + EXPECT_TRUE(store_->SetIntValue(kTestOptionA, kTestInt1)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->SetIntValue(kTestOptionB, kTestInt2)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetIntValue(kTestOptionA, &out1)); + EXPECT_TRUE(store_->GetIntValue(kTestOptionB, &out2)); EXPECT_EQ(kTestInt1, out1); EXPECT_EQ(kTestInt2, out2); - EXPECT_TRUE(store.RemoveValue(kTestOptionA)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.RemoveValue(kTestOptionB)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_FALSE(store.GetIntValue(kTestOptionA, &out1)); - EXPECT_FALSE(store.GetIntValue(kTestOptionB, &out2)); - EXPECT_TRUE(store.SetIntValue(kTestOptionA, kNegInt)); - EXPECT_TRUE(store.GetIntValue(kTestOptionA, &out1)); + EXPECT_TRUE(store_->RemoveValue(kTestOptionA)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->RemoveValue(kTestOptionB)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_FALSE(store_->GetIntValue(kTestOptionA, &out1)); + EXPECT_FALSE(store_->GetIntValue(kTestOptionB, &out2)); + EXPECT_TRUE(store_->SetIntValue(kTestOptionA, kNegInt)); + EXPECT_TRUE(store_->GetIntValue(kTestOptionA, &out1)); EXPECT_EQ(kNegInt, out1); - EXPECT_TRUE(store.SetIntValue(kTestOptionA, kZero)); - EXPECT_TRUE(store.GetIntValue(kTestOptionA, &out1)); + EXPECT_TRUE(store_->SetIntValue(kTestOptionA, kZero)); + EXPECT_TRUE(store_->GetIntValue(kTestOptionA, &out1)); EXPECT_EQ(kZero, out1); } -TEST(OptionsFile, Persist) { - { - OptionsFile store(kTestFile); - // Clear contents of the file on disk. - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.SetStringValue(kTestOptionA, kTestString1)); - EXPECT_TRUE(store.SetIntValue(kTestOptionB, kNegInt)); - EXPECT_TRUE(store.Save()); - } - { - OptionsFile store(kTestFile); - // Load the saved contents from above. - EXPECT_TRUE(store.Load()); - std::string out1; - int out2; - EXPECT_TRUE(store.GetStringValue(kTestOptionA, &out1)); - EXPECT_TRUE(store.GetIntValue(kTestOptionB, &out2)); - EXPECT_EQ(kTestString1, out1); - EXPECT_EQ(kNegInt, out2); - } +TEST_F(OptionsFileTest, Persist) { + // Clear contents of the file on disk. + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kTestString1)); + EXPECT_TRUE(store_->SetIntValue(kTestOptionB, kNegInt)); + EXPECT_TRUE(store_->Save()); + + // Load the saved contents from above. + OpenStore(); + EXPECT_TRUE(store_->Load()); + std::string out1; + int out2; + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out1)); + EXPECT_TRUE(store_->GetIntValue(kTestOptionB, &out2)); + EXPECT_EQ(kTestString1, out1); + EXPECT_EQ(kNegInt, out2); } -TEST(OptionsFile, SpecialCharacters) { - OptionsFile store(kTestFile); +TEST_F(OptionsFileTest, SpecialCharacters) { // Clear contents of the file on disk. - EXPECT_TRUE(store.Save()); + EXPECT_TRUE(store_->Save()); std::string out; - EXPECT_FALSE(store.SetStringValue(kOptionWithEquals, kTestString1)); - EXPECT_FALSE(store.GetStringValue(kOptionWithEquals, &out)); - EXPECT_FALSE(store.SetStringValue(kOptionWithNewline, kTestString1)); - EXPECT_FALSE(store.GetStringValue(kOptionWithNewline, &out)); - EXPECT_TRUE(store.SetStringValue(kOptionWithUtf8, kValueWithUtf8)); - EXPECT_TRUE(store.SetStringValue(kTestOptionA, kTestString1)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.GetStringValue(kTestOptionA, &out)); + EXPECT_FALSE(store_->SetStringValue(kOptionWithEquals, kTestString1)); + EXPECT_FALSE(store_->GetStringValue(kOptionWithEquals, &out)); + EXPECT_FALSE(store_->SetStringValue(kOptionWithNewline, kTestString1)); + EXPECT_FALSE(store_->GetStringValue(kOptionWithNewline, &out)); + EXPECT_TRUE(store_->SetStringValue(kOptionWithUtf8, kValueWithUtf8)); + EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kTestString1)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out)); EXPECT_EQ(kTestString1, out); - EXPECT_TRUE(store.GetStringValue(kOptionWithUtf8, &out)); + EXPECT_TRUE(store_->GetStringValue(kOptionWithUtf8, &out)); EXPECT_EQ(kValueWithUtf8, out); - EXPECT_FALSE(store.SetStringValue(kTestOptionA, kValueWithNewline)); - EXPECT_TRUE(store.GetStringValue(kTestOptionA, &out)); + EXPECT_FALSE(store_->SetStringValue(kTestOptionA, kValueWithNewline)); + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out)); EXPECT_EQ(kTestString1, out); - EXPECT_TRUE(store.SetStringValue(kTestOptionA, kValueWithEquals)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.GetStringValue(kTestOptionA, &out)); + EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kValueWithEquals)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out)); EXPECT_EQ(kValueWithEquals, out); - EXPECT_TRUE(store.SetStringValue(kEmptyString, kTestString2)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.GetStringValue(kEmptyString, &out)); + EXPECT_TRUE(store_->SetStringValue(kEmptyString, kTestString2)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kEmptyString, &out)); EXPECT_EQ(kTestString2, out); - EXPECT_TRUE(store.SetStringValue(kTestOptionB, kEmptyString)); - EXPECT_TRUE(store.Save()); - EXPECT_TRUE(store.Load()); - EXPECT_TRUE(store.GetStringValue(kTestOptionB, &out)); + EXPECT_TRUE(store_->SetStringValue(kTestOptionB, kEmptyString)); + EXPECT_TRUE(store_->Save()); + EXPECT_TRUE(store_->Load()); + EXPECT_TRUE(store_->GetStringValue(kTestOptionB, &out)); EXPECT_EQ(kEmptyString, out); } diff --git a/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.cc b/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.cc index 43be440e7bd..a7f65c57acc 100644 --- a/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.cc +++ b/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.cc @@ -29,13 +29,14 @@ #pragma warning(disable:4786) #endif -#include <cassert> +#include <assert.h> #ifdef POSIX #include <string.h> #include <errno.h> #include <fcntl.h> #include <sys/time.h> +#include <sys/select.h> #include <unistd.h> #include <signal.h> #endif @@ -78,6 +79,7 @@ typedef char* SockOptArg; namespace talk_base { +#if defined(WIN32) // Standard MTUs, from RFC 1191 const uint16 PACKET_MAXIMUMS[] = { 65535, // Theoretical maximum, Hyperchannel @@ -105,6 +107,7 @@ static const int IP_HEADER_SIZE = 20u; static const int IPV6_HEADER_SIZE = 40u; static const int ICMP_HEADER_SIZE = 8u; static const int ICMP_PING_TIMEOUT_MILLIS = 10000u; +#endif class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { public: @@ -498,7 +501,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { } void MaybeRemapSendError() { -#if defined(OSX) +#if defined(OSX) || defined(IOS) // https://developer.apple.com/library/mac/documentation/Darwin/ // Reference/ManPages/man2/sendto.2.html // ENOBUFS - The output queue for a network interface is full. @@ -517,7 +520,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { *slevel = IPPROTO_IP; *sopt = IP_DONTFRAGMENT; break; -#elif defined(IOS) || defined(OSX) || defined(BSD) +#elif defined(IOS) || defined(OSX) || defined(BSD) || defined(__native_client__) LOG(LS_WARNING) << "Socket::OPT_DONTFRAGMENT not supported."; return -1; #elif defined(POSIX) @@ -540,6 +543,8 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { case OPT_DSCP: LOG(LS_WARNING) << "Socket::OPT_DSCP not supported."; return -1; + case OPT_RTP_SENDTIME_EXTN_ID: + return -1; // No logging is necessary as this not a OS socket option. default: ASSERT(false); return -1; @@ -1199,9 +1204,7 @@ class Signaler : public EventDispatcher { }; PhysicalSocketServer::PhysicalSocketServer() - : fWait_(false), - last_tick_tracked_(0), - last_tick_dispatch_count_(0) { + : fWait_(false) { signal_wakeup_ = new Signaler(this, &fWait_); #ifdef WIN32 socket_ev_ = WSACreateEvent(); @@ -1489,10 +1492,14 @@ bool PhysicalSocketServer::InstallSignal(int signum, void (*handler)(int)) { return false; } act.sa_handler = handler; +#if !defined(__native_client__) // Use SA_RESTART so that our syscalls don't get EINTR, since we don't need it // and it's a nuisance. Though some syscalls still return EINTR and there's no // real standard for which ones. :( act.sa_flags = SA_RESTART; +#else + act.sa_flags = 0; +#endif if (sigaction(signum, &act, NULL) != 0) { LOG_ERR(LS_ERROR) << "Couldn't set sigaction"; return false; @@ -1507,12 +1514,6 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { int cmsElapsed = 0; uint32 msStart = Time(); -#if LOGGING - if (last_tick_dispatch_count_ == 0) { - last_tick_tracked_ = msStart; - } -#endif - fWait_ = true; while (fWait_) { std::vector<WSAEVENT> events; @@ -1562,27 +1563,10 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { cmsNext, false); -#if 0 // LOGGING - // we track this information purely for logging purposes. - last_tick_dispatch_count_++; - if (last_tick_dispatch_count_ >= 1000) { - int32 elapsed = TimeSince(last_tick_tracked_); - LOG(INFO) << "PhysicalSocketServer took " << elapsed - << "ms for 1000 events"; - - // If we get more than 1000 events in a second, we are spinning badly - // (normally it should take about 8-20 seconds). - ASSERT(elapsed > 1000); - - last_tick_tracked_ = Time(); - last_tick_dispatch_count_ = 0; - } -#endif - if (dw == WSA_WAIT_FAILED) { // Failed? // TODO: need a better strategy than this! - int error = WSAGetLastError(); + WSAGetLastError(); ASSERT(false); return false; } else if (dw == WSA_WAIT_TIMEOUT) { diff --git a/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.h b/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.h index 709f85ab105..9173f238dff 100644 --- a/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.h +++ b/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.h @@ -127,8 +127,6 @@ class PhysicalSocketServer : public SocketServer { Signaler* signal_wakeup_; CriticalSection crit_; bool fWait_; - uint32 last_tick_tracked_; - int last_tick_dispatch_count_; #ifdef WIN32 WSAEVENT socket_ev_; #endif diff --git a/chromium/third_party/libjingle/source/talk/base/physicalsocketserver_unittest.cc b/chromium/third_party/libjingle/source/talk/base/physicalsocketserver_unittest.cc index 329cf5d5f25..cbdbd9cad6a 100644 --- a/chromium/third_party/libjingle/source/talk/base/physicalsocketserver_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/physicalsocketserver_unittest.cc @@ -33,6 +33,7 @@ #include "talk/base/physicalsocketserver.h" #include "talk/base/scoped_ptr.h" #include "talk/base/socket_unittest.h" +#include "talk/base/testutils.h" #include "talk/base/thread.h" namespace talk_base { @@ -74,21 +75,11 @@ TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv6) { } -#ifdef OSX -// This test crashes the OS X kernel on 10.6 (at bsd/netinet/tcp_subr.c:2118). -TEST_F(PhysicalSocketTest, DISABLED_TestConnectWithClosedSocketIPv4) { -#else TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv4) { -#endif SocketTest::TestConnectWithClosedSocketIPv4(); } -#ifdef OSX -// This test crashes the OS X kernel on 10.6 (at bsd/netinet/tcp_subr.c:2118). -TEST_F(PhysicalSocketTest, DISABLED_TestConnectWithClosedSocketIPv6) { -#else TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv6) { -#endif SocketTest::TestConnectWithClosedSocketIPv6(); } @@ -227,7 +218,7 @@ Thread *PosixSignalDeliveryTest::signaled_thread_ = NULL; // Test receiving a synchronous signal while not in Wait() and then entering // Wait() afterwards. TEST_F(PosixSignalDeliveryTest, RaiseThenWait) { - ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal); + ASSERT_TRUE(ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal)); raise(SIGTERM); EXPECT_TRUE(ss_->Wait(0, true)); EXPECT_TRUE(ExpectSignal(SIGTERM)); diff --git a/chromium/third_party/libjingle/source/talk/base/profiler.h b/chromium/third_party/libjingle/source/talk/base/profiler.h index 90c5c722a30..f74a540ae47 100644 --- a/chromium/third_party/libjingle/source/talk/base/profiler.h +++ b/chromium/third_party/libjingle/source/talk/base/profiler.h @@ -57,7 +57,9 @@ #include "talk/base/sharedexclusivelock.h" // Profiling could be switched via a build flag, but for now, it's always on. +#ifndef ENABLE_PROFILING #define ENABLE_PROFILING +#endif #ifdef ENABLE_PROFILING diff --git a/chromium/third_party/libjingle/source/talk/base/profiler_unittest.cc b/chromium/third_party/libjingle/source/talk/base/profiler_unittest.cc index f451e5fab83..a39b32c4995 100644 --- a/chromium/third_party/libjingle/source/talk/base/profiler_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/profiler_unittest.cc @@ -47,13 +47,15 @@ namespace talk_base { TEST(ProfilerTest, TestFunction) { ASSERT_TRUE(Profiler::Instance()->Clear()); + // Profile a long-running function. const char* function_name = TestFunc(); const ProfilerEvent* event = Profiler::Instance()->GetEvent(function_name); ASSERT_TRUE(event != NULL); EXPECT_FALSE(event->is_started()); EXPECT_EQ(1, event->event_count()); - EXPECT_NEAR(kWaitSec, event->mean(), kTolerance); + EXPECT_NEAR(kWaitSec, event->mean(), kTolerance * 3); + // Run it a second time. TestFunc(); EXPECT_FALSE(event->is_started()); @@ -95,7 +97,9 @@ TEST(ProfilerTest, TestScopedEvents) { // Check the result. EXPECT_FALSE(event2->is_started()); EXPECT_EQ(1, event2->event_count()); - EXPECT_NEAR(kEvent2WaitSec, event2->mean(), kTolerance); + + // The difference here can be as much as 0.33, so we need high tolerance. + EXPECT_NEAR(kEvent2WaitSec, event2->mean(), kTolerance * 4); // Make sure event1 is unchanged. EXPECT_FALSE(event1->is_started()); EXPECT_EQ(1, event1->event_count()); diff --git a/chromium/third_party/libjingle/source/talk/base/proxydetect.cc b/chromium/third_party/libjingle/source/talk/base/proxydetect.cc index 7292f3b9fd2..8f7f7f87260 100644 --- a/chromium/third_party/libjingle/source/talk/base/proxydetect.cc +++ b/chromium/third_party/libjingle/source/talk/base/proxydetect.cc @@ -638,27 +638,27 @@ bool IsDefaultBrowserFirefox() { if (ERROR_SUCCESS != result) return false; - wchar_t* value = NULL; DWORD size, type; + bool success = false; result = RegQueryValueEx(key, L"", 0, &type, NULL, &size); - if (REG_SZ != type) { - result = ERROR_ACCESS_DENIED; // Any error is fine - } else if (ERROR_SUCCESS == result) { - value = new wchar_t[size+1]; + if (result == ERROR_SUCCESS && type == REG_SZ) { + wchar_t* value = new wchar_t[size+1]; BYTE* buffer = reinterpret_cast<BYTE*>(value); result = RegQueryValueEx(key, L"", 0, &type, buffer, &size); - } - RegCloseKey(key); - - bool success = false; - if (ERROR_SUCCESS == result) { - value[size] = L'\0'; - for (size_t i = 0; i < size; ++i) { - value[i] = tolowercase(value[i]); + if (result == ERROR_SUCCESS) { + // Size returned by RegQueryValueEx is in bytes, convert to number of + // wchar_t's. + size /= sizeof(value[0]); + value[size] = L'\0'; + for (size_t i = 0; i < size; ++i) { + value[i] = tolowercase(value[i]); + } + success = (NULL != strstr(value, L"firefox.exe")); } - success = (NULL != strstr(value, L"firefox.exe")); + delete[] value; } - delete [] value; + + RegCloseKey(key); return success; } diff --git a/chromium/third_party/libjingle/source/talk/base/proxydetect_unittest.cc b/chromium/third_party/libjingle/source/talk/base/proxydetect_unittest.cc index 685066d9432..e09518b5f41 100644 --- a/chromium/third_party/libjingle/source/talk/base/proxydetect_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/proxydetect_unittest.cc @@ -51,7 +51,6 @@ static const std::string kFirefoxCorruptHeader = "iuahueqe32164"; static const std::string kProxyAddress = "proxy.net.com"; -static const int kProxyPort = 9999; // Mocking out platform specific path to firefox prefs file. class FirefoxPrefsFileSystem : public FakeFileSystem { diff --git a/chromium/third_party/libjingle/source/talk/base/refcount.h b/chromium/third_party/libjingle/source/talk/base/refcount.h index 38cf147621a..e8950e9b04c 100644 --- a/chromium/third_party/libjingle/source/talk/base/refcount.h +++ b/chromium/third_party/libjingle/source/talk/base/refcount.h @@ -28,7 +28,7 @@ #ifndef TALK_APP_BASE_REFCOUNT_H_ #define TALK_APP_BASE_REFCOUNT_H_ -#include <cstring> +#include <string.h> #include "talk/base/criticalsection.h" diff --git a/chromium/third_party/libjingle/source/talk/base/rollingaccumulator.h b/chromium/third_party/libjingle/source/talk/base/rollingaccumulator.h index cdad0251f3f..dfda8fec072 100644 --- a/chromium/third_party/libjingle/source/talk/base/rollingaccumulator.h +++ b/chromium/third_party/libjingle/source/talk/base/rollingaccumulator.h @@ -42,11 +42,8 @@ template<typename T> class RollingAccumulator { public: explicit RollingAccumulator(size_t max_count) - : count_(0), - next_index_(0), - sum_(0.0), - sum_2_(0.0), - samples_(max_count) { + : samples_(max_count) { + Reset(); } ~RollingAccumulator() { } @@ -59,12 +56,29 @@ class RollingAccumulator { return count_; } + void Reset() { + count_ = 0U; + next_index_ = 0U; + sum_ = 0.0; + sum_2_ = 0.0; + max_ = T(); + max_stale_ = false; + min_ = T(); + min_stale_ = false; + } + void AddSample(T sample) { if (count_ == max_count()) { // Remove oldest sample. T sample_to_remove = samples_[next_index_]; sum_ -= sample_to_remove; sum_2_ -= sample_to_remove * sample_to_remove; + if (sample_to_remove >= max_) { + max_stale_ = true; + } + if (sample_to_remove <= min_) { + min_stale_ = true; + } } else { // Increase count of samples. ++count_; @@ -73,6 +87,14 @@ class RollingAccumulator { samples_[next_index_] = sample; sum_ += sample; sum_2_ += sample * sample; + if (count_ == 1 || sample >= max_) { + max_ = sample; + max_stale_ = false; + } + if (count_ == 1 || sample <= min_) { + min_ = sample; + min_stale_ = false; + } // Update next_index_. next_index_ = (next_index_ + 1) % max_count(); } @@ -81,17 +103,43 @@ class RollingAccumulator { return static_cast<T>(sum_); } - T ComputeMean() const { + double ComputeMean() const { if (count_ == 0) { - return static_cast<T>(0); + return 0.0; + } + return sum_ / count_; + } + + T ComputeMax() const { + if (max_stale_) { + ASSERT(count_ > 0 && + "It shouldn't be possible for max_stale_ && count_ == 0"); + max_ = samples_[next_index_]; + for (size_t i = 1u; i < count_; i++) { + max_ = _max(max_, samples_[(next_index_ + i) % max_count()]); + } + max_stale_ = false; + } + return max_; + } + + T ComputeMin() const { + if (min_stale_) { + ASSERT(count_ > 0 && + "It shouldn't be possible for min_stale_ && count_ == 0"); + min_ = samples_[next_index_]; + for (size_t i = 1u; i < count_; i++) { + min_ = _min(min_, samples_[(next_index_ + i) % max_count()]); + } + min_stale_ = false; } - return static_cast<T>(sum_ / count_); + return min_; } // O(n) time complexity. // Weights nth sample with weight (learning_rate)^n. Learning_rate should be // between (0.0, 1.0], otherwise the non-weighted mean is returned. - T ComputeWeightedMean(double learning_rate) const { + double ComputeWeightedMean(double learning_rate) const { if (count_ < 1 || learning_rate <= 0.0 || learning_rate >= 1.0) { return ComputeMean(); } @@ -106,27 +154,31 @@ class RollingAccumulator { size_t index = (next_index_ + max_size - i - 1) % max_size; weighted_mean += current_weight * samples_[index]; } - return static_cast<T>(weighted_mean / weight_sum); + return weighted_mean / weight_sum; } // Compute estimated variance. Estimation is more accurate // as the number of samples grows. - T ComputeVariance() const { + double ComputeVariance() const { if (count_ == 0) { - return static_cast<T>(0); + return 0.0; } // Var = E[x^2] - (E[x])^2 double count_inv = 1.0 / count_; double mean_2 = sum_2_ * count_inv; double mean = sum_ * count_inv; - return static_cast<T>(mean_2 - (mean * mean)); + return mean_2 - (mean * mean); } private: size_t count_; size_t next_index_; - double sum_; // Sum(x) - double sum_2_; // Sum(x*x) + double sum_; // Sum(x) - double to avoid overflow + double sum_2_; // Sum(x*x) - double to avoid overflow + mutable T max_; + mutable bool max_stale_; + mutable T min_; + mutable bool min_stale_; std::vector<T> samples_; DISALLOW_COPY_AND_ASSIGN(RollingAccumulator); diff --git a/chromium/third_party/libjingle/source/talk/base/rollingaccumulator_unittest.cc b/chromium/third_party/libjingle/source/talk/base/rollingaccumulator_unittest.cc index c2831033645..e6d0ea2b750 100644 --- a/chromium/third_party/libjingle/source/talk/base/rollingaccumulator_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/rollingaccumulator_unittest.cc @@ -40,8 +40,10 @@ TEST(RollingAccumulatorTest, ZeroSamples) { RollingAccumulator<int> accum(10); EXPECT_EQ(0U, accum.count()); - EXPECT_EQ(0, accum.ComputeMean()); - EXPECT_EQ(0, accum.ComputeVariance()); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeMean()); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeVariance()); + EXPECT_EQ(0, accum.ComputeMin()); + EXPECT_EQ(0, accum.ComputeMax()); } TEST(RollingAccumulatorTest, SomeSamples) { @@ -52,9 +54,11 @@ TEST(RollingAccumulatorTest, SomeSamples) { EXPECT_EQ(4U, accum.count()); EXPECT_EQ(6, accum.ComputeSum()); - EXPECT_EQ(1, accum.ComputeMean()); - EXPECT_EQ(2, accum.ComputeWeightedMean(kLearningRate)); - EXPECT_EQ(1, accum.ComputeVariance()); + EXPECT_DOUBLE_EQ(1.5, accum.ComputeMean()); + EXPECT_NEAR(2.26666, accum.ComputeWeightedMean(kLearningRate), 0.01); + EXPECT_DOUBLE_EQ(1.25, accum.ComputeVariance()); + EXPECT_EQ(0, accum.ComputeMin()); + EXPECT_EQ(3, accum.ComputeMax()); } TEST(RollingAccumulatorTest, RollingSamples) { @@ -65,9 +69,36 @@ TEST(RollingAccumulatorTest, RollingSamples) { EXPECT_EQ(10U, accum.count()); EXPECT_EQ(65, accum.ComputeSum()); - EXPECT_EQ(6, accum.ComputeMean()); - EXPECT_EQ(10, accum.ComputeWeightedMean(kLearningRate)); - EXPECT_NEAR(9, accum.ComputeVariance(), 1); + EXPECT_DOUBLE_EQ(6.5, accum.ComputeMean()); + EXPECT_NEAR(10.0, accum.ComputeWeightedMean(kLearningRate), 0.01); + EXPECT_NEAR(9.0, accum.ComputeVariance(), 1.0); + EXPECT_EQ(2, accum.ComputeMin()); + EXPECT_EQ(11, accum.ComputeMax()); +} + +TEST(RollingAccumulatorTest, ResetSamples) { + RollingAccumulator<int> accum(10); + + for (int i = 0; i < 10; ++i) { + accum.AddSample(100); + } + EXPECT_EQ(10U, accum.count()); + EXPECT_DOUBLE_EQ(100.0, accum.ComputeMean()); + EXPECT_EQ(100, accum.ComputeMin()); + EXPECT_EQ(100, accum.ComputeMax()); + + accum.Reset(); + EXPECT_EQ(0U, accum.count()); + + for (int i = 0; i < 5; ++i) { + accum.AddSample(i); + } + + EXPECT_EQ(5U, accum.count()); + EXPECT_EQ(10, accum.ComputeSum()); + EXPECT_DOUBLE_EQ(2.0, accum.ComputeMean()); + EXPECT_EQ(0, accum.ComputeMin()); + EXPECT_EQ(4, accum.ComputeMax()); } TEST(RollingAccumulatorTest, RollingSamplesDouble) { @@ -81,22 +112,24 @@ TEST(RollingAccumulatorTest, RollingSamplesDouble) { EXPECT_DOUBLE_EQ(87.5, accum.ComputeMean()); EXPECT_NEAR(105.049, accum.ComputeWeightedMean(kLearningRate), 0.1); EXPECT_NEAR(229.166667, accum.ComputeVariance(), 25); + EXPECT_DOUBLE_EQ(65.0, accum.ComputeMin()); + EXPECT_DOUBLE_EQ(110.0, accum.ComputeMax()); } TEST(RollingAccumulatorTest, ComputeWeightedMeanCornerCases) { RollingAccumulator<int> accum(10); - EXPECT_EQ(0, accum.ComputeWeightedMean(kLearningRate)); - EXPECT_EQ(0, accum.ComputeWeightedMean(0.0)); - EXPECT_EQ(0, accum.ComputeWeightedMean(1.1)); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeWeightedMean(kLearningRate)); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeWeightedMean(0.0)); + EXPECT_DOUBLE_EQ(0.0, accum.ComputeWeightedMean(1.1)); for (int i = 0; i < 8; ++i) { accum.AddSample(i); } - EXPECT_EQ(3, accum.ComputeMean()); - EXPECT_EQ(3, accum.ComputeWeightedMean(0)); - EXPECT_EQ(3, accum.ComputeWeightedMean(1.1)); - EXPECT_EQ(6, accum.ComputeWeightedMean(kLearningRate)); + EXPECT_DOUBLE_EQ(3.5, accum.ComputeMean()); + EXPECT_DOUBLE_EQ(3.5, accum.ComputeWeightedMean(0)); + EXPECT_DOUBLE_EQ(3.5, accum.ComputeWeightedMean(1.1)); + EXPECT_NEAR(6.0, accum.ComputeWeightedMean(kLearningRate), 0.1); } } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/safe_conversions.h b/chromium/third_party/libjingle/source/talk/base/safe_conversions.h new file mode 100644 index 00000000000..d246d4ffce1 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/safe_conversions.h @@ -0,0 +1,96 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Borrowed from Chromium's src/base/numerics/safe_conversions.h. + +#ifndef TALK_BASE_SAFE_CONVERSIONS_H_ +#define TALK_BASE_SAFE_CONVERSIONS_H_ + +#include <limits> + +#include "talk/base/common.h" +#include "talk/base/logging.h" +#include "talk/base/safe_conversions_impl.h" + +namespace talk_base { + +inline void Check(bool condition) { + if (!condition) { + LOG(LS_ERROR) << "CHECK failed."; + Break(); + // The program should have crashed at this point. + } +} + +// Convenience function that returns true if the supplied value is in range +// for the destination type. +template <typename Dst, typename Src> +inline bool IsValueInRangeForNumericType(Src value) { + return internal::RangeCheck<Dst>(value) == internal::TYPE_VALID; +} + +// checked_cast<> is analogous to static_cast<> for numeric types, +// except that it CHECKs that the specified numeric conversion will not +// overflow or underflow. NaN source will always trigger a CHECK. +template <typename Dst, typename Src> +inline Dst checked_cast(Src value) { + Check(IsValueInRangeForNumericType<Dst>(value)); + return static_cast<Dst>(value); +} + +// saturated_cast<> is analogous to static_cast<> for numeric types, except +// that the specified numeric conversion will saturate rather than overflow or +// underflow. NaN assignment to an integral will trigger a CHECK condition. +template <typename Dst, typename Src> +inline Dst saturated_cast(Src value) { + // Optimization for floating point values, which already saturate. + if (std::numeric_limits<Dst>::is_iec559) + return static_cast<Dst>(value); + + switch (internal::RangeCheck<Dst>(value)) { + case internal::TYPE_VALID: + return static_cast<Dst>(value); + + case internal::TYPE_UNDERFLOW: + return std::numeric_limits<Dst>::min(); + + case internal::TYPE_OVERFLOW: + return std::numeric_limits<Dst>::max(); + + // Should fail only on attempting to assign NaN to a saturated integer. + case internal::TYPE_INVALID: + Check(false); + return std::numeric_limits<Dst>::max(); + } + + Check(false); // NOTREACHED(); + return static_cast<Dst>(value); +} + +} // namespace talk_base + +#endif // TALK_BASE_SAFE_CONVERSIONS_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/safe_conversions_impl.h b/chromium/third_party/libjingle/source/talk/base/safe_conversions_impl.h new file mode 100644 index 00000000000..391e5966b9a --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/safe_conversions_impl.h @@ -0,0 +1,205 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Borrowed from Chromium's src/base/numerics/safe_conversions_impl.h. + +#ifndef TALK_BASE_SAFE_CONVERSIONS_IMPL_H_ +#define TALK_BASE_SAFE_CONVERSIONS_IMPL_H_ + +#include <limits> + +namespace talk_base { +namespace internal { + +enum DstSign { + DST_UNSIGNED, + DST_SIGNED +}; + +enum SrcSign { + SRC_UNSIGNED, + SRC_SIGNED +}; + +enum DstRange { + OVERLAPS_RANGE, + CONTAINS_RANGE +}; + +// Helper templates to statically determine if our destination type can contain +// all values represented by the source type. + +template <typename Dst, typename Src, + DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ? + DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ? + SRC_SIGNED : SRC_UNSIGNED> +struct StaticRangeCheck {}; + +template <typename Dst, typename Src> +struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> { + typedef std::numeric_limits<Dst> DstLimits; + typedef std::numeric_limits<Src> SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = DstLimits::is_iec559 ? + DstLimits::max_exponent : + (sizeof(Dst) * 8 - 1); + static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ? + SrcLimits::max_exponent : + (sizeof(Src) * 8 - 1); + static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ? + CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template <typename Dst, typename Src> +struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> { + static const DstRange value = sizeof(Dst) >= sizeof(Src) ? + CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template <typename Dst, typename Src> +struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_UNSIGNED> { + typedef std::numeric_limits<Dst> DstLimits; + typedef std::numeric_limits<Src> SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = DstLimits::is_iec559 ? + DstLimits::max_exponent : + (sizeof(Dst) * 8 - 1); + static const size_t kSrcMaxExponent = sizeof(Src) * 8; + static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ? + CONTAINS_RANGE : OVERLAPS_RANGE; +}; + +template <typename Dst, typename Src> +struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> { + static const DstRange value = OVERLAPS_RANGE; +}; + + +enum RangeCheckResult { + TYPE_VALID = 0, // Value can be represented by the destination type. + TYPE_UNDERFLOW = 1, // Value would overflow. + TYPE_OVERFLOW = 2, // Value would underflow. + TYPE_INVALID = 3 // Source value is invalid (i.e. NaN). +}; + +// This macro creates a RangeCheckResult from an upper and lower bound +// check by taking advantage of the fact that only NaN can be out of range in +// both directions at once. +#define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \ + RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \ + ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) + +template <typename Dst, + typename Src, + DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ? + DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ? + SRC_SIGNED : SRC_UNSIGNED, + DstRange IsSrcRangeContained = StaticRangeCheck<Dst, Src>::value> +struct RangeCheckImpl {}; + +// The following templates are for ranges that must be verified at runtime. We +// split it into checks based on signedness to avoid confusing casts and +// compiler warnings on signed an unsigned comparisons. + +// Dst range always contains the result: nothing to check. +template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned> +struct RangeCheckImpl<Dst, Src, IsDstSigned, IsSrcSigned, CONTAINS_RANGE> { + static RangeCheckResult Check(Src value) { + return TYPE_VALID; + } +}; + +// Signed to signed narrowing. +template <typename Dst, typename Src> +struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> { + static RangeCheckResult Check(Src value) { + typedef std::numeric_limits<Dst> DstLimits; + return DstLimits::is_iec559 ? + BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast<Src>(DstLimits::max()), + value >= static_cast<Src>(DstLimits::max() * -1)) : + BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast<Src>(DstLimits::max()), + value >= static_cast<Src>(DstLimits::min())); + } +}; + +// Unsigned to unsigned narrowing. +template <typename Dst, typename Src> +struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { + static RangeCheckResult Check(Src value) { + typedef std::numeric_limits<Dst> DstLimits; + return BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast<Src>(DstLimits::max()), true); + } +}; + +// Unsigned to signed. +template <typename Dst, typename Src> +struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> { + static RangeCheckResult Check(Src value) { + typedef std::numeric_limits<Dst> DstLimits; + return sizeof(Dst) > sizeof(Src) ? TYPE_VALID : + BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast<Src>(DstLimits::max()), true); + } +}; + +// Signed to unsigned. +template <typename Dst, typename Src> +struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> { + static RangeCheckResult Check(Src value) { + typedef std::numeric_limits<Dst> DstLimits; + typedef std::numeric_limits<Src> SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static const size_t kDstMaxExponent = sizeof(Dst) * 8; + static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ? + SrcLimits::max_exponent : + (sizeof(Src) * 8 - 1); + return (kDstMaxExponent >= kSrcMaxExponent) ? + BASE_NUMERIC_RANGE_CHECK_RESULT(true, value >= static_cast<Src>(0)) : + BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast<Src>(DstLimits::max()), + value >= static_cast<Src>(0)); + } +}; + +template <typename Dst, typename Src> +inline RangeCheckResult RangeCheck(Src value) { + COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized, + argument_must_be_numeric); + COMPILE_ASSERT(std::numeric_limits<Dst>::is_specialized, + result_must_be_numeric); + return RangeCheckImpl<Dst, Src>::Check(value); +} + +} // namespace internal +} // namespace talk_base + +#endif // TALK_BASE_SAFE_CONVERSIONS_IMPL_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/scoped_ptr.h b/chromium/third_party/libjingle/source/talk/base/scoped_ptr.h index 90f743c6285..5158a9e69cd 100644 --- a/chromium/third_party/libjingle/source/talk/base/scoped_ptr.h +++ b/chromium/third_party/libjingle/source/talk/base/scoped_ptr.h @@ -88,8 +88,8 @@ #ifndef TALK_BASE_SCOPED_PTR_H__ #define TALK_BASE_SCOPED_PTR_H__ -#include <cstddef> // for std::ptrdiff_t -#include <stdlib.h> // for free() decl +#include <stddef.h> // for ptrdiff_t +#include <stdlib.h> // for free() decl #include <algorithm> // For std::swap(). diff --git a/chromium/third_party/libjingle/source/talk/base/scoped_ref_ptr.h b/chromium/third_party/libjingle/source/talk/base/scoped_ref_ptr.h index 3ce72cbce6d..ae1ab0f230e 100644 --- a/chromium/third_party/libjingle/source/talk/base/scoped_ref_ptr.h +++ b/chromium/third_party/libjingle/source/talk/base/scoped_ref_ptr.h @@ -80,6 +80,8 @@ #ifndef TALK_BASE_SCOPED_REF_PTR_H_ #define TALK_BASE_SCOPED_REF_PTR_H_ +#include <stddef.h> + namespace talk_base { template <class T> diff --git a/chromium/third_party/libjingle/source/talk/examples/plus/testutil/libjingleplus_unittest.cc b/chromium/third_party/libjingle/source/talk/base/scopedptrcollection.h index d7f63252261..ec2726e272c 100644 --- a/chromium/third_party/libjingle/source/talk/examples/plus/testutil/libjingleplus_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/scopedptrcollection.h @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2006, Google Inc. + * Copyright 2014 Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,28 +25,53 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "testing/base/gunit.h" -#include "talk/libjingle-plus/libjingleplus.h" -#include "talk/libjingle-plus/testutil/libjingleplus_test_notifier.h" +// Stores a collection of pointers that are deleted when the container is +// destructed. -#if defined(_MSC_VER) && (_MSC_VER < 1400) -void __cdecl std::_Throw(const std::exception &) {} -std::_Prhand std::_Raise_handler =0; -#endif +#ifndef TALK_BASE_SCOPEDPTRCOLLECTION_H_ +#define TALK_BASE_SCOPEDPTRCOLLECTION_H_ + +#include <algorithm> +#include <vector> + +#include "talk/base/basictypes.h" +#include "talk/base/constructormagic.h" namespace talk_base { -TEST(LibjingleTest, ConstructDestruct) { - for (int i = 0; i < 5; ++i) { - LibjinglePlus *libjingleplus = new LibjinglePlus(new Notifier); - libjingleplus->Login("eaterleaver0", "Buzzt3st", "talk.google.com", false, false); +template<class T> +class ScopedPtrCollection { + public: + typedef std::vector<T*> VectorT; + + ScopedPtrCollection() { } + ~ScopedPtrCollection() { + for (typename VectorT::iterator it = collection_.begin(); + it != collection_.end(); ++it) { + delete *it; + } + } + + const VectorT& collection() const { return collection_; } + void Reserve(size_t size) { + collection_.reserve(size); + } + void PushBack(T* t) { + collection_.push_back(t); + } - delete libjingleplus; + // Remove |t| from the collection without deleting it. + void Remove(T* t) { + collection_.erase(std::remove(collection_.begin(), collection_.end(), t), + collection_.end()); } -} -} -int main(int argc, char** argv) { - testing::ParseGUnitFlags(&argc, argv); - return RUN_ALL_TESTS(); -} + private: + VectorT collection_; + + DISALLOW_COPY_AND_ASSIGN(ScopedPtrCollection); +}; + +} // namespace talk_base + +#endif // TALK_BASE_SCOPEDPTRCOLLECTION_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/scopedptrcollection_unittest.cc b/chromium/third_party/libjingle/source/talk/base/scopedptrcollection_unittest.cc new file mode 100644 index 00000000000..dd9002e42f1 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/scopedptrcollection_unittest.cc @@ -0,0 +1,90 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/base/scopedptrcollection.h" +#include "talk/base/gunit.h" + +namespace talk_base { + +namespace { + +class InstanceCounter { + public: + explicit InstanceCounter(int* num_instances) + : num_instances_(num_instances) { + ++(*num_instances_); + } + ~InstanceCounter() { + --(*num_instances_); + } + + private: + int* num_instances_; + + DISALLOW_COPY_AND_ASSIGN(InstanceCounter); +}; + +} // namespace + +class ScopedPtrCollectionTest : public testing::Test { + protected: + ScopedPtrCollectionTest() + : num_instances_(0), + collection_(new ScopedPtrCollection<InstanceCounter>()) { + } + + int num_instances_; + scoped_ptr<ScopedPtrCollection<InstanceCounter> > collection_; +}; + +TEST_F(ScopedPtrCollectionTest, PushBack) { + EXPECT_EQ(0u, collection_->collection().size()); + EXPECT_EQ(0, num_instances_); + const int kNum = 100; + for (int i = 0; i < kNum; ++i) { + collection_->PushBack(new InstanceCounter(&num_instances_)); + } + EXPECT_EQ(static_cast<size_t>(kNum), collection_->collection().size()); + EXPECT_EQ(kNum, num_instances_); + collection_.reset(); + EXPECT_EQ(0, num_instances_); +} + +TEST_F(ScopedPtrCollectionTest, Remove) { + InstanceCounter* ic = new InstanceCounter(&num_instances_); + collection_->PushBack(ic); + EXPECT_EQ(1u, collection_->collection().size()); + collection_->Remove(ic); + EXPECT_EQ(1, num_instances_); + collection_.reset(); + EXPECT_EQ(1, num_instances_); + delete ic; + EXPECT_EQ(0, num_instances_); +} + + +} // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/sharedexclusivelock_unittest.cc b/chromium/third_party/libjingle/source/talk/base/sharedexclusivelock_unittest.cc index 46b7fdfdc2c..e280aa28a88 100644 --- a/chromium/third_party/libjingle/source/talk/base/sharedexclusivelock_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/sharedexclusivelock_unittest.cc @@ -148,7 +148,8 @@ class SharedExclusiveLockTest int value_; }; -TEST_F(SharedExclusiveLockTest, TestSharedShared) { +// Flaky: https://code.google.com/p/webrtc/issues/detail?id=3318 +TEST_F(SharedExclusiveLockTest, DISABLED_TestSharedShared) { int value0, value1; bool done0, done1; ReadTask reader0(shared_exclusive_lock_.get(), &value_, &done0); diff --git a/chromium/third_party/libjingle/source/talk/base/signalthread_unittest.cc b/chromium/third_party/libjingle/source/talk/base/signalthread_unittest.cc index e5734d4df11..7bc73f05d0c 100644 --- a/chromium/third_party/libjingle/source/talk/base/signalthread_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/signalthread_unittest.cc @@ -50,19 +50,19 @@ class SignalThreadTest : public testing::Test, public sigslot::has_slots<> { ASSERT_TRUE(harness_ != NULL); ++harness_->thread_started_; EXPECT_EQ(harness_->main_thread_, Thread::Current()); - EXPECT_FALSE(worker()->started()); // not started yet + EXPECT_FALSE(worker()->RunningForTest()); // not started yet } virtual void OnWorkStop() { ++harness_->thread_stopped_; EXPECT_EQ(harness_->main_thread_, Thread::Current()); - EXPECT_TRUE(worker()->started()); // not stopped yet + EXPECT_TRUE(worker()->RunningForTest()); // not stopped yet } virtual void OnWorkDone() { ++harness_->thread_done_; EXPECT_EQ(harness_->main_thread_, Thread::Current()); - EXPECT_TRUE(worker()->started()); // not stopped yet + EXPECT_TRUE(worker()->RunningForTest()); // not stopped yet } virtual void DoWork() { diff --git a/chromium/third_party/libjingle/source/talk/base/sigslottester.h b/chromium/third_party/libjingle/source/talk/base/sigslottester.h new file mode 100755 index 00000000000..9422318e236 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/sigslottester.h @@ -0,0 +1,216 @@ +// This file was GENERATED by command: +// pump.py sigslottester.h.pump +// DO NOT EDIT BY HAND!!! + +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_BASE_SIGSLOTTESTER_H_ +#define TALK_BASE_SIGSLOTTESTER_H_ + +// To generate sigslottester.h from sigslottester.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py sigslottester.h.pump + + +// SigslotTester(s) are utility classes to check if signals owned by an +// object are being invoked at the right time and with the right arguments. +// They are meant to be used in tests. Tests must provide "capture" pointers +// (i.e. address of variables) where the arguments from the signal callback +// can be stored. +// +// Example: +// /* Some signal */ +// sigslot::signal1<const std::string&> foo; +// +// /* We want to monitor foo in some test. Note how signal argument is +// const std::string&, but capture-type is std::string. Capture type +// must be type that can be assigned to. */ +// std::string capture; +// SigslotTester1<const std::string&, std::string> slot(&foo, &capture); +// foo.emit("hello"); +// EXPECT_EQ(1, slot.callback_count()); +// EXPECT_EQ("hello", capture); +// /* See unit-tests for more examples */ + +#include "talk/base/constructormagic.h" +#include "talk/base/sigslot.h" + +namespace talk_base { + +// For all the templates below: +// - A1-A5 is the type of the argument i in the callback. Signals may and often +// do use const-references here for efficiency. +// - C1-C5 is the type of the variable to capture argument i. These should be +// non-const value types suitable for use as lvalues. + +template <class A1, class C1> +class SigslotTester1 : public sigslot::has_slots<> { + public: + SigslotTester1(sigslot::signal1<A1>* signal, + C1* capture1) + : callback_count_(0), + capture1_(capture1) { + signal->connect(this, &SigslotTester1::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1) { + callback_count_++; + *capture1_ = arg1; + } + + int callback_count_; + C1* capture1_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester1); +}; + +template <class A1, class A2, class C1, class C2> +class SigslotTester2 : public sigslot::has_slots<> { + public: + SigslotTester2(sigslot::signal2<A1, A2>* signal, + C1* capture1, C2* capture2) + : callback_count_(0), + capture1_(capture1), capture2_(capture2) { + signal->connect(this, &SigslotTester2::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1, A2 arg2) { + callback_count_++; + *capture1_ = arg1; + *capture2_ = arg2; + } + + int callback_count_; + C1* capture1_; + C2* capture2_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester2); +}; + +template <class A1, class A2, class A3, class C1, class C2, class C3> +class SigslotTester3 : public sigslot::has_slots<> { + public: + SigslotTester3(sigslot::signal3<A1, A2, A3>* signal, + C1* capture1, C2* capture2, C3* capture3) + : callback_count_(0), + capture1_(capture1), capture2_(capture2), capture3_(capture3) { + signal->connect(this, &SigslotTester3::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1, A2 arg2, A3 arg3) { + callback_count_++; + *capture1_ = arg1; + *capture2_ = arg2; + *capture3_ = arg3; + } + + int callback_count_; + C1* capture1_; + C2* capture2_; + C3* capture3_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester3); +}; + +template <class A1, class A2, class A3, class A4, class C1, class C2, class C3, + class C4> +class SigslotTester4 : public sigslot::has_slots<> { + public: + SigslotTester4(sigslot::signal4<A1, A2, A3, A4>* signal, + C1* capture1, C2* capture2, C3* capture3, C4* capture4) + : callback_count_(0), + capture1_(capture1), capture2_(capture2), capture3_(capture3), + capture4_(capture4) { + signal->connect(this, &SigslotTester4::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1, A2 arg2, A3 arg3, A4 arg4) { + callback_count_++; + *capture1_ = arg1; + *capture2_ = arg2; + *capture3_ = arg3; + *capture4_ = arg4; + } + + int callback_count_; + C1* capture1_; + C2* capture2_; + C3* capture3_; + C4* capture4_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester4); +}; + +template <class A1, class A2, class A3, class A4, class A5, class C1, class C2, + class C3, class C4, class C5> +class SigslotTester5 : public sigslot::has_slots<> { + public: + SigslotTester5(sigslot::signal5<A1, A2, A3, A4, A5>* signal, + C1* capture1, C2* capture2, C3* capture3, C4* capture4, + C5* capture5) + : callback_count_(0), + capture1_(capture1), capture2_(capture2), capture3_(capture3), + capture4_(capture4), capture5_(capture5) { + signal->connect(this, &SigslotTester5::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) { + callback_count_++; + *capture1_ = arg1; + *capture2_ = arg2; + *capture3_ = arg3; + *capture4_ = arg4; + *capture5_ = arg5; + } + + int callback_count_; + C1* capture1_; + C2* capture2_; + C3* capture3_; + C4* capture4_; + C5* capture5_; + + DISALLOW_COPY_AND_ASSIGN(SigslotTester5); +}; +} // namespace talk_base + +#endif // TALK_BASE_SIGSLOTTESTER_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/sigslottester.h.pump b/chromium/third_party/libjingle/source/talk/base/sigslottester.h.pump new file mode 100755 index 00000000000..dce1c7b26ea --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/sigslottester.h.pump @@ -0,0 +1,102 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_BASE_SIGSLOTTESTER_H_ +#define TALK_BASE_SIGSLOTTESTER_H_ + +// To generate sigslottester.h from sigslottester.h.pump, execute: +// /home/build/google3/third_party/gtest/scripts/pump.py sigslottester.h.pump + + +// SigslotTester(s) are utility classes to check if signals owned by an +// object are being invoked at the right time and with the right arguments. +// They are meant to be used in tests. Tests must provide "capture" pointers +// (i.e. address of variables) where the arguments from the signal callback +// can be stored. +// +// Example: +// /* Some signal */ +// sigslot::signal1<const std::string&> foo; +// +// /* We want to monitor foo in some test. Note how signal argument is +// const std::string&, but capture-type is std::string. Capture type +// must be type that can be assigned to. */ +// std::string capture; +// SigslotTester1<const std::string&, std::string> slot(&foo, &capture); +// foo.emit("hello"); +// EXPECT_EQ(1, slot.callback_count()); +// EXPECT_EQ("hello", capture); +// /* See unit-tests for more examples */ + +#include "talk/base/constructormagic.h" +#include "talk/base/sigslot.h" + +namespace talk_base { + +// For all the templates below: +// - A1-A5 is the type of the argument i in the callback. Signals may and often +// do use const-references here for efficiency. +// - C1-C5 is the type of the variable to capture argument i. These should be +// non-const value types suitable for use as lvalues. + +$var n = 5 +$range i 1..n +$for i [[ +$range j 1..i + +template <$for j , [[class A$j]], $for j , [[class C$j]]> +class SigslotTester$i : public sigslot::has_slots<> { + public: + SigslotTester$i(sigslot::signal$i<$for j , [[A$j]]>* signal, + $for j , [[C$j* capture$j]]) + : callback_count_(0), + $for j , [[capture$j[[]]_(capture$j)]] { + signal->connect(this, &SigslotTester$i::OnSignalCallback); + } + + int callback_count() const { return callback_count_; } + + private: + void OnSignalCallback($for j , [[A$j arg$j]]) { + callback_count_++;$for j [[ + + *capture$j[[]]_ = arg$j;]] + + } + + int callback_count_;$for j [[ + + C$j* capture$j[[]]_;]] + + + DISALLOW_COPY_AND_ASSIGN(SigslotTester$i); +}; + +]] +} // namespace talk_base + +#endif // TALK_BASE_SIGSLOTTESTER_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/sigslottester_unittest.cc b/chromium/third_party/libjingle/source/talk/base/sigslottester_unittest.cc new file mode 100755 index 00000000000..b427ef67c2d --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/sigslottester_unittest.cc @@ -0,0 +1,74 @@ +#include "talk/base/sigslottester.h" + +#include "talk/base/gunit.h" +#include "talk/base/sigslot.h" + +namespace talk_base { + +TEST(SigslotTester, TestSignal1Arg) { + sigslot::signal1<int> source1; + int capture1; + SigslotTester1<int, int> slot1(&source1, &capture1); + EXPECT_EQ(0, slot1.callback_count()); + + source1.emit(10); + EXPECT_EQ(1, slot1.callback_count()); + EXPECT_EQ(10, capture1); + + source1.emit(20); + EXPECT_EQ(2, slot1.callback_count()); + EXPECT_EQ(20, capture1); +} + +TEST(SigslotTester, TestSignal2Args) { + sigslot::signal2<int, char> source2; + int capture1; + char capture2; + SigslotTester2<int, char, int, char> slot2(&source2, &capture1, &capture2); + EXPECT_EQ(0, slot2.callback_count()); + + source2.emit(10, 'x'); + EXPECT_EQ(1, slot2.callback_count()); + EXPECT_EQ(10, capture1); + EXPECT_EQ('x', capture2); + + source2.emit(20, 'y'); + EXPECT_EQ(2, slot2.callback_count()); + EXPECT_EQ(20, capture1); + EXPECT_EQ('y', capture2); +} + +// Since it applies for 1 and 2 args, we assume it will work for up to 5 args. + +TEST(SigslotTester, TestSignalWithConstReferenceArgs) { + sigslot::signal1<const std::string&> source1; + std::string capture1; + SigslotTester1<const std::string&, std::string> slot1(&source1, &capture1); + EXPECT_EQ(0, slot1.callback_count()); + source1.emit("hello"); + EXPECT_EQ(1, slot1.callback_count()); + EXPECT_EQ("hello", capture1); +} + +TEST(SigslotTester, TestSignalWithPointerToConstArgs) { + sigslot::signal1<const std::string*> source1; + const std::string* capture1; + SigslotTester1<const std::string*, const std::string*> slot1(&source1, + &capture1); + EXPECT_EQ(0, slot1.callback_count()); + source1.emit(NULL); + EXPECT_EQ(1, slot1.callback_count()); + EXPECT_EQ(NULL, capture1); +} + +TEST(SigslotTester, TestSignalWithConstPointerArgs) { + sigslot::signal1<std::string* const> source1; + std::string* capture1; + SigslotTester1<std::string* const, std::string*> slot1(&source1, &capture1); + EXPECT_EQ(0, slot1.callback_count()); + source1.emit(NULL); + EXPECT_EQ(1, slot1.callback_count()); + EXPECT_EQ(NULL, capture1); +} + +} // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/socket.h b/chromium/third_party/libjingle/source/talk/base/socket.h index e738060f89d..590645f83d4 100644 --- a/chromium/third_party/libjingle/source/talk/base/socket.h +++ b/chromium/third_party/libjingle/source/talk/base/socket.h @@ -2,26 +2,26 @@ * libjingle * Copyright 2004--2005, Google Inc. * - * Redistribution and use in source and binary forms, with or without + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - * 1. Redistributions of source code must retain the above copyright notice, + * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products + * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -185,7 +185,10 @@ class Socket { OPT_SNDBUF, // send buffer size OPT_NODELAY, // whether Nagle algorithm is enabled OPT_IPV6_V6ONLY, // Whether the socket is IPv6 only. - OPT_DSCP // DSCP code + OPT_DSCP, // DSCP code + OPT_RTP_SENDTIME_EXTN_ID, // This is a non-traditional socket option param. + // This is specific to libjingle and will be used + // if SendTime option is needed at socket level. }; virtual int GetOption(Option opt, int* value) = 0; virtual int SetOption(Option opt, int value) = 0; diff --git a/chromium/third_party/libjingle/source/talk/base/socket_unittest.cc b/chromium/third_party/libjingle/source/talk/base/socket_unittest.cc index a9c4dbb0de4..e76d113b2e9 100644 --- a/chromium/third_party/libjingle/source/talk/base/socket_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/socket_unittest.cc @@ -172,8 +172,8 @@ void SocketTest::TestUdpIPv6() { } void SocketTest::TestUdpReadyToSendIPv4() { -#if !defined(OSX) - // TODO(ronghuawu): Enable this test (currently failed on build bots) on mac. +#if !defined(OSX) && !defined(IOS) + // TODO(ronghuawu): Enable this test on mac/ios. UdpReadyToSend(kIPv4Loopback); #endif } diff --git a/chromium/third_party/libjingle/source/talk/base/socketaddress.cc b/chromium/third_party/libjingle/source/talk/base/socketaddress.cc index 193a2328208..792d414adcf 100644 --- a/chromium/third_party/libjingle/source/talk/base/socketaddress.cc +++ b/chromium/third_party/libjingle/source/talk/base/socketaddress.cc @@ -34,7 +34,9 @@ #if defined(OPENBSD) #include <netinet/in_systm.h> #endif +#if !defined(__native_client__) #include <netinet/ip.h> +#endif #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> diff --git a/chromium/third_party/libjingle/source/talk/base/sslfingerprint.cc b/chromium/third_party/libjingle/source/talk/base/sslfingerprint.cc new file mode 100644 index 00000000000..dfd5551abcd --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/sslfingerprint.cc @@ -0,0 +1,114 @@ +/* + * libjingle + * Copyright 2012, Google Inc. + * Copyright 2012, RTFM Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/base/sslfingerprint.h" + +#include <ctype.h> +#include <string> + +#include "talk/base/helpers.h" +#include "talk/base/messagedigest.h" +#include "talk/base/stringencode.h" + +namespace talk_base { + +SSLFingerprint* SSLFingerprint::Create( + const std::string& algorithm, const talk_base::SSLIdentity* identity) { + if (!identity) { + return NULL; + } + + return Create(algorithm, &(identity->certificate())); +} + +SSLFingerprint* SSLFingerprint::Create( + const std::string& algorithm, const talk_base::SSLCertificate* cert) { + uint8 digest_val[64]; + size_t digest_len; + bool ret = cert->ComputeDigest( + algorithm, digest_val, sizeof(digest_val), &digest_len); + if (!ret) { + return NULL; + } + + return new SSLFingerprint(algorithm, digest_val, digest_len); +} + +SSLFingerprint* SSLFingerprint::CreateFromRfc4572( + const std::string& algorithm, const std::string& fingerprint) { + if (algorithm.empty() || !talk_base::IsFips180DigestAlgorithm(algorithm)) + return NULL; + + if (fingerprint.empty()) + return NULL; + + size_t value_len; + char value[talk_base::MessageDigest::kMaxSize]; + value_len = talk_base::hex_decode_with_delimiter(value, sizeof(value), + fingerprint.c_str(), + fingerprint.length(), + ':'); + if (!value_len) + return NULL; + + return new SSLFingerprint(algorithm, + reinterpret_cast<uint8*>(value), + value_len); +} + +SSLFingerprint::SSLFingerprint( + const std::string& algorithm, const uint8* digest_in, size_t digest_len) + : algorithm(algorithm) { + digest.SetData(digest_in, digest_len); +} + +SSLFingerprint::SSLFingerprint(const SSLFingerprint& from) + : algorithm(from.algorithm), digest(from.digest) {} + +bool SSLFingerprint::operator==(const SSLFingerprint& other) const { + return algorithm == other.algorithm && + digest == other.digest; +} + +std::string SSLFingerprint::GetRfc4572Fingerprint() const { + std::string fingerprint = + talk_base::hex_encode_with_delimiter( + digest.data(), digest.length(), ':'); + std::transform(fingerprint.begin(), fingerprint.end(), + fingerprint.begin(), ::toupper); + return fingerprint; +} + +std::string SSLFingerprint::ToString() { + std::string fp_str = algorithm; + fp_str.append(" "); + fp_str.append(GetRfc4572Fingerprint()); + return fp_str; +} + +} // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/sslfingerprint.h b/chromium/third_party/libjingle/source/talk/base/sslfingerprint.h index b85778947e6..a803d2129a7 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslfingerprint.h +++ b/chromium/third_party/libjingle/source/talk/base/sslfingerprint.h @@ -29,81 +29,35 @@ #ifndef TALK_BASE_SSLFINGERPRINT_H_ #define TALK_BASE_SSLFINGERPRINT_H_ -#include <ctype.h> #include <string> #include "talk/base/buffer.h" -#include "talk/base/helpers.h" -#include "talk/base/messagedigest.h" #include "talk/base/sslidentity.h" -#include "talk/base/stringencode.h" namespace talk_base { +class SSLCertificate; + struct SSLFingerprint { static SSLFingerprint* Create(const std::string& algorithm, - const talk_base::SSLIdentity* identity) { - if (!identity) { - return NULL; - } - - return Create(algorithm, &(identity->certificate())); - } + const talk_base::SSLIdentity* identity); static SSLFingerprint* Create(const std::string& algorithm, - const talk_base::SSLCertificate* cert) { - uint8 digest_val[64]; - size_t digest_len; - bool ret = cert->ComputeDigest( - algorithm, digest_val, sizeof(digest_val), &digest_len); - if (!ret) { - return NULL; - } - - return new SSLFingerprint(algorithm, digest_val, digest_len); - } + const talk_base::SSLCertificate* cert); static SSLFingerprint* CreateFromRfc4572(const std::string& algorithm, - const std::string& fingerprint) { - if (algorithm.empty()) - return NULL; - - if (fingerprint.empty()) - return NULL; - - size_t value_len; - char value[talk_base::MessageDigest::kMaxSize]; - value_len = talk_base::hex_decode_with_delimiter(value, sizeof(value), - fingerprint.c_str(), - fingerprint.length(), - ':'); - if (!value_len) - return NULL; - - return new SSLFingerprint(algorithm, - reinterpret_cast<uint8*>(value), - value_len); - } + const std::string& fingerprint); SSLFingerprint(const std::string& algorithm, const uint8* digest_in, - size_t digest_len) : algorithm(algorithm) { - digest.SetData(digest_in, digest_len); - } - SSLFingerprint(const SSLFingerprint& from) - : algorithm(from.algorithm), digest(from.digest) {} - bool operator==(const SSLFingerprint& other) const { - return algorithm == other.algorithm && - digest == other.digest; - } - - std::string GetRfc4572Fingerprint() const { - std::string fingerprint = - talk_base::hex_encode_with_delimiter( - digest.data(), digest.length(), ':'); - std::transform(fingerprint.begin(), fingerprint.end(), - fingerprint.begin(), ::toupper); - return fingerprint; - } + size_t digest_len); + + SSLFingerprint(const SSLFingerprint& from); + + bool operator==(const SSLFingerprint& other) const; + + std::string GetRfc4572Fingerprint() const; + + std::string ToString(); std::string algorithm; talk_base::Buffer digest; diff --git a/chromium/third_party/libjingle/source/talk/base/sslidentity.cc b/chromium/third_party/libjingle/source/talk/base/sslidentity.cc index 8f704dc300e..d2d2b11f173 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslidentity.cc +++ b/chromium/third_party/libjingle/source/talk/base/sslidentity.cc @@ -115,6 +115,10 @@ SSLIdentity* SSLIdentity::Generate(const std::string& common_name) { return NULL; } +SSLIdentity* GenerateForTest(const SSLIdentityParams& params) { + return NULL; +} + SSLIdentity* SSLIdentity::FromPEMStrings(const std::string& private_key, const std::string& certificate) { return NULL; @@ -130,6 +134,10 @@ SSLIdentity* SSLIdentity::Generate(const std::string& common_name) { return OpenSSLIdentity::Generate(common_name); } +SSLIdentity* SSLIdentity::GenerateForTest(const SSLIdentityParams& params) { + return OpenSSLIdentity::GenerateForTest(params); +} + SSLIdentity* SSLIdentity::FromPEMStrings(const std::string& private_key, const std::string& certificate) { return OpenSSLIdentity::FromPEMStrings(private_key, certificate); @@ -145,6 +153,10 @@ SSLIdentity* SSLIdentity::Generate(const std::string& common_name) { return NSSIdentity::Generate(common_name); } +SSLIdentity* SSLIdentity::GenerateForTest(const SSLIdentityParams& params) { + return NSSIdentity::GenerateForTest(params); +} + SSLIdentity* SSLIdentity::FromPEMStrings(const std::string& private_key, const std::string& certificate) { return NSSIdentity::FromPEMStrings(private_key, certificate); diff --git a/chromium/third_party/libjingle/source/talk/base/sslidentity.h b/chromium/third_party/libjingle/source/talk/base/sslidentity.h index 89b1008983c..b930f063ce2 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslidentity.h +++ b/chromium/third_party/libjingle/source/talk/base/sslidentity.h @@ -81,9 +81,10 @@ class SSLCertificate { virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const = 0; // Compute the digest of the certificate given algorithm - virtual bool ComputeDigest(const std::string &algorithm, - unsigned char* digest, std::size_t size, - std::size_t* length) const = 0; + virtual bool ComputeDigest(const std::string& algorithm, + unsigned char* digest, + size_t size, + size_t* length) const = 0; }; // SSLCertChain is a simple wrapper for a vector of SSLCertificates. It serves @@ -132,6 +133,16 @@ class SSLCertChain { DISALLOW_COPY_AND_ASSIGN(SSLCertChain); }; +// Parameters for generating an identity for testing. If common_name is +// non-empty, it will be used for the certificate's subject and issuer name, +// otherwise a random string will be used. |not_before| and |not_after| are +// offsets to the current time in number of seconds. +struct SSLIdentityParams { + std::string common_name; + int not_before; // in seconds. + int not_after; // in seconds. +}; + // Our identity in an SSL negotiation: a keypair and certificate (both // with the same public key). // This too is pretty much immutable once created. @@ -144,6 +155,9 @@ class SSLIdentity { // Caller is responsible for freeing the returned object. static SSLIdentity* Generate(const std::string& common_name); + // Generates an identity with the specified validity period. + static SSLIdentity* GenerateForTest(const SSLIdentityParams& params); + // Construct an identity from a private key and a certificate. static SSLIdentity* FromPEMStrings(const std::string& private_key, const std::string& certificate); diff --git a/chromium/third_party/libjingle/source/talk/base/sslidentity_unittest.cc b/chromium/third_party/libjingle/source/talk/base/sslidentity_unittest.cc index b63b8b9d439..fdb165c9f61 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslidentity_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/sslidentity_unittest.cc @@ -177,32 +177,33 @@ TEST_F(SSLIdentityTest, DigestSHA512) { TEST_F(SSLIdentityTest, FromPEMStrings) { static const char kRSA_PRIVATE_KEY_PEM[] = "-----BEGIN RSA PRIVATE KEY-----\n" - "MIICXQIBAAKBgQDCueE4a9hDMZ3sbVZdlXOz9ZA+cvzie3zJ9gXnT/BCt9P4b9HE\n" - "vD/tr73YBqD3Wr5ZWScmyGYF9EMn0r3rzBxv6oooLU5TdUvOm4rzUjkCLQaQML8o\n" - "NxXq+qW/j3zUKGikLhaaAl/amaX2zSWUsRQ1CpngQ3+tmDNH4/25TncNmQIDAQAB\n" - "AoGAUcuU0Id0k10fMjYHZk4mCPzot2LD2Tr4Aznl5vFMQipHzv7hhZtx2xzMSRcX\n" - "vG+Qr6VkbcUWHgApyWubvZXCh3+N7Vo2aYdMAQ8XqmFpBdIrL5CVdVfqFfEMlgEy\n" - "LSZNG5klnrIfl3c7zQVovLr4eMqyl2oGfAqPQz75+fecv1UCQQD6wNHch9NbAG1q\n" - "yuFEhMARB6gDXb+5SdzFjjtTWW5uJfm4DcZLoYyaIZm0uxOwsUKd0Rsma+oGitS1\n" - "CXmuqfpPAkEAxszyN3vIdpD44SREEtyKZBMNOk5pEIIGdbeMJC5/XHvpxww9xkoC\n" - "+39NbvUZYd54uT+rafbx4QZKc0h9xA/HlwJBAL37lYVWy4XpPv1olWCKi9LbUCqs\n" - "vvQtyD1N1BkEayy9TQRsO09WKOcmigRqsTJwOx7DLaTgokEuspYvhagWVPUCQE/y\n" - "0+YkTbYBD1Xbs9SyBKXCU6uDJRWSdO6aZi2W1XloC9gUwDMiSJjD1Wwt/YsyYPJ+\n" - "/Hyc5yFL2l0KZimW/vkCQQCjuZ/lPcH46EuzhdbRfumDOG5N3ld7UhGI1TIRy17W\n" - "dGF90cG33/L6BfS8Ll+fkkW/2AMRk8FDvF4CZi2nfW4L\n" + "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMYRkbhmI7kVA/rM\n" + "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" + "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" + "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAECgYAvgOs4FJcgvp+TuREx7YtiYVsH\n" + "mwQPTum2z/8VzWGwR8BBHBvIpVe1MbD/Y4seyI2aco/7UaisatSgJhsU46/9Y4fq\n" + "2TwXH9QANf4at4d9n/R6rzwpAJOpgwZgKvdQjkfrKTtgLV+/dawvpxUYkRH4JZM1\n" + "CVGukMfKNrSVH4Ap4QJBAOJmGV1ASPnB4r4nc99at7JuIJmd7fmuVUwUgYi4XgaR\n" + "WhScBsgYwZ/JoywdyZJgnbcrTDuVcWG56B3vXbhdpMsCQQDf9zeJrjnPZ3Cqm79y\n" + "kdqANep0uwZciiNiWxsQrCHztywOvbFhdp8iYVFG9EK8DMY41Y5TxUwsHD+67zao\n" + "ZNqJAkEA1suLUP/GvL8IwuRneQd2tWDqqRQ/Td3qq03hP7e77XtF/buya3Ghclo5\n" + "54czUR89QyVfJEC6278nzA7n2h1uVQJAcG6mztNL6ja/dKZjYZye2CY44QjSlLo0\n" + "MTgTSjdfg/28fFn2Jjtqf9Pi/X+50LWI/RcYMC2no606wRk9kyOuIQJBAK6VSAim\n" + "1pOEjsYQn0X5KEIrz1G3bfCbB848Ime3U2/FWlCHMr6ch8kCZ5d1WUeJD3LbwMNG\n" + "UCXiYxSsu20QNVw=\n" "-----END RSA PRIVATE KEY-----\n"; static const char kCERT_PEM[] = "-----BEGIN CERTIFICATE-----\n" - "MIIBmTCCAQICCQCPNJORW/M13DANBgkqhkiG9w0BAQUFADARMQ8wDQYDVQQDDAZ3\n" - "ZWJydGMwHhcNMTMwNjE0MjIzMDAxWhcNMTQwNjE0MjIzMDAxWjARMQ8wDQYDVQQD\n" - "DAZ3ZWJydGMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMK54Thr2EMxnext\n" - "Vl2Vc7P1kD5y/OJ7fMn2BedP8EK30/hv0cS8P+2vvdgGoPdavllZJybIZgX0QyfS\n" - "vevMHG/qiigtTlN1S86bivNSOQItBpAwvyg3Fer6pb+PfNQoaKQuFpoCX9qZpfbN\n" - "JZSxFDUKmeBDf62YM0fj/blOdw2ZAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAECMt\n" - "UZb35H8TnjGx4XPzco/kbnurMLFFWcuve/DwTsuf10Ia9N4md8LY0UtgIgtyNqWc\n" - "ZwyRMwxONF6ty3wcaIiPbGqiAa55T3YRuPibkRmck9CjrmM9JAtyvqHnpHd2TsBD\n" - "qCV42aXS3onOXDQ1ibuWq0fr0//aj0wo4KV474c=\n" + "MIIBmTCCAQKgAwIBAgIEbzBSAjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZX\n" + "ZWJSVEMwHhcNMTQwMTAyMTgyNDQ3WhcNMTQwMjAxMTgyNDQ3WjARMQ8wDQYDVQQD\n" + "EwZXZWJSVEMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYRkbhmI7kVA/rM\n" + "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" + "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" + "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAUflI\n" + "VUe5Krqf5RVa5C3u/UTAOAUJBiDS3VANTCLBxjuMsvqOG0WvaYWP3HYPgrz0jXK2\n" + "LJE/mGw3MyFHEqi81jh95J+ypl6xKW6Rm8jKLR87gUvCaVYn/Z4/P3AqcQTB7wOv\n" + "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\n" "-----END CERTIFICATE-----\n"; talk_base::scoped_ptr<SSLIdentity> identity( diff --git a/chromium/third_party/libjingle/source/talk/base/sslstreamadapter.h b/chromium/third_party/libjingle/source/talk/base/sslstreamadapter.h index 3a7797370c3..1811f9566b8 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslstreamadapter.h +++ b/chromium/third_party/libjingle/source/talk/base/sslstreamadapter.h @@ -2,26 +2,26 @@ * libjingle * Copyright 2004--2008, Google Inc. * - * Redistribution and use in source and binary forms, with or without + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - * 1. Redistributions of source code must retain the above copyright notice, + * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products + * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -116,17 +116,6 @@ class SSLStreamAdapter : public StreamAdapterInterface { // underlying stream opens. virtual int StartSSLWithPeer() = 0; - // Specify the certificate that our peer is expected to use in - // peer-to-peer mode. Only this certificate will be accepted during - // SSL verification. The certificate is assumed to have been - // obtained through some other secure channel (such as the XMPP - // channel). (This could also specify the certificate authority that - // will sign the peer's certificate.) - // SSLStream takes ownership of the SSLCertificate object and will - // free it when appropriate. Should be called no more than once on a - // given SSLStream instance. - virtual void SetPeerCertificate(SSLCertificate* cert) = 0; - // Specify the digest of the certificate that our peer is expected to use in // peer-to-peer mode. Only this certificate will be accepted during // SSL verification. The certificate is assumed to have been @@ -138,11 +127,9 @@ class SSLStreamAdapter : public StreamAdapterInterface { const unsigned char* digest_val, size_t digest_len) = 0; - // Retrieves the peer's X.509 certificate, if a certificate has been - // provided by SetPeerCertificate or a connection has been established. If - // a connection has been established, this returns the - // certificate transmitted over SSL, including the entire chain. - // The returned certificate is owned by the caller. + // Retrieves the peer's X.509 certificate, if a connection has been + // established. It returns the transmitted over SSL, including the entire + // chain. The returned certificate is owned by the caller. virtual bool GetPeerCertificate(SSLCertificate** cert) const = 0; // Key Exporter interface from RFC 5705 diff --git a/chromium/third_party/libjingle/source/talk/base/sslstreamadapter_unittest.cc b/chromium/third_party/libjingle/source/talk/base/sslstreamadapter_unittest.cc index 4b2fd6d84c0..fe276b3fcc7 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslstreamadapter_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/sslstreamadapter_unittest.cc @@ -49,32 +49,33 @@ static int kExporterContextLen = sizeof(kExporterContext); static const char kRSA_PRIVATE_KEY_PEM[] = "-----BEGIN RSA PRIVATE KEY-----\n" - "MIICXQIBAAKBgQDCueE4a9hDMZ3sbVZdlXOz9ZA+cvzie3zJ9gXnT/BCt9P4b9HE\n" - "vD/tr73YBqD3Wr5ZWScmyGYF9EMn0r3rzBxv6oooLU5TdUvOm4rzUjkCLQaQML8o\n" - "NxXq+qW/j3zUKGikLhaaAl/amaX2zSWUsRQ1CpngQ3+tmDNH4/25TncNmQIDAQAB\n" - "AoGAUcuU0Id0k10fMjYHZk4mCPzot2LD2Tr4Aznl5vFMQipHzv7hhZtx2xzMSRcX\n" - "vG+Qr6VkbcUWHgApyWubvZXCh3+N7Vo2aYdMAQ8XqmFpBdIrL5CVdVfqFfEMlgEy\n" - "LSZNG5klnrIfl3c7zQVovLr4eMqyl2oGfAqPQz75+fecv1UCQQD6wNHch9NbAG1q\n" - "yuFEhMARB6gDXb+5SdzFjjtTWW5uJfm4DcZLoYyaIZm0uxOwsUKd0Rsma+oGitS1\n" - "CXmuqfpPAkEAxszyN3vIdpD44SREEtyKZBMNOk5pEIIGdbeMJC5/XHvpxww9xkoC\n" - "+39NbvUZYd54uT+rafbx4QZKc0h9xA/HlwJBAL37lYVWy4XpPv1olWCKi9LbUCqs\n" - "vvQtyD1N1BkEayy9TQRsO09WKOcmigRqsTJwOx7DLaTgokEuspYvhagWVPUCQE/y\n" - "0+YkTbYBD1Xbs9SyBKXCU6uDJRWSdO6aZi2W1XloC9gUwDMiSJjD1Wwt/YsyYPJ+\n" - "/Hyc5yFL2l0KZimW/vkCQQCjuZ/lPcH46EuzhdbRfumDOG5N3ld7UhGI1TIRy17W\n" - "dGF90cG33/L6BfS8Ll+fkkW/2AMRk8FDvF4CZi2nfW4L\n" + "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMYRkbhmI7kVA/rM\n" + "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" + "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" + "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAECgYAvgOs4FJcgvp+TuREx7YtiYVsH\n" + "mwQPTum2z/8VzWGwR8BBHBvIpVe1MbD/Y4seyI2aco/7UaisatSgJhsU46/9Y4fq\n" + "2TwXH9QANf4at4d9n/R6rzwpAJOpgwZgKvdQjkfrKTtgLV+/dawvpxUYkRH4JZM1\n" + "CVGukMfKNrSVH4Ap4QJBAOJmGV1ASPnB4r4nc99at7JuIJmd7fmuVUwUgYi4XgaR\n" + "WhScBsgYwZ/JoywdyZJgnbcrTDuVcWG56B3vXbhdpMsCQQDf9zeJrjnPZ3Cqm79y\n" + "kdqANep0uwZciiNiWxsQrCHztywOvbFhdp8iYVFG9EK8DMY41Y5TxUwsHD+67zao\n" + "ZNqJAkEA1suLUP/GvL8IwuRneQd2tWDqqRQ/Td3qq03hP7e77XtF/buya3Ghclo5\n" + "54czUR89QyVfJEC6278nzA7n2h1uVQJAcG6mztNL6ja/dKZjYZye2CY44QjSlLo0\n" + "MTgTSjdfg/28fFn2Jjtqf9Pi/X+50LWI/RcYMC2no606wRk9kyOuIQJBAK6VSAim\n" + "1pOEjsYQn0X5KEIrz1G3bfCbB848Ime3U2/FWlCHMr6ch8kCZ5d1WUeJD3LbwMNG\n" + "UCXiYxSsu20QNVw=\n" "-----END RSA PRIVATE KEY-----\n"; static const char kCERT_PEM[] = "-----BEGIN CERTIFICATE-----\n" - "MIIBmTCCAQICCQCPNJORW/M13DANBgkqhkiG9w0BAQUFADARMQ8wDQYDVQQDDAZ3\n" - "ZWJydGMwHhcNMTMwNjE0MjIzMDAxWhcNMTQwNjE0MjIzMDAxWjARMQ8wDQYDVQQD\n" - "DAZ3ZWJydGMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMK54Thr2EMxnext\n" - "Vl2Vc7P1kD5y/OJ7fMn2BedP8EK30/hv0cS8P+2vvdgGoPdavllZJybIZgX0QyfS\n" - "vevMHG/qiigtTlN1S86bivNSOQItBpAwvyg3Fer6pb+PfNQoaKQuFpoCX9qZpfbN\n" - "JZSxFDUKmeBDf62YM0fj/blOdw2ZAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAECMt\n" - "UZb35H8TnjGx4XPzco/kbnurMLFFWcuve/DwTsuf10Ia9N4md8LY0UtgIgtyNqWc\n" - "ZwyRMwxONF6ty3wcaIiPbGqiAa55T3YRuPibkRmck9CjrmM9JAtyvqHnpHd2TsBD\n" - "qCV42aXS3onOXDQ1ibuWq0fr0//aj0wo4KV474c=\n" + "MIIBmTCCAQKgAwIBAgIEbzBSAjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZX\n" + "ZWJSVEMwHhcNMTQwMTAyMTgyNDQ3WhcNMTQwMjAxMTgyNDQ3WjARMQ8wDQYDVQQD\n" + "EwZXZWJSVEMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYRkbhmI7kVA/rM\n" + "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" + "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" + "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAUflI\n" + "VUe5Krqf5RVa5C3u/UTAOAUJBiDS3VANTCLBxjuMsvqOG0WvaYWP3HYPgrz0jXK2\n" + "LJE/mGw3MyFHEqi81jh95J+ypl6xKW6Rm8jKLR87gUvCaVYn/Z4/P3AqcQTB7wOv\n" + "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\n" "-----END CERTIFICATE-----\n"; #define MAYBE_SKIP_TEST(feature) \ @@ -208,13 +209,47 @@ class SSLStreamAdapterTestBase : public testing::Test, ~SSLStreamAdapterTestBase() { // Put it back for the next test. talk_base::SetRandomTestMode(false); - talk_base::CleanupSSL(); } static void SetUpTestCase() { talk_base::InitializeSSL(); } + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } + + // Recreate the client/server identities with the specified validity period. + // |not_before| and |not_after| are offsets from the current time in number + // of seconds. + void ResetIdentitiesWithValidity(int not_before, int not_after) { + client_stream_ = + new SSLDummyStream(this, "c2s", &client_buffer_, &server_buffer_); + server_stream_ = + new SSLDummyStream(this, "s2c", &server_buffer_, &client_buffer_); + + client_ssl_.reset(talk_base::SSLStreamAdapter::Create(client_stream_)); + server_ssl_.reset(talk_base::SSLStreamAdapter::Create(server_stream_)); + + client_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent); + server_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent); + + talk_base::SSLIdentityParams client_params; + client_params.common_name = "client"; + client_params.not_before = not_before; + client_params.not_after = not_after; + client_identity_ = talk_base::SSLIdentity::GenerateForTest(client_params); + + talk_base::SSLIdentityParams server_params; + server_params.common_name = "server"; + server_params.not_before = not_before; + server_params.not_after = not_after; + server_identity_ = talk_base::SSLIdentity::GenerateForTest(server_params); + + client_ssl_->SetIdentity(client_identity_); + server_ssl_->SetIdentity(server_identity_); + } + virtual void OnEvent(talk_base::StreamInterface *stream, int sig, int err) { LOG(LS_INFO) << "SSLStreamAdapterTestBase::OnEvent sig=" << sig; @@ -227,24 +262,6 @@ class SSLStreamAdapterTestBase : public testing::Test, } } - void SetPeerIdentitiesByCertificate(bool correct) { - LOG(LS_INFO) << "Setting peer identities by certificate"; - - if (correct) { - client_ssl_->SetPeerCertificate(server_identity_->certificate(). - GetReference()); - server_ssl_->SetPeerCertificate(client_identity_->certificate(). - GetReference()); - } else { - // If incorrect, set up to expect our own certificate at the peer - client_ssl_->SetPeerCertificate(client_identity_->certificate(). - GetReference()); - server_ssl_->SetPeerCertificate(server_identity_->certificate(). - GetReference()); - } - identities_set_ = true; - } - void SetPeerIdentitiesByDigest(bool correct) { unsigned char digest[20]; size_t digest_len; @@ -253,8 +270,8 @@ class SSLStreamAdapterTestBase : public testing::Test, LOG(LS_INFO) << "Setting peer identities by digest"; rv = server_identity_->certificate().ComputeDigest(talk_base::DIGEST_SHA_1, - digest, 20, - &digest_len); + digest, 20, + &digest_len); ASSERT_TRUE(rv); if (!correct) { LOG(LS_INFO) << "Setting bogus digest for server cert"; @@ -266,7 +283,7 @@ class SSLStreamAdapterTestBase : public testing::Test, rv = client_identity_->certificate().ComputeDigest(talk_base::DIGEST_SHA_1, - digest, 20, &digest_len); + digest, 20, &digest_len); ASSERT_TRUE(rv); if (!correct) { LOG(LS_INFO) << "Setting bogus digest for client cert"; @@ -722,17 +739,6 @@ TEST_F(SSLStreamAdapterTestTLS, TestTLSBogusDigest) { TestHandshake(false); }; -// Test a handshake with a peer certificate -TEST_F(SSLStreamAdapterTestTLS, TestTLSPeerCertificate) { - SetPeerIdentitiesByCertificate(true); - TestHandshake(); -}; - -// Test a handshake with a bogus peer certificate -TEST_F(SSLStreamAdapterTestTLS, TestTLSBogusPeerCertificate) { - SetPeerIdentitiesByCertificate(false); - TestHandshake(false); -}; // Test moving a bunch of data // Basic tests: DTLS @@ -762,7 +768,7 @@ TEST_F(SSLStreamAdapterTestDTLS, }; // Test a handshake with small MTU -TEST_F(SSLStreamAdapterTestDTLS, DISABLED_TestDTLSConnectWithSmallMtu) { +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSConnectWithSmallMtu) { MAYBE_SKIP_TEST(HaveDtls); SetMtu(700); SetHandshakeWait(20000); @@ -887,6 +893,24 @@ TEST_F(SSLStreamAdapterTestDTLS, TestDTLSExporter) { ASSERT_TRUE(!memcmp(client_out, server_out, sizeof(client_out))); } +// Test not yet valid certificates are not rejected. +TEST_F(SSLStreamAdapterTestDTLS, TestCertNotYetValid) { + MAYBE_SKIP_TEST(HaveDtls); + long one_day = 60 * 60 * 24; + // Make the certificates not valid until one day later. + ResetIdentitiesWithValidity(one_day, one_day); + TestHandshake(); +} + +// Test expired certificates are not rejected. +TEST_F(SSLStreamAdapterTestDTLS, TestCertExpired) { + MAYBE_SKIP_TEST(HaveDtls); + long one_day = 60 * 60 * 24; + // Make the certificates already expired. + ResetIdentitiesWithValidity(-one_day, -one_day); + TestHandshake(); +} + // Test data transfer using certs created from strings. TEST_F(SSLStreamAdapterTestDTLSFromPEMStrings, TestTransfer) { MAYBE_SKIP_TEST(HaveDtls); diff --git a/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.cc b/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.cc index b42faa80c60..7be2878d016 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.cc +++ b/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.cc @@ -80,13 +80,6 @@ StreamState SSLStreamAdapterHelper::GetState() const { // not reached } -void SSLStreamAdapterHelper::SetPeerCertificate(SSLCertificate* cert) { - ASSERT(peer_certificate_.get() == NULL); - ASSERT(peer_certificate_digest_algorithm_.empty()); - ASSERT(ssl_server_name_.empty()); - peer_certificate_.reset(cert); -} - bool SSLStreamAdapterHelper::GetPeerCertificate(SSLCertificate** cert) const { if (!peer_certificate_) return false; diff --git a/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.h b/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.h index 7c280566127..5023d52ec19 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.h +++ b/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.h @@ -59,7 +59,6 @@ class SSLStreamAdapterHelper : public SSLStreamAdapter { virtual int StartSSLWithServer(const char* server_name); virtual int StartSSLWithPeer(); - virtual void SetPeerCertificate(SSLCertificate* cert); virtual bool SetPeerCertificateDigest(const std::string& digest_alg, const unsigned char* digest_val, size_t digest_len); @@ -88,8 +87,8 @@ class SSLStreamAdapterHelper : public SSLStreamAdapter { // Must be implemented by descendents virtual int BeginSSL() = 0; virtual void Cleanup() = 0; - virtual bool GetDigestLength(const std::string &algorithm, - std::size_t *length) = 0; + virtual bool GetDigestLength(const std::string& algorithm, + size_t* length) = 0; enum SSLState { // Before calling one of the StartSSL methods, data flows @@ -114,12 +113,10 @@ class SSLStreamAdapterHelper : public SSLStreamAdapter { // in traditional mode, the server name that the server's certificate // must specify. Empty in peer-to-peer mode. std::string ssl_server_name_; - // In peer-to-peer mode, the certificate that the peer must - // present. Empty in traditional mode. + // The peer's certificate. Only used for GetPeerCertificate. scoped_ptr<SSLCertificate> peer_certificate_; - // In peer-to-peer mode, the digest of the certificate that - // the peer must present. + // The digest of the certificate that the peer must present. Buffer peer_certificate_digest_value_; std::string peer_certificate_digest_algorithm_; diff --git a/chromium/third_party/libjingle/source/talk/base/stream.cc b/chromium/third_party/libjingle/source/talk/base/stream.cc index c1cf90743a0..02ae4094fa5 100644 --- a/chromium/third_party/libjingle/source/talk/base/stream.cc +++ b/chromium/third_party/libjingle/source/talk/base/stream.cc @@ -495,7 +495,7 @@ bool FileStream::Flush() { return false; } -#if defined(POSIX) +#if defined(POSIX) && !defined(__native_client__) bool FileStream::TryLock() { if (file_ == NULL) { @@ -711,7 +711,7 @@ void AsyncWriteStream::ClearBufferAndWrite() { } } -#ifdef POSIX +#if defined(POSIX) && !defined(__native_client__) // Have to identically rewrite the FileStream destructor or else it would call // the base class's Close() instead of the sub-class's. diff --git a/chromium/third_party/libjingle/source/talk/base/stream.h b/chromium/third_party/libjingle/source/talk/base/stream.h index 4571def9bb9..fceb4a80f0d 100644 --- a/chromium/third_party/libjingle/source/talk/base/stream.h +++ b/chromium/third_party/libjingle/source/talk/base/stream.h @@ -28,6 +28,8 @@ #ifndef TALK_BASE_STREAM_H_ #define TALK_BASE_STREAM_H_ +#include <stdio.h> + #include "talk/base/basictypes.h" #include "talk/base/buffer.h" #include "talk/base/criticalsection.h" @@ -449,7 +451,7 @@ class FileStream : public StreamInterface { virtual bool Flush(); -#if defined(POSIX) +#if defined(POSIX) && !defined(__native_client__) // Tries to aquire an exclusive lock on the file. // Use OpenShare(...) on win32 to get similar functionality. bool TryLock(); @@ -497,7 +499,6 @@ class CircularFileStream : public FileStream { size_t read_segment_available_; }; - // A stream which pushes writes onto a separate thread and // returns from the write call immediately. class AsyncWriteStream : public StreamInterface { @@ -539,7 +540,7 @@ class AsyncWriteStream : public StreamInterface { }; -#ifdef POSIX +#if defined(POSIX) && !defined(__native_client__) // A FileStream that is actually not a file, but the output or input of a // sub-command. See "man 3 popen" for documentation of the underlying OS popen() // function. diff --git a/chromium/third_party/libjingle/source/talk/base/stringencode.cc b/chromium/third_party/libjingle/source/talk/base/stringencode.cc index 194848e1ec6..4c88a9772f1 100644 --- a/chromium/third_party/libjingle/source/talk/base/stringencode.cc +++ b/chromium/third_party/libjingle/source/talk/base/stringencode.cc @@ -27,8 +27,8 @@ #include "talk/base/stringencode.h" -#include <cstdio> -#include <cstdlib> +#include <stdio.h> +#include <stdlib.h> #include "talk/base/basictypes.h" #include "talk/base/common.h" diff --git a/chromium/third_party/libjingle/source/talk/base/stringutils.h b/chromium/third_party/libjingle/source/talk/base/stringutils.h index 9f9e1a65a55..2c12118b96f 100644 --- a/chromium/third_party/libjingle/source/talk/base/stringutils.h +++ b/chromium/third_party/libjingle/source/talk/base/stringutils.h @@ -31,6 +31,7 @@ #include <ctype.h> #include <stdarg.h> #include <stdio.h> +#include <string.h> #ifdef WIN32 #include <malloc.h> @@ -46,7 +47,6 @@ #endif // !BSD #endif // POSIX -#include <cstring> #include <string> #include "talk/base/basictypes.h" diff --git a/chromium/third_party/libjingle/source/talk/base/template_util.h b/chromium/third_party/libjingle/source/talk/base/template_util.h index 5cc4ba430a2..00f1d7d4a11 100644 --- a/chromium/third_party/libjingle/source/talk/base/template_util.h +++ b/chromium/third_party/libjingle/source/talk/base/template_util.h @@ -5,7 +5,7 @@ #ifndef TALK_BASE_TEMPLATE_UTIL_H_ #define TALK_BASE_TEMPLATE_UTIL_H_ -#include <cstddef> // For size_t. +#include <stddef.h> // For size_t. namespace talk_base { diff --git a/chromium/third_party/libjingle/source/talk/base/testclient.cc b/chromium/third_party/libjingle/source/talk/base/testclient.cc index 04d60309963..92e1f397424 100644 --- a/chromium/third_party/libjingle/source/talk/base/testclient.cc +++ b/chromium/third_party/libjingle/source/talk/base/testclient.cc @@ -25,7 +25,6 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "talk/base/dscp.h" #include "talk/base/testclient.h" #include "talk/base/thread.h" #include "talk/base/timeutils.h" @@ -59,12 +58,14 @@ bool TestClient::CheckConnState(AsyncPacketSocket::State state) { } int TestClient::Send(const char* buf, size_t size) { - return socket_->Send(buf, size, DSCP_NO_CHANGE); + talk_base::PacketOptions options; + return socket_->Send(buf, size, options); } int TestClient::SendTo(const char* buf, size_t size, const SocketAddress& dest) { - return socket_->SendTo(buf, size, dest, DSCP_NO_CHANGE); + talk_base::PacketOptions options; + return socket_->SendTo(buf, size, dest, options); } TestClient::Packet* TestClient::NextPacket() { @@ -106,7 +107,7 @@ bool TestClient::CheckNextPacket(const char* buf, size_t size, bool res = false; Packet* packet = NextPacket(); if (packet) { - res = (packet->size == size && std::memcmp(packet->buf, buf, size) == 0); + res = (packet->size == size && memcmp(packet->buf, buf, size) == 0); if (addr) *addr = packet->addr; delete packet; diff --git a/chromium/third_party/libjingle/source/talk/base/testechoserver.h b/chromium/third_party/libjingle/source/talk/base/testechoserver.h index 5c1045423c3..380f9615363 100644 --- a/chromium/third_party/libjingle/source/talk/base/testechoserver.h +++ b/chromium/third_party/libjingle/source/talk/base/testechoserver.h @@ -69,7 +69,8 @@ class TestEchoServer : public sigslot::has_slots<> { void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t size, const SocketAddress& remote_addr, const PacketTime& packet_time) { - socket->Send(buf, size, DSCP_NO_CHANGE); + talk_base::PacketOptions options; + socket->Send(buf, size, options); } void OnClose(AsyncPacketSocket* socket, int err) { ClientList::iterator it = diff --git a/chromium/third_party/libjingle/source/talk/base/testutils.h b/chromium/third_party/libjingle/source/talk/base/testutils.h index e8ad7200940..86e946fc0d2 100644 --- a/chromium/third_party/libjingle/source/talk/base/testutils.h +++ b/chromium/third_party/libjingle/source/talk/base/testutils.h @@ -32,6 +32,8 @@ #ifdef LINUX #include <X11/Xlib.h> +#include <X11/extensions/Xrandr.h> + // X defines a few macros that stomp on types that gunit.h uses. #undef None #undef Bool @@ -43,6 +45,7 @@ #include "talk/base/common.h" #include "talk/base/gunit.h" #include "talk/base/nethelpers.h" +#include "talk/base/pathutils.h" #include "talk/base/stream.h" #include "talk/base/stringencode.h" #include "talk/base/stringutils.h" @@ -449,6 +452,30 @@ inline bool ReadFile(const char* filename, std::string* contents) { return success; } +// Look in parent dir for parallel directory. +inline talk_base::Pathname GetSiblingDirectory( + const std::string& parallel_dir) { + talk_base::Pathname path = talk_base::Filesystem::GetCurrentDirectory(); + while (!path.empty()) { + talk_base::Pathname potential_parallel_dir = path; + potential_parallel_dir.AppendFolder(parallel_dir); + if (talk_base::Filesystem::IsFolder(potential_parallel_dir)) { + return potential_parallel_dir; + } + + path.SetFolder(path.parent_folder()); + } + return path; +} + +inline talk_base::Pathname GetGoogle3Directory() { + return GetSiblingDirectory("google3"); +} + +inline talk_base::Pathname GetTalkDirectory() { + return GetSiblingDirectory("talk"); +} + /////////////////////////////////////////////////////////////////////////////// // Unittest predicates which are similar to STREQ, but for raw memory /////////////////////////////////////////////////////////////////////////////// @@ -601,6 +628,16 @@ inline bool IsScreencastingAvailable() { LOG(LS_WARNING) << "No X Display available."; return false; } + int ignored_int, major_version, minor_version; + if (!XRRQueryExtension(display, &ignored_int, &ignored_int) || + !XRRQueryVersion(display, &major_version, &minor_version) || + major_version < 1 || + (major_version < 2 && minor_version < 3)) { + LOG(LS_WARNING) << "XRandr version: " << major_version << "." + << minor_version; + LOG(LS_WARNING) << "XRandr is not supported or is too old (pre 1.3)."; + return false; + } #endif return true; } diff --git a/chromium/third_party/libjingle/source/talk/base/thread.cc b/chromium/third_party/libjingle/source/talk/base/thread.cc index d07efb51568..87e4ffff614 100644 --- a/chromium/third_party/libjingle/source/talk/base/thread.cc +++ b/chromium/third_party/libjingle/source/talk/base/thread.cc @@ -145,20 +145,18 @@ struct ThreadInit { Thread::Thread(SocketServer* ss) : MessageQueue(ss), priority_(PRIORITY_NORMAL), - started_(false), + running_(true, false), #if defined(WIN32) thread_(NULL), thread_id_(0), #endif - owned_(true), - delete_self_when_complete_(false) { + owned_(true) { SetName("Thread", this); // default name } Thread::~Thread() { Stop(); - if (active_) - Clear(NULL); + Clear(NULL); } bool Thread::SleepMs(int milliseconds) { @@ -181,7 +179,7 @@ bool Thread::SleepMs(int milliseconds) { } bool Thread::SetName(const std::string& name, const void* obj) { - if (started_) return false; + if (running()) return false; name_ = name; if (obj) { char buf[16]; @@ -193,7 +191,7 @@ bool Thread::SetName(const std::string& name, const void* obj) { bool Thread::SetPriority(ThreadPriority priority) { #if defined(WIN32) - if (started_) { + if (running()) { BOOL ret = FALSE; if (priority == PRIORITY_NORMAL) { ret = ::SetThreadPriority(thread_, THREAD_PRIORITY_NORMAL); @@ -212,7 +210,7 @@ bool Thread::SetPriority(ThreadPriority priority) { return true; #else // TODO: Implement for Linux/Mac if possible. - if (started_) return false; + if (running()) return false; priority_ = priority; return true; #endif @@ -221,8 +219,8 @@ bool Thread::SetPriority(ThreadPriority priority) { bool Thread::Start(Runnable* runnable) { ASSERT(owned_); if (!owned_) return false; - ASSERT(!started_); - if (started_) return false; + ASSERT(!running()); + if (running()) return false; Restart(); // reset fStop_ if the thread is being restarted @@ -241,7 +239,7 @@ bool Thread::Start(Runnable* runnable) { thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreRun, init, flags, &thread_id_); if (thread_) { - started_ = true; + running_.Set(); if (priority_ != PRIORITY_NORMAL) { SetPriority(priority_); ::ResumeThread(thread_); @@ -252,6 +250,9 @@ bool Thread::Start(Runnable* runnable) { #elif defined(POSIX) pthread_attr_t attr; pthread_attr_init(&attr); + + // Thread priorities are not supported in NaCl. +#if !defined(__native_client__) if (priority_ != PRIORITY_NORMAL) { if (priority_ == PRIORITY_IDLE) { // There is no POSIX-standard way to set a below-normal priority for an @@ -279,18 +280,20 @@ bool Thread::Start(Runnable* runnable) { } } } +#endif // !defined(__native_client__) + int error_code = pthread_create(&thread_, &attr, PreRun, init); if (0 != error_code) { LOG(LS_ERROR) << "Unable to create pthread, error " << error_code; return false; } - started_ = true; + running_.Set(); #endif return true; } void Thread::Join() { - if (started_) { + if (running()) { ASSERT(!IsCurrent()); #if defined(WIN32) WaitForSingleObject(thread_, INFINITE); @@ -301,7 +304,7 @@ void Thread::Join() { void *pv; pthread_join(thread_, &pv); #endif - started_ = false; + running_.Reset(); } } @@ -352,10 +355,6 @@ void* Thread::PreRun(void* pv) { } else { init->thread->Run(); } - if (init->thread->delete_self_when_complete_) { - init->thread->started_ = false; - delete init->thread; - } delete init; return NULL; } @@ -398,7 +397,6 @@ void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { bool ready = false; { CritScope cs(&crit_); - EnsureActive(); _SendMessage smsg; smsg.thread = current_thread; smsg.msg = msg; @@ -518,7 +516,7 @@ bool Thread::WrapCurrent() { } bool Thread::WrapCurrentWithThreadManager(ThreadManager* thread_manager) { - if (started_) + if (running()) return false; #if defined(WIN32) // We explicitly ask for no rights other than synchronization. @@ -533,7 +531,7 @@ bool Thread::WrapCurrentWithThreadManager(ThreadManager* thread_manager) { thread_ = pthread_self(); #endif owned_ = false; - started_ = true; + running_.Set(); thread_manager->SetCurrentThread(this); return true; } @@ -546,7 +544,7 @@ void Thread::UnwrapCurrent() { LOG_GLE(LS_ERROR) << "When unwrapping thread, failed to close handle."; } #endif - started_ = false; + running_.Reset(); } diff --git a/chromium/third_party/libjingle/source/talk/base/thread.h b/chromium/third_party/libjingle/source/talk/base/thread.h index 4dc09f641d7..4cbf721fa63 100644 --- a/chromium/third_party/libjingle/source/talk/base/thread.h +++ b/chromium/third_party/libjingle/source/talk/base/thread.h @@ -37,6 +37,7 @@ #include <pthread.h> #endif #include "talk/base/constructormagic.h" +#include "talk/base/event.h" #include "talk/base/messagequeue.h" #ifdef WIN32 @@ -143,15 +144,8 @@ class Thread : public MessageQueue { bool SetPriority(ThreadPriority priority); // Starts the execution of the thread. - bool started() const { return started_; } bool Start(Runnable* runnable = NULL); - // Used for fire-and-forget threads. Deletes this thread object when the - // Run method returns. - void Release() { - delete_self_when_complete_ = true; - } - // Tells the thread to stop and waits until it is joined. // Never call Stop on the current thread. Instead use the inherited Quit // function which will exit the base MessageQueue without terminating the @@ -218,38 +212,24 @@ class Thread : public MessageQueue { bool WrapCurrent(); void UnwrapCurrent(); + // Expose private method running() for tests. + // + // DANGER: this is a terrible public API. Most callers that might want to + // call this likely do not have enough control/knowledge of the Thread in + // question to guarantee that the returned value remains true for the duration + // of whatever code is conditionally executing because of the return value! + bool RunningForTest() { return running(); } + // This is a legacy call-site that probably doesn't need to exist in the first + // place. + // TODO(fischman): delete once the ASSERT added in channelmanager.cc sticks + // for a month (ETA 2014/06/22). + bool RunningForChannelManager() { return running(); } + protected: // Blocks the calling thread until this thread has terminated. void Join(); private: - // Helper class to facilitate executing a functor on a thread. - template <class ReturnT, class FunctorT> - class FunctorMessageHandler : public MessageHandler { - public: - explicit FunctorMessageHandler(const FunctorT& functor) - : functor_(functor) {} - virtual void OnMessage(Message* msg) { - result_ = functor_(); - } - const ReturnT& result() const { return result_; } - private: - FunctorT functor_; - ReturnT result_; - }; - - // Specialization for ReturnT of void. - template <class FunctorT> - class FunctorMessageHandler<void, FunctorT> : public MessageHandler { - public: - explicit FunctorMessageHandler(const FunctorT& functor) - : functor_(functor) {} - virtual void OnMessage(Message* msg) { functor_(); } - void result() const {} - private: - FunctorT functor_; - }; - static void *PreRun(void *pv); // ThreadManager calls this instead WrapCurrent() because @@ -257,10 +237,13 @@ class Thread : public MessageQueue { // being created. bool WrapCurrentWithThreadManager(ThreadManager* thread_manager); + // Return true if the thread was started and hasn't yet stopped. + bool running() { return running_.Wait(0); } + std::list<_SendMessage> sendlist_; std::string name_; ThreadPriority priority_; - bool started_; + Event running_; // Signalled means running. #ifdef POSIX pthread_t thread_; @@ -272,7 +255,6 @@ class Thread : public MessageQueue { #endif bool owned_; - bool delete_self_when_complete_; friend class ThreadManager; diff --git a/chromium/third_party/libjingle/source/talk/base/thread_unittest.cc b/chromium/third_party/libjingle/source/talk/base/thread_unittest.cc index 896fbabc5fd..d7d6a0129e4 100644 --- a/chromium/third_party/libjingle/source/talk/base/thread_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/thread_unittest.cc @@ -25,6 +25,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "talk/base/asyncinvoker.h" #include "talk/base/asyncudpsocket.h" #include "talk/base/event.h" #include "talk/base/gunit.h" @@ -38,8 +39,6 @@ using namespace talk_base; -const int MAX = 65536; - // Generates a sequence of numbers (collaboratively). class TestGenerator { public: @@ -87,7 +86,6 @@ class SocketClient : public TestGenerator, public sigslot::has_slots<> { uint32 prev = reinterpret_cast<const uint32*>(buf)[0]; uint32 result = Next(prev); - //socket_->set_readable(last < MAX); post_thread_->PostDelayed(200, post_handler_, 0, new TestMessage(result)); } @@ -101,7 +99,7 @@ class SocketClient : public TestGenerator, public sigslot::has_slots<> { class MessageClient : public MessageHandler, public TestGenerator { public: MessageClient(Thread* pth, Socket* socket) - : thread_(pth), socket_(socket) { + : socket_(socket) { } virtual ~MessageClient() { @@ -116,7 +114,6 @@ class MessageClient : public MessageHandler, public TestGenerator { } private: - Thread* thread_; Socket* socket_; }; @@ -150,16 +147,22 @@ class SignalWhenDestroyedThread : public Thread { }; // Function objects to test Thread::Invoke. -struct Functor1 { +struct FunctorA { int operator()() { return 42; } }; -class Functor2 { +class FunctorB { public: - explicit Functor2(bool* flag) : flag_(flag) {} + explicit FunctorB(bool* flag) : flag_(flag) {} void operator()() { if (flag_) *flag_ = true; } private: bool* flag_; }; +struct FunctorC { + int operator()() { + Thread::Current()->ProcessMessages(50); + return 24; + } +}; // See: https://code.google.com/p/webrtc/issues/detail?id=2409 TEST(ThreadTest, DISABLED_Main) { @@ -258,40 +261,22 @@ TEST(ThreadTest, Wrap) { current_thread->UnwrapCurrent(); CustomThread* cthread = new CustomThread(); EXPECT_TRUE(cthread->WrapCurrent()); - EXPECT_TRUE(cthread->started()); + EXPECT_TRUE(cthread->RunningForTest()); EXPECT_FALSE(cthread->IsOwned()); cthread->UnwrapCurrent(); - EXPECT_FALSE(cthread->started()); + EXPECT_FALSE(cthread->RunningForTest()); delete cthread; current_thread->WrapCurrent(); } -// Test that calling Release on a thread causes it to self-destruct when -// it's finished running -TEST(ThreadTest, Release) { - scoped_ptr<Event> event(new Event(true, false)); - // Ensure the event is initialized. - event->Reset(); - - Thread* thread = new SignalWhenDestroyedThread(event.get()); - thread->Start(); - thread->Release(); - - // The event should get signaled when the thread completes, which should - // be nearly instantaneous, since it doesn't do anything. For safety, - // give it 3 seconds in case the machine is under load. - bool signaled = event->Wait(3000); - EXPECT_TRUE(signaled); -} - TEST(ThreadTest, Invoke) { // Create and start the thread. Thread thread; thread.Start(); // Try calling functors. - EXPECT_EQ(42, thread.Invoke<int>(Functor1())); + EXPECT_EQ(42, thread.Invoke<int>(FunctorA())); bool called = false; - Functor2 f2(&called); + FunctorB f2(&called); thread.Invoke<void>(f2); EXPECT_TRUE(called); // Try calling bare functions. @@ -303,6 +288,152 @@ TEST(ThreadTest, Invoke) { thread.Invoke<void>(&LocalFuncs::Func2); } +class AsyncInvokeTest : public testing::Test { + public: + void IntCallback(int value) { + EXPECT_EQ(expected_thread_, Thread::Current()); + int_value_ = value; + } + void AsyncInvokeIntCallback(AsyncInvoker* invoker, Thread* thread) { + expected_thread_ = thread; + invoker->AsyncInvoke(thread, FunctorC(), + &AsyncInvokeTest::IntCallback, + static_cast<AsyncInvokeTest*>(this)); + invoke_started_.Set(); + } + void SetExpectedThreadForIntCallback(Thread* thread) { + expected_thread_ = thread; + } + + protected: + enum { kWaitTimeout = 1000 }; + AsyncInvokeTest() + : int_value_(0), + invoke_started_(true, false), + expected_thread_(NULL) {} + + int int_value_; + Event invoke_started_; + Thread* expected_thread_; +}; + +TEST_F(AsyncInvokeTest, FireAndForget) { + AsyncInvoker invoker; + // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functor. + bool called = false; + invoker.AsyncInvoke<void>(&thread, FunctorB(&called)); + EXPECT_TRUE_WAIT(called, kWaitTimeout); +} + +TEST_F(AsyncInvokeTest, WithCallback) { + AsyncInvoker invoker; + // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functor. + SetExpectedThreadForIntCallback(Thread::Current()); + invoker.AsyncInvoke(&thread, FunctorA(), + &AsyncInvokeTest::IntCallback, + static_cast<AsyncInvokeTest*>(this)); + EXPECT_EQ_WAIT(42, int_value_, kWaitTimeout); +} + +TEST_F(AsyncInvokeTest, CancelInvoker) { + // Create and start the thread. + Thread thread; + thread.Start(); + // Try destroying invoker during call. + { + AsyncInvoker invoker; + invoker.AsyncInvoke(&thread, FunctorC(), + &AsyncInvokeTest::IntCallback, + static_cast<AsyncInvokeTest*>(this)); + } + // With invoker gone, callback should be cancelled. + Thread::Current()->ProcessMessages(kWaitTimeout); + EXPECT_EQ(0, int_value_); +} + +TEST_F(AsyncInvokeTest, CancelCallingThread) { + AsyncInvoker invoker; + { // Create and start the thread. + Thread thread; + thread.Start(); + // Try calling functor. + thread.Invoke<void>(Bind(&AsyncInvokeTest::AsyncInvokeIntCallback, + static_cast<AsyncInvokeTest*>(this), + &invoker, Thread::Current())); + // Wait for the call to begin. + ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); + } + // Calling thread is gone. Return message shouldn't happen. + Thread::Current()->ProcessMessages(kWaitTimeout); + EXPECT_EQ(0, int_value_); +} + +TEST_F(AsyncInvokeTest, KillInvokerBeforeExecute) { + Thread thread; + thread.Start(); + { + AsyncInvoker invoker; + // Try calling functor. + thread.Invoke<void>(Bind(&AsyncInvokeTest::AsyncInvokeIntCallback, + static_cast<AsyncInvokeTest*>(this), + &invoker, Thread::Current())); + // Wait for the call to begin. + ASSERT_TRUE(invoke_started_.Wait(kWaitTimeout)); + } + // Invoker is destroyed. Function should not execute. + Thread::Current()->ProcessMessages(kWaitTimeout); + EXPECT_EQ(0, int_value_); +} + +TEST_F(AsyncInvokeTest, Flush) { + AsyncInvoker invoker; + bool flag1 = false; + bool flag2 = false; + // Queue two async calls to the current thread. + invoker.AsyncInvoke<void>(Thread::Current(), + FunctorB(&flag1)); + invoker.AsyncInvoke<void>(Thread::Current(), + FunctorB(&flag2)); + // Because we haven't pumped messages, these should not have run yet. + EXPECT_FALSE(flag1); + EXPECT_FALSE(flag2); + // Force them to run now. + invoker.Flush(Thread::Current()); + EXPECT_TRUE(flag1); + EXPECT_TRUE(flag2); +} + +TEST_F(AsyncInvokeTest, FlushWithIds) { + AsyncInvoker invoker; + bool flag1 = false; + bool flag2 = false; + // Queue two async calls to the current thread, one with a message id. + invoker.AsyncInvoke<void>(Thread::Current(), + FunctorB(&flag1), + 5); + invoker.AsyncInvoke<void>(Thread::Current(), + FunctorB(&flag2)); + // Because we haven't pumped messages, these should not have run yet. + EXPECT_FALSE(flag1); + EXPECT_FALSE(flag2); + // Execute pending calls with id == 5. + invoker.Flush(Thread::Current(), 5); + EXPECT_TRUE(flag1); + EXPECT_FALSE(flag2); + flag1 = false; + // Execute all pending calls. The id == 5 call should not execute again. + invoker.Flush(Thread::Current()); + EXPECT_FALSE(flag1); + EXPECT_TRUE(flag2); +} + + #ifdef WIN32 class ComThreadTest : public testing::Test, public MessageHandler { public: diff --git a/chromium/third_party/libjingle/source/talk/base/timeutils.cc b/chromium/third_party/libjingle/source/talk/base/timeutils.cc index 54db3418bbb..c4e84cc20db 100644 --- a/chromium/third_party/libjingle/source/talk/base/timeutils.cc +++ b/chromium/third_party/libjingle/source/talk/base/timeutils.cc @@ -25,6 +25,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <stdint.h> + #ifdef POSIX #include <sys/time.h> #if defined(OSX) || defined(IOS) @@ -45,7 +47,6 @@ namespace talk_base { -const uint32 LAST = 0xFFFFFFFF; const uint32 HALF = 0x80000000; uint64 TimeNanos() { @@ -190,16 +191,30 @@ int32 TimeDiff(uint32 later, uint32 earlier) { if (earlier <= later) { return static_cast<long>(later - earlier); } else { - return static_cast<long>(later + (LAST - earlier) + 1); + return static_cast<long>(later + (UINT32_MAX - earlier) + 1); } } else { if (later <= earlier) { return -static_cast<long>(earlier - later); } else { - return -static_cast<long>(earlier + (LAST - later) + 1); + return -static_cast<long>(earlier + (UINT32_MAX - later) + 1); } } #endif } +TimestampWrapAroundHandler::TimestampWrapAroundHandler() + : last_ts_(0), num_wrap_(0) {} + +int64 TimestampWrapAroundHandler::Unwrap(uint32 ts) { + if (ts < last_ts_) { + if (last_ts_ > 0xf0000000 && ts < 0x0fffffff) { + ++num_wrap_; + } + } + last_ts_ = ts; + int64_t unwrapped_ts = ts + (num_wrap_ << 32); + return unwrapped_ts; +} + } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/timeutils.h b/chromium/third_party/libjingle/source/talk/base/timeutils.h index f13c3f2ef2d..6de9df67142 100644 --- a/chromium/third_party/libjingle/source/talk/base/timeutils.h +++ b/chromium/third_party/libjingle/source/talk/base/timeutils.h @@ -97,6 +97,17 @@ inline int64 UnixTimestampNanosecsToNtpMillisecs(int64 unix_ts_ns) { return unix_ts_ns / kNumNanosecsPerMillisec + kJan1970AsNtpMillisecs; } +class TimestampWrapAroundHandler { + public: + TimestampWrapAroundHandler(); + + int64 Unwrap(uint32 ts); + + private: + uint32 last_ts_; + int64 num_wrap_; +}; + } // namespace talk_base #endif // TALK_BASE_TIMEUTILS_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/timeutils_unittest.cc b/chromium/third_party/libjingle/source/talk/base/timeutils_unittest.cc index 0fc5eb19c8a..a078abee2c1 100644 --- a/chromium/third_party/libjingle/source/talk/base/timeutils_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/timeutils_unittest.cc @@ -160,4 +160,27 @@ TEST(TimeTest, DISABLED_CurrentTmTime) { EXPECT_TRUE(0 <= microseconds && microseconds < 1000000); } +class TimestampWrapAroundHandlerTest : public testing::Test { + public: + TimestampWrapAroundHandlerTest() {} + + protected: + TimestampWrapAroundHandler wraparound_handler_; +}; + +TEST_F(TimestampWrapAroundHandlerTest, Unwrap) { + uint32 ts = 0xfffffff2; + int64 unwrapped_ts = ts; + EXPECT_EQ(ts, wraparound_handler_.Unwrap(ts)); + ts = 2; + unwrapped_ts += 0x10; + EXPECT_EQ(unwrapped_ts, wraparound_handler_.Unwrap(ts)); + ts = 0xfffffff2; + unwrapped_ts += 0xfffffff0; + EXPECT_EQ(unwrapped_ts, wraparound_handler_.Unwrap(ts)); + ts = 0; + unwrapped_ts += 0xe; + EXPECT_EQ(unwrapped_ts, wraparound_handler_.Unwrap(ts)); +} + } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/transformadapter.cc b/chromium/third_party/libjingle/source/talk/base/transformadapter.cc index 53a55a85b4d..2a240eb9f73 100644 --- a/chromium/third_party/libjingle/source/talk/base/transformadapter.cc +++ b/chromium/third_party/libjingle/source/talk/base/transformadapter.cc @@ -27,7 +27,7 @@ #include "talk/base/transformadapter.h" -#include <cstring> +#include <string.h> #include "talk/base/common.h" diff --git a/chromium/third_party/libjingle/source/talk/base/unittest_main.cc b/chromium/third_party/libjingle/source/talk/base/unittest_main.cc index bca3671b0c4..def763c30d8 100644 --- a/chromium/third_party/libjingle/source/talk/base/unittest_main.cc +++ b/chromium/third_party/libjingle/source/talk/base/unittest_main.cc @@ -12,7 +12,6 @@ #include "talk/base/fileutils.h" #include "talk/base/gunit.h" #include "talk/base/logging.h" -#include "talk/base/pathutils.h" DEFINE_bool(help, false, "prints this message"); DEFINE_string(log, "", "logging options to use"); @@ -47,28 +46,6 @@ int TestCrtReportHandler(int report_type, char* msg, int* retval) { } #endif // WIN32 -talk_base::Pathname GetTalkDirectory() { - // Locate talk directory. - talk_base::Pathname path = talk_base::Filesystem::GetCurrentDirectory(); - std::string talk_folder_name("talk"); - talk_folder_name += path.folder_delimiter(); - while (path.folder_name() != talk_folder_name && !path.empty()) { - path.SetFolder(path.parent_folder()); - } - - // If not running inside "talk" folder, then assume running in its parent - // folder. - if (path.empty()) { - path = talk_base::Filesystem::GetCurrentDirectory(); - path.AppendFolder("talk"); - // Make sure the folder exist. - if (!talk_base::Filesystem::IsFolder(path)) { - path.clear(); - } - } - return path; -} - int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); FlagList::SetFlagsFromCommandLine(&argc, argv, false); diff --git a/chromium/third_party/libjingle/source/talk/base/unixfilesystem.cc b/chromium/third_party/libjingle/source/talk/base/unixfilesystem.cc index 74168f267bd..8ac74994586 100644 --- a/chromium/third_party/libjingle/source/talk/base/unixfilesystem.cc +++ b/chromium/third_party/libjingle/source/talk/base/unixfilesystem.cc @@ -42,26 +42,39 @@ #if defined(POSIX) && !defined(OSX) #include <sys/types.h> -#ifdef ANDROID +#if defined(ANDROID) #include <sys/statfs.h> -#else +#elif !defined(__native_client__) #include <sys/statvfs.h> -#endif // ANDROID +#endif // !defined(__native_client__) +#include <limits.h> #include <pwd.h> #include <stdio.h> -#include <unistd.h> #endif // POSIX && !OSX -#ifdef LINUX +#if defined(LINUX) #include <ctype.h> #include <algorithm> #endif +#if defined(__native_client__) && !defined(__GLIBC__) +#include <sys/syslimits.h> +#endif + #include "talk/base/fileutils.h" #include "talk/base/pathutils.h" #include "talk/base/stream.h" #include "talk/base/stringutils.h" +#if defined(IOS) +// Defined in iosfilesystem.mm. No header file to discourage use +// elsewhere; other places should use GetApp{Data,Temp}Folder() in +// this file. Don't copy/paste. I mean it. +char* IOSDataDirectory(); +char* IOSTempDirectory(); +void IOSAppName(talk_base::Pathname* path); +#endif + namespace talk_base { #if !defined(ANDROID) && !defined(IOS) @@ -81,6 +94,17 @@ void UnixFilesystem::SetAppTempFolder(const std::string& folder) { } #endif +UnixFilesystem::UnixFilesystem() { +#if defined(IOS) + if (!provided_app_data_folder_) + provided_app_data_folder_ = IOSDataDirectory(); + if (!provided_app_temp_folder_) + provided_app_temp_folder_ = IOSTempDirectory(); +#endif +} + +UnixFilesystem::~UnixFilesystem() {} + bool UnixFilesystem::CreateFolder(const Pathname &path, mode_t mode) { std::string pathname(path.pathname()); int len = pathname.length(); @@ -363,10 +387,15 @@ bool UnixFilesystem::GetAppPathname(Pathname* path) { if (success) path->SetPathname(path8); return success; +#elif defined(__native_client__) + return false; +#elif IOS + IOSAppName(path); + return true; #else // OSX - char buffer[NAME_MAX+1]; - size_t len = readlink("/proc/self/exe", buffer, ARRAY_SIZE(buffer) - 1); - if (len <= 0) + char buffer[PATH_MAX + 2]; + ssize_t len = readlink("/proc/self/exe", buffer, ARRAY_SIZE(buffer) - 1); + if ((len <= 0) || (len == PATH_MAX + 1)) return false; buffer[len] = '\0'; path->SetPathname(buffer); @@ -448,6 +477,7 @@ bool UnixFilesystem::GetAppDataFolder(Pathname* path, bool per_user) { if (!CreateFolder(*path, 0700)) { return false; } +#if !defined(__native_client__) // If the folder already exists, it may have the wrong mode or be owned by // someone else, both of which are security problems. Setting the mode // avoids both issues since it will fail if the path is not owned by us. @@ -455,6 +485,7 @@ bool UnixFilesystem::GetAppDataFolder(Pathname* path, bool per_user) { LOG_ERR(LS_ERROR) << "Can't set mode on " << path; return false; } +#endif return true; } @@ -489,6 +520,9 @@ bool UnixFilesystem::GetAppTempFolder(Pathname* path) { } bool UnixFilesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { +#ifdef __native_client__ + return false; +#else // __native_client__ ASSERT(NULL != freebytes); // TODO: Consider making relative paths absolute using cwd. // TODO: When popping off a symlink, push back on the components of the @@ -510,11 +544,12 @@ bool UnixFilesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { #endif // ANDROID #if defined(LINUX) || defined(ANDROID) *freebytes = static_cast<int64>(vfs.f_bsize) * vfs.f_bavail; -#elif defined(OSX) +#elif defined(OSX) || defined(IOS) *freebytes = static_cast<int64>(vfs.f_frsize) * vfs.f_bavail; #endif return true; +#endif // !__native_client__ } Pathname UnixFilesystem::GetCurrentDirectory() { @@ -544,3 +579,11 @@ char* UnixFilesystem::CopyString(const std::string& str) { } } // namespace talk_base + +#if defined(__native_client__) +extern "C" int __attribute__((weak)) +link(const char* oldpath, const char* newpath) { + errno = EACCES; + return -1; +} +#endif diff --git a/chromium/third_party/libjingle/source/talk/base/unixfilesystem.h b/chromium/third_party/libjingle/source/talk/base/unixfilesystem.h index aa9c920e625..d709115fe9f 100644 --- a/chromium/third_party/libjingle/source/talk/base/unixfilesystem.h +++ b/chromium/third_party/libjingle/source/talk/base/unixfilesystem.h @@ -36,13 +36,17 @@ namespace talk_base { class UnixFilesystem : public FilesystemInterface { public: + UnixFilesystem(); + virtual ~UnixFilesystem(); #if defined(ANDROID) || defined(IOS) -// Android does not have a native code API to fetch the app data or temp -// folders. That needs to be passed into this class from Java. Similarly, iOS -// only supports an Objective-C API for fetching the folder locations, so that -// needs to be passed in here from Objective-C. - + // Android does not have a native code API to fetch the app data or temp + // folders. That needs to be passed into this class from Java. Similarly, iOS + // only supports an Objective-C API for fetching the folder locations, so that + // needs to be passed in here from Objective-C. Or at least that used to be + // the case; now the ctor will do the work if necessary and possible. + // TODO(fischman): add an Android version that uses JNI and drop the + // SetApp*Folder() APIs once external users stop using them. static void SetAppDataFolder(const std::string& folder); static void SetAppTempFolder(const std::string& folder); #endif diff --git a/chromium/third_party/libjingle/source/talk/base/versionparsing.cc b/chromium/third_party/libjingle/source/talk/base/versionparsing.cc index 03f3dec1ee9..6a57dc9fe0c 100644 --- a/chromium/third_party/libjingle/source/talk/base/versionparsing.cc +++ b/chromium/third_party/libjingle/source/talk/base/versionparsing.cc @@ -27,7 +27,7 @@ #include "talk/base/versionparsing.h" -#include <cstdlib> +#include <stdlib.h> namespace talk_base { diff --git a/chromium/third_party/libjingle/source/talk/base/virtualsocket_unittest.cc b/chromium/third_party/libjingle/source/talk/base/virtualsocket_unittest.cc index b31b8c8b074..58dab143a27 100644 --- a/chromium/third_party/libjingle/source/talk/base/virtualsocket_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/virtualsocket_unittest.cc @@ -25,11 +25,11 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <math.h> #include <time.h> #ifdef POSIX #include <netinet/in.h> #endif -#include <cmath> #include "talk/base/logging.h" #include "talk/base/gunit.h" @@ -69,7 +69,7 @@ struct Sender : public MessageHandler { count += size; memcpy(dummy, &cur_time, sizeof(cur_time)); - socket->Send(dummy, size, DSCP_NO_CHANGE); + socket->Send(dummy, size, options); last_send = cur_time; thread->PostDelayed(NextDelay(), this, 1); @@ -77,6 +77,7 @@ struct Sender : public MessageHandler { Thread* thread; scoped_ptr<AsyncUDPSocket> socket; + talk_base::PacketOptions options; bool done; uint32 rate; // bytes per second uint32 count; @@ -685,7 +686,7 @@ class VirtualSocketServerTest : public testing::Test { double num = receiver.samples * receiver.sum_sq - receiver.sum * receiver.sum; double den = receiver.samples * (receiver.samples - 1); - const double sample_stddev = std::sqrt(num / den); + const double sample_stddev = sqrt(num / den); LOG(LS_VERBOSE) << "mean=" << sample_mean << " stddev=" << sample_stddev; EXPECT_LE(500u, receiver.samples); @@ -1001,7 +1002,7 @@ TEST_F(VirtualSocketServerTest, CreatesStandardDistribution) { double dev = (*f)[i].second - mean; sum_sq_dev += dev * dev; } - const double stddev = std::sqrt(sum_sq_dev / f->size()); + const double stddev = sqrt(sum_sq_dev / f->size()); EXPECT_NEAR(kTestMean[midx], mean, 0.1 * kTestMean[midx]) << "M=" << kTestMean[midx] << " SD=" << kStdDev diff --git a/chromium/third_party/libjingle/source/talk/base/virtualsocketserver.cc b/chromium/third_party/libjingle/source/talk/base/virtualsocketserver.cc index 6d95c19d1d1..6589ebb5a20 100644 --- a/chromium/third_party/libjingle/source/talk/base/virtualsocketserver.cc +++ b/chromium/third_party/libjingle/source/talk/base/virtualsocketserver.cc @@ -28,9 +28,9 @@ #include "talk/base/virtualsocketserver.h" #include <errno.h> +#include <math.h> #include <algorithm> -#include <cmath> #include <map> #include <vector> @@ -80,7 +80,7 @@ class Packet : public MessageData { : size_(size), consumed_(0), from_(from) { ASSERT(NULL != data); data_ = new char[size_]; - std::memcpy(data_, data, size_); + memcpy(data_, data, size_); } virtual ~Packet() { @@ -283,7 +283,7 @@ class VirtualSocket : public AsyncSocket, public MessageHandler { // Return the packet at the front of the queue. Packet* packet = recv_buffer_.front(); size_t data_read = _min(cb, packet->size()); - std::memcpy(pv, packet->data(), data_read); + memcpy(pv, packet->data(), data_read); *paddr = packet->from(); if (data_read < packet->size()) { @@ -963,11 +963,11 @@ void VirtualSocketServer::UpdateDelayDistribution() { } } -static double PI = 4 * std::atan(1.0); +static double PI = 4 * atan(1.0); static double Normal(double x, double mean, double stddev) { double a = (x - mean) * (x - mean) / (2 * stddev * stddev); - return std::exp(-a) / (stddev * sqrt(2 * PI)); + return exp(-a) / (stddev * sqrt(2 * PI)); } #if 0 // static unused gives a warning diff --git a/chromium/third_party/libjingle/source/talk/base/virtualsocketserver.h b/chromium/third_party/libjingle/source/talk/base/virtualsocketserver.h index 280ae657295..56e37a14e3b 100644 --- a/chromium/third_party/libjingle/source/talk/base/virtualsocketserver.h +++ b/chromium/third_party/libjingle/source/talk/base/virtualsocketserver.h @@ -28,7 +28,8 @@ #ifndef TALK_BASE_VIRTUALSOCKETSERVER_H_ #define TALK_BASE_VIRTUALSOCKETSERVER_H_ -#include <cassert> +#include <assert.h> + #include <deque> #include <map> diff --git a/chromium/third_party/libjingle/source/talk/base/win32regkey.cc b/chromium/third_party/libjingle/source/talk/base/win32regkey.cc index 403fdc014c0..614f698e7c4 100644 --- a/chromium/third_party/libjingle/source/talk/base/win32regkey.cc +++ b/chromium/third_party/libjingle/source/talk/base/win32regkey.cc @@ -984,21 +984,21 @@ std::wstring RegKey::GetParentKeyInfo(std::wstring* key_name) { uint32 RegKey::GetValueCount() { DWORD num_values = 0; - LONG res = ::RegQueryInfoKey( - h_key_, // key handle - NULL, // buffer for class name - NULL, // size of class string - NULL, // reserved - NULL, // number of subkeys - NULL, // longest subkey size - NULL, // longest class string - &num_values, // number of values for this key - NULL, // longest value name - NULL, // longest value data - NULL, // security descriptor - NULL); // last write time - - ASSERT(res == ERROR_SUCCESS); + if (ERROR_SUCCESS != ::RegQueryInfoKey( + h_key_, // key handle + NULL, // buffer for class name + NULL, // size of class string + NULL, // reserved + NULL, // number of subkeys + NULL, // longest subkey size + NULL, // longest class string + &num_values, // number of values for this key + NULL, // longest value name + NULL, // longest value data + NULL, // security descriptor + NULL)) { // last write time + ASSERT(false); + } return num_values; } @@ -1028,21 +1028,21 @@ uint32 RegKey::GetSubkeyCount() { // number of values for key DWORD num_subkeys = 0; - LONG res = ::RegQueryInfoKey( - h_key_, // key handle - NULL, // buffer for class name - NULL, // size of class string - NULL, // reserved - &num_subkeys, // number of subkeys - NULL, // longest subkey size - NULL, // longest class string - NULL, // number of values for this key - NULL, // longest value name - NULL, // longest value data - NULL, // security descriptor - NULL); // last write time - - ASSERT(res == ERROR_SUCCESS); + if (ERROR_SUCCESS != ::RegQueryInfoKey( + h_key_, // key handle + NULL, // buffer for class name + NULL, // size of class string + NULL, // reserved + &num_subkeys, // number of subkeys + NULL, // longest subkey size + NULL, // longest class string + NULL, // number of values for this key + NULL, // longest value name + NULL, // longest value data + NULL, // security descriptor + NULL)) { // last write time + ASSERT(false); + } return num_subkeys; } diff --git a/chromium/third_party/libjingle/source/talk/base/win32toolhelp_unittest.cc b/chromium/third_party/libjingle/source/talk/base/win32toolhelp_unittest.cc index 529bef95ab5..e740345978c 100644 --- a/chromium/third_party/libjingle/source/talk/base/win32toolhelp_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/win32toolhelp_unittest.cc @@ -267,7 +267,6 @@ TEST_F(Win32ToolhelpTest, TestCurrentNextCalled) { } TEST_F(Win32ToolhelpTest, TestCurrentProcess) { - int size = MAX_PATH; WCHAR buf[MAX_PATH]; GetModuleFileName(NULL, buf, ARRAY_SIZE(buf)); std::wstring name = ToUtf16(Pathname(ToUtf8(buf)).filename()); diff --git a/chromium/third_party/libjingle/source/talk/base/windowpickerfactory.h b/chromium/third_party/libjingle/source/talk/base/windowpickerfactory.h index b55cc7dc35e..e9ba6c46a2a 100644 --- a/chromium/third_party/libjingle/source/talk/base/windowpickerfactory.h +++ b/chromium/third_party/libjingle/source/talk/base/windowpickerfactory.h @@ -55,7 +55,7 @@ class WindowPickerFactory { return new Win32WindowPicker(); #elif defined(OSX) return new MacWindowPicker(); -#elif defined(LINUX) +#elif defined(LINUX) && defined(HAVE_X11) return new LinuxWindowPicker(); #else return NULL; diff --git a/chromium/third_party/libjingle/source/talk/base/winping.cc b/chromium/third_party/libjingle/source/talk/base/winping.cc index 001740ad26d..fd25a2374d4 100644 --- a/chromium/third_party/libjingle/source/talk/base/winping.cc +++ b/chromium/third_party/libjingle/source/talk/base/winping.cc @@ -27,8 +27,8 @@ #include "talk/base/winping.h" +#include <assert.h> #include <Iphlpapi.h> -#include <cassert> #include "talk/base/byteorder.h" #include "talk/base/common.h" diff --git a/chromium/third_party/libjingle/source/talk/build/OWNERS b/chromium/third_party/libjingle/source/talk/build/OWNERS new file mode 100644 index 00000000000..3ee6b4bf5f9 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/build/OWNERS @@ -0,0 +1,5 @@ + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* diff --git a/chromium/third_party/libjingle/source/talk/build/common.gypi b/chromium/third_party/libjingle/source/talk/build/common.gypi index 50acb39ea95..8261c2085e3 100644 --- a/chromium/third_party/libjingle/source/talk/build/common.gypi +++ b/chromium/third_party/libjingle/source/talk/build/common.gypi @@ -46,7 +46,8 @@ }, 'target_defaults': { 'include_dirs': [ - '../..', + '<(libjingle_root)', + '<(DEPTH)', '../../third_party', '../../third_party/webrtc', '../../webrtc', @@ -85,6 +86,9 @@ 'conditions': [ ['clang==1', { 'cflags': [ + '-Wall', + '-Wextra', + '-Wunused-variable', # TODO(ronghuawu): Fix the warning caused by # LateBindingSymbolTable::TableInfo from # latebindingsymboltable.cc.def and remove below flag. @@ -101,11 +105,10 @@ ['OS=="ios"', { 'defines': [ 'IOS', - 'HAVE_NSS_SSL_H=1', - 'SSL_USE_NSS_RNG', ], - 'defines!': [ - 'HAVE_OPENSSL_SSL_H=1', + }, { + 'defines': [ + 'HAVE_SCTP', ], }], ['OS=="ios" or (OS=="mac" and target_arch!="ia32")', { @@ -114,15 +117,55 @@ ], }], ['os_posix==1', { + 'configurations': { + 'Debug_Base': { + 'defines': [ + # Chromium's build/common.gypi defines this for all posix _except_ + # for ios & mac. We want it there as well, e.g. because ASSERT + # and friends trigger off of it. + '_DEBUG', + ], + }, + }, 'defines': [ 'HASH_NAMESPACE=__gnu_cxx', 'POSIX', 'DISABLE_DYNAMIC_CAST', - 'HAVE_OPENSSL_SSL_H=1', # The POSIX standard says we have to define this. '_REENTRANT', ], }], + # TODO(jiayl): collapse the following 5 defines into 2, one for NSS and + # one for OPENSSL, and update the relevant code. + ['use_openssl==1', { + 'defines': [ + 'SSL_USE_OPENSSL', + 'HAVE_OPENSSL_SSL_H', + ], + 'dependencies': [ + '<(DEPTH)/third_party/openssl/openssl.gyp:openssl', + ], + }, { + 'defines': [ + 'SSL_USE_NSS', + 'HAVE_NSS_SSL_H', + 'SSL_USE_NSS_RNG', + ], + 'conditions': [ + ['os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', { + 'dependencies': [ + '<(DEPTH)/build/linux/system.gyp:ssl', + ], + }], + ['OS == "mac" or OS == "ios" or OS == "win"', { + 'dependencies': [ + '<(DEPTH)/net/third_party/nss/ssl.gyp:libssl', + '<(DEPTH)/third_party/nss/nss.gyp:nspr', + '<(DEPTH)/third_party/nss/nss.gyp:nss', + ], + }], + ], + }], ], }, # target_defaults } diff --git a/chromium/third_party/libjingle/source/talk/build/ios_test.plist b/chromium/third_party/libjingle/source/talk/build/ios_test.plist new file mode 100644 index 00000000000..c2fb0617f33 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/build/ios_test.plist @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>com.Google.${PRODUCT_NAME:rfc1034identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1.0</string> +</dict> +</plist> diff --git a/chromium/third_party/libjingle/source/talk/build/ios_tests.gypi b/chromium/third_party/libjingle/source/talk/build/ios_tests.gypi new file mode 100644 index 00000000000..baf1f100a61 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/build/ios_tests.gypi @@ -0,0 +1,55 @@ +# +# libjingle +# Copyright 2014, Google Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# Include this .gypi in an ObjC target's definition to allow it to be +# used as an iOS or OS/X application. + +{ + 'conditions': [ + ['OS=="ios"', { + 'variables': { + 'infoplist_file': './ios_test.plist', + }, + 'mac_bundle': 1, + 'mac_bundle_resources': [ + '<(infoplist_file)', + ], + # The plist is listed above so that it appears in XCode's file list, + # but we don't actually want to bundle it. + 'mac_bundle_resources!': [ + '<(infoplist_file)', + ], + 'xcode_settings': { + 'CLANG_ENABLE_OBJC_ARC': 'YES', + # common.gypi enables this for mac but we want this to be disabled + # like it is for ios. + 'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO', + 'INFOPLIST_FILE': '<(infoplist_file)', + }, + }], + ], # conditions +} diff --git a/chromium/third_party/libjingle/source/talk/build/isolate.gypi b/chromium/third_party/libjingle/source/talk/build/isolate.gypi index 7b0ac1254d6..24f7ea1edc1 100644 --- a/chromium/third_party/libjingle/source/talk/build/isolate.gypi +++ b/chromium/third_party/libjingle/source/talk/build/isolate.gypi @@ -31,6 +31,11 @@ # build/common.gypi is different for the standalone and Chromium builds. Gyp # doesn't permit conditional inclusion or variable expansion in include paths. # http://code.google.com/p/gyp/wiki/InputFormatReference#Including_Other_Files +# +# Local modifications: +# * Removed include of '../chrome/version.gypi'. +# * Removal passing of version_full variable created in version.gypi: +# '--extra-variable', 'version_full=<(version_full)', # This file is meant to be included into a target to provide a rule # to "build" .isolate files into a .isolated file. @@ -63,6 +68,9 @@ # # The generated .isolated file will be: # <(PRODUCT_DIR)/foo_test.isolated +# +# See http://dev.chromium.org/developers/testing/isolated-testing/for-swes +# for more information. { 'rules': [ @@ -73,7 +81,6 @@ # Files that are known to be involved in this step. '<(DEPTH)/tools/swarming_client/isolate.py', '<(DEPTH)/tools/swarming_client/run_isolated.py', - '<(DEPTH)/tools/swarming_client/googletest/run_test_cases.py', # Disable file tracking by the build driver for now. This means the # project must have the proper build-time dependency for their runtime @@ -90,47 +97,51 @@ 'outputs': [ '<(PRODUCT_DIR)/<(RULE_INPUT_ROOT).isolated', ], + 'action': [ + 'python', + '<(DEPTH)/tools/swarming_client/isolate.py', + '<(test_isolation_mode)', + '--result', '<@(_outputs)', + '--isolate', '<(RULE_INPUT_PATH)', + + # Variables should use the -V FOO=<(FOO) form so frequent values, + # like '0' or '1', aren't stripped out by GYP. Run 'isolate.py help' for + # more details. + # + # This list needs to be kept in sync with the cmd line options + # in src/build/android/pylib/gtest/setup.py. + + # Path variables are used to replace file paths when loading a .isolate + # file + '--path-variable', 'DEPTH', '<(DEPTH)', + '--path-variable', 'PRODUCT_DIR', '<(PRODUCT_DIR) ', + + '--config-variable', 'OS=<(OS)', + '--config-variable', 'chromeos=<(chromeos)', + '--config-variable', 'component=<(component)', + # TODO(kbr): move this to chrome_tests.gypi:gles2_conform_tests_run + # once support for user-defined config variables is added. + '--config-variable', + 'internal_gles2_conform_tests=<(internal_gles2_conform_tests)', + '--config-variable', 'icu_use_data_file_flag=<(icu_use_data_file_flag)', + '--config-variable', 'use_openssl=<(use_openssl)', + ], 'conditions': [ - ["test_isolation_outdir==''", { - 'action': [ - 'python', - '<(DEPTH)/tools/swarming_client/isolate.py', - '<(test_isolation_mode)', - # GYP will eliminate duplicate arguments so '<(PRODUCT_DIR)' cannot - # be provided twice. To work around this behavior, append '/'. - # - # Also have a space after <(PRODUCT_DIR) or visual studio will - # escape the argument wrappping " with the \ and merge it into - # the following arguments. - # - # Other variables should use the -V FOO=<(FOO) form so frequent - # values, like '0' or '1', aren't stripped out by GYP. - '--outdir', '<(PRODUCT_DIR)/ ', - '--variable', 'PRODUCT_DIR', '<(PRODUCT_DIR) ', - '--variable', 'OS=<(OS)', - '--result', '<@(_outputs)', - '--isolate', '<(RULE_INPUT_PATH)', - ], - }, { + # Note: When gyp merges lists, it appends them to the old value. + ['OS=="mac"', { + # <(mac_product_name) can contain a space, so don't use FOO=<(FOO) + # form. 'action': [ - 'python', - '<(DEPTH)/tools/swarming_client/isolate.py', - '<(test_isolation_mode)', - '--outdir', '<(test_isolation_outdir)', - # See comment above. - '--variable', 'PRODUCT_DIR', '<(PRODUCT_DIR) ', - '--variable', 'OS=<(OS)', - '--result', '<@(_outputs)', - '--isolate', '<(RULE_INPUT_PATH)', + '--extra-variable', 'mac_product_name', '<(mac_product_name)', ], }], + ["test_isolation_outdir!=''", { + 'action': [ '--isolate-server', '<(test_isolation_outdir)' ], + }], ['test_isolation_fail_on_missing == 0', { - 'action': ['--ignore_broken_items'], - }, - ], + 'action': ['--ignore_broken_items'], + }], ], - - 'msvs_cygwin_shell': 0, }, ], } diff --git a/chromium/third_party/libjingle/source/talk/examples/android/AndroidManifest.xml b/chromium/third_party/libjingle/source/talk/examples/android/AndroidManifest.xml index 59974f7a834..f898641f8c2 100644 --- a/chromium/third_party/libjingle/source/talk/examples/android/AndroidManifest.xml +++ b/chromium/third_party/libjingle/source/talk/examples/android/AndroidManifest.xml @@ -17,10 +17,12 @@ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher" + android:debuggable="true" android:allowBackup="false"> <activity android:name="AppRTCDemoActivity" android:label="@string/app_name" - android:screenOrientation="landscape" + android:screenOrientation="fullUser" + android:configChanges="orientation|screenSize" android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/chromium/third_party/libjingle/source/talk/examples/android/README b/chromium/third_party/libjingle/source/talk/examples/android/README index feabadb979c..faf4e924d01 100644 --- a/chromium/third_party/libjingle/source/talk/examples/android/README +++ b/chromium/third_party/libjingle/source/talk/examples/android/README @@ -1,19 +1,9 @@ This directory contains an example Android client for http://apprtc.appspot.com Prerequisites: -- Make sure gclient is checking out tools necessary to target Android: your - .gclient file should contain a line like: - target_os = ['android', 'unix'] - Make sure to re-run gclient sync after adding this to download the tools. -- Env vars need to be set up to target Android; easiest way to do this is to run - (from the libjingle trunk directory): - . ./build/android/envsetup.sh - Note that this clobbers any previously-set $GYP_DEFINES so it must be done - before the next item. +- "Android Specific Steps" on http://www.webrtc.org/reference/getting-started - Set up webrtc-related GYP variables: export GYP_DEFINES="build_with_libjingle=1 build_with_chromium=0 libjingle_java=1 $GYP_DEFINES" - export JAVA_HOME=</path/to/JDK> - export PATH=$JAVA_HOME/bin:$PATH To cause WEBRTC_LOGGING to emit to Android's logcat, add enable_tracing=1 to the $GYP_DEFINES above. - When targeting both desktop & android, make sure to use a different output_dir @@ -34,7 +24,7 @@ the dialog box. Alternatively, replace the <NNN> from the desktop chrome into the following command: -adb shell am start -a android.intent.action.VIEW -d '"https://apprtc.appspot.com/?r=<NNN>"' +adb shell am start -n org.appspot.apprtc/.AppRTCDemoActivity -a android.intent.action.VIEW -d '"https://apprtc.appspot.com/?r=<NNN>"' This should result in the app launching on Android and connecting to the apprtc page displayed in the desktop browser. diff --git a/chromium/third_party/libjingle/source/talk/examples/call/call_main.cc b/chromium/third_party/libjingle/source/talk/examples/call/call_main.cc index 2ee796b1c25..33d5385719d 100644 --- a/chromium/third_party/libjingle/source/talk/examples/call/call_main.cc +++ b/chromium/third_party/libjingle/source/talk/examples/call/call_main.cc @@ -25,9 +25,10 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <cstdio> -#include <cstring> +#include <stdio.h> +#include <string.h> #include <time.h> + #include <iomanip> #include <iostream> #include <vector> @@ -45,9 +46,6 @@ #include "talk/examples/call/console.h" #include "talk/examples/call/mediaenginefactory.h" #include "talk/p2p/base/constants.h" -#ifdef ANDROID -#include "talk/media/other/androidmediaengine.h" -#endif #include "talk/session/media/mediasessionclient.h" #include "talk/session/media/srtpfilter.h" #include "talk/xmpp/xmppauth.h" @@ -185,7 +183,7 @@ static const int DEFAULT_PORT = 5222; static std::vector<cricket::AudioCodec> codecs; static const cricket::AudioCodec ISAC(103, "ISAC", 40000, 16000, 1, 0); -cricket::MediaEngine *AndroidMediaEngineFactory() { +cricket::MediaEngineInterface *CreateAndroidMediaEngine() { cricket::FakeMediaEngine *engine = new cricket::FakeMediaEngine(); codecs.push_back(ISAC); @@ -438,7 +436,7 @@ int main(int argc, char **argv) { } #ifdef ANDROID - InitAndroidMediaEngineFactory(AndroidMediaEngineFactory); + MediaEngineFactory::SetCreateFunction(&CreateAndroidMediaEngine); #endif #if WIN32 diff --git a/chromium/third_party/libjingle/source/talk/examples/call/console.cc b/chromium/third_party/libjingle/source/talk/examples/call/console.cc index dec3b4abc11..647601e8170 100644 --- a/chromium/third_party/libjingle/source/talk/examples/call/console.cc +++ b/chromium/third_party/libjingle/source/talk/examples/call/console.cc @@ -27,12 +27,14 @@ #define _CRT_SECURE_NO_DEPRECATE 1 +#include <assert.h> + #ifdef POSIX #include <signal.h> #include <termios.h> #include <unistd.h> #endif // POSIX -#include <cassert> + #include "talk/base/logging.h" #include "talk/base/messagequeue.h" #include "talk/base/stringutils.h" @@ -46,28 +48,29 @@ static void DoNothing(int unused) {} Console::Console(talk_base::Thread *thread, CallClient *client) : client_(client), client_thread_(thread), - console_thread_(new talk_base::Thread()) {} + stopped_(false) {} Console::~Console() { Stop(); } void Console::Start() { - if (!console_thread_) { + if (stopped_) { // stdin was closed in Stop(), so we can't restart. LOG(LS_ERROR) << "Cannot re-start"; return; } - if (console_thread_->started()) { + if (console_thread_) { LOG(LS_WARNING) << "Already started"; return; } + console_thread_.reset(new talk_base::Thread()); console_thread_->Start(); console_thread_->Post(this, MSG_START); } void Console::Stop() { - if (console_thread_ && console_thread_->started()) { + if (console_thread_) { #ifdef WIN32 CloseHandle(GetStdHandle(STD_INPUT_HANDLE)); #else @@ -78,6 +81,7 @@ void Console::Stop() { #endif console_thread_->Stop(); console_thread_.reset(); + stopped_ = true; } } diff --git a/chromium/third_party/libjingle/source/talk/examples/call/console.h b/chromium/third_party/libjingle/source/talk/examples/call/console.h index 4a90a7fe540..f0f36e34677 100644 --- a/chromium/third_party/libjingle/source/talk/examples/call/console.h +++ b/chromium/third_party/libjingle/source/talk/examples/call/console.h @@ -28,7 +28,7 @@ #ifndef TALK_EXAMPLES_CALL_CONSOLE_H_ #define TALK_EXAMPLES_CALL_CONSOLE_H_ -#include <cstdio> +#include <stdio.h> #include "talk/base/thread.h" #include "talk/base/messagequeue.h" @@ -64,6 +64,7 @@ class Console : public talk_base::MessageHandler { CallClient *client_; talk_base::Thread *client_thread_; talk_base::scoped_ptr<talk_base::Thread> console_thread_; + bool stopped_; }; #endif // TALK_EXAMPLES_CALL_CONSOLE_H_ diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/Info.plist b/chromium/third_party/libjingle/source/talk/examples/chat/Info.plist deleted file mode 100644 index ecd083ac639..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/chat/Info.plist +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleIdentifier</key> - <string>com.google.call</string> - <key>CFBundleName</key> - <string>chat</string> -</dict> -</plist> - diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/chat_main.cc b/chromium/third_party/libjingle/source/talk/examples/chat/chat_main.cc deleted file mode 100644 index 09a454e2d88..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/chat/chat_main.cc +++ /dev/null @@ -1,159 +0,0 @@ -/* - * libjingle - * Copyright 2004--2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// -// A simple text chat application, largely copied from examples/call. -// - -#include <iostream> - -#include "talk/base/logging.h" -#include "talk/base/ssladapter.h" - -#ifdef OSX -#include "talk/base/maccocoasocketserver.h" -#elif defined(WIN32) -#include "talk/base/win32socketserver.h" -#else -#include "talk/base/physicalsocketserver.h" -#endif - -#include "talk/xmpp/constants.h" -#include "talk/xmpp/xmppauth.h" -#include "talk/xmpp/xmppclientsettings.h" -#include "talk/xmpp/xmpppump.h" -#include "talk/xmpp/xmppsocket.h" - -#include "talk/examples/chat/chatapp.h" -#include "talk/examples/chat/consoletask.h" - -static const int kDefaultPort = 5222; - -int main(int argc, char* argv[]) { - // TODO(pmclean): Remove duplication of code with examples/call. - // Set up debugging. - bool debug = true; - if (debug) { - talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE); - } - - // Set up the crypto subsystem. - talk_base::InitializeSSL(); - - // Parse username and password, if present. - buzz::Jid jid; - std::string username; - talk_base::InsecureCryptStringImpl pass; - if (argc > 1) { - username = argv[1]; - if (argc > 2) { - pass.password() = argv[2]; - } - } - - // ... else prompt for them - if (username.empty()) { - printf("JID: "); - std::cin >> username; - } - if (username.find('@') == std::string::npos) { - username.append("@localhost"); - } - - jid = buzz::Jid(username); - if (!jid.IsValid() || jid.node() == "") { - printf("Invalid JID. JIDs should be in the form user@domain\n"); - return 1; - } - - if (pass.password().empty()) { - buzz::ConsoleTask::SetEcho(false); - printf("Password: "); - std::cin >> pass.password(); - buzz::ConsoleTask::SetEcho(true); - printf("\n"); - } - - // OTP (this can be skipped) - std::string otp_token; - printf("OTP: "); - fflush(stdin); - std::getline(std::cin, otp_token); - - // Setup the connection settings. - buzz::XmppClientSettings xcs; - xcs.set_user(jid.node()); - xcs.set_resource("chat"); - xcs.set_host(jid.domain()); - bool allow_plain = false; - xcs.set_allow_plain(allow_plain); - xcs.set_use_tls(buzz::TLS_REQUIRED); - xcs.set_pass(talk_base::CryptString(pass)); - if (!otp_token.empty() && *otp_token.c_str() != '\n') { - xcs.set_auth_token(buzz::AUTH_MECHANISM_OAUTH2, otp_token); - } - - // Build the server spec - std::string host; - int port; - - std::string server = "talk.google.com"; - int colon = server.find(':'); - if (colon == -1) { - host = server; - port = kDefaultPort; - } else { - host = server.substr(0, colon); - port = atoi(server.substr(colon + 1).c_str()); - } - xcs.set_server(talk_base::SocketAddress(host, port)); - - talk_base::Thread* main_thread = talk_base::Thread::Current(); -#if WIN32 - // Need to pump messages on our main thread on Windows. - talk_base::Win32Thread w32_thread; - talk_base::ThreadManager::Instance()->SetCurrentThread(&w32_thread); -#elif defined(OSX) - talk_base::MacCocoaSocketServer ss; - talk_base::SocketServerScope ss_scope(&ss); -#else - talk_base::PhysicalSocketServer ss; -#endif - - buzz::XmppPump* pump = new buzz::XmppPump(); - ChatApp *client = new ChatApp(pump->client(), main_thread); - - // Start pumping messages! - pump->DoLogin(xcs, new buzz::XmppSocket(buzz::TLS_REQUIRED), new XmppAuth()); - - main_thread->Run(); - pump->DoDisconnect(); - - delete client; - - return 0; -} diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.cc b/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.cc deleted file mode 100644 index 59b1c69fb9a..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.cc +++ /dev/null @@ -1,251 +0,0 @@ -/* - * libjingle - * Copyright 2004--2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "talk/examples/chat/chatapp.h" - -#include "talk/examples/chat/consoletask.h" -#include "talk/examples/chat/textchatsendtask.h" -#include "talk/examples/chat/textchatreceivetask.h" -#include "talk/xmpp/presenceouttask.h" -#include "talk/xmpp/presencereceivetask.h" - -#ifdef WIN32 -#define snprintf _snprintf -#endif - -ChatApp::ChatApp(buzz::XmppClient* xmpp_client, talk_base::Thread* main_thread) - : xmpp_client_(xmpp_client), - presence_out_task_(), - presence_receive_task_(), - message_send_task_(), - message_received_task_(), - console_task_(new buzz::ConsoleTask(main_thread)), - ui_state_(STATE_BASE) { - xmpp_client_->SignalStateChange.connect(this, &ChatApp::OnStateChange); - - console_task_->TextInputHandler.connect(this, &ChatApp::OnConsoleMessage); - console_task_->Start(); -} - -ChatApp::~ChatApp() { - if (presence_out_task_ != NULL) { - // Check out - BroadcastPresence(away); - } -} - -void ChatApp::Quit() { - talk_base::Thread::Current()->Quit(); -} - -void ChatApp::OnXmppOpen() { - presence_out_task_.reset(new buzz::PresenceOutTask(xmpp_client_)); - presence_receive_task_.reset(new buzz::PresenceReceiveTask(xmpp_client_)); - presence_receive_task_->PresenceUpdate.connect(this, - &ChatApp::OnPresenceUpdate); - message_send_task_.reset(new buzz::TextChatSendTask(xmpp_client_)); - message_received_task_.reset(new buzz::TextChatReceiveTask(xmpp_client_)); - message_received_task_->SignalTextChatReceived.connect( - this, &ChatApp::OnTextMessage); - - presence_out_task_->Start(); - presence_receive_task_->Start(); - message_send_task_->Start(); - message_received_task_->Start(); -} - -void ChatApp::BroadcastPresence(PresenceState state) { - buzz::PresenceStatus status; - status.set_jid(xmpp_client_->jid()); - status.set_available(state == online); - status.set_show(state == online ? buzz::PresenceStatus::SHOW_ONLINE - : buzz::PresenceStatus::SHOW_AWAY); - presence_out_task_->Send(status); -} - -// UI Stuff -static const char* kMenuChoiceQuit = "0"; -static const char* kMenuChoiceRoster = "1"; -static const char* kMenuChoiceChat = "2"; - -static const char* kUIStrings[3][2] = { - {kMenuChoiceQuit, "Quit"}, - {kMenuChoiceRoster, "Roster"}, - {kMenuChoiceChat, "Send"}}; - -void ChatApp::PrintMenu() { - char buff[128]; - int numMenuItems = sizeof(kUIStrings) / sizeof(kUIStrings[0]); - for (int index = 0; index < numMenuItems; ++index) { - snprintf(buff, sizeof(buff), "%s) %s\n", kUIStrings[index][0], - kUIStrings[index][1]); - console_task_->Print(buff); - } - console_task_->Print("choice:"); -} - -void ChatApp::PrintRoster() { - int index = 0; - for (RosterList::iterator iter = roster_list_.begin(); - iter != roster_list_.end(); ++iter) { - const buzz::Jid& jid = iter->second.jid(); - console_task_->Print( - "%d: (*) %s@%s [%s] \n", - index++, - jid.node().c_str(), - jid.domain().c_str(), - jid.resource().c_str()); - } -} - -void ChatApp::PromptJid() { - PrintRoster(); - console_task_->Print("choice:"); -} - -void ChatApp::PromptChatMessage() { - console_task_->Print(":"); -} - -bool ChatApp::GetRosterItem(int index, buzz::PresenceStatus* status) { - int found_index = 0; - for (RosterList::iterator iter = roster_list_.begin(); - iter != roster_list_.end() && found_index <= index; ++iter) { - if (found_index == index) { - *status = iter->second; - return true; - } - found_index++; - } - - return false; -} - -void ChatApp::HandleBaseInput(const std::string& message) { - if (message == kMenuChoiceQuit) { - Quit(); - } else if (message == kMenuChoiceRoster) { - PrintRoster(); - } else if (message == kMenuChoiceChat) { - ui_state_ = STATE_PROMPTJID; - PromptJid(); - } else if (message == "") { - PrintMenu(); - } -} - -void ChatApp::HandleJidInput(const std::string& message) { - if (isdigit(message[0])) { - // It's an index-based roster choice. - int index = 0; - buzz::PresenceStatus status; - if (!talk_base::FromString(message, &index) || - !GetRosterItem(index, &status)) { - // fail, so drop back - ui_state_ = STATE_BASE; - return; - } - - chat_dest_jid_ = status.jid(); - } else { - // It's an explicit address. - chat_dest_jid_ = buzz::Jid(message.c_str()); - } - ui_state_ = STATE_CHATTING; - PromptChatMessage(); -} - -void ChatApp::HandleChatInput(const std::string& message) { - if (message == "") { - ui_state_ = STATE_BASE; - PrintMenu(); - } else { - message_send_task_->Send(chat_dest_jid_, message); - PromptChatMessage(); - } -} - -// Connection state notifications -void ChatApp::OnStateChange(buzz::XmppEngine::State state) { - switch (state) { - // Nonexistent state - case buzz::XmppEngine::STATE_NONE: - break; - - // Nonexistent state - case buzz::XmppEngine::STATE_START: - break; - - // Exchanging stream headers, authenticating and so on. - case buzz::XmppEngine::STATE_OPENING: - break; - - // Authenticated and bound. - case buzz::XmppEngine::STATE_OPEN: - OnXmppOpen(); - BroadcastPresence(online); - PrintMenu(); - break; - - // Session closed, possibly due to error. - case buzz::XmppEngine::STATE_CLOSED: - break; - } -} - -// Presence Notifications -void ChatApp::OnPresenceUpdate(const buzz::PresenceStatus& status) { - if (status.available()) { - roster_list_[status.jid().Str()] = status; - } else { - RosterList::iterator iter = roster_list_.find(status.jid().Str()); - if (iter != roster_list_.end()) { - roster_list_.erase(iter); - } - } -} - -// Text message handlers -void ChatApp::OnTextMessage(const buzz::Jid& from, const buzz::Jid& to, - const std::string& message) { - console_task_->Print("%s says: %s\n", from.node().c_str(), message.c_str()); -} - -void ChatApp::OnConsoleMessage(const std::string &message) { - switch (ui_state_) { - case STATE_BASE: - HandleBaseInput(message); - break; - - case STATE_PROMPTJID: - HandleJidInput(message); - break; - - case STATE_CHATTING: - HandleChatInput(message); - break; - } -} diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.h b/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.h deleted file mode 100644 index cc032a67f61..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * libjingle - * Copyright 2004--2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_EXAMPLES_CHAT_CHATAPP_H_ -#define TALK_EXAMPLES_CHAT_CHATAPP_H_ - -#include "talk/base/thread.h" -#include "talk/base/scoped_ptr.h" - -#include "talk/xmpp/jid.h" -#include "talk/xmpp/xmppclient.h" - -namespace buzz { -class XmppClient; -class PresenceOutTask; -class PresenceReceiveTask; -class TextChatSendTask; -class TextChatReceiveTask; -class ConsoleTask; -class PresenceStatus; -} - -// This is an example chat app for libjingle, showing how to use xmpp tasks, -// data, callbacks, etc. It has a simple text-based UI for logging in, -// sending and receiving messages, and printing the roster. -class ChatApp: public sigslot::has_slots<> { - public: - // Arguments: - // xmpp_client Points to the XmppClient for the communication channel - // (typically created by the XmppPump object). - // main_thread Wraps the application's main thread. Subsidiary threads - // for the various tasks will be forked off of this. - ChatApp(buzz::XmppClient* xmpp_client, talk_base::Thread* main_thread); - - // Shuts down and releases all of the contained tasks/threads - ~ChatApp(); - - // Shuts down the current thread and quits - void Quit(); - - private: - // - // Initialization - // - // Called explicitly after the connection to the chat server is established. - void OnXmppOpen(); - - // - // UI Stuff - // - // Prints the app main menu on the console. - // Called when ui_state_ == STATE_BASE. - void PrintMenu(); - - // Prints a numbered list of the logged-in user's roster on the console. - void PrintRoster(); - - // Prints a prompt for the user to enter either the index from the - // roster list of the user they wish to chat with, or a fully-qualified - // (user@server.ext) jid. - // Called when when ui_state_ == STATE_PROMPTJID. - void PromptJid(); - - // Prints a prompt on the console for the user to enter a message to send. - // Called when when ui_state_ == STATE_CHATTING. - void PromptChatMessage(); - - // Sends our presence state to the chat server (and on to your roster list). - // Arguments: - // state Specifies the presence state to show. - enum PresenceState {online, away}; - void BroadcastPresence(PresenceState state); - - // Returns the RosterItem associated with the specified index. - // Just a helper to select a roster item from a numbered list in the UI. - bool GetRosterItem(int index, buzz::PresenceStatus* status); - - // - // Input Handling - // - // Receives input when ui_state_ == STATE_BASE. Handles choices from the - // main menu. - void HandleBaseInput(const std::string& message); - - // Receives input when ui_state_ == STATE_PROMPTJID. Handles selection - // of a JID to chat to. - void HandleJidInput(const std::string& message); - - // Receives input when ui_state_ == STATE_CHATTING. Handles text messages. - void HandleChatInput(const std::string& message); - - // - // signal/slot Callbacks - // - // Connected to the XmppClient::SignalStateChange slot. Receives - // notifications of state changes of the connection. - void OnStateChange(buzz::XmppEngine::State state); - - // Connected to the PresenceReceiveTask::PresenceUpdate slot. - // Receives status messages for the logged-in user's roster (i.e. - // an initial list from the server and people coming/going). - void OnPresenceUpdate(const buzz::PresenceStatus& status); - - // Connected to the TextChatReceiveTask::SignalTextChatReceived slot. - // Called when we receive a text chat from someone else. - void OnTextMessage(const buzz::Jid& from, const buzz::Jid& to, - const std::string& message); - - // Receives text input from the console task. This is where any input - // from the user comes in. - // Arguments: - // message What the user typed. - void OnConsoleMessage(const std::string &message); - - // The XmppClient object associated with this chat application instance. - buzz::XmppClient* xmpp_client_; - - // We send presence information through this object. - talk_base::scoped_ptr<buzz::PresenceOutTask> presence_out_task_; - - // We receive others presence information through this object. - talk_base::scoped_ptr<buzz::PresenceReceiveTask> presence_receive_task_; - - // We send text messages though this object. - talk_base::scoped_ptr<buzz::TextChatSendTask> message_send_task_; - - // We receive messages through this object. - talk_base::scoped_ptr<buzz::TextChatReceiveTask> message_received_task_; - - // UI gets drawn and receives input through this task. - talk_base::scoped_ptr< buzz::ConsoleTask> console_task_; - - // The list of JIDs for the people in the logged-in users roster. - // RosterList roster_list_; - typedef std::map<std::string, buzz::PresenceStatus> RosterList; - RosterList roster_list_; - - // The JID of the user currently being chatted with. - buzz::Jid chat_dest_jid_; - - // UI State constants - enum UIState { STATE_BASE, STATE_PROMPTJID, STATE_CHATTING }; - UIState ui_state_; -}; - -#endif // TALK_EXAMPLES_CHAT_CHATAPP_H_ - diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/consoletask.cc b/chromium/third_party/libjingle/source/talk/examples/chat/consoletask.cc deleted file mode 100644 index 2577c79cb54..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/chat/consoletask.cc +++ /dev/null @@ -1,177 +0,0 @@ -/* - * libjingle - * Copyright 2004--2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// TODO(pmclean): Perhaps this should be unified with examples/call/console.cc -// and refactor to talk/base. -#include "talk/examples/chat/consoletask.h" - -#define _CRT_SECURE_NO_DEPRECATE 1 - -#include <stdarg.h> -#ifdef POSIX -#include <signal.h> -#include <termios.h> -#include <unistd.h> -#endif // POSIX -#include <cassert> - -#include "talk/base/logging.h" - -#ifdef POSIX -static void DoNothing(int unused) {} -#endif - -namespace buzz { - -ConsoleTask::ConsoleTask(talk_base::Thread *thread) : - client_thread_(thread), - console_thread_(new talk_base::Thread()) { -} - -ConsoleTask::~ConsoleTask() { - Stop(); -} - -void ConsoleTask::Start() { - if (!console_thread_) { - // stdin was closed in Stop(), so we can't restart. - LOG(LS_ERROR) << "Cannot re-start"; - return; - } - if (console_thread_->started()) { - LOG(LS_WARNING) << "Already started"; - return; - } - console_thread_->Start(); - console_thread_->Post(this, MSG_START); -} - -void ConsoleTask::Stop() { - if (console_thread_ && console_thread_->started()) { -#ifdef WIN32 - CloseHandle(GetStdHandle(STD_INPUT_HANDLE)); -#else - close(fileno(stdin)); - // This forces the read() in fgets() to return with errno = EINTR. fgets() - // will retry the read() and fail, thus returning. - pthread_kill(console_thread_->GetPThread(), SIGUSR1); -#endif - console_thread_->Stop(); - console_thread_.reset(); - } -} - -void ConsoleTask::SetEcho(bool on) { -#ifdef WIN32 - HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); - if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL)) - return; - - DWORD mode; - if (!GetConsoleMode(hIn, &mode)) - return; - - if (on) { - mode = mode | ENABLE_ECHO_INPUT; - } else { - mode = mode & ~ENABLE_ECHO_INPUT; - } - - SetConsoleMode(hIn, mode); -#else // MAC & LINUX - const int fd = fileno(stdin); - if (fd == -1) { - return; - } - - struct termios tcflags; - if (tcgetattr(fd, &tcflags) == -1) { - return; - } - - if (on) { - tcflags.c_lflag |= ECHO; - } else { - tcflags.c_lflag &= ~ECHO; - } - - tcsetattr(fd, TCSANOW, &tcflags); -#endif -} - -void ConsoleTask::Print(const char* format, ...) { - va_list ap; - va_start(ap, format); - - char buf[4096]; - int size = vsnprintf(buf, sizeof(buf), format, ap); - assert(size >= 0); - assert(size < static_cast<int>(sizeof(buf))); - buf[size] = '\0'; - printf("%s", buf); - fflush(stdout); - - va_end(ap); -} - -void ConsoleTask::RunConsole() { - char input_buffer[128]; - while (fgets(input_buffer, sizeof(input_buffer), stdin) != NULL) { - client_thread_->Post(this, MSG_INPUT, - new talk_base::TypedMessageData<std::string>(input_buffer)); - } -} - -void ConsoleTask::OnMessage(talk_base::Message *msg) { - switch (msg->message_id) { - case MSG_START: -#ifdef POSIX - // Install a no-op signal so that we can abort RunConsole() by raising - // SIGUSR1. - struct sigaction act; - act.sa_handler = &DoNothing; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - if (sigaction(SIGUSR1, &act, NULL) < 0) { - LOG(LS_WARNING) << "Can't install signal"; - } -#endif - RunConsole(); - break; - - case MSG_INPUT: - talk_base::TypedMessageData<std::string> *data = - static_cast<talk_base::TypedMessageData<std::string>*>(msg->pdata); - // Trim off the .line-terminator to make processing easier. - std::string parsed_message = - data->data().substr(0, data->data().length() - 1); - TextInputHandler(parsed_message); - break; - } -} - -} // namespace buzz diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/consoletask.h b/chromium/third_party/libjingle/source/talk/examples/chat/consoletask.h deleted file mode 100644 index 1d45b3a7f53..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/chat/consoletask.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * libjingle - * Copyright 2004--2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_EXAMPLES_CHAT_CONSOLETASK_H_ -#define TALK_EXAMPLES_CHAT_CONSOLETASK_H_ - -#include <cstdio> - -#include "talk/base/thread.h" -#include "talk/base/sigslot.h" - -namespace buzz { - -// -// Provides properly threaded console I/O. -// -class ConsoleTask : public talk_base::MessageHandler { - public: - // Arguments: - // thread The main application thread. Input messages get posted through - // this. - explicit ConsoleTask(talk_base::Thread *thread); - - // Shuts down the thread associated with this task. - ~ConsoleTask(); - - // Slot for text inputs handler. - sigslot::signal1<const std::string&> TextInputHandler; - - // Starts reading lines from the console and passes them to the - // TextInputHandler. - void Start(); - - // Stops reading lines and shuts down the thread. Cannot be restarted. - void Stop(); - - // Thread messages (especialy text-input messages) come in through here. - virtual void OnMessage(talk_base::Message *msg); - - // printf() style output to the console. - void Print(const char* format, ...); - - // Turns on/off the echo of input characters on the console. - // Arguments: - // on If true turns echo on, off otherwise. - static void SetEcho(bool on); - - private: - /** Message IDs (for OnMessage()). */ - enum { - MSG_START, - MSG_INPUT, - }; - - // Starts up polling for console input - void RunConsole(); - - // The main application thread - talk_base::Thread *client_thread_; - - // The tread associated with this console object - talk_base::scoped_ptr<talk_base::Thread> console_thread_; -}; - -} // namespace buzz - -#endif // TALK_EXAMPLES_CHAT_CONSOLETASK_H_ - diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/textchatsendtask.cc b/chromium/third_party/libjingle/source/talk/examples/chat/textchatsendtask.cc deleted file mode 100644 index ba144530294..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/chat/textchatsendtask.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * libjingle - * Copyright 2004--2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "talk/examples/chat/textchatsendtask.h" - -#include "talk/xmpp/constants.h" -#include "talk/xmpp/xmppclient.h" - -namespace buzz { -TextChatSendTask::TextChatSendTask(XmppTaskParentInterface* parent) - : XmppTask(parent) { -} - -TextChatSendTask::~TextChatSendTask() { - Stop(); -} - -XmppReturnStatus TextChatSendTask::Send(const Jid& to, - const std::string& textmessage) { - // Make sure we are actually connected. - if (GetState() != STATE_INIT && GetState() != STATE_START) { - return XMPP_RETURN_BADSTATE; - } - - // Put together the chat stanza... - XmlElement* message_stanza = new XmlElement(QN_MESSAGE); - - // ... and specify the required attributes... - message_stanza->AddAttr(QN_TO, to.Str()); - message_stanza->AddAttr(QN_TYPE, "chat"); - message_stanza->AddAttr(QN_LANG, "en"); - - // ... and fill out the body. - XmlElement* message_body = new XmlElement(QN_BODY); - message_body->AddText(textmessage); - message_stanza->AddElement(message_body); - - // Now queue it up. - QueueStanza(message_stanza); - - return XMPP_RETURN_OK; -} - -int TextChatSendTask::ProcessStart() { - const XmlElement* stanza = NextStanza(); - if (stanza == NULL) { - return STATE_BLOCKED; - } - - if (SendStanza(stanza) != XMPP_RETURN_OK) { - return STATE_ERROR; - } - - return STATE_START; -} - -} // namespace buzz diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m b/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m deleted file mode 100644 index d6c86d8422f..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m +++ /dev/null @@ -1,340 +0,0 @@ -/* - * libjingle - * Copyright 2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "APPRTCAppClient.h" - -#import <dispatch/dispatch.h> - -#import "GAEChannelClient.h" -#import "RTCICEServer.h" - -@interface APPRTCAppClient () - -@property(nonatomic, strong) dispatch_queue_t backgroundQueue; -@property(nonatomic, copy) NSString *baseURL; -@property(nonatomic, strong) GAEChannelClient *gaeChannel; -@property(nonatomic, copy) NSString *postMessageUrl; -@property(nonatomic, copy) NSString *pcConfig; -@property(nonatomic, strong) NSMutableString *roomHtml; -@property(atomic, strong) NSMutableArray *sendQueue; -@property(nonatomic, copy) NSString *token; - -@property(nonatomic, assign) BOOL verboseLogging; - -@end - -@implementation APPRTCAppClient - -@synthesize ICEServerDelegate = _ICEServerDelegate; -@synthesize messageHandler = _messageHandler; - -@synthesize backgroundQueue = _backgroundQueue; -@synthesize baseURL = _baseURL; -@synthesize gaeChannel = _gaeChannel; -@synthesize postMessageUrl = _postMessageUrl; -@synthesize pcConfig = _pcConfig; -@synthesize roomHtml = _roomHtml; -@synthesize sendQueue = _sendQueue; -@synthesize token = _token; -@synthesize verboseLogging = _verboseLogging; - -- (id)init { - if (self = [super init]) { - _backgroundQueue = dispatch_queue_create("RTCBackgroundQueue", NULL); - _sendQueue = [NSMutableArray array]; - // Uncomment to see Request/Response logging. - // _verboseLogging = YES; - } - return self; -} - -#pragma mark - Public methods - -- (void)connectToRoom:(NSURL *)url { - NSURLRequest *request = [self getRequestFromUrl:url]; - [NSURLConnection connectionWithRequest:request delegate:self]; -} - -- (void)sendData:(NSData *)data { - @synchronized(self) { - [self maybeLogMessage:@"Send message"]; - [self.sendQueue addObject:[data copy]]; - } - [self requestQueueDrainInBackground]; -} - -#pragma mark - Internal methods - -- (NSString*)findVar:(NSString*)name - strippingQuotes:(BOOL)strippingQuotes { - NSError* error; - NSString* pattern = - [NSString stringWithFormat:@".*\n *var %@ = ([^\n]*);\n.*", name]; - NSRegularExpression *regexp = - [NSRegularExpression regularExpressionWithPattern:pattern - options:0 - error:&error]; - NSAssert(!error, @"Unexpected error compiling regex: ", - error.localizedDescription); - - NSRange fullRange = NSMakeRange(0, [self.roomHtml length]); - NSArray *matches = - [regexp matchesInString:self.roomHtml options:0 range:fullRange]; - if ([matches count] != 1) { - [self showMessage:[NSString stringWithFormat:@"%d matches for %@ in %@", - [matches count], name, self.roomHtml]]; - return nil; - } - NSRange matchRange = [matches[0] rangeAtIndex:1]; - NSString* value = [self.roomHtml substringWithRange:matchRange]; - if (strippingQuotes) { - NSAssert([value length] > 2, - @"Can't strip quotes from short string: [%@]", value); - NSAssert(([value characterAtIndex:0] == '\'' && - [value characterAtIndex:[value length] - 1] == '\''), - @"Can't strip quotes from unquoted string: [%@]", value); - value = [value substringWithRange:NSMakeRange(1, [value length] - 2)]; - } - return value; -} - -- (NSURLRequest *)getRequestFromUrl:(NSURL *)url { - self.roomHtml = [NSMutableString stringWithCapacity:20000]; - NSString *path = - [NSString stringWithFormat:@"https:%@", [url resourceSpecifier]]; - NSURLRequest *request = - [NSURLRequest requestWithURL:[NSURL URLWithString:path]]; - return request; -} - -- (void)maybeLogMessage:(NSString *)message { - if (self.verboseLogging) { - NSLog(@"%@", message); - } -} - -- (void)requestQueueDrainInBackground { - dispatch_async(self.backgroundQueue, ^(void) { - // TODO(hughv): This can block the UI thread. Fix. - @synchronized(self) { - if ([self.postMessageUrl length] < 1) { - return; - } - for (NSData *data in self.sendQueue) { - NSString *url = [NSString stringWithFormat:@"%@/%@", - self.baseURL, - self.postMessageUrl]; - [self sendData:data withUrl:url]; - } - [self.sendQueue removeAllObjects]; - } - }); -} - -- (void)sendData:(NSData *)data withUrl:(NSString *)url { - NSMutableURLRequest *request = - [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; - request.HTTPMethod = @"POST"; - [request setHTTPBody:data]; - NSURLResponse *response; - NSError *error; - NSData *responseData = [NSURLConnection sendSynchronousRequest:request - returningResponse:&response - error:&error]; - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - int status = [httpResponse statusCode]; - NSAssert(status == 200, - @"Bad response [%d] to message: %@\n\n%@", - status, - [NSString stringWithUTF8String:[data bytes]], - [NSString stringWithUTF8String:[responseData bytes]]); -} - -- (void)showMessage:(NSString *)message { - NSLog(@"%@", message); - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Unable to join" - message:message - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil]; - [alertView show]; -} - -- (void)updateICEServers:(NSMutableArray *)ICEServers - withTurnServer:(NSString *)turnServerUrl { - if ([turnServerUrl length] < 1) { - [self.ICEServerDelegate onICEServers:ICEServers]; - return; - } - dispatch_async(self.backgroundQueue, ^(void) { - NSMutableURLRequest *request = [NSMutableURLRequest - requestWithURL:[NSURL URLWithString:turnServerUrl]]; - [request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"]; - [request addValue:@"https://apprtc.appspot.com" - forHTTPHeaderField:@"origin"]; - NSURLResponse *response; - NSError *error; - NSData *responseData = [NSURLConnection sendSynchronousRequest:request - returningResponse:&response - error:&error]; - if (!error) { - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData - options:0 - error:&error]; - NSAssert(!error, @"Unable to parse. %@", error.localizedDescription); - NSString *username = json[@"username"]; - NSString *password = json[@"password"]; - NSArray* uris = json[@"uris"]; - for (int i = 0; i < [uris count]; ++i) { - NSString *turnServer = [uris objectAtIndex:i]; - RTCICEServer *ICEServer = - [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:turnServer] - username:username - password:password]; - NSLog(@"Added ICE Server: %@", ICEServer); - [ICEServers addObject:ICEServer]; - } - } else { - NSLog(@"Unable to get TURN server. Error: %@", error.description); - } - - dispatch_async(dispatch_get_main_queue(), ^(void) { - [self.ICEServerDelegate onICEServers:ICEServers]; - }); - }); -} - -#pragma mark - NSURLConnectionDataDelegate methods - -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { - NSString *roomHtml = [NSString stringWithUTF8String:[data bytes]]; - [self maybeLogMessage: - [NSString stringWithFormat:@"Received %d chars", [roomHtml length]]]; - [self.roomHtml appendString:roomHtml]; -} - -- (void)connection:(NSURLConnection *)connection - didReceiveResponse:(NSURLResponse *)response { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - int statusCode = [httpResponse statusCode]; - [self maybeLogMessage: - [NSString stringWithFormat: - @"Response received\nURL\n%@\nStatus [%d]\nHeaders\n%@", - [httpResponse URL], - statusCode, - [httpResponse allHeaderFields]]]; - NSAssert(statusCode == 200, @"Invalid response of %d received.", statusCode); -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)connection { - [self maybeLogMessage:[NSString stringWithFormat:@"finished loading %d chars", - [self.roomHtml length]]]; - NSRegularExpression* fullRegex = - [NSRegularExpression regularExpressionWithPattern:@"room is full" - options:0 - error:nil]; - if ([fullRegex - numberOfMatchesInString:self.roomHtml - options:0 - range:NSMakeRange(0, [self.roomHtml length])]) { - [self showMessage:@"Room full"]; - return; - } - - - NSString *fullUrl = [[[connection originalRequest] URL] absoluteString]; - NSRange queryRange = [fullUrl rangeOfString:@"?"]; - self.baseURL = [fullUrl substringToIndex:queryRange.location]; - [self maybeLogMessage: - [NSString stringWithFormat:@"Base URL: %@", self.baseURL]]; - - self.token = [self findVar:@"channelToken" strippingQuotes:YES]; - if (!self.token) - return; - [self maybeLogMessage:[NSString stringWithFormat:@"Token: %@", self.token]]; - - NSString* roomKey = [self findVar:@"roomKey" strippingQuotes:YES]; - NSString* me = [self findVar:@"me" strippingQuotes:YES]; - if (!roomKey || !me) - return; - self.postMessageUrl = - [NSString stringWithFormat:@"/message?r=%@&u=%@", roomKey, me]; - [self maybeLogMessage:[NSString stringWithFormat:@"POST message URL: %@", - self.postMessageUrl]]; - - NSString* pcConfig = [self findVar:@"pcConfig" strippingQuotes:NO]; - if (!pcConfig) - return; - [self maybeLogMessage: - [NSString stringWithFormat:@"PC Config JSON: %@", pcConfig]]; - - NSString *turnServerUrl = [self findVar:@"turnUrl" strippingQuotes:YES]; - if (turnServerUrl) { - [self maybeLogMessage: - [NSString stringWithFormat:@"TURN server request URL: %@", - turnServerUrl]]; - } - - NSError *error; - NSData *pcData = [pcConfig dataUsingEncoding:NSUTF8StringEncoding]; - NSDictionary *json = - [NSJSONSerialization JSONObjectWithData:pcData options:0 error:&error]; - NSAssert(!error, @"Unable to parse. %@", error.localizedDescription); - NSArray *servers = [json objectForKey:@"iceServers"]; - NSMutableArray *ICEServers = [NSMutableArray array]; - for (NSDictionary *server in servers) { - NSString *url = [server objectForKey:@"url"]; - NSString *username = json[@"username"]; - NSString *credential = [server objectForKey:@"credential"]; - if (!username) { - username = @""; - } - if (!credential) { - credential = @""; - } - [self maybeLogMessage: - [NSString stringWithFormat:@"url [%@] - credential [%@]", - url, - credential]]; - RTCICEServer *ICEServer = - [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:url] - username:username - password:credential]; - NSLog(@"Added ICE Server: %@", ICEServer); - [ICEServers addObject:ICEServer]; - } - [self updateICEServers:ICEServers withTurnServer:turnServerUrl]; - - [self maybeLogMessage: - [NSString stringWithFormat:@"About to open GAE with token: %@", - self.token]]; - self.gaeChannel = - [[GAEChannelClient alloc] initWithToken:self.token - delegate:self.messageHandler]; -} - -@end diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m b/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m deleted file mode 100644 index 65cdd097944..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m +++ /dev/null @@ -1,454 +0,0 @@ -/* - * libjingle - * Copyright 2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "APPRTCAppDelegate.h" - -#import "APPRTCViewController.h" -#import "RTCICECandidate.h" -#import "RTCICEServer.h" -#import "RTCMediaConstraints.h" -#import "RTCMediaStream.h" -#import "RTCPair.h" -#import "RTCPeerConnection.h" -#import "RTCPeerConnectionDelegate.h" -#import "RTCPeerConnectionFactory.h" -#import "RTCSessionDescription.h" - -@interface PCObserver : NSObject<RTCPeerConnectionDelegate> - -- (id)initWithDelegate:(id<APPRTCSendMessage>)delegate; - -@end - -@implementation PCObserver { - id<APPRTCSendMessage> _delegate; -} - -- (id)initWithDelegate:(id<APPRTCSendMessage>)delegate { - if (self = [super init]) { - _delegate = delegate; - } - return self; -} - -- (void)peerConnectionOnError:(RTCPeerConnection *)peerConnection { - NSLog(@"PCO onError."); - NSAssert(NO, @"PeerConnection failed."); -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection - signalingStateChanged:(RTCSignalingState)stateChanged { - NSLog(@"PCO onSignalingStateChange: %d", stateChanged); -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection - addedStream:(RTCMediaStream *)stream { - NSLog(@"PCO onAddStream."); - dispatch_async(dispatch_get_main_queue(), ^(void) { - NSAssert([stream.audioTracks count] >= 1, - @"Expected at least 1 audio stream"); - //NSAssert([stream.videoTracks count] >= 1, - // @"Expected at least 1 video stream"); - // TODO(hughv): Add video support - }); -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection - removedStream:(RTCMediaStream *)stream { - NSLog(@"PCO onRemoveStream."); - // TODO(hughv): Remove video track. -} - -- (void) - peerConnectionOnRenegotiationNeeded:(RTCPeerConnection *)peerConnection { - NSLog(@"PCO onRenegotiationNeeded."); - // TODO(hughv): Handle this. -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection - gotICECandidate:(RTCICECandidate *)candidate { - NSLog(@"PCO onICECandidate.\n Mid[%@] Index[%d] Sdp[%@]", - candidate.sdpMid, - candidate.sdpMLineIndex, - candidate.sdp); - NSDictionary *json = - @{ @"type" : @"candidate", - @"label" : [NSNumber numberWithInt:candidate.sdpMLineIndex], - @"id" : candidate.sdpMid, - @"candidate" : candidate.sdp }; - NSError *error; - NSData *data = - [NSJSONSerialization dataWithJSONObject:json options:0 error:&error]; - if (!error) { - [_delegate sendData:data]; - } else { - NSAssert(NO, @"Unable to serialize JSON object with error: %@", - error.localizedDescription); - } -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection - iceGatheringChanged:(RTCICEGatheringState)newState { - NSLog(@"PCO onIceGatheringChange. %d", newState); -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection - iceConnectionChanged:(RTCICEConnectionState)newState { - NSLog(@"PCO onIceConnectionChange. %d", newState); - if (newState == RTCICEConnectionConnected) - [self displayLogMessage:@"ICE Connection Connected."]; - NSAssert(newState != RTCICEConnectionFailed, @"ICE Connection failed!"); -} - -- (void)displayLogMessage:(NSString *)message { - [_delegate displayLogMessage:message]; -} - -@end - -@interface APPRTCAppDelegate () - -@property(nonatomic, strong) APPRTCAppClient *client; -@property(nonatomic, strong) PCObserver *pcObserver; -@property(nonatomic, strong) RTCPeerConnection *peerConnection; -@property(nonatomic, strong) RTCPeerConnectionFactory *peerConnectionFactory; -@property(nonatomic, strong) NSMutableArray *queuedRemoteCandidates; - -@end - -@implementation APPRTCAppDelegate - -@synthesize window = _window; -@synthesize viewController = _viewController; -@synthesize client = _client; -@synthesize pcObserver = _pcObserver; -@synthesize peerConnection = _peerConnection; -@synthesize peerConnectionFactory = _peerConnectionFactory; -@synthesize queuedRemoteCandidates = _queuedRemoteCandidates; - -#pragma mark - UIApplicationDelegate methods - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [RTCPeerConnectionFactory initializeSSL]; - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - self.viewController = - [[APPRTCViewController alloc] initWithNibName:@"APPRTCViewController" - bundle:nil]; - self.window.rootViewController = self.viewController; - [self.window makeKeyAndVisible]; - return YES; -} - -- (void)applicationWillResignActive:(UIApplication *)application { - [self displayLogMessage:@"Application lost focus, connection broken."]; - [self disconnect]; - [self.viewController resetUI]; -} - -- (void)applicationDidEnterBackground:(UIApplication *)application { -} - -- (void)applicationWillEnterForeground:(UIApplication *)application { -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { -} - -- (void)applicationWillTerminate:(UIApplication *)application { -} - -- (BOOL)application:(UIApplication *)application - openURL:(NSURL *)url - sourceApplication:(NSString *)sourceApplication - annotation:(id)annotation { - if (self.client) { - return NO; - } - self.client = [[APPRTCAppClient alloc] init]; - self.client.ICEServerDelegate = self; - self.client.messageHandler = self; - [self.client connectToRoom:url]; - return YES; -} - -- (void)displayLogMessage:(NSString *)message { - NSLog(@"%@", message); - [self.viewController displayText:message]; -} - -#pragma mark - RTCSendMessage method - -- (void)sendData:(NSData *)data { - [self.client sendData:data]; -} - -#pragma mark - ICEServerDelegate method - -- (void)onICEServers:(NSArray *)servers { - self.queuedRemoteCandidates = [NSMutableArray array]; - self.peerConnectionFactory = [[RTCPeerConnectionFactory alloc] init]; - RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] init]; - self.pcObserver = [[PCObserver alloc] initWithDelegate:self]; - self.peerConnection = - [self.peerConnectionFactory peerConnectionWithICEServers:servers - constraints:constraints - delegate:self.pcObserver]; - RTCMediaStream *lms = - [self.peerConnectionFactory mediaStreamWithLabel:@"ARDAMS"]; - // TODO(hughv): Add video. - [lms addAudioTrack:[self.peerConnectionFactory audioTrackWithID:@"ARDAMSa0"]]; - [self.peerConnection addStream:lms constraints:constraints]; - [self displayLogMessage:@"onICEServers - add local stream."]; -} - -#pragma mark - GAEMessageHandler methods - -- (void)onOpen { - [self displayLogMessage:@"GAE onOpen - create offer."]; - RTCPair *audio = - [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"]; - // TODO(hughv): Add video. - // RTCPair *video = [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" - // value:@"true"]; - NSArray *mandatory = @[ audio /*, video*/ ]; - RTCMediaConstraints *constraints = - [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory - optionalConstraints:nil]; - [self.peerConnection createOfferWithDelegate:self constraints:constraints]; - [self displayLogMessage:@"PC - createOffer."]; -} - -- (void)onMessage:(NSString *)data { - NSString *message = [self unHTMLifyString:data]; - NSError *error; - NSDictionary *objects = [NSJSONSerialization - JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding] - options:0 - error:&error]; - NSAssert(!error, - @"%@", - [NSString stringWithFormat:@"Error: %@", error.description]); - NSAssert([objects count] > 0, @"Invalid JSON object"); - NSString *value = [objects objectForKey:@"type"]; - [self displayLogMessage: - [NSString stringWithFormat:@"GAE onMessage type - %@", value]]; - if ([value compare:@"candidate"] == NSOrderedSame) { - NSString *mid = [objects objectForKey:@"id"]; - NSNumber *sdpLineIndex = [objects objectForKey:@"label"]; - NSString *sdp = [objects objectForKey:@"candidate"]; - RTCICECandidate *candidate = - [[RTCICECandidate alloc] initWithMid:mid - index:sdpLineIndex.intValue - sdp:sdp]; - if (self.queuedRemoteCandidates) { - [self.queuedRemoteCandidates addObject:candidate]; - } else { - [self.peerConnection addICECandidate:candidate]; - } - } else if (([value compare:@"offer"] == NSOrderedSame) || - ([value compare:@"answer"] == NSOrderedSame)) { - NSString *sdpString = [objects objectForKey:@"sdp"]; - RTCSessionDescription *sdp = [[RTCSessionDescription alloc] - initWithType:value sdp:[APPRTCAppDelegate preferISAC:sdpString]]; - [self.peerConnection setRemoteDescriptionWithDelegate:self - sessionDescription:sdp]; - [self displayLogMessage:@"PC - setRemoteDescription."]; - } else if ([value compare:@"bye"] == NSOrderedSame) { - [self disconnect]; - } else { - NSAssert(NO, @"Invalid message: %@", data); - } -} - -- (void)onClose { - [self displayLogMessage:@"GAE onClose."]; - [self disconnect]; -} - -- (void)onError:(int)code withDescription:(NSString *)description { - [self displayLogMessage: - [NSString stringWithFormat:@"GAE onError: %@", description]]; - [self disconnect]; -} - -#pragma mark - RTCSessionDescriptonDelegate methods - -// Match |pattern| to |string| and return the first group of the first -// match, or nil if no match was found. -+ (NSString *)firstMatch:(NSRegularExpression *)pattern - withString:(NSString *)string { - NSTextCheckingResult* result = - [pattern firstMatchInString:string - options:0 - range:NSMakeRange(0, [string length])]; - if (!result) - return nil; - return [string substringWithRange:[result rangeAtIndex:1]]; -} - -// Mangle |origSDP| to prefer the ISAC/16k audio codec. -+ (NSString *)preferISAC:(NSString *)origSDP { - int mLineIndex = -1; - NSString* isac16kRtpMap = nil; - NSArray* lines = [origSDP componentsSeparatedByString:@"\n"]; - NSRegularExpression* isac16kRegex = [NSRegularExpression - regularExpressionWithPattern:@"^a=rtpmap:(\\d+) ISAC/16000[\r]?$" - options:0 - error:nil]; - for (int i = 0; - (i < [lines count]) && (mLineIndex == -1 || isac16kRtpMap == nil); - ++i) { - NSString* line = [lines objectAtIndex:i]; - if ([line hasPrefix:@"m=audio "]) { - mLineIndex = i; - continue; - } - isac16kRtpMap = [self firstMatch:isac16kRegex withString:line]; - } - if (mLineIndex == -1) { - NSLog(@"No m=audio line, so can't prefer iSAC"); - return origSDP; - } - if (isac16kRtpMap == nil) { - NSLog(@"No ISAC/16000 line, so can't prefer iSAC"); - return origSDP; - } - NSArray* origMLineParts = - [[lines objectAtIndex:mLineIndex] componentsSeparatedByString:@" "]; - NSMutableArray* newMLine = - [NSMutableArray arrayWithCapacity:[origMLineParts count]]; - int origPartIndex = 0; - // Format is: m=<media> <port> <proto> <fmt> ... - [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]]; - [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]]; - [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]]; - [newMLine addObject:isac16kRtpMap]; - for (; origPartIndex < [origMLineParts count]; ++origPartIndex) { - if ([isac16kRtpMap compare:[origMLineParts objectAtIndex:origPartIndex]] - != NSOrderedSame) { - [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex]]; - } - } - NSMutableArray* newLines = [NSMutableArray arrayWithCapacity:[lines count]]; - [newLines addObjectsFromArray:lines]; - [newLines replaceObjectAtIndex:mLineIndex - withObject:[newMLine componentsJoinedByString:@" "]]; - return [newLines componentsJoinedByString:@"\n"]; -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection - didCreateSessionDescription:(RTCSessionDescription *)origSdp - error:(NSError *)error { - if (error) { - [self displayLogMessage:@"SDP onFailure."]; - NSAssert(NO, error.description); - return; - } - - [self displayLogMessage:@"SDP onSuccess(SDP) - set local description."]; - RTCSessionDescription* sdp = - [[RTCSessionDescription alloc] - initWithType:origSdp.type - sdp:[APPRTCAppDelegate preferISAC:origSdp.description]]; - [self.peerConnection setLocalDescriptionWithDelegate:self - sessionDescription:sdp]; - [self displayLogMessage:@"PC setLocalDescription."]; - dispatch_async(dispatch_get_main_queue(), ^(void) { - NSDictionary *json = @{ @"type" : sdp.type, @"sdp" : sdp.description }; - NSError *error; - NSData *data = - [NSJSONSerialization dataWithJSONObject:json options:0 error:&error]; - NSAssert(!error, - @"%@", - [NSString stringWithFormat:@"Error: %@", error.description]); - [self sendData:data]; - }); -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection - didSetSessionDescriptionWithError:(NSError *)error { - if (error) { - [self displayLogMessage:@"SDP onFailure."]; - NSAssert(NO, error.description); - return; - } - - [self displayLogMessage:@"SDP onSuccess() - possibly drain candidates"]; - dispatch_async(dispatch_get_main_queue(), ^(void) { - // TODO(hughv): Handle non-initiator case. http://s10/46622051 - if (self.peerConnection.remoteDescription) { - [self displayLogMessage:@"SDP onSuccess - drain candidates"]; - [self drainRemoteCandidates]; - } - }); -} - -#pragma mark - internal methods - -- (void)disconnect { - [self.client - sendData:[@"{\"type\": \"bye\"}" dataUsingEncoding:NSUTF8StringEncoding]]; - self.peerConnection = nil; - self.peerConnectionFactory = nil; - self.pcObserver = nil; - self.client.ICEServerDelegate = nil; - self.client.messageHandler = nil; - self.client = nil; - [RTCPeerConnectionFactory deinitializeSSL]; -} - -- (void)drainRemoteCandidates { - for (RTCICECandidate *candidate in self.queuedRemoteCandidates) { - [self.peerConnection addICECandidate:candidate]; - } - self.queuedRemoteCandidates = nil; -} - -- (NSString *)unHTMLifyString:(NSString *)base { - // TODO(hughv): Investigate why percent escapes are being added. Removing - // them isn't necessary on Android. - // convert HTML escaped characters to UTF8. - NSString *removePercent = - [base stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - // remove leading and trailing ". - NSRange range; - range.length = [removePercent length] - 2; - range.location = 1; - NSString *removeQuotes = [removePercent substringWithRange:range]; - // convert \" to ". - NSString *removeEscapedQuotes = - [removeQuotes stringByReplacingOccurrencesOfString:@"\\\"" - withString:@"\""]; - // convert \\ to \. - NSString *removeBackslash = - [removeEscapedQuotes stringByReplacingOccurrencesOfString:@"\\\\" - withString:@"\\"]; - return removeBackslash; -} - -@end diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCViewController.m b/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCViewController.m deleted file mode 100644 index bd346efcd3a..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCViewController.m +++ /dev/null @@ -1,88 +0,0 @@ -/* - * libjingle - * Copyright 2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "APPRTCViewController.h" - -@interface APPRTCViewController () - -@end - -@implementation APPRTCViewController - -@synthesize textField = _textField; -@synthesize textInstructions = _textInstructions; -@synthesize textOutput = _textOutput; - -- (void)viewDidLoad { - [super viewDidLoad]; - self.textField.delegate = self; - [self.textField becomeFirstResponder]; -} - -- (void)displayText:(NSString *)text { - dispatch_async(dispatch_get_main_queue(), ^(void) { - NSString *output = - [NSString stringWithFormat:@"%@\n%@", self.textOutput.text, text]; - self.textOutput.text = output; - }); -} - -- (void)resetUI { - self.textField.text = nil; - self.textField.hidden = NO; - self.textInstructions.hidden = NO; - self.textOutput.hidden = YES; - self.textOutput.text = nil; -} - -#pragma mark - UITextFieldDelegate - -- (void)textFieldDidEndEditing:(UITextField *)textField { - NSString *room = textField.text; - if ([room length] == 0) { - return; - } - textField.hidden = YES; - self.textInstructions.hidden = YES; - self.textOutput.hidden = NO; - // TODO(hughv): Instead of launching a URL with apprtc scheme, change to - // prepopulating the textField with a valid URL missing the room. This allows - // the user to have the simplicity of just entering the room or the ability to - // override to a custom appspot instance. Remove apprtc:// when this is done. - NSString *url = - [NSString stringWithFormat:@"apprtc://apprtc.appspot.com/?r=%@", room]; - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]]; -} - -- (BOOL)textFieldShouldReturn:(UITextField *)textField { - // There is no other control that can take focus, so manually resign focus - // when return (Join) is pressed to trigger |textFieldDidEndEditing|. - [textField resignFirstResponder]; - return YES; -} - -@end diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/GAEChannelClient.m b/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/GAEChannelClient.m deleted file mode 100644 index e0d9a807692..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/GAEChannelClient.m +++ /dev/null @@ -1,107 +0,0 @@ -/* - * libjingle - * Copyright 2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GAEChannelClient.h" - -#import "RTCPeerConnectionFactory.h" - -@interface GAEChannelClient () - -@property(nonatomic, assign) id<GAEMessageHandler> delegate; -@property(nonatomic, strong) UIWebView *webView; - -@end - -@implementation GAEChannelClient - -@synthesize delegate = _delegate; -@synthesize webView = _webView; - -- (id)initWithToken:(NSString *)token delegate:(id<GAEMessageHandler>)delegate { - self = [super init]; - if (self) { - _webView = [[UIWebView alloc] init]; - _webView.delegate = self; - _delegate = delegate; - NSString *htmlPath = - [[NSBundle mainBundle] pathForResource:@"ios_channel" ofType:@"html"]; - NSURL *htmlUrl = [NSURL fileURLWithPath:htmlPath]; - NSString *path = [NSString stringWithFormat:@"%@?token=%@", - [htmlUrl absoluteString], - token]; - - [_webView - loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]]; - } - return self; -} - -- (void)dealloc { - _webView.delegate = nil; - [_webView stopLoading]; -} - -#pragma mark - UIWebViewDelegate method - -- (BOOL)webView:(UIWebView *)webView - shouldStartLoadWithRequest:(NSURLRequest *)request - navigationType:(UIWebViewNavigationType)navigationType { - NSString *scheme = [request.URL scheme]; - if ([scheme compare:@"js-frame"] != NSOrderedSame) { - return YES; - } - NSString *resourceSpecifier = [request.URL resourceSpecifier]; - NSRange range = [resourceSpecifier rangeOfString:@":"]; - NSString *method; - NSString *message; - if (range.length == 0 && range.location == NSNotFound) { - method = resourceSpecifier; - } else { - method = [resourceSpecifier substringToIndex:range.location]; - message = [resourceSpecifier substringFromIndex:range.location + 1]; - } - dispatch_async(dispatch_get_main_queue(), ^(void) { - if ([method compare:@"onopen"] == NSOrderedSame) { - [self.delegate onOpen]; - } else if ([method compare:@"onmessage"] == NSOrderedSame) { - [self.delegate onMessage:message]; - } else if ([method compare:@"onclose"] == NSOrderedSame) { - [self.delegate onClose]; - } else if ([method compare:@"onerror"] == NSOrderedSame) { - // TODO(hughv): Get error. - int code = -1; - NSString *description = message; - [self.delegate onError:code withDescription:description]; - } else { - NSAssert(NO, @"Invalid message sent from UIWebView: %@", - resourceSpecifier); - } - }); - return YES; -} - -@end diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/README b/chromium/third_party/libjingle/source/talk/examples/ios/README deleted file mode 100644 index 9c0d134171a..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/ios/README +++ /dev/null @@ -1,3 +0,0 @@ -This directory contains an example iOS client for http://apprtc.appspot.com - -See ../../app/webrtc/objc/README for information on how to use it. diff --git a/chromium/third_party/libjingle/source/talk/examples/login/login_main.cc b/chromium/third_party/libjingle/source/talk/examples/login/login_main.cc index 55bb893ad49..5c5d1d78387 100644 --- a/chromium/third_party/libjingle/source/talk/examples/login/login_main.cc +++ b/chromium/third_party/libjingle/source/talk/examples/login/login_main.cc @@ -25,7 +25,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <cstdio> +#include <stdio.h> + #include <iostream> #include "talk/base/thread.h" diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppClient.h b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCAppClient.h index c737beaa99a..8b21db58586 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppClient.h +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCAppClient.h @@ -29,13 +29,18 @@ #import "GAEChannelClient.h" -// Called when there are RTCICEServers. -@protocol ICEServerDelegate<NSObject> +@class APPRTCAppClient; +@protocol APPRTCAppClientDelegate -- (void)onICEServers:(NSArray*)servers; +- (void)appClient:(APPRTCAppClient*)appClient + didErrorWithMessage:(NSString*)message; +- (void)appClient:(APPRTCAppClient*)appClient + didReceiveICEServers:(NSArray*)servers; @end +@class RTCMediaConstraints; + // Negotiates signaling for chatting with apprtc.appspot.com "rooms". // Uses the client<->server specifics of the apprtc AppEngine webapp. // @@ -43,12 +48,21 @@ // call connectToRoom(). apprtc.appspot.com will signal that is successful via // onOpen through the browser channel. Then you should call sendData() and wait // for the registered handler to be called with received messages. -@interface APPRTCAppClient : NSObject<NSURLConnectionDataDelegate> +@interface APPRTCAppClient : NSObject + +@property(nonatomic) BOOL initiator; +@property(nonatomic, copy, readonly) RTCMediaConstraints* videoConstraints; +@property(nonatomic, weak) id<APPRTCAppClientDelegate> delegate; -@property(nonatomic, assign) id<ICEServerDelegate> ICEServerDelegate; -@property(nonatomic, assign) id<GAEMessageHandler> messageHandler; +- (instancetype)initWithDelegate:(id<APPRTCAppClientDelegate>)delegate + messageHandler:(id<GAEMessageHandler>)handler; +- (void)connectToRoom:(NSURL*)room; +- (void)sendData:(NSData*)data; -- (void)connectToRoom:(NSURL *)room; -- (void)sendData:(NSData *)data; +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Disallow init and don't add to documentation +- (instancetype)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ @end diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCAppClient.m b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCAppClient.m new file mode 100644 index 00000000000..853496f81b5 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCAppClient.m @@ -0,0 +1,320 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "APPRTCAppClient.h" + +#import <dispatch/dispatch.h> + +#import "GAEChannelClient.h" +#import "RTCICEServer.h" +#import "RTCMediaConstraints.h" +#import "RTCPair.h" + +@implementation APPRTCAppClient { + dispatch_queue_t _backgroundQueue; + GAEChannelClient* _gaeChannel; + NSURL* _postMessageURL; + BOOL _verboseLogging; + __weak id<GAEMessageHandler> _messageHandler; +} + +- (instancetype)initWithDelegate:(id<APPRTCAppClientDelegate>)delegate + messageHandler:(id<GAEMessageHandler>)handler { + if (self = [super init]) { + _delegate = delegate; + _messageHandler = handler; + _backgroundQueue = dispatch_queue_create("RTCBackgroundQueue", + DISPATCH_QUEUE_SERIAL); + // Uncomment to see Request/Response logging. + // _verboseLogging = YES; + } + return self; +} + +- (void)connectToRoom:(NSURL*)url { + NSString* urlString = + [[url absoluteString] stringByAppendingString:@"&t=json"]; + NSURL* requestURL = [NSURL URLWithString:urlString]; + NSURLRequest* request = [NSURLRequest requestWithURL:requestURL]; + [self sendURLRequest:request + completionHandler:^(NSError* error, + NSHTTPURLResponse* httpResponse, + NSData* responseData) { + int statusCode = [httpResponse statusCode]; + [self logVerbose:[NSString stringWithFormat: + @"Response received\nURL\n%@\nStatus [%d]\nHeaders\n%@", + [httpResponse URL], + statusCode, + [httpResponse allHeaderFields]]]; + NSAssert(statusCode == 200, + @"Invalid response of %d received while connecting to: %@", + statusCode, + urlString); + if (statusCode != 200) { + return; + } + [self handleResponseData:responseData + forRoomRequest:request]; + }]; +} + +- (void)sendData:(NSData*)data { + NSParameterAssert([data length] > 0); + NSString* message = [NSString stringWithUTF8String:[data bytes]]; + [self logVerbose:[NSString stringWithFormat:@"Send message:\n%@", message]]; + if (!_postMessageURL) { + return; + } + NSMutableURLRequest* request = + [NSMutableURLRequest requestWithURL:_postMessageURL]; + request.HTTPMethod = @"POST"; + [request setHTTPBody:data]; + [self sendURLRequest:request + completionHandler:^(NSError* error, + NSHTTPURLResponse* httpResponse, + NSData* responseData) { + int status = [httpResponse statusCode]; + NSString* response = [responseData length] > 0 ? + [NSString stringWithUTF8String:[responseData bytes]] : + nil; + NSAssert(status == 200, + @"Bad response [%d] to message: %@\n\n%@", + status, + message, + response); + }]; +} + +#pragma mark - Private + +- (void)logVerbose:(NSString*)message { + if (_verboseLogging) { + NSLog(@"%@", message); + } +} + +- (void)handleResponseData:(NSData*)responseData + forRoomRequest:(NSURLRequest*)request { + NSDictionary* roomJSON = [self parseJSONData:responseData]; + [self logVerbose:[NSString stringWithFormat:@"Room JSON:\n%@", roomJSON]]; + NSParameterAssert(roomJSON); + if (roomJSON[@"error"]) { + NSArray* errorMessages = roomJSON[@"error_messages"]; + NSMutableString* message = [NSMutableString string]; + for (NSString* errorMessage in errorMessages) { + [message appendFormat:@"%@\n", errorMessage]; + } + [self.delegate appClient:self didErrorWithMessage:message]; + return; + } + NSString* pcConfig = roomJSON[@"pc_config"]; + NSData* pcConfigData = [pcConfig dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary* pcConfigJSON = [self parseJSONData:pcConfigData]; + [self logVerbose:[NSString stringWithFormat:@"PCConfig JSON:\n%@", + pcConfigJSON]]; + NSParameterAssert(pcConfigJSON); + + NSArray* iceServers = [self parseICEServersForPCConfigJSON:pcConfigJSON]; + [self requestTURNServerForICEServers:iceServers + turnServerUrl:roomJSON[@"turn_url"]]; + + _initiator = [roomJSON[@"initiator"] boolValue]; + [self logVerbose:[NSString stringWithFormat:@"Initiator: %d", _initiator]]; + _postMessageURL = [self parsePostMessageURLForRoomJSON:roomJSON + request:request]; + [self logVerbose:[NSString stringWithFormat:@"POST message URL:\n%@", + _postMessageURL]]; + _videoConstraints = [self parseVideoConstraintsForRoomJSON:roomJSON]; + [self logVerbose:[NSString stringWithFormat:@"Media constraints:\n%@", + _videoConstraints]]; + NSString* token = roomJSON[@"token"]; + [self logVerbose: + [NSString stringWithFormat:@"About to open GAE with token: %@", + token]]; + _gaeChannel = + [[GAEChannelClient alloc] initWithToken:token + delegate:_messageHandler]; +} + +- (NSDictionary*)parseJSONData:(NSData*)data { + NSError* error = nil; + NSDictionary* json = + [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + NSAssert(!error, @"Unable to parse. %@", error.localizedDescription); + return json; +} + +- (NSArray*)parseICEServersForPCConfigJSON:(NSDictionary*)pcConfigJSON { + NSMutableArray* result = [NSMutableArray array]; + NSArray* iceServers = pcConfigJSON[@"iceServers"]; + for (NSDictionary* iceServer in iceServers) { + NSString* url = iceServer[@"urls"]; + NSString* username = pcConfigJSON[@"username"]; + NSString* credential = iceServer[@"credential"]; + username = username ? username : @""; + credential = credential ? credential : @""; + [self logVerbose:[NSString stringWithFormat:@"url [%@] - credential [%@]", + url, + credential]]; + RTCICEServer* server = + [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:url] + username:username + password:credential]; + [result addObject:server]; + } + return result; +} + +- (NSURL*)parsePostMessageURLForRoomJSON:(NSDictionary*)roomJSON + request:(NSURLRequest*)request { + NSString* requestUrl = [[request URL] absoluteString]; + NSRange queryRange = [requestUrl rangeOfString:@"?"]; + NSString* baseUrl = [requestUrl substringToIndex:queryRange.location]; + NSString* roomKey = roomJSON[@"room_key"]; + NSParameterAssert([roomKey length] > 0); + NSString* me = roomJSON[@"me"]; + NSParameterAssert([me length] > 0); + NSString* postMessageUrl = + [NSString stringWithFormat:@"%@/message?r=%@&u=%@", baseUrl, roomKey, me]; + return [NSURL URLWithString:postMessageUrl]; +} + +- (RTCMediaConstraints*)parseVideoConstraintsForRoomJSON: + (NSDictionary*)roomJSON { + NSString* mediaConstraints = roomJSON[@"media_constraints"]; + RTCMediaConstraints* constraints = nil; + if ([mediaConstraints length] > 0) { + NSData* constraintsData = + [mediaConstraints dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary* constraintsJSON = [self parseJSONData:constraintsData]; + id video = constraintsJSON[@"video"]; + if ([video isKindOfClass:[NSDictionary class]]) { + NSDictionary* mandatory = video[@"mandatory"]; + NSMutableArray* mandatoryContraints = + [NSMutableArray arrayWithCapacity:[mandatory count]]; + [mandatory enumerateKeysAndObjectsUsingBlock:^( + id key, id obj, BOOL* stop) { + [mandatoryContraints addObject:[[RTCPair alloc] initWithKey:key + value:obj]]; + }]; + // TODO(tkchin): figure out json formats for optional constraints. + constraints = + [[RTCMediaConstraints alloc] + initWithMandatoryConstraints:mandatoryContraints + optionalConstraints:nil]; + } else if ([video isKindOfClass:[NSNumber class]] && [video boolValue]) { + constraints = [[RTCMediaConstraints alloc] init]; + } + } + return constraints; +} + +- (void)requestTURNServerWithUrl:(NSString*)turnServerUrl + completionHandler: + (void (^)(RTCICEServer* turnServer))completionHandler { + NSURL* turnServerURL = [NSURL URLWithString:turnServerUrl]; + NSMutableURLRequest* request = + [NSMutableURLRequest requestWithURL:turnServerURL]; + [request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"]; + [request addValue:@"https://apprtc.appspot.com" + forHTTPHeaderField:@"origin"]; + [self sendURLRequest:request + completionHandler:^(NSError* error, + NSHTTPURLResponse* response, + NSData* responseData) { + if (error) { + NSLog(@"Unable to get TURN server."); + completionHandler(nil); + return; + } + NSDictionary* json = [self parseJSONData:responseData]; + NSString* username = json[@"username"]; + NSString* password = json[@"password"]; + NSArray* uris = json[@"uris"]; + NSParameterAssert([uris count] > 0); + RTCICEServer* turnServer = + [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:uris[0]] + username:username + password:password]; + completionHandler(turnServer); + }]; +} + +- (void)requestTURNServerForICEServers:(NSArray*)iceServers + turnServerUrl:(NSString*)turnServerUrl { + BOOL isTurnPresent = NO; + for (RTCICEServer* iceServer in iceServers) { + if ([[iceServer.URI scheme] isEqualToString:@"turn"]) { + isTurnPresent = YES; + break; + } + } + if (!isTurnPresent) { + [self requestTURNServerWithUrl:turnServerUrl + completionHandler:^(RTCICEServer* turnServer) { + NSArray* servers = iceServers; + if (turnServer) { + servers = [servers arrayByAddingObject:turnServer]; + } + NSLog(@"ICE servers:\n%@", servers); + [self.delegate appClient:self didReceiveICEServers:servers]; + }]; + } else { + NSLog(@"ICE servers:\n%@", iceServers); + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate appClient:self didReceiveICEServers:iceServers]; + }); + } +} + +- (void)sendURLRequest:(NSURLRequest*)request + completionHandler:(void (^)(NSError* error, + NSHTTPURLResponse* httpResponse, + NSData* responseData))completionHandler { + dispatch_async(_backgroundQueue, ^{ + NSError* error = nil; + NSURLResponse* response = nil; + NSData* responseData = [NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:&error]; + NSParameterAssert(!response || + [response isKindOfClass:[NSHTTPURLResponse class]]); + if (error) { + NSLog(@"Failed URL request for:%@\nError:%@", request, error); + } + dispatch_async(dispatch_get_main_queue(), ^{ + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + completionHandler(error, httpResponse, responseData); + }); + }); +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.h b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.h new file mode 100644 index 00000000000..98fe755ef63 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.h @@ -0,0 +1,66 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> + +// Used to log messages to destination like UI. +@protocol APPRTCLogger<NSObject> +- (void)logMessage:(NSString*)message; +@end + +@class RTCVideoTrack; +@class APPRTCConnectionManager; + +// Used to provide AppRTC connection information. +@protocol APPRTCConnectionManagerDelegate<NSObject> + +- (void)connectionManager:(APPRTCConnectionManager*)manager + didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack; + +- (void)connectionManager:(APPRTCConnectionManager*)manager + didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack; + +- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager; + +- (void)connectionManager:(APPRTCConnectionManager*)manager + didErrorWithMessage:(NSString*)errorMessage; + +@end + +// Abstracts the network connection aspect of AppRTC. The delegate will receive +// information about connection status as changes occur. +@interface APPRTCConnectionManager : NSObject + +@property(nonatomic, weak) id<APPRTCConnectionManagerDelegate> delegate; +@property(nonatomic, weak) id<APPRTCLogger> logger; + +- (instancetype)initWithDelegate:(id<APPRTCConnectionManagerDelegate>)delegate + logger:(id<APPRTCLogger>)logger; +- (BOOL)connectToRoomWithURL:(NSURL*)url; +- (void)disconnect; + +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.m b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.m new file mode 100644 index 00000000000..b411a621544 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.m @@ -0,0 +1,495 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "APPRTCConnectionManager.h" + +#import <AVFoundation/AVFoundation.h> +#import "APPRTCAppClient.h" +#import "GAEChannelClient.h" +#import "RTCICECandidate.h" +#import "RTCMediaConstraints.h" +#import "RTCMediaStream.h" +#import "RTCPair.h" +#import "RTCPeerConnection.h" +#import "RTCPeerConnectionDelegate.h" +#import "RTCPeerConnectionFactory.h" +#import "RTCSessionDescription.h" +#import "RTCSessionDescriptionDelegate.h" +#import "RTCStatsDelegate.h" +#import "RTCVideoCapturer.h" +#import "RTCVideoSource.h" + +@interface APPRTCConnectionManager () + <APPRTCAppClientDelegate, GAEMessageHandler, RTCPeerConnectionDelegate, + RTCSessionDescriptionDelegate, RTCStatsDelegate> + +@property(nonatomic, strong) APPRTCAppClient* client; +@property(nonatomic, strong) RTCPeerConnection* peerConnection; +@property(nonatomic, strong) RTCPeerConnectionFactory* peerConnectionFactory; +@property(nonatomic, strong) RTCVideoSource* videoSource; +@property(nonatomic, strong) NSMutableArray* queuedRemoteCandidates; + +@end + +@implementation APPRTCConnectionManager { + NSTimer* _statsTimer; +} + +- (instancetype)initWithDelegate:(id<APPRTCConnectionManagerDelegate>)delegate + logger:(id<APPRTCLogger>)logger { + if (self = [super init]) { + self.delegate = delegate; + self.logger = logger; + self.peerConnectionFactory = [[RTCPeerConnectionFactory alloc] init]; + // TODO(tkchin): turn this into a button. + // Uncomment for stat logs. + // _statsTimer = + // [NSTimer scheduledTimerWithTimeInterval:10 + // target:self + // selector:@selector(didFireStatsTimer:) + // userInfo:nil + // repeats:YES]; + } + return self; +} + +- (void)dealloc { + [self disconnect]; +} + +- (BOOL)connectToRoomWithURL:(NSURL*)url { + if (self.client) { + // Already have a connection. + return NO; + } + self.client = [[APPRTCAppClient alloc] initWithDelegate:self + messageHandler:self]; + [self.client connectToRoom:url]; + return YES; +} + +- (void)disconnect { + if (!self.client) { + return; + } + [self.client + sendData:[@"{\"type\": \"bye\"}" dataUsingEncoding:NSUTF8StringEncoding]]; + [self.peerConnection close]; + self.peerConnection = nil; + self.client = nil; + self.videoSource = nil; + self.queuedRemoteCandidates = nil; +} + +#pragma mark - APPRTCAppClientDelegate + +- (void)appClient:(APPRTCAppClient*)appClient + didErrorWithMessage:(NSString*)message { + [self.delegate connectionManager:self + didErrorWithMessage:message]; +} + +- (void)appClient:(APPRTCAppClient*)appClient + didReceiveICEServers:(NSArray*)servers { + self.queuedRemoteCandidates = [NSMutableArray array]; + RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc] + initWithMandatoryConstraints: + @[ + [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"], + [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"] + ] + optionalConstraints: + @[ + [[RTCPair alloc] initWithKey:@"internalSctpDataChannels" + value:@"true"], + [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" + value:@"true"] + ]]; + self.peerConnection = + [self.peerConnectionFactory peerConnectionWithICEServers:servers + constraints:constraints + delegate:self]; + RTCMediaStream* lms = + [self.peerConnectionFactory mediaStreamWithLabel:@"ARDAMS"]; + + // The iOS simulator doesn't provide any sort of camera capture + // support or emulation (http://goo.gl/rHAnC1) so don't bother + // trying to open a local stream. + RTCVideoTrack* localVideoTrack; + + // TODO(tkchin): local video capture for OSX. See + // https://code.google.com/p/webrtc/issues/detail?id=3417. +#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE + NSString* cameraID = nil; + for (AVCaptureDevice* captureDevice in + [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { + if (captureDevice.position == AVCaptureDevicePositionFront) { + cameraID = [captureDevice localizedName]; + break; + } + } + NSAssert(cameraID, @"Unable to get the front camera id"); + + RTCVideoCapturer* capturer = + [RTCVideoCapturer capturerWithDeviceName:cameraID]; + self.videoSource = [self.peerConnectionFactory + videoSourceWithCapturer:capturer + constraints:self.client.videoConstraints]; + localVideoTrack = + [self.peerConnectionFactory videoTrackWithID:@"ARDAMSv0" + source:self.videoSource]; + if (localVideoTrack) { + [lms addVideoTrack:localVideoTrack]; + } + [self.delegate connectionManager:self + didReceiveLocalVideoTrack:localVideoTrack]; +#endif + + [lms addAudioTrack:[self.peerConnectionFactory audioTrackWithID:@"ARDAMSa0"]]; + [self.peerConnection addStream:lms constraints:constraints]; + [self.logger logMessage:@"onICEServers - added local stream."]; +} + +#pragma mark - GAEMessageHandler methods + +- (void)onOpen { + if (!self.client.initiator) { + [self.logger logMessage:@"Callee; waiting for remote offer"]; + return; + } + [self.logger logMessage:@"GAE onOpen - create offer."]; + RTCPair* audio = + [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"]; + RTCPair* video = + [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]; + NSArray* mandatory = @[ audio, video ]; + RTCMediaConstraints* constraints = + [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory + optionalConstraints:nil]; + [self.peerConnection createOfferWithDelegate:self constraints:constraints]; + [self.logger logMessage:@"PC - createOffer."]; +} + +- (void)onMessage:(NSDictionary*)messageData { + NSString* type = messageData[@"type"]; + NSAssert(type, @"Missing type: %@", messageData); + [self.logger logMessage:[NSString stringWithFormat:@"GAE onMessage type - %@", + type]]; + if ([type isEqualToString:@"candidate"]) { + NSString* mid = messageData[@"id"]; + NSNumber* sdpLineIndex = messageData[@"label"]; + NSString* sdp = messageData[@"candidate"]; + RTCICECandidate* candidate = + [[RTCICECandidate alloc] initWithMid:mid + index:sdpLineIndex.intValue + sdp:sdp]; + if (self.queuedRemoteCandidates) { + [self.queuedRemoteCandidates addObject:candidate]; + } else { + [self.peerConnection addICECandidate:candidate]; + } + } else if ([type isEqualToString:@"offer"] || + [type isEqualToString:@"answer"]) { + NSString* sdpString = messageData[@"sdp"]; + RTCSessionDescription* sdp = [[RTCSessionDescription alloc] + initWithType:type + sdp:[[self class] preferISAC:sdpString]]; + [self.peerConnection setRemoteDescriptionWithDelegate:self + sessionDescription:sdp]; + [self.logger logMessage:@"PC - setRemoteDescription."]; + } else if ([type isEqualToString:@"bye"]) { + [self.delegate connectionManagerDidReceiveHangup:self]; + } else { + NSAssert(NO, @"Invalid message: %@", messageData); + } +} + +- (void)onClose { + [self.logger logMessage:@"GAE onClose."]; + [self.delegate connectionManagerDidReceiveHangup:self]; +} + +- (void)onError:(int)code withDescription:(NSString*)description { + NSString* message = [NSString stringWithFormat:@"GAE onError: %d, %@", + code, description]; + [self.logger logMessage:message]; + [self.delegate connectionManager:self + didErrorWithMessage:message]; +} + +#pragma mark - RTCPeerConnectionDelegate + +- (void)peerConnectionOnError:(RTCPeerConnection*)peerConnection { + dispatch_async(dispatch_get_main_queue(), ^{ + NSString* message = @"PeerConnection error"; + NSLog(@"%@", message); + NSAssert(NO, @"PeerConnection failed."); + [self.delegate connectionManager:self + didErrorWithMessage:message]; + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + signalingStateChanged:(RTCSignalingState)stateChanged { + dispatch_async(dispatch_get_main_queue(), ^{ + NSLog(@"PCO onSignalingStateChange: %d", stateChanged); + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + addedStream:(RTCMediaStream*)stream { + dispatch_async(dispatch_get_main_queue(), ^{ + NSLog(@"PCO onAddStream."); + NSAssert([stream.audioTracks count] == 1 || [stream.videoTracks count] == 1, + @"Expected audio or video track"); + NSAssert([stream.audioTracks count] <= 1, + @"Expected at most 1 audio stream"); + NSAssert([stream.videoTracks count] <= 1, + @"Expected at most 1 video stream"); + if ([stream.videoTracks count] != 0) { + [self.delegate connectionManager:self + didReceiveRemoteVideoTrack:stream.videoTracks[0]]; + } + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + removedStream:(RTCMediaStream*)stream { + dispatch_async(dispatch_get_main_queue(), + ^{ NSLog(@"PCO onRemoveStream."); }); +} + +- (void)peerConnectionOnRenegotiationNeeded:(RTCPeerConnection*)peerConnection { + dispatch_async(dispatch_get_main_queue(), ^{ + NSLog(@"PCO onRenegotiationNeeded - ignoring because AppRTC has a " + "predefined negotiation strategy"); + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + gotICECandidate:(RTCICECandidate*)candidate { + dispatch_async(dispatch_get_main_queue(), ^{ + NSLog(@"PCO onICECandidate.\n Mid[%@] Index[%li] Sdp[%@]", + candidate.sdpMid, + (long)candidate.sdpMLineIndex, + candidate.sdp); + NSDictionary* json = @{ + @"type" : @"candidate", + @"label" : @(candidate.sdpMLineIndex), + @"id" : candidate.sdpMid, + @"candidate" : candidate.sdp + }; + NSError* error; + NSData* data = + [NSJSONSerialization dataWithJSONObject:json options:0 error:&error]; + if (!error) { + [self.client sendData:data]; + } else { + NSAssert(NO, + @"Unable to serialize JSON object with error: %@", + error.localizedDescription); + } + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + iceGatheringChanged:(RTCICEGatheringState)newState { + dispatch_async(dispatch_get_main_queue(), + ^{ NSLog(@"PCO onIceGatheringChange. %d", newState); }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + iceConnectionChanged:(RTCICEConnectionState)newState { + dispatch_async(dispatch_get_main_queue(), ^{ + NSLog(@"PCO onIceConnectionChange. %d", newState); + if (newState == RTCICEConnectionConnected) + [self.logger logMessage:@"ICE Connection Connected."]; + NSAssert(newState != RTCICEConnectionFailed, @"ICE Connection failed!"); + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didOpenDataChannel:(RTCDataChannel*)dataChannel { + NSAssert(NO, @"AppRTC doesn't use DataChannels"); +} + +#pragma mark - RTCSessionDescriptionDelegate + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didCreateSessionDescription:(RTCSessionDescription*)origSdp + error:(NSError*)error { + dispatch_async(dispatch_get_main_queue(), ^{ + if (error) { + [self.logger logMessage:@"SDP onFailure."]; + NSAssert(NO, error.description); + return; + } + [self.logger logMessage:@"SDP onSuccess(SDP) - set local description."]; + RTCSessionDescription* sdp = [[RTCSessionDescription alloc] + initWithType:origSdp.type + sdp:[[self class] preferISAC:origSdp.description]]; + [self.peerConnection setLocalDescriptionWithDelegate:self + sessionDescription:sdp]; + [self.logger logMessage:@"PC setLocalDescription."]; + NSDictionary* json = @{@"type" : sdp.type, @"sdp" : sdp.description}; + NSError* jsonError; + NSData* data = [NSJSONSerialization dataWithJSONObject:json + options:0 + error:&jsonError]; + NSAssert(!jsonError, @"Error: %@", jsonError.description); + [self.client sendData:data]; + }); +} + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didSetSessionDescriptionWithError:(NSError*)error { + dispatch_async(dispatch_get_main_queue(), ^{ + if (error) { + [self.logger logMessage:@"SDP onFailure."]; + NSAssert(NO, error.description); + return; + } + [self.logger logMessage:@"SDP onSuccess() - possibly drain candidates"]; + if (!self.client.initiator) { + if (self.peerConnection.remoteDescription && + !self.peerConnection.localDescription) { + [self.logger logMessage:@"Callee, setRemoteDescription succeeded"]; + RTCPair* audio = [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" + value:@"true"]; + RTCPair* video = [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" + value:@"true"]; + NSArray* mandatory = @[ audio, video ]; + RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc] + initWithMandatoryConstraints:mandatory + optionalConstraints:nil]; + [self.peerConnection createAnswerWithDelegate:self + constraints:constraints]; + [self.logger logMessage:@"PC - createAnswer."]; + } else { + [self.logger logMessage:@"SDP onSuccess - drain candidates"]; + [self drainRemoteCandidates]; + } + } else { + if (self.peerConnection.remoteDescription) { + [self.logger logMessage:@"SDP onSuccess - drain candidates"]; + [self drainRemoteCandidates]; + } + } + }); +} + +#pragma mark - RTCStatsDelegate methods + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didGetStats:(NSArray*)stats { + dispatch_async(dispatch_get_main_queue(), ^{ + NSString* message = [NSString stringWithFormat:@"Stats:\n %@", stats]; + [self.logger logMessage:message]; + }); +} + +#pragma mark - Private + +// Match |pattern| to |string| and return the first group of the first +// match, or nil if no match was found. ++ (NSString*)firstMatch:(NSRegularExpression*)pattern + withString:(NSString*)string { + NSTextCheckingResult* result = + [pattern firstMatchInString:string + options:0 + range:NSMakeRange(0, [string length])]; + if (!result) + return nil; + return [string substringWithRange:[result rangeAtIndex:1]]; +} + +// Mangle |origSDP| to prefer the ISAC/16k audio codec. ++ (NSString*)preferISAC:(NSString*)origSDP { + int mLineIndex = -1; + NSString* isac16kRtpMap = nil; + NSArray* lines = [origSDP componentsSeparatedByString:@"\n"]; + NSRegularExpression* isac16kRegex = [NSRegularExpression + regularExpressionWithPattern:@"^a=rtpmap:(\\d+) ISAC/16000[\r]?$" + options:0 + error:nil]; + for (int i = 0; + (i < [lines count]) && (mLineIndex == -1 || isac16kRtpMap == nil); + ++i) { + NSString* line = [lines objectAtIndex:i]; + if ([line hasPrefix:@"m=audio "]) { + mLineIndex = i; + continue; + } + isac16kRtpMap = [self firstMatch:isac16kRegex withString:line]; + } + if (mLineIndex == -1) { + NSLog(@"No m=audio line, so can't prefer iSAC"); + return origSDP; + } + if (isac16kRtpMap == nil) { + NSLog(@"No ISAC/16000 line, so can't prefer iSAC"); + return origSDP; + } + NSArray* origMLineParts = + [[lines objectAtIndex:mLineIndex] componentsSeparatedByString:@" "]; + NSMutableArray* newMLine = + [NSMutableArray arrayWithCapacity:[origMLineParts count]]; + int origPartIndex = 0; + // Format is: m=<media> <port> <proto> <fmt> ... + [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]]; + [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]]; + [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex++]]; + [newMLine addObject:isac16kRtpMap]; + for (; origPartIndex < [origMLineParts count]; ++origPartIndex) { + if (![isac16kRtpMap + isEqualToString:[origMLineParts objectAtIndex:origPartIndex]]) { + [newMLine addObject:[origMLineParts objectAtIndex:origPartIndex]]; + } + } + NSMutableArray* newLines = [NSMutableArray arrayWithCapacity:[lines count]]; + [newLines addObjectsFromArray:lines]; + [newLines replaceObjectAtIndex:mLineIndex + withObject:[newMLine componentsJoinedByString:@" "]]; + return [newLines componentsJoinedByString:@"\n"]; +} + +- (void)drainRemoteCandidates { + for (RTCICECandidate* candidate in self.queuedRemoteCandidates) { + [self.peerConnection addICECandidate:candidate]; + } + self.queuedRemoteCandidates = nil; +} + +- (void)didFireStatsTimer:(NSTimer*)timer { + if (self.peerConnection) { + [self.peerConnection getStatsWithDelegate:self + mediaStreamTrack:nil + statsOutputLevel:RTCStatsOutputLevelDebug]; + } +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/GAEChannelClient.h b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/GAEChannelClient.h index 49a928d8213..dbaecebd974 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/GAEChannelClient.h +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/GAEChannelClient.h @@ -25,7 +25,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import <UIKit/UIKit.h> +#import <Foundation/Foundation.h> // These methods will be called by the AppEngine chanel. The documentation // for these methods is found here. (Yes, it is a JS API.) @@ -33,17 +33,20 @@ @protocol GAEMessageHandler<NSObject> - (void)onOpen; -- (void)onMessage:(NSString *)data; +- (void)onMessage:(NSDictionary*)data; - (void)onClose; -- (void)onError:(int)code withDescription:(NSString *)description; +- (void)onError:(int)code withDescription:(NSString*)description; @end // Initialize with a token for an AppRTC data channel. This will load // ios_channel.html and use the token to establish a data channel between the // application and AppEngine. -@interface GAEChannelClient : NSObject<UIWebViewDelegate> +@interface GAEChannelClient : NSObject -- (id)initWithToken:(NSString *)token delegate:(id<GAEMessageHandler>)delegate; +@property(nonatomic, weak) id<GAEMessageHandler> delegate; + +- (instancetype)initWithToken:(NSString*)token + delegate:(id<GAEMessageHandler>)delegate; @end diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/GAEChannelClient.m b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/GAEChannelClient.m new file mode 100644 index 00000000000..a95e99a8d6c --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/GAEChannelClient.m @@ -0,0 +1,167 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GAEChannelClient.h" + +#import "RTCPeerConnectionFactory.h" + +#if TARGET_OS_IPHONE + +#import <UIKit/UIKit.h> + +@interface GAEChannelClient () <UIWebViewDelegate> + +@property(nonatomic, strong) UIWebView* webView; + +#else + +#import <WebKit/WebKit.h> + +@interface GAEChannelClient () + +@property(nonatomic, strong) WebView* webView; + +#endif + +@end + +@implementation GAEChannelClient + +- (instancetype)initWithToken:(NSString*)token + delegate:(id<GAEMessageHandler>)delegate { + NSParameterAssert([token length] > 0); + NSParameterAssert(delegate); + self = [super init]; + if (self) { +#if TARGET_OS_IPHONE + _webView = [[UIWebView alloc] init]; + _webView.delegate = self; +#else + _webView = [[WebView alloc] init]; + _webView.policyDelegate = self; +#endif + _delegate = delegate; + NSString* htmlPath = + [[NSBundle mainBundle] pathForResource:@"channel" ofType:@"html"]; + NSURL* htmlUrl = [NSURL fileURLWithPath:htmlPath]; + NSString* path = [NSString + stringWithFormat:@"%@?token=%@", [htmlUrl absoluteString], token]; +#if TARGET_OS_IPHONE + [_webView +#else + [[_webView mainFrame] +#endif + loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]]; + } + return self; +} + +- (void)dealloc { +#if TARGET_OS_IPHONE + _webView.delegate = nil; + [_webView stopLoading]; +#else + _webView.policyDelegate = nil; + [[_webView mainFrame] stopLoading]; +#endif +} + +#if TARGET_OS_IPHONE +#pragma mark - UIWebViewDelegate + +- (BOOL)webView:(UIWebView*)webView + shouldStartLoadWithRequest:(NSURLRequest*)request + navigationType:(UIWebViewNavigationType)navigationType { +#else +// WebPolicyDelegate is an informal delegate. +#pragma mark - WebPolicyDelegate + +- (void)webView:(WebView*)webView + decidePolicyForNavigationAction:(NSDictionary*)actionInformation + request:(NSURLRequest*)request + frame:(WebFrame*)frame + decisionListener:(id<WebPolicyDecisionListener>)listener { +#endif + NSString* scheme = [request.URL scheme]; + NSAssert(scheme, @"scheme is nil: %@", request); + if (![scheme isEqualToString:@"js-frame"]) { +#if TARGET_OS_IPHONE + return YES; +#else + [listener use]; + return; +#endif + } + dispatch_async(dispatch_get_main_queue(), ^{ + NSString* queuedMessage = [webView + stringByEvaluatingJavaScriptFromString:@"popQueuedMessage();"]; + NSAssert([queuedMessage length], @"Empty queued message from JS"); + + NSDictionary* queuedMessageDict = + [GAEChannelClient jsonStringToDictionary:queuedMessage]; + NSString* method = queuedMessageDict[@"type"]; + NSAssert(method, @"Missing method: %@", queuedMessageDict); + NSDictionary* payload = queuedMessageDict[@"payload"]; // May be nil. + + if ([method isEqualToString:@"onopen"]) { + [self.delegate onOpen]; + } else if ([method isEqualToString:@"onmessage"]) { + NSDictionary* payloadData = + [GAEChannelClient jsonStringToDictionary:payload[@"data"]]; + [self.delegate onMessage:payloadData]; + } else if ([method isEqualToString:@"onclose"]) { + [self.delegate onClose]; + } else if ([method isEqualToString:@"onerror"]) { + NSNumber* codeNumber = payload[@"code"]; + int code = [codeNumber intValue]; + NSAssert([codeNumber isEqualToNumber:[NSNumber numberWithInt:code]], + @"Unexpected non-integral code: %@", payload); + [self.delegate onError:code withDescription:payload[@"description"]]; + } else { + NSAssert(NO, @"Invalid message sent from UIWebView: %@", queuedMessage); + } + }); +#if TARGET_OS_IPHONE + return NO; +#else + [listener ignore]; + return; +#endif +} + +#pragma mark - Private + ++ (NSDictionary*)jsonStringToDictionary:(NSString*)str { + NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding]; + NSError* error; + NSDictionary* dict = + [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + NSAssert(!error, @"Invalid JSON? %@", str); + return dict; +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/ios_channel.html b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/channel.html index a55b8f48bc3..86846dd6870 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/ios_channel.html +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/channel.html @@ -6,10 +6,11 @@ Helper HTML that redirects Google AppEngine's Channel API to Objective C. This is done by hosting this page in an iOS application. The hosting class creates a UIWebView control and implements the UIWebViewDelegate - protocol. Then when there is a channel message, it is encoded in an IFRAME. - That IFRAME is added to the DOM which triggers a navigation event - |shouldStartLoadWithRequest| in Objective C which can then be routed in the - application as desired. + protocol. Then when there is a channel message it is queued in JS, + and an IFRAME is added to the DOM, triggering a navigation event + |shouldStartLoadWithRequest| in Objective C which can then fetch the + message using |popQueuedMessage|. This queuing is necessary to avoid URL + length limits in UIWebView (which are undocumented). --> <body onbeforeunload="closeSocket()" onload="openSocket()"> <script type="text/javascript"> @@ -38,6 +39,10 @@ var channel = null; var socket = null; + // In-order queue of messages to be delivered to ObjectiveC. + // Each is a JSON.stringify()'d dictionary containing a 'type' + // field and optionally a 'payload'. + var messageQueue = []; function openSocket() { if (!QueryString.token || !QueryString.token.match(/^[A-z0-9_-]+$/)) { @@ -52,17 +57,13 @@ sendMessageToObjC("onopen"); }, 'onmessage': function(msg) { - sendMessageToObjC("onmessage:" + - encodeURIComponent(JSON.stringify(msg.data))); + sendMessageToObjC("onmessage", msg); }, 'onclose': function() { sendMessageToObjC("onclose"); }, 'onerror': function(err) { - sendMessageToObjC("onerror:" + - encodeURIComponent(JSON.stringify(err.code)) + - ":message:" + - encodeURIComponent(JSON.stringify(err.description))); + sendMessageToObjC("onerror", err); } }); } @@ -73,9 +74,10 @@ // Add an IFRAME to the DOM to trigger a navigation event. Then remove // it as it is no longer needed. Only one event is generated. - function sendMessageToObjC(message) { + function sendMessageToObjC(type, payload) { + messageQueue.push(JSON.stringify({'type': type, 'payload': payload})); var iframe = document.createElement("IFRAME"); - iframe.setAttribute("src", "js-frame:" + message); + iframe.setAttribute("src", "js-frame:"); // For some reason we need to set a non-empty size for the iOS6 // simulator... iframe.setAttribute("height", "1px"); @@ -83,6 +85,10 @@ document.documentElement.appendChild(iframe); iframe.parentNode.removeChild(iframe); } + + function popQueuedMessage() { + return messageQueue.shift(); + } </script> </body> </html> diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.h b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.h new file mode 100644 index 00000000000..196b39fd9d4 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.h @@ -0,0 +1,34 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <UIKit/UIKit.h> + +// The main application class of the AppRTCDemo iOS app demonstrating +// interoperability between the Objective C implementation of PeerConnection +// and the apprtc.appspot.com demo webapp. +@interface APPRTCAppDelegate : NSObject<UIApplicationDelegate> +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.h b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.m index 22754e3ad26..58963d53e93 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.h +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.m @@ -25,32 +25,41 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import <UIKit/UIKit.h> +#import "APPRTCAppDelegate.h" -#import "GAEChannelClient.h" -#import "APPRTCAppClient.h" -#import "RTCSessionDescriptonDelegate.h" +#import "APPRTCViewController.h" +#import "RTCPeerConnectionFactory.h" -// Used to send a message to an apprtc.appspot.com "room". -@protocol APPRTCSendMessage<NSObject> +@implementation APPRTCAppDelegate { + UIWindow* _window; +} -- (void)sendData:(NSData *)data; -// Logging helper. -- (void)displayLogMessage:(NSString *)message; -@end +#pragma mark - UIApplicationDelegate methods + +- (BOOL)application:(UIApplication*)application + didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + [RTCPeerConnectionFactory initializeSSL]; + _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + APPRTCViewController* viewController = + [[APPRTCViewController alloc] initWithNibName:@"APPRTCViewController" + bundle:nil]; + _window.rootViewController = viewController; + [_window makeKeyAndVisible]; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication*)application { + [[self appRTCViewController] applicationWillResignActive:application]; +} -@class APPRTCViewController; +- (void)applicationWillTerminate:(UIApplication*)application { + [RTCPeerConnectionFactory deinitializeSSL]; +} -// The main application class of the AppRTCDemo iOS app demonstrating -// interoperability between the Objcective C implementation of PeerConnection -// and the apprtc.appspot.com demo webapp. -@interface APPRTCAppDelegate : UIResponder<ICEServerDelegate, - GAEMessageHandler, - APPRTCSendMessage, - RTCSessionDescriptonDelegate, - UIApplicationDelegate> +#pragma mark - Private -@property (strong, nonatomic) UIWindow *window; -@property (strong, nonatomic) APPRTCViewController *viewController; +- (APPRTCViewController*)appRTCViewController { + return (APPRTCViewController*)_window.rootViewController; +} @end diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCViewController.h b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.h index 6b107a564c2..5b10199c2ce 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCViewController.h +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.h @@ -30,11 +30,11 @@ // The view controller that is displayed when AppRTCDemo is loaded. @interface APPRTCViewController : UIViewController<UITextFieldDelegate> -@property (weak, nonatomic) IBOutlet UITextField *textField; -@property (weak, nonatomic) IBOutlet UITextView *textInstructions; -@property (weak, nonatomic) IBOutlet UITextView *textOutput; +@property(weak, nonatomic) IBOutlet UITextField* roomInput; +@property(weak, nonatomic) IBOutlet UITextView* instructionsView; +@property(weak, nonatomic) IBOutlet UITextView* logView; +@property(weak, nonatomic) IBOutlet UIView* blackView; -- (void)displayText:(NSString *)text; -- (void)resetUI; +- (void)applicationWillResignActive:(UIApplication*)application; @end diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.m b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.m new file mode 100644 index 00000000000..a4a0bd33d1b --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.m @@ -0,0 +1,231 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "APPRTCViewController.h" + +#import <AVFoundation/AVFoundation.h> +#import "APPRTCConnectionManager.h" +#import "RTCEAGLVideoView.h" + +// Padding space for local video view with its parent. +static CGFloat const kLocalViewPadding = 20; + +@interface APPRTCViewController () +<APPRTCConnectionManagerDelegate, APPRTCLogger, RTCEAGLVideoViewDelegate> +@property(nonatomic, assign) UIInterfaceOrientation statusBarOrientation; +@property(nonatomic, strong) RTCEAGLVideoView* localVideoView; +@property(nonatomic, strong) RTCEAGLVideoView* remoteVideoView; +@end + +@implementation APPRTCViewController { + APPRTCConnectionManager* _connectionManager; + CGSize _localVideoSize; + CGSize _remoteVideoSize; +} + +- (instancetype)initWithNibName:(NSString*)nibName + bundle:(NSBundle*)bundle { + if (self = [super initWithNibName:nibName bundle:bundle]) { + _connectionManager = + [[APPRTCConnectionManager alloc] initWithDelegate:self + logger:self]; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + self.statusBarOrientation = + [UIApplication sharedApplication].statusBarOrientation; + self.roomInput.delegate = self; + [self.roomInput becomeFirstResponder]; +} + +- (void)viewDidLayoutSubviews { + if (self.statusBarOrientation != + [UIApplication sharedApplication].statusBarOrientation) { + self.statusBarOrientation = + [UIApplication sharedApplication].statusBarOrientation; + [[NSNotificationCenter defaultCenter] + postNotificationName:@"StatusBarOrientationDidChange" + object:nil]; + } +} + +- (void)applicationWillResignActive:(UIApplication*)application { + [self logMessage:@"Application lost focus, connection broken."]; + [self disconnect]; +} + +#pragma mark - APPRTCConnectionManagerDelegate + +- (void)connectionManager:(APPRTCConnectionManager*)manager + didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack { + self.localVideoView.hidden = NO; + self.localVideoView.videoTrack = localVideoTrack; +} + +- (void)connectionManager:(APPRTCConnectionManager*)manager + didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack { + self.remoteVideoView.videoTrack = remoteVideoTrack; +} + +- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager { + [self showAlertWithMessage:@"Remote hung up."]; + [self disconnect]; +} + +- (void)connectionManager:(APPRTCConnectionManager*)manager + didErrorWithMessage:(NSString*)message { + [self showAlertWithMessage:message]; + [self disconnect]; +} + +#pragma mark - APPRTCLogger + +- (void)logMessage:(NSString*)message { + dispatch_async(dispatch_get_main_queue(), ^{ + NSString* output = + [NSString stringWithFormat:@"%@\n%@", self.logView.text, message]; + self.logView.text = output; + [self.logView + scrollRangeToVisible:NSMakeRange([self.logView.text length], 0)]; + }); +} + +#pragma mark - RTCEAGLVideoViewDelegate + +- (void)videoView:(RTCEAGLVideoView*)videoView + didChangeVideoSize:(CGSize)size { + if (videoView == self.localVideoView) { + _localVideoSize = size; + } else if (videoView == self.remoteVideoView) { + _remoteVideoSize = size; + } else { + NSParameterAssert(NO); + } + [self updateVideoViewLayout]; +} + +#pragma mark - UITextFieldDelegate + +- (void)textFieldDidEndEditing:(UITextField*)textField { + NSString* room = textField.text; + if ([room length] == 0) { + return; + } + textField.hidden = YES; + self.instructionsView.hidden = YES; + self.logView.hidden = NO; + NSString* url = + [NSString stringWithFormat:@"https://apprtc.appspot.com/?r=%@", room]; + [_connectionManager connectToRoomWithURL:[NSURL URLWithString:url]]; + [self setupCaptureSession]; +} + +- (BOOL)textFieldShouldReturn:(UITextField*)textField { + // There is no other control that can take focus, so manually resign focus + // when return (Join) is pressed to trigger |textFieldDidEndEditing|. + [textField resignFirstResponder]; + return YES; +} + +#pragma mark - Private + +- (void)disconnect { + [self resetUI]; + [_connectionManager disconnect]; +} + +- (void)showAlertWithMessage:(NSString*)message { + UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:nil + message:message + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alertView show]; +} + +- (void)resetUI { + [self.roomInput resignFirstResponder]; + self.roomInput.text = nil; + self.roomInput.hidden = NO; + self.instructionsView.hidden = NO; + self.logView.hidden = YES; + self.logView.text = nil; + self.blackView.hidden = YES; + [self.remoteVideoView removeFromSuperview]; + self.remoteVideoView = nil; + [self.localVideoView removeFromSuperview]; + self.localVideoView = nil; +} + +- (void)setupCaptureSession { + self.blackView.hidden = NO; + self.remoteVideoView = + [[RTCEAGLVideoView alloc] initWithFrame:self.blackView.bounds]; + self.remoteVideoView.delegate = self; + self.remoteVideoView.transform = CGAffineTransformMakeScale(-1, 1); + [self.blackView addSubview:self.remoteVideoView]; + + self.localVideoView = + [[RTCEAGLVideoView alloc] initWithFrame:self.blackView.bounds]; + self.localVideoView.delegate = self; + [self.blackView addSubview:self.localVideoView]; + [self updateVideoViewLayout]; +} + +- (void)updateVideoViewLayout { + // TODO(tkchin): handle rotation. + CGSize defaultAspectRatio = CGSizeMake(4, 3); + CGSize localAspectRatio = CGSizeEqualToSize(_localVideoSize, CGSizeZero) ? + defaultAspectRatio : _localVideoSize; + CGSize remoteAspectRatio = CGSizeEqualToSize(_remoteVideoSize, CGSizeZero) ? + defaultAspectRatio : _remoteVideoSize; + + CGRect remoteVideoFrame = + AVMakeRectWithAspectRatioInsideRect(remoteAspectRatio, + self.blackView.bounds); + self.remoteVideoView.frame = remoteVideoFrame; + + CGRect localVideoFrame = + AVMakeRectWithAspectRatioInsideRect(localAspectRatio, + self.blackView.bounds); + localVideoFrame.size.width = localVideoFrame.size.width / 3; + localVideoFrame.size.height = localVideoFrame.size.height / 3; + localVideoFrame.origin.x = CGRectGetMaxX(self.blackView.bounds) + - localVideoFrame.size.width - kLocalViewPadding; + localVideoFrame.origin.y = CGRectGetMaxY(self.blackView.bounds) + - localVideoFrame.size.height - kLocalViewPadding; + self.localVideoView.frame = localVideoFrame; +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/AppRTCDemo-Prefix.pch b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/AppRTCDemo-Prefix.pch index 3ac2c3b1a5c..3ac2c3b1a5c 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/AppRTCDemo-Prefix.pch +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/AppRTCDemo-Prefix.pch diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/Default.png b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/Default.png Binary files differindex 4c8ca6f693f..4c8ca6f693f 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/Default.png +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/Default.png diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/Info.plist b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/Info.plist index 72504aa5c88..b61648057c8 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/Info.plist +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/Info.plist @@ -38,19 +38,6 @@ <array> <string>iPhoneOS</string> </array> - <key>CFBundleURLTypes</key> - <array> - <dict> - <key>CFBundleTypeRole</key> - <string>Editor</string> - <key>CFBundleURLName</key> - <string>com.google.apprtcdemo</string> - <key>CFBundleURLSchemes</key> - <array> - <string>apprtc</string> - </array> - </dict> - </array> <key>CFBundleVersion</key> <string>1.0</string> <key>UIRequiredDeviceCapabilities</key> @@ -70,8 +57,6 @@ <key>UISupportedInterfaceOrientations</key> <array> <string>UIInterfaceOrientationPortrait</string> - <string>UIInterfaceOrientationLandscapeLeft</string> - <string>UIInterfaceOrientationLandscapeRight</string> </array> </dict> </plist> diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/ResourceRules.plist b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/ResourceRules.plist index e7ec329dccb..e7ec329dccb 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/ResourceRules.plist +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/ResourceRules.plist diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/en.lproj/APPRTCViewController.xib b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/en.lproj/APPRTCViewController.xib index cd73ea64ef0..cb2dc8394e0 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/en.lproj/APPRTCViewController.xib +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/en.lproj/APPRTCViewController.xib @@ -1,14 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00"> <data> - <int key="IBDocument.SystemTarget">1552</int> - <string key="IBDocument.SystemVersion">12D78</string> - <string key="IBDocument.InterfaceBuilderVersion">3084</string> - <string key="IBDocument.AppKitVersion">1187.37</string> - <string key="IBDocument.HIToolboxVersion">626.00</string> + <int key="IBDocument.SystemTarget">1536</int> + <string key="IBDocument.SystemVersion">13B42</string> + <string key="IBDocument.InterfaceBuilderVersion">4514</string> + <string key="IBDocument.AppKitVersion">1265</string> + <string key="IBDocument.HIToolboxVersion">696.00</string> <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> - <string key="NS.object.0">2083</string> + <string key="NS.object.0">3747</string> </object> <array key="IBDocument.IntegratedClassDependencies"> <string>IBNSLayoutConstraint</string> @@ -34,7 +34,7 @@ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> </object> <object class="IBUIView" id="774585933"> - <nil key="NSNextResponder"/> + <reference key="NSNextResponder"/> <int key="NSvFlags">274</int> <array class="NSMutableArray" key="NSSubviews"> <object class="IBUITextView" id="176994284"> @@ -42,7 +42,8 @@ <int key="NSvFlags">292</int> <string key="NSFrame">{{20, 20}, {280, 141}}</string> <reference key="NSSuperview" ref="774585933"/> - <reference key="NSNextKeyView" ref="546385578"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="634862110"/> <string key="NSReuseIdentifierKey">_NS:9</string> <object class="NSColor" key="IBUIBackgroundColor" id="621995359"> <int key="NSColorSpace">1</int> @@ -51,7 +52,7 @@ <bool key="IBUIClipsSubviews">YES</bool> <bool key="IBUIUserInteractionEnabled">NO</bool> <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> - <string key="IBUIText">Use Safari and open a URL with a scheme of apprtc to load the test app and connect. i.e. apprtc://apprtc.appspot.com/?r=12345678 Or just enter the room below to connect to apprtc.</string> + <string key="IBUIText">Enter the room below to connect to apprtc.</string> <object class="IBUITextInputTraits" key="IBUITextInputTraits"> <int key="IBUIAutocapitalizationType">2</int> <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> @@ -60,8 +61,8 @@ <int key="type">1</int> <double key="pointSize">14</double> </object> - <object class="NSFont" key="IBUIFont" id="371333696"> - <string key="NSName">Helvetica</string> + <object class="NSFont" key="IBUIFont" id="144501234"> + <string key="NSName">HelveticaNeue</string> <double key="NSSize">14</double> <int key="NSfFlags">16</int> </object> @@ -71,7 +72,8 @@ <int key="NSvFlags">292</int> <string key="NSFrame">{{20, 180}, {280, 30}}</string> <reference key="NSSuperview" ref="774585933"/> - <reference key="NSNextKeyView" ref="634862110"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="261050959"/> <string key="NSReuseIdentifierKey">_NS:9</string> <bool key="IBUIOpaque">NO</bool> <bool key="IBUIClipsSubviews">YES</bool> @@ -95,13 +97,15 @@ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> </object> <reference key="IBUIFontDescription" ref="166497611"/> - <reference key="IBUIFont" ref="371333696"/> + <reference key="IBUIFont" ref="144501234"/> </object> <object class="IBUITextView" id="634862110"> <reference key="NSNextResponder" ref="774585933"/> <int key="NSvFlags">-2147483356</int> - <string key="NSFrame">{{20, 20}, {280, 508}}</string> + <string key="NSFrame">{{20, 20}, {280, 190}}</string> <reference key="NSSuperview" ref="774585933"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="546385578"/> <string key="NSReuseIdentifierKey">_NS:9</string> <reference key="IBUIBackgroundColor" ref="621995359"/> <bool key="IBUIClipsSubviews">YES</bool> @@ -114,10 +118,26 @@ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> </object> <reference key="IBUIFontDescription" ref="166497611"/> - <reference key="IBUIFont" ref="371333696"/> + <reference key="IBUIFont" ref="144501234"/> + </object> + <object class="IBUIView" id="261050959"> + <reference key="NSNextResponder" ref="774585933"/> + <int key="NSvFlags">-2147483356</int> + <string key="NSFrame">{{20, 228}, {280, 300}}</string> + <reference key="NSSuperview" ref="774585933"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView"/> + <string key="NSReuseIdentifierKey">_NS:9</string> + <object class="NSColor" key="IBUIBackgroundColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MAA</bytes> + </object> + <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> </object> </array> <string key="NSFrame">{{0, 20}, {320, 548}}</string> + <reference key="NSSuperview"/> + <reference key="NSWindow"/> <reference key="NSNextKeyView" ref="176994284"/> <object class="NSColor" key="IBUIBackgroundColor"> <int key="NSColorSpace">3</int> @@ -140,7 +160,7 @@ </array> </object> <string key="IBUITargetRuntime">IBCocoaTouchFramework</string> - <string key="IBUIDisplayName">Retina 4 Full Screen</string> + <string key="IBUIDisplayName">Retina 4-inch Full Screen</string> <int key="IBUIType">2</int> </object> <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string> @@ -158,7 +178,7 @@ </object> <object class="IBConnectionRecord"> <object class="IBCocoaTouchOutletConnection" key="connection"> - <string key="label">textField</string> + <string key="label">roomInput</string> <reference key="source" ref="372490531"/> <reference key="destination" ref="546385578"/> </object> @@ -166,7 +186,7 @@ </object> <object class="IBConnectionRecord"> <object class="IBCocoaTouchOutletConnection" key="connection"> - <string key="label">textInstructions</string> + <string key="label">instructionsView</string> <reference key="source" ref="372490531"/> <reference key="destination" ref="176994284"/> </object> @@ -174,12 +194,20 @@ </object> <object class="IBConnectionRecord"> <object class="IBCocoaTouchOutletConnection" key="connection"> - <string key="label">textOutput</string> + <string key="label">logView</string> <reference key="source" ref="372490531"/> <reference key="destination" ref="634862110"/> </object> <int key="connectionID">138</int> </object> + <object class="IBConnectionRecord"> + <object class="IBCocoaTouchOutletConnection" key="connection"> + <string key="label">blackView</string> + <reference key="source" ref="372490531"/> + <reference key="destination" ref="261050959"/> + </object> + <int key="connectionID">151</int> + </object> </array> <object class="IBMutableOrderedSet" key="objectRecords"> <array key="orderedObjects"> @@ -204,12 +232,12 @@ <int key="objectID">6</int> <reference key="object" ref="774585933"/> <array class="NSMutableArray" key="children"> - <object class="IBNSLayoutConstraint" id="117610664"> + <object class="IBNSLayoutConstraint" id="933872207"> <reference key="firstItem" ref="774585933"/> - <int key="firstAttribute">6</int> + <int key="firstAttribute">4</int> <int key="relation">0</int> - <reference key="secondItem" ref="546385578"/> - <int key="secondAttribute">6</int> + <reference key="secondItem" ref="261050959"/> + <int key="secondAttribute">4</int> <float key="multiplier">1</float> <object class="IBNSLayoutSymbolicConstant" key="constant"> <double key="value">20</double> @@ -217,88 +245,145 @@ <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> <int key="scoringType">8</int> - <float key="scoringTypeFloat">29</float> + <float key="scoringTypeFloat">23</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> - <object class="IBNSLayoutConstraint" id="555801739"> - <reference key="firstItem" ref="546385578"/> + <object class="IBNSLayoutConstraint" id="863471211"> + <reference key="firstItem" ref="261050959"/> <int key="firstAttribute">3</int> <int key="relation">0</int> <reference key="secondItem" ref="774585933"/> <int key="secondAttribute">3</int> <float key="multiplier">1</float> <object class="IBLayoutConstant" key="constant"> - <double key="value">180</double> + <double key="value">228</double> </object> <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> <int key="scoringType">3</int> <float key="scoringTypeFloat">9</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> - <object class="IBNSLayoutConstraint" id="860801955"> + <object class="IBNSLayoutConstraint" id="590654550"> <reference key="firstItem" ref="546385578"/> <int key="firstAttribute">5</int> <int key="relation">0</int> - <reference key="secondItem" ref="774585933"/> + <reference key="secondItem" ref="261050959"/> <int key="secondAttribute">5</int> <float key="multiplier">1</float> + <object class="IBLayoutConstant" key="constant"> + <double key="value">0.0</double> + </object> + <float key="priority">1000</float> + <reference key="containingView" ref="774585933"/> + <int key="scoringType">6</int> + <float key="scoringTypeFloat">24</float> + <int key="contentType">2</int> + <bool key="placeholder">NO</bool> + </object> + <object class="IBNSLayoutConstraint" id="734153049"> + <reference key="firstItem" ref="546385578"/> + <int key="firstAttribute">6</int> + <int key="relation">0</int> + <reference key="secondItem" ref="261050959"/> + <int key="secondAttribute">6</int> + <float key="multiplier">1</float> + <object class="IBLayoutConstant" key="constant"> + <double key="value">0.0</double> + </object> + <float key="priority">1000</float> + <reference key="containingView" ref="774585933"/> + <int key="scoringType">6</int> + <float key="scoringTypeFloat">24</float> + <int key="contentType">2</int> + <bool key="placeholder">NO</bool> + </object> + <object class="IBNSLayoutConstraint" id="117610664"> + <reference key="firstItem" ref="774585933"/> + <int key="firstAttribute">6</int> + <int key="relation">0</int> + <reference key="secondItem" ref="546385578"/> + <int key="secondAttribute">6</int> + <float key="multiplier">1</float> <object class="IBNSLayoutSymbolicConstant" key="constant"> <double key="value">20</double> </object> <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> - <int key="scoringType">8</int> + <int key="scoringType">0</int> <float key="scoringTypeFloat">29</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> - <object class="IBNSLayoutConstraint" id="19985792"> - <reference key="firstItem" ref="634862110"/> - <int key="firstAttribute">3</int> + <object class="IBNSLayoutConstraint" id="860801955"> + <reference key="firstItem" ref="546385578"/> + <int key="firstAttribute">5</int> <int key="relation">0</int> <reference key="secondItem" ref="774585933"/> - <int key="secondAttribute">3</int> + <int key="secondAttribute">5</int> <float key="multiplier">1</float> <object class="IBNSLayoutSymbolicConstant" key="constant"> <double key="value">20</double> </object> <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> - <int key="scoringType">8</int> + <int key="scoringType">0</int> <float key="scoringTypeFloat">29</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> - <object class="IBNSLayoutConstraint" id="1001701893"> - <reference key="firstItem" ref="774585933"/> - <int key="firstAttribute">6</int> + <object class="IBNSLayoutConstraint" id="544488581"> + <reference key="firstItem" ref="634862110"/> + <int key="firstAttribute">4</int> <int key="relation">0</int> - <reference key="secondItem" ref="634862110"/> - <int key="secondAttribute">6</int> + <reference key="secondItem" ref="546385578"/> + <int key="secondAttribute">4</int> + <float key="multiplier">1</float> + <object class="IBLayoutConstant" key="constant"> + <double key="value">0.0</double> + </object> + <float key="priority">1000</float> + <reference key="containingView" ref="774585933"/> + <int key="scoringType">6</int> + <float key="scoringTypeFloat">24</float> + <int key="contentType">2</int> + <bool key="placeholder">NO</bool> + </object> + <object class="IBNSLayoutConstraint" id="19985792"> + <reference key="firstItem" ref="634862110"/> + <int key="firstAttribute">3</int> + <int key="relation">0</int> + <reference key="secondItem" ref="774585933"/> + <int key="secondAttribute">3</int> <float key="multiplier">1</float> <object class="IBNSLayoutSymbolicConstant" key="constant"> <double key="value">20</double> </object> <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> - <int key="scoringType">8</int> + <int key="scoringType">0</int> <float key="scoringTypeFloat">29</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> - <object class="IBNSLayoutConstraint" id="914503793"> + <object class="IBNSLayoutConstraint" id="1001701893"> <reference key="firstItem" ref="774585933"/> - <int key="firstAttribute">4</int> + <int key="firstAttribute">6</int> <int key="relation">0</int> <reference key="secondItem" ref="634862110"/> - <int key="secondAttribute">4</int> + <int key="secondAttribute">6</int> <float key="multiplier">1</float> <object class="IBNSLayoutSymbolicConstant" key="constant"> <double key="value">20</double> </object> <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> - <int key="scoringType">8</int> + <int key="scoringType">0</int> <float key="scoringTypeFloat">29</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> <object class="IBNSLayoutConstraint" id="858545289"> <reference key="firstItem" ref="634862110"/> @@ -312,9 +397,10 @@ </object> <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> - <int key="scoringType">8</int> + <int key="scoringType">0</int> <float key="scoringTypeFloat">29</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> <object class="IBNSLayoutConstraint" id="1039342825"> <reference key="firstItem" ref="774585933"/> @@ -328,9 +414,10 @@ </object> <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> - <int key="scoringType">8</int> + <int key="scoringType">0</int> <float key="scoringTypeFloat">29</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> <object class="IBNSLayoutConstraint" id="663764352"> <reference key="firstItem" ref="176994284"/> @@ -344,9 +431,10 @@ </object> <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> - <int key="scoringType">8</int> + <int key="scoringType">0</int> <float key="scoringTypeFloat">29</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> <object class="IBNSLayoutConstraint" id="46028745"> <reference key="firstItem" ref="176994284"/> @@ -360,13 +448,15 @@ </object> <float key="priority">1000</float> <reference key="containingView" ref="774585933"/> - <int key="scoringType">8</int> + <int key="scoringType">0</int> <float key="scoringTypeFloat">29</float> <int key="contentType">3</int> + <bool key="placeholder">NO</bool> </object> <reference ref="176994284"/> <reference ref="546385578"/> <reference ref="634862110"/> + <reference ref="261050959"/> </array> <reference key="parent" ref="0"/> </object> @@ -389,6 +479,7 @@ <int key="scoringType">3</int> <float key="scoringTypeFloat">9</float> <int key="contentType">1</int> + <bool key="placeholder">NO</bool> </object> </array> <reference key="parent" ref="774585933"/> @@ -425,11 +516,6 @@ <reference key="parent" ref="176994284"/> </object> <object class="IBObjectRecord"> - <int key="objectID">124</int> - <reference key="object" ref="555801739"/> - <reference key="parent" ref="774585933"/> - </object> - <object class="IBObjectRecord"> <int key="objectID">126</int> <reference key="object" ref="117610664"/> <reference key="parent" ref="774585933"/> @@ -437,7 +523,25 @@ <object class="IBObjectRecord"> <int key="objectID">128</int> <reference key="object" ref="634862110"/> - <array class="NSMutableArray" key="children"/> + <array class="NSMutableArray" key="children"> + <object class="IBNSLayoutConstraint" id="988159807"> + <reference key="firstItem" ref="634862110"/> + <int key="firstAttribute">8</int> + <int key="relation">0</int> + <nil key="secondItem"/> + <int key="secondAttribute">0</int> + <float key="multiplier">1</float> + <object class="IBLayoutConstant" key="constant"> + <double key="value">190</double> + </object> + <float key="priority">1000</float> + <reference key="containingView" ref="634862110"/> + <int key="scoringType">3</int> + <float key="scoringTypeFloat">9</float> + <int key="contentType">1</int> + <bool key="placeholder">NO</bool> + </object> + </array> <reference key="parent" ref="774585933"/> </object> <object class="IBObjectRecord"> @@ -446,11 +550,6 @@ <reference key="parent" ref="774585933"/> </object> <object class="IBObjectRecord"> - <int key="objectID">136</int> - <reference key="object" ref="914503793"/> - <reference key="parent" ref="774585933"/> - </object> - <object class="IBObjectRecord"> <int key="objectID">137</int> <reference key="object" ref="1001701893"/> <reference key="parent" ref="774585933"/> @@ -460,6 +559,41 @@ <reference key="object" ref="19985792"/> <reference key="parent" ref="774585933"/> </object> + <object class="IBObjectRecord"> + <int key="objectID">141</int> + <reference key="object" ref="988159807"/> + <reference key="parent" ref="634862110"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">142</int> + <reference key="object" ref="261050959"/> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">148</int> + <reference key="object" ref="734153049"/> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">149</int> + <reference key="object" ref="590654550"/> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">153</int> + <reference key="object" ref="863471211"/> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">154</int> + <reference key="object" ref="933872207"/> + <reference key="parent" ref="774585933"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">155</int> + <reference key="object" ref="544488581"/> + <reference key="parent" ref="774585933"/> + </object> </array> </object> <dictionary class="NSMutableDictionary" key="flattenedProperties"> @@ -471,31 +605,43 @@ <boolean value="NO" key="104.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/> <string key="107.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string key="123.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> - <string key="124.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string key="126.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string key="128.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <array key="128.IBViewMetadataConstraints"> + <reference ref="988159807"/> + </array> <boolean value="NO" key="128.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/> <string key="133.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> - <string key="136.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string key="137.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string key="139.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="141.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="142.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <boolean value="NO" key="142.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/> + <string key="148.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="149.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="153.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="154.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> + <string key="155.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string key="57.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <array class="NSMutableArray" key="57.IBViewMetadataConstraints"> <reference ref="234302232"/> </array> <boolean value="NO" key="57.IBViewMetadataTranslatesAutoresizingMaskIntoConstraints"/> <string key="6.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> - <array key="6.IBViewMetadataConstraints"> + <array class="NSMutableArray" key="6.IBViewMetadataConstraints"> <reference ref="46028745"/> <reference ref="663764352"/> <reference ref="1039342825"/> <reference ref="858545289"/> - <reference ref="914503793"/> <reference ref="1001701893"/> <reference ref="19985792"/> + <reference ref="544488581"/> <reference ref="860801955"/> - <reference ref="555801739"/> <reference ref="117610664"/> + <reference ref="734153049"/> + <reference ref="590654550"/> + <reference ref="863471211"/> + <reference ref="933872207"/> </array> <string key="62.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> <string key="63.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string> @@ -505,11 +651,43 @@ <nil key="activeLocalization"/> <dictionary class="NSMutableDictionary" key="localizations"/> <nil key="sourceID"/> - <int key="maxID">139</int> + <int key="maxID">155</int> </object> <object class="IBClassDescriber" key="IBDocument.Classes"> <array class="NSMutableArray" key="referencedPartialClassDescriptions"> <object class="IBPartialClassDescription"> + <string key="className">APPRTCViewController</string> + <string key="superclassName">UIViewController</string> + <dictionary class="NSMutableDictionary" key="outlets"> + <string key="blackView">UIView</string> + <string key="roomInput">UITextField</string> + <string key="instructionsView">UITextView</string> + <string key="logView">UITextView</string> + </dictionary> + <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName"> + <object class="IBToOneOutletInfo" key="blackView"> + <string key="name">blackView</string> + <string key="candidateClassName">UIView</string> + </object> + <object class="IBToOneOutletInfo" key="roomInput"> + <string key="name">roomInput</string> + <string key="candidateClassName">UITextField</string> + </object> + <object class="IBToOneOutletInfo" key="instructionsView"> + <string key="name">instructionsView</string> + <string key="candidateClassName">UITextView</string> + </object> + <object class="IBToOneOutletInfo" key="logView"> + <string key="name">logView</string> + <string key="candidateClassName">UITextView</string> + </object> + </dictionary> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">./Classes/APPRTCViewController.h</string> + </object> + </object> + <object class="IBPartialClassDescription"> <string key="className">NSLayoutConstraint</string> <string key="superclassName">NSObject</string> <object class="IBClassDescriptionSource" key="sourceIdentifier"> @@ -521,9 +699,18 @@ </object> <int key="IBDocument.localizationMode">0</int> <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string> + <bool key="IBDocument.previouslyAttemptedUpgradeToXcode5">YES</bool> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string> + <real value="1536" key="NS.object.0"/> + </object> + <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string> + <integer value="4600" key="NS.object.0"/> + </object> <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> <int key="IBDocument.defaultPropertyAccessControl">3</int> <bool key="IBDocument.UseAutolayout">YES</bool> - <string key="IBCocoaTouchPluginVersion">2083</string> + <string key="IBCocoaTouchPluginVersion">3747</string> </data> </archive> diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/main.m b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/main.m index bf35f4cbfe9..e9a1f63efb6 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/main.m +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/ios/main.m @@ -29,7 +29,7 @@ #import "APPRTCAppDelegate.h" -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { @autoreleasepool { return UIApplicationMain( argc, argv, nil, NSStringFromClass([APPRTCAppDelegate class])); diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.h b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.h new file mode 100644 index 00000000000..77011f194a0 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.h @@ -0,0 +1,31 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + +@interface APPRTCAppDelegate : NSObject<NSApplicationDelegate> +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.m b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.m new file mode 100644 index 00000000000..d6bd4fc039e --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.m @@ -0,0 +1,77 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "APPRTCAppDelegate.h" + +#import "APPRTCViewController.h" +#import "RTCPeerConnectionFactory.h" + +@interface APPRTCAppDelegate () <NSWindowDelegate> +@end + +@implementation APPRTCAppDelegate { + APPRTCViewController* _viewController; + NSWindow* _window; +} + +#pragma mark - NSApplicationDelegate + +- (void)applicationDidFinishLaunching:(NSNotification*)notification { + [RTCPeerConnectionFactory initializeSSL]; + NSScreen* screen = [NSScreen mainScreen]; + NSRect visibleRect = [screen visibleFrame]; + NSRect windowRect = NSMakeRect(NSMidX(visibleRect), + NSMidY(visibleRect), + 1320, + 1140); + NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask; + _window = [[NSWindow alloc] initWithContentRect:windowRect + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + _window.delegate = self; + [_window makeKeyAndOrderFront:self]; + [_window makeMainWindow]; + _viewController = [[APPRTCViewController alloc] initWithNibName:nil + bundle:nil]; + [_window setContentView:[_viewController view]]; +} + +#pragma mark - NSWindow + +- (void)windowWillClose:(NSNotification*)notification { + [_viewController windowWillClose:notification]; + [RTCPeerConnectionFactory deinitializeSSL]; + [NSApp terminate:self]; +} + +@end + diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.h b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.h new file mode 100644 index 00000000000..3ef058c6cf9 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.h @@ -0,0 +1,34 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + +@interface APPRTCViewController : NSViewController + +- (void)windowWillClose:(NSNotification*)notification; + +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m new file mode 100644 index 00000000000..cf5b836012a --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m @@ -0,0 +1,312 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "APPRTCViewController.h" + +#import <AVFoundation/AVFoundation.h> +#import "APPRTCConnectionManager.h" +#import "RTCNSGLVideoView.h" + +static NSUInteger const kContentWidth = 1280; +static NSUInteger const kContentHeight = 720; +static NSUInteger const kRoomFieldWidth = 80; +static NSUInteger const kLogViewHeight = 280; + +@class APPRTCMainView; +@protocol APPRTCMainViewDelegate + +- (void)appRTCMainView:(APPRTCMainView*)mainView + didEnterRoomId:(NSString*)roomId; + +@end + +@interface APPRTCMainView : NSView + +@property(nonatomic, weak) id<APPRTCMainViewDelegate> delegate; +@property(nonatomic, readonly) RTCNSGLVideoView* localVideoView; +@property(nonatomic, readonly) RTCNSGLVideoView* remoteVideoView; + +- (void)displayLogMessage:(NSString*)message; + +@end + +@interface APPRTCMainView () <NSTextFieldDelegate, RTCNSGLVideoViewDelegate> +@end +@implementation APPRTCMainView { + NSScrollView* _scrollView; + NSTextField* _roomLabel; + NSTextField* _roomField; + NSTextView* _logView; + RTCNSGLVideoView* _localVideoView; + RTCNSGLVideoView* _remoteVideoView; + CGSize _localVideoSize; + CGSize _remoteVideoSize; +} + ++ (BOOL)requiresConstraintBasedLayout { + return YES; +} + +- (instancetype)initWithFrame:(NSRect)frame { + if (self = [super initWithFrame:frame]) { + [self setupViews]; + } + return self; +} + +- (void)updateConstraints { + NSParameterAssert( + _roomField != nil && _scrollView != nil && _remoteVideoView != nil); + [self removeConstraints:[self constraints]]; + NSDictionary* viewsDictionary = + NSDictionaryOfVariableBindings(_roomLabel, + _roomField, + _scrollView, + _remoteVideoView); + + NSSize remoteViewSize = [self remoteVideoViewSize]; + NSDictionary* metrics = @{ + @"kLogViewHeight" : @(kLogViewHeight), + @"kRoomFieldWidth" : @(kRoomFieldWidth), + @"remoteViewWidth" : @(remoteViewSize.width), + @"remoteViewHeight" : @(remoteViewSize.height), + }; + // Declare this separately to avoid compiler warning about splitting string + // within an NSArray expression. + NSString* verticalConstraint = + @"V:|-[_roomLabel]-[_roomField]-[_scrollView(kLogViewHeight)]" + "-[_remoteVideoView(remoteViewHeight)]-|"; + NSArray* constraintFormats = @[ + verticalConstraint, + @"|-[_roomLabel]", + @"|-[_roomField(kRoomFieldWidth)]", + @"|-[_scrollView(remoteViewWidth)]-|", + @"|-[_remoteVideoView(remoteViewWidth)]-|", + ]; + for (NSString* constraintFormat in constraintFormats) { + NSArray* constraints = + [NSLayoutConstraint constraintsWithVisualFormat:constraintFormat + options:0 + metrics:metrics + views:viewsDictionary]; + for (NSLayoutConstraint* constraint in constraints) { + [self addConstraint:constraint]; + } + } + [super updateConstraints]; +} + +- (void)displayLogMessage:(NSString*)message { + _logView.string = + [NSString stringWithFormat:@"%@%@\n", _logView.string, message]; + NSRange range = NSMakeRange([_logView.string length], 0); + [_logView scrollRangeToVisible:range]; +} + +#pragma mark - NSControl delegate + +- (void)controlTextDidEndEditing:(NSNotification*)notification { + NSDictionary* userInfo = [notification userInfo]; + NSInteger textMovement = [userInfo[@"NSTextMovement"] intValue]; + if (textMovement == NSReturnTextMovement) { + [self.delegate appRTCMainView:self didEnterRoomId:_roomField.stringValue]; + } +} + +#pragma mark - RTCNSGLVideoViewDelegate + +- (void)videoView:(RTCNSGLVideoView*)videoView + didChangeVideoSize:(NSSize)size { + if (videoView == _remoteVideoView) { + _remoteVideoSize = size; + } else if (videoView == _localVideoView) { + _localVideoSize = size; + } else { + return; + } + [self setNeedsUpdateConstraints:YES]; +} + +#pragma mark - Private + +- (void)setupViews { + NSParameterAssert([[self subviews] count] == 0); + + _roomLabel = [[NSTextField alloc] initWithFrame:NSZeroRect]; + [_roomLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; + [_roomLabel setBezeled:NO]; + [_roomLabel setDrawsBackground:NO]; + [_roomLabel setEditable:NO]; + [_roomLabel setStringValue:@"Enter AppRTC room id:"]; + [self addSubview:_roomLabel]; + + _roomField = [[NSTextField alloc] initWithFrame:NSZeroRect]; + [_roomField setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self addSubview:_roomField]; + [_roomField setEditable:YES]; + [_roomField setDelegate:self]; + + _logView = [[NSTextView alloc] initWithFrame:NSZeroRect]; + [_logView setMinSize:NSMakeSize(0, kLogViewHeight)]; + [_logView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; + [_logView setVerticallyResizable:YES]; + [_logView setAutoresizingMask:NSViewWidthSizable]; + NSTextContainer* textContainer = [_logView textContainer]; + NSSize containerSize = NSMakeSize(kContentWidth, FLT_MAX); + [textContainer setContainerSize:containerSize]; + [textContainer setWidthTracksTextView:YES]; + [_logView setEditable:NO]; + + _scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect]; + [_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO]; + [_scrollView setHasVerticalScroller:YES]; + [_scrollView setDocumentView:_logView]; + [self addSubview:_scrollView]; + + NSOpenGLPixelFormatAttribute attributes[] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAOpenGLProfile, + NSOpenGLProfileVersion3_2Core, + 0 + }; + NSOpenGLPixelFormat* pixelFormat = + [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + _remoteVideoView = [[RTCNSGLVideoView alloc] initWithFrame:NSZeroRect + pixelFormat:pixelFormat]; + [_remoteVideoView setTranslatesAutoresizingMaskIntoConstraints:NO]; + _remoteVideoView.delegate = self; + [self addSubview:_remoteVideoView]; + + // TODO(tkchin): create local video view. + // https://code.google.com/p/webrtc/issues/detail?id=3417. +} + +- (NSSize)remoteVideoViewSize { + if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) { + return _remoteVideoSize; + } else { + return NSMakeSize(kContentWidth, kContentHeight); + } +} + +- (NSSize)localVideoViewSize { + return NSZeroSize; +} + +@end + +@interface APPRTCViewController () + <APPRTCConnectionManagerDelegate, APPRTCMainViewDelegate, APPRTCLogger> +@property(nonatomic, readonly) APPRTCMainView* mainView; +@end + +@implementation APPRTCViewController { + APPRTCConnectionManager* _connectionManager; +} + +- (instancetype)initWithNibName:(NSString*)nibName + bundle:(NSBundle*)bundle { + if (self = [super initWithNibName:nibName bundle:bundle]) { + _connectionManager = + [[APPRTCConnectionManager alloc] initWithDelegate:self + logger:self]; + } + return self; +} + +- (void)dealloc { + [self disconnect]; +} + +- (void)loadView { + APPRTCMainView* view = [[APPRTCMainView alloc] initWithFrame:NSZeroRect]; + [view setTranslatesAutoresizingMaskIntoConstraints:NO]; + view.delegate = self; + self.view = view; +} + +- (void)windowWillClose:(NSNotification*)notification { + [self disconnect]; +} + +#pragma mark - APPRTCConnectionManagerDelegate + +- (void)connectionManager:(APPRTCConnectionManager*)manager + didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack { + self.mainView.localVideoView.videoTrack = localVideoTrack; +} + +- (void)connectionManager:(APPRTCConnectionManager*)manager + didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack { + self.mainView.remoteVideoView.videoTrack = remoteVideoTrack; +} + +- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager { + [self showAlertWithMessage:@"Remote closed connection"]; + [self disconnect]; +} + +- (void)connectionManager:(APPRTCConnectionManager*)manager + didErrorWithMessage:(NSString*)message { + [self showAlertWithMessage:message]; + [self disconnect]; +} + +#pragma mark - APPRTCLogger + +- (void)logMessage:(NSString*)message { + [self.mainView displayLogMessage:message]; +} + +#pragma mark - APPRTCMainViewDelegate + +- (void)appRTCMainView:(APPRTCMainView*)mainView + didEnterRoomId:(NSString*)roomId { + NSString* urlString = + [NSString stringWithFormat:@"https://apprtc.appspot.com/?r=%@", roomId]; + [_connectionManager connectToRoomWithURL:[NSURL URLWithString:urlString]]; +} + +#pragma mark - Private + +- (APPRTCMainView*)mainView { + return (APPRTCMainView*)self.view; +} + +- (void)showAlertWithMessage:(NSString*)message { + NSAlert* alert = [[NSAlert alloc] init]; + [alert setMessageText:message]; + [alert runModal]; +} + +- (void)disconnect { + self.mainView.remoteVideoView.videoTrack = nil; + [_connectionManager disconnect]; +} + +@end diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/Info.plist b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/Info.plist new file mode 100644 index 00000000000..4dcb2403812 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/Info.plist @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!DOCTYPE plist PUBLIC "-//Apple/DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>com.Google.${PRODUCT_NAME:rfc1034identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>LSMinimumSystemVersion</key> + <string>${MACOSX_DEPLOYMENT_TARGET}</string> + <key>NSPrincipalClass</key> + <string>NSApplication</string> +</dict> +</plist>
\ No newline at end of file diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/main.m b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/main.m new file mode 100644 index 00000000000..9ce3de27a98 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/AppRTCDemo/mac/main.m @@ -0,0 +1,39 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + +#import "APPRTCAppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + [NSApplication sharedApplication]; + APPRTCAppDelegate* delegate = [[APPRTCAppDelegate alloc] init]; + [NSApp setDelegate:delegate]; + [NSApp run]; + } +} diff --git a/chromium/third_party/libjingle/source/talk/examples/ios/Icon.png b/chromium/third_party/libjingle/source/talk/examples/objc/Icon.png Binary files differindex 55773ca9d99..55773ca9d99 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/Icon.png +++ b/chromium/third_party/libjingle/source/talk/examples/objc/Icon.png diff --git a/chromium/third_party/libjingle/source/talk/examples/objc/README b/chromium/third_party/libjingle/source/talk/examples/objc/README new file mode 100644 index 00000000000..bfe18b37c5c --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/examples/objc/README @@ -0,0 +1,3 @@ +This directory contains sample iOS and mac clients for http://apprtc.appspot.com + +See ../../app/webrtc/objc/README for information on how to use it. diff --git a/chromium/third_party/libjingle/source/talk/examples/pcp/pcp_main.cc b/chromium/third_party/libjingle/source/talk/examples/pcp/pcp_main.cc deleted file mode 100644 index 1b8974dea03..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/pcp/pcp_main.cc +++ /dev/null @@ -1,715 +0,0 @@ -#define _CRT_SECURE_NO_DEPRECATE 1 - -#include <time.h> - -#if defined(POSIX) -#include <unistd.h> -#endif - -#include <iomanip> -#include <iostream> -#include <string> - -#if HAVE_CONFIG_H -#include "config.h" -#endif // HAVE_CONFIG_H - -#include "talk/base/sslconfig.h" // For SSL_USE_* - -#if SSL_USE_OPENSSL -#define USE_SSL_TUNNEL -#endif - -#include "talk/base/basicdefs.h" -#include "talk/base/common.h" -#include "talk/base/helpers.h" -#include "talk/base/logging.h" -#include "talk/base/ssladapter.h" -#include "talk/base/stringutils.h" -#include "talk/base/thread.h" -#include "talk/p2p/base/sessionmanager.h" -#include "talk/p2p/client/autoportallocator.h" -#include "talk/p2p/client/sessionmanagertask.h" -#include "talk/xmpp/xmppengine.h" -#ifdef USE_SSL_TUNNEL -#include "talk/session/tunnel/securetunnelsessionclient.h" -#endif -#include "talk/session/tunnel/tunnelsessionclient.h" -#include "talk/xmpp/xmppclient.h" -#include "talk/xmpp/xmppclientsettings.h" -#include "talk/xmpp/xmpppump.h" -#include "talk/xmpp/xmppsocket.h" - -#ifndef MAX_PATH -#define MAX_PATH 256 -#endif - -#if defined(_MSC_VER) && (_MSC_VER < 1400) -// The following are necessary to properly link when compiling STL without -// /EHsc, otherwise known as C++ exceptions. -void __cdecl std::_Throw(const std::exception &) {} -std::_Prhand std::_Raise_handler = 0; -#endif - -enum { - MSG_LOGIN_COMPLETE = 1, - MSG_LOGIN_FAILED, - MSG_DONE, -}; - -buzz::Jid gUserJid; -talk_base::InsecureCryptStringImpl gUserPass; -std::string gXmppHost = "talk.google.com"; -int gXmppPort = 5222; -buzz::TlsOptions gXmppUseTls = buzz::TLS_REQUIRED; - -class DebugLog : public sigslot::has_slots<> { -public: - DebugLog() : - debug_input_buf_(NULL), debug_input_len_(0), debug_input_alloc_(0), - debug_output_buf_(NULL), debug_output_len_(0), debug_output_alloc_(0), - censor_password_(false) - {} - char * debug_input_buf_; - int debug_input_len_; - int debug_input_alloc_; - char * debug_output_buf_; - int debug_output_len_; - int debug_output_alloc_; - bool censor_password_; - - void Input(const char * data, int len) { - if (debug_input_len_ + len > debug_input_alloc_) { - char * old_buf = debug_input_buf_; - debug_input_alloc_ = 4096; - while (debug_input_alloc_ < debug_input_len_ + len) { - debug_input_alloc_ *= 2; - } - debug_input_buf_ = new char[debug_input_alloc_]; - memcpy(debug_input_buf_, old_buf, debug_input_len_); - delete[] old_buf; - } - memcpy(debug_input_buf_ + debug_input_len_, data, len); - debug_input_len_ += len; - DebugPrint(debug_input_buf_, &debug_input_len_, false); - } - - void Output(const char * data, int len) { - if (debug_output_len_ + len > debug_output_alloc_) { - char * old_buf = debug_output_buf_; - debug_output_alloc_ = 4096; - while (debug_output_alloc_ < debug_output_len_ + len) { - debug_output_alloc_ *= 2; - } - debug_output_buf_ = new char[debug_output_alloc_]; - memcpy(debug_output_buf_, old_buf, debug_output_len_); - delete[] old_buf; - } - memcpy(debug_output_buf_ + debug_output_len_, data, len); - debug_output_len_ += len; - DebugPrint(debug_output_buf_, &debug_output_len_, true); - } - - static bool - IsAuthTag(const char * str, size_t len) { - if (str[0] == '<' && str[1] == 'a' && - str[2] == 'u' && - str[3] == 't' && - str[4] == 'h' && - str[5] <= ' ') { - std::string tag(str, len); - - if (tag.find("mechanism") != std::string::npos) - return true; - - } - return false; - } - - void - DebugPrint(char * buf, int * plen, bool output) { - int len = *plen; - if (len > 0) { - time_t tim = time(NULL); - struct tm * now = localtime(&tim); - char *time_string = asctime(now); - if (time_string) { - size_t time_len = strlen(time_string); - if (time_len > 0) { - time_string[time_len-1] = 0; // trim off terminating \n - } - } - LOG(INFO) << (output ? "SEND >>>>>>>>>>>>>>>>>>>>>>>>>" : "RECV <<<<<<<<<<<<<<<<<<<<<<<<<") - << " : " << time_string; - - bool indent; - int start = 0, nest = 3; - for (int i = 0; i < len; i += 1) { - if (buf[i] == '>') { - if ((i > 0) && (buf[i-1] == '/')) { - indent = false; - } else if ((start + 1 < len) && (buf[start + 1] == '/')) { - indent = false; - nest -= 2; - } else { - indent = true; - } - - // Output a tag - LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i + 1 - start); - - if (indent) - nest += 2; - - // Note if it's a PLAIN auth tag - if (IsAuthTag(buf + start, i + 1 - start)) { - censor_password_ = true; - } - - // incr - start = i + 1; - } - - if (buf[i] == '<' && start < i) { - if (censor_password_) { - LOG(INFO) << std::setw(nest) << " " << "## TEXT REMOVED ##"; - censor_password_ = false; - } - else { - LOG(INFO) << std::setw(nest) << " " << std::string(buf + start, i - start); - } - start = i; - } - } - len = len - start; - memcpy(buf, buf + start, len); - *plen = len; - } - } - -}; - -static DebugLog debug_log_; - -// Prints out a usage message then exits. -void Usage() { - std::cerr << "Usage:" << std::endl; - std::cerr << " pcp [options] <my_jid> (server mode)" << std::endl; - std::cerr << " pcp [options] <my_jid> <src_file> <dst_full_jid>:<dst_file> (client sending)" << std::endl; - std::cerr << " pcp [options] <my_jid> <src_full_jid>:<src_file> <dst_file> (client rcv'ing)" << std::endl; - std::cerr << " --verbose" << std::endl; - std::cerr << " --xmpp-host=<host>" << std::endl; - std::cerr << " --xmpp-port=<port>" << std::endl; - std::cerr << " --xmpp-use-tls=(true|false)" << std::endl; - exit(1); -} - -// Prints out an error message, a usage message, then exits. -void Error(const std::string& msg) { - std::cerr << "error: " << msg << std::endl; - std::cerr << std::endl; - Usage(); -} - -void FatalError(const std::string& msg) { - std::cerr << "error: " << msg << std::endl; - std::cerr << std::endl; - exit(1); -} - -// Determines whether the given string is an option. If so, the name and -// value are appended to the given strings. -bool ParseArg(const char* arg, std::string* name, std::string* value) { - if (strncmp(arg, "--", 2) != 0) - return false; - - const char* eq = strchr(arg + 2, '='); - if (eq) { - if (name) - name->append(arg + 2, eq); - if (value) - value->append(eq + 1, arg + strlen(arg)); - } else { - if (name) - name->append(arg + 2, arg + strlen(arg)); - if (value) - value->clear(); - } - - return true; -} - -int ParseIntArg(const std::string& name, const std::string& value) { - char* end; - long val = strtol(value.c_str(), &end, 10); - if (*end != '\0') - Error(std::string("value of option ") + name + " must be an integer"); - return static_cast<int>(val); -} - -#ifdef WIN32 -#pragma warning(push) -// disable "unreachable code" warning b/c it varies between dbg and opt -#pragma warning(disable: 4702) -#endif -bool ParseBoolArg(const std::string& name, const std::string& value) { - if (value == "true") - return true; - else if (value == "false") - return false; - else { - Error(std::string("value of option ") + name + " must be true or false"); - return false; - } -} -#ifdef WIN32 -#pragma warning(pop) -#endif - -void ParseFileArg(const char* arg, buzz::Jid* jid, std::string* file) { - const char* sep = strchr(arg, ':'); - if (!sep) { - *file = arg; - } else { - buzz::Jid jid_arg(std::string(arg, sep-arg)); - if (jid_arg.IsBare()) - Error("A full JID is required for the source or destination arguments."); - *jid = jid_arg; - *file = std::string(sep+1); - } -} - - -void SetConsoleEcho(bool on) { -#ifdef WIN32 - HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); - if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL)) - return; - - DWORD mode; - if (!GetConsoleMode(hIn, &mode)) - return; - - if (on) { - mode = mode | ENABLE_ECHO_INPUT; - } else { - mode = mode & ~ENABLE_ECHO_INPUT; - } - - SetConsoleMode(hIn, mode); -#else - int re; - if (on) - re = system("stty echo"); - else - re = system("stty -echo"); - if (-1 == re) - return; -#endif -} - -// Fills in a settings object with the values from the arguments. -buzz::XmppClientSettings LoginSettings() { - buzz::XmppClientSettings xcs; - xcs.set_user(gUserJid.node()); - xcs.set_host(gUserJid.domain()); - xcs.set_resource("pcp"); - xcs.set_pass(talk_base::CryptString(gUserPass)); - talk_base::SocketAddress server(gXmppHost, gXmppPort); - xcs.set_server(server); - xcs.set_use_tls(gXmppUseTls); - return xcs; -} - -// Runs the current thread until a message with the given ID is seen. -uint32 Loop(const std::vector<uint32>& ids) { - talk_base::Message msg; - while (talk_base::Thread::Current()->Get(&msg)) { - if (msg.phandler == NULL) { - if (std::find(ids.begin(), ids.end(), msg.message_id) != ids.end()) - return msg.message_id; - std::cout << "orphaned message: " << msg.message_id; - continue; - } - talk_base::Thread::Current()->Dispatch(&msg); - } - return 0; -} - -#ifdef WIN32 -#pragma warning(disable:4355) -#endif - -class CustomXmppPump : public buzz::XmppPumpNotify, public buzz::XmppPump { -public: - CustomXmppPump() : XmppPump(this), server_(false) { } - - void Serve(cricket::TunnelSessionClient* client) { - client->SignalIncomingTunnel.connect(this, - &CustomXmppPump::OnIncomingTunnel); - server_ = true; - } - - void OnStateChange(buzz::XmppEngine::State state) { - switch (state) { - case buzz::XmppEngine::STATE_START: - std::cout << "connecting..." << std::endl; - break; - case buzz::XmppEngine::STATE_OPENING: - std::cout << "logging in..." << std::endl; - break; - case buzz::XmppEngine::STATE_OPEN: - std::cout << "logged in..." << std::endl; - talk_base::Thread::Current()->Post(NULL, MSG_LOGIN_COMPLETE); - break; - case buzz::XmppEngine::STATE_CLOSED: - std::cout << "logged out..." << std::endl; - talk_base::Thread::Current()->Post(NULL, MSG_LOGIN_FAILED); - break; - } - } - - void OnIncomingTunnel(cricket::TunnelSessionClient* client, buzz::Jid jid, - std::string description, cricket::Session* session) { - std::cout << "IncomingTunnel from " << jid.Str() - << ": " << description << std::endl; - if (!server_ || file_) { - client->DeclineTunnel(session); - return; - } - std::string filename; - bool send; - if (strncmp(description.c_str(), "send:", 5) == 0) { - send = true; - } else if (strncmp(description.c_str(), "recv:", 5) == 0) { - send = false; - } else { - client->DeclineTunnel(session); - return; - } - filename = description.substr(5); - talk_base::StreamInterface* stream = client->AcceptTunnel(session); - if (!ProcessStream(stream, filename, send)) - talk_base::Thread::Current()->Post(NULL, MSG_DONE); - - // TODO: There is a potential memory leak, however, since the PCP - // app doesn't work right now, I can't verify the fix actually works, so - // comment out the following line until we fix the PCP app. - - // delete stream; - } - - bool ProcessStream(talk_base::StreamInterface* stream, - const std::string& filename, bool send) { - ASSERT(file_); - sending_ = send; - file_.reset(new talk_base::FileStream); - buffer_len_ = 0; - int err; - if (!file_->Open(filename.c_str(), sending_ ? "rb" : "wb", &err)) { - std::cerr << "Error opening <" << filename << ">: " - << std::strerror(err) << std::endl; - return false; - } - stream->SignalEvent.connect(this, &CustomXmppPump::OnStreamEvent); - if (stream->GetState() == talk_base::SS_CLOSED) { - std::cerr << "Failed to establish P2P tunnel" << std::endl; - return false; - } - if (stream->GetState() == talk_base::SS_OPEN) { - OnStreamEvent(stream, - talk_base::SE_OPEN | talk_base::SE_READ | talk_base::SE_WRITE, 0); - } - return true; - } - - void OnStreamEvent(talk_base::StreamInterface* stream, int events, - int error) { - if (events & talk_base::SE_CLOSE) { - if (error == 0) { - std::cout << "Tunnel closed normally" << std::endl; - } else { - std::cout << "Tunnel closed with error: " << error << std::endl; - } - Cleanup(stream); - return; - } - if (events & talk_base::SE_OPEN) { - std::cout << "Tunnel connected" << std::endl; - } - talk_base::StreamResult result; - size_t count; - if (sending_ && (events & talk_base::SE_WRITE)) { - LOG(LS_VERBOSE) << "Tunnel SE_WRITE"; - while (true) { - size_t write_pos = 0; - while (write_pos < buffer_len_) { - result = stream->Write(buffer_ + write_pos, buffer_len_ - write_pos, - &count, &error); - if (result == talk_base::SR_SUCCESS) { - write_pos += count; - continue; - } - if (result == talk_base::SR_BLOCK) { - buffer_len_ -= write_pos; - memmove(buffer_, buffer_ + write_pos, buffer_len_); - LOG(LS_VERBOSE) << "Tunnel write block"; - return; - } - if (result == talk_base::SR_EOS) { - std::cout << "Tunnel closed unexpectedly on write" << std::endl; - } else { - std::cout << "Tunnel write error: " << error << std::endl; - } - Cleanup(stream); - return; - } - buffer_len_ = 0; - while (buffer_len_ < sizeof(buffer_)) { - result = file_->Read(buffer_ + buffer_len_, - sizeof(buffer_) - buffer_len_, - &count, &error); - if (result == talk_base::SR_SUCCESS) { - buffer_len_ += count; - continue; - } - if (result == talk_base::SR_EOS) { - if (buffer_len_ > 0) - break; - std::cout << "End of file" << std::endl; - // A hack until we have friendly shutdown - Cleanup(stream, true); - return; - } else if (result == talk_base::SR_BLOCK) { - std::cout << "File blocked unexpectedly on read" << std::endl; - } else { - std::cout << "File read error: " << error << std::endl; - } - Cleanup(stream); - return; - } - } - } - if (!sending_ && (events & talk_base::SE_READ)) { - LOG(LS_VERBOSE) << "Tunnel SE_READ"; - while (true) { - buffer_len_ = 0; - while (buffer_len_ < sizeof(buffer_)) { - result = stream->Read(buffer_ + buffer_len_, - sizeof(buffer_) - buffer_len_, - &count, &error); - if (result == talk_base::SR_SUCCESS) { - buffer_len_ += count; - continue; - } - if (result == talk_base::SR_BLOCK) { - if (buffer_len_ > 0) - break; - LOG(LS_VERBOSE) << "Tunnel read block"; - return; - } - if (result == talk_base::SR_EOS) { - std::cout << "Tunnel closed unexpectedly on read" << std::endl; - } else { - std::cout << "Tunnel read error: " << error << std::endl; - } - Cleanup(stream); - return; - } - size_t write_pos = 0; - while (write_pos < buffer_len_) { - result = file_->Write(buffer_ + write_pos, buffer_len_ - write_pos, - &count, &error); - if (result == talk_base::SR_SUCCESS) { - write_pos += count; - continue; - } - if (result == talk_base::SR_EOS) { - std::cout << "File closed unexpectedly on write" << std::endl; - } else if (result == talk_base::SR_BLOCK) { - std::cout << "File blocked unexpectedly on write" << std::endl; - } else { - std::cout << "File write error: " << error << std::endl; - } - Cleanup(stream); - return; - } - } - } - } - - void Cleanup(talk_base::StreamInterface* stream, bool delay = false) { - LOG(LS_VERBOSE) << "Closing"; - stream->Close(); - file_.reset(); - if (!server_) { - if (delay) - talk_base::Thread::Current()->PostDelayed(2000, NULL, MSG_DONE); - else - talk_base::Thread::Current()->Post(NULL, MSG_DONE); - } - } - -private: - bool server_, sending_; - talk_base::scoped_ptr<talk_base::FileStream> file_; - char buffer_[1024 * 64]; - size_t buffer_len_; -}; - -int main(int argc, char **argv) { - talk_base::LogMessage::LogThreads(); - talk_base::LogMessage::LogTimestamps(); - - // TODO: Default the username to the current users's name. - - // Parse the arguments. - - int index = 1; - while (index < argc) { - std::string name, value; - if (!ParseArg(argv[index], &name, &value)) - break; - - if (name == "help") { - Usage(); - } else if (name == "verbose") { - talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE); - } else if (name == "xmpp-host") { - gXmppHost = value; - } else if (name == "xmpp-port") { - gXmppPort = ParseIntArg(name, value); - } else if (name == "xmpp-use-tls") { - gXmppUseTls = ParseBoolArg(name, value)? - buzz::TLS_REQUIRED : buzz::TLS_DISABLED; - } else { - Error(std::string("unknown option: ") + name); - } - - index += 1; - } - - if (index >= argc) - Error("bad arguments"); - gUserJid = buzz::Jid(argv[index++]); - if (!gUserJid.IsValid()) - Error("bad arguments"); - - char path[MAX_PATH]; -#if WIN32 - GetCurrentDirectoryA(MAX_PATH, path); -#else - if (NULL == getcwd(path, MAX_PATH)) - Error("Unable to get current path"); -#endif - - std::cout << "Directory: " << std::string(path) << std::endl; - - buzz::Jid gSrcJid; - buzz::Jid gDstJid; - std::string gSrcFile; - std::string gDstFile; - - bool as_server = true; - if (index + 2 == argc) { - ParseFileArg(argv[index], &gSrcJid, &gSrcFile); - ParseFileArg(argv[index+1], &gDstJid, &gDstFile); - if(gSrcJid.Str().empty() == gDstJid.Str().empty()) - Error("Exactly one of source JID or destination JID must be empty."); - as_server = false; - } else if (index != argc) { - Error("bad arguments"); - } - - std::cout << "Password: "; - SetConsoleEcho(false); - std::cin >> gUserPass.password(); - SetConsoleEcho(true); - std::cout << std::endl; - - talk_base::InitializeSSL(); - // Log in. - CustomXmppPump pump; - pump.client()->SignalLogInput.connect(&debug_log_, &DebugLog::Input); - pump.client()->SignalLogOutput.connect(&debug_log_, &DebugLog::Output); - pump.DoLogin(LoginSettings(), new buzz::XmppSocket(gXmppUseTls), 0); - //new XmppAuth()); - - // Wait until login succeeds. - std::vector<uint32> ids; - ids.push_back(MSG_LOGIN_COMPLETE); - ids.push_back(MSG_LOGIN_FAILED); - if (MSG_LOGIN_FAILED == Loop(ids)) - FatalError("Failed to connect"); - - { - talk_base::scoped_ptr<buzz::XmlElement> presence( - new buzz::XmlElement(buzz::QN_PRESENCE)); - presence->AddElement(new buzz::XmlElement(buzz::QN_PRIORITY)); - presence->AddText("-1", 1); - pump.SendStanza(presence.get()); - } - - std::string user_jid_str = pump.client()->jid().Str(); - std::cout << "Logged in as " << user_jid_str << std::endl; - - // Prepare the random number generator. - talk_base::InitRandom(user_jid_str.c_str(), user_jid_str.size()); - - // Create the P2P session manager. - talk_base::BasicNetworkManager network_manager; - AutoPortAllocator allocator(&network_manager, "pcp_agent"); - allocator.SetXmppClient(pump.client()); - cricket::SessionManager session_manager(&allocator); -#ifdef USE_SSL_TUNNEL - cricket::SecureTunnelSessionClient session_client(pump.client()->jid(), - &session_manager); - if (!session_client.GenerateIdentity()) - FatalError("Failed to generate SSL identity"); -#else // !USE_SSL_TUNNEL - cricket::TunnelSessionClient session_client(pump.client()->jid(), - &session_manager); -#endif // USE_SSL_TUNNEL - cricket::SessionManagerTask *receiver = - new cricket::SessionManagerTask(pump.client(), &session_manager); - receiver->EnableOutgoingMessages(); - receiver->Start(); - - bool success = true; - - // Establish the appropriate connection. - if (as_server) { - pump.Serve(&session_client); - } else { - talk_base::StreamInterface* stream = NULL; - std::string filename; - bool sending; - if (gSrcJid.Str().empty()) { - std::string message("recv:"); - message.append(gDstFile); - stream = session_client.CreateTunnel(gDstJid, message); - filename = gSrcFile; - sending = true; - } else { - std::string message("send:"); - message.append(gSrcFile); - stream = session_client.CreateTunnel(gSrcJid, message); - filename = gDstFile; - sending = false; - } - success = pump.ProcessStream(stream, filename, sending); - } - - if (success) { - // Wait until the copy is done. - ids.clear(); - ids.push_back(MSG_DONE); - ids.push_back(MSG_LOGIN_FAILED); - Loop(ids); - } - - // Log out. - pump.DoDisconnect(); - - return 0; -} diff --git a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/conductor.cc b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/conductor.cc index b35a054828b..bbab3d06e35 100644 --- a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/conductor.cc +++ b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/conductor.cc @@ -105,6 +105,7 @@ bool Conductor::InitializePeerConnection() { peer_connection_ = peer_connection_factory_->CreatePeerConnection(servers, NULL, NULL, + NULL, this); if (!peer_connection_.get()) { main_wnd_->MessageBox("Error", diff --git a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/linux/main.cc b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/linux/main.cc index aee1bb100d6..4ef81cdac72 100644 --- a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/linux/main.cc +++ b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/linux/main.cc @@ -32,6 +32,7 @@ #include "talk/examples/peerconnection/client/linux/main_wnd.h" #include "talk/examples/peerconnection/client/peer_connection_client.h" +#include "talk/base/ssladapter.h" #include "talk/base/thread.h" class CustomSocketServer : public talk_base::PhysicalSocketServer { @@ -94,6 +95,7 @@ int main(int argc, char* argv[]) { CustomSocketServer socket_server(thread, &wnd); thread->set_socketserver(&socket_server); + talk_base::InitializeSSL(); // Must be constructed after we set the socketserver. PeerConnectionClient client; talk_base::scoped_refptr<Conductor> conductor( @@ -111,7 +113,7 @@ int main(int argc, char* argv[]) { //while (gtk_events_pending()) { // gtk_main_iteration(); //} - + talk_base::CleanupSSL(); return 0; } diff --git a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/main.cc b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/main.cc index bd0a5c3c17c..e68c78ef870 100644 --- a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/main.cc +++ b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/main.cc @@ -28,6 +28,7 @@ #include "talk/examples/peerconnection/client/conductor.h" #include "talk/examples/peerconnection/client/main_wnd.h" #include "talk/examples/peerconnection/client/peer_connection_client.h" +#include "talk/base/ssladapter.h" #include "talk/base/win32socketinit.h" #include "talk/base/win32socketserver.h" @@ -44,6 +45,7 @@ int PASCAL wWinMain(HINSTANCE instance, HINSTANCE prev_instance, return -1; } + talk_base::InitializeSSL(); PeerConnectionClient client; talk_base::scoped_refptr<Conductor> conductor( new talk_base::RefCountedObject<Conductor>(&client, &wnd)); @@ -68,5 +70,6 @@ int PASCAL wWinMain(HINSTANCE instance, HINSTANCE prev_instance, } } + talk_base::CleanupSSL(); return 0; } diff --git a/chromium/third_party/libjingle/source/talk/examples/peerconnection/peerconnection.scons b/chromium/third_party/libjingle/source/talk/examples/peerconnection/peerconnection.scons deleted file mode 100644 index 7a7d2f3be95..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/peerconnection/peerconnection.scons +++ /dev/null @@ -1,64 +0,0 @@ -# -*- Python -*- -import talk - -Import('env') - -if env.Bit('have_webrtc_voice') and env.Bit('have_webrtc_video'): - talk.App( - env, - name = 'peerconnection_client', - # TODO: Build peerconnection_client on mac. - libs = [ - 'base', - 'expat', - 'json', - 'p2p', - 'peerconnection', - 'phone', - 'srtp', - 'xmllite', - 'xmpp', - 'yuvscaler', - ], - win_srcs = [ - 'client/conductor.cc', - 'client/defaults.cc', - 'client/main.cc', - 'client/main_wnd.cc', - 'client/peer_connection_client.cc', - ], - posix_libs = [ - 'crypto', - 'securetunnel', - 'ssl', - ], - lin_srcs = [ - 'client/conductor.cc', - 'client/defaults.cc', - 'client/peer_connection_client.cc', - 'client/linux/main.cc', - 'client/linux/main_wnd.cc', - ], - lin_packages = [ - 'glib-2.0', - 'gobject-2.0', - 'gtk+-2.0', - ], - lin_libs = [ - 'sound', - ], - win_link_flags = [ - ('', '/nodefaultlib:libcmt')[env.Bit('debug')], - ], - ) - - talk.App( - env, - name = 'peerconnection_server', - srcs = [ - 'server/data_socket.cc', - 'server/main.cc', - 'server/peer_channel.cc', - 'server/utils.cc', - ], - ) diff --git a/chromium/third_party/libjingle/source/talk/examples/plus/libjingleplus.cc b/chromium/third_party/libjingle/source/talk/examples/plus/libjingleplus.cc deleted file mode 100644 index 4d27dfd120b..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/plus/libjingleplus.cc +++ /dev/null @@ -1,736 +0,0 @@ -/* - * libjingle - * Copyright 2006, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <iostream> -#include "libjingleplus.h" -#ifdef WIN32 -#include "talk/base/win32socketserver.h" -#endif -#include "talk/base/physicalsocketserver.h" -#include "talk/base/logging.h" -#include "talk/examples/login/xmppauth.h" -#include "talk/examples/login/xmppsocket.h" -#include "talk/examples/login/xmpppump.h" -#include "presencepushtask.h" -#include "talk/app/status.h" -#include "talk/app/message.h" -#include "rostertask.h" -#include "talk/app/iqtask.h" -#include "talk/app/presenceouttask.h" -#include "talk/app/receivemessagetask.h" -#include "talk/app/rostersettask.h" -#include "talk/app/sendmessagetask.h" - -enum { - MSG_START, - - // main thread to worker - MSG_LOGIN, - MSG_DISCONNECT, - MSG_SEND_PRESENCE, - MSG_SEND_DIRECTED_PRESENCE, - MSG_SEND_DIRECTED_MUC_PRESENCE, - MSG_SEND_XMPP_MESSAGE, - MSG_SEND_XMPP_IQ, - MSG_UPDATE_ROSTER_ITEM, - MSG_REMOVE_ROSTER_ITEM, - - // worker thread to main thread - MSG_STATE_CHANGE, - MSG_STATUS_UPDATE, - MSG_STATUS_ERROR, - MSG_ROSTER_REFRESH_STARTED, - MSG_ROSTER_REFRESH_FINISHED, - MSG_ROSTER_ITEM_UPDATED, - MSG_ROSTER_ITEM_REMOVED, - MSG_ROSTER_SUBSCRIBE, - MSG_ROSTER_UNSUBSCRIBE, - MSG_ROSTER_SUBSCRIBED, - MSG_ROSTER_UNSUBSCRIBED, - MSG_INCOMING_MESSAGE, - MSG_IQ_COMPLETE, - MSG_XMPP_INPUT, - MSG_XMPP_OUTPUT -}; - -class LibjinglePlusWorker : public talk_base::MessageHandler, - public XmppPumpNotify, - public sigslot::has_slots<> { - public: - LibjinglePlusWorker(LibjinglePlus *ljp, LibjinglePlusNotify *notify) : - worker_thread_(NULL), ljp_(ljp), notify_(notify), - ppt_(NULL), rmt_(NULL), rt_(NULL), is_test_login_(false) { - - main_thread_.reset(new talk_base::AutoThread()); -#ifdef WIN32 - ss_.reset(new talk_base::Win32SocketServer(main_thread_.get())); - main_thread_->set_socketserver(ss_.get()); -#endif - - pump_.reset(new XmppPump(this)); - - pump_->client()->SignalLogInput.connect(this, &LibjinglePlusWorker::OnInputDebug); - pump_->client()->SignalLogOutput.connect(this, &LibjinglePlusWorker::OnOutputDebug); - //pump_->client()->SignalStateChange.connect(this, &LibjinglePlusWorker::OnStateChange); - } - - ~LibjinglePlusWorker() { - if (worker_thread_) { - worker_thread_->Send(this, MSG_DISCONNECT); - delete worker_thread_; - } - } - - virtual void OnMessage(talk_base::Message *msg) { - switch (msg->message_id) { - case MSG_START: - LoginW(); - break; - case MSG_DISCONNECT: - DisconnectW(); - break; - case MSG_SEND_XMPP_MESSAGE: - SendXmppMessageW(static_cast<SendMessageData*>(msg->pdata)->m_); - delete msg->pdata; - break; - case MSG_SEND_XMPP_IQ: - SendXmppIqW(static_cast<SendIqData*>(msg->pdata)->to_jid_, - static_cast<SendIqData*>(msg->pdata)->is_get_, - static_cast<SendIqData*>(msg->pdata)->xml_element_); - delete msg->pdata; - break; - case MSG_SEND_PRESENCE: - SendPresenceW(static_cast<SendPresenceData*>(msg->pdata)->s_); - delete msg->pdata; - break; - case MSG_SEND_DIRECTED_PRESENCE: - SendDirectedPresenceW(static_cast<SendDirectedPresenceData*>(msg->pdata)->j_, - static_cast<SendDirectedPresenceData*>(msg->pdata)->s_); - delete msg->pdata; - break; - case MSG_SEND_DIRECTED_MUC_PRESENCE: - SendDirectedMUCPresenceW(static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->j_, - static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->s_, - static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->un_, - static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->ac_, - static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->am_, - static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->role_); - delete msg->pdata; - break; - case MSG_UPDATE_ROSTER_ITEM: - UpdateRosterItemW(static_cast<UpdateRosterItemData*>(msg->pdata)->jid_, - static_cast<UpdateRosterItemData*>(msg->pdata)->n_, - static_cast<UpdateRosterItemData*>(msg->pdata)->g_, - static_cast<UpdateRosterItemData*>(msg->pdata)->grt_); - delete msg->pdata; - break; - case MSG_REMOVE_ROSTER_ITEM: - RemoveRosterItemW(static_cast<JidData*>(msg->pdata)->jid_); - delete msg->pdata; - break; - - - - - case MSG_STATUS_UPDATE: - OnStatusUpdateW(static_cast<SendPresenceData*>(msg->pdata)->s_); - delete msg->pdata; - break; - case MSG_STATUS_ERROR: - OnStatusErrorW(static_cast<StatusErrorData*>(msg->pdata)->stanza_); - delete msg->pdata; - break; - case MSG_STATE_CHANGE: - OnStateChangeW(static_cast<StateChangeData*>(msg->pdata)->s_); - delete msg->pdata; - break; - case MSG_ROSTER_REFRESH_STARTED: - OnRosterRefreshStartedW(); - break; - case MSG_ROSTER_REFRESH_FINISHED: - OnRosterRefreshFinishedW(); - break; - case MSG_ROSTER_ITEM_UPDATED: - OnRosterItemUpdatedW(static_cast<RosterItemData*>(msg->pdata)->ri_); - delete msg->pdata; - break; - case MSG_ROSTER_ITEM_REMOVED: - OnRosterItemRemovedW(static_cast<RosterItemData*>(msg->pdata)->ri_); - delete msg->pdata; - break; - case MSG_ROSTER_SUBSCRIBE: - OnRosterSubscribeW(static_cast<JidData*>(msg->pdata)->jid_); - delete msg->pdata; - break; - case MSG_ROSTER_UNSUBSCRIBE: - OnRosterUnsubscribeW(static_cast<JidData*>(msg->pdata)->jid_); - delete msg->pdata; - break; - case MSG_ROSTER_SUBSCRIBED: - OnRosterSubscribedW(static_cast<JidData*>(msg->pdata)->jid_); - delete msg->pdata; - break; - case MSG_ROSTER_UNSUBSCRIBED: - OnRosterUnsubscribedW(static_cast<JidData*>(msg->pdata)->jid_); - delete msg->pdata; - break; - case MSG_INCOMING_MESSAGE: - OnIncomingMessageW(static_cast<XmppMessageData*>(msg->pdata)->m_); - delete msg->pdata; - break; - case MSG_IQ_COMPLETE: - OnIqCompleteW(static_cast<IqCompleteData*>(msg->pdata)->success_, - static_cast<IqCompleteData*>(msg->pdata)->stanza_); - delete msg->pdata; - break; - case MSG_XMPP_OUTPUT: - OnOutputDebugW(static_cast<StringData*>(msg->pdata)->s_); - delete msg->pdata; - break; - case MSG_XMPP_INPUT: - OnInputDebugW(static_cast<StringData*>(msg->pdata)->s_); - delete msg->pdata; - break; - } - } - - void Login(const std::string &jid, const std::string &password, - const std::string &machine_address, bool is_test, bool cookie_auth) { - is_test_login_ = is_test; - - xcs_.set_user(jid); - if (cookie_auth) { - xcs_.set_auth_cookie(password); - } else { - talk_base::InsecureCryptStringImpl pass; - pass.password() = password; - xcs_.set_pass(talk_base::CryptString(pass)); - } - xcs_.set_host(is_test ? "google.com" : "gmail.com"); - xcs_.set_resource("libjingleplus"); - xcs_.set_server(talk_base::SocketAddress(machine_address, 5222)); - xcs_.set_use_tls(!is_test); - if (is_test) { - xcs_.set_allow_plain(true); - } - - worker_thread_ = new talk_base::Thread(&pss_); - worker_thread_->Start(); - worker_thread_->Send(this, MSG_START); - } - - void SendXmppMessage(const buzz::XmppMessage &m) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - worker_thread_->Post(this, MSG_SEND_XMPP_MESSAGE, new SendMessageData(m)); - } - - void SendXmppIq(const buzz::Jid &to_jid, bool is_get, - const buzz::XmlElement *xml_element) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - worker_thread_->Post(this, MSG_SEND_XMPP_IQ, - new SendIqData(to_jid, is_get, xml_element)); - } - - void SendPresence(const buzz::Status & s) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - worker_thread_->Post(this, MSG_SEND_PRESENCE, new SendPresenceData(s)); - } - - void SendDirectedPresence (const buzz::Jid &j, const buzz::Status &s) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - worker_thread_->Post(this, MSG_SEND_DIRECTED_PRESENCE, new SendDirectedPresenceData(j,s)); - } - - void SendDirectedMUCPresence(const buzz::Jid &j, const buzz::Status &s, - const std::string &un, const std::string &ac, - const std::string &am, const std::string &role) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - worker_thread_->Post(this, MSG_SEND_DIRECTED_MUC_PRESENCE, new SendDirectedMUCPresenceData(j,s,un,ac,am, role)); - } - - void UpdateRosterItem(const buzz::Jid & jid, const std::string & name, - const std::vector<std::string> & groups, buzz::GrType grt) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - worker_thread_->Post(this, MSG_UPDATE_ROSTER_ITEM, new UpdateRosterItemData(jid,name,groups,grt)); - } - - void RemoveRosterItemW(const buzz::Jid &jid) { - buzz::RosterSetTask *rst = new buzz::RosterSetTask(pump_.get()->client()); - rst->Remove(jid); - rst->Start(); - } - - void RemoveRosterItem(const buzz::Jid &jid) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - worker_thread_->Post(this, MSG_REMOVE_ROSTER_ITEM, new JidData(jid)); - } - - void DoCallbacks() { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - talk_base::Message m; - while (main_thread_->Get(&m, 0)) { - main_thread_->Dispatch(&m); - } - } - - private: - - struct UpdateRosterItemData : public talk_base::MessageData { - UpdateRosterItemData(const buzz::Jid &jid, const std::string &name, - const std::vector<std::string> &groups, buzz::GrType grt) : - jid_(jid), n_(name), g_(groups), grt_(grt) {} - buzz::Jid jid_; - std::string n_; - std::vector<std::string> g_; - buzz::GrType grt_; - }; - - void UpdateRosterItemW(const buzz::Jid &jid, const std::string &name, - const std::vector<std::string> &groups, buzz::GrType grt) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - buzz::RosterSetTask *rst = new buzz::RosterSetTask(pump_.get()->client()); - rst->Update(jid, name, groups, grt); - rst->Start(); - } - - struct StringData : public talk_base::MessageData { - StringData(std::string s) : s_(s) {} - std::string s_; - }; - - void OnInputDebugW(const std::string &data) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnXmppInput(data); - } - - void OnInputDebug(const char *data, int len) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_XMPP_INPUT, new StringData(std::string(data,len))); - if (notify_) - notify_->WakeupMainThread(); - } - - void OnOutputDebugW(const std::string &data) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnXmppOutput(data); - } - - void OnOutputDebug(const char *data, int len) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_XMPP_OUTPUT, new StringData(std::string(data,len))); - if (notify_) - notify_->WakeupMainThread(); - } - - struct StateChangeData : public talk_base::MessageData { - StateChangeData(buzz::XmppEngine::State state) : s_(state) {} - buzz::XmppEngine::State s_; - }; - - void OnStateChange(buzz::XmppEngine::State state) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - switch (state) { - case buzz::XmppEngine::STATE_OPEN: - ppt_ = new buzz::PresencePushTask(pump_.get()->client()); - ppt_->SignalStatusUpdate.connect(this, - &LibjinglePlusWorker::OnStatusUpdate); - ppt_->SignalStatusError.connect(this, - &LibjinglePlusWorker::OnStatusError); - ppt_->Start(); - - rmt_ = new buzz::ReceiveMessageTask(pump_.get()->client(), buzz::XmppEngine::HL_ALL); - rmt_->SignalIncomingMessage.connect(this, &LibjinglePlusWorker::OnIncomingMessage); - rmt_->Start(); - - rt_ = new buzz::RosterTask(pump_.get()->client()); - rt_->SignalRosterItemUpdated.connect(this, &LibjinglePlusWorker::OnRosterItemUpdated); - rt_->SignalRosterItemRemoved.connect(this, &LibjinglePlusWorker::OnRosterItemRemoved); - rt_->SignalSubscribe.connect(this, &LibjinglePlusWorker::OnRosterSubscribe); - rt_->SignalUnsubscribe.connect(this, &LibjinglePlusWorker::OnRosterUnsubscribe); - rt_->SignalSubscribed.connect(this, &LibjinglePlusWorker::OnRosterSubscribed); - rt_->SignalUnsubscribed.connect(this, &LibjinglePlusWorker::OnRosterUnsubscribed); - rt_->SignalRosterRefreshStarted.connect(this, &LibjinglePlusWorker::OnRosterRefreshStarted); - rt_->SignalRosterRefreshFinished.connect(this, &LibjinglePlusWorker::OnRosterRefreshFinished); - rt_->Start(); - rt_->RefreshRosterNow(); - - break; - } - main_thread_->Post(this, MSG_STATE_CHANGE, new StateChangeData(state)); - if (notify_) - notify_->WakeupMainThread(); - } - - void OnStateChangeW(buzz::XmppEngine::State state) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnStateChange(state); - } - - struct RosterItemData : public talk_base::MessageData { - RosterItemData(const buzz::RosterItem &ri) : ri_(ri) {} - buzz::RosterItem ri_; - }; - - void OnRosterItemUpdatedW(const buzz::RosterItem &ri) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnRosterItemUpdated(ri); - } - - void OnRosterItemUpdated(const buzz::RosterItem &ri, bool huh) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_ROSTER_ITEM_UPDATED, new RosterItemData(ri)); - if (notify_) - notify_->WakeupMainThread(); - } - - void OnRosterItemRemovedW(const buzz::RosterItem &ri) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnRosterItemRemoved(ri); - } - - void OnRosterItemRemoved(const buzz::RosterItem &ri) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_ROSTER_ITEM_REMOVED, new RosterItemData(ri)); - if (notify_) - notify_->WakeupMainThread(); - } - - struct JidData : public talk_base::MessageData { - JidData(const buzz::Jid& jid) : jid_(jid) {} - const buzz::Jid jid_; - }; - - void OnRosterSubscribeW(const buzz::Jid& jid) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnRosterSubscribe(jid); - } - - void OnRosterSubscribe(const buzz::Jid& jid) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_ROSTER_SUBSCRIBE, new JidData(jid)); - if (notify_) - notify_->WakeupMainThread(); - } - - void OnRosterUnsubscribeW(const buzz::Jid &jid) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnRosterUnsubscribe(jid); - } - - void OnRosterUnsubscribe(const buzz::Jid &jid) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_ROSTER_UNSUBSCRIBE, new JidData(jid)); - if (notify_) - notify_->WakeupMainThread(); - } - - void OnRosterSubscribedW(const buzz::Jid &jid) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnRosterSubscribed(jid); - } - - void OnRosterSubscribed(const buzz::Jid &jid) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_ROSTER_SUBSCRIBED, new JidData(jid)); - if (notify_) - notify_->WakeupMainThread(); - } - - void OnRosterUnsubscribedW(const buzz::Jid &jid) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnRosterUnsubscribed(jid); - } - - void OnRosterUnsubscribed(const buzz::Jid &jid) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_ROSTER_UNSUBSCRIBED, new JidData(jid)); - if (notify_) - notify_->WakeupMainThread(); - } - - void OnRosterRefreshStartedW() { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnRosterRefreshStarted(); - } - - void OnRosterRefreshStarted() { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_ROSTER_REFRESH_STARTED); - if (notify_) - notify_->WakeupMainThread(); - } - - void OnRosterRefreshFinishedW() { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnRosterRefreshFinished(); - } - - void OnRosterRefreshFinished() { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_ROSTER_REFRESH_FINISHED); - if (notify_) - notify_->WakeupMainThread(); - } - - struct XmppMessageData : talk_base::MessageData { - XmppMessageData(const buzz::XmppMessage &m) : m_(m) {} - buzz::XmppMessage m_; - }; - - void OnIncomingMessageW(const buzz::XmppMessage &msg) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnMessage(msg); - } - - void OnIncomingMessage(const buzz::XmppMessage &msg) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_INCOMING_MESSAGE, new XmppMessageData(msg)); - if (notify_) - notify_->WakeupMainThread(); - } - - void OnStatusUpdateW (const buzz::Status &status) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnStatusUpdate(status); - } - - void OnStatusUpdate (const buzz::Status &status) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_STATUS_UPDATE, new SendPresenceData(status)); - if (notify_) - notify_->WakeupMainThread(); - } - - struct StatusErrorData : talk_base::MessageData { - StatusErrorData(const buzz::XmlElement &stanza) : stanza_(stanza) {} - buzz::XmlElement stanza_; - }; - - void OnStatusErrorW (const buzz::XmlElement &stanza) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnStatusError(stanza); - } - - void OnStatusError (const buzz::XmlElement &stanza) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_STATUS_ERROR, new StatusErrorData(stanza)); - if (notify_) - notify_->WakeupMainThread(); - } - - void LoginW() { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - XmppSocket* socket = new XmppSocket(true); - pump_->DoLogin(xcs_, socket, is_test_login_ ? NULL : new XmppAuth()); - socket->SignalCloseEvent.connect(this, - &LibjinglePlusWorker::OnXmppSocketClose); - } - - void DisconnectW() { - assert(talk_base::ThreadManager::CurrentThread() == worker_thread_); - pump_->DoDisconnect(); - } - - void SendXmppMessageW(const buzz::XmppMessage &m) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - buzz::SendMessageTask * smt = new buzz::SendMessageTask(pump_.get()->client()); - smt->Send(m); - smt->Start(); - } - - void SendXmppIqW(const buzz::Jid &to_jid, bool is_get, - const buzz::XmlElement *xml_element) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - buzz::IqTask *iq_task = new buzz::IqTask(pump_.get()->client(), - is_get, to_jid, const_cast<buzz::XmlElement *>(xml_element)); - iq_task->SignalDone.connect(this, &LibjinglePlusWorker::OnIqComplete); - iq_task->Start(); - } - - struct IqCompleteData : public talk_base::MessageData { - IqCompleteData(bool success, const buzz::XmlElement *stanza) : - success_(success), stanza_(*stanza) {} - bool success_; - buzz::XmlElement stanza_; - }; - - void OnIqCompleteW(bool success, const buzz::XmlElement& stanza) { - assert(talk_base::ThreadManager::CurrentThread() != worker_thread_); - if (notify_) - notify_->OnIqDone(success, stanza); - } - - void OnIqComplete(bool success, const buzz::XmlElement *stanza) { - assert(talk_base::ThreadManager::CurrentThread() == worker_thread_); - main_thread_->Post(this, MSG_IQ_COMPLETE, - new IqCompleteData(success, stanza)); - if (notify_) - notify_->WakeupMainThread(); - } - - void SendPresenceW(const buzz::Status & s) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - buzz::PresenceOutTask *pot = new buzz::PresenceOutTask(pump_.get()->client()); - pot->Send(s); - pot->Start(); - } - - - void SendDirectedMUCPresenceW(const buzz::Jid & j, const buzz::Status & s, - const std::string &user_nick, const std::string &api_capability, - const std::string &api_message, const std::string &role) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - buzz::PresenceOutTask *pot = new buzz::PresenceOutTask(pump_.get()->client()); - pot->SendDirectedMUC(j,s,user_nick,api_capability,api_message, role); - pot->Start(); - } - - void SendDirectedPresenceW(const buzz::Jid & j, const buzz::Status & s) { - assert (talk_base::ThreadManager::CurrentThread() == worker_thread_); - buzz::PresenceOutTask *pot = new buzz::PresenceOutTask(pump_.get()->client()); - pot->SendDirected(j,s); - pot->Start(); - } - - void OnXmppSocketClose(int error) { - notify_->OnSocketClose(error); - } - - struct SendMessageData : public talk_base::MessageData { - SendMessageData(const buzz::XmppMessage &m) : m_(m) {} - buzz::XmppMessage m_; - }; - - struct SendIqData : public talk_base::MessageData { - SendIqData(const buzz::Jid &jid, bool is_get, const buzz::XmlElement *m) - : to_jid_(jid), is_get_(is_get), xml_element_(m) {} - buzz::Jid to_jid_; - bool is_get_; - const buzz::XmlElement *xml_element_; - }; - - struct SendPresenceData : public talk_base::MessageData { - SendPresenceData(const buzz::Status &s) : s_(s) {} - buzz::Status s_; - }; - - struct SendDirectedPresenceData : public talk_base::MessageData { - SendDirectedPresenceData(const buzz::Jid &j, const buzz::Status &s) : j_(j), s_(s) {} - buzz::Jid j_; - buzz::Status s_; - }; - - struct SendDirectedMUCPresenceData : public talk_base::MessageData { - SendDirectedMUCPresenceData(const buzz::Jid &j, const buzz::Status &s, - const std::string &un, const std::string &ac, - const std::string &am, const std::string &role) - : j_(j), s_(s), un_(un), ac_(ac), am_(am), role_(role) {} - buzz::Jid j_; - buzz::Status s_; - std::string un_; - std::string ac_; - std::string am_; - std::string role_; - }; - - talk_base::scoped_ptr<talk_base::Win32SocketServer> ss_; - talk_base::scoped_ptr<talk_base::Thread> main_thread_; - talk_base::Thread *worker_thread_; - - LibjinglePlus *ljp_; - LibjinglePlusNotify *notify_; - buzz::XmppClientSettings xcs_; - talk_base::PhysicalSocketServer pss_; - - talk_base::scoped_ptr<XmppPump> pump_; - buzz::PresencePushTask * ppt_; - buzz::ReceiveMessageTask * rmt_; - buzz::RosterTask * rt_; - - bool is_test_login_; -}; - -LibjinglePlus::LibjinglePlus(LibjinglePlusNotify *notify) -{ - worker_ = new LibjinglePlusWorker(this, notify); -} - -LibjinglePlus::~LibjinglePlus() -{ - delete worker_; - worker_ = NULL; -} - -void LibjinglePlus::Login(const std::string &jid, - const std::string &password, - const std::string &machine_address, - bool is_test, bool cookie_auth) { - worker_->Login(jid, password, machine_address, is_test, cookie_auth); -} - -void LibjinglePlus::SendPresence(const buzz::Status & s) { - worker_->SendPresence(s); -} - -void LibjinglePlus::SendDirectedPresence(const buzz::Jid & j, const buzz::Status & s) { - worker_->SendDirectedPresence(j,s); -} - -void LibjinglePlus::SendDirectedMUCPresence(const buzz::Jid & j, - const buzz::Status & s, const std::string &user_nick, - const std::string &api_capability, const std::string &api_message, - const std::string &role) { - worker_->SendDirectedMUCPresence(j,s,user_nick,api_capability,api_message, - role); -} - -void LibjinglePlus::SendXmppMessage(const buzz::XmppMessage & m) { - worker_->SendXmppMessage(m); -} - -void LibjinglePlus::SendXmppIq(const buzz::Jid &to_jid, bool is_get, - const buzz::XmlElement *iq_element) { - worker_->SendXmppIq(to_jid, is_get, iq_element); -} - -void LibjinglePlus::DoCallbacks() { - worker_->DoCallbacks(); -} diff --git a/chromium/third_party/libjingle/source/talk/examples/plus/libjingleplus.h b/chromium/third_party/libjingle/source/talk/examples/plus/libjingleplus.h deleted file mode 100644 index a2898f51a3f..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/plus/libjingleplus.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * libjingle - * Copyright 2006, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// LibjinglePlus is a class that connects to Google Talk, creates -// some common tasks, and emits signals when things change - -#ifndef LIBJINGLEPLUS_H__ -#define LIBJINGLEPLUS_H__ - -#include "talk/base/basicdefs.h" -#include "talk/app/rosteritem.h" -#include "talk/app/message.h" -#include "talk/app/status.h" -#include "talk/xmpp/xmppengine.h" -#include "talk/base/scoped_ptr.h" - - -class LibjinglePlusWorker; - -class LibjinglePlusNotify { - public: - virtual ~LibjinglePlusNotify() {} - - /* Libjingle+ works on its own thread. It will call WakeupMainThread - * when it has something to report. The main thread should then wake up, - * and call DoCallbacks on the LibjinglePlus object. - * - * This function gets called from libjingle+'s worker thread. All other - * methods in LibjinglePlusNotify get called from the thread you call - * DoCallbacks() on. - * - * If running on Windows, libjingle+ will use Windows messages to generate - * callbacks from the main thread, and you don't need to do anything here. - */ - virtual void WakeupMainThread() = 0; - - /* Connection */ - /* Called when the connection state changes */ - virtual void OnStateChange(buzz::XmppEngine::State) = 0; - - /* Called when the socket closes */ - virtual void OnSocketClose(int error_code) = 0; - - /* Called when XMPP is being sent or received. Used for debugging */ - virtual void OnXmppOutput(const std::string &output) = 0; - virtual void OnXmppInput(const std::string &input) = 0; - - /* Presence */ - /* Called when someone's Status is updated */ - virtual void OnStatusUpdate(const buzz::Status &status) = 0; - - /* Called when a status update results in an error */ - virtual void OnStatusError(const buzz::XmlElement &stanza) = 0; - - /* Called with an IQ return code */ - virtual void OnIqDone(bool success, const buzz::XmlElement &stanza) = 0; - - /* Message */ - /* Called when a message comes in. */ - virtual void OnMessage(const buzz::XmppMessage &message) = 0; - - /* Roster */ - - /* Called when we start refreshing the roster */ - virtual void OnRosterRefreshStarted() = 0; - /* Called when we have the entire roster */ - virtual void OnRosterRefreshFinished() = 0; - /* Called when an item on the roster is created or updated */ - virtual void OnRosterItemUpdated(const buzz::RosterItem &ri) = 0; - /* Called when an item on the roster is removed */ - virtual void OnRosterItemRemoved(const buzz::RosterItem &ri) = 0; - - /* Subscriptions */ - virtual void OnRosterSubscribe(const buzz::Jid &jid) = 0; - virtual void OnRosterUnsubscribe(const buzz::Jid &jid) = 0; - virtual void OnRosterSubscribed(const buzz::Jid &jid) = 0; - virtual void OnRosterUnsubscribed(const buzz::Jid &jid) = 0; - -}; - -class LibjinglePlus -{ - public: - /* Provide the constructor with your interface. */ - LibjinglePlus(LibjinglePlusNotify *notify); - ~LibjinglePlus(); - - /* Logs in and starts doing stuff - * - * If cookie_auth is true, password must be a Gaia SID. Otherwise, - * it should be the user's password - */ - void Login(const std::string &username, const std::string &password, - const std::string &machine_address, bool is_test, bool cookie_auth); - - /* Set Presence */ - void SendPresence(const buzz::Status & s); - void SendDirectedPresence(const buzz::Jid & j, const buzz::Status & s); - void SendDirectedMUCPresence(const buzz::Jid & j, const buzz::Status & s, - const std::string &user_nick, const std::string &api_capability, - const std::string &api_message, const std::string &role); - - /* Send Message */ - void SendXmppMessage(const buzz::XmppMessage & m); - - /* Send IQ */ - void SendXmppIq(const buzz::Jid &to_jid, bool is_get, - const buzz::XmlElement *iq_element); - - /* Set Roster */ - void UpdateRosterItem(const buzz::Jid & jid, const std::string & name, - const std::vector<std::string> & groups, buzz::GrType grt); - void RemoveRosterItem(const buzz::Jid &jid); - - /* Call this from the thread you want to receive callbacks on. Typically, this will be called - * after your WakeupMainThread() notify function is called. - * - * On Windows, libjingle+ will trigger its callback from the Windows message loop, and - * you needn't call this yourself. - */ - void DoCallbacks(); - - private: - void LoginInternal(const std::string &jid, const std::string &password, - const std::string &machine_address, bool is_test); - - LibjinglePlusWorker *worker_; -}; - -#endif // LIBJINGLE_PLUS_H__ diff --git a/chromium/third_party/libjingle/source/talk/examples/plus/presencepushtask.cc b/chromium/third_party/libjingle/source/talk/examples/plus/presencepushtask.cc deleted file mode 100644 index 9d5ed28db7e..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/plus/presencepushtask.cc +++ /dev/null @@ -1,201 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "talk/base/stringencode.h" -#include "presencepushtask.h" -#include "talk/xmpp/constants.h" -#include <sstream> - - -namespace buzz { - -// string helper functions ----------------------------------------------------- - -static bool -IsXmlSpace(int ch) { - return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; -} - -static bool -ListContainsToken(const std::string & list, const std::string & token) { - size_t i = list.find(token); - if (i == std::string::npos || token.empty()) - return false; - bool boundary_before = (i == 0 || IsXmlSpace(list[i - 1])); - bool boundary_after = (i == list.length() - token.length() || IsXmlSpace(list[i + token.length()])); - return boundary_before && boundary_after; -} - - -bool -PresencePushTask::HandleStanza(const XmlElement * stanza) { - if (stanza->Name() != QN_PRESENCE) - return false; - if (stanza->HasAttr(QN_TYPE) && stanza->Attr(QN_TYPE) != STR_UNAVAILABLE) { - if (stanza->Attr(QN_TYPE) == STR_ERROR) { - // Pass on the error. - const XmlElement* error_xml_elem = stanza->FirstNamed(QN_ERROR); - if (!error_xml_elem) { - return false; - } - SignalStatusError(*error_xml_elem); - return true; - } - } - QueueStanza(stanza); - return true; -} - -static bool IsUtf8FirstByte(int c) { - return (((c)&0x80)==0) || // is single byte - ((unsigned char)((c)-0xc0)<0x3e); // or is lead byte -} - -int -PresencePushTask::ProcessStart() { - const XmlElement * stanza = NextStanza(); - if (stanza == NULL) - return STATE_BLOCKED; - Status s; - - s.set_jid(Jid(stanza->Attr(QN_FROM))); - - if (stanza->Attr(QN_TYPE) == STR_UNAVAILABLE) { - s.set_available(false); - SignalStatusUpdate(s); - } - else { - s.set_available(true); - const XmlElement * status = stanza->FirstNamed(QN_STATUS); - if (status != NULL) { - s.set_status(status->BodyText()); - - // Truncate status messages longer than 300 bytes - if (s.status().length() > 300) { - size_t len = 300; - - // Be careful not to split legal utf-8 chars in half - while (!IsUtf8FirstByte(s.status()[len]) && len > 0) { - len -= 1; - } - std::string truncated(s.status(), 0, len); - s.set_status(truncated); - } - } - - const XmlElement * priority = stanza->FirstNamed(QN_PRIORITY); - if (priority != NULL) { - int pri; - if (talk_base::FromString(priority->BodyText(), &pri)) { - s.set_priority(pri); - } - } - - const XmlElement * show = stanza->FirstNamed(QN_SHOW); - if (show == NULL || show->FirstChild() == NULL) { - s.set_show(Status::SHOW_ONLINE); - } - else { - if (show->BodyText() == "away") { - s.set_show(Status::SHOW_AWAY); - } - else if (show->BodyText() == "xa") { - s.set_show(Status::SHOW_XA); - } - else if (show->BodyText() == "dnd") { - s.set_show(Status::SHOW_DND); - } - else if (show->BodyText() == "chat") { - s.set_show(Status::SHOW_CHAT); - } - else { - s.set_show(Status::SHOW_ONLINE); - } - } - - const XmlElement * caps = stanza->FirstNamed(QN_CAPS_C); - if (caps != NULL) { - std::string node = caps->Attr(QN_NODE); - std::string ver = caps->Attr(QN_VER); - std::string exts = caps->Attr(QN_EXT); - - s.set_know_capabilities(true); - std::string capability; - std::stringstream ss(exts); - while (ss >> capability) { - s.AddCapability(capability); - } - - s->set_caps_node(node); - s->set_version(ver); - } - - const XmlElement* delay = stanza->FirstNamed(kQnDelayX); - if (delay != NULL) { - // Ideally we would parse this according to the Psuedo ISO-8601 rules - // that are laid out in JEP-0082: - // http://www.jabber.org/jeps/jep-0082.html - std::string stamp = delay->Attr(kQnStamp); - s.set_sent_time(stamp); - } - - const XmlElement *nick = stanza->FirstNamed(kQnNickname); - if (nick) { - std::string user_nick = nick->BodyText(); - s.set_user_nick(user_nick); - } - - const XmlElement *plugin = stanza->FirstNamed(QN_PLUGIN); - if (plugin) { - const XmlElement *api_cap = plugin->FirstNamed(QN_CAPABILITY); - if (api_cap) { - const std::string &api_capability = api_cap->BodyText(); - s.set_api_capability(api_capability); - } - const XmlElement *api_msg = plugin->FirstNamed(QN_DATA); - if (api_msg) { - const std::string &api_message = api_msg->BodyText(); - s.set_api_message(api_message); - } - } - - const XmlElement* data_x = stanza->FirstNamed(QN_MUC_USER_X); - if (data_x != NULL) { - const XmlElement* item = data_x->FirstNamed(QN_MUC_USER_ITEM); - if (item != NULL) { - s.set_muc_role(item->Attr(QN_ROLE)); - } - } - - SignalStatusUpdate(s); - } - - return STATE_START; -} - - -} diff --git a/chromium/third_party/libjingle/source/talk/examples/plus/rostertask.cc b/chromium/third_party/libjingle/source/talk/examples/plus/rostertask.cc deleted file mode 100644 index 1344d08316d..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/plus/rostertask.cc +++ /dev/null @@ -1,218 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "rostertask.h" -#include "talk/xmpp/constants.h" -#include "talk/base/stream.h" - -#undef WIN32 -#ifdef WIN32 -#include "talk/app/win32/offlineroster.h" -#endif - -namespace buzz { - -class RosterTask::RosterGetTask : public XmppTask { -public: - RosterGetTask(Task * parent) : XmppTask(parent, XmppEngine::HL_SINGLE), - done_(false) {} - - virtual int ProcessStart(); - virtual int ProcessResponse(); - -protected: - virtual bool HandleStanza(const XmlElement * stanza); - - bool done_; -}; - -//============================================================================== -// RosterTask -//============================================================================== -void RosterTask::RefreshRosterNow() { - RosterGetTask* get_task = new RosterGetTask(this); - ResumeTimeout(); - get_task->Start(); -} - -void RosterTask::TranslateItems(const XmlElement * rosterQueryResult) { -#if defined(FEATURE_ENABLE_PSTN) -#ifdef WIN32 - // We build up a list of contacts which have had information persisted offline. - // we'll remove items from this list if we get a buzz::SUBSCRIBE_REMOVE - // subscription. After updating all the items from the server, we'll then - // update (and merge) any roster items left in our map of offline items - XmlElement *el_local = OfflineRoster::RetrieveOfflineRoster(GetClient()->jid()); - std::map<buzz::Jid, RosterItem> jid_to_item; - if (el_local) { - for (XmlElement *el_item = el_local->FirstNamed(QN_ROSTER_ITEM); - el_item != NULL; - el_item = el_item->NextNamed(QN_ROSTER_ITEM)) { - RosterItem roster_item; - roster_item.FromXml(el_item); - - jid_to_item[roster_item.jid()] = roster_item; - } - } -#endif // WIN32 -#endif // FEATURE_ENABLE_PSTN - - const XmlElement * xml_item; - for (xml_item = rosterQueryResult->FirstNamed(QN_ROSTER_ITEM); - xml_item != NULL; xml_item = xml_item->NextNamed(QN_ROSTER_ITEM)) { - RosterItem roster_item; - roster_item.FromXml(xml_item); - - if (roster_item.subscription() == buzz::SUBSCRIBE_REMOVE) { - SignalRosterItemRemoved(roster_item); - -#if defined(FEATURE_ENABLE_PSTN) -#ifdef WIN32 - std::map<buzz::Jid, RosterItem>::iterator it = - jid_to_item.find(roster_item.jid()); - - if (it != jid_to_item.end()) - jid_to_item.erase(it); -#endif -#endif - } else { - SignalRosterItemUpdated(roster_item, false); - } - } - -#if defined(FEATURE_ENABLE_PSTN) -#ifdef WIN32 - for (std::map<buzz::Jid, RosterItem>::iterator it = jid_to_item.begin(); - it != jid_to_item.end(); ++it) { - SignalRosterItemUpdated(it->second, true); - } -#endif -#endif -} - -int RosterTask::ProcessStart() { - const XmlElement * stanza = NextStanza(); - if (stanza == NULL) - return STATE_BLOCKED; - - if (stanza->Name() == QN_IQ) { - SuspendTimeout(); - bool result = (stanza->Attr(QN_TYPE) == STR_RESULT); - if (result) - SignalRosterRefreshStarted(); - - TranslateItems(stanza->FirstNamed(QN_ROSTER_QUERY)); - - if (result) - SignalRosterRefreshFinished(); - } else if (stanza->Name() == QN_PRESENCE) { - Jid jid(stanza->Attr(QN_FROM)); - std::string type = stanza->Attr(QN_TYPE); - if (type == "subscribe") - SignalSubscribe(jid); - else if (type == "unsubscribe") - SignalUnsubscribe(jid); - else if (type == "subscribed") - SignalSubscribed(jid); - else if (type == "unsubscribed") - SignalUnsubscribed(jid); - } - - return STATE_START; -} - -bool RosterTask::HandleStanza(const XmlElement * stanza) { - if (!MatchRequestIq(stanza, STR_SET, QN_ROSTER_QUERY)) { - // Not a roster IQ. Look for a presence instead - if (stanza->Name() != QN_PRESENCE) - return false; - if (!stanza->HasAttr(QN_TYPE)) - return false; - std::string type = stanza->Attr(QN_TYPE); - if (type == "subscribe" || type == "unsubscribe" || - type == "subscribed" || type == "unsubscribed") { - QueueStanza(stanza); - return true; - } - return false; - } - - // only respect roster push from the server - Jid from(stanza->Attr(QN_FROM)); - if (from != JID_EMPTY && - !from.BareEquals(GetClient()->jid()) && - from != Jid(GetClient()->jid().domain())) - return false; - - XmlElement * result = MakeIqResult(stanza); - result->AddElement(new XmlElement(QN_ROSTER_QUERY, true)); - SendStanza(result); - - QueueStanza(stanza); - return true; -} - - -//============================================================================== -// RosterTask::RosterGetTask -//============================================================================== -int RosterTask::RosterGetTask::ProcessStart() { - talk_base::scoped_ptr<XmlElement> get(MakeIq(STR_GET, JID_EMPTY, task_id())); - get->AddElement(new XmlElement(QN_ROSTER_QUERY, true)); - get->AddAttr(QN_XMLNS_GR, NS_GR, 1); - get->AddAttr(QN_GR_EXT, "2", 1); - get->AddAttr(QN_GR_INCLUDE, "all", 1); - if (SendStanza(get.get()) != XMPP_RETURN_OK) { - return STATE_ERROR; - } - return STATE_RESPONSE; -} - -int RosterTask::RosterGetTask::ProcessResponse() { - if (done_) - return STATE_DONE; - return STATE_BLOCKED; -} - -bool RosterTask::RosterGetTask::HandleStanza(const XmlElement * stanza) { - if (!MatchResponseIq(stanza, JID_EMPTY, task_id())) - return false; - - if (stanza->Attr(QN_TYPE) != STR_RESULT) - return false; - - // Queue the stanza with the parent so these don't get handled out of order - RosterTask* parent = static_cast<RosterTask*>(GetParent()); - parent->QueueStanza(stanza); - - // Wake ourselves so we can go into the done state - done_ = true; - Wake(); - return true; -} - -} diff --git a/chromium/third_party/libjingle/source/talk/examples/plus/rostertask.h b/chromium/third_party/libjingle/source/talk/examples/plus/rostertask.h deleted file mode 100644 index beab1f9a5e8..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/plus/rostertask.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _PHONE_CLIENT_ROSTERTASK_H_ -#define _PHONE_CLIENT_ROSTERTASK_H_ - -#include "talk/xmpp/xmppclient.h" -#include "talk/xmpp/xmpptask.h" -#include "talk/app/rosteritem.h" -#include "talk/base/sigslot.h" - -namespace buzz { - -class RosterTask : public XmppTask { -public: - RosterTask(Task * parent) : - XmppTask(parent, XmppEngine::HL_TYPE) {} - - // Roster items removed or updated. This can come from a push or a get - sigslot::signal2<const RosterItem &, bool> SignalRosterItemUpdated; - sigslot::signal1<const RosterItem &> SignalRosterItemRemoved; - - // Subscription messages - sigslot::signal1<const Jid &> SignalSubscribe; - sigslot::signal1<const Jid &> SignalUnsubscribe; - sigslot::signal1<const Jid &> SignalSubscribed; - sigslot::signal1<const Jid &> SignalUnsubscribed; - - // Roster get - void RefreshRosterNow(); - sigslot::signal0<> SignalRosterRefreshStarted; - sigslot::signal0<> SignalRosterRefreshFinished; - - virtual int ProcessStart(); - -protected: - void TranslateItems(const XmlElement *rosterQueryResult); - - virtual bool HandleStanza(const XmlElement * stanza); - - // Inner class for doing the roster get - class RosterGetTask; - friend class RosterGetTask; -}; - -} - -#endif // _PHONE_CLIENT_ROSTERTASK_H_ diff --git a/chromium/third_party/libjingle/source/talk/examples/plus/testutil/libjingleplus_main.cc b/chromium/third_party/libjingle/source/talk/examples/plus/testutil/libjingleplus_main.cc deleted file mode 100644 index b5a0686ba76..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/plus/testutil/libjingleplus_main.cc +++ /dev/null @@ -1,119 +0,0 @@ -/* - * libjingle - * Copyright 2006, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <iostream> -#include <string> - -#include "talk/base/thread.h" -#include "talk/libjingle-plus/libjingleplus.h" -#include "talk/libjingle-plus/testutil/libjingleplus_test_notifier.h" - -#if defined(_MSC_VER) && (_MSC_VER < 1400) -void __cdecl std::_Throw(const std::exception &) {} -std::_Prhand std::_Raise_handler =0; -#endif - - -void SetConsoleEcho(bool on) { -#ifdef WIN32 - HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); - if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL)) - return; - - DWORD mode; - if (!GetConsoleMode(hIn, &mode)) - return; - - if (on) { - mode = mode | ENABLE_ECHO_INPUT; - } else { - mode = mode & ~ENABLE_ECHO_INPUT; - } - - SetConsoleMode(hIn, mode); -#else - if (on) - system("stty echo"); - else - system("stty -echo"); -#endif -} - -int main (int argc, char **argv) -{ - std::string username; - std::string password; - - bool gaia = false; - - for (int i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-gaia")) - gaia = true; - } - - std::cout << "Username: "; - std::cin >> username; - std::cout << (gaia ? "Gaia cookie: " : "Password: "); - SetConsoleEcho(false); - std::cin >> password; - SetConsoleEcho(true); - - // Create a LibjinglePlus object and give it the notifier interface - LibjinglePlus ljp(new Notifier); - - // Login - ljp.Login(username, password, "talk.google.com", false, gaia); - - buzz::Status s; - s.set_available(true); - s.set_show(buzz::Status::SHOW_ONLINE); - s.set_status("I'm online."); - - buzz::XmppMessage m; - m.set_to(buzz::Jid(username + "@gmail.com")); - m.set_body("What's up?"); - - // Typically, you would wait for WakeupMainThread to be called, and then call - // DoCallbacks. Because I have nothing else to do on the main thread, I'm just going - // to do a few things after 10 seconds and then poll every 2ms. - Sleep(10000); - // ljp.DoCallbacks(); - ljp.SendPresence(s); - ljp.SendXmppMessage(m); - -#ifdef WIN32 - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) { - DispatchMessage(&msg); - } -#else - for (;;) { - ljp.DoCallbacks(); - Sleep(2); - } -#endif -} diff --git a/chromium/third_party/libjingle/source/talk/examples/plus/testutil/libjingleplus_test_notifier.h b/chromium/third_party/libjingle/source/talk/examples/plus/testutil/libjingleplus_test_notifier.h deleted file mode 100644 index 3a1af552a14..00000000000 --- a/chromium/third_party/libjingle/source/talk/examples/plus/testutil/libjingleplus_test_notifier.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * libjingle - * Copyright 2006, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <iostream> -#include <string> - -#include "talk/libjingle-plus/libjingleplus.h" - -class Notifier : virtual public LibjinglePlusNotify { - virtual void OnStateChange(buzz::XmppEngine::State state) { - std::cout << "State change: " << state << std::endl; - } - - virtual void OnSocketClose(int error_code) { - std::cout << "Socket close: " << error_code << std::endl; - } - - virtual void OnXmppOutput(const std::string &output) { - std::cout << ">>>>>>>>" << std::endl << output << std::endl << ">>>>>>>>" << std::endl; - } - - virtual void OnXmppInput(const std::string &input) { - std::cout << "<<<<<<<<" << std::endl << input << std::endl << "<<<<<<<<" << std::endl; - } - - - virtual void OnStatusUpdate(const buzz::Status &status) { - std::string from = status.jid().Str(); - std::cout << from << " - " << status.status() << std::endl; - } - - virtual void OnStatusError(const buzz::XmlElement &stanza) { - } - - virtual void OnIqDone(bool success, const buzz::XmlElement &stanza) { - } - - virtual void OnMessage(const buzz::XmppMessage &m) { - if (m.body() != "") - std::cout << m.from().Str() << ": " << m.body() << std::endl; - } - - void OnRosterItemUpdated(const buzz::RosterItem &ri) { - std::cout << "Roster item: " << ri.jid().Str() << std::endl; - } - - virtual void OnRosterItemRemoved(const buzz::RosterItem &ri) { - std::cout << "Roster item removed: " << ri.jid().Str() << std::endl; - } - - virtual void OnRosterSubscribe(const buzz::Jid& jid) { - std::cout << "Subscribing: " << jid.Str() << std::endl; - } - - virtual void OnRosterUnsubscribe(const buzz::Jid &jid) { - std::cout << "Unsubscribing: " <<jid.Str() << std::endl; - } - - virtual void OnRosterSubscribed(const buzz::Jid &jid) { - std::cout << "Subscribed: " << jid.Str() << std::endl; - } - - virtual void OnRosterUnsubscribed(const buzz::Jid &jid) { - std::cout << "Unsubscribed: " << jid.Str() << std::endl; - } - - virtual void OnRosterRefreshStarted() { - std::cout << "Refreshing roster." << std::endl; - } - - virtual void OnRosterRefreshFinished() { - std::cout << "Roster refreshed." << std::endl; - } - - virtual void WakeupMainThread() { - } -}; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/relayserver_main.cc b/chromium/third_party/libjingle/source/talk/examples/relayserver/relayserver_main.cc index 11e8a5bf16e..11e8a5bf16e 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayserver_main.cc +++ b/chromium/third_party/libjingle/source/talk/examples/relayserver/relayserver_main.cc diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stunserver_main.cc b/chromium/third_party/libjingle/source/talk/examples/stunserver/stunserver_main.cc index 446794486e9..446794486e9 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunserver_main.cc +++ b/chromium/third_party/libjingle/source/talk/examples/stunserver/stunserver_main.cc diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/turnserver_main.cc b/chromium/third_party/libjingle/source/talk/examples/turnserver/turnserver_main.cc index d40fede0324..d40fede0324 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/turnserver_main.cc +++ b/chromium/third_party/libjingle/source/talk/examples/turnserver/turnserver_main.cc diff --git a/chromium/third_party/libjingle/source/talk/libjingle.gyp b/chromium/third_party/libjingle/source/talk/libjingle.gyp index dced3d924be..2182561e041 100755 --- a/chromium/third_party/libjingle/source/talk/libjingle.gyp +++ b/chromium/third_party/libjingle/source/talk/libjingle.gyp @@ -27,7 +27,6 @@ { 'includes': ['build/common.gypi'], - 'conditions': [ ['os_posix == 1 and OS != "mac" and OS != "ios"', { 'conditions': [ @@ -54,6 +53,9 @@ 'sources': [ 'app/webrtc/java/jni/peerconnection_jni.cc' ], + 'include_dirs': [ + '<(DEPTH)/third_party/libyuv/include', + ], 'conditions': [ ['OS=="linux"', { 'defines': [ @@ -80,6 +82,7 @@ 'variables': { 'java_src_dir': 'app/webrtc/java/src', 'webrtc_modules_dir': '<(webrtc_root)/modules', + 'build_jar_log': '<(INTERMEDIATE_DIR)/build_jar.log', 'peerconnection_java_files': [ 'app/webrtc/java/src/org/webrtc/AudioSource.java', 'app/webrtc/java/src/org/webrtc/AudioTrack.java', @@ -105,6 +108,8 @@ # included here, or better yet, build a proper .jar in webrtc # and include it here. 'android_java_files': [ + 'app/webrtc/java/android/org/webrtc/VideoRendererGui.java', + 'app/webrtc/java/src/org/webrtc/MediaCodecVideoEncoder.java', '<(webrtc_modules_dir)/audio_device/android/java/src/org/webrtc/voiceengine/AudioManagerAndroid.java', '<(webrtc_modules_dir)/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java', '<(webrtc_modules_dir)/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java', @@ -137,10 +142,13 @@ }], ], 'action': [ - 'build/build_jar.sh', '<(java_home)', '<@(_outputs)', - '<(INTERMEDIATE_DIR)', - '<(build_classpath)', - '<@(java_files)' + 'bash', '-ec', + 'mkdir -p <(INTERMEDIATE_DIR) && ' + '{ build/build_jar.sh <(java_home) <@(_outputs) ' + ' <(INTERMEDIATE_DIR)/build_jar.tmp ' + ' <(build_classpath) <@(java_files) ' + ' > <(build_jar_log) 2>&1 || ' + ' { cat <(build_jar_log) ; exit 1; } }' ], }, ], @@ -150,7 +158,8 @@ }, ], }], - ['OS=="ios" or (OS=="mac" and target_arch!="ia32")', { + ['OS=="ios" or (OS=="mac" and target_arch!="ia32" and mac_sdk>="10.7")', { + # The >= 10.7 above is required for ARC. 'targets': [ { 'target_name': 'libjingle_peerconnection_objc', @@ -161,8 +170,11 @@ 'sources': [ 'app/webrtc/objc/RTCAudioTrack+Internal.h', 'app/webrtc/objc/RTCAudioTrack.mm', + 'app/webrtc/objc/RTCDataChannel+Internal.h', + 'app/webrtc/objc/RTCDataChannel.mm', 'app/webrtc/objc/RTCEnumConverter.h', 'app/webrtc/objc/RTCEnumConverter.mm', + 'app/webrtc/objc/RTCI420Frame+Internal.h', 'app/webrtc/objc/RTCI420Frame.mm', 'app/webrtc/objc/RTCICECandidate+Internal.h', 'app/webrtc/objc/RTCICECandidate.mm', @@ -178,6 +190,7 @@ 'app/webrtc/objc/RTCMediaStream.mm', 'app/webrtc/objc/RTCMediaStreamTrack+Internal.h', 'app/webrtc/objc/RTCMediaStreamTrack.mm', + 'app/webrtc/objc/RTCOpenGLVideoRenderer.mm', 'app/webrtc/objc/RTCPair.m', 'app/webrtc/objc/RTCPeerConnection+Internal.h', 'app/webrtc/objc/RTCPeerConnection.mm', @@ -186,6 +199,8 @@ 'app/webrtc/objc/RTCPeerConnectionObserver.mm', 'app/webrtc/objc/RTCSessionDescription+Internal.h', 'app/webrtc/objc/RTCSessionDescription.mm', + 'app/webrtc/objc/RTCStatsReport+Internal.h', + 'app/webrtc/objc/RTCStatsReport.mm', 'app/webrtc/objc/RTCVideoCapturer+Internal.h', 'app/webrtc/objc/RTCVideoCapturer.mm', 'app/webrtc/objc/RTCVideoRenderer+Internal.h', @@ -196,6 +211,7 @@ 'app/webrtc/objc/RTCVideoTrack.mm', 'app/webrtc/objc/public/RTCAudioSource.h', 'app/webrtc/objc/public/RTCAudioTrack.h', + 'app/webrtc/objc/public/RTCDataChannel.h', 'app/webrtc/objc/public/RTCI420Frame.h', 'app/webrtc/objc/public/RTCICECandidate.h', 'app/webrtc/objc/public/RTCICEServer.h', @@ -203,16 +219,18 @@ 'app/webrtc/objc/public/RTCMediaSource.h', 'app/webrtc/objc/public/RTCMediaStream.h', 'app/webrtc/objc/public/RTCMediaStreamTrack.h', + 'app/webrtc/objc/public/RTCOpenGLVideoRenderer.h', 'app/webrtc/objc/public/RTCPair.h', 'app/webrtc/objc/public/RTCPeerConnection.h', 'app/webrtc/objc/public/RTCPeerConnectionDelegate.h', 'app/webrtc/objc/public/RTCPeerConnectionFactory.h', 'app/webrtc/objc/public/RTCSessionDescription.h', - 'app/webrtc/objc/public/RTCSessionDescriptonDelegate.h', + 'app/webrtc/objc/public/RTCSessionDescriptionDelegate.h', + 'app/webrtc/objc/public/RTCStatsDelegate.h', + 'app/webrtc/objc/public/RTCStatsReport.h', 'app/webrtc/objc/public/RTCTypes.h', 'app/webrtc/objc/public/RTCVideoCapturer.h', 'app/webrtc/objc/public/RTCVideoRenderer.h', - 'app/webrtc/objc/public/RTCVideoRendererDelegate.h', 'app/webrtc/objc/public/RTCVideoSource.h', 'app/webrtc/objc/public/RTCVideoTrack.h', ], @@ -228,13 +246,50 @@ ], 'link_settings': { 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/Foundation.framework', '-lstdc++', ], }, 'xcode_settings': { 'CLANG_ENABLE_OBJC_ARC': 'YES', + # common.gypi enables this for mac but we want this to be disabled + # like it is for ios. + 'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO', }, + 'conditions': [ + ['OS=="ios"', { + 'sources': [ + 'app/webrtc/objc/RTCEAGLVideoView+Internal.h', + 'app/webrtc/objc/RTCEAGLVideoView.m', + 'app/webrtc/objc/public/RTCEAGLVideoView.h', + ], + 'link_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework CoreGraphics', + '-framework GLKit', + ], + }, + }, + }], + ['OS=="mac"', { + 'sources': [ + 'app/webrtc/objc/RTCNSGLVideoView.m', + 'app/webrtc/objc/public/RTCNSGLVideoView.h', + ], + 'xcode_settings': { + # Need to build against 10.7 framework for full ARC support + # on OSX. + 'MACOSX_DEPLOYMENT_TARGET' : '10.7', + }, + 'link_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework Cocoa', + ], + }, + }, + }], + ], }, # target libjingle_peerconnection_objc ], }], @@ -257,6 +312,8 @@ 'base/asyncfile.h', 'base/asynchttprequest.cc', 'base/asynchttprequest.h', + 'base/asyncinvoker.cc', + 'base/asyncinvoker.h', 'base/asyncpacketsocket.h', 'base/asyncresolverinterface.h', 'base/asyncsocket.cc', @@ -279,6 +336,7 @@ 'base/bytebuffer.cc', 'base/bytebuffer.h', 'base/byteorder.h', + 'base/callback.h', 'base/checks.cc', 'base/checks.h', 'base/common.cc', @@ -380,6 +438,7 @@ 'base/scoped_autorelease_pool.h', 'base/scoped_ptr.h', 'base/scoped_ref_ptr.h', + 'base/scopedptrcollection.h', 'base/sec_buffer.h', 'base/sha1.cc', 'base/sha1.h', @@ -406,6 +465,7 @@ 'base/ssladapter.cc', 'base/ssladapter.h', 'base/sslconfig.h', + 'base/sslfingerprint.cc', 'base/sslfingerprint.h', 'base/sslidentity.cc', 'base/sslidentity.h', @@ -504,6 +564,8 @@ 'xmpp/pubsub_task.h', 'xmpp/pubsubclient.cc', 'xmpp/pubsubclient.h', + 'xmpp/pubsubstateclient.cc', + 'xmpp/pubsubstateclient.h', 'xmpp/pubsubtasks.cc', 'xmpp/pubsubtasks.h', 'xmpp/receivetask.cc', @@ -539,13 +601,6 @@ 'xmpp/xmppthread.h', ], 'conditions': [ - ['OS=="mac" or OS=="ios" or OS=="win"', { - 'dependencies': [ - # The chromium copy of nss should NOT be used on platforms that - # have NSS as system libraries, such as linux. - '<(DEPTH)/third_party/nss/nss.gyp:nss', - ], - }], ['OS=="android"', { 'sources': [ 'base/ifaddrs-android.cc', @@ -643,16 +698,16 @@ }], ['OS=="ios"', { 'sources': [ + 'base/iosfilesystem.mm', 'base/scoped_autorelease_pool.mm', ], 'dependencies': [ - '../net/third_party/nss/ssl.gyp:libssl', + '<(DEPTH)/net/third_party/nss/ssl.gyp:libssl', ], 'all_dependent_settings': { 'xcode_settings': { 'OTHER_LDFLAGS': [ '-framework Foundation', - '-framework IOKit', '-framework Security', '-framework SystemConfiguration', '-framework UIKit', @@ -706,13 +761,9 @@ 'base/unixfilesystem.h', ], 'conditions': [ - ['OS=="linux" or OS=="android"', { - 'dependencies': [ - '<(DEPTH)/third_party/openssl/openssl.gyp:openssl', - ], - }], ['OS!="ios"', { 'sources': [ + 'base/openssl.h', 'base/openssladapter.cc', 'base/openssladapter.h', 'base/openssldigest.cc', @@ -772,13 +823,20 @@ { 'target_name': 'libjingle_media', 'type': 'static_library', + 'include_dirs': [ + # TODO(jiayl): move this into the direct_dependent_settings of + # usrsctp.gyp. + '<(DEPTH)/third_party/usrsctp', + ], 'dependencies': [ '<(DEPTH)/third_party/libyuv/libyuv.gyp:libyuv', + '<(DEPTH)/third_party/usrsctp/usrsctp.gyp:usrsctplib', '<(webrtc_root)/modules/modules.gyp:video_capture_module', '<(webrtc_root)/modules/modules.gyp:video_render_module', - '<(webrtc_root)/video_engine/video_engine.gyp:video_engine_core', + '<(webrtc_root)/webrtc.gyp:webrtc', '<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:field_trial_default', 'libjingle', 'libjingle_sound', ], @@ -832,6 +890,8 @@ 'media/base/videoprocessor.h', 'media/base/videorenderer.h', 'media/base/voiceprocessor.h', + 'media/base/yuvframegenerator.cc', + 'media/base/yuvframegenerator.h', 'media/devices/deviceinfo.h', 'media/devices/devicemanager.cc', 'media/devices/devicemanager.h', @@ -839,14 +899,14 @@ 'media/devices/filevideocapturer.cc', 'media/devices/filevideocapturer.h', 'media/devices/videorendererfactory.h', + 'media/devices/yuvframescapturer.cc', + 'media/devices/yuvframescapturer.h', 'media/other/linphonemediaengine.h', - # TODO(ronghuawu): Enable when SCTP is ready. - # 'media/sctp/sctpdataengine.cc', - # 'media/sctp/sctpdataengine.h', - 'media/sctp/sctputils.cc', - 'media/sctp/sctputils.h', + 'media/sctp/sctpdataengine.cc', + 'media/sctp/sctpdataengine.h', 'media/webrtc/webrtccommon.h', 'media/webrtc/webrtcexport.h', + 'media/webrtc/webrtcmediaengine.cc', 'media/webrtc/webrtcmediaengine.h', 'media/webrtc/webrtcpassthroughrender.cc', 'media/webrtc/webrtcpassthroughrender.h', @@ -858,6 +918,8 @@ 'media/webrtc/webrtcvideoencoderfactory.h', 'media/webrtc/webrtcvideoengine.cc', 'media/webrtc/webrtcvideoengine.h', + 'media/webrtc/webrtcvideoengine2.cc', + 'media/webrtc/webrtcvideoengine2.h', 'media/webrtc/webrtcvideoframe.cc', 'media/webrtc/webrtcvideoframe.h', 'media/webrtc/webrtcvie.h', @@ -952,7 +1014,6 @@ }], ['OS=="ios"', { 'sources': [ - 'media/devices/iosdeviceinfo.cc', 'media/devices/mobiledevicemanager.cc', ], 'include_dirs': [ @@ -960,6 +1021,13 @@ # libjpeg which pulls in libyuv which currently disabled. '../third_party/libyuv/include', ], + 'dependencies!': [ + '<(DEPTH)/third_party/usrsctp/usrsctp.gyp:usrsctplib', + ], + 'sources!': [ + 'media/sctp/sctpdataengine.cc', + 'media/sctp/sctpdataengine.h', + ], }], ['OS=="android"', { 'sources': [ @@ -984,10 +1052,6 @@ '<(DEPTH)/testing/gtest/include', ], }, - 'defines': [ - # TODO(ronghuawu): enable SCTP when it's ready. - # 'HAVE_SCTP', - ], 'sources': [ 'p2p/base/asyncstuntcpsocket.cc', 'p2p/base/asyncstuntcpsocket.h', @@ -1081,6 +1145,8 @@ 'session/tunnel/securetunnelsessionclient.h', 'session/media/audiomonitor.cc', 'session/media/audiomonitor.h', + 'session/media/bundlefilter.cc', + 'session/media/bundlefilter.h', 'session/media/call.cc', 'session/media/call.h', 'session/media/channel.cc', @@ -1106,8 +1172,6 @@ 'session/media/soundclip.h', 'session/media/srtpfilter.cc', 'session/media/srtpfilter.h', - 'session/media/ssrcmuxfilter.cc', - 'session/media/ssrcmuxfilter.h', 'session/media/typingmonitor.cc', 'session/media/typingmonitor.h', 'session/media/voicechannel.h', @@ -1163,8 +1227,12 @@ 'app/webrtc/portallocatorfactory.cc', 'app/webrtc/portallocatorfactory.h', 'app/webrtc/proxy.h', + 'app/webrtc/remoteaudiosource.cc', + 'app/webrtc/remoteaudiosource.h', 'app/webrtc/remotevideocapturer.cc', 'app/webrtc/remotevideocapturer.h', + 'app/webrtc/sctputils.cc', + 'app/webrtc/sctputils.h', 'app/webrtc/statscollector.cc', 'app/webrtc/statscollector.h', 'app/webrtc/statstypes.h', diff --git a/chromium/third_party/libjingle/source/talk/libjingle.scons b/chromium/third_party/libjingle/source/talk/libjingle.scons deleted file mode 100644 index 87b43f50ed0..00000000000 --- a/chromium/third_party/libjingle/source/talk/libjingle.scons +++ /dev/null @@ -1,790 +0,0 @@ -import talk -Import("env") - -talk.Library(env, name = "expat", - cppdefines = [ - "XML_STATIC", - ], - srcs = [ - "third_party/expat-2.0.1/lib/xmlparse.c", - "third_party/expat-2.0.1/lib/xmlrole.c", - "third_party/expat-2.0.1/lib/xmltok.c", - ], - includedirs = [ - "third_party/expat-2.0.1/lib", - ], - win_cppdefines = [ - "COMPILED_FROM_DSP", - ], - posix_cppdefines = [ - "HAVE_EXPAT_CONFIG_H", - ], -) -talk.Library(env, name = "gunit", - srcs = [ - "testing/gtest/src/gtest-all.cc", - ], - includedirs = [ - "testing/gtest/include", - "third_party/expat-2.0.1/lib", - "third_party/srtp", - "testing/gtest", - ], - cppdefines = [ - "EXPAT_RELATIVE_PATH", - "GTEST_RELATIVE_PATH", - "SRTP_RELATIVE_PATH", - ], -) -talk.Library(env, name = "srtp", - srcs = [ - "third_party/srtp/crypto/cipher/aes.c", - "third_party/srtp/crypto/cipher/aes_cbc.c", - "third_party/srtp/crypto/cipher/aes_icm.c", - "third_party/srtp/crypto/cipher/cipher.c", - "third_party/srtp/crypto/cipher/null_cipher.c", - "third_party/srtp/crypto/hash/auth.c", - "third_party/srtp/crypto/hash/hmac.c", - "third_party/srtp/crypto/hash/null_auth.c", - "third_party/srtp/crypto/hash/sha1.c", - "third_party/srtp/crypto/replay/rdb.c", - "third_party/srtp/crypto/replay/rdbx.c", - "third_party/srtp/crypto/replay/ut_sim.c", - "third_party/srtp/crypto/math/datatypes.c", - "third_party/srtp/crypto/math/stat.c", - "third_party/srtp/crypto/kernel/alloc.c", - "third_party/srtp/crypto/kernel/crypto_kernel.c", - "third_party/srtp/crypto/kernel/err.c", - "third_party/srtp/crypto/kernel/key.c", - "third_party/srtp/crypto/rng/ctr_prng.c", - "third_party/srtp/crypto/rng/rand_source.c", - "third_party/srtp/srtp/ekt.c", - "third_party/srtp/srtp/srtp.c", - ], - includedirs = [ - "third_party/srtp/include", - "third_party/srtp/crypto/include", - ], - win_ccflags = [ - "/wd4701", - "/wd4702", - ], -) -# Set up the SSL/TLS includes -if 'NSS_BUILD_PLATFORM' in env['ENV']: - SSL_INCLUDES = [ - "third_party/mozilla/dist/public/nss", - "third_party/mozilla/dist/" + env['ENV']['NSS_BUILD_PLATFORM']+ "/include" - ] - SSL_LIBS = [ - "ssl3", - "nss3", - "nssutil3", - "plc4", - "plds4", - "nspr4", - ] -else: - SSL_INCLUDES = ["third_party/openssl/include"] - SSL_LIBS = ["crypto", "ssl"] - -talk.Library(env, name = "jingle", - lin_packages = [ - "x11", - "xcomposite", - "xrender", - ], - lin_srcs = [ - "base/latebindingsymboltable.cc", - "base/latebindingsymboltable.h.def", - "base/latebindingsymboltable.cc.def", - "base/linux.cc", - "base/linuxfdwalk.c", - "base/linuxwindowpicker.cc", - "media/devices/libudevsymboltable.cc", - "media/devices/linuxdeviceinfo.cc", - "media/devices/linuxdevicemanager.cc", - "media/devices/v4llookup.cc", - "sound/alsasoundsystem.cc", - "sound/alsasymboltable.cc", - "sound/linuxsoundsystem.cc", - "sound/pulseaudiosoundsystem.cc", - "sound/pulseaudiosymboltable.cc", - ], - dependent_target_settings = { - 'lin_libs': [ - "dl", - "pthread", - "rt", - "gthread-2.0", - ], - 'mac_libs': SSL_LIBS, - 'win_libs': [ - "winmm.lib", - ], - }, - mac_srcs = [ - "base/macasyncsocket.cc", - "base/maccocoasocketserver.mm", - "base/maccocoathreadhelper.mm", - "base/macconversion.cc", - "base/macsocketserver.cc", - "base/macutils.cc", - "base/macwindowpicker.cc", - "base/scoped_autorelease_pool.mm", - "media/devices/carbonvideorenderer.cc", - "media/devices/macdeviceinfo.cc", - "media/devices/macdevicemanager.cc", - "media/devices/macdevicemanagermm.mm", - ], - posix_srcs = [ - "base/unixfilesystem.cc", - "base/posix.cc", - ], - linphone_srcs = [ - "media/other/linphonemediaengine.cc", - ], - cppdefines = [ - "FEATURE_ENABLE_VOICEMAIL", - "EXPAT_RELATIVE_PATH", - "GTEST_RELATIVE_PATH", - "SRTP_RELATIVE_PATH", - "XML_STATIC", - ], - srcs = [ - "base/asyncfile.cc", - "base/asynchttprequest.cc", - "base/asyncsocket.cc", - "base/asynctcpsocket.cc", - "base/asyncudpsocket.cc", - "base/autodetectproxy.cc", - "base/bandwidthsmoother.cc", - "base/base64.cc", - "base/basicpacketsocketfactory.cc", - "base/bytebuffer.cc", - "base/checks.cc", - "base/common.cc", - "base/cpumonitor.cc", - "base/crc32.cc", - "base/diskcache.cc", - "base/event.cc", - "base/filelock.cc", - "base/fileutils.cc", - "base/firewallsocketserver.cc", - "base/flags.cc", - "base/helpers.cc", - "base/host.cc", - "base/httpbase.cc", - "base/httpclient.cc", - "base/httpcommon.cc", - "base/httprequest.cc", - "base/httpserver.cc", - "base/ipaddress.cc", - "base/logging.cc", - "base/md5.cc", - "base/messagedigest.cc", - "base/messagehandler.cc", - "base/messagequeue.cc", - "base/multipart.cc", - "base/natserver.cc", - "base/natsocketfactory.cc", - "base/nattypes.cc", - "base/nethelpers.cc", - "base/network.cc", - "base/nssidentity.cc", - "base/nssstreamadapter.cc", - "base/openssladapter.cc", - "base/openssldigest.cc", - "base/opensslidentity.cc", - "base/opensslstreamadapter.cc", - "base/optionsfile.cc", - "base/pathutils.cc", - "base/physicalsocketserver.cc", - "base/profiler.cc", - "base/proxydetect.cc", - "base/proxyinfo.cc", - "base/proxyserver.cc", - "base/ratelimiter.cc", - "base/ratetracker.cc", - "base/sha1.cc", - "base/sharedexclusivelock.cc", - "base/signalthread.cc", - "base/socketadapters.cc", - "base/socketaddress.cc", - "base/socketaddresspair.cc", - "base/socketpool.cc", - "base/socketstream.cc", - "base/ssladapter.cc", - "base/sslsocketfactory.cc", - "base/sslidentity.cc", - "base/sslstreamadapter.cc", - "base/sslstreamadapterhelper.cc", - "base/stream.cc", - "base/stringencode.cc", - "base/stringutils.cc", - "base/systeminfo.cc", - "base/task.cc", - "base/taskparent.cc", - "base/taskrunner.cc", - "base/testclient.cc", - "base/thread.cc", - "base/timeutils.cc", - "base/timing.cc", - "base/transformadapter.cc", - "base/urlencode.cc", - "base/versionparsing.cc", - "base/virtualsocketserver.cc", - "base/worker.cc", - "p2p/base/constants.cc", - "p2p/base/dtlstransportchannel.cc", - "p2p/base/p2ptransport.cc", - "p2p/base/p2ptransportchannel.cc", - "p2p/base/parsing.cc", - "p2p/base/port.cc", - "p2p/base/portallocator.cc", - "p2p/base/portallocatorsessionproxy.cc", - "p2p/base/portproxy.cc", - "p2p/base/pseudotcp.cc", - "p2p/base/relayport.cc", - "p2p/base/relayserver.cc", - "p2p/base/rawtransport.cc", - "p2p/base/rawtransportchannel.cc", - "p2p/base/session.cc", - "p2p/base/sessiondescription.cc", - "p2p/base/sessionmanager.cc", - "p2p/base/sessionmessages.cc", - "p2p/base/stun.cc", - "p2p/base/stunport.cc", - "p2p/base/stunrequest.cc", - "p2p/base/stunserver.cc", - "p2p/base/tcpport.cc", - "p2p/base/transport.cc", - "p2p/base/transportchannel.cc", - "p2p/base/transportchannelproxy.cc", - "p2p/base/transportdescriptionfactory.cc", - "p2p/base/turnport.cc", - "p2p/base/turnserver.cc", - "p2p/client/basicportallocator.cc", - "p2p/client/connectivitychecker.cc", - "p2p/client/httpportallocator.cc", - "p2p/client/socketmonitor.cc", - "session/tunnel/pseudotcpchannel.cc", - "session/tunnel/tunnelsessionclient.cc", - "session/tunnel/securetunnelsessionclient.cc", - "media/base/capturemanager.cc", - "media/base/capturerenderadapter.cc", - "media/base/codec.cc", - "media/base/constants.cc", - "media/base/cpuid.cc", - "media/base/filemediaengine.cc", - "media/base/hybridvideoengine.cc", - "media/base/mediaengine.cc", - "media/base/rtpdataengine.cc", - "media/base/rtpdump.cc", - "media/base/rtputils.cc", - "media/base/streamparams.cc", - "media/base/videoadapter.cc", - "media/base/videocapturer.cc", - "media/base/mutedvideocapturer.cc", - "media/base/videocommon.cc", - "media/base/videoframe.cc", - "media/devices/devicemanager.cc", - "media/devices/filevideocapturer.cc", - "media/sctp/sctputils.cc", - "session/media/audiomonitor.cc", - "session/media/call.cc", - "session/media/channel.cc", - "session/media/channelmanager.cc", - "session/media/currentspeakermonitor.cc", - "session/media/mediamessages.cc", - "session/media/mediamonitor.cc", - "session/media/mediarecorder.cc", - "session/media/mediasession.cc", - "session/media/mediasessionclient.cc", - "session/media/rtcpmuxfilter.cc", - "session/media/rtcpmuxfilter.cc", - "session/media/soundclip.cc", - "session/media/srtpfilter.cc", - "session/media/ssrcmuxfilter.cc", - "session/media/typingmonitor.cc", - "sound/nullsoundsystem.cc", - "sound/nullsoundsystemfactory.cc", - "sound/platformsoundsystem.cc", - "sound/platformsoundsystemfactory.cc", - "sound/soundsysteminterface.cc", - "sound/soundsystemproxy.cc", - "xmllite/qname.cc", - "xmllite/xmlbuilder.cc", - "xmllite/xmlconstants.cc", - "xmllite/xmlelement.cc", - "xmllite/xmlnsstack.cc", - "xmllite/xmlparser.cc", - "xmllite/xmlprinter.cc", - "xmpp/chatroommoduleimpl.cc", - "xmpp/constants.cc", - "xmpp/discoitemsquerytask.cc", - "xmpp/hangoutpubsubclient.cc", - "xmpp/iqtask.cc", - "xmpp/jid.cc", - "xmpp/jingleinfotask.cc", - "xmpp/moduleimpl.cc", - "xmpp/mucroomconfigtask.cc", - "xmpp/mucroomdiscoverytask.cc", - "xmpp/mucroomlookuptask.cc", - "xmpp/mucroomuniquehangoutidtask.cc", - "xmpp/pingtask.cc", - "xmpp/presenceouttask.cc", - "xmpp/presencereceivetask.cc", - "xmpp/presencestatus.cc", - "xmpp/pubsubclient.cc", - "xmpp/pubsub_task.cc", - "xmpp/pubsubtasks.cc", - "xmpp/receivetask.cc", - "xmpp/rostermoduleimpl.cc", - "xmpp/saslmechanism.cc", - "xmpp/xmppclient.cc", - "xmpp/xmppengineimpl.cc", - "xmpp/xmppengineimpl_iq.cc", - "xmpp/xmpplogintask.cc", - "xmpp/xmppstanzaparser.cc", - "xmpp/xmpptask.cc", - "xmpp/xmppauth.cc", - "xmpp/xmpppump.cc", - "xmpp/xmppsocket.cc", - "xmpp/xmppthread.cc", - ], - includedirs = [ - "third_party/libudev", - "third_party/expat-2.0.1/lib", - "testing/gtest/include", - "third_party/srtp/include", - "third_party/srtp/crypto/include", - ] + SSL_INCLUDES, - win_srcs = [ - "base/diskcache_win32.cc", - "base/schanneladapter.cc", - "base/win32.cc", - "base/win32regkey.cc", - "base/win32filesystem.cc", - "base/win32securityerrors.cc", - "base/win32socketserver.cc", - "base/win32socketinit.cc", - "base/win32window.cc", - "base/win32windowpicker.cc", - "base/winfirewall.cc", - "base/winping.cc", - "media/devices/gdivideorenderer.cc", - "media/devices/win32deviceinfo.cc", - "media/devices/win32devicemanager.cc", - ], - mac_ccflags = [ - "-Wno-deprecated-declarations", - ], - extra_srcs = [ - "media/devices/dummydevicemanager.cc", - "base/dbus.cc", - "base/libdbusglibsymboltable.cc", - "base/json.cc", - "base/natserver_main.cc", - ], -) -talk.Library(env, name = "videorenderer", - lin_srcs = [ - "media/devices/gtkvideorenderer.cc", - ], - lin_packages = [ - "gobject-2.0", - "gthread-2.0", - "gtk+-2.0", - ], -) -talk.Library(env, name = "unittest_main", - libs = [ - "gunit", - ], - srcs = [ - "base/unittest_main.cc", - ], - includedirs = [ - "testing/gtest/include", - "third_party/expat-2.0.1/lib", - "third_party/srtp", - "testing/gtest", - ], - cppdefines = [ - "EXPAT_RELATIVE_PATH", - "GTEST_RELATIVE_PATH", - "SRTP_RELATIVE_PATH", - ], -) -talk.App(env, name = "login", - libs = [ - "jingle", - "expat", - ], - srcs = [ - "examples/login/login_main.cc", - ], - posix_libs = SSL_LIBS, - lin_libs = [ - "videorenderer", - ], -) -talk.App(env, name = "chat", - libs = [ - "jingle", - "expat", - ], - srcs = [ - "examples/chat/chatapp.cc", - "examples/chat/chat_main.cc", - "examples/chat/consoletask.cc", - "examples/chat/textchatreceivetask.cc", - "examples/chat/textchatsendtask.cc", - ], - posix_libs = SSL_LIBS, -) -talk.App(env, name = "call", - mac_frameworks = [ - "AudioToolbox", - "AudioUnit", - "Cocoa", - "CoreAudio", - "CoreFoundation", - "IOKit", - "QTKit", - "QuickTime", - ], - win_libs = [ - "d3d9.lib", - "gdi32.lib", - "powrprof.lib", - "strmiids.lib", - "winmm.lib", - ], - posix_libs = SSL_LIBS, - lin_libs = [ - "videorenderer", - ], - srcs = [ - "examples/call/call_main.cc", - "examples/call/callclient.cc", - "examples/call/console.cc", - "examples/call/friendinvitesendtask.cc", - "examples/call/mediaenginefactory.cc", - "examples/call/mucinviterecvtask.cc", - "examples/call/mucinvitesendtask.cc", - "examples/call/presencepushtask.cc", - ], - libs = [ - "jingle", - "expat", - "srtp", - ], -) -talk.App(env, name = "relayserver", - libs = [ - "jingle", - ], - srcs = [ - "p2p/base/relayserver_main.cc", - ], -) -talk.App(env, name = "stunserver", - libs = [ - "jingle", - ], - srcs = [ - "p2p/base/stunserver_main.cc", - ], -) -talk.App(env, name = "turnserver", - lin_libs = [ - "crypto", - "ssl", - ], - srcs = [ - "p2p/base/turnserver_main.cc", - ], - libs = [ - "jingle", - ], -) -talk.Unittest(env, name = "base", - lin_srcs = [ - "base/latebindingsymboltable_unittest.cc", - "base/linux_unittest.cc", - "base/linuxfdwalk_unittest.cc", - ], - mac_srcs = [ - "base/macsocketserver_unittest.cc", - "base/macutils_unittest.cc", - "base/macwindowpicker_unittest.cc", - ], - posix_srcs = [ - "base/sslidentity_unittest.cc", - "base/sslstreamadapter_unittest.cc", - ], - cppdefines = [ - "EXPAT_RELATIVE_PATH", - "GTEST_RELATIVE_PATH", - "SRTP_RELATIVE_PATH", - ], - srcs = [ - "base/asynchttprequest_unittest.cc", - "base/atomicops_unittest.cc", - "base/autodetectproxy_unittest.cc", - "base/bandwidthsmoother_unittest.cc", - "base/base64_unittest.cc", - "base/basictypes_unittest.cc", - "base/bind_unittest.cc", - "base/buffer_unittest.cc", - "base/bytebuffer_unittest.cc", - "base/byteorder_unittest.cc", - "base/cpumonitor_unittest.cc", - "base/crc32_unittest.cc", - "base/event_unittest.cc", - "base/filelock_unittest.cc", - "base/fileutils_unittest.cc", - "base/helpers_unittest.cc", - "base/host_unittest.cc", - "base/httpbase_unittest.cc", - "base/httpcommon_unittest.cc", - "base/httpserver_unittest.cc", - "base/ipaddress_unittest.cc", - "base/logging_unittest.cc", - "base/md5digest_unittest.cc", - "base/messagedigest_unittest.cc", - "base/messagequeue_unittest.cc", - "base/multipart_unittest.cc", - "base/nat_unittest.cc", - "base/network_unittest.cc", - "base/nullsocketserver_unittest.cc", - "base/optionsfile_unittest.cc", - "base/pathutils_unittest.cc", - "base/physicalsocketserver_unittest.cc", - "base/profiler_unittest.cc", - "base/proxy_unittest.cc", - "base/proxydetect_unittest.cc", - "base/ratelimiter_unittest.cc", - "base/ratetracker_unittest.cc", - "base/referencecountedsingletonfactory_unittest.cc", - "base/rollingaccumulator_unittest.cc", - "base/sha1digest_unittest.cc", - "base/sharedexclusivelock_unittest.cc", - "base/signalthread_unittest.cc", - "base/sigslot_unittest.cc", - "base/socket_unittest.cc", - "base/socketaddress_unittest.cc", - "base/stream_unittest.cc", - "base/stringencode_unittest.cc", - "base/stringutils_unittest.cc", - "base/systeminfo_unittest.cc", - "base/task_unittest.cc", - "base/testclient_unittest.cc", - "base/thread_unittest.cc", - "base/timeutils_unittest.cc", - "base/urlencode_unittest.cc", - "base/versionparsing_unittest.cc", - "base/virtualsocket_unittest.cc", - "base/windowpicker_unittest.cc", - ], - includedirs = [ - "testing/gtest/include", - "third_party/expat-2.0.1/lib", - "third_party/srtp", - "testing/gtest", - ], - win_srcs = [ - "base/win32_unittest.cc", - "base/win32regkey_unittest.cc", - "base/win32socketserver_unittest.cc", - "base/win32toolhelp_unittest.cc", - "base/win32window_unittest.cc", - "base/win32windowpicker_unittest.cc", - "base/winfirewall_unittest.cc", - ], - libs = [ - "jingle", - ], - extra_srcs = [ - "base/dbus_unittest.cc", - "base/json_unittest.cc", - "base/linuxwindowpicker_unittest.cc", - ], -) -talk.Unittest(env, name = "p2p", - mac_FRAMEWORKS = [ - "Foundation", - "IOKit", - "QTKit", - ], - mac_libs = SSL_LIBS, - cppdefines = [ - "EXPAT_RELATIVE_PATH", - "GTEST_RELATIVE_PATH", - "SRTP_RELATIVE_PATH", - ], - srcs = [ - "p2p/base/dtlstransportchannel_unittest.cc", - "p2p/base/p2ptransportchannel_unittest.cc", - "p2p/base/port_unittest.cc", - "p2p/base/portallocatorsessionproxy_unittest.cc", - "p2p/base/pseudotcp_unittest.cc", - "p2p/base/relayport_unittest.cc", - "p2p/base/relayserver_unittest.cc", - "p2p/base/session_unittest.cc", - "p2p/base/stun_unittest.cc", - "p2p/base/stunport_unittest.cc", - "p2p/base/stunrequest_unittest.cc", - "p2p/base/stunserver_unittest.cc", - "p2p/base/transport_unittest.cc", - "p2p/base/transportdescriptionfactory_unittest.cc", - "p2p/base/turnport_unittest.cc", - "p2p/client/connectivitychecker_unittest.cc", - "p2p/client/portallocator_unittest.cc", - ], - includedirs = [ - "testing/gtest/include", - "third_party/expat-2.0.1/lib", - "third_party/srtp", - "testing/gtest", - ], - libs = [ - "jingle", - "expat", - ], -) -talk.Unittest(env, name = "media", - win_libs = [ - "winmm.lib", - "strmiids", - ], - cppdefines = [ - "EXPAT_RELATIVE_PATH", - "GTEST_RELATIVE_PATH", - "SRTP_RELATIVE_PATH", - ], - srcs = [ - "media/base/capturemanager_unittest.cc", - "media/base/codec_unittest.cc", - "media/base/filemediaengine_unittest.cc", - "media/base/rtpdataengine_unittest.cc", - "media/base/rtpdump_unittest.cc", - "media/base/rtputils_unittest.cc", - "media/base/testutils.cc", - "media/base/videocapturer_unittest.cc", - "media/base/videocommon_unittest.cc", - "media/devices/devicemanager_unittest.cc", - "media/devices/filevideocapturer_unittest.cc", - "media/sctp/sctputils_unittest.cc", - "session/media/channel_unittest.cc", - "session/media/channelmanager_unittest.cc", - "session/media/currentspeakermonitor_unittest.cc", - "session/media/mediarecorder_unittest.cc", - "session/media/mediamessages_unittest.cc", - "session/media/mediasession_unittest.cc", - "session/media/mediasessionclient_unittest.cc", - "session/media/rtcpmuxfilter_unittest.cc", - "session/media/srtpfilter_unittest.cc", - "session/media/ssrcmuxfilter_unittest.cc", - ], - includedirs = [ - "testing/gtest/include", - "third_party/expat-2.0.1/lib", - "third_party/srtp", - "testing/gtest", - ], - libs = [ - "jingle", - "expat", - "srtp", - ], - extra_srcs = [ - "media/devices/dummydevicemanager_unittest.cc", - ], -) -talk.Unittest(env, name = "sound", - libs = [ - "jingle", - ], - srcs = [ - "sound/automaticallychosensoundsystem_unittest.cc", - ], - mac_libs = SSL_LIBS, - - includedirs = [ - "testing/gtest/include", - "third_party/expat-2.0.1/lib", - "third_party/srtp", - "testing/gtest", - ], - cppdefines = [ - "EXPAT_RELATIVE_PATH", - "GTEST_RELATIVE_PATH", - "SRTP_RELATIVE_PATH", - ], -) -talk.Unittest(env, name = "xmllite", - libs = [ - "jingle", - "expat", - ], - srcs = [ - "xmllite/qname_unittest.cc", - "xmllite/xmlbuilder_unittest.cc", - "xmllite/xmlelement_unittest.cc", - "xmllite/xmlnsstack_unittest.cc", - "xmllite/xmlparser_unittest.cc", - "xmllite/xmlprinter_unittest.cc", - ], - mac_libs = SSL_LIBS, - includedirs = [ - "testing/gtest/include", - "third_party/expat-2.0.1/lib", - "third_party/srtp", - "testing/gtest", - ], - cppdefines = [ - "EXPAT_RELATIVE_PATH", - "GTEST_RELATIVE_PATH", - "SRTP_RELATIVE_PATH", - ], -) -talk.Unittest(env, name = "xmpp", - mac_libs = SSL_LIBS, - cppdefines = [ - "EXPAT_RELATIVE_PATH", - "GTEST_RELATIVE_PATH", - "SRTP_RELATIVE_PATH", - ], - srcs = [ - "xmpp/hangoutpubsubclient_unittest.cc", - "xmpp/jid_unittest.cc", - "xmpp/mucroomconfigtask_unittest.cc", - "xmpp/mucroomdiscoverytask_unittest.cc", - "xmpp/mucroomlookuptask_unittest.cc", - "xmpp/mucroomuniquehangoutidtask_unittest.cc", - "xmpp/pingtask_unittest.cc", - "xmpp/pubsubclient_unittest.cc", - "xmpp/pubsubtasks_unittest.cc", - "xmpp/util_unittest.cc", - "xmpp/xmppengine_unittest.cc", - "xmpp/xmpplogintask_unittest.cc", - "xmpp/xmppstanzaparser_unittest.cc", - ], - includedirs = [ - "testing/gtest/include", - "third_party/expat-2.0.1/lib", - "third_party/srtp", - "testing/gtest", - ], - libs = [ - "jingle", - "expat", - ], - extra_srcs = [ - "xmpp/chatroommodule_unittest.cc", - "xmpp/rostermodule_unittest.cc", - ], -) diff --git a/chromium/third_party/libjingle/source/talk/libjingle_examples.gyp b/chromium/third_party/libjingle/source/talk/libjingle_examples.gyp index 1e2eeb0fb75..f69c5dcf8dc 100755 --- a/chromium/third_party/libjingle/source/talk/libjingle_examples.gyp +++ b/chromium/third_party/libjingle/source/talk/libjingle_examples.gyp @@ -51,7 +51,7 @@ 'libjingle.gyp:libjingle_p2p', ], 'sources': [ - 'p2p/base/relayserver_main.cc', + 'examples/relayserver/relayserver_main.cc', ], }, # target relayserver { @@ -62,7 +62,7 @@ 'libjingle.gyp:libjingle_p2p', ], 'sources': [ - 'p2p/base/stunserver_main.cc', + 'examples/stunserver/stunserver_main.cc', ], }, # target stunserver { @@ -73,7 +73,7 @@ 'libjingle.gyp:libjingle_p2p', ], 'sources': [ - 'p2p/base/turnserver_main.cc', + 'examples/turnserver/turnserver_main.cc', ], }, # target turnserver { @@ -218,7 +218,7 @@ ], # targets }], # OS=="linux" or OS=="win" - ['OS=="ios"', { + ['OS=="ios" or (OS=="mac" and target_arch!="ia32" and mac_sdk>="10.8")', { 'targets': [ { 'target_name': 'AppRTCDemo', @@ -226,44 +226,71 @@ 'product_name': 'AppRTCDemo', 'mac_bundle': 1, 'mac_bundle_resources': [ - 'examples/ios/AppRTCDemo/ResourceRules.plist', - 'examples/ios/AppRTCDemo/en.lproj/APPRTCViewController.xib', - 'examples/ios/AppRTCDemo/ios_channel.html', - 'examples/ios/Icon.png', + 'examples/objc/AppRTCDemo/channel.html', ], 'dependencies': [ 'libjingle.gyp:libjingle_peerconnection_objc', ], 'conditions': [ + ['OS=="ios"', { + 'mac_bundle_resources': [ + 'examples/objc/AppRTCDemo/ios/ResourceRules.plist', + 'examples/objc/AppRTCDemo/ios/en.lproj/APPRTCViewController.xib', + 'examples/objc/Icon.png', + ], + 'sources': [ + 'examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.h', + 'examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.m', + 'examples/objc/AppRTCDemo/ios/APPRTCViewController.h', + 'examples/objc/AppRTCDemo/ios/APPRTCViewController.m', + 'examples/objc/AppRTCDemo/ios/AppRTCDemo-Prefix.pch', + 'examples/objc/AppRTCDemo/ios/main.m', + ], + 'xcode_settings': { + 'INFOPLIST_FILE': 'examples/objc/AppRTCDemo/ios/Info.plist', + }, + }], + ['OS=="mac"', { + 'sources': [ + 'examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.h', + 'examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.m', + 'examples/objc/AppRTCDemo/mac/APPRTCViewController.h', + 'examples/objc/AppRTCDemo/mac/APPRTCViewController.m', + 'examples/objc/AppRTCDemo/mac/main.m', + ], + 'xcode_settings': { + 'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO', + 'INFOPLIST_FILE': 'examples/objc/AppRTCDemo/mac/Info.plist', + 'MACOSX_DEPLOYMENT_TARGET' : '10.8', + 'OTHER_LDFLAGS': [ + '-framework AVFoundation', + '-framework WebKit', + ], + }, + }], ['target_arch=="ia32"', { 'dependencies' : [ '<(DEPTH)/testing/iossim/iossim.gyp:iossim#host', ], }], ], + 'include_dirs': [ + 'examples/objc/APPRTCDemo', + ], 'sources': [ - 'examples/ios/AppRTCDemo/APPRTCAppClient.h', - 'examples/ios/AppRTCDemo/APPRTCAppClient.m', - 'examples/ios/AppRTCDemo/APPRTCAppDelegate.h', - 'examples/ios/AppRTCDemo/APPRTCAppDelegate.m', - 'examples/ios/AppRTCDemo/APPRTCViewController.h', - 'examples/ios/AppRTCDemo/APPRTCViewController.m', - 'examples/ios/AppRTCDemo/AppRTCDemo-Prefix.pch', - 'examples/ios/AppRTCDemo/GAEChannelClient.h', - 'examples/ios/AppRTCDemo/GAEChannelClient.m', - 'examples/ios/AppRTCDemo/main.m', + 'examples/objc/AppRTCDemo/APPRTCAppClient.h', + 'examples/objc/AppRTCDemo/APPRTCAppClient.m', + 'examples/objc/AppRTCDemo/APPRTCConnectionManager.h', + 'examples/objc/AppRTCDemo/APPRTCConnectionManager.m', + 'examples/objc/AppRTCDemo/GAEChannelClient.h', + 'examples/objc/AppRTCDemo/GAEChannelClient.m', ], 'xcode_settings': { 'CLANG_ENABLE_OBJC_ARC': 'YES', - 'INFOPLIST_FILE': 'examples/ios/AppRTCDemo/Info.plist', - 'OTHER_LDFLAGS': [ - '-framework Foundation', - '-framework UIKit', - ], }, }, # target AppRTCDemo ], # targets - }], # OS=="ios" + }], # OS=="ios" or (OS=="mac" and target_arch!="ia32" and mac_sdk>="10.8") ['OS=="android"', { 'targets': [ @@ -296,26 +323,26 @@ 'examples/android/res/values/strings.xml', 'examples/android/src/org/appspot/apprtc/AppRTCClient.java', 'examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java', + 'examples/android/src/org/appspot/apprtc/AppRTCGLView.java', 'examples/android/src/org/appspot/apprtc/UnhandledExceptionHandler.java', - 'examples/android/src/org/appspot/apprtc/FramePool.java', 'examples/android/src/org/appspot/apprtc/GAEChannelClient.java', - 'examples/android/src/org/appspot/apprtc/VideoStreamsView.java', ], 'outputs': [ '<(PRODUCT_DIR)/AppRTCDemo-debug.apk', ], 'variables': { - 'ant_log': '<(INTERMEDIATE_DIR)/ant.log', + 'ant_log': '../../<(INTERMEDIATE_DIR)/ant.log', # ../.. to compensate for the cd examples/android below. }, 'action': [ 'bash', '-ec', 'rm -fr <(_outputs) examples/android/{bin,libs} && ' + 'mkdir -p <(INTERMEDIATE_DIR) && ' # Must happen _before_ the cd below 'mkdir -p examples/android/libs/<(android_app_abi) && ' 'cp <(PRODUCT_DIR)/libjingle_peerconnection.jar examples/android/libs/ &&' '<(android_strip) -o examples/android/libs/<(android_app_abi)/libjingle_peerconnection_so.so <(PRODUCT_DIR)/libjingle_peerconnection_so.so &&' 'cd examples/android && ' - 'mkdir -p <(INTERMEDIATE_DIR) && ' - '{ ant -q -l <(ant_log) debug || ' + '{ ANDROID_SDK_ROOT=<(android_sdk_root) ' + 'ant debug > <(ant_log) 2>&1 || ' ' { cat <(ant_log) ; exit 1; } } && ' 'cd - > /dev/null && ' 'cp examples/android/bin/AppRTCDemo-debug.apk <(_outputs)' diff --git a/chromium/third_party/libjingle/source/talk/libjingle_media_unittest.isolate b/chromium/third_party/libjingle/source/talk/libjingle_media_unittest.isolate index 36b50b5f3aa..666478b438c 100644 --- a/chromium/third_party/libjingle/source/talk/libjingle_media_unittest.isolate +++ b/chromium/third_party/libjingle/source/talk/libjingle_media_unittest.isolate @@ -29,16 +29,14 @@ ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_media_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_tracked': [ 'media/testdata/captured-320x240-2s-48.frames', - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_media_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_untracked': [ - '../tools/swarming_client/', + '<(DEPTH)/tools/swarming_client/', ], }, }], diff --git a/chromium/third_party/libjingle/source/talk/libjingle_p2p_unittest.isolate b/chromium/third_party/libjingle/source/talk/libjingle_p2p_unittest.isolate index b5ad4ff2d89..9ff0d77ad6e 100644 --- a/chromium/third_party/libjingle/source/talk/libjingle_p2p_unittest.isolate +++ b/chromium/third_party/libjingle/source/talk/libjingle_p2p_unittest.isolate @@ -29,15 +29,13 @@ ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_p2p_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_tracked': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_p2p_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_untracked': [ - '../tools/swarming_client/', + '<(DEPTH)/tools/swarming_client/', ], }, }], diff --git a/chromium/third_party/libjingle/source/talk/libjingle_peerconnection_unittest.isolate b/chromium/third_party/libjingle/source/talk/libjingle_peerconnection_unittest.isolate index e7dd6878a64..df07d4a9de3 100644 --- a/chromium/third_party/libjingle/source/talk/libjingle_peerconnection_unittest.isolate +++ b/chromium/third_party/libjingle/source/talk/libjingle_peerconnection_unittest.isolate @@ -29,15 +29,13 @@ ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_peerconnection_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_tracked': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_peerconnection_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_untracked': [ - '../tools/swarming_client/', + '<(DEPTH)/tools/swarming_client/', ], }, }], diff --git a/chromium/third_party/libjingle/source/talk/libjingle_sound_unittest.isolate b/chromium/third_party/libjingle/source/talk/libjingle_sound_unittest.isolate index 7166337956c..728e8104184 100644 --- a/chromium/third_party/libjingle/source/talk/libjingle_sound_unittest.isolate +++ b/chromium/third_party/libjingle/source/talk/libjingle_sound_unittest.isolate @@ -29,15 +29,13 @@ ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_sound_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_tracked': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_sound_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_untracked': [ - '../tools/swarming_client/', + '<(DEPTH)/tools/swarming_client/', ], }, }], diff --git a/chromium/third_party/libjingle/source/talk/libjingle_tests.gyp b/chromium/third_party/libjingle/source/talk/libjingle_tests.gyp index 7bfea590d7d..016f0a506ac 100755 --- a/chromium/third_party/libjingle/source/talk/libjingle_tests.gyp +++ b/chromium/third_party/libjingle/source/talk/libjingle_tests.gyp @@ -39,7 +39,11 @@ '<(DEPTH)/testing/gtest/include', '<(DEPTH)/testing/gtest', ], + 'defines': ['_VARIADIC_MAX=10'], 'direct_dependent_settings': { + 'defines': [ + '_VARIADIC_MAX=10', + ], 'include_dirs': [ '<(DEPTH)/testing/gtest/include', ], @@ -100,6 +104,7 @@ { 'target_name': 'libjingle_unittest', 'type': 'executable', + 'includes': [ 'build/ios_tests.gypi', ], 'dependencies': [ 'gunit', 'libjingle.gyp:libjingle', @@ -116,8 +121,10 @@ 'base/buffer_unittest.cc', 'base/bytebuffer_unittest.cc', 'base/byteorder_unittest.cc', + 'base/callback_unittest.cc', 'base/cpumonitor_unittest.cc', 'base/crc32_unittest.cc', + 'base/criticalsection_unittest.cc', 'base/event_unittest.cc', 'base/filelock_unittest.cc', 'base/fileutils_unittest.cc', @@ -144,6 +151,7 @@ 'base/ratetracker_unittest.cc', 'base/referencecountedsingletonfactory_unittest.cc', 'base/rollingaccumulator_unittest.cc', + 'base/scopedptrcollection_unittest.cc', 'base/sha1digest_unittest.cc', 'base/sharedexclusivelock_unittest.cc', 'base/signalthread_unittest.cc', @@ -279,6 +287,7 @@ 'media/base/videoengine_unittest.h', 'media/devices/dummydevicemanager_unittest.cc', 'media/devices/filevideocapturer_unittest.cc', + 'media/sctp/sctpdataengine_unittest.cc', 'media/webrtc/webrtcpassthroughrender_unittest.cc', 'media/webrtc/webrtcvideocapturer_unittest.cc', # Omitted because depends on non-open-source testdata files. @@ -288,8 +297,10 @@ # Disabled because some tests fail. # TODO(ronghuawu): Reenable these tests. # 'media/devices/devicemanager_unittest.cc', - # 'media/webrtc/webrtcvideoengine_unittest.cc', - # 'media/webrtc/webrtcvoiceengine_unittest.cc', + 'media/webrtc/webrtcvideoengine_unittest.cc', + 'media/webrtc/webrtcvideoengine2_unittest.cc', + 'media/webrtc/webrtcvideoengine2_unittest.h', + 'media/webrtc/webrtcvoiceengine_unittest.cc', ], 'conditions': [ ['OS=="win"', { @@ -304,6 +315,11 @@ }, }, }], + ['OS=="ios"', { + 'sources!': [ + 'media/sctp/sctpdataengine_unittest.cc', + ], + }], ], }, # target libjingle_media_unittest { @@ -341,6 +357,7 @@ 'p2p/client/connectivitychecker_unittest.cc', 'p2p/client/fakeportallocator.h', 'p2p/client/portallocator_unittest.cc', + 'session/media/bundlefilter_unittest.cc', 'session/media/channel_unittest.cc', 'session/media/channelmanager_unittest.cc', 'session/media/currentspeakermonitor_unittest.cc', @@ -350,7 +367,6 @@ 'session/media/mediasessionclient_unittest.cc', 'session/media/rtcpmuxfilter_unittest.cc', 'session/media/srtpfilter_unittest.cc', - 'session/media/ssrcmuxfilter_unittest.cc', ], 'conditions': [ ['OS=="win"', { @@ -368,20 +384,25 @@ 'target_name': 'libjingle_peerconnection_unittest', 'type': 'executable', 'dependencies': [ + '<(DEPTH)/testing/gmock.gyp:gmock', 'gunit', 'libjingle.gyp:libjingle', 'libjingle.gyp:libjingle_p2p', 'libjingle.gyp:libjingle_peerconnection', 'libjingle_unittest_main', ], - # TODO(ronghuawu): Reenable below unit tests that require gmock. + 'direct_dependent_settings': { + 'include_dirs': [ + '<(DEPTH)/testing/gmock/include', + ], + }, 'sources': [ - # 'app/webrtc/datachannel_unittest.cc', + 'app/webrtc/datachannel_unittest.cc', 'app/webrtc/dtmfsender_unittest.cc', 'app/webrtc/jsepsessiondescription_unittest.cc', 'app/webrtc/localaudiosource_unittest.cc', - # 'app/webrtc/mediastream_unittest.cc', - # 'app/webrtc/mediastreamhandler_unittest.cc', + 'app/webrtc/mediastream_unittest.cc', + 'app/webrtc/mediastreamhandler_unittest.cc', 'app/webrtc/mediastreamsignaling_unittest.cc', 'app/webrtc/peerconnection_unittest.cc', 'app/webrtc/peerconnectionendtoend_unittest.cc', @@ -389,6 +410,8 @@ 'app/webrtc/peerconnectioninterface_unittest.cc', # 'app/webrtc/peerconnectionproxy_unittest.cc', 'app/webrtc/remotevideocapturer_unittest.cc', + 'app/webrtc/sctputils.cc', + 'app/webrtc/statscollector_unittest.cc', 'app/webrtc/test/fakeaudiocapturemodule.cc', 'app/webrtc/test/fakeaudiocapturemodule.h', 'app/webrtc/test/fakeaudiocapturemodule_unittest.cc', @@ -407,6 +430,21 @@ 'app/webrtc/webrtcsdp_unittest.cc', 'app/webrtc/webrtcsession_unittest.cc', ], + 'conditions': [ + ['OS=="android"', { + # We want gmock features that use tr1::tuple, but we currently + # don't support the variadic templates used by libstdc++'s + # implementation. gmock supports this scenario by providing its + # own implementation but we must opt in to it. + 'defines': [ + 'GTEST_USE_OWN_TR1_TUPLE=1', + # GTEST_USE_OWN_TR1_TUPLE only works if GTEST_HAS_TR1_TUPLE is set. + # gmock r625 made it so that GTEST_HAS_TR1_TUPLE is set to 0 + # automatically on android, so it has to be set explicitly here. + 'GTEST_HAS_TR1_TUPLE=1', + ], + }], + ], }, # target libjingle_peerconnection_unittest ], 'conditions': [ @@ -476,11 +514,37 @@ # does just fine on 10.6 too). 'targets': [ { - 'target_name': 'libjingle_peerconnection_objc_test', + 'target_name': 'libjingle_peerconnection_objc_test', + 'type': 'executable', + 'includes': [ 'build/ios_tests.gypi', ], + 'dependencies': [ + 'gunit', + 'libjingle.gyp:libjingle_peerconnection_objc', + ], + 'sources': [ + 'app/webrtc/objctests/RTCPeerConnectionSyncObserver.h', + 'app/webrtc/objctests/RTCPeerConnectionSyncObserver.m', + 'app/webrtc/objctests/RTCPeerConnectionTest.mm', + 'app/webrtc/objctests/RTCSessionDescriptionSyncObserver.h', + 'app/webrtc/objctests/RTCSessionDescriptionSyncObserver.m', + # TODO(fischman): figure out if this works for ios or if it + # needs a GUI driver. + 'app/webrtc/objctests/mac/main.mm', + ], + 'FRAMEWORK_SEARCH_PATHS': [ + '$(inherited)', + '$(SDKROOT)/Developer/Library/Frameworks', + '$(DEVELOPER_LIBRARY_DIR)/Frameworks', + ], + + # TODO(fischman): there is duplication here with + # build/ios_tests.gypi, because for historical reasons the + # mac x64 bots expect this unittest to be in a bundle + # directory (.app). Once the bots don't expect this + # anymore, remove this duplication. 'variables': { - 'infoplist_file': './app/webrtc/objctests/Info.plist', + 'infoplist_file': 'build/ios_test.plist', }, - 'type': 'executable', 'mac_bundle': 1, 'mac_bundle_resources': [ '<(infoplist_file)', @@ -492,31 +556,18 @@ ], 'xcode_settings': { 'CLANG_ENABLE_OBJC_ARC': 'YES', + # common.gypi enables this for mac but we want this to be disabled + # like it is for ios. + 'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO', 'INFOPLIST_FILE': '<(infoplist_file)', }, - 'dependencies': [ - 'gunit', - 'libjingle.gyp:libjingle_peerconnection_objc', - ], - 'FRAMEWORK_SEARCH_PATHS': [ - '$(inherited)', - '$(SDKROOT)/Developer/Library/Frameworks', - '$(DEVELOPER_LIBRARY_DIR)/Frameworks', - ], - 'sources': [ - 'app/webrtc/objctests/RTCPeerConnectionSyncObserver.h', - 'app/webrtc/objctests/RTCPeerConnectionSyncObserver.m', - 'app/webrtc/objctests/RTCPeerConnectionTest.mm', - 'app/webrtc/objctests/RTCSessionDescriptionSyncObserver.h', - 'app/webrtc/objctests/RTCSessionDescriptionSyncObserver.m', - ], 'conditions': [ - ['OS=="mac" or OS=="ios"', { - 'sources': [ - # TODO(fischman): figure out if this works for ios or if it - # needs a GUI driver. - 'app/webrtc/objctests/mac/main.mm', - ], + ['OS=="mac"', { + 'xcode_settings': { + # Need to build against 10.7 framework for full ARC support + # on OSX. + 'MACOSX_DEPLOYMENT_TARGET' : '10.7', + }, }], ], }, # target libjingle_peerconnection_objc_test diff --git a/chromium/third_party/libjingle/source/talk/libjingle_unittest.isolate b/chromium/third_party/libjingle/source/talk/libjingle_unittest.isolate index e678af013e4..0507f6a8eeb 100644 --- a/chromium/third_party/libjingle/source/talk/libjingle_unittest.isolate +++ b/chromium/third_party/libjingle/source/talk/libjingle_unittest.isolate @@ -29,15 +29,13 @@ ['OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'command': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_tracked': [ - '../testing/test_env.py', '<(PRODUCT_DIR)/libjingle_unittest<(EXECUTABLE_SUFFIX)', ], 'isolate_dependency_untracked': [ - '../tools/swarming_client/', + '<(DEPTH)/tools/swarming_client/', ], }, }], diff --git a/chromium/third_party/libjingle/source/talk/main.scons b/chromium/third_party/libjingle/source/talk/main.scons deleted file mode 100644 index 76b24af6413..00000000000 --- a/chromium/third_party/libjingle/source/talk/main.scons +++ /dev/null @@ -1,889 +0,0 @@ -# -*- Python -*- -# -# -# All the helper functions are defined in: -# - site_scons/talk.py -# Use 'import talk' in any .scons file to get access to it. -# Add any new helper functions to it; unittest are available -# in talk_unittest.py. -# -# Each 'component' that is built is defined in a .scons file. -# See talk.Components(...) for further info on file naming convention. -# -# To add a new platform clone and modify the root_env object. Remember to add -# the new environment object to the envs list otherwise it will not be included -# in the build. -# -# -# - -import talk -import os -import platform - -#------------------------------------------------------------------------------- -# The build files/directories to 'build'. -# If the name is the name of a directory then that directory shall contain a -# .scons file with the same name as the directory itself: -# Ex: The directory session/phone contains a file called phone.scons -# This list must be in order of library dependencies. e.g., if -# session/phone/phone.scons defines a target that links to a library target -# defined in sound/sound.scons, then 'sound' must come first. -# When no particular order is imposed by library dependencies, try to keep in -# mostly alphabetical order. -# -components = talk.Components("libjingle.scons") - -#------------------------------------------------------------------------------- -# Build environments -# - -# The list of build environments. -envs = [] - -# The root of all builds. -root_env = Environment( - tools = [ - 'component_bits', - 'component_setup', - 'replace_strings', - 'talk_noops', - #'talk_utils', - ], - BUILD_SCONSCRIPTS = components, - DESTINATION_ROOT = '$MAIN_DIR/build', - CPPPATH = [ - '$OBJ_ROOT', # generated headers are relative to here - '$MAIN_DIR/..', # TODO(dape): how can we use GOOGLECLIENT instead? - ], - CPPDEFINES = [ - 'LOGGING=1', - - # Feature selection - 'FEATURE_ENABLE_SSL', - 'FEATURE_ENABLE_VOICEMAIL', - 'FEATURE_ENABLE_PSTN', - 'HAVE_SRTP', - ], - # Ensure the os environment is captured for any scripts we call out to - ENV = os.environ, -) - -# This is where we set common environments -# -# Detect if building on 64-bit or 32-bit platform. -DeclareBit('build_platform_64bit', 'Platform of the build machine is 64-bit') -if platform.architecture()[0] == "64bit": - root_env.SetBits('build_platform_64bit') - -# This bit denotes that an env is for 64-bit builds. When set, all build -# artifacts will be 64-bit. When unset, all build artifacts will be 32-bit. -DeclareBit('host_platform_64bit', - 'Platform of the host machine (where artifacts will execute) is ' - '64-bit') - -# This bit denotes that we are cross-compiling using a sysroot. -DeclareBit('cross_compile', - 'Cross compiling using the SYSROOT environment variable') - -def CrossArch(env): - """Return whether or not the host platform architecture differs from the build - environment architecture.""" - if env.Bit('cross_compile'): - # The architecture of the Python process may not match the architecture of - # the sysroot, so we just assume it's not a cross-arch build or that it - # doesn't matter. Currently it only matters if you try to build a cross-arch - # Debian package, so just don't do that. - return False - else: - return env.Bit('host_platform_64bit') != env.Bit('build_platform_64bit') -root_env.AddMethod(CrossArch) - -DeclareBit('use_static_openssl', 'Build OpenSSL as a static library') - -DeclareBit('have_dbus_glib', - 'Whether the build system has the dbus-glib-1 package') -DeclareBit('have_libpulse', - 'Whether the build system has the libpulse package') - - -# List all the locales we localize to. -root_env.AppendUnique(locales = [ - 'af', 'am', 'ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en', 'en-GB', - 'es', 'es-419', 'et', 'eu', 'fa', 'fi', 'fil', 'fr', 'fr-CA', 'gl', 'gu', - 'hi', 'hr', 'hu', 'id', 'is', 'it', 'iw', 'ja', 'kn', 'ko', 'lt', 'lv', - 'ml', 'mr', 'ms', 'nl', 'no', 'or', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', - 'sk', 'sl', 'sr', 'sv', 'sw', 'ta', 'te', 'th', 'tl', 'tr', 'uk', 'ur', - 'vi', 'zh-CN', 'zh-HK', 'zh-TW', 'zu']) - -AddTargetGroup('all_breakpads', 'breakpad files can be built') - -AddTargetGroup('all_dsym', 'dsym debug packages can be built') - -#------------------------------------------------------------------------------- -# W I N D O W S -# -win_env = root_env.Clone( - tools = [ - 'atlmfc_vc80', - #'code_signing', - 'component_targets_msvs', - 'directx_9_0_c', - #'grid_builder', - 'midl', - 'target_platform_windows' - ], - # Don't use default vc80 midl.exe. It doesn't understand vista_sdk idl files. - MIDL = '$PLATFORM_SDK_VISTA_6_0_DIR/Bin/midl.exe ', - WIX_DIR = '$GOOGLECLIENT/third_party/wix/v3_0_2925/files', - # Flags for debug and optimization are added to CCFLAGS instead - CCPDBFLAGS = '', - CCFLAGS_DEBUG = '', - CCFLAGS_OPTIMIZED = '', - # We force a x86 target even when building on x64 Windows platforms. - TARGET_ARCH = 'x86', -) - - -win_env.Decider('MD5-timestamp') -win_env.Append( - COMPONENT_LIBRARY_PUBLISH = True, # Put dlls in output dir too - CCFLAGS = [ - '/Fd${TARGET}.pdb', # pdb per object allows --jobs= - '/WX', # warnings are errors - '/Zc:forScope', # handle 'for (int i = 0 ...)' right - '/EHs-c-', # disable C++ EH - '/GR-', # disable RTTI - '/Gy', # enable function level linking - '/wd4996', # ignore POSIX deprecated warnings - - # promote certain level 4 warnings - '/w14701', # potentially uninitialized var - '/w14702', # unreachable code - '/w14706', # assignment within a conditional - '/w14709', # comma operator within array index - '/w14063', # case 'identifier' is not a valid value for switch of enum - '/w14064', # switch of incomplete enum 'enumeration' - '/w14057', # 'identifier1' indirection to slightly different base - # types from 'identifier2' - '/w14263', # member function does not override any base class virtual - # member function - '/w14266', # no override available for virtual memberfunction from base - # 'type'; function is hidden - '/w14296', # expression is always false - '/w14355', # 'this' : used in base member initializer list - ], - CPPDEFINES = [ - '_ATL_CSTRING_EXPLICIT_CONSTRUCTORS', - # TODO(dape): encapsulate all string operations that are not based - # on std::string/std::wstring and make sure we use the safest versions - # available on all platforms. - '_CRT_SECURE_NO_WARNINGS', - '_USE_32BIT_TIME_T', - '_UNICODE', - 'UNICODE', - '_HAS_EXCEPTIONS=0', - 'WIN32', - # TODO(dape): remove this from logging.cc and enable here instead. - #'WIN32_LEAN_AND_MEAN', - - 'WINVER=0x0500', - '_WIN32_WINNT=0x0501', - '_WIN32_IE=0x0501', - # The Vista platform SDK 6.0 needs at least - # this NTDDI version or else the headers - # that LMI includes from it won't compile. - 'NTDDI_VERSION=NTDDI_WINXP', - - # npapi.h requires the following: - '_WINDOWS', - ], - CPPPATH = [ - '$THIRD_PARTY/wtl_71/include', - '$PLATFORM_SDK_VISTA_6_0_DIR/Include', - ], - LIBPATH = [ - '$PLATFORM_SDK_VISTA_6_0_DIR/Lib' - ], - LINKFLAGS = [ - '-manifest', # TODO(thaloun): Why do we need this? - # Some of the third-party libraries we link in don't have public symbols, so - # ignore that linker warning. - '/ignore:4221', - '/nxcompat', # Binary was tested to be be compatible with Windows DEP. - '/dynamicbase', # Use ASLR to dynamically rebase at load-time. - '/fixed:no', # Binary can be loaded at any base-address. - ], - MIDLFLAGS = [ - '/win32', - '/I$PLATFORM_SDK_VISTA_6_0_DIR/include' - ] -) - -# TODO(dape): Figure out what this does; found it in -# omaha/main.scons. This fixes the problem with redefinition -# of OS_WINDOWS symbol. -win_env.FilterOut(CPPDEFINES = ['OS_WINDOWS=OS_WINDOWS']) - -# Set up digital signing -DeclareBit('test_signing', 'Sign binaries with the test certificate') -win_env.SetBitFromOption('test_signing', False) -if win_env.Bit('test_signing'): - win_env.Replace( - CERTIFICATE_PATH = win_env.File( - '$GOOGLECLIENT/tools/test_key/testkey.pfx').abspath, - CERTIFICATE_PASSWORD = 'test', - ) -AddTargetGroup('signed_binaries', 'digitally signed binaries can be built') - -win_dbg_env = win_env.Clone( - BUILD_TYPE = 'dbg', - BUILD_TYPE_DESCRIPTION = 'Windows debug build', - BUILD_GROUPS = ['default', 'all'], - tools = ['target_debug'], -) - -win_dbg_env.Prepend( - CCFLAGS = [ - '/ZI', # enable debugging - '/Od', # disable optimizations - '/MTd', # link with LIBCMTD.LIB debug lib - '/RTC1', # enable runtime checks - ], -) - -envs.append(win_dbg_env) - -win_dbg64_env = win_dbg_env.Clone( - BUILD_TYPE = 'dbg64', - BUILD_TYPE_DESCRIPTION = 'Windows debug 64bit build', - BUILD_GROUPS = ['all'], -) - -win_dbg64_env.FilterOut(CCFLAGS = ['/ZI']) - -win_dbg64_env.Append( - CCFLAGS = [ - '/Zi', # enable debugging that is 64 bit compatible. - # TODO(fbarchard): fix warnings and remove these disables. - '/wd4244', # disable cast warning - '/wd4267', # disable cast warning - ], - ARFLAGS = [ - '/MACHINE:x64', - ], - CPPDEFINES = [ - 'WIN64', - 'ARCH_CPU_64_BITS', - ], - LIBFLAGS = [ - '/MACHINE:x64', - ], - LINKFLAGS = [ - '/MACHINE:x64', - ], -) - -win_dbg64_env.FilterOut(CPPDEFINES = ['_USE_32BIT_TIME_T']) - -win_dbg64_env.Prepend( - LIBPATH = [ - '$VC80_DIR/vc/lib/amd64', - '$ATLMFC_VC80_DIR/lib/amd64', - '$PLATFORM_SDK_VISTA_6_0_DIR/Lib/x64', - ], -) -win_dbg64_env.PrependENVPath( - 'PATH', - win_dbg64_env.Dir('$VC80_DIR/vc/bin/x86_amd64')) - -win_dbg64_env.SetBits('host_platform_64bit') - -envs.append(win_dbg64_env) - -win_coverage_env = win_dbg_env.Clone( - tools = ['code_coverage'], - BUILD_TYPE = 'coverage', - BUILD_TYPE_DESCRIPTION = 'Windows code coverage build', - BUILD_GROUPS = ['all'], -) - -win_coverage_env.Append( - CPPDEFINES = [ - 'COVERAGE_ENABLED', - ], -) - -envs.append(win_coverage_env) - -win_opt_env = win_env.Clone( - BUILD_TYPE = 'opt', - BUILD_TYPE_DESCRIPTION = 'Windows opt build', - BUILD_GROUPS = ['all'], - tools = ['target_optimized'], -) - -win_opt_env.Prepend( - CCFLAGS=[ - '/Zi', # enable debugging - '/O1', # optimize for size - '/fp:fast', # float faster but less precise - '/MT', # link with LIBCMT.LIB (multi-threaded, static linked crt) - '/GS', # enable security checks - ], - LINKFLAGS = [ - '/safeseh', # protect against attacks against exception handlers - '/opt:ref', # Remove unused references (functions/data). - ], -) - -envs.append(win_opt_env) - -#------------------------------------------------------------------------------- -# P O S I X -# -posix_env = root_env.Clone() -posix_env.Append( - CPPDEFINES = [ - 'HASHNAMESPACE=__gnu_cxx', - 'HASH_NAMESPACE=__gnu_cxx', - 'POSIX', - 'DISABLE_DYNAMIC_CAST', - # The POSIX standard says we have to define this. - '_REENTRANT', - ], - CCFLAGS = [ - '-Wall', - '-Werror', - '-Wno-switch', - '-fno-exceptions', - # Needed for a clean ABI and for link-time dead-code removal to work - # properly. - '-fvisibility=hidden', - # Generate debugging info in the DWARF2 format. - '-gdwarf-2', - # Generate maximal debugging information. (It is stripped from what we ship - # to users, so we want it for both dbg and opt.) - # Note that hammer automatically supplies "-g" for mac/linux dbg, so that - # flag must be filtered out of linux_dbg and mac_dbg envs below. - '-g3', - ], - CXXFLAGS = [ - '-Wno-non-virtual-dtor', - '-Wno-ctor-dtor-privacy', - '-fno-rtti', - ], -) - -# Switch-hit between NSS and OpenSSL -if 'NSS_BUILD_PLATFORM' in root_env['ENV']: - posix_env.AppendUnique(CPPDEFINES=['HAVE_NSS_SSL_H=1', - 'NSS_SSL_RELATIVE_PATH']) -else: - posix_env.AppendUnique(CPPDEFINES=['HAVE_OPENSSL_SSL_H=1']) - - -#------------------------------------------------------------------------------- -# M A C OSX -# -mac_env = posix_env.Clone( - tools = [ - 'target_platform_mac', - #'talk_mac', - #'fill_plist', - ], -) -# Use static OpenSSL on mac so that we can use the latest APIs on all -# supported mac platforms (10.5+). -mac_env.SetBits('use_static_openssl') - -# For libjingle we don't specify a sysroot or minimum OS version. -mac_osx_version_min_32 = "" -mac_osx_version_min_64 = "" - -# Generic mac environment common to all targets -mac_env.Append( - CPPDEFINES = [ - 'OSX', - ], - CCFLAGS = [ - '-arch', 'i386', - '-fasm-blocks', - ], - LINKFLAGS = [ - '-Wl,-search_paths_first', - # This flag makes all members of a static library be included in the - # final exe - that increases the size of the exe, but without it - # Obj-C categories aren't properly included in the exe. - # TODO(thaloun): consider only defining for libs that actually have objc. - '-ObjC', - '-arch', 'i386', - '-dead_strip', - ], - FRAMEWORKS = [ - 'CoreServices', - 'Security', - 'SystemConfiguration', - 'OpenGL', - 'CoreAudio', - 'Quartz', - 'Cocoa', - 'QTKit', - ] -) - -if 'NSS_BUILD_PLATFORM' in root_env['ENV']: - mac_env.AppendUnique(LINKFLAGS = ['-Lthird_party/mozilla/dist/' + root_env['ENV']['NSS_BUILD_PLATFORM'] + '/lib']) -else: - mac_env.AppendUnique(LINKFLAGS = ['-Lthird_party/openssl']) - - -# add debug flags to environment -def mac_debug_include(env): - env.Append( - CCFLAGS = [ - '-O0', - ], - CPPDEFINES = [ - 'DEBUG=1', - ], - ) - # Remove -g set by hammer, which is not what we want (we have set -g3 above). - env.FilterOut(CCFLAGS = ['-g']) - -# add 32/64 bit specific options to specified environment -def mac_common_include_x86_32(env): - env.Append( - CCFLAGS = [ - '-m32', - ], - LINKFLAGS = [ - '-m32', - ], - FRAMEWORKS = [ - 'Carbon', - 'QuickTime', - ], - ) - envs.append(env) - -def mac_common_include_x86_64(env): - env.Append( - CCFLAGS = [ - '-m64', - '-fPIC', - ], - CPPDEFINES = [ - 'ARCH_CPU_64_BITS', - 'CARBON_DEPRECATED', - ], - LINKFLAGS = [ - '-m64', - ], - FRAMEWORKS = [ - 'AppKit', - ], - ) - env.SetBits('host_platform_64bit') - envs.append(env) - -def mac_osx_version_min(env, ver): - if ver != "": - sdk_path = '/Developer/SDKs/MacOSX%s.sdk' % ver - env.Append( - CCFLAGS = [ - '-mmacosx-version-min=' + ver, - '-isysroot', sdk_path, - ], - LINKFLAGS = [ - '-mmacosx-version-min=' + ver, - '-isysroot', sdk_path, - ], - osx_sdk_path = sdk_path, - osx_version_min = ver, - ) - -# Create all environments -mac_dbg_env = mac_env.Clone( - BUILD_TYPE = 'dbg', - BUILD_TYPE_DESCRIPTION = 'Mac debug build', - BUILD_GROUPS = ['default', 'all'], - tools = ['target_debug'], -) - -mac_opt_env = mac_env.Clone( - BUILD_TYPE = 'opt', - BUILD_TYPE_DESCRIPTION = 'Mac opt build', - BUILD_GROUPS = ['all'], - tools = ['target_optimized'], -) - -mac_dbg64_env = mac_dbg_env.Clone( - BUILD_TYPE = 'dbg64', - BUILD_TYPE_DESCRIPTION = 'Mac debug 64bit build', - BUILD_GROUPS = ['all'], -) - -mac_opt64_env = mac_opt_env.Clone( - BUILD_TYPE = 'opt64', - BUILD_TYPE_DESCRIPTION = 'Mac opt 64bit build', - BUILD_GROUPS = ['all'], -) - -mac_debug_include(mac_dbg_env) -mac_debug_include(mac_dbg64_env) -mac_common_include_x86_32(mac_dbg_env) -mac_common_include_x86_32(mac_opt_env) -mac_common_include_x86_64(mac_dbg64_env) -mac_common_include_x86_64(mac_opt64_env) -mac_osx_version_min(mac_dbg_env, mac_osx_version_min_32) -mac_osx_version_min(mac_opt_env, mac_osx_version_min_32) -mac_osx_version_min(mac_dbg64_env, mac_osx_version_min_64) -mac_osx_version_min(mac_opt64_env, mac_osx_version_min_64) - - -#------------------------------------------------------------------------------- -# L I N U X -# -linux_common_env = posix_env.Clone( - tools = [ - 'target_platform_linux', - 'talk_linux', - ], -) - -linux_common_env.Append( - CPPDEFINES = [ - 'LINUX', - ], - CCFLAGS = [ - # Needed for link-time dead-code removal to work properly. - '-ffunction-sections', - '-fdata-sections', - ], - LINKFLAGS = [ - # Enable dead-code removal. - '-Wl,--gc-sections', - # Elide dependencies on shared libraries that we're not actually using. - '-Wl,--as-needed', - '-Wl,--start-group', - ], - _LIBFLAGS = ['-Wl,--end-group'], -) - -# Remove default rpath set by Hammer. Hammer sets it to LIB_DIR, which is wrong. -# The rpath is the _run-time_ library search path for the resulting binary, i.e. -# the one used by ld.so at load time. Setting it equal to the path to build -# output on the build machine is nonsense. -linux_common_env.Replace( - RPATH = [], -) - -# Enable the optional DBus-GLib code if the build machine has the required -# dependency. -linux_common_env.EnableFeatureWherePackagePresent('have_dbus_glib', - 'HAVE_DBUS_GLIB', - 'dbus-glib-1') - -def linux_common_include_x86_32(env): - """Include x86-32 settings into an env based on linux_common.""" - env.Append( - CCFLAGS = [ - '-m32', - ], - LINKFLAGS = [ - '-m32', - ], - ) - -def linux_common_include_x86_64(env): - """Include x86-64 settings into an env based on linux_common.""" - env.Append( - CCFLAGS = [ - '-m64', - '-fPIC', - ], - LINKFLAGS = [ - '-m64', - ], - ) - env.SetBits('host_platform_64bit') - -#------------------------------------------------------------------------------- -# L I N U X -- C R O S S -- B U I L D - -# Cross build requires the following tool names be provided by the environment: -linux_cross_common_env = linux_common_env.Clone( - AR = os.environ.get("AR"), - AS = os.environ.get("AS"), - LD = os.environ.get("LD"), - NM = os.environ.get("NM"), - RANLIB = os.environ.get("RANLIB"), - CC = str(os.environ.get("CC")) + - ' --sysroot=' + str(os.environ.get("SYSROOT")), - CXX = str(os.environ.get("CXX")) + - ' --sysroot=' + str(os.environ.get("SYSROOT")), -) -linux_cross_common_env.SetBits('cross_compile') - -# The rest of these paths and flags are optional: -if os.environ.get("CPPPATH"): - linux_cross_common_env.Append( - CPPPATH = os.environ.get("CPPPATH").split(':'), - ) -if os.environ.get("LIBPATH"): - linux_cross_common_env.Append( - LIBPATH = os.environ.get("LIBPATH").split(':'), - ) -if os.environ.get("CFLAGS"): - linux_cross_common_env.Append( - CFLAGS = os.environ.get("CFLAGS").split(' '), - ) -if os.environ.get("CCFLAGS"): - linux_cross_common_env.Append( - CCFLAGS = os.environ.get("CCFLAGS").split(' '), - ) -if os.environ.get("CXXFLAGS"): - linux_cross_common_env.Append( - CXXFLAGS = os.environ.get("CXXFLAGS").split(' '), - ) -if os.environ.get("LIBFLAGS"): - linux_cross_common_env.Append( - _LIBFLAGS = os.environ.get("LIBFLAGS").split(' '), - ) -if os.environ.get("LINKFLAGS"): - linux_cross_common_env.Prepend( - LINKFLAGS = os.environ.get("LINKFLAGS").split(' '), - ) - -#------------------------------------------------------------------------------- -# L I N U X -- T R A D I T I O N A L -- X 8 6 -# -# Settings that are specific to our desktop Linux x86 targets. -def linux_common_include_traditional(env): - """Include traditional Linux settings into an env based on linux_common.""" - # OpenSSL has infamously poor ABI stability, so that building against one - # version and running against a different one often will not work. Since our - # non-ChromeOS Linux builds are used on many different distros and distro - # versions, this means we can't safely dynamically link to OpenSSL because the - # product would end up being broken on any computer with a different version - # installed. So instead we build it ourself and statically link to it. - env.SetBits('use_static_openssl') - # Enable the optional PulseAudio code if the build machine has the required - # dependency. - # TODO(?): This belongs in linux_common_env, but we can't safely move it there - # yet because pkg-config is not being used properly with ChromeOS builds (see - # TODO below). - env.EnableFeatureWherePackagePresent('have_libpulse', - 'HAVE_LIBPULSE', - 'libpulse') - -def linux_traditional_include_dbg(env): - """Include traditional Linux dbg settings into an env based on the above.""" - # Remove -g set by hammer, which is not what we want (we have set -g3 above). - env.FilterOut(CCFLAGS = ['-g']) - -def linux_traditional_include_opt(env): - """Include traditional Linux opt settings into an env based on the above.""" - # Remove -O2 set by hammer, which is not what we want. - env.FilterOut(CCFLAGS = ['-O2']) - env.Append(CCFLAGS = ['-Os']) - -def gen_linux_nonhermetic(linux_env, type_suffix, desc_suffix): - groups = ['nonhermetic'] - if not linux_env.CrossArch(): - groups = groups + ['nonhermetic-native'] - # The non-hermetic, native-arch dbg build is the default. - dbg_groups = groups + ['default'] - native_desc = ', native ' - # No suffix for native modes. - type_suffix = '' - else: - groups = groups + ['nonhermetic-cross'] - dbg_groups = groups - native_desc = ', cross-built for ' - - linux_dbg_env = linux_env.Clone( - BUILD_TYPE = 'dbg' + type_suffix, - BUILD_TYPE_DESCRIPTION = 'Linux debug build%s%s' % (native_desc, - desc_suffix), - BUILD_GROUPS = dbg_groups, - tools = ['target_debug'], - ) - linux_traditional_include_dbg(linux_dbg_env) - envs.append(linux_dbg_env) - - linux_opt_env = linux_env.Clone( - BUILD_TYPE = 'opt' + type_suffix, - BUILD_TYPE_DESCRIPTION = 'Linux optimized build%s%s' % (native_desc, - desc_suffix), - BUILD_GROUPS = groups, - tools = ['target_optimized'], - ) - linux_traditional_include_opt(linux_opt_env) - envs.append(linux_opt_env) - -linux_nonhermetic_common_env = linux_common_env.Clone() -linux_common_include_traditional(linux_nonhermetic_common_env) - -linux_nonhermetic_x86_32_env = linux_nonhermetic_common_env.Clone() -linux_common_include_x86_32(linux_nonhermetic_x86_32_env) -gen_linux_nonhermetic(linux_nonhermetic_x86_32_env, '32', '32-bit') - -linux_nonhermetic_x86_64_env = linux_nonhermetic_common_env.Clone() -linux_common_include_x86_64(linux_nonhermetic_x86_64_env) -gen_linux_nonhermetic(linux_nonhermetic_x86_64_env, '64', '64-bit') - -def gen_linux_hermetic(linux_env, type_suffix, desc): - groups = ['hermetic'] - - linux_dbg_env = linux_env.Clone( - BUILD_TYPE = 'hermetic-dbg' + type_suffix, - BUILD_TYPE_DESCRIPTION = 'Hermetic %s Linux debug build' % desc, - BUILD_GROUPS = groups, - tools = ['target_debug'], - ) - linux_traditional_include_dbg(linux_dbg_env) - envs.append(linux_dbg_env) - - linux_opt_env = linux_env.Clone( - BUILD_TYPE = 'hermetic-opt' + type_suffix, - BUILD_TYPE_DESCRIPTION = 'Hermetic %s Linux optimized build' % desc, - BUILD_GROUPS = groups, - tools = ['target_optimized'], - ) - linux_traditional_include_opt(linux_opt_env) - envs.append(linux_opt_env) - -linux_hermetic_common_env = linux_cross_common_env.Clone() -linux_common_include_traditional(linux_hermetic_common_env) - -linux_hermetic_x86_32_env = linux_hermetic_common_env.Clone() -linux_common_include_x86_32(linux_hermetic_x86_32_env) -gen_linux_hermetic(linux_hermetic_x86_32_env, '32', '32-bit') - -linux_hermetic_x86_64_env = linux_hermetic_common_env.Clone() -linux_common_include_x86_64(linux_hermetic_x86_64_env) -gen_linux_hermetic(linux_hermetic_x86_64_env, '64', '64-bit') - -#------------------------------------------------------------------------------- -# L I N U X -- C R O S S -- B U I L D -- A R M - -# TODO(noahric): All the following Linux builds are running against a sysroot -# but improperly using the host machine's pkg-config environment. The ChromeOS -# ones should probably be using -# https://cs.corp.google.com/#chrome/src/build/linux/pkg-config-wrapper. - -linux_cross_arm_env = linux_cross_common_env.Clone() -linux_cross_arm_env.Append( - CPPDEFINES = [ - 'NACL_BUILD_ARCH=arm', - 'DISABLE_EFFECTS=1', - ], - CCFLAGS = [ - '-fPIC', - ], -) -DeclareBit('arm', 'ARM build') -linux_cross_arm_env.SetBits('arm') - -# Detect NEON support from the -mfpu build flag. -DeclareBit('arm_neon', 'ARM supporting neon') -if '-mfpu=neon' in linux_cross_arm_env['CFLAGS'] or \ - '-mfpu=neon' in linux_cross_arm_env['CCFLAGS'] or \ - '-mfpu=neon' in linux_cross_arm_env['CXXFLAGS']: - print "Building with ARM NEON support." - linux_cross_arm_env.SetBits('arm_neon') - -# Detect hardfp from the -mfloat-abi build flag -DeclareBit('arm_hardfp', 'ARM supporting hardfp') -if '-mfloat-abi=hard' in linux_cross_arm_env['CFLAGS'] or \ - '-mfloat-abi=hard' in linux_cross_arm_env['CCFLAGS'] or \ - '-mfloat-abi=hard' in linux_cross_arm_env['CXXFLAGS']: - print "Building with hard floating point support." - linux_cross_arm_env.SetBits('arm_hardfp') - -linux_cross_arm_dbg_env = linux_cross_arm_env.Clone( - BUILD_TYPE = 'arm-dbg', - BUILD_TYPE_DESCRIPTION = 'Cross-compiled ARM debug build', - BUILD_GROUPS = ['arm'], - tools = ['target_debug'], -) -envs.append(linux_cross_arm_dbg_env) - -linux_cross_arm_opt_env = linux_cross_arm_env.Clone( - BUILD_TYPE = 'arm-opt', - BUILD_TYPE_DESCRIPTION = 'Cross-compiled ARM optimized build', - BUILD_GROUPS = ['arm'], - tools = ['target_optimized'], -) -envs.append(linux_cross_arm_opt_env) - - - -# Create a group for installers -AddTargetGroup('all_installers', 'installers that can be built') - -# Parse child .scons files -BuildEnvironments(envs) - -# Explicitly set which targets to build when not stated on commandline -Default(None) -# Build the following, which excludes unit test output (ie running them) -# To run unittests, specify the test to run, or run_all_tests. See -h option. -Default(['all_libraries', 'all_programs', 'all_test_programs']) - -# .sln creation code lifted from googleclient/bar/main.scons. Must be after -# the call to BuildEnvironments for all_foo aliases to be defined. -# Run 'hammer --mode=all --vsproj' to generate -DeclareBit('vsproj', 'Generate Visual Studio projects and solution files.') -win_env.SetBitFromOption('vsproj', False) - -if win_env.Bit('vsproj'): - vs_env = win_env.Clone() - vs_env.Append( - COMPONENT_VS_SOURCE_SUFFIXES = [ - '.def', - '.grd', - '.html', - '.idl', - '.mk', - '.txt', - '.py', - '.scons', - '.wxs.template', - ] - ) - - # Source project - p = vs_env.ComponentVSDirProject( - 'flute_source', - ['$MAIN_DIR', - ], - COMPONENT_VS_SOURCE_FOLDERS = [ - # Files are assigned to first matching folder. Folder names of None - # are filters. - (None, '$DESTINATION_ROOT'), - ('flute', '$MAIN_DIR'), - ('google3', '$GOOGLE3'), - ('third_party', '$THIRD_PARTY'), - ], - # Force source project to main dir, so that Visual Studio can find the - # source files corresponding to build errors. - COMPONENT_VS_PROJECT_DIR = '$MAIN_DIR', - ) - vs_env.AlwaysBuild(p) - - # Solution and target projects - s = vs_env.ComponentVSSolution( - # 'libjingle', # Please uncomment this line if you build VS proj files. - ['all_libraries', 'all_programs', 'all_test_programs'], - projects = [p], - ) - - print '***Unfortunately the vsproj creator isn\'t smart enough to ' - print '***automatically get the correct output locations. It is very easy' - print '***though to change it in the properties pane to the following' - print '***$(SolutionDir)/build/<foo>/staging/<bar>.exe' - Default(None) - Default([s]) diff --git a/chromium/third_party/libjingle/source/talk/media/base/audiorenderer.h b/chromium/third_party/libjingle/source/talk/media/base/audiorenderer.h index 273312fab35..155331820f4 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/audiorenderer.h +++ b/chromium/third_party/libjingle/source/talk/media/base/audiorenderer.h @@ -30,18 +30,42 @@ namespace cricket { -// Abstract interface for holding the voice channel IDs. +// Abstract interface for rendering the audio data. class AudioRenderer { public: + class Sink { + public: + // Callback to receive data from the AudioRenderer. + virtual void OnData(const void* audio_data, + int bits_per_sample, + int sample_rate, + int number_of_channels, + int number_of_frames) = 0; + + // Called when the AudioRenderer is going away. + virtual void OnClose() = 0; + + protected: + virtual ~Sink() {} + }; + + // Sets a sink to the AudioRenderer. There can be only one sink connected + // to the renderer at a time. + virtual void SetSink(Sink* sink) {} + // Add the WebRtc VoE channel to the renderer. // For local stream, multiple WebRtc VoE channels can be connected to the // renderer. While for remote stream, only one WebRtc VoE channel can be // connected to the renderer. - virtual void AddChannel(int channel_id) = 0; + // TODO(xians): Remove this interface after Chrome switches to the + // AudioRenderer::Sink interface. + virtual void AddChannel(int channel_id) {} // Remove the WebRtc VoE channel from the renderer. // This method is called when the VoE channel is going away. - virtual void RemoveChannel(int channel_id) = 0; + // TODO(xians): Remove this interface after Chrome switches to the + // AudioRenderer::Sink interface. + virtual void RemoveChannel(int channel_id) {} protected: virtual ~AudioRenderer() {} diff --git a/chromium/third_party/libjingle/source/talk/media/base/codec.cc b/chromium/third_party/libjingle/source/talk/media/base/codec.cc index 2d54c9907a4..6e65560c2ec 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/codec.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/codec.cc @@ -31,6 +31,7 @@ #include <sstream> #include "talk/base/common.h" +#include "talk/base/logging.h" #include "talk/base/stringencode.h" #include "talk/base/stringutils.h" @@ -160,6 +161,55 @@ std::string VideoCodec::ToString() const { return os.str(); } +VideoCodec VideoCodec::CreateRtxCodec(int rtx_payload_type, + int associated_payload_type) { + VideoCodec rtx_codec(rtx_payload_type, kRtxCodecName, 0, 0, 0, 0); + rtx_codec.SetParam(kCodecParamAssociatedPayloadType, associated_payload_type); + return rtx_codec; +} + +VideoCodec::CodecType VideoCodec::GetCodecType() const { + const char* payload_name = name.c_str(); + if (_stricmp(payload_name, kRedCodecName) == 0) { + return CODEC_RED; + } + if (_stricmp(payload_name, kUlpfecCodecName) == 0) { + return CODEC_ULPFEC; + } + if (_stricmp(payload_name, kRtxCodecName) == 0) { + return CODEC_RTX; + } + + return CODEC_VIDEO; +} + +bool VideoCodec::ValidateCodecFormat() const { + if (id < 0 || id > 127) { + LOG(LS_ERROR) << "Codec with invalid payload type: " << ToString(); + return false; + } + if (GetCodecType() != CODEC_VIDEO) { + return true; + } + + // Video validation from here on. + + if (width <= 0 || height <= 0) { + LOG(LS_ERROR) << "Codec with invalid dimensions: " << ToString(); + return false; + } + int min_bitrate = -1; + int max_bitrate = -1; + if (GetParam(kCodecParamMinBitrate, &min_bitrate) && + GetParam(kCodecParamMaxBitrate, &max_bitrate)) { + if (max_bitrate < min_bitrate) { + LOG(LS_ERROR) << "Codec with max < min bitrate: " << ToString(); + return false; + } + } + return true; +} + std::string DataCodec::ToString() const { std::ostringstream os; os << "DataCodec[" << id << ":" << name << "]"; diff --git a/chromium/third_party/libjingle/source/talk/media/base/codec.h b/chromium/third_party/libjingle/source/talk/media/base/codec.h index 56fc975adb2..0e9bf3ca073 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/codec.h +++ b/chromium/third_party/libjingle/source/talk/media/base/codec.h @@ -120,6 +120,8 @@ struct Codec { name = c.name; clockrate = c.clockrate; preference = c.preference; + params = c.params; + feedback_params = c.feedback_params; return *this; } @@ -127,7 +129,9 @@ struct Codec { return this->id == c.id && // id is reserved in objective-c name == c.name && clockrate == c.clockrate && - preference == c.preference; + preference == c.preference && + params == c.params && + feedback_params == c.feedback_params; } bool operator!=(const Codec& c) const { @@ -242,6 +246,22 @@ struct VideoCodec : public Codec { bool operator!=(const VideoCodec& c) const { return !(*this == c); } + + static VideoCodec CreateRtxCodec(int rtx_payload_type, + int associated_payload_type); + + enum CodecType { + CODEC_VIDEO, + CODEC_RED, + CODEC_ULPFEC, + CODEC_RTX, + }; + + CodecType GetCodecType() const; + // Validates a VideoCodec's payload type, dimensions and bitrates etc. If they + // don't make sense (such as max < min bitrate), and error is logged and + // ValidateCodecFormat returns false. + bool ValidateCodecFormat() const; }; struct DataCodec : public Codec { diff --git a/chromium/third_party/libjingle/source/talk/media/base/codec_unittest.cc b/chromium/third_party/libjingle/source/talk/media/base/codec_unittest.cc index f0ffd8f5a1b..35d1ab76133 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/codec_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/codec_unittest.cc @@ -34,12 +34,52 @@ using cricket::DataCodec; using cricket::FeedbackParam; using cricket::VideoCodec; using cricket::VideoEncoderConfig; +using cricket::kCodecParamAssociatedPayloadType; +using cricket::kCodecParamMaxBitrate; +using cricket::kCodecParamMinBitrate; class CodecTest : public testing::Test { public: CodecTest() {} }; +TEST_F(CodecTest, TestCodecOperators) { + Codec c0(96, "D", 1000, 0); + c0.SetParam("a", 1); + + Codec c1 = c0; + EXPECT_TRUE(c1 == c0); + + int param_value0; + int param_value1; + EXPECT_TRUE(c0.GetParam("a", ¶m_value0)); + EXPECT_TRUE(c1.GetParam("a", ¶m_value1)); + EXPECT_EQ(param_value0, param_value1); + + c1.id = 86; + EXPECT_TRUE(c0 != c1); + + c1 = c0; + c1.name = "x"; + EXPECT_TRUE(c0 != c1); + + c1 = c0; + c1.clockrate = 2000; + EXPECT_TRUE(c0 != c1); + + c1 = c0; + c1.preference = 1; + EXPECT_TRUE(c0 != c1); + + c1 = c0; + c1.SetParam("a", 2); + EXPECT_TRUE(c0 != c1); + + Codec c5; + Codec c6(0, "", 0, 0); + EXPECT_TRUE(c5 == c6); +} + TEST_F(CodecTest, TestAudioCodecOperators) { AudioCodec c0(96, "A", 44100, 20000, 2, 3); AudioCodec c1(95, "A", 44100, 20000, 2, 3); @@ -238,23 +278,6 @@ TEST_F(CodecTest, TestDataCodecMatches) { EXPECT_FALSE(c1.Matches(DataCodec(95, "D", 0))); } -TEST_F(CodecTest, TestDataCodecOperators) { - DataCodec c0(96, "D", 3); - DataCodec c1(95, "D", 3); - DataCodec c2(96, "x", 3); - DataCodec c3(96, "D", 1); - EXPECT_TRUE(c0 != c1); - EXPECT_TRUE(c0 != c2); - EXPECT_TRUE(c0 != c3); - - DataCodec c4; - DataCodec c5(0, "", 0); - DataCodec c6 = c0; - EXPECT_TRUE(c5 == c4); - EXPECT_TRUE(c6 != c4); - EXPECT_TRUE(c6 == c0); -} - TEST_F(CodecTest, TestSetParamAndGetParam) { AudioCodec codec; codec.SetParam("a", "1"); @@ -292,3 +315,81 @@ TEST_F(CodecTest, TestIntersectFeedbackParams) { EXPECT_FALSE(c1.HasFeedbackParam(b2)); EXPECT_FALSE(c1.HasFeedbackParam(c3)); } + +TEST_F(CodecTest, TestGetCodecType) { + // Codec type comparison should be case insenstive on names. + const VideoCodec codec(96, "V", 320, 200, 30, 3); + const VideoCodec rtx_codec(96, "rTx", 320, 200, 30, 3); + const VideoCodec ulpfec_codec(96, "ulpFeC", 320, 200, 30, 3); + const VideoCodec red_codec(96, "ReD", 320, 200, 30, 3); + EXPECT_EQ(VideoCodec::CODEC_VIDEO, codec.GetCodecType()); + EXPECT_EQ(VideoCodec::CODEC_RTX, rtx_codec.GetCodecType()); + EXPECT_EQ(VideoCodec::CODEC_ULPFEC, ulpfec_codec.GetCodecType()); + EXPECT_EQ(VideoCodec::CODEC_RED, red_codec.GetCodecType()); +} + +TEST_F(CodecTest, TestCreateRtxCodec) { + VideoCodec rtx_codec = VideoCodec::CreateRtxCodec(96, 120); + EXPECT_EQ(96, rtx_codec.id); + EXPECT_EQ(VideoCodec::CODEC_RTX, rtx_codec.GetCodecType()); + int associated_payload_type; + ASSERT_TRUE(rtx_codec.GetParam(kCodecParamAssociatedPayloadType, + &associated_payload_type)); + EXPECT_EQ(120, associated_payload_type); +} + +TEST_F(CodecTest, TestValidateCodecFormat) { + const VideoCodec codec(96, "V", 320, 200, 30, 3); + ASSERT_TRUE(codec.ValidateCodecFormat()); + + // Accept 0-127 as payload types. + VideoCodec low_payload_type = codec; + low_payload_type.id = 0; + VideoCodec high_payload_type = codec; + high_payload_type.id = 127; + ASSERT_TRUE(low_payload_type.ValidateCodecFormat()); + EXPECT_TRUE(high_payload_type.ValidateCodecFormat()); + + // Reject negative payloads. + VideoCodec negative_payload_type = codec; + negative_payload_type.id = -1; + EXPECT_FALSE(negative_payload_type.ValidateCodecFormat()); + + // Reject too-high payloads. + VideoCodec too_high_payload_type = codec; + too_high_payload_type.id = 128; + EXPECT_FALSE(too_high_payload_type.ValidateCodecFormat()); + + // Reject zero-width codecs. + VideoCodec zero_width = codec; + zero_width.width = 0; + EXPECT_FALSE(zero_width.ValidateCodecFormat()); + + // Reject zero-height codecs. + VideoCodec zero_height = codec; + zero_height.height = 0; + EXPECT_FALSE(zero_height.ValidateCodecFormat()); + + // Accept non-video codecs with zero dimensions. + VideoCodec zero_width_rtx_codec = VideoCodec::CreateRtxCodec(96, 120); + zero_width_rtx_codec.width = 0; + EXPECT_TRUE(zero_width_rtx_codec.ValidateCodecFormat()); + + // Reject codecs with min bitrate > max bitrate. + VideoCodec incorrect_bitrates = codec; + incorrect_bitrates.params[kCodecParamMinBitrate] = "100"; + incorrect_bitrates.params[kCodecParamMaxBitrate] = "80"; + EXPECT_FALSE(incorrect_bitrates.ValidateCodecFormat()); + + // Accept min bitrate == max bitrate. + VideoCodec equal_bitrates = codec; + equal_bitrates.params[kCodecParamMinBitrate] = "100"; + equal_bitrates.params[kCodecParamMaxBitrate] = "100"; + EXPECT_TRUE(equal_bitrates.ValidateCodecFormat()); + + // Accept min bitrate < max bitrate. + VideoCodec different_bitrates = codec; + different_bitrates.params[kCodecParamMinBitrate] = "99"; + different_bitrates.params[kCodecParamMaxBitrate] = "100"; + EXPECT_TRUE(different_bitrates.ValidateCodecFormat()); +} diff --git a/chromium/third_party/libjingle/source/talk/media/base/constants.cc b/chromium/third_party/libjingle/source/talk/media/base/constants.cc index 9162ce4fd12..cd10ef75f3c 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/constants.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/constants.cc @@ -40,6 +40,8 @@ const float kLowSystemCpuThreshold = 0.65f; const float kProcessCpuThreshold = 0.10f; const char kRtxCodecName[] = "rtx"; +const char kRedCodecName[] = "red"; +const char kUlpfecCodecName[] = "ulpfec"; // RTP payload type is in the 0-127 range. Use 128 to indicate "all" payload // types. @@ -78,12 +80,14 @@ const int kPreferredStereo = 0; const int kPreferredUseInbandFec = 0; const char kRtcpFbParamNack[] = "nack"; +const char kRtcpFbNackParamPli[] = "pli"; const char kRtcpFbParamRemb[] = "goog-remb"; const char kRtcpFbParamCcm[] = "ccm"; const char kRtcpFbCcmParamFir[] = "fir"; const char kCodecParamMaxBitrate[] = "x-google-max-bitrate"; const char kCodecParamMinBitrate[] = "x-google-min-bitrate"; +const char kCodecParamStartBitrate[] = "x-google-start-bitrate"; const char kCodecParamMaxQuantization[] = "x-google-max-quantization"; const char kCodecParamPort[] = "x-google-port"; @@ -95,4 +99,20 @@ const char kGoogleSctpDataCodecName[] = "google-sctp-data"; const char kComfortNoiseCodecName[] = "CN"; +const int kRtpAudioLevelHeaderExtensionDefaultId = 1; +const char kRtpAudioLevelHeaderExtension[] = + "urn:ietf:params:rtp-hdrext:ssrc-audio-level"; + +const int kRtpTimestampOffsetHeaderExtensionDefaultId = 2; +const char kRtpTimestampOffsetHeaderExtension[] = + "urn:ietf:params:rtp-hdrext:toffset"; + +const int kRtpAbsoluteSenderTimeHeaderExtensionDefaultId = 3; +const char kRtpAbsoluteSenderTimeHeaderExtension[] = + "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"; + +const int kNumDefaultUnsignalledVideoRecvStreams = 0; + + } // namespace cricket + diff --git a/chromium/third_party/libjingle/source/talk/media/base/constants.h b/chromium/third_party/libjingle/source/talk/media/base/constants.h index b80c0fc106c..dc5405d1729 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/constants.h +++ b/chromium/third_party/libjingle/source/talk/media/base/constants.h @@ -44,6 +44,9 @@ extern const float kLowSystemCpuThreshold; extern const float kProcessCpuThreshold; extern const char kRtxCodecName[]; +extern const char kRedCodecName[]; +extern const char kUlpfecCodecName[]; + // Codec parameters extern const int kWildcardPayloadType; @@ -89,6 +92,7 @@ extern const int kPreferredUseInbandFec; // rtcp-fb messages according to RFC 4585 extern const char kRtcpFbParamNack[]; +extern const char kRtcpFbNackParamPli[]; // rtcp-fb messages according to // http://tools.ietf.org/html/draft-alvestrand-rmcat-remb-00 extern const char kRtcpFbParamRemb[]; @@ -98,6 +102,7 @@ extern const char kRtcpFbCcmParamFir[]; // Google specific parameters extern const char kCodecParamMaxBitrate[]; extern const char kCodecParamMinBitrate[]; +extern const char kCodecParamStartBitrate[]; extern const char kCodecParamMaxQuantization[]; extern const char kCodecParamPort[]; @@ -115,6 +120,23 @@ extern const char kGoogleSctpDataCodecName[]; extern const char kComfortNoiseCodecName[]; +// Extension header for audio levels, as defined in +// http://tools.ietf.org/html/draft-ietf-avtext-client-to-mixer-audio-level-03 +extern const int kRtpAudioLevelHeaderExtensionDefaultId; +extern const char kRtpAudioLevelHeaderExtension[]; + +// Extension header for RTP timestamp offset, see RFC 5450 for details: +// http://tools.ietf.org/html/rfc5450 +extern const int kRtpTimestampOffsetHeaderExtensionDefaultId; +extern const char kRtpTimestampOffsetHeaderExtension[]; + +// Extension header for absolute send time, see url for details: +// http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time +extern const int kRtpAbsoluteSenderTimeHeaderExtensionDefaultId; +extern const char kRtpAbsoluteSenderTimeHeaderExtension[]; + +extern const int kNumDefaultUnsignalledVideoRecvStreams; } // namespace cricket #endif // TALK_MEDIA_BASE_CONSTANTS_H_ + diff --git a/chromium/third_party/libjingle/source/talk/media/base/fakemediaengine.h b/chromium/third_party/libjingle/source/talk/media/base/fakemediaengine.h index 1a4e8aba897..27fbeb094e5 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/fakemediaengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/fakemediaengine.h @@ -281,7 +281,8 @@ class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> { } return set_sending(flag != SEND_NOTHING); } - virtual bool SetSendBandwidth(bool autobw, int bps) { return true; } + virtual bool SetStartSendBandwidth(int bps) { return true; } + virtual bool SetMaxSendBandwidth(int bps) { return true; } virtual bool AddRecvStream(const StreamParams& sp) { if (!RtpHelper<VoiceMediaChannel>::AddRecvStream(sp)) return false; @@ -315,17 +316,18 @@ class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> { return true; } virtual bool SetLocalRenderer(uint32 ssrc, AudioRenderer* renderer) { - std::map<uint32, AudioRenderer*>::iterator it = local_renderers_.find(ssrc); + std::map<uint32, VoiceChannelAudioSink*>::iterator it = + local_renderers_.find(ssrc); if (renderer) { if (it != local_renderers_.end()) { - ASSERT(it->second == renderer); + ASSERT(it->second->renderer() == renderer); } else { - local_renderers_.insert(std::make_pair(ssrc, renderer)); - renderer->AddChannel(0); + local_renderers_.insert(std::make_pair( + ssrc, new VoiceChannelAudioSink(renderer))); } } else { if (it != local_renderers_.end()) { - it->second->RemoveChannel(0); + delete it->second; local_renderers_.erase(it); } else { return false; @@ -418,6 +420,34 @@ class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> { double left, right; }; + class VoiceChannelAudioSink : public AudioRenderer::Sink { + public: + explicit VoiceChannelAudioSink(AudioRenderer* renderer) + : renderer_(renderer) { + renderer_->AddChannel(0); + renderer_->SetSink(this); + } + virtual ~VoiceChannelAudioSink() { + if (renderer_) { + renderer_->RemoveChannel(0); + renderer_->SetSink(NULL); + } + } + virtual void OnData(const void* audio_data, + int bits_per_sample, + int sample_rate, + int number_of_channels, + int number_of_frames) OVERRIDE {} + virtual void OnClose() OVERRIDE { + renderer_ = NULL; + } + AudioRenderer* renderer() const { return renderer_; } + + private: + AudioRenderer* renderer_; + }; + + FakeVoiceEngine* engine_; std::vector<AudioCodec> recv_codecs_; std::vector<AudioCodec> send_codecs_; @@ -429,7 +459,7 @@ class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> { bool ringback_tone_loop_; int time_since_last_typing_; AudioOptions options_; - std::map<uint32, AudioRenderer*> local_renderers_; + std::map<uint32, VoiceChannelAudioSink*> local_renderers_; std::map<uint32, AudioRenderer*> remote_renderers_; }; @@ -446,7 +476,9 @@ class FakeVideoMediaChannel : public RtpHelper<VideoMediaChannel> { explicit FakeVideoMediaChannel(FakeVideoEngine* engine) : engine_(engine), sent_intra_frame_(false), - requested_intra_frame_(false) {} + requested_intra_frame_(false), + start_bps_(-1), + max_bps_(-1) {} ~FakeVideoMediaChannel(); const std::vector<VideoCodec>& recv_codecs() const { return recv_codecs_; } @@ -457,6 +489,8 @@ class FakeVideoMediaChannel : public RtpHelper<VideoMediaChannel> { const std::map<uint32, VideoRenderer*>& renderers() const { return renderers_; } + int start_bps() const { return start_bps_; } + int max_bps() const { return max_bps_; } bool GetSendStreamFormat(uint32 ssrc, VideoFormat* format) { if (send_formats_.find(ssrc) == send_formats_.end()) { return false; @@ -534,7 +568,14 @@ class FakeVideoMediaChannel : public RtpHelper<VideoMediaChannel> { bool HasCapturer(uint32 ssrc) const { return capturers_.find(ssrc) != capturers_.end(); } - virtual bool SetSendBandwidth(bool autobw, int bps) { return true; } + virtual bool SetStartSendBandwidth(int bps) { + start_bps_ = bps; + return true; + } + virtual bool SetMaxSendBandwidth(int bps) { + max_bps_ = bps; + return true; + } virtual bool AddRecvStream(const StreamParams& sp) { if (!RtpHelper<VideoMediaChannel>::AddRecvStream(sp)) return false; @@ -548,7 +589,8 @@ class FakeVideoMediaChannel : public RtpHelper<VideoMediaChannel> { return true; } - virtual bool GetStats(VideoMediaInfo* info) { return false; } + virtual bool GetStats(const StatsOptions& options, + VideoMediaInfo* info) { return false; } virtual bool SendIntraFrame() { sent_intra_frame_ = true; return true; @@ -591,6 +633,8 @@ class FakeVideoMediaChannel : public RtpHelper<VideoMediaChannel> { bool sent_intra_frame_; bool requested_intra_frame_; VideoOptions options_; + int start_bps_; + int max_bps_; }; class FakeSoundclipMedia : public SoundclipMedia { @@ -601,12 +645,11 @@ class FakeSoundclipMedia : public SoundclipMedia { class FakeDataMediaChannel : public RtpHelper<DataMediaChannel> { public: explicit FakeDataMediaChannel(void* unused) - : auto_bandwidth_(false), send_blocked_(false), max_bps_(-1) {} + : send_blocked_(false), max_bps_(-1) {} ~FakeDataMediaChannel() {} const std::vector<DataCodec>& recv_codecs() const { return recv_codecs_; } const std::vector<DataCodec>& send_codecs() const { return send_codecs_; } const std::vector<DataCodec>& codecs() const { return send_codecs(); } - bool auto_bandwidth() const { return auto_bandwidth_; } int max_bps() const { return max_bps_; } virtual bool SetRecvCodecs(const std::vector<DataCodec>& codecs) { @@ -630,8 +673,8 @@ class FakeDataMediaChannel : public RtpHelper<DataMediaChannel> { set_playout(receive); return true; } - virtual bool SetSendBandwidth(bool autobw, int bps) { - auto_bandwidth_ = autobw; + virtual bool SetStartSendBandwidth(int bps) { return true; } + virtual bool SetMaxSendBandwidth(int bps) { max_bps_ = bps; return true; } @@ -669,7 +712,6 @@ class FakeDataMediaChannel : public RtpHelper<DataMediaChannel> { std::vector<DataCodec> send_codecs_; SendDataParams last_sent_data_params_; std::string last_sent_data_; - bool auto_bandwidth_; bool send_blocked_; int max_bps_; }; @@ -695,6 +737,10 @@ class FakeBaseEngine { const std::vector<RtpHeaderExtension>& rtp_header_extensions() const { return rtp_header_extensions_; } + void set_rtp_header_extensions( + const std::vector<RtpHeaderExtension>& extensions) { + rtp_header_extensions_ = extensions; + } protected: int loglevel_; @@ -778,6 +824,8 @@ class FakeVoiceEngine : public FakeBaseEngine { bool SetLocalMonitor(bool enable) { return true; } + bool StartAecDump(talk_base::PlatformFile file) { return false; } + bool RegisterProcessor(uint32 ssrc, VoiceProcessor* voice_processor, MediaProcessorDirection direction) { if (direction == MPD_RX) { @@ -915,18 +963,25 @@ class FakeMediaEngine : } virtual ~FakeMediaEngine() {} - virtual void SetAudioCodecs(const std::vector<AudioCodec> codecs) { + void SetAudioCodecs(const std::vector<AudioCodec>& codecs) { voice_.SetCodecs(codecs); } - - virtual void SetVideoCodecs(const std::vector<VideoCodec> codecs) { + void SetVideoCodecs(const std::vector<VideoCodec>& codecs) { video_.SetCodecs(codecs); } + void SetAudioRtpHeaderExtensions( + const std::vector<RtpHeaderExtension>& extensions) { + voice_.set_rtp_header_extensions(extensions); + } + void SetVideoRtpHeaderExtensions( + const std::vector<RtpHeaderExtension>& extensions) { + video_.set_rtp_header_extensions(extensions); + } + FakeVoiceMediaChannel* GetVoiceChannel(size_t index) { return voice_.GetChannel(index); } - FakeVideoMediaChannel* GetVideoChannel(size_t index) { return video_.GetChannel(index); } diff --git a/chromium/third_party/libjingle/source/talk/media/base/fakevideorenderer.h b/chromium/third_party/libjingle/source/talk/media/base/fakevideorenderer.h index 362e592951e..cab77dda720 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/fakevideorenderer.h +++ b/chromium/third_party/libjingle/source/talk/media/base/fakevideorenderer.h @@ -48,6 +48,7 @@ class FakeVideoRenderer : public VideoRenderer { } virtual bool SetSize(int width, int height, int reserved) { + talk_base::CritScope cs(&crit_); width_ = width; height_ = height; ++num_set_sizes_; @@ -56,6 +57,7 @@ class FakeVideoRenderer : public VideoRenderer { } virtual bool RenderFrame(const VideoFrame* frame) { + talk_base::CritScope cs(&crit_); // TODO(zhurunz) Check with VP8 team to see if we can remove this // tolerance on Y values. black_frame_ = CheckFrameColorYuv(6, 48, 128, 128, 128, 128, frame); @@ -79,11 +81,26 @@ class FakeVideoRenderer : public VideoRenderer { } int errors() const { return errors_; } - int width() const { return width_; } - int height() const { return height_; } - int num_set_sizes() const { return num_set_sizes_; } - int num_rendered_frames() const { return num_rendered_frames_; } - bool black_frame() const { return black_frame_; } + int width() const { + talk_base::CritScope cs(&crit_); + return width_; + } + int height() const { + talk_base::CritScope cs(&crit_); + return height_; + } + int num_set_sizes() const { + talk_base::CritScope cs(&crit_); + return num_set_sizes_; + } + int num_rendered_frames() const { + talk_base::CritScope cs(&crit_); + return num_rendered_frames_; + } + bool black_frame() const { + talk_base::CritScope cs(&crit_); + return black_frame_; + } sigslot::signal3<int, int, int> SignalSetSize; sigslot::signal1<const VideoFrame*> SignalRenderFrame; @@ -143,6 +160,7 @@ class FakeVideoRenderer : public VideoRenderer { int num_set_sizes_; int num_rendered_frames_; bool black_frame_; + mutable talk_base::CriticalSection crit_; }; } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.cc b/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.cc index dfec607d286..e8c356e4fb5 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.cc @@ -25,7 +25,7 @@ #include "talk/media/base/filemediaengine.h" -#include <climits> +#include <limits.h> #include "talk/base/buffer.h" #include "talk/base/event.h" diff --git a/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.h b/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.h index dfdb037ab38..6656cdfa181 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.h @@ -133,6 +133,7 @@ class FileMediaEngine : public MediaEngineInterface { virtual bool FindVideoCodec(const VideoCodec& codec) { return true; } virtual void SetVoiceLogging(int min_sev, const char* filter) {} virtual void SetVideoLogging(int min_sev, const char* filter) {} + virtual bool StartAecDump(talk_base::PlatformFile) { return false; } virtual bool RegisterVideoProcessor(VideoProcessor* processor) { return true; @@ -242,7 +243,8 @@ class FileVoiceChannel : public VoiceMediaChannel { virtual bool AddRecvStream(const StreamParams& sp) { return true; } virtual bool RemoveRecvStream(uint32 ssrc) { return true; } virtual bool MuteStream(uint32 ssrc, bool on) { return false; } - virtual bool SetSendBandwidth(bool autobw, int bps) { return true; } + virtual bool SetStartSendBandwidth(int bps) { return true; } + virtual bool SetMaxSendBandwidth(int bps) { return true; } virtual bool SetOptions(const AudioOptions& options) { options_ = options; return true; @@ -295,7 +297,9 @@ class FileVideoChannel : public VideoMediaChannel { virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer) { return false; } - virtual bool GetStats(VideoMediaInfo* info) { return true; } + virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info) { + return true; + } virtual bool SendIntraFrame() { return false; } virtual bool RequestIntraFrame() { return false; } @@ -310,7 +314,8 @@ class FileVideoChannel : public VideoMediaChannel { virtual bool AddRecvStream(const StreamParams& sp) { return true; } virtual bool RemoveRecvStream(uint32 ssrc) { return true; } virtual bool MuteStream(uint32 ssrc, bool on) { return false; } - virtual bool SetSendBandwidth(bool autobw, int bps) { return true; } + virtual bool SetStartSendBandwidth(int bps) { return true; } + virtual bool SetMaxSendBandwidth(int bps) { return true; } virtual bool SetOptions(const VideoOptions& options) { options_ = options; return true; diff --git a/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.cc b/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.cc index 6863311f2dd..8e992f0abfe 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.cc @@ -183,9 +183,12 @@ bool HybridVideoMediaChannel::SetSendRtpHeaderExtensions( active_channel_->SetSendRtpHeaderExtensions(extensions); } -bool HybridVideoMediaChannel::SetSendBandwidth(bool autobw, int bps) { - return active_channel_ && - active_channel_->SetSendBandwidth(autobw, bps); +bool HybridVideoMediaChannel::SetStartSendBandwidth(int bps) { + return active_channel_ && active_channel_->SetStartSendBandwidth(bps); +} + +bool HybridVideoMediaChannel::SetMaxSendBandwidth(int bps) { + return active_channel_ && active_channel_->SetMaxSendBandwidth(bps); } bool HybridVideoMediaChannel::SetSend(bool send) { @@ -270,10 +273,11 @@ bool HybridVideoMediaChannel::RequestIntraFrame() { active_channel_->RequestIntraFrame(); } -bool HybridVideoMediaChannel::GetStats(VideoMediaInfo* info) { +bool HybridVideoMediaChannel::GetStats( + const StatsOptions& options, VideoMediaInfo* info) { // TODO(juberti): Ensure that returning no stats until SetSendCodecs is OK. return active_channel_ && - active_channel_->GetStats(info); + active_channel_->GetStats(options, info); } void HybridVideoMediaChannel::OnPacketReceived( diff --git a/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.h b/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.h index a49a1aa2c88..8cfb884f12f 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.h @@ -75,7 +75,8 @@ class HybridVideoMediaChannel : public VideoMediaChannel { virtual bool SetSendStreamFormat(uint32 ssrc, const VideoFormat& format); virtual bool SetSendRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions); - virtual bool SetSendBandwidth(bool autobw, int bps); + virtual bool SetStartSendBandwidth(int bps); + virtual bool SetMaxSendBandwidth(int bps); virtual bool SetSend(bool send); virtual bool AddRecvStream(const StreamParams& sp); @@ -85,7 +86,7 @@ class HybridVideoMediaChannel : public VideoMediaChannel { virtual bool SendIntraFrame(); virtual bool RequestIntraFrame(); - virtual bool GetStats(VideoMediaInfo* info); + virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info); virtual void OnPacketReceived(talk_base::Buffer* packet, const talk_base::PacketTime& packet_time); diff --git a/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine_unittest.cc b/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine_unittest.cc new file mode 100644 index 00000000000..aa9d4ac2070 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine_unittest.cc @@ -0,0 +1,486 @@ +/* + * libjingle + * Copyright 2004 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/base/gunit.h" +#include "talk/media/base/fakemediaengine.h" +#include "talk/media/base/fakenetworkinterface.h" +#include "talk/media/base/fakevideocapturer.h" +#include "talk/media/base/hybridvideoengine.h" +#include "talk/media/base/mediachannel.h" +#include "talk/media/base/testutils.h" + +static const cricket::VideoCodec kGenericCodec(97, "Generic", 640, 360, 30, 0); +static const cricket::VideoCodec kVp8Codec(100, "VP8", 640, 360, 30, 0); +static const cricket::VideoCodec kCodecsVp8Only[] = { kVp8Codec }; +static const cricket::VideoCodec kCodecsGenericOnly[] = { kGenericCodec }; +static const cricket::VideoCodec kCodecsVp8First[] = { kVp8Codec, + kGenericCodec }; +static const cricket::VideoCodec kCodecsGenericFirst[] = { kGenericCodec, + kVp8Codec }; + +using cricket::StreamParams; + +class FakeVp8VideoEngine : public cricket::FakeVideoEngine { + public: + FakeVp8VideoEngine() { + SetCodecs(MAKE_VECTOR(kCodecsVp8Only)); + } +}; +class FakeGenericVideoEngine : public cricket::FakeVideoEngine { + public: + FakeGenericVideoEngine() { + SetCodecs(MAKE_VECTOR(kCodecsGenericOnly)); + } + + // For testing purposes, mimic the behavior of a media engine that throws out + // resolutions that don't match the codec list. A width or height of 0 + // trivially will never match the codec list, so this is sufficient for + // testing the case we want (0x0). + virtual bool FindCodec(const cricket::VideoCodec& codec) { + if (codec.width == 0 || codec.height == 0) { + return false; + } else { + return cricket::FakeVideoEngine::FindCodec(codec); + } + } +}; +class HybridVideoEngineForTest : public cricket::HybridVideoEngine< + FakeVp8VideoEngine, FakeGenericVideoEngine> { + public: + HybridVideoEngineForTest() + : + num_ch1_send_on_(0), + num_ch1_send_off_(0), + send_width_(0), + send_height_(0) { } + cricket::FakeVideoEngine* sub_engine1() { return &video1_; } + cricket::FakeVideoEngine* sub_engine2() { return &video2_; } + + // From base class HybridVideoEngine. + void OnSendChange1(cricket::VideoMediaChannel* channel1, bool send) { + if (send) { + ++num_ch1_send_on_; + } else { + ++num_ch1_send_off_; + } + } + // From base class HybridVideoEngine + void OnNewSendResolution(int width, int height) { + send_width_ = width; + send_height_ = height; + } + + int num_ch1_send_on() const { return num_ch1_send_on_; } + int num_ch1_send_off() const { return num_ch1_send_off_; } + + int send_width() const { return send_width_; } + int send_height() const { return send_height_; } + + private: + int num_ch1_send_on_; + int num_ch1_send_off_; + + int send_width_; + int send_height_; +}; + +class HybridVideoEngineTest : public testing::Test { + public: + HybridVideoEngineTest() : sub_channel1_(NULL), sub_channel2_(NULL) { + } + ~HybridVideoEngineTest() { + engine_.Terminate(); + } + bool SetupEngine() { + bool result = engine_.Init(talk_base::Thread::Current()); + if (result) { + channel_.reset(engine_.CreateChannel(NULL)); + result = (channel_.get() != NULL); + sub_channel1_ = engine_.sub_engine1()->GetChannel(0); + sub_channel2_ = engine_.sub_engine2()->GetChannel(0); + } + return result; + } + bool SetupRenderAndAddStream(const StreamParams& sp) { + if (!SetupEngine()) + return false; + channel_->SetInterface(transport_.get()); + return channel_->SetRecvCodecs(engine_.codecs()) && + channel_->AddSendStream(sp) && + channel_->SetRender(true); + } + void DeliverPacket(const void* data, int len) { + talk_base::Buffer packet(data, len); + channel_->OnPacketReceived(&packet, talk_base::CreatePacketTime(0)); + } + void DeliverRtcp(const void* data, int len) { + talk_base::Buffer packet(data, len); + channel_->OnRtcpReceived(&packet, talk_base::CreatePacketTime(0)); + } + + protected: + void TestSetSendCodecs(cricket::FakeVideoEngine* sub_engine, + const std::vector<cricket::VideoCodec>& codecs) { + EXPECT_TRUE(SetupRenderAndAddStream(StreamParams::CreateLegacy(1234))); + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + cricket::FakeVideoMediaChannel* sub_channel = sub_engine->GetChannel(0); + ASSERT_EQ(1U, sub_channel->send_codecs().size()); + EXPECT_EQ(codecs[0], sub_channel->send_codecs()[0]); + EXPECT_TRUE(channel_->SetSend(true)); + EXPECT_TRUE(sub_channel->sending()); + } + void TestSetSendBandwidth(cricket::FakeVideoEngine* sub_engine, + const std::vector<cricket::VideoCodec>& codecs, + int start_bitrate, + int max_bitrate) { + EXPECT_TRUE(SetupRenderAndAddStream(StreamParams::CreateLegacy(1234))); + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->SetStartSendBandwidth(start_bitrate)); + EXPECT_TRUE(channel_->SetMaxSendBandwidth(max_bitrate)); + cricket::FakeVideoMediaChannel* sub_channel = sub_engine->GetChannel(0); + EXPECT_EQ(start_bitrate, sub_channel->start_bps()); + EXPECT_EQ(max_bitrate, sub_channel->max_bps()); + } + HybridVideoEngineForTest engine_; + talk_base::scoped_ptr<cricket::HybridVideoMediaChannel> channel_; + talk_base::scoped_ptr<cricket::FakeNetworkInterface> transport_; + cricket::FakeVideoMediaChannel* sub_channel1_; + cricket::FakeVideoMediaChannel* sub_channel2_; +}; + +TEST_F(HybridVideoEngineTest, StartupShutdown) { + EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); + engine_.Terminate(); +} + +// Tests that SetDefaultVideoEncoderConfig passes down to both engines. +TEST_F(HybridVideoEngineTest, SetDefaultVideoEncoderConfig) { + cricket::VideoEncoderConfig config( + cricket::VideoCodec(105, "", 640, 400, 30, 0), 1, 2); + EXPECT_TRUE(engine_.SetDefaultEncoderConfig(config)); + + cricket::VideoEncoderConfig config_1 = config; + config_1.max_codec.name = kCodecsVp8Only[0].name; + EXPECT_EQ(config_1, engine_.sub_engine1()->default_encoder_config()); + + cricket::VideoEncoderConfig config_2 = config; + config_2.max_codec.name = kCodecsGenericOnly[0].name; + EXPECT_EQ(config_2, engine_.sub_engine2()->default_encoder_config()); +} + +// Tests that GetDefaultVideoEncoderConfig picks a meaningful encoder config +// based on the underlying engine config and then after a call to +// SetDefaultEncoderConfig on the hybrid engine. +TEST_F(HybridVideoEngineTest, SetDefaultVideoEncoderConfigDefaultValue) { + cricket::VideoEncoderConfig blank_config; + cricket::VideoEncoderConfig meaningful_config1( + cricket::VideoCodec(111, "abcd", 320, 240, 30, 0), 1, 2); + cricket::VideoEncoderConfig meaningful_config2( + cricket::VideoCodec(111, "abcd", 1280, 720, 30, 0), 1, 2); + cricket::VideoEncoderConfig meaningful_config3( + cricket::VideoCodec(111, "abcd", 640, 360, 30, 0), 1, 2); + engine_.sub_engine1()->SetDefaultEncoderConfig(blank_config); + engine_.sub_engine2()->SetDefaultEncoderConfig(blank_config); + EXPECT_EQ(blank_config, engine_.GetDefaultEncoderConfig()); + + engine_.sub_engine2()->SetDefaultEncoderConfig(meaningful_config2); + EXPECT_EQ(meaningful_config2, engine_.GetDefaultEncoderConfig()); + + engine_.sub_engine1()->SetDefaultEncoderConfig(meaningful_config1); + EXPECT_EQ(meaningful_config1, engine_.GetDefaultEncoderConfig()); + + EXPECT_TRUE(engine_.SetDefaultEncoderConfig(meaningful_config3)); + // The overall config should now match, though the codec name will have been + // rewritten for the first media engine. + meaningful_config3.max_codec.name = kCodecsVp8Only[0].name; + EXPECT_EQ(meaningful_config3, engine_.GetDefaultEncoderConfig()); +} + +// Tests that our engine has the right codecs in the right order. +TEST_F(HybridVideoEngineTest, CheckCodecs) { + const std::vector<cricket::VideoCodec>& c = engine_.codecs(); + ASSERT_EQ(2U, c.size()); + EXPECT_EQ(kVp8Codec, c[0]); + EXPECT_EQ(kGenericCodec, c[1]); +} + +// Tests that our engine has the right caps. +TEST_F(HybridVideoEngineTest, CheckCaps) { + EXPECT_EQ(cricket::VIDEO_SEND | cricket::VIDEO_RECV, + engine_.GetCapabilities()); +} + +// Tests that we can create and destroy a channel. +TEST_F(HybridVideoEngineTest, CreateChannel) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(sub_channel1_ != NULL); + EXPECT_TRUE(sub_channel2_ != NULL); +} + +// Tests that we properly handle failures in CreateChannel. +TEST_F(HybridVideoEngineTest, CreateChannelFail) { + engine_.sub_engine1()->set_fail_create_channel(true); + EXPECT_FALSE(SetupEngine()); + EXPECT_TRUE(channel_.get() == NULL); + EXPECT_TRUE(sub_channel1_ == NULL); + EXPECT_TRUE(sub_channel2_ == NULL); + engine_.sub_engine1()->set_fail_create_channel(false); + engine_.sub_engine2()->set_fail_create_channel(true); + EXPECT_FALSE(SetupEngine()); + EXPECT_TRUE(channel_.get() == NULL); + EXPECT_TRUE(sub_channel1_ == NULL); + EXPECT_TRUE(sub_channel2_ == NULL); +} + +// Test that we set our inbound codecs and settings properly. +TEST_F(HybridVideoEngineTest, SetLocalDescription) { + EXPECT_TRUE(SetupEngine()); + channel_->SetInterface(transport_.get()); + EXPECT_TRUE(channel_->SetRecvCodecs(engine_.codecs())); + ASSERT_EQ(1U, sub_channel1_->recv_codecs().size()); + ASSERT_EQ(1U, sub_channel2_->recv_codecs().size()); + EXPECT_EQ(kVp8Codec, sub_channel1_->recv_codecs()[0]); + EXPECT_EQ(kGenericCodec, sub_channel2_->recv_codecs()[0]); + StreamParams stream; + stream.id = "TestStream"; + stream.ssrcs.push_back(1234); + stream.cname = "5678"; + EXPECT_TRUE(channel_->AddSendStream(stream)); + EXPECT_EQ(1234U, sub_channel1_->send_ssrc()); + EXPECT_EQ(1234U, sub_channel2_->send_ssrc()); + EXPECT_EQ("5678", sub_channel1_->rtcp_cname()); + EXPECT_EQ("5678", sub_channel2_->rtcp_cname()); + EXPECT_TRUE(channel_->SetRender(true)); + // We've called SetRender, so we should be playing out, but not yet sending. + EXPECT_TRUE(sub_channel1_->playout()); + EXPECT_TRUE(sub_channel2_->playout()); + EXPECT_FALSE(sub_channel1_->sending()); + EXPECT_FALSE(sub_channel2_->sending()); + // We may get SetSend(false) calls during call setup. + // Since this causes no change in state, they should no-op and return true. + EXPECT_TRUE(channel_->SetSend(false)); + EXPECT_FALSE(sub_channel1_->sending()); + EXPECT_FALSE(sub_channel2_->sending()); +} + +TEST_F(HybridVideoEngineTest, OnNewSendResolution) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->SetSendCodecs(MAKE_VECTOR(kCodecsVp8First))); + EXPECT_EQ(640, engine_.send_width()); + EXPECT_EQ(360, engine_.send_height()); +} + +// Test that we converge to the active channel for engine 1. +TEST_F(HybridVideoEngineTest, SetSendCodecs1) { + // This will nuke the object that sub_channel2_ points to. + TestSetSendCodecs(engine_.sub_engine1(), MAKE_VECTOR(kCodecsVp8First)); + EXPECT_TRUE(engine_.sub_engine2()->GetChannel(0) == NULL); +} + +// Test that we converge to the active channel for engine 2. +TEST_F(HybridVideoEngineTest, SetSendCodecs2) { + // This will nuke the object that sub_channel1_ points to. + TestSetSendCodecs(engine_.sub_engine2(), MAKE_VECTOR(kCodecsGenericFirst)); + EXPECT_TRUE(engine_.sub_engine1()->GetChannel(0) == NULL); +} + +// Test that we don't accidentally eat 0x0 in SetSendCodecs +TEST_F(HybridVideoEngineTest, SetSendCodecs0x0) { + EXPECT_TRUE(SetupRenderAndAddStream(StreamParams::CreateLegacy(1234))); + // Send using generic codec, but with 0x0 resolution. + std::vector<cricket::VideoCodec> codecs(MAKE_VECTOR(kCodecsGenericFirst)); + codecs.resize(1); + codecs[0].width = 0; + codecs[0].height = 0; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); +} + +// Test setting the send bandwidth for VP8. +TEST_F(HybridVideoEngineTest, SetSendBandwidth1) { + TestSetSendBandwidth(engine_.sub_engine1(), + MAKE_VECTOR(kCodecsVp8First), + 100000, + 384000); +} + +// Test setting the send bandwidth for a generic codec. +TEST_F(HybridVideoEngineTest, SetSendBandwidth2) { + TestSetSendBandwidth(engine_.sub_engine2(), + MAKE_VECTOR(kCodecsGenericFirst), + 100001, + 384002); +} + +// Test that we dump RTP packets that arrive early. +TEST_F(HybridVideoEngineTest, HandleEarlyRtp) { + static const uint8 kPacket[1024] = { 0 }; + static const uint8 kRtcp[1024] = { 1 }; + EXPECT_TRUE(SetupRenderAndAddStream(StreamParams::CreateLegacy(1234))); + DeliverPacket(kPacket, sizeof(kPacket)); + DeliverRtcp(kRtcp, sizeof(kRtcp)); + EXPECT_TRUE(sub_channel1_->CheckNoRtp()); + EXPECT_TRUE(sub_channel2_->CheckNoRtp()); + EXPECT_TRUE(sub_channel1_->CheckNoRtcp()); + EXPECT_TRUE(sub_channel2_->CheckNoRtcp()); +} + +// Test that we properly pass on normal RTP packets. +TEST_F(HybridVideoEngineTest, HandleRtp) { + static const uint8 kPacket[1024] = { 0 }; + static const uint8 kRtcp[1024] = { 1 }; + EXPECT_TRUE(SetupRenderAndAddStream(StreamParams::CreateLegacy(1234))); + EXPECT_TRUE(channel_->SetSendCodecs(MAKE_VECTOR(kCodecsVp8First))); + EXPECT_TRUE(channel_->SetSend(true)); + DeliverPacket(kPacket, sizeof(kPacket)); + DeliverRtcp(kRtcp, sizeof(kRtcp)); + EXPECT_TRUE(sub_channel1_->CheckRtp(kPacket, sizeof(kPacket))); + EXPECT_TRUE(sub_channel1_->CheckRtcp(kRtcp, sizeof(kRtcp))); +} + +// Test that we properly connect media error signal. +TEST_F(HybridVideoEngineTest, MediaErrorSignal) { + cricket::VideoMediaErrorCatcher catcher; + + // Verify no signal from either channel before the active channel is set. + EXPECT_TRUE(SetupEngine()); + channel_->SignalMediaError.connect(&catcher, + &cricket::VideoMediaErrorCatcher::OnError); + sub_channel1_->SignalMediaError(1, cricket::VideoMediaChannel::ERROR_OTHER); + EXPECT_EQ(0U, catcher.ssrc()); + sub_channel2_->SignalMediaError(2, + cricket::VideoMediaChannel::ERROR_REC_DEVICE_OPEN_FAILED); + EXPECT_EQ(0U, catcher.ssrc()); + + // Set vp8 as active channel and verify that a signal comes from it. + EXPECT_TRUE(channel_->SetSendCodecs(MAKE_VECTOR(kCodecsVp8First))); + sub_channel1_->SignalMediaError(1, cricket::VideoMediaChannel::ERROR_OTHER); + EXPECT_EQ(cricket::VideoMediaChannel::ERROR_OTHER, catcher.error()); + EXPECT_EQ(1U, catcher.ssrc()); + + // Set generic codec as active channel and verify that a signal comes from it. + EXPECT_TRUE(SetupEngine()); + channel_->SignalMediaError.connect(&catcher, + &cricket::VideoMediaErrorCatcher::OnError); + EXPECT_TRUE(channel_->SetSendCodecs(MAKE_VECTOR(kCodecsGenericFirst))); + sub_channel2_->SignalMediaError(2, + cricket::VideoMediaChannel::ERROR_REC_DEVICE_OPEN_FAILED); + EXPECT_EQ(cricket::VideoMediaChannel::ERROR_REC_DEVICE_OPEN_FAILED, + catcher.error()); + EXPECT_EQ(2U, catcher.ssrc()); +} + +// Test that SetSend doesn't re-enter. +TEST_F(HybridVideoEngineTest, RepeatSetSend) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->SetSendCodecs(MAKE_VECTOR(kCodecsVp8First))); + + // Verify initial status. + EXPECT_FALSE(channel_->sending()); + EXPECT_FALSE(sub_channel1_->sending()); + EXPECT_EQ(0, engine_.num_ch1_send_on()); + EXPECT_EQ(0, engine_.num_ch1_send_off()); + + // Verfiy SetSend(true) works correctly. + EXPECT_TRUE(channel_->SetSend(true)); + EXPECT_TRUE(channel_->sending()); + EXPECT_TRUE(sub_channel1_->sending()); + EXPECT_EQ(1, engine_.num_ch1_send_on()); + EXPECT_EQ(0, engine_.num_ch1_send_off()); + + // SetSend(true) again and verify nothing changes. + EXPECT_TRUE(channel_->SetSend(true)); + EXPECT_TRUE(channel_->sending()); + EXPECT_TRUE(sub_channel1_->sending()); + EXPECT_EQ(1, engine_.num_ch1_send_on()); + EXPECT_EQ(0, engine_.num_ch1_send_off()); + + // Verify SetSend(false) works correctly. + EXPECT_TRUE(channel_->SetSend(false)); + EXPECT_FALSE(channel_->sending()); + EXPECT_FALSE(sub_channel1_->sending()); + EXPECT_EQ(1, engine_.num_ch1_send_on()); + EXPECT_EQ(1, engine_.num_ch1_send_off()); + + // SetSend(false) again and verfiy nothing changes. + EXPECT_TRUE(channel_->SetSend(false)); + EXPECT_FALSE(channel_->sending()); + EXPECT_FALSE(sub_channel1_->sending()); + EXPECT_EQ(1, engine_.num_ch1_send_on()); + EXPECT_EQ(1, engine_.num_ch1_send_off()); +} + +// Test that SetOptions. +TEST_F(HybridVideoEngineTest, SetOptions) { + cricket::VideoOptions vmo; + vmo.video_high_bitrate.Set(true); + vmo.system_low_adaptation_threshhold.Set(0.10f); + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->SetOptions(vmo)); + + bool high_bitrate; + float low; + EXPECT_TRUE(sub_channel1_->GetOptions(&vmo)); + EXPECT_TRUE(vmo.video_high_bitrate.Get(&high_bitrate)); + EXPECT_TRUE(high_bitrate); + EXPECT_TRUE(vmo.system_low_adaptation_threshhold.Get(&low)); + EXPECT_EQ(0.10f, low); + EXPECT_TRUE(sub_channel2_->GetOptions(&vmo)); + EXPECT_TRUE(vmo.video_high_bitrate.Get(&high_bitrate)); + EXPECT_TRUE(high_bitrate); + EXPECT_TRUE(vmo.system_low_adaptation_threshhold.Get(&low)); + EXPECT_EQ(0.10f, low); + + vmo.video_high_bitrate.Set(false); + vmo.system_low_adaptation_threshhold.Set(0.50f); + + EXPECT_TRUE(channel_->SetOptions(vmo)); + EXPECT_TRUE(sub_channel1_->GetOptions(&vmo)); + EXPECT_TRUE(vmo.video_high_bitrate.Get(&high_bitrate)); + EXPECT_FALSE(high_bitrate); + EXPECT_TRUE(vmo.system_low_adaptation_threshhold.Get(&low)); + EXPECT_EQ(0.50f, low); + EXPECT_TRUE(sub_channel2_->GetOptions(&vmo)); + EXPECT_TRUE(vmo.video_high_bitrate.Get(&high_bitrate)); + EXPECT_FALSE(high_bitrate); + EXPECT_TRUE(vmo.system_low_adaptation_threshhold.Get(&low)); + EXPECT_EQ(0.50f, low); +} + +TEST_F(HybridVideoEngineTest, SetCapturer) { + EXPECT_TRUE(SetupEngine()); + // Set vp8 as active channel and verify that capturer can be set. + EXPECT_TRUE(channel_->SetSendCodecs(MAKE_VECTOR(kCodecsVp8First))); + cricket::FakeVideoCapturer fake_video_capturer; + EXPECT_TRUE(channel_->SetCapturer(0, &fake_video_capturer)); + EXPECT_TRUE(channel_->SetCapturer(0, NULL)); + + // Set generic codec active channel and verify that capturer can be set. + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->SetSendCodecs(MAKE_VECTOR(kCodecsGenericFirst))); + EXPECT_TRUE(channel_->SetCapturer(0, &fake_video_capturer)); + EXPECT_TRUE(channel_->SetCapturer(0, NULL)); +} diff --git a/chromium/third_party/libjingle/source/talk/media/base/mediachannel.h b/chromium/third_party/libjingle/source/talk/media/base/mediachannel.h index 94ae03f8d80..34d2deff709 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/mediachannel.h +++ b/chromium/third_party/libjingle/source/talk/media/base/mediachannel.h @@ -50,10 +50,6 @@ class RateLimiter; class Timing; } -namespace webrtc { -struct DataChannelInit; -} - namespace cricket { class AudioRenderer; @@ -66,6 +62,7 @@ class VideoRenderer; const int kMinRtpHeaderExtensionId = 1; const int kMaxRtpHeaderExtensionId = 255; const int kScreencastDefaultFps = 5; +const int kHighStartBitrate = 1500; // Used in AudioOptions and VideoOptions to signify "unset" values. template <class T> @@ -172,8 +169,8 @@ struct AudioOptions { adjust_agc_delta.SetFrom(change.adjust_agc_delta); experimental_agc.SetFrom(change.experimental_agc); experimental_aec.SetFrom(change.experimental_aec); + experimental_ns.SetFrom(change.experimental_ns); aec_dump.SetFrom(change.aec_dump); - experimental_acm.SetFrom(change.experimental_acm); tx_agc_target_dbov.SetFrom(change.tx_agc_target_dbov); tx_agc_digital_compression_gain.SetFrom( change.tx_agc_digital_compression_gain); @@ -185,6 +182,7 @@ struct AudioOptions { recording_sample_rate.SetFrom(change.recording_sample_rate); playout_sample_rate.SetFrom(change.playout_sample_rate); dscp.SetFrom(change.dscp); + opus_fec.SetFrom(change.opus_fec); } bool operator==(const AudioOptions& o) const { @@ -199,9 +197,9 @@ struct AudioOptions { conference_mode == o.conference_mode && experimental_agc == o.experimental_agc && experimental_aec == o.experimental_aec && + experimental_ns == o.experimental_ns && adjust_agc_delta == o.adjust_agc_delta && aec_dump == o.aec_dump && - experimental_acm == o.experimental_acm && tx_agc_target_dbov == o.tx_agc_target_dbov && tx_agc_digital_compression_gain == o.tx_agc_digital_compression_gain && tx_agc_limiter == o.tx_agc_limiter && @@ -210,7 +208,8 @@ struct AudioOptions { rx_agc_limiter == o.rx_agc_limiter && recording_sample_rate == o.recording_sample_rate && playout_sample_rate == o.playout_sample_rate && - dscp == o.dscp; + dscp == o.dscp && + opus_fec == o.opus_fec; } std::string ToString() const { @@ -228,8 +227,8 @@ struct AudioOptions { ost << ToStringIfSet("agc_delta", adjust_agc_delta); ost << ToStringIfSet("experimental_agc", experimental_agc); ost << ToStringIfSet("experimental_aec", experimental_aec); + ost << ToStringIfSet("experimental_ns", experimental_ns); ost << ToStringIfSet("aec_dump", aec_dump); - ost << ToStringIfSet("experimental_acm", experimental_acm); ost << ToStringIfSet("tx_agc_target_dbov", tx_agc_target_dbov); ost << ToStringIfSet("tx_agc_digital_compression_gain", tx_agc_digital_compression_gain); @@ -241,6 +240,7 @@ struct AudioOptions { ost << ToStringIfSet("recording_sample_rate", recording_sample_rate); ost << ToStringIfSet("playout_sample_rate", playout_sample_rate); ost << ToStringIfSet("dscp", dscp); + ost << ToStringIfSet("opus_fec", opus_fec); ost << "}"; return ost.str(); } @@ -265,8 +265,8 @@ struct AudioOptions { Settable<int> adjust_agc_delta; Settable<bool> experimental_agc; Settable<bool> experimental_aec; + Settable<bool> experimental_ns; Settable<bool> aec_dump; - Settable<bool> experimental_acm; // Note that tx_agc_* only applies to non-experimental AGC. Settable<uint16> tx_agc_target_dbov; Settable<uint16> tx_agc_digital_compression_gain; @@ -278,6 +278,8 @@ struct AudioOptions { Settable<uint32> playout_sample_rate; // Set DSCP value for packet sent from audio channel. Settable<bool> dscp; + // Set Opus FEC + Settable<bool> opus_fec; }; // Options that can be applied to a VideoMediaChannel or a VideoMediaEngine. @@ -285,10 +287,17 @@ struct AudioOptions { // We are moving all of the setting of options to structs like this, // but some things currently still use flags. struct VideoOptions { + enum HighestBitrate { + NORMAL, + HIGH, + VERY_HIGH + }; + VideoOptions() { process_adaptation_threshhold.Set(kProcessCpuThreshold); system_low_adaptation_threshhold.Set(kLowSystemCpuThreshold); system_high_adaptation_threshhold.Set(kHighSystemCpuThreshold); + unsignalled_recv_stream_limit.Set(kNumDefaultUnsignalledVideoRecvStreams); } void SetAll(const VideoOptions& change) { @@ -300,13 +309,21 @@ struct VideoOptions { video_noise_reduction.SetFrom(change.video_noise_reduction); video_one_layer_screencast.SetFrom(change.video_one_layer_screencast); video_high_bitrate.SetFrom(change.video_high_bitrate); - video_watermark.SetFrom(change.video_watermark); + video_start_bitrate.SetFrom(change.video_start_bitrate); video_temporal_layer_screencast.SetFrom( change.video_temporal_layer_screencast); video_temporal_layer_realtime.SetFrom( change.video_temporal_layer_realtime); video_leaky_bucket.SetFrom(change.video_leaky_bucket); + video_highest_bitrate.SetFrom(change.video_highest_bitrate); cpu_overuse_detection.SetFrom(change.cpu_overuse_detection); + cpu_underuse_threshold.SetFrom(change.cpu_underuse_threshold); + cpu_overuse_threshold.SetFrom(change.cpu_overuse_threshold); + cpu_underuse_encode_rsd_threshold.SetFrom( + change.cpu_underuse_encode_rsd_threshold); + cpu_overuse_encode_rsd_threshold.SetFrom( + change.cpu_overuse_encode_rsd_threshold); + cpu_overuse_encode_usage.SetFrom(change.cpu_overuse_encode_usage); conference_mode.SetFrom(change.conference_mode); process_adaptation_threshhold.SetFrom(change.process_adaptation_threshhold); system_low_adaptation_threshhold.SetFrom( @@ -317,6 +334,13 @@ struct VideoOptions { lower_min_bitrate.SetFrom(change.lower_min_bitrate); dscp.SetFrom(change.dscp); suspend_below_min_bitrate.SetFrom(change.suspend_below_min_bitrate); + unsignalled_recv_stream_limit.SetFrom(change.unsignalled_recv_stream_limit); + use_simulcast_adapter.SetFrom(change.use_simulcast_adapter); + skip_encoding_unused_streams.SetFrom(change.skip_encoding_unused_streams); + screencast_min_bitrate.SetFrom(change.screencast_min_bitrate); + use_improved_wifi_bandwidth_estimator.SetFrom( + change.use_improved_wifi_bandwidth_estimator); + use_payload_padding.SetFrom(change.use_payload_padding); } bool operator==(const VideoOptions& o) const { @@ -328,11 +352,19 @@ struct VideoOptions { video_noise_reduction == o.video_noise_reduction && video_one_layer_screencast == o.video_one_layer_screencast && video_high_bitrate == o.video_high_bitrate && - video_watermark == o.video_watermark && + video_start_bitrate == o.video_start_bitrate && video_temporal_layer_screencast == o.video_temporal_layer_screencast && video_temporal_layer_realtime == o.video_temporal_layer_realtime && video_leaky_bucket == o.video_leaky_bucket && + video_highest_bitrate == o.video_highest_bitrate && cpu_overuse_detection == o.cpu_overuse_detection && + cpu_underuse_threshold == o.cpu_underuse_threshold && + cpu_overuse_threshold == o.cpu_overuse_threshold && + cpu_underuse_encode_rsd_threshold == + o.cpu_underuse_encode_rsd_threshold && + cpu_overuse_encode_rsd_threshold == + o.cpu_overuse_encode_rsd_threshold && + cpu_overuse_encode_usage == o.cpu_overuse_encode_usage && conference_mode == o.conference_mode && process_adaptation_threshhold == o.process_adaptation_threshhold && system_low_adaptation_threshhold == @@ -342,7 +374,14 @@ struct VideoOptions { buffered_mode_latency == o.buffered_mode_latency && lower_min_bitrate == o.lower_min_bitrate && dscp == o.dscp && - suspend_below_min_bitrate == o.suspend_below_min_bitrate; + suspend_below_min_bitrate == o.suspend_below_min_bitrate && + unsignalled_recv_stream_limit == o.unsignalled_recv_stream_limit && + use_simulcast_adapter == o.use_simulcast_adapter && + skip_encoding_unused_streams == o.skip_encoding_unused_streams && + screencast_min_bitrate == o.screencast_min_bitrate && + use_improved_wifi_bandwidth_estimator == + o.use_improved_wifi_bandwidth_estimator && + use_payload_padding == o.use_payload_padding; } std::string ToString() const { @@ -356,13 +395,22 @@ struct VideoOptions { ost << ToStringIfSet("noise reduction", video_noise_reduction); ost << ToStringIfSet("1 layer screencast", video_one_layer_screencast); ost << ToStringIfSet("high bitrate", video_high_bitrate); - ost << ToStringIfSet("watermark", video_watermark); + ost << ToStringIfSet("start bitrate", video_start_bitrate); ost << ToStringIfSet("video temporal layer screencast", video_temporal_layer_screencast); ost << ToStringIfSet("video temporal layer realtime", video_temporal_layer_realtime); ost << ToStringIfSet("leaky bucket", video_leaky_bucket); + ost << ToStringIfSet("highest video bitrate", video_highest_bitrate); ost << ToStringIfSet("cpu overuse detection", cpu_overuse_detection); + ost << ToStringIfSet("cpu underuse threshold", cpu_underuse_threshold); + ost << ToStringIfSet("cpu overuse threshold", cpu_overuse_threshold); + ost << ToStringIfSet("cpu underuse encode rsd threshold", + cpu_underuse_encode_rsd_threshold); + ost << ToStringIfSet("cpu overuse encode rsd threshold", + cpu_overuse_encode_rsd_threshold); + ost << ToStringIfSet("cpu overuse encode usage", + cpu_overuse_encode_usage); ost << ToStringIfSet("conference mode", conference_mode); ost << ToStringIfSet("process", process_adaptation_threshhold); ost << ToStringIfSet("low", system_low_adaptation_threshhold); @@ -372,6 +420,15 @@ struct VideoOptions { ost << ToStringIfSet("dscp", dscp); ost << ToStringIfSet("suspend below min bitrate", suspend_below_min_bitrate); + ost << ToStringIfSet("num channels for early receive", + unsignalled_recv_stream_limit); + ost << ToStringIfSet("use simulcast adapter", use_simulcast_adapter); + ost << ToStringIfSet("skip encoding unused streams", + skip_encoding_unused_streams); + ost << ToStringIfSet("screencast min bitrate", screencast_min_bitrate); + ost << ToStringIfSet("improved wifi bwe", + use_improved_wifi_bandwidth_estimator); + ost << ToStringIfSet("payload padding", use_payload_padding); ost << "}"; return ost.str(); } @@ -392,18 +449,38 @@ struct VideoOptions { Settable<bool> video_one_layer_screencast; // Experimental: Enable WebRtc higher bitrate? Settable<bool> video_high_bitrate; - // Experimental: Add watermark to the rendered video image. - Settable<bool> video_watermark; + // Experimental: Enable WebRtc higher start bitrate? + Settable<int> video_start_bitrate; // Experimental: Enable WebRTC layered screencast. Settable<bool> video_temporal_layer_screencast; // Experimental: Enable WebRTC temporal layer strategy for realtime video. Settable<bool> video_temporal_layer_realtime; // Enable WebRTC leaky bucket when sending media packets. Settable<bool> video_leaky_bucket; + // Set highest bitrate mode for video. + Settable<HighestBitrate> video_highest_bitrate; // Enable WebRTC Cpu Overuse Detection, which is a new version of the CPU // adaptation algorithm. So this option will override the // |adapt_input_to_cpu_usage|. Settable<bool> cpu_overuse_detection; + // Low threshold (t1) for cpu overuse adaptation. (Adapt up) + // Metric: encode usage (m1). m1 < t1 => underuse. + Settable<int> cpu_underuse_threshold; + // High threshold (t1) for cpu overuse adaptation. (Adapt down) + // Metric: encode usage (m1). m1 > t1 => overuse. + Settable<int> cpu_overuse_threshold; + // Low threshold (t2) for cpu overuse adaptation. (Adapt up) + // Metric: relative standard deviation of encode time (m2). + // Optional threshold. If set, (m1 < t1 && m2 < t2) => underuse. + // Note: t2 will have no effect if t1 is not set. + Settable<int> cpu_underuse_encode_rsd_threshold; + // High threshold (t2) for cpu overuse adaptation. (Adapt down) + // Metric: relative standard deviation of encode time (m2). + // Optional threshold. If set, (m1 > t1 || m2 > t2) => overuse. + // Note: t2 will have no effect if t1 is not set. + Settable<int> cpu_overuse_encode_rsd_threshold; + // Use encode usage for cpu detection. + Settable<bool> cpu_overuse_encode_usage; // Use conference mode? Settable<bool> conference_mode; // Threshhold for process cpu adaptation. (Process limit) @@ -421,6 +498,19 @@ struct VideoOptions { // Enable WebRTC suspension of video. No video frames will be sent when the // bitrate is below the configured minimum bitrate. Settable<bool> suspend_below_min_bitrate; + // Limit on the number of early receive channels that can be created. + Settable<int> unsignalled_recv_stream_limit; + // Enable use of simulcast adapter. + Settable<bool> use_simulcast_adapter; + // Enables the encoder to skip encoding stream not actually sent due to too + // low available bit rate. + Settable<bool> skip_encoding_unused_streams; + // Force screencast to use a minimum bitrate + Settable<int> screencast_min_bitrate; + // Enable improved bandwidth estiamtor on wifi. + Settable<bool> use_improved_wifi_bandwidth_estimator; + // Enable payload padding. + Settable<bool> use_payload_padding; }; // A class for playing out soundclips. @@ -539,8 +629,14 @@ class MediaChannel : public sigslot::has_slots<> { const std::vector<RtpHeaderExtension>& extensions) = 0; virtual bool SetSendRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions) = 0; - // Sets the rate control to use when sending data. - virtual bool SetSendBandwidth(bool autobw, int bps) = 0; + // Returns the absoulte sendtime extension id value from media channel. + virtual int GetRtpSendTimeExtnId() const { + return -1; + } + // Sets the initial bandwidth to use when sending starts. + virtual bool SetStartSendBandwidth(int bps) = 0; + // Sets the maximum allowed bandwidth to use when sending data. + virtual bool SetMaxSendBandwidth(int bps) = 0; // Base method to send packet using NetworkInterface. bool SendPacket(talk_base::Buffer* packet) { @@ -671,6 +767,20 @@ struct MediaSenderInfo { std::vector<SsrcReceiverInfo> remote_stats; }; +template<class T> +struct VariableInfo { + VariableInfo() + : min_val(), + mean(0.0), + max_val(), + variance(0.0) { + } + T min_val; + double mean; + T max_val; + double variance; +}; + struct MediaReceiverInfo { MediaReceiverInfo() : bytes_rcvd(0), @@ -711,6 +821,7 @@ struct MediaReceiverInfo { int packets_rcvd; int packets_lost; float fraction_lost; + std::string codec_name; std::vector<SsrcReceiverInfo> local_stats; std::vector<SsrcSenderInfo> remote_stats; }; @@ -747,7 +858,14 @@ struct VoiceReceiverInfo : public MediaReceiverInfo { jitter_buffer_preferred_ms(0), delay_estimate_ms(0), audio_level(0), - expand_rate(0) { + expand_rate(0), + decoding_calls_to_silence_generator(0), + decoding_calls_to_neteq(0), + decoding_normal(0), + decoding_plc(0), + decoding_cng(0), + decoding_plc_cng(0), + capture_start_ntp_time_ms(-1) { } int ext_seqnum; @@ -758,15 +876,26 @@ struct VoiceReceiverInfo : public MediaReceiverInfo { int audio_level; // fraction of synthesized speech inserted through pre-emptive expansion float expand_rate; + int decoding_calls_to_silence_generator; + int decoding_calls_to_neteq; + int decoding_normal; + int decoding_plc; + int decoding_cng; + int decoding_plc_cng; + // Estimated capture start time in NTP time in ms. + int64 capture_start_ntp_time_ms; }; struct VideoSenderInfo : public MediaSenderInfo { VideoSenderInfo() : packets_cached(0), firs_rcvd(0), + plis_rcvd(0), nacks_rcvd(0), - frame_width(0), - frame_height(0), + input_frame_width(0), + input_frame_height(0), + send_frame_width(0), + send_frame_height(0), framerate_input(0), framerate_sent(0), nominal_bitrate(0), @@ -775,15 +904,19 @@ struct VideoSenderInfo : public MediaSenderInfo { capture_jitter_ms(0), avg_encode_ms(0), encode_usage_percent(0), + encode_rsd(0), capture_queue_delay_ms_per_s(0) { } std::vector<SsrcGroup> ssrc_groups; int packets_cached; int firs_rcvd; + int plis_rcvd; int nacks_rcvd; - int frame_width; - int frame_height; + int input_frame_width; + int input_frame_height; + int send_frame_width; + int send_frame_height; int framerate_input; int framerate_sent; int nominal_bitrate; @@ -792,13 +925,18 @@ struct VideoSenderInfo : public MediaSenderInfo { int capture_jitter_ms; int avg_encode_ms; int encode_usage_percent; + int encode_rsd; int capture_queue_delay_ms_per_s; + VariableInfo<int> adapt_frame_drops; + VariableInfo<int> effects_frame_drops; + VariableInfo<double> capturer_frame_time; }; struct VideoReceiverInfo : public MediaReceiverInfo { VideoReceiverInfo() : packets_concealed(0), firs_sent(0), + plis_sent(0), nacks_sent(0), frame_width(0), frame_height(0), @@ -813,12 +951,14 @@ struct VideoReceiverInfo : public MediaReceiverInfo { min_playout_delay_ms(0), render_delay_ms(0), target_delay_ms(0), - current_delay_ms(0) { + current_delay_ms(0), + capture_start_ntp_time_ms(-1) { } std::vector<SsrcGroup> ssrc_groups; int packets_concealed; int firs_sent; + int plis_sent; int nacks_sent; int frame_width; int frame_height; @@ -849,6 +989,9 @@ struct VideoReceiverInfo : public MediaReceiverInfo { int target_delay_ms; // Current overall delay, possibly ramping towards target_delay_ms. int current_delay_ms; + + // Estimated capture start time in NTP time in ms. + int64 capture_start_ntp_time_ms; }; struct DataSenderInfo : public MediaSenderInfo { @@ -875,7 +1018,8 @@ struct BandwidthEstimationInfo { actual_enc_bitrate(0), retransmit_bitrate(0), transmit_bitrate(0), - bucket_delay(0) { + bucket_delay(0), + total_received_propagation_delta_ms(0) { } int available_send_bandwidth; @@ -885,6 +1029,11 @@ struct BandwidthEstimationInfo { int retransmit_bitrate; int transmit_bitrate; int bucket_delay; + // The following stats are only valid when + // StatsOptions::include_received_propagation_stats is true. + int total_received_propagation_delta_ms; + std::vector<int> recent_received_propagation_delta_ms; + std::vector<int64> recent_received_packet_group_arrival_time_ms; }; struct VoiceMediaInfo { @@ -916,6 +1065,12 @@ struct DataMediaInfo { std::vector<DataReceiverInfo> receivers; }; +struct StatsOptions { + StatsOptions() : include_received_propagation_stats(false) {} + + bool include_received_propagation_stats; +}; + class VoiceMediaChannel : public MediaChannel { public: enum Error { @@ -1034,7 +1189,12 @@ class VideoMediaChannel : public MediaChannel { // |capturer|. If |ssrc| is non zero create a new stream with |ssrc| as SSRC. virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer) = 0; // Gets quality stats for the channel. - virtual bool GetStats(VideoMediaInfo* info) = 0; + virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info) = 0; + // This is needed for MediaMonitor to use the same template for voice, video + // and data MediaChannels. + bool GetStats(VideoMediaInfo* info) { + return GetStats(StatsOptions(), info); + } // Send an intra frame to the receivers. virtual bool SendIntraFrame() = 0; @@ -1157,11 +1317,8 @@ class DataMediaChannel : public MediaChannel { // Signal when the media channel is ready to send the stream. Arguments are: // writable(bool) sigslot::signal1<bool> SignalReadyToSend; - // Signal for notifying when a new stream is added from the remote side. Used - // for the in-band negotioation through the OPEN message for SCTP data - // channel. - sigslot::signal2<const std::string&, const webrtc::DataChannelInit&> - SignalNewStreamReceived; + // Signal for notifying that the remote side has closed the DataChannel. + sigslot::signal1<uint32> SignalStreamClosedRemotely; }; } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/base/mediaengine.cc b/chromium/third_party/libjingle/source/talk/media/base/mediaengine.cc index 021cf81f1c2..289f2290a47 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/mediaengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/mediaengine.cc @@ -42,6 +42,13 @@ const int MediaEngineInterface::kDefaultAudioDelayOffset = 0; #if defined(HAVE_WEBRTC_VIDEO) #include "talk/media/webrtc/webrtcvideoengine.h" #endif // HAVE_WEBRTC_VIDEO +#if defined(HAVE_LMI) +#include "talk/media/base/hybridvideoengine.h" +#include "talk/media/lmi/lmimediaengine.h" +#endif // HAVE_LMI +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG namespace cricket { #if defined(HAVE_WEBRTC_VOICE) @@ -51,22 +58,59 @@ namespace cricket { #endif #if defined(HAVE_WEBRTC_VIDEO) +#if !defined(HAVE_LMI) template<> CompositeMediaEngine<WebRtcVoiceEngine, WebRtcVideoEngine>:: CompositeMediaEngine() { video_.SetVoiceEngine(&voice_); } #define VIDEO_ENG_NAME WebRtcVideoEngine +#else +// If we have both WebRtcVideoEngine and LmiVideoEngine, enable dual-stack. +// This small class here allows us to hook the WebRtcVideoChannel up to +// the capturer owned by the LMI engine, without infecting the rest of the +// HybridVideoEngine classes with this abstraction violation. +class WebRtcLmiHybridVideoEngine + : public HybridVideoEngine<WebRtcVideoEngine, LmiVideoEngine> { + public: + void SetVoiceEngine(WebRtcVoiceEngine* engine) { + video1_.SetVoiceEngine(engine); + } +}; +template<> +CompositeMediaEngine<WebRtcVoiceEngine, WebRtcLmiHybridVideoEngine>:: + CompositeMediaEngine() { + video_.SetVoiceEngine(&voice_); +} +#define VIDEO_ENG_NAME WebRtcLmiHybridVideoEngine +#endif +#elif defined(HAVE_LMI) +#define VIDEO_ENG_NAME LmiVideoEngine +#else +#define VIDEO_ENG_NAME NullVideoEngine #endif +MediaEngineFactory::MediaEngineCreateFunction + MediaEngineFactory::create_function_ = NULL; +MediaEngineFactory::MediaEngineCreateFunction + MediaEngineFactory::SetCreateFunction(MediaEngineCreateFunction function) { + MediaEngineCreateFunction old_function = create_function_; + create_function_ = function; + return old_function; +}; + MediaEngineInterface* MediaEngineFactory::Create() { + if (create_function_) { + return create_function_(); + } else { #if defined(HAVE_LINPHONE) - return new LinphoneMediaEngine("", ""); + return new LinphoneMediaEngine("", ""); #elif defined(AUDIO_ENG_NAME) && defined(VIDEO_ENG_NAME) - return new CompositeMediaEngine<AUDIO_ENG_NAME, VIDEO_ENG_NAME>(); + return new CompositeMediaEngine<AUDIO_ENG_NAME, VIDEO_ENG_NAME>(); #else - return new NullMediaEngine(); + return new NullMediaEngine(); #endif + } } }; // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/base/mediaengine.h b/chromium/third_party/libjingle/source/talk/media/base/mediaengine.h index f9165728dde..326b722bd00 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/mediaengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/mediaengine.h @@ -32,10 +32,12 @@ #include <CoreAudio/CoreAudio.h> #endif -#include <climits> +#include <limits.h> + #include <string> #include <vector> +#include "talk/base/fileutils.h" #include "talk/base/sigslotrepeater.h" #include "talk/media/base/codec.h" #include "talk/media/base/mediachannel.h" @@ -135,6 +137,9 @@ class MediaEngineInterface { virtual void SetVoiceLogging(int min_sev, const char* filter) = 0; virtual void SetVideoLogging(int min_sev, const char* filter) = 0; + // Starts AEC dump using existing file. + virtual bool StartAecDump(talk_base::PlatformFile file) = 0; + // Voice processors for effects. virtual bool RegisterVoiceProcessor(uint32 ssrc, VoiceProcessor* video_processor, @@ -153,7 +158,18 @@ class MediaEngineInterface { #if !defined(DISABLE_MEDIA_ENGINE_FACTORY) class MediaEngineFactory { public: + typedef cricket::MediaEngineInterface* (*MediaEngineCreateFunction)(); + // Creates a media engine, using either the compiled system default or the + // creation function specified in SetCreateFunction, if specified. static MediaEngineInterface* Create(); + // Sets the function used when calling Create. If unset, the compiled system + // default will be used. Returns the old create function, or NULL if one + // wasn't set. Likewise, NULL can be used as the |function| parameter to + // reset to the default behavior. + static MediaEngineCreateFunction SetCreateFunction( + MediaEngineCreateFunction function); + private: + static MediaEngineCreateFunction create_function_; }; #endif @@ -253,6 +269,10 @@ class CompositeMediaEngine : public MediaEngineInterface { video_.SetLogging(min_sev, filter); } + virtual bool StartAecDump(talk_base::PlatformFile file) { + return voice_.StartAecDump(file); + } + virtual bool RegisterVoiceProcessor(uint32 ssrc, VoiceProcessor* processor, MediaProcessorDirection direction) { @@ -309,6 +329,7 @@ class NullVoiceEngine { return rtp_header_extensions_; } void SetLogging(int min_sev, const char* filter) {} + bool StartAecDump(talk_base::PlatformFile file) { return false; } bool RegisterProcessor(uint32 ssrc, VoiceProcessor* voice_processor, MediaProcessorDirection direction) { return true; } diff --git a/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.cc b/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.cc index 0f84c836fc6..3d0efc43b0e 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.cc @@ -290,8 +290,8 @@ void RtpDataMediaChannel::OnPacketReceived( SignalDataReceived(params, data, data_len); } -bool RtpDataMediaChannel::SetSendBandwidth(bool autobw, int bps) { - if (autobw || bps <= 0) { +bool RtpDataMediaChannel::SetMaxSendBandwidth(int bps) { + if (bps <= 0) { bps = kDataMaxBandwidth; } send_limiter_.reset(new talk_base::RateLimiter(bps / 8, 1.0)); diff --git a/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.h b/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.h index 59e6589532d..d5abeef68a4 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.h @@ -96,7 +96,8 @@ class RtpDataMediaChannel : public DataMediaChannel { timing_ = timing; } - virtual bool SetSendBandwidth(bool autobw, int bps); + virtual bool SetStartSendBandwidth(int bps) { return true; } + virtual bool SetMaxSendBandwidth(int bps); virtual bool SetRecvRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions) { return true; } virtual bool SetSendRtpHeaderExtensions( diff --git a/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine_unittest.cc b/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine_unittest.cc index a86ab3b3125..640c18dbfcc 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine_unittest.cc @@ -31,6 +31,7 @@ #include "talk/base/gunit.h" #include "talk/base/helpers.h" #include "talk/base/scoped_ptr.h" +#include "talk/base/ssladapter.h" #include "talk/base/timing.h" #include "talk/media/base/constants.h" #include "talk/media/base/fakenetworkinterface.h" @@ -82,6 +83,14 @@ class FakeDataReceiver : public sigslot::has_slots<> { class RtpDataMediaChannelTest : public testing::Test { protected: + static void SetUpTestCase() { + talk_base::InitializeSSL(); + } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } + virtual void SetUp() { // Seed needed for each test to satisfy expectations. iface_.reset(new cricket::FakeNetworkInterface()); @@ -387,7 +396,7 @@ TEST_F(RtpDataMediaChannelTest, SendDataRate) { // With rtp overhead of 32 bytes, each one of our packets is 36 // bytes, or 288 bits. So, a limit of 872bps will allow 3 packets, // but not four. - dmc->SetSendBandwidth(false, 872); + dmc->SetMaxSendBandwidth(872); EXPECT_TRUE(dmc->SendData(params, payload, &result)); EXPECT_TRUE(dmc->SendData(params, payload, &result)); diff --git a/chromium/third_party/libjingle/source/talk/media/base/rtpdump.h b/chromium/third_party/libjingle/source/talk/media/base/rtpdump.h index 9d7b679d589..ceacab2cda7 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/rtpdump.h +++ b/chromium/third_party/libjingle/source/talk/media/base/rtpdump.h @@ -28,7 +28,8 @@ #ifndef TALK_MEDIA_BASE_RTPDUMP_H_ #define TALK_MEDIA_BASE_RTPDUMP_H_ -#include <cstring> +#include <string.h> + #include <string> #include <vector> diff --git a/chromium/third_party/libjingle/source/talk/media/base/testutils.cc b/chromium/third_party/libjingle/source/talk/media/base/testutils.cc index 9b1b16d21fc..73206138413 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/testutils.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/testutils.cc @@ -35,6 +35,7 @@ #include "talk/base/pathutils.h" #include "talk/base/stream.h" #include "talk/base/stringutils.h" +#include "talk/base/testutils.h" #include "talk/media/base/rtpdump.h" #include "talk/media/base/videocapturer.h" #include "talk/media/base/videoframe.h" @@ -254,7 +255,7 @@ void VideoCapturerListener::OnFrameCaptured(VideoCapturer* capturer, // Returns the absolute path to a file in the testdata/ directory. std::string GetTestFilePath(const std::string& filename) { // Locate test data directory. - talk_base::Pathname path = GetTalkDirectory(); + talk_base::Pathname path = testing::GetTalkDirectory(); EXPECT_FALSE(path.empty()); // must be run from inside "talk" path.AppendFolder("media"); path.AppendFolder("testdata"); diff --git a/chromium/third_party/libjingle/source/talk/media/base/videoadapter.cc b/chromium/third_party/libjingle/source/talk/media/base/videoadapter.cc index 22b1f7d8ddb..76ec52775c0 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videoadapter.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/videoadapter.cc @@ -30,15 +30,16 @@ #include "talk/base/logging.h" #include "talk/base/timeutils.h" #include "talk/media/base/constants.h" +#include "talk/media/base/videocommon.h" #include "talk/media/base/videoframe.h" namespace cricket { // TODO(fbarchard): Make downgrades settable static const int kMaxCpuDowngrades = 2; // Downgrade at most 2 times for CPU. -// The number of milliseconds of data to require before acting on cpu sampling -// information. -static const size_t kCpuLoadMinSampleTime = 5000; +// The number of cpu samples to require before adapting. This value depends on +// the cpu monitor sampling frequency being 2000ms. +static const int kCpuLoadMinSamples = 3; // The amount of weight to give to each new cpu load sample. The lower the // value, the slower we'll adapt to changing cpu conditions. static const float kCpuLoadWeightCoefficient = 0.4f; @@ -162,8 +163,9 @@ float VideoAdapter::FindLowerScale(int width, int height, VideoAdapter::VideoAdapter() : output_num_pixels_(INT_MAX), scale_third_(false), - frames_(0), - adapted_frames_(0), + frames_in_(0), + frames_out_(0), + frames_scaled_(0), adaption_changes_(0), previous_width_(0), previous_height_(0), @@ -177,9 +179,14 @@ VideoAdapter::~VideoAdapter() { void VideoAdapter::SetInputFormat(const VideoFormat& format) { talk_base::CritScope cs(&critical_section_); + int64 old_input_interval = input_format_.interval; input_format_ = format; output_format_.interval = talk_base::_max( output_format_.interval, input_format_.interval); + if (old_input_interval != input_format_.interval) { + LOG(LS_INFO) << "VAdapt input interval changed from " + << old_input_interval << " to " << input_format_.interval; + } } void CoordinatedVideoAdapter::SetInputFormat(const VideoFormat& format) { @@ -205,12 +212,23 @@ void CoordinatedVideoAdapter::SetInputFormat(const VideoFormat& format) { } } +void CoordinatedVideoAdapter::set_cpu_smoothing(bool enable) { + LOG(LS_INFO) << "CPU smoothing is now " + << (enable ? "enabled" : "disabled"); + cpu_smoothing_ = enable; +} + void VideoAdapter::SetOutputFormat(const VideoFormat& format) { talk_base::CritScope cs(&critical_section_); + int64 old_output_interval = output_format_.interval; output_format_ = format; output_num_pixels_ = output_format_.width * output_format_.height; output_format_.interval = talk_base::_max( output_format_.interval, input_format_.interval); + if (old_output_interval != output_format_.interval) { + LOG(LS_INFO) << "VAdapt output interval changed from " + << old_output_interval << " to " << output_format_.interval; + } } const VideoFormat& VideoAdapter::input_format() { @@ -218,6 +236,10 @@ const VideoFormat& VideoAdapter::input_format() { return input_format_; } +bool VideoAdapter::drops_all_frames() const { + return output_num_pixels_ == 0; +} + const VideoFormat& VideoAdapter::output_format() { talk_base::CritScope cs(&critical_section_); return output_format_; @@ -239,13 +261,13 @@ int VideoAdapter::GetOutputNumPixels() const { // TODO(fbarchard): Add AdaptFrameRate function that only drops frames but // not resolution. -bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, +bool VideoAdapter::AdaptFrame(VideoFrame* in_frame, VideoFrame** out_frame) { + talk_base::CritScope cs(&critical_section_); if (!in_frame || !out_frame) { return false; } - talk_base::CritScope cs(&critical_section_); - ++frames_; + ++frames_in_; // Update input to actual frame dimensions. VideoFormat format(static_cast<int>(in_frame->GetWidth()), @@ -273,12 +295,25 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, } } if (should_drop) { + // Show VAdapt log every 90 frames dropped. (3 seconds) + if ((frames_in_ - frames_out_) % 90 == 0) { + // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed + // in default calls. + LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_ + << " / out " << frames_out_ + << " / in " << frames_in_ + << " Changes: " << adaption_changes_ + << " Input: " << in_frame->GetWidth() + << "x" << in_frame->GetHeight() + << " i" << input_format_.interval + << " Output: i" << output_format_.interval; + } *out_frame = NULL; return true; } float scale = 1.f; - if (output_num_pixels_) { + if (output_num_pixels_ < input_format_.width * input_format_.height) { scale = VideoAdapter::FindClosestViewScale( static_cast<int>(in_frame->GetWidth()), static_cast<int>(in_frame->GetHeight()), @@ -286,22 +321,35 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, output_format_.width = static_cast<int>(in_frame->GetWidth() * scale + .5f); output_format_.height = static_cast<int>(in_frame->GetHeight() * scale + .5f); + } else { + output_format_.width = static_cast<int>(in_frame->GetWidth()); + output_format_.height = static_cast<int>(in_frame->GetHeight()); } - if (!StretchToOutputFrame(in_frame)) { - return false; - } + if (!black_output_ && + in_frame->GetWidth() == static_cast<size_t>(output_format_.width) && + in_frame->GetHeight() == static_cast<size_t>(output_format_.height)) { + // The dimensions are correct and we aren't muting, so use the input frame. + *out_frame = in_frame; + } else { + if (!StretchToOutputFrame(in_frame)) { + LOG(LS_VERBOSE) << "VAdapt Stretch Failed."; + return false; + } - *out_frame = output_frame_.get(); + *out_frame = output_frame_.get(); + } - // Show VAdapt log every 300 frames. (10 seconds) - // TODO(fbarchard): Consider GetLogSeverity() to change interval to less - // for LS_VERBOSE and more for LS_INFO. - bool show = frames_ % 300 == 0; + ++frames_out_; if (in_frame->GetWidth() != (*out_frame)->GetWidth() || in_frame->GetHeight() != (*out_frame)->GetHeight()) { - ++adapted_frames_; + ++frames_scaled_; } + // Show VAdapt log every 90 frames output. (3 seconds) + // TODO(fbarchard): Consider GetLogSeverity() to change interval to less + // for LS_VERBOSE and more for LS_INFO. + bool show = (frames_out_) % 90 == 0; + // TODO(fbarchard): LOG the previous output resolution and track input // resolution changes as well. Consider dropping the statistics into their // own class which could be queried publically. @@ -315,14 +363,17 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, if (show) { // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed // in default calls. - LOG(LS_INFO) << "VAdapt Frame: " << adapted_frames_ - << " / " << frames_ + LOG(LS_INFO) << "VAdapt Frame: scaled " << frames_scaled_ + << " / out " << frames_out_ + << " / in " << frames_in_ << " Changes: " << adaption_changes_ << " Input: " << in_frame->GetWidth() << "x" << in_frame->GetHeight() + << " i" << input_format_.interval << " Scale: " << scale << " Output: " << (*out_frame)->GetWidth() << "x" << (*out_frame)->GetHeight() + << " i" << output_format_.interval << " Changed: " << (changed ? "true" : "false"); } previous_width_ = (*out_frame)->GetWidth(); @@ -331,6 +382,12 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, return true; } +void VideoAdapter::set_scale_third(bool enable) { + LOG(LS_INFO) << "Video Adapter third scaling is now " + << (enable ? "enabled" : "disabled"); + scale_third_ = enable; +} + // Scale or Blacken the frame. Returns true if successful. bool VideoAdapter::StretchToOutputFrame(const VideoFrame* in_frame) { int output_width = output_format_.width; @@ -382,7 +439,8 @@ CoordinatedVideoAdapter::CoordinatedVideoAdapter() view_adaptation_(true), view_switch_(false), cpu_downgrade_count_(0), - cpu_adapt_wait_time_(0), + cpu_load_min_samples_(kCpuLoadMinSamples), + cpu_load_num_samples_(0), high_system_threshold_(kHighSystemCpuThreshold), low_system_threshold_(kLowSystemCpuThreshold), process_threshold_(kProcessCpuThreshold), @@ -390,7 +448,7 @@ CoordinatedVideoAdapter::CoordinatedVideoAdapter() view_desired_interval_(0), encoder_desired_num_pixels_(INT_MAX), cpu_desired_num_pixels_(INT_MAX), - adapt_reason_(0), + adapt_reason_(ADAPTREASON_NONE), system_load_average_(kCpuLoadInitialAverage) { } @@ -450,6 +508,48 @@ void CoordinatedVideoAdapter::OnOutputFormatRequest(const VideoFormat& format) { << " To: " << new_width << "x" << new_height; } +void CoordinatedVideoAdapter::set_cpu_load_min_samples( + int cpu_load_min_samples) { + if (cpu_load_min_samples_ != cpu_load_min_samples) { + LOG(LS_INFO) << "VAdapt Change Cpu Adapt Min Samples from: " + << cpu_load_min_samples_ << " to " + << cpu_load_min_samples; + cpu_load_min_samples_ = cpu_load_min_samples; + } +} + +void CoordinatedVideoAdapter::set_high_system_threshold( + float high_system_threshold) { + ASSERT(high_system_threshold <= 1.0f); + ASSERT(high_system_threshold >= 0.0f); + if (high_system_threshold_ != high_system_threshold) { + LOG(LS_INFO) << "VAdapt Change High System Threshold from: " + << high_system_threshold_ << " to " << high_system_threshold; + high_system_threshold_ = high_system_threshold; + } +} + +void CoordinatedVideoAdapter::set_low_system_threshold( + float low_system_threshold) { + ASSERT(low_system_threshold <= 1.0f); + ASSERT(low_system_threshold >= 0.0f); + if (low_system_threshold_ != low_system_threshold) { + LOG(LS_INFO) << "VAdapt Change Low System Threshold from: " + << low_system_threshold_ << " to " << low_system_threshold; + low_system_threshold_ = low_system_threshold; + } +} + +void CoordinatedVideoAdapter::set_process_threshold(float process_threshold) { + ASSERT(process_threshold <= 1.0f); + ASSERT(process_threshold >= 0.0f); + if (process_threshold_ != process_threshold) { + LOG(LS_INFO) << "VAdapt Change High Process Threshold from: " + << process_threshold_ << " to " << process_threshold; + process_threshold_ = process_threshold; + } +} + // A Bandwidth GD request for new resolution void CoordinatedVideoAdapter::OnEncoderResolutionRequest( int width, int height, AdaptRequest request) { @@ -552,22 +652,18 @@ void CoordinatedVideoAdapter::OnCpuLoadUpdated( // we'll still calculate this information, in case smoothing is later enabled. system_load_average_ = kCpuLoadWeightCoefficient * system_load + (1.0f - kCpuLoadWeightCoefficient) * system_load_average_; + ++cpu_load_num_samples_; if (cpu_smoothing_) { system_load = system_load_average_; } - // If we haven't started taking samples yet, wait until we have at least - // the correct number of samples per the wait time. - if (cpu_adapt_wait_time_ == 0) { - cpu_adapt_wait_time_ = talk_base::TimeAfter(kCpuLoadMinSampleTime); - } AdaptRequest request = FindCpuRequest(current_cpus, max_cpus, process_load, system_load); // Make sure we're not adapting too quickly. if (request != KEEP) { - if (talk_base::TimeIsLater(talk_base::Time(), - cpu_adapt_wait_time_)) { + if (cpu_load_num_samples_ < cpu_load_min_samples_) { LOG(LS_VERBOSE) << "VAdapt CPU load high/low but do not adapt until " - << talk_base::TimeUntil(cpu_adapt_wait_time_) << " ms"; + << (cpu_load_min_samples_ - cpu_load_num_samples_) + << " more samples"; request = KEEP; } } @@ -609,7 +705,7 @@ bool CoordinatedVideoAdapter::AdaptToMinimumFormat(int* new_width, } int old_num_pixels = GetOutputNumPixels(); int min_num_pixels = INT_MAX; - adapt_reason_ = 0; + adapt_reason_ = ADAPTREASON_NONE; // Reduce resolution based on encoder bandwidth (GD). if (encoder_desired_num_pixels_ && @@ -650,7 +746,7 @@ bool CoordinatedVideoAdapter::AdaptToMinimumFormat(int* new_width, static_cast<int>(input.height * scale + .5f); } if (scale == 1.0f) { - adapt_reason_ = 0; + adapt_reason_ = ADAPTREASON_NONE; } *new_width = new_output.width = static_cast<int>(input.width * scale + .5f); *new_height = new_output.height = static_cast<int>(input.height * scale + @@ -688,7 +784,7 @@ bool CoordinatedVideoAdapter::AdaptToMinimumFormat(int* new_width, if (changed) { // When any adaptation occurs, historic CPU load levels are no longer // accurate. Clear out our state so we can re-learn at the new normal. - cpu_adapt_wait_time_ = talk_base::TimeAfter(kCpuLoadMinSampleTime); + cpu_load_num_samples_ = 0; system_load_average_ = kCpuLoadInitialAverage; } diff --git a/chromium/third_party/libjingle/source/talk/media/base/videoadapter.h b/chromium/third_party/libjingle/source/talk/media/base/videoadapter.h index 12be4fab1ab..0634942d9be 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videoadapter.h +++ b/chromium/third_party/libjingle/source/talk/media/base/videoadapter.h @@ -28,7 +28,6 @@ #include "talk/base/common.h" // For ASSERT #include "talk/base/criticalsection.h" -#include "talk/base/logging.h" #include "talk/base/scoped_ptr.h" #include "talk/base/sigslot.h" #include "talk/media/base/videocommon.h" @@ -52,6 +51,8 @@ class VideoAdapter { int GetOutputNumPixels() const; const VideoFormat& input_format(); + // Returns true if the adapter is dropping frames in calls to AdaptFrame. + bool drops_all_frames() const; const VideoFormat& output_format(); // If the parameter black is true, the adapted frames will be black. void SetBlackOutput(bool black); @@ -60,15 +61,15 @@ class VideoAdapter { // true and set the output frame to NULL if the input frame is dropped. Return // true and set the out frame to output_frame_ if the input frame is adapted // successfully. Return false otherwise. - // output_frame_ is owned by the VideoAdapter that has the best knowledge on - // the output frame. - bool AdaptFrame(const VideoFrame* in_frame, VideoFrame** out_frame); - - void set_scale_third(bool enable) { - LOG(LS_INFO) << "Video Adapter third scaling is now " - << (enable ? "enabled" : "disabled"); - scale_third_ = enable; - } + // Note that, if no adaptation is required, |out_frame| will refer directly + // in_frame. If a copy is always required, the caller must do an explicit + // copy. + // If a copy has taken place, |output_frame_| is owned by the VideoAdapter + // and will remain usable until the adapter is destroyed or AdaptFrame is + // called again. + bool AdaptFrame(VideoFrame* in_frame, VideoFrame** out_frame); + + void set_scale_third(bool enable); bool scale_third() const { return scale_third_; } protected: @@ -87,8 +88,9 @@ class VideoAdapter { VideoFormat output_format_; int output_num_pixels_; bool scale_third_; // True if adapter allows scaling to 1/3 and 2/3. - int frames_; // Number of input frames. - int adapted_frames_; // Number of frames scaled. + int frames_in_; // Number of input frames. + int frames_out_; // Number of output frames. + int frames_scaled_; // Number of frames scaled. int adaption_changes_; // Number of changes in scale factor. size_t previous_width_; // Previous adapter output width. size_t previous_height_; // Previous adapter output height. @@ -110,6 +112,7 @@ class CoordinatedVideoAdapter public: enum AdaptRequest { UPGRADE, KEEP, DOWNGRADE }; enum AdaptReasonEnum { + ADAPTREASON_NONE = 0, ADAPTREASON_CPU = 1, ADAPTREASON_BANDWIDTH = 2, ADAPTREASON_VIEW = 4 @@ -127,11 +130,7 @@ class CoordinatedVideoAdapter // Enable or disable smoothing when doing CPU adaptation. When smoothing is // enabled, system CPU load is tracked using an exponential weighted // average. - void set_cpu_smoothing(bool enable) { - LOG(LS_INFO) << "CPU smoothing is now " - << (enable ? "enabled" : "disabled"); - cpu_smoothing_ = enable; - } + void set_cpu_smoothing(bool enable); bool cpu_smoothing() const { return cpu_smoothing_; } // Enable or disable video adaptation due to the change of the GD void set_gd_adaptation(bool enable) { gd_adaptation_ = enable; } @@ -149,46 +148,16 @@ class CoordinatedVideoAdapter // When the video is decreased, set the waiting time for CPU adaptation to // decrease video again. - void set_cpu_adapt_wait_time(uint32 cpu_adapt_wait_time) { - if (cpu_adapt_wait_time_ != static_cast<int>(cpu_adapt_wait_time)) { - LOG(LS_INFO) << "VAdapt Change Cpu Adapt Wait Time from: " - << cpu_adapt_wait_time_ << " to " - << cpu_adapt_wait_time; - cpu_adapt_wait_time_ = static_cast<int>(cpu_adapt_wait_time); - } - } + void set_cpu_load_min_samples(int cpu_load_min_samples); + int cpu_load_min_samples() const { return cpu_load_min_samples_; } // CPU system load high threshold for reducing resolution. e.g. 0.85f - void set_high_system_threshold(float high_system_threshold) { - ASSERT(high_system_threshold <= 1.0f); - ASSERT(high_system_threshold >= 0.0f); - if (high_system_threshold_ != high_system_threshold) { - LOG(LS_INFO) << "VAdapt Change High System Threshold from: " - << high_system_threshold_ << " to " << high_system_threshold; - high_system_threshold_ = high_system_threshold; - } - } + void set_high_system_threshold(float high_system_threshold); float high_system_threshold() const { return high_system_threshold_; } // CPU system load low threshold for increasing resolution. e.g. 0.70f - void set_low_system_threshold(float low_system_threshold) { - ASSERT(low_system_threshold <= 1.0f); - ASSERT(low_system_threshold >= 0.0f); - if (low_system_threshold_ != low_system_threshold) { - LOG(LS_INFO) << "VAdapt Change Low System Threshold from: " - << low_system_threshold_ << " to " << low_system_threshold; - low_system_threshold_ = low_system_threshold; - } - } + void set_low_system_threshold(float low_system_threshold); float low_system_threshold() const { return low_system_threshold_; } // CPU process load threshold for reducing resolution. e.g. 0.10f - void set_process_threshold(float process_threshold) { - ASSERT(process_threshold <= 1.0f); - ASSERT(process_threshold >= 0.0f); - if (process_threshold_ != process_threshold) { - LOG(LS_INFO) << "VAdapt Change High Process Threshold from: " - << process_threshold_ << " to " << process_threshold; - process_threshold_ = process_threshold; - } - } + void set_process_threshold(float process_threshold); float process_threshold() const { return process_threshold_; } // Handle the format request from the server via Jingle update message. @@ -220,7 +189,8 @@ class CoordinatedVideoAdapter bool view_adaptation_; // True if view adaptation is enabled. bool view_switch_; // True if view switch is enabled. int cpu_downgrade_count_; - int cpu_adapt_wait_time_; + int cpu_load_min_samples_; + int cpu_load_num_samples_; // cpu system load thresholds relative to max cpus. float high_system_threshold_; float low_system_threshold_; diff --git a/chromium/third_party/libjingle/source/talk/media/base/videocapturer.cc b/chromium/third_party/libjingle/source/talk/media/base/videocapturer.cc index 26fcfa9fbb4..59860a40ceb 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videocapturer.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/videocapturer.cc @@ -59,10 +59,16 @@ enum { }; static const int64 kMaxDistance = ~(static_cast<int64>(1) << 63); +#ifdef LINUX static const int kYU12Penalty = 16; // Needs to be higher than MJPG index. +#endif static const int kDefaultScreencastFps = 5; typedef talk_base::TypedMessageData<CaptureState> StateChangeParams; +// Limit stats data collections to ~20 seconds of 30fps data before dropping +// old data in case stats aren't reset for long periods of time. +static const size_t kMaxAccumulatorSize = 600; + } // namespace ///////////////////////////////////////////////////////////////////// @@ -92,11 +98,19 @@ bool CapturedFrame::GetDataSize(uint32* size) const { ///////////////////////////////////////////////////////////////////// // Implementation of class VideoCapturer ///////////////////////////////////////////////////////////////////// -VideoCapturer::VideoCapturer() : thread_(talk_base::Thread::Current()) { +VideoCapturer::VideoCapturer() + : thread_(talk_base::Thread::Current()), + adapt_frame_drops_data_(kMaxAccumulatorSize), + effect_frame_drops_data_(kMaxAccumulatorSize), + frame_time_data_(kMaxAccumulatorSize) { Construct(); } -VideoCapturer::VideoCapturer(talk_base::Thread* thread) : thread_(thread) { +VideoCapturer::VideoCapturer(talk_base::Thread* thread) + : thread_(thread), + adapt_frame_drops_data_(kMaxAccumulatorSize), + effect_frame_drops_data_(kMaxAccumulatorSize), + frame_time_data_(kMaxAccumulatorSize) { Construct(); } @@ -111,6 +125,10 @@ void VideoCapturer::Construct() { screencast_max_pixels_ = 0; muted_ = false; black_frame_count_down_ = kNumBlackFramesOnMute; + enable_video_adapter_ = true; + adapt_frame_drops_ = 0; + effect_frame_drops_ = 0; + previous_frame_time_ = 0.0; } const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const { @@ -118,6 +136,7 @@ const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const { } bool VideoCapturer::StartCapturing(const VideoFormat& capture_format) { + previous_frame_time_ = frame_length_time_reporter_.TimerNow(); CaptureState result = Start(capture_format); const bool success = (result == CS_RUNNING) || (result == CS_STARTING); if (!success) { @@ -257,7 +276,7 @@ bool VideoCapturer::GetBestCaptureFormat(const VideoFormat& format, best_format->width = best->width; best_format->height = best->height; best_format->fourcc = best->fourcc; - best_format->interval = talk_base::_max(format.interval, best->interval); + best_format->interval = best->interval; LOG(LS_INFO) << " Best " << best_format->ToString() << " Interval " << best_format->interval << " distance " << best_distance; } @@ -301,10 +320,25 @@ std::string VideoCapturer::ToString(const CapturedFrame* captured_frame) const { std::ostringstream ss; ss << fourcc_name << captured_frame->width << "x" << captured_frame->height - << "x" << VideoFormat::IntervalToFps(captured_frame->elapsed_time); + << "x" << VideoFormat::IntervalToFpsFloat(captured_frame->elapsed_time); return ss.str(); } +void VideoCapturer::GetStats(VariableInfo<int>* adapt_drops_stats, + VariableInfo<int>* effect_drops_stats, + VariableInfo<double>* frame_time_stats, + VideoFormat* last_captured_frame_format) { + talk_base::CritScope cs(&frame_stats_crit_); + GetVariableSnapshot(adapt_frame_drops_data_, adapt_drops_stats); + GetVariableSnapshot(effect_frame_drops_data_, effect_drops_stats); + GetVariableSnapshot(frame_time_data_, frame_time_stats); + *last_captured_frame_format = last_captured_frame_format_; + + adapt_frame_drops_data_.Reset(); + effect_frame_drops_data_.Reset(); + frame_time_data_.Reset(); +} + void VideoCapturer::OnFrameCaptured(VideoCapturer*, const CapturedFrame* captured_frame) { if (muted_) { @@ -477,23 +511,29 @@ void VideoCapturer::OnFrameCaptured(VideoCapturer*, } VideoFrame* adapted_frame = &i420_frame; - if (!SignalAdaptFrame.is_empty() && !IsScreencast()) { + if (enable_video_adapter_ && !IsScreencast()) { VideoFrame* out_frame = NULL; - SignalAdaptFrame(this, adapted_frame, &out_frame); + video_adapter_.AdaptFrame(adapted_frame, &out_frame); if (!out_frame) { - return; // VideoAdapter dropped the frame. + // VideoAdapter dropped the frame. + ++adapt_frame_drops_; + return; } adapted_frame = out_frame; } if (!muted_ && !ApplyProcessors(adapted_frame)) { // Processor dropped the frame. + ++effect_frame_drops_; return; } if (muted_) { adapted_frame->SetToBlack(); } SignalVideoFrame(this, adapted_frame); + + UpdateStats(captured_frame); + #endif // VIDEO_FRAME_NAME } @@ -577,9 +617,9 @@ int64 VideoCapturer::GetFormatDistance(const VideoFormat& desired, int desired_width = desired.width; int desired_height = desired.height; int64 delta_w = supported.width - desired_width; - int64 supported_fps = VideoFormat::IntervalToFps(supported.interval); - int64 delta_fps = - supported_fps - VideoFormat::IntervalToFps(desired.interval); + float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval); + float delta_fps = + supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval); // Check height of supported height compared to height we would like it to be. int64 aspect_h = desired_width ? supported.width * desired_height / desired_width @@ -606,9 +646,9 @@ int64 VideoCapturer::GetFormatDistance(const VideoFormat& desired, // Require camera fps to be at least 96% of what is requested, or higher, // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97 if (delta_fps < 0) { - int64 min_desirable_fps = delta_w ? - VideoFormat::IntervalToFps(desired.interval) * 29 / 30 : - VideoFormat::IntervalToFps(desired.interval) * 24 / 30; + float min_desirable_fps = delta_w ? + VideoFormat::IntervalToFpsFloat(desired.interval) * 28.f / 30.f : + VideoFormat::IntervalToFpsFloat(desired.interval) * 23.f / 30.f; delta_fps = -delta_fps; if (supported_fps < min_desirable_fps) { distance |= static_cast<int64>(1) << 62; @@ -616,10 +656,11 @@ int64 VideoCapturer::GetFormatDistance(const VideoFormat& desired, distance |= static_cast<int64>(1) << 15; } } + int64 idelta_fps = static_cast<int>(delta_fps); // 12 bits for width and height and 8 bits for fps and fourcc. distance |= - (delta_w << 28) | (delta_h << 16) | (delta_fps << 8) | delta_fourcc; + (delta_w << 28) | (delta_h << 16) | (idelta_fps << 8) | delta_fourcc; return distance; } @@ -667,4 +708,35 @@ bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const { format.height > max_format_->height; } +void VideoCapturer::UpdateStats(const CapturedFrame* captured_frame) { + // Update stats protected from fetches from different thread. + talk_base::CritScope cs(&frame_stats_crit_); + + last_captured_frame_format_.width = captured_frame->width; + last_captured_frame_format_.height = captured_frame->height; + // TODO(ronghuawu): Useful to report interval as well? + last_captured_frame_format_.interval = 0; + last_captured_frame_format_.fourcc = captured_frame->fourcc; + + double time_now = frame_length_time_reporter_.TimerNow(); + if (previous_frame_time_ != 0.0) { + adapt_frame_drops_data_.AddSample(adapt_frame_drops_); + effect_frame_drops_data_.AddSample(effect_frame_drops_); + frame_time_data_.AddSample(time_now - previous_frame_time_); + } + previous_frame_time_ = time_now; + effect_frame_drops_ = 0; + adapt_frame_drops_ = 0; +} + +template<class T> +void VideoCapturer::GetVariableSnapshot( + const talk_base::RollingAccumulator<T>& data, + VariableInfo<T>* stats) { + stats->max_val = data.ComputeMax(); + stats->mean = data.ComputeMean(); + stats->min_val = data.ComputeMin(); + stats->variance = data.ComputeVariance(); +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/base/videocapturer.h b/chromium/third_party/libjingle/source/talk/media/base/videocapturer.h index 2bd68bca162..6b1c46ddd35 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videocapturer.h +++ b/chromium/third_party/libjingle/source/talk/media/base/videocapturer.h @@ -34,9 +34,13 @@ #include "talk/base/basictypes.h" #include "talk/base/criticalsection.h" #include "talk/base/messagehandler.h" +#include "talk/base/rollingaccumulator.h" #include "talk/base/scoped_ptr.h" #include "talk/base/sigslot.h" #include "talk/base/thread.h" +#include "talk/base/timing.h" +#include "talk/media/base/mediachannel.h" +#include "talk/media/base/videoadapter.h" #include "talk/media/base/videocommon.h" #include "talk/media/devices/devicemanager.h" @@ -97,12 +101,10 @@ struct CapturedFrame { // capturing. The subclasses implement the video capturer for various types of // capturers and various platforms. // -// The captured frames may need to be adapted (for example, cropping). Adaptors -// can be registered to the capturer or applied externally to the capturer. -// If the adaptor is needed, it acts as the downstream of VideoCapturer, adapts -// the captured frames, and delivers the adapted frames to other components -// such as the encoder. Effects can also be registered to the capturer or -// applied externally. +// The captured frames may need to be adapted (for example, cropping). +// Video adaptation is built into and enabled by default. After a frame has +// been captured from the device, it is sent to the video adapter, then video +// processors, then out to the encoder. // // Programming model: // Create an object of a subclass of VideoCapturer @@ -111,6 +113,7 @@ struct CapturedFrame { // SignalFrameCaptured.connect() // Find the capture format for Start() by either calling GetSupportedFormats() // and selecting one of the supported or calling GetBestCaptureFormat(). +// video_adapter()->OnOutputFormatRequest(desired_encoding_format) // Start() // GetCaptureFormat() optionally // Stop() @@ -255,12 +258,8 @@ class VideoCapturer // Signal the captured frame to downstream. sigslot::signal2<VideoCapturer*, const CapturedFrame*, sigslot::multi_threaded_local> SignalFrameCaptured; - // If slots are connected to SignalAdaptFrame, this signals with parameters - // of this capturer instance, the input video frame and output frame - // pointer, respectively. - sigslot::signal3<VideoCapturer*, const VideoFrame*, VideoFrame**, - sigslot::multi_threaded_local> SignalAdaptFrame; - // Signal the captured frame converted to I420 to downstream. + // Signal the captured and possibly adapted frame to downstream consumers + // such as the encoder. sigslot::signal2<VideoCapturer*, const VideoFrame*, sigslot::multi_threaded_local> SignalVideoFrame; @@ -277,6 +276,27 @@ class VideoCapturer screencast_max_pixels_ = talk_base::_max(0, p); } + // If true, run video adaptation. By default, video adaptation is enabled + // and users must call video_adapter()->OnOutputFormatRequest() + // to receive frames. + bool enable_video_adapter() const { return enable_video_adapter_; } + void set_enable_video_adapter(bool enable_video_adapter) { + enable_video_adapter_ = enable_video_adapter; + } + + CoordinatedVideoAdapter* video_adapter() { return &video_adapter_; } + const CoordinatedVideoAdapter* video_adapter() const { + return &video_adapter_; + } + + // Gets statistics for tracked variables recorded since the last call to + // GetStats. Note that calling GetStats resets any gathered data so it + // should be called only periodically to log statistics. + void GetStats(VariableInfo<int>* adapt_drop_stats, + VariableInfo<int>* effect_drop_stats, + VariableInfo<double>* frame_time_stats, + VideoFormat* last_captured_frame_format); + protected: // Callback attached to SignalFrameCaptured where SignalVideoFrames is called. void OnFrameCaptured(VideoCapturer* video_capturer, @@ -297,6 +317,12 @@ class VideoCapturer void SetCaptureFormat(const VideoFormat* format) { capture_format_.reset(format ? new VideoFormat(*format) : NULL); + if (capture_format_) { + ASSERT(capture_format_->interval > 0 && + "Capture format expected to have positive interval."); + // Video adapter really only cares about capture format interval. + video_adapter_.SetInputFormat(*capture_format_); + } } void SetSupportedFormats(const std::vector<VideoFormat>& formats); @@ -323,6 +349,15 @@ class VideoCapturer // Returns true if format doesn't fulfill all applied restrictions. bool ShouldFilterFormat(const VideoFormat& format) const; + void UpdateStats(const CapturedFrame* captured_frame); + + // Helper function to save statistics on the current data from a + // RollingAccumulator into stats. + template<class T> + static void GetVariableSnapshot( + const talk_base::RollingAccumulator<T>& data, + VariableInfo<T>* stats); + talk_base::Thread* thread_; std::string id_; CaptureState capture_state_; @@ -341,6 +376,21 @@ class VideoCapturer bool muted_; int black_frame_count_down_; + bool enable_video_adapter_; + CoordinatedVideoAdapter video_adapter_; + + talk_base::Timing frame_length_time_reporter_; + talk_base::CriticalSection frame_stats_crit_; + + int adapt_frame_drops_; + talk_base::RollingAccumulator<int> adapt_frame_drops_data_; + int effect_frame_drops_; + talk_base::RollingAccumulator<int> effect_frame_drops_data_; + double previous_frame_time_; + talk_base::RollingAccumulator<double> frame_time_data_; + // The captured frame format before potential adapation. + VideoFormat last_captured_frame_format_; + talk_base::CriticalSection crit_; VideoProcessors video_processors_; diff --git a/chromium/third_party/libjingle/source/talk/media/base/videocapturer_unittest.cc b/chromium/third_party/libjingle/source/talk/media/base/videocapturer_unittest.cc index 82a95fb637d..9f025e37bb8 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videocapturer_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/videocapturer_unittest.cc @@ -31,6 +31,12 @@ const int kMsCallbackWait = 500; const int kMinHdHeight = 720; const uint32 kTimeout = 5000U; +void NormalizeVideoSize(int* expected_width, int* expected_height) { + // WebRtcVideoFrame truncates the frame size to a multiple of 4. + *expected_width = *expected_width & ~3; + *expected_height = *expected_height & ~3; +} + } // namespace // Sets the elapsed time in the video frame to 0. @@ -94,6 +100,7 @@ class VideoCapturerTest }; TEST_F(VideoCapturerTest, CaptureState) { + EXPECT_TRUE(capturer_.enable_video_adapter()); EXPECT_EQ(cricket::CS_RUNNING, capturer_.Start(cricket::VideoFormat( 640, 480, @@ -227,6 +234,59 @@ TEST_F(VideoCapturerTest, ScreencastScaledMaxPixels) { EXPECT_EQ(2, renderer_.num_rendered_frames()); } +TEST_F(VideoCapturerTest, ScreencastScaledOddWidth) { + capturer_.SetScreencast(true); + + int kWidth = 1281; + int kHeight = 720; + + std::vector<cricket::VideoFormat> formats; + formats.push_back(cricket::VideoFormat(kWidth, kHeight, + cricket::VideoFormat::FpsToInterval(5), cricket::FOURCC_ARGB)); + capturer_.ResetSupportedFormats(formats); + + EXPECT_EQ(cricket::CS_RUNNING, capturer_.Start(cricket::VideoFormat( + kWidth, + kHeight, + cricket::VideoFormat::FpsToInterval(30), + cricket::FOURCC_ARGB))); + EXPECT_TRUE(capturer_.IsRunning()); + EXPECT_EQ(0, renderer_.num_rendered_frames()); + int expected_width = kWidth; + int expected_height = kHeight; + NormalizeVideoSize(&expected_width, &expected_height); + renderer_.SetSize(expected_width, expected_height, 0); + EXPECT_TRUE(capturer_.CaptureFrame()); + EXPECT_EQ(1, renderer_.num_rendered_frames()); +} + +TEST_F(VideoCapturerTest, ScreencastScaledSuperLarge) { + capturer_.SetScreencast(true); + + const int kMaxWidth = 4096; + const int kMaxHeight = 3072; + int kWidth = kMaxWidth + 4; + int kHeight = kMaxHeight + 4; + + std::vector<cricket::VideoFormat> formats; + formats.push_back(cricket::VideoFormat(kWidth, kHeight, + cricket::VideoFormat::FpsToInterval(5), cricket::FOURCC_ARGB)); + capturer_.ResetSupportedFormats(formats); + + EXPECT_EQ(cricket::CS_RUNNING, capturer_.Start(cricket::VideoFormat( + kWidth, + kHeight, + cricket::VideoFormat::FpsToInterval(30), + cricket::FOURCC_ARGB))); + EXPECT_TRUE(capturer_.IsRunning()); + EXPECT_EQ(0, renderer_.num_rendered_frames()); + int expected_width = 2050; + int expected_height = 1538; + NormalizeVideoSize(&expected_width, &expected_height); + renderer_.SetSize(expected_width, expected_height, 0); + EXPECT_TRUE(capturer_.CaptureFrame()); + EXPECT_EQ(1, renderer_.num_rendered_frames()); +} TEST_F(VideoCapturerTest, TestFourccMatch) { cricket::VideoFormat desired(640, 480, @@ -507,23 +567,23 @@ TEST_F(VideoCapturerTest, TestFpsFormats) { cricket::VideoFormat::FpsToInterval(10), cricket::FOURCC_ANY)); cricket::VideoFormat best; - // expect 30 fps to choose 30 fps format + // Expect 30 fps to choose 30 fps format. EXPECT_TRUE(capturer_.GetBestCaptureFormat(required_formats[0], &best)); EXPECT_EQ(640, best.width); EXPECT_EQ(400, best.height); EXPECT_EQ(cricket::VideoFormat::FpsToInterval(30), best.interval); - // expect 20 fps to choose 20 fps format + // Expect 20 fps to choose 30 fps format. EXPECT_TRUE(capturer_.GetBestCaptureFormat(required_formats[1], &best)); EXPECT_EQ(640, best.width); EXPECT_EQ(400, best.height); - EXPECT_EQ(cricket::VideoFormat::FpsToInterval(20), best.interval); + EXPECT_EQ(cricket::VideoFormat::FpsToInterval(30), best.interval); - // expect 10 fps to choose 15 fps format but set fps to 10 + // Expect 10 fps to choose 15 fps format and set fps to 15. EXPECT_TRUE(capturer_.GetBestCaptureFormat(required_formats[2], &best)); EXPECT_EQ(640, best.width); EXPECT_EQ(480, best.height); - EXPECT_EQ(cricket::VideoFormat::FpsToInterval(10), best.interval); + EXPECT_EQ(cricket::VideoFormat::FpsToInterval(15), best.interval); // We have VGA 60 fps and 15 fps. Choose best fps. supported_formats.clear(); @@ -539,23 +599,23 @@ TEST_F(VideoCapturerTest, TestFpsFormats) { cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420)); capturer_.ResetSupportedFormats(supported_formats); - // expect 30 fps to choose 60 fps format, but will set best fps to 30 + // Expect 30 fps to choose 60 fps format and will set best fps to 60. EXPECT_TRUE(capturer_.GetBestCaptureFormat(required_formats[0], &best)); EXPECT_EQ(640, best.width); EXPECT_EQ(480, best.height); - EXPECT_EQ(cricket::VideoFormat::FpsToInterval(30), best.interval); + EXPECT_EQ(cricket::VideoFormat::FpsToInterval(60), best.interval); - // expect 20 fps to choose 60 fps format, but will set best fps to 20 + // Expect 20 fps to choose 60 fps format, and will set best fps to 60. EXPECT_TRUE(capturer_.GetBestCaptureFormat(required_formats[1], &best)); EXPECT_EQ(640, best.width); EXPECT_EQ(480, best.height); - EXPECT_EQ(cricket::VideoFormat::FpsToInterval(20), best.interval); + EXPECT_EQ(cricket::VideoFormat::FpsToInterval(60), best.interval); - // expect 10 fps to choose 10 fps + // Expect 10 fps to choose 15 fps. EXPECT_TRUE(capturer_.GetBestCaptureFormat(required_formats[2], &best)); EXPECT_EQ(640, best.width); EXPECT_EQ(480, best.height); - EXPECT_EQ(cricket::VideoFormat::FpsToInterval(10), best.interval); + EXPECT_EQ(cricket::VideoFormat::FpsToInterval(15), best.interval); } TEST_F(VideoCapturerTest, TestRequest16x10_9) { diff --git a/chromium/third_party/libjingle/source/talk/media/base/videocommon.cc b/chromium/third_party/libjingle/source/talk/media/base/videocommon.cc index b051d526a3c..12d0ee71015 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videocommon.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/videocommon.cc @@ -236,7 +236,8 @@ std::string VideoFormat::ToString() const { } std::ostringstream ss; - ss << fourcc_name << width << "x" << height << "x" << IntervalToFps(interval); + ss << fourcc_name << width << "x" << height << "x" + << IntervalToFpsFloat(interval); return ss.str(); } diff --git a/chromium/third_party/libjingle/source/talk/media/base/videocommon.h b/chromium/third_party/libjingle/source/talk/media/base/videocommon.h index cf24f6fbb39..c83a3d8d13a 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videocommon.h +++ b/chromium/third_party/libjingle/source/talk/media/base/videocommon.h @@ -212,11 +212,20 @@ struct VideoFormat : VideoFormatPod { } static int IntervalToFps(int64 interval) { - // Normalize the interval first. - interval = talk_base::_max(interval, kMinimumInterval); + if (!interval) { + return 0; + } return static_cast<int>(talk_base::kNumNanosecsPerSec / interval); } + static float IntervalToFpsFloat(int64 interval) { + if (!interval) { + return 0.f; + } + return static_cast<float>(talk_base::kNumNanosecsPerSec) / + static_cast<float>(interval); + } + bool operator==(const VideoFormat& format) const { return width == format.width && height == format.height && interval == format.interval && fourcc == format.fourcc; diff --git a/chromium/third_party/libjingle/source/talk/media/base/videocommon_unittest.cc b/chromium/third_party/libjingle/source/talk/media/base/videocommon_unittest.cc index 455a47b79a0..90bcd0aeceb 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videocommon_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/videocommon_unittest.cc @@ -57,6 +57,7 @@ TEST(VideoCommonTest, TestVideoFormatFps) { EXPECT_EQ(VideoFormat::kMinimumInterval, VideoFormat::FpsToInterval(0)); EXPECT_EQ(talk_base::kNumNanosecsPerSec / 20, VideoFormat::FpsToInterval(20)); EXPECT_EQ(20, VideoFormat::IntervalToFps(talk_base::kNumNanosecsPerSec / 20)); + EXPECT_EQ(0, VideoFormat::IntervalToFps(0)); } // Test IsSize0x0 @@ -70,7 +71,7 @@ TEST(VideoCommonTest, TestVideoFormatIsSize0x0) { // Test ToString: print fourcc when it is printable. TEST(VideoCommonTest, TestVideoFormatToString) { VideoFormat format; - EXPECT_EQ("0x0x10000", format.ToString()); + EXPECT_EQ("0x0x0", format.ToString()); format.fourcc = FOURCC_I420; format.width = 640; diff --git a/chromium/third_party/libjingle/source/talk/media/base/videoengine_unittest.h b/chromium/third_party/libjingle/source/talk/media/base/videoengine_unittest.h index d9266f2c4b4..382fb775e85 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videoengine_unittest.h +++ b/chromium/third_party/libjingle/source/talk/media/base/videoengine_unittest.h @@ -474,17 +474,29 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(cricket::CS_RUNNING, video_capturer_->Start(format)); EXPECT_TRUE(channel_->SetCapturer(kSsrc, video_capturer_.get())); } + // Utility method to setup an additional stream to send and receive video. + // Used to test send and recv between two streams. void SetUpSecondStream() { - EXPECT_TRUE(channel_->AddRecvStream( - cricket::StreamParams::CreateLegacy(kSsrc))); + SetUpSecondStreamWithNoRecv(); + // Setup recv for second stream. EXPECT_TRUE(channel_->AddRecvStream( cricket::StreamParams::CreateLegacy(kSsrc + 2))); + // Make the second renderer available for use by a new stream. + EXPECT_TRUE(channel_->SetRenderer(kSsrc + 2, &renderer2_)); + } + // Setup an additional stream just to send video. Defer add recv stream. + // This is required if you want to test unsignalled recv of video rtp packets. + void SetUpSecondStreamWithNoRecv() { // SetUp() already added kSsrc make sure duplicate SSRCs cant be added. + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(kSsrc))); EXPECT_FALSE(channel_->AddSendStream( cricket::StreamParams::CreateLegacy(kSsrc))); EXPECT_TRUE(channel_->AddSendStream( cricket::StreamParams::CreateLegacy(kSsrc + 2))); + // We dont add recv for the second stream. + // Setup the receive and renderer for second stream after send. video_capturer_2_.reset(new cricket::FakeVideoCapturer()); cricket::VideoFormat format(640, 480, cricket::VideoFormat::FpsToInterval(30), @@ -492,8 +504,6 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(cricket::CS_RUNNING, video_capturer_2_->Start(format)); EXPECT_TRUE(channel_->SetCapturer(kSsrc + 2, video_capturer_2_.get())); - // Make the second renderer available for use by a new stream. - EXPECT_TRUE(channel_->SetRenderer(kSsrc + 2, &renderer2_)); } virtual void TearDown() { channel_.reset(); @@ -524,7 +534,6 @@ class VideoMediaChannelTest : public testing::Test, if (video_capturer_) { EXPECT_EQ(cricket::CS_RUNNING, video_capturer_->Start(capture_format)); } - if (video_capturer_2_) { EXPECT_EQ(cricket::CS_RUNNING, video_capturer_2_->Start(capture_format)); } @@ -540,6 +549,12 @@ class VideoMediaChannelTest : public testing::Test, bool SetSend(bool send) { return channel_->SetSend(send); } + bool SetSendStreamFormat(uint32 ssrc, const cricket::VideoCodec& codec) { + return channel_->SetSendStreamFormat(ssrc, cricket::VideoFormat( + codec.width, codec.height, + cricket::VideoFormat::FpsToInterval(codec.framerate), + cricket::FOURCC_ANY)); + } int DrainOutgoingPackets() { int packets = 0; do { @@ -777,21 +792,53 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_FRAME_WAIT(3, codec.width, codec.height, kTimeout); EXPECT_EQ(2, renderer_.num_set_sizes()); } + void SendReceiveManyAndGetStats(const cricket::VideoCodec& codec, + int duration_sec, int fps) { + EXPECT_TRUE(SetOneCodec(codec)); + EXPECT_TRUE(SetSend(true)); + EXPECT_TRUE(channel_->SetRender(true)); + EXPECT_EQ(0, renderer_.num_rendered_frames()); + for (int i = 0; i < duration_sec; ++i) { + for (int frame = 1; frame <= fps; ++frame) { + EXPECT_TRUE(WaitAndSendFrame(1000 / fps)); + EXPECT_FRAME_WAIT(frame + i * fps, codec.width, codec.height, kTimeout); + } + cricket::VideoMediaInfo info; + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); + // For webrtc, |framerate_sent| and |framerate_rcvd| depend on periodic + // callbacks (1 sec). + // Received |fraction_lost| and |packets_lost| are from sent RTCP packet. + // One sent packet needed (sent about once per second). + // |framerate_input|, |framerate_decoded| and |framerate_output| are using + // RateTracker. RateTracker needs to be called twice (with >1 second in + // b/w calls) before a framerate is calculated. + // Therefore insert frames (and call GetStats each sec) for a few seconds + // before testing stats. + } + talk_base::scoped_ptr<const talk_base::Buffer> p(GetRtpPacket(0)); + EXPECT_EQ(codec.id, GetPayloadType(p.get())); + } + // Test that stats work properly for a 1-1 call. void GetStats() { - SendAndReceive(DefaultCodec()); + const int kDurationSec = 3; + const int kFps = 10; + SendReceiveManyAndGetStats(DefaultCodec(), kDurationSec, kFps); + cricket::VideoMediaInfo info; - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.senders.size()); // TODO(whyuan): bytes_sent and bytes_rcvd are different. Are both payload? + // For webrtc, bytes_sent does not include the RTP header length. EXPECT_GT(info.senders[0].bytes_sent, 0); EXPECT_EQ(NumRtpPackets(), info.senders[0].packets_sent); EXPECT_EQ(0.0, info.senders[0].fraction_lost); EXPECT_EQ(0, info.senders[0].firs_rcvd); + EXPECT_EQ(0, info.senders[0].plis_rcvd); EXPECT_EQ(0, info.senders[0].nacks_rcvd); - EXPECT_EQ(DefaultCodec().width, info.senders[0].frame_width); - EXPECT_EQ(DefaultCodec().height, info.senders[0].frame_height); + EXPECT_EQ(DefaultCodec().width, info.senders[0].send_frame_width); + EXPECT_EQ(DefaultCodec().height, info.senders[0].send_frame_height); EXPECT_GT(info.senders[0].framerate_input, 0); EXPECT_GT(info.senders[0].framerate_sent, 0); @@ -803,8 +850,10 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(NumRtpPackets(), info.receivers[0].packets_rcvd); EXPECT_EQ(0.0, info.receivers[0].fraction_lost); EXPECT_EQ(0, info.receivers[0].packets_lost); - EXPECT_EQ(0, info.receivers[0].packets_concealed); + // TODO(asapersson): Not set for webrtc. Handle missing stats. + // EXPECT_EQ(0, info.receivers[0].packets_concealed); EXPECT_EQ(0, info.receivers[0].firs_sent); + EXPECT_EQ(0, info.receivers[0].plis_sent); EXPECT_EQ(0, info.receivers[0].nacks_sent); EXPECT_EQ(DefaultCodec().width, info.receivers[0].frame_width); EXPECT_EQ(DefaultCodec().height, info.receivers[0].frame_height); @@ -839,19 +888,15 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_FRAME_ON_RENDERER_WAIT( renderer2, 1, DefaultCodec().width, DefaultCodec().height, kTimeout); cricket::VideoMediaInfo info; - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.senders.size()); // TODO(whyuan): bytes_sent and bytes_rcvd are different. Are both payload? + // For webrtc, bytes_sent does not include the RTP header length. EXPECT_GT(info.senders[0].bytes_sent, 0); EXPECT_EQ(NumRtpPackets(), info.senders[0].packets_sent); - EXPECT_EQ(0.0, info.senders[0].fraction_lost); - EXPECT_EQ(0, info.senders[0].firs_rcvd); - EXPECT_EQ(0, info.senders[0].nacks_rcvd); - EXPECT_EQ(DefaultCodec().width, info.senders[0].frame_width); - EXPECT_EQ(DefaultCodec().height, info.senders[0].frame_height); - EXPECT_GT(info.senders[0].framerate_input, 0); - EXPECT_GT(info.senders[0].framerate_sent, 0); + EXPECT_EQ(DefaultCodec().width, info.senders[0].send_frame_width); + EXPECT_EQ(DefaultCodec().height, info.senders[0].send_frame_height); ASSERT_EQ(2U, info.receivers.size()); for (size_t i = 0; i < info.receivers.size(); ++i) { @@ -859,16 +904,8 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(i + 1, info.receivers[i].ssrcs()[0]); EXPECT_EQ(NumRtpBytes(), info.receivers[i].bytes_rcvd); EXPECT_EQ(NumRtpPackets(), info.receivers[i].packets_rcvd); - EXPECT_EQ(0.0, info.receivers[i].fraction_lost); - EXPECT_EQ(0, info.receivers[i].packets_lost); - EXPECT_EQ(0, info.receivers[i].packets_concealed); - EXPECT_EQ(0, info.receivers[i].firs_sent); - EXPECT_EQ(0, info.receivers[i].nacks_sent); EXPECT_EQ(DefaultCodec().width, info.receivers[i].frame_width); EXPECT_EQ(DefaultCodec().height, info.receivers[i].frame_height); - EXPECT_GT(info.receivers[i].framerate_rcvd, 0); - EXPECT_GT(info.receivers[i].framerate_decoded, 0); - EXPECT_GT(info.receivers[i].framerate_output, 0); } } // Test that stats work properly for a conf call with multiple send streams. @@ -893,8 +930,11 @@ class VideoMediaChannelTest : public testing::Test, talk_base::scoped_ptr<cricket::FakeVideoCapturer> capturer( new cricket::FakeVideoCapturer); capturer->SetScreencast(true); - cricket::VideoFormat format(1024, 768, - cricket::VideoFormat::FpsToInterval(5), 0); + const int kTestWidth = 160; + const int kTestHeight = 120; + cricket::VideoFormat format(kTestWidth, kTestHeight, + cricket::VideoFormat::FpsToInterval(5), + cricket::FOURCC_I420); EXPECT_EQ(cricket::CS_RUNNING, capturer->Start(format)); EXPECT_TRUE(channel_->AddSendStream( cricket::StreamParams::CreateLegacy(5678))); @@ -902,37 +942,39 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_TRUE(channel_->AddRecvStream( cricket::StreamParams::CreateLegacy(5678))); EXPECT_TRUE(channel_->SetRenderer(5678, &renderer1)); - EXPECT_TRUE(capturer->CaptureCustomFrame(1024, 768, cricket::FOURCC_I420)); - EXPECT_FRAME_ON_RENDERER_WAIT(renderer1, 1, 1024, 768, kTimeout); + EXPECT_TRUE(capturer->CaptureCustomFrame( + kTestWidth, kTestHeight, cricket::FOURCC_I420)); + EXPECT_FRAME_ON_RENDERER_WAIT( + renderer1, 1, kTestWidth, kTestHeight, kTimeout); // Get stats, and make sure they are correct for two senders. cricket::VideoMediaInfo info; - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(2U, info.senders.size()); EXPECT_EQ(NumRtpPackets(), info.senders[0].packets_sent + info.senders[1].packets_sent); EXPECT_EQ(1U, info.senders[0].ssrcs().size()); EXPECT_EQ(1234U, info.senders[0].ssrcs()[0]); - EXPECT_EQ(DefaultCodec().width, info.senders[0].frame_width); - EXPECT_EQ(DefaultCodec().height, info.senders[0].frame_height); + EXPECT_EQ(DefaultCodec().width, info.senders[0].send_frame_width); + EXPECT_EQ(DefaultCodec().height, info.senders[0].send_frame_height); EXPECT_EQ(1U, info.senders[1].ssrcs().size()); EXPECT_EQ(5678U, info.senders[1].ssrcs()[0]); - EXPECT_EQ(1024, info.senders[1].frame_width); - EXPECT_EQ(768, info.senders[1].frame_height); + EXPECT_EQ(kTestWidth, info.senders[1].send_frame_width); + EXPECT_EQ(kTestHeight, info.senders[1].send_frame_height); // The capturer must be unregistered here as it runs out of it's scope next. EXPECT_TRUE(channel_->SetCapturer(5678, NULL)); } - // Test that we can set the bandwidth to auto or a specific value. + // Test that we can set the bandwidth. void SetSendBandwidth() { - EXPECT_TRUE(channel_->SetSendBandwidth(true, -1)); - EXPECT_TRUE(channel_->SetSendBandwidth(true, 128 * 1024)); - EXPECT_TRUE(channel_->SetSendBandwidth(false, -1)); - EXPECT_TRUE(channel_->SetSendBandwidth(false, 128 * 1024)); + EXPECT_TRUE(channel_->SetStartSendBandwidth(64 * 1024)); + EXPECT_TRUE(channel_->SetMaxSendBandwidth(-1)); // <= 0 means unlimited. + EXPECT_TRUE(channel_->SetMaxSendBandwidth(128 * 1024)); } // Test that we can set the SSRC for the default send source. void SetSendSsrc() { EXPECT_TRUE(SetDefaultCodec()); + EXPECT_TRUE(SetSendStreamFormat(kSsrc, DefaultCodec())); EXPECT_TRUE(SetSend(true)); EXPECT_TRUE(SendFrame()); EXPECT_TRUE_WAIT(NumRtpPackets() > 0, kTimeout); @@ -954,6 +996,7 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_TRUE(channel_->AddSendStream( cricket::StreamParams::CreateLegacy(999))); EXPECT_TRUE(channel_->SetCapturer(999u, video_capturer_.get())); + EXPECT_TRUE(SetSendStreamFormat(999u, DefaultCodec())); EXPECT_TRUE(SetSend(true)); EXPECT_TRUE(WaitAndSendFrame(0)); EXPECT_TRUE_WAIT(NumRtpPackets() > 0, kTimeout); @@ -1180,6 +1223,8 @@ class VideoMediaChannelTest : public testing::Test, // some (e.g. 1) of these 3 frames after the renderer is set again. EXPECT_GT_FRAME_ON_RENDERER_WAIT( renderer1, 2, DefaultCodec().width, DefaultCodec().height, kTimeout); + // Detach |renderer1| before exit as there might be frames come late. + EXPECT_TRUE(channel_->SetRenderer(kSsrc, NULL)); } // Tests the behavior of incoming streams in a conference scenario. @@ -1221,9 +1266,11 @@ class VideoMediaChannelTest : public testing::Test, // Tests that we can add and remove capturers and frames are sent out properly void AddRemoveCapturer() { - const cricket::VideoCodec codec(DefaultCodec()); + cricket::VideoCodec codec = DefaultCodec(); + codec.width = 320; + codec.height = 240; const int time_between_send = TimeBetweenSend(codec); - EXPECT_TRUE(SetDefaultCodec()); + EXPECT_TRUE(SetOneCodec(codec)); EXPECT_TRUE(SetSend(true)); EXPECT_TRUE(channel_->SetRender(true)); EXPECT_EQ(0, renderer_.num_rendered_frames()); @@ -1232,8 +1279,9 @@ class VideoMediaChannelTest : public testing::Test, talk_base::scoped_ptr<cricket::FakeVideoCapturer> capturer( new cricket::FakeVideoCapturer); capturer->SetScreencast(true); - cricket::VideoFormat format(1024, 768, - cricket::VideoFormat::FpsToInterval(30), 0); + cricket::VideoFormat format(480, 360, + cricket::VideoFormat::FpsToInterval(30), + cricket::FOURCC_I420); EXPECT_EQ(cricket::CS_RUNNING, capturer->Start(format)); // All capturers start generating frames with the same timestamp. ViE does // not allow the same timestamp to be used. Capture one frame before @@ -1305,11 +1353,6 @@ class VideoMediaChannelTest : public testing::Test, void AddRemoveCapturerMultipleSources() { // WebRTC implementation will drop frames if pushed to quickly. Wait the // interval time to avoid that. - const cricket::VideoFormat send_format( - 1024, - 768, - cricket::VideoFormat::FpsToInterval(30), - 0); // WebRTC implementation will drop frames if pushed to quickly. Wait the // interval time to avoid that. // Set up the stream associated with the engine. @@ -1352,11 +1395,17 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_TRUE(SetSend(true)); EXPECT_TRUE(channel_->SetRender(true)); // Test capturer associated with engine. - EXPECT_TRUE(capturer1->CaptureCustomFrame(1024, 768, cricket::FOURCC_I420)); - EXPECT_FRAME_ON_RENDERER_WAIT(renderer1, 1, 1024, 768, kTimeout); + const int kTestWidth = 160; + const int kTestHeight = 120; + EXPECT_TRUE(capturer1->CaptureCustomFrame( + kTestWidth, kTestHeight, cricket::FOURCC_I420)); + EXPECT_FRAME_ON_RENDERER_WAIT( + renderer1, 1, kTestWidth, kTestHeight, kTimeout); // Capture a frame with additional capturer2, frames should be received - EXPECT_TRUE(capturer2->CaptureCustomFrame(1024, 768, cricket::FOURCC_I420)); - EXPECT_FRAME_ON_RENDERER_WAIT(renderer2, 1, 1024, 768, kTimeout); + EXPECT_TRUE(capturer2->CaptureCustomFrame( + kTestWidth, kTestHeight, cricket::FOURCC_I420)); + EXPECT_FRAME_ON_RENDERER_WAIT( + renderer2, 1, kTestWidth, kTestHeight, kTimeout); // Successfully remove the capturer. EXPECT_TRUE(channel_->SetCapturer(kSsrc, NULL)); // Fail to re-remove the capturer. @@ -1533,6 +1582,7 @@ class VideoMediaChannelTest : public testing::Test, // frames being dropped. void SetSendStreamFormat0x0() { EXPECT_TRUE(SetOneCodec(DefaultCodec())); + EXPECT_TRUE(SetSendStreamFormat(kSsrc, DefaultCodec())); EXPECT_TRUE(SetSend(true)); EXPECT_TRUE(channel_->SetRender(true)); EXPECT_EQ(0, renderer_.num_rendered_frames()); @@ -1635,33 +1685,156 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(1, renderer2_.num_rendered_frames()); } - // Disconnect the first stream and re-use it with another SSRC + // Set up 2 streams where the first stream uses the default channel. + // Then disconnect the first stream and verify default channel becomes + // available. + // Then add a new stream with |new_ssrc|. The new stream should re-use the + // default channel. void TwoStreamsReUseFirstStream(const cricket::VideoCodec& codec) { SetUpSecondStream(); + // Default channel used by the first stream. + EXPECT_EQ(kSsrc, channel_->GetDefaultChannelSsrc()); EXPECT_TRUE(channel_->RemoveRecvStream(kSsrc)); EXPECT_FALSE(channel_->RemoveRecvStream(kSsrc)); - // SSRC 0 should map to the "default" stream. I.e. the first added stream. - EXPECT_TRUE(channel_->RemoveSendStream(0)); - // Make sure that the first added stream was indeed the "default" stream. + EXPECT_TRUE(channel_->RemoveSendStream(kSsrc)); EXPECT_FALSE(channel_->RemoveSendStream(kSsrc)); - // Make sure that the "default" stream is indeed removed and that removing - // the default stream has an effect. - EXPECT_FALSE(channel_->RemoveSendStream(0)); - + // Default channel is no longer used by a stream. + EXPECT_EQ(0u, channel_->GetDefaultChannelSsrc()); SetRendererAsDefault(); + uint32 new_ssrc = kSsrc + 100; EXPECT_TRUE(channel_->AddSendStream( - cricket::StreamParams::CreateLegacy(kSsrc))); + cricket::StreamParams::CreateLegacy(new_ssrc))); + // Re-use default channel. + EXPECT_EQ(new_ssrc, channel_->GetDefaultChannelSsrc()); EXPECT_FALSE(channel_->AddSendStream( - cricket::StreamParams::CreateLegacy(kSsrc))); + cricket::StreamParams::CreateLegacy(new_ssrc))); EXPECT_TRUE(channel_->AddRecvStream( - cricket::StreamParams::CreateLegacy(kSsrc))); + cricket::StreamParams::CreateLegacy(new_ssrc))); EXPECT_FALSE(channel_->AddRecvStream( - cricket::StreamParams::CreateLegacy(kSsrc))); + cricket::StreamParams::CreateLegacy(new_ssrc))); - EXPECT_TRUE(channel_->SetCapturer(kSsrc, video_capturer_.get())); + EXPECT_TRUE(channel_->SetCapturer(new_ssrc, video_capturer_.get())); SendAndReceive(codec); - EXPECT_TRUE(channel_->RemoveSendStream(0)); + EXPECT_TRUE(channel_->RemoveSendStream(new_ssrc)); + EXPECT_EQ(0u, channel_->GetDefaultChannelSsrc()); + } + + // Tests that we can send and receive frames with early receive. + void TwoStreamsSendAndUnsignalledRecv(const cricket::VideoCodec& codec) { + cricket::VideoOptions vmo; + vmo.conference_mode.Set(true); + vmo.unsignalled_recv_stream_limit.Set(1); + EXPECT_TRUE(channel_->SetOptions(vmo)); + SetUpSecondStreamWithNoRecv(); + // Test sending and receiving on first stream. + EXPECT_TRUE(channel_->SetRender(true)); + Send(codec); + EXPECT_EQ_WAIT(2, NumRtpPackets(), kTimeout); + EXPECT_EQ_WAIT(1, renderer_.num_rendered_frames(), kTimeout); + // The first send is not expected to yield frames, because the ssrc + // is not signalled yet. With unsignalled recv enabled, we will drop frames + // instead of packets. + EXPECT_EQ(0, renderer2_.num_rendered_frames()); + // Give a chance for the decoder to process before adding the receiver. + talk_base::Thread::Current()->ProcessMessages(100); + // Test sending and receiving on second stream. + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(kSsrc + 2))); + EXPECT_TRUE(channel_->SetRenderer(kSsrc + 2, &renderer2_)); + SendFrame(); + EXPECT_EQ_WAIT(2, renderer_.num_rendered_frames(), kTimeout); + EXPECT_EQ(4, NumRtpPackets()); + // The second send is expected to yield frame as the ssrc is signalled now. + // Decode should succeed here, though we received the key frame earlier. + // Without early recv, we would have dropped it and decoding would have + // failed. + EXPECT_EQ_WAIT(1, renderer2_.num_rendered_frames(), kTimeout); + } + + // Tests that we cannot receive key frames with unsignalled recv disabled. + void TwoStreamsSendAndFailUnsignalledRecv(const cricket::VideoCodec& codec) { + cricket::VideoOptions vmo; + vmo.conference_mode.Set(true); + vmo.unsignalled_recv_stream_limit.Set(0); + EXPECT_TRUE(channel_->SetOptions(vmo)); + SetUpSecondStreamWithNoRecv(); + // Test sending and receiving on first stream. + EXPECT_TRUE(channel_->SetRender(true)); + Send(codec); + EXPECT_EQ_WAIT(2, NumRtpPackets(), kTimeout); + talk_base::Thread::Current()->ProcessMessages(100); + EXPECT_EQ_WAIT(1, renderer_.num_rendered_frames(), kTimeout); + EXPECT_EQ_WAIT(0, renderer2_.num_rendered_frames(), kTimeout); + // Give a chance for the decoder to process before adding the receiver. + talk_base::Thread::Current()->ProcessMessages(10); + // Test sending and receiving on second stream. + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(kSsrc + 2))); + EXPECT_TRUE(channel_->SetRenderer(kSsrc + 2, &renderer2_)); + SendFrame(); + EXPECT_TRUE_WAIT(renderer_.num_rendered_frames() >= 1, kTimeout); + EXPECT_EQ_WAIT(4, NumRtpPackets(), kTimeout); + // We dont expect any frames here, because the key frame would have been + // lost in the earlier packet. This is the case we want to solve with early + // receive. + EXPECT_EQ(0, renderer2_.num_rendered_frames()); + } + + // Tests that we drop key frames when conference mode is disabled and we + // receive rtp packets on unsignalled streams. + void TwoStreamsSendAndFailUnsignalledRecvInOneToOne( + const cricket::VideoCodec& codec) { + cricket::VideoOptions vmo; + vmo.conference_mode.Set(false); + vmo.unsignalled_recv_stream_limit.Set(1); + EXPECT_TRUE(channel_->SetOptions(vmo)); + SetUpSecondStreamWithNoRecv(); + // Test sending and receiving on first stream. + EXPECT_TRUE(channel_->SetRender(true)); + Send(codec); + EXPECT_EQ_WAIT(2, NumRtpPackets(), kTimeout); + // In one-to-one mode, we deliver frames to the default channel if there + // is no registered recv channel for the ssrc. + EXPECT_TRUE_WAIT(renderer_.num_rendered_frames() >= 1, kTimeout); + // Give a chance for the decoder to process before adding the receiver. + talk_base::Thread::Current()->ProcessMessages(100); + // Test sending and receiving on second stream. + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(kSsrc + 2))); + EXPECT_TRUE(channel_->SetRenderer(kSsrc + 2, &renderer2_)); + SendFrame(); + EXPECT_TRUE_WAIT(renderer_.num_rendered_frames() >= 1, kTimeout); + EXPECT_EQ_WAIT(4, NumRtpPackets(), kTimeout); + // We dont expect any frames here, because the key frame would have been + // delivered to default channel. + EXPECT_EQ(0, renderer2_.num_rendered_frames()); + } + + // Tests that we drop key frames when conference mode is enabled and we + // receive rtp packets on unsignalled streams. Removal of a unsignalled recv + // stream is successful. + void TwoStreamsAddAndRemoveUnsignalledRecv( + const cricket::VideoCodec& codec) { + cricket::VideoOptions vmo; + vmo.conference_mode.Set(true); + vmo.unsignalled_recv_stream_limit.Set(1); + EXPECT_TRUE(channel_->SetOptions(vmo)); + SetUpSecondStreamWithNoRecv(); + // Sending and receiving on first stream. + EXPECT_TRUE(channel_->SetRender(true)); + Send(codec); + EXPECT_EQ_WAIT(2, NumRtpPackets(), kTimeout); + EXPECT_EQ_WAIT(1, renderer_.num_rendered_frames(), kTimeout); + // The first send is not expected to yield frames, because the ssrc + // is no signalled yet. With unsignalled recv enabled, we will drop frames + // instead of packets. + EXPECT_EQ(0, renderer2_.num_rendered_frames()); + // Give a chance for the decoder to process before adding the receiver. + talk_base::Thread::Current()->ProcessMessages(100); + // Ensure that we can remove the unsignalled recv stream that was created + // when the first video packet with unsignalled recv ssrc is received. + EXPECT_TRUE(channel_->RemoveRecvStream(kSsrc + 2)); } VideoEngineOverride<E> engine_; diff --git a/chromium/third_party/libjingle/source/talk/media/base/videoframe.cc b/chromium/third_party/libjingle/source/talk/media/base/videoframe.cc index 7a82305f55f..cf5f852fc95 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videoframe.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/videoframe.cc @@ -27,7 +27,7 @@ #include "talk/media/base/videoframe.h" -#include <cstring> +#include <string.h> #if !defined(DISABLE_YUV) #include "libyuv/compare.h" diff --git a/chromium/third_party/libjingle/source/talk/media/base/yuvframegenerator.cc b/chromium/third_party/libjingle/source/talk/media/base/yuvframegenerator.cc new file mode 100644 index 00000000000..57b5314361a --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/base/yuvframegenerator.cc @@ -0,0 +1,262 @@ +#include "talk/media/base/yuvframegenerator.h" + +#include <string.h> +#include <sstream> + +#include "talk/base/basictypes.h" +#include "talk/base/common.h" + +namespace cricket { + +// These values were figured out by trial and error. If you change any +// basic parameters e.g. unit-bar size or bars-x-offset, you may need to change +// background-width/background-height. +const int kBarcodeBackgroundWidth = 160; +const int kBarcodeBackgroundHeight = 100; +const int kBarsXOffset = 12; +const int kBarsYOffset = 4; +const int kUnitBarSize = 2; +const int kBarcodeNormalBarHeight = 80; +const int kBarcodeGuardBarHeight = 96; +const int kBarcodeMaxEncodableDigits = 7; + +YuvFrameGenerator::YuvFrameGenerator(int width, int height, + bool enable_barcode) { + width_ = width; + height_ = height; + frame_index_ = 0; + int size = width_ * height_; + int qsize = size / 4; + frame_data_size_ = size + 2 * qsize; + y_data_ = new uint8[size]; + u_data_ = new uint8[qsize]; + v_data_ = new uint8[qsize]; + if (enable_barcode) { + ASSERT(width_ >= kBarcodeBackgroundWidth); + ASSERT(height_>= kBarcodeBackgroundHeight); + barcode_start_x_ = 0; + barcode_start_y_ = height_ - kBarcodeBackgroundHeight; + } else { + barcode_start_x_ = -1; + barcode_start_y_ = -1; + } +} + +YuvFrameGenerator::~YuvFrameGenerator() { + delete y_data_; + delete u_data_; + delete v_data_; +} + +void YuvFrameGenerator::GenerateNextFrame(uint8* frame_buffer, + int32 barcode_value) { + int size = width_ * height_; + int qsize = size / 4; + memset(y_data_, 0, size); + memset(u_data_, 0, qsize); + memset(v_data_, 0, qsize); + + DrawLandscape(y_data_, width_, height_); + DrawGradientX(u_data_, width_/2, height_/2); + DrawGradientY(v_data_, width_/2, height_/2); + DrawMovingLineX(u_data_, width_/2, height_/2, frame_index_); + DrawMovingLineY(v_data_, width_/2, height_/2, frame_index_); + DrawBouncingCube(y_data_, width_, height_, frame_index_); + + if (barcode_value >= 0) { + ASSERT(barcode_start_x_ != -1); + DrawBarcode(barcode_value); + } + + memcpy(frame_buffer, y_data_, size); + frame_buffer += size; + memcpy(frame_buffer, u_data_, qsize); + frame_buffer += qsize; + memcpy(frame_buffer, v_data_, qsize); + + frame_index_ = (frame_index_ + 1) & 0x0000FFFF; +} + +void YuvFrameGenerator::DrawLandscape(uint8 *p, int w, int h) { + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + p[x + y * w] = x % (y+1); + if (((x > w / 2 - (w / 32)) && (x < w / 2 + (w / 32))) || + ((y > h / 2 - (h / 32)) && (y < h / 2 + (h / 32)))) { + p[x + y * w] = (((x + y) / 8 % 2)) ? 255 : 0; + } + } + } +} + +void YuvFrameGenerator::DrawGradientX(uint8 *p, int w, int h) { + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + p[x + y * w] = (x << 8) / w; + } + } +} + +void YuvFrameGenerator::DrawGradientY(uint8 *p, int w, int h) { + int x, y; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + p[x + y * w] = (y << 8) / h; + } + } +} + +void YuvFrameGenerator::DrawMovingLineX(uint8 *p, int w, int h, int n) { + int x, y; + x = n % (w * 2); + if (x >= w) x = w + w - x - 1; + for (y = 0; y < h; y++) { + p[x + y * w] = 255; + } +} + +void YuvFrameGenerator::DrawMovingLineY(uint8 *p, int w, int h, int n) { + int x, y; + y = n % (h * 2); + if (y >= h) y = h + h - y - 1; + for (x = 0; x < w; x++) { + p[x + y * w] = 255; + } +} + +void YuvFrameGenerator::DrawBouncingCube(uint8 *p, int w, int h, int n) { + int x, y, pw, ph, px, py; + pw = w / 16; + ph = h / 16; + px = n % (w * 2); + py = n % (h * 2); + if (px >= w) px = w + w - px - 1; + if (py >= h) py = h + h - py - 1; + for (y = py - ph; y < py + ph; y++) { + if (y >=0 && y < h) { + for (x = px - pw; x < px + pw; x++) { + if (x >= 0 && x < w) { + p[x + y * w] = 255; + } + } + } + } +} + +void YuvFrameGenerator::GetBarcodeBounds(int* top, int* left, + int* width, int* height) { + ASSERT(barcode_start_x_ != -1); + *top = barcode_start_y_; + *left = barcode_start_x_; + *width = kBarcodeBackgroundWidth; + *height = kBarcodeBackgroundHeight; +} + +static void ComputeBarcodeDigits(uint32 value, std::stringstream* result) { + // Serialize |value| as 7-char string, padded with 0's to the left. + result->width(kBarcodeMaxEncodableDigits); + result->fill('0'); + *result << value; + + // Compute check-digit and append to result. Steps described here: + // http://en.wikipedia.org/wiki/European_Article_Number#Calculation_of_checksum_digit + int sum = 0; + for (int pos = 1; pos <= kBarcodeMaxEncodableDigits; pos++) { + char next_char; + result->get(next_char); + uint8 digit = next_char - '0'; + sum += digit * (pos % 2 ? 3 : 1); + } + uint8 check_digit = sum % 10; + if (check_digit != 0) { + check_digit = 10 - check_digit; + } + + *result << static_cast<int>(check_digit); + result->seekg(0); +} + +void YuvFrameGenerator::DrawBarcode(uint32 value) { + std::stringstream value_str_stream; + ComputeBarcodeDigits(value, &value_str_stream); + + // Draw white filled rectangle as background to barcode. + DrawBlockRectangle(y_data_, barcode_start_x_, barcode_start_y_, + kBarcodeBackgroundWidth, kBarcodeBackgroundHeight, + width_, 255); + DrawBlockRectangle(u_data_, barcode_start_x_ / 2, barcode_start_y_ / 2, + kBarcodeBackgroundWidth / 2, kBarcodeBackgroundHeight / 2, + width_ / 2, 128); + DrawBlockRectangle(v_data_, barcode_start_x_ / 2, barcode_start_y_ / 2, + kBarcodeBackgroundWidth / 2, kBarcodeBackgroundHeight / 2, + width_ / 2, 128); + + // Scan through chars (digits) and draw black bars. + int x = barcode_start_x_ + kBarsXOffset; + int y = barcode_start_y_ + kBarsYOffset; + int pos = 0; + x = DrawSideGuardBars(x, y, kBarcodeGuardBarHeight); + while (true) { + char next_char; + value_str_stream.get(next_char); + if (!value_str_stream.good()) { + break; + } + if (pos++ == 4) { + x = DrawMiddleGuardBars(x, y, kBarcodeGuardBarHeight); + } + uint8 digit = next_char - '0'; + x = DrawEanEncodedDigit(digit, x, y, kBarcodeNormalBarHeight, pos > 4); + } + x = DrawSideGuardBars(x, y, kBarcodeGuardBarHeight); +} + +int YuvFrameGenerator::DrawMiddleGuardBars(int x, int y, int height) { + x += kUnitBarSize; + DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0); + x += (kUnitBarSize * 2); + DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0); + return x + (kUnitBarSize * 2); +} + +int YuvFrameGenerator::DrawSideGuardBars(int x, int y, int height) { + DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0); + x += (kUnitBarSize * 2); + DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0); + return x + kUnitBarSize; +} + +// For each digit: 0-9, |kEanEncodings| contains a bit-mask indicating +// which bars are black (1) and which are blank (0). These are for the L-code +// only. R-code values are bitwise negation of these. Reference: +// http://en.wikipedia.org/wiki/European_Article_Number#Binary_encoding_of_data_digits_into_EAN-13_barcode // NOLINT +const uint8 kEanEncodings[] = { 13, 25, 19, 61, 35, 49, 47, 59, 55, 11 }; + +int YuvFrameGenerator::DrawEanEncodedDigit(int digit, int x, int y, + int height, bool flip) { + uint8 ean_encoding = kEanEncodings[digit]; + if (flip) { + ean_encoding = ~ean_encoding; + } + uint8 mask = 0x40; + for (int i = 6; i >= 0; i--, mask >>= 1) { + if (ean_encoding & mask) { + DrawBlockRectangle(y_data_, x, y, kUnitBarSize, height, width_, 0); + } + x += kUnitBarSize; + } + return x; +} + +void YuvFrameGenerator::DrawBlockRectangle(uint8* p, + int x_start, int y_start, int width, int height, int pitch, uint8 value) { + for (int x = x_start; x < x_start + width; x++) { + for (int y = y_start; y < y_start + height; y++) { + p[x + y * pitch] = value; + } + } +} + +} // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/base/yuvframegenerator.h b/chromium/third_party/libjingle/source/talk/media/base/yuvframegenerator.h new file mode 100644 index 00000000000..4adf971f640 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/base/yuvframegenerator.h @@ -0,0 +1,78 @@ +// Generates YUV420 frames with a "landscape with striped crosshair" in the +// Y-plane, plus a horizontal gradient in the U-plane and a vertical one in the +// V-plane. This makes for a nice mix of colours that is suited for both +// catching visual errors and making sure e.g. YUV->RGB/BGR conversion looks +// the same on different platforms. +// There is also a solid box bouncing around in the Y-plane, and two differently +// coloured lines bouncing horizontally and vertically in the U and V plane. +// This helps illustrating how the frame boundary goes, and can aid as a quite +// handy visual help for noticing e.g. packet loss if the frames are encoded +// and sent over the network. + +#ifndef TALK_MEDIA_BASE_YUVFRAMEGENERATOR_H_ +#define TALK_MEDIA_BASE_YUVFRAMEGENERATOR_H_ + +#include "talk/base/basictypes.h" + +namespace cricket { + +class YuvFrameGenerator { + public: + // Constructs a frame-generator that produces frames of size |width|x|height|. + // If |enable_barcode| is specified, barcodes can be included in the frames + // when calling |GenerateNextFrame(uint8*, uint32)|. If |enable_barcode| is + // |true| then |width|x|height| should be at least 160x100; otherwise this + // constructor will abort. + YuvFrameGenerator(int width, int height, bool enable_barcode); + ~YuvFrameGenerator(); + + int GetFrameSize() { return frame_data_size_; } + + // Generate the next frame and return it in the provided |frame_buffer|. If + // barcode_value is not |nullptr| the value referred by it will be encoded + // into a barcode in the frame. The value should in the range: + // [0..9,999,999]. If the value exceeds this range or barcodes were not + // requested in the constructor, this function will abort. + void GenerateNextFrame(uint8* frame_buffer, int32 barcode_value); + + int GetHeight() { return height_; } + int GetWidth() { return width_; } + + // Fetch the bounds of the barcode from the generator. The barcode will + // always be at this location. This function will abort if barcodes were not + // requested in the constructor. + void GetBarcodeBounds(int* top, int* left, int* width, int* height); + + private: + void DrawLandscape(uint8 *p, int w, int h); + void DrawGradientX(uint8 *p, int w, int h); + void DrawGradientY(uint8 *p, int w, int h); + void DrawMovingLineX(uint8 *p, int w, int h, int n); + void DrawMovingLineY(uint8 *p, int w, int h, int n); + void DrawBouncingCube(uint8 *p, int w, int h, int n); + + void DrawBarcode(uint32 value); + int DrawSideGuardBars(int x, int y, int height); + int DrawMiddleGuardBars(int x, int y, int height); + int DrawEanEncodedDigit(int digit, int x, int y, int height, bool r_code); + void DrawBlockRectangle(uint8* p, int x_start, int y_start, + int width, int height, int pitch, uint8 value); + + private: + int width_; + int height_; + int frame_index_; + int frame_data_size_; + uint8* y_data_; + uint8* u_data_; + uint8* v_data_; + + int barcode_start_x_; + int barcode_start_y_; + + DISALLOW_COPY_AND_ASSIGN(YuvFrameGenerator); +}; + +} // namespace cricket + +#endif // TALK_MEDIA_BASE_YUVFRAMEGENERATOR_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.cc b/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.cc index 150b55855d9..75b935ce592 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.cc @@ -37,8 +37,7 @@ #include "talk/media/base/mediacommon.h" #include "talk/media/devices/deviceinfo.h" #include "talk/media/devices/filevideocapturer.h" - -#if !defined(IOS) +#include "talk/media/devices/yuvframescapturer.h" #if defined(HAVE_WEBRTC_VIDEO) #include "talk/media/webrtc/webrtcvideocapturer.h" @@ -50,8 +49,6 @@ #endif -#endif - namespace { bool StringMatchWithWildcard( @@ -67,7 +64,6 @@ namespace cricket { // Initialize to empty string. const char DeviceManagerInterface::kDefaultDeviceName[] = ""; - class DefaultVideoCapturerFactory : public VideoCapturerFactory { public: DefaultVideoCapturerFactory() {} @@ -180,12 +176,27 @@ bool DeviceManager::GetVideoCaptureDevice(const std::string& name, } } - // If |name| is a valid name for a file, return a file video capturer device. + // If |name| is a valid name for a file or yuvframedevice, + // return a fake video capturer device. + if (GetFakeVideoCaptureDevice(name, out)) { + return true; + } + + return false; +} + +bool DeviceManager::GetFakeVideoCaptureDevice(const std::string& name, + Device* out) const { if (talk_base::Filesystem::IsFile(name)) { *out = FileVideoCapturer::CreateFileVideoCapturerDevice(name); return true; } + if (name == YuvFramesCapturer::kYuvFrameDeviceName) { + *out = YuvFramesCapturer::CreateYuvFramesCapturerDevice(); + return true; + } + return false; } @@ -201,23 +212,12 @@ void DeviceManager::ClearVideoCaptureDeviceMaxFormat( } VideoCapturer* DeviceManager::CreateVideoCapturer(const Device& device) const { -#if defined(IOS) - LOG_F(LS_ERROR) << " should never be called!"; - return NULL; -#else - // TODO(hellner): Throw out the creation of a file video capturer once the - // refactoring is completed. - if (FileVideoCapturer::IsFileVideoCapturerDevice(device)) { - FileVideoCapturer* capturer = new FileVideoCapturer; - if (!capturer->Init(device)) { - delete capturer; - return NULL; - } - LOG(LS_INFO) << "Created file video capturer " << device.name; - capturer->set_repeat(talk_base::kForever); + VideoCapturer* capturer = ConstructFakeVideoCapturer(device); + if (capturer) { return capturer; } - VideoCapturer* capturer = device_video_capturer_factory_->Create(device); + + capturer = device_video_capturer_factory_->Create(device); if (!capturer) { return NULL; } @@ -229,7 +229,29 @@ VideoCapturer* DeviceManager::CreateVideoCapturer(const Device& device) const { capturer->ConstrainSupportedFormats(video_format); } return capturer; -#endif +} + +VideoCapturer* DeviceManager::ConstructFakeVideoCapturer( + const Device& device) const { + // TODO(hellner): Throw out the creation of a file video capturer once the + // refactoring is completed. + if (FileVideoCapturer::IsFileVideoCapturerDevice(device)) { + FileVideoCapturer* capturer = new FileVideoCapturer; + if (!capturer->Init(device)) { + delete capturer; + return NULL; + } + LOG(LS_INFO) << "Created file video capturer " << device.name; + capturer->set_repeat(talk_base::kForever); + return capturer; + } + + if (YuvFramesCapturer::IsYuvFramesCapturerDevice(device)) { + YuvFramesCapturer* capturer = new YuvFramesCapturer(); + capturer->Init(); + return capturer; + } + return NULL; } bool DeviceManager::GetWindows( diff --git a/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.h b/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.h index 90da89157f8..f6099f36d23 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.h +++ b/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.h @@ -201,6 +201,8 @@ class DeviceManager : public DeviceManagerInterface { // The exclusion_list MUST be a NULL terminated list. static bool ShouldDeviceBeIgnored(const std::string& device_name, const char* const exclusion_list[]); + bool GetFakeVideoCaptureDevice(const std::string& name, Device* out) const; + VideoCapturer* ConstructFakeVideoCapturer(const Device& device) const; bool initialized_; talk_base::scoped_ptr<VideoCapturerFactory> device_video_capturer_factory_; diff --git a/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer.cc b/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer.cc index f5c078d3fa4..e79783faacd 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer.cc @@ -209,8 +209,14 @@ bool FileVideoCapturer::Init(const Device& device) { std::vector<VideoFormat> supported; supported.push_back(format); + // TODO(thorcarpenter): Report the actual file video format as the supported + // format. Do not use kMinimumInterval as it conflicts with video adaptation. SetId(device.id); SetSupportedFormats(supported); + + // TODO(wuwang): Design an E2E integration test for video adaptation, + // then remove the below call to disable the video adapter. + set_enable_video_adapter(false); return true; } diff --git a/chromium/third_party/libjingle/source/talk/media/devices/linuxdevicemanager.cc b/chromium/third_party/libjingle/source/talk/media/devices/linuxdevicemanager.cc index e3e55ff7959..8e58d99da19 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/linuxdevicemanager.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/linuxdevicemanager.cc @@ -302,6 +302,12 @@ LinuxDeviceWatcher::LinuxDeviceWatcher(DeviceManagerInterface* dm) LinuxDeviceWatcher::~LinuxDeviceWatcher() { } +static talk_base::PhysicalSocketServer* CurrentSocketServer() { + talk_base::SocketServer* ss = + talk_base::ThreadManager::Instance()->WrapCurrentThread()->socketserver(); + return reinterpret_cast<talk_base::PhysicalSocketServer*>(ss); +} + bool LinuxDeviceWatcher::Start() { // We deliberately return true in the failure paths here because libudev is // not a critical component of a Linux system so it may not be present/usable, @@ -341,16 +347,14 @@ bool LinuxDeviceWatcher::Start() { LOG_ERR(LS_ERROR) << "udev_monitor_enable_receiving()"; return true; } - static_cast<talk_base::PhysicalSocketServer*>( - talk_base::Thread::Current()->socketserver())->Add(this); + CurrentSocketServer()->Add(this); registered_ = true; return true; } void LinuxDeviceWatcher::Stop() { if (registered_) { - static_cast<talk_base::PhysicalSocketServer*>( - talk_base::Thread::Current()->socketserver())->Remove(this); + CurrentSocketServer()->Remove(this); registered_ = false; } if (udev_monitor_) { @@ -380,8 +384,7 @@ void LinuxDeviceWatcher::OnEvent(uint32 ff, int err) { LOG_ERR(LS_WARNING) << "udev_monitor_receive_device()"; // Stop listening to avoid potential livelock (an fd with EOF in it is // always considered readable). - static_cast<talk_base::PhysicalSocketServer*>( - talk_base::Thread::Current()->socketserver())->Remove(this); + CurrentSocketServer()->Remove(this); registered_ = false; return; } diff --git a/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanager.cc b/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanager.cc index e92408e416c..805558836d6 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanager.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanager.cc @@ -65,7 +65,6 @@ static const char* const kFilteredVideoDevicesName[] = { "Sonix SN9C201p", // Crashes in OpenAComponent and CloseComponent NULL, }; -static const int kVideoDeviceOpenAttempts = 3; static const UInt32 kAudioDeviceNameLength = 64; // Obj-C functions defined in macdevicemanagermm.mm // TODO(ronghuawu): have a shared header for these function defines. diff --git a/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanagermm.mm b/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanagermm.mm index 8cc77518cf4..fdde91fa526 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanagermm.mm +++ b/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanagermm.mm @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2010, Google Inc. + * Copyright 2010, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -27,7 +27,7 @@ // support GCC compiler #ifndef __has_feature -# define __has_feature(x) 0 +#define __has_feature(x) 0 #endif #include "talk/media/devices/devicemanager.h" @@ -42,7 +42,7 @@ cricket::DeviceManagerInterface* manager_; } - (id)init:(cricket::DeviceManagerInterface*)manager; -- (void)onDevicesChanged:(NSNotification *)notification; +- (void)onDevicesChanged:(NSNotification*)notification; @end @implementation DeviceWatcherImpl @@ -50,14 +50,16 @@ if ((self = [super init])) { assert(manager != NULL); manager_ = manager; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(onDevicesChanged:) - name:QTCaptureDeviceWasConnectedNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(onDevicesChanged:) - name:QTCaptureDeviceWasDisconnectedNotification - object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(onDevicesChanged:) + name:QTCaptureDeviceWasConnectedNotification + object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(onDevicesChanged:) + name:QTCaptureDeviceWasDisconnectedNotification + object:nil]; } return self; } @@ -68,7 +70,7 @@ [super dealloc]; #endif } -- (void)onDevicesChanged:(NSNotification *)notification { +- (void)onDevicesChanged:(NSNotification*)notification { manager_->SignalDevicesChange(); } @end @@ -83,9 +85,7 @@ DeviceWatcherImpl* CreateDeviceWatcherCallback( #else @autoreleasepool #endif - { - impl = [[DeviceWatcherImpl alloc] init:manager]; - } + { impl = [[DeviceWatcherImpl alloc] init:manager]; } #if !__has_feature(objc_arc) [pool drain]; #endif @@ -115,20 +115,19 @@ bool GetQTKitVideoDevices(std::vector<Device>* devices) { static NSString* const kFormat = @"localizedDisplayName: \"%@\", " @"modelUniqueID: \"%@\", uniqueID \"%@\", isConnected: %d, " @"isOpen: %d, isInUseByAnotherApplication: %d"; - NSString* info = [NSString stringWithFormat:kFormat, - [qt_capture_device localizedDisplayName], - [qt_capture_device modelUniqueID], - [qt_capture_device uniqueID], - [qt_capture_device isConnected], - [qt_capture_device isOpen], - [qt_capture_device isInUseByAnotherApplication]]; + NSString* info = [NSString + stringWithFormat:kFormat, + [qt_capture_device localizedDisplayName], + [qt_capture_device modelUniqueID], + [qt_capture_device uniqueID], + [qt_capture_device isConnected], + [qt_capture_device isOpen], + [qt_capture_device isInUseByAnotherApplication]]; LOG(LS_INFO) << [info UTF8String]; - std::string name([[qt_capture_device localizedDisplayName] - UTF8String]); - devices->push_back(Device(name, - [[qt_capture_device uniqueID] - UTF8String])); + std::string name([[qt_capture_device localizedDisplayName] UTF8String]); + devices->push_back( + Device(name, [[qt_capture_device uniqueID] UTF8String])); } } #if !__has_feature(objc_arc) diff --git a/chromium/third_party/libjingle/source/talk/media/devices/v4llookup.cc b/chromium/third_party/libjingle/source/talk/media/devices/v4llookup.cc index ff128a4ae3b..76eafa71692 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/v4llookup.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/v4llookup.cc @@ -12,13 +12,12 @@ #include <fcntl.h> #include <linux/types.h> #include <linux/videodev2.h> +#include <string.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> -#include <cstring> - #include "talk/base/logging.h" namespace cricket { @@ -50,10 +49,10 @@ bool V4LLookup::CheckIsV4L2Device(const std::string& device_path) { is_v4l2 = true; } else { - LOG(LS_ERROR) << "VIDIOC_QUERYCAP failed for " << device_path; + LOG_ERRNO(LS_ERROR) << "VIDIOC_QUERYCAP failed for " << device_path; } } else { - LOG(LS_ERROR) << "Failed to open " << device_path; + LOG_ERRNO(LS_ERROR) << "Failed to open " << device_path; } } } diff --git a/chromium/third_party/libjingle/source/talk/media/devices/yuvframescapturer.cc b/chromium/third_party/libjingle/source/talk/media/devices/yuvframescapturer.cc new file mode 100644 index 00000000000..648094bf43a --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/devices/yuvframescapturer.cc @@ -0,0 +1,173 @@ +#include "talk/media/devices/yuvframescapturer.h" + +#include "talk/base/bytebuffer.h" +#include "talk/base/criticalsection.h" +#include "talk/base/logging.h" +#include "talk/base/thread.h" + +#include "webrtc/system_wrappers/interface/clock.h" + +namespace cricket { +/////////////////////////////////////////////////////////////////////// +// Definition of private class YuvFramesThread that periodically generates +// frames. +/////////////////////////////////////////////////////////////////////// +class YuvFramesCapturer::YuvFramesThread + : public talk_base::Thread, public talk_base::MessageHandler { + public: + explicit YuvFramesThread(YuvFramesCapturer* capturer) + : capturer_(capturer), + finished_(false) { + } + + virtual ~YuvFramesThread() { + Stop(); + } + + // Override virtual method of parent Thread. Context: Worker Thread. + virtual void Run() { + // Read the first frame and start the message pump. The pump runs until + // Stop() is called externally or Quit() is called by OnMessage(). + int waiting_time_ms = 0; + if (capturer_) { + capturer_->ReadFrame(true); + PostDelayed(waiting_time_ms, this); + Thread::Run(); + } + + talk_base::CritScope cs(&crit_); + finished_ = true; + } + + // Override virtual method of parent MessageHandler. Context: Worker Thread. + virtual void OnMessage(talk_base::Message* /*pmsg*/) { + int waiting_time_ms = 0; + if (capturer_) { + capturer_->ReadFrame(false); + PostDelayed(waiting_time_ms, this); + } else { + Quit(); + } + } + + // Check if Run() is finished. + bool Finished() const { + talk_base::CritScope cs(&crit_); + return finished_; + } + + private: + YuvFramesCapturer* capturer_; + mutable talk_base::CriticalSection crit_; + bool finished_; + + DISALLOW_COPY_AND_ASSIGN(YuvFramesThread); +}; + +///////////////////////////////////////////////////////////////////// +// Implementation of class YuvFramesCapturer. +///////////////////////////////////////////////////////////////////// + +const char* YuvFramesCapturer::kYuvFrameDeviceName = "YuvFramesGenerator"; + +// TODO(shaowei): allow width_ and height_ to be configurable. +YuvFramesCapturer::YuvFramesCapturer() + : frames_generator_thread(NULL), + width_(640), + height_(480), + frame_index_(0), + barcode_interval_(1) { +} + +YuvFramesCapturer::~YuvFramesCapturer() { + Stop(); + delete[] static_cast<char*>(captured_frame_.data); +} + +void YuvFramesCapturer::Init() { + int size = width_ * height_; + int qsize = size / 4; + frame_generator_ = new YuvFrameGenerator(width_, height_, true); + frame_data_size_ = size + 2 * qsize; + captured_frame_.data = new char[frame_data_size_]; + captured_frame_.fourcc = FOURCC_IYUV; + captured_frame_.pixel_height = 1; + captured_frame_.pixel_width = 1; + captured_frame_.width = width_; + captured_frame_.height = height_; + captured_frame_.data_size = frame_data_size_; + + // Enumerate the supported formats. We have only one supported format. + VideoFormat format(width_, height_, VideoFormat::kMinimumInterval, + FOURCC_IYUV); + std::vector<VideoFormat> supported; + supported.push_back(format); + SetSupportedFormats(supported); +} + +CaptureState YuvFramesCapturer::Start(const VideoFormat& capture_format) { + if (IsRunning()) { + LOG(LS_ERROR) << "Yuv Frame Generator is already running"; + return CS_FAILED; + } + SetCaptureFormat(&capture_format); + + barcode_reference_timestamp_millis_ = + static_cast<int64>(talk_base::Time()) * 1000; + // Create a thread to generate frames. + frames_generator_thread = new YuvFramesThread(this); + bool ret = frames_generator_thread->Start(); + if (ret) { + LOG(LS_INFO) << "Yuv Frame Generator started"; + return CS_RUNNING; + } else { + LOG(LS_ERROR) << "Yuv Frame Generator failed to start"; + return CS_FAILED; + } +} + +bool YuvFramesCapturer::IsRunning() { + return frames_generator_thread && !frames_generator_thread->Finished(); +} + +void YuvFramesCapturer::Stop() { + if (frames_generator_thread) { + frames_generator_thread->Stop(); + frames_generator_thread = NULL; + LOG(LS_INFO) << "Yuv Frame Generator stopped"; + } + SetCaptureFormat(NULL); +} + +bool YuvFramesCapturer::GetPreferredFourccs(std::vector<uint32>* fourccs) { + if (!fourccs) { + return false; + } + fourccs->push_back(GetSupportedFormats()->at(0).fourcc); + return true; +} + +// Executed in the context of YuvFramesThread. +void YuvFramesCapturer::ReadFrame(bool first_frame) { + // 1. Signal the previously read frame to downstream. + if (!first_frame) { + SignalFrameCaptured(this, &captured_frame_); + } + uint8* buffer = new uint8[frame_data_size_]; + frame_generator_->GenerateNextFrame(buffer, GetBarcodeValue()); + frame_index_++; + memmove(captured_frame_.data, buffer, frame_data_size_); + delete[] buffer; +} + + +int32 YuvFramesCapturer::GetBarcodeValue() { + if (barcode_reference_timestamp_millis_ == -1 || + frame_index_ % barcode_interval_ != 0) { + return -1; + } + int64 now_millis = static_cast<int64>(talk_base::Time()) * 1000; + return static_cast<int32>(now_millis - barcode_reference_timestamp_millis_); +} + +} // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/devices/yuvframescapturer.h b/chromium/third_party/libjingle/source/talk/media/devices/yuvframescapturer.h new file mode 100644 index 00000000000..78865258409 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/devices/yuvframescapturer.h @@ -0,0 +1,71 @@ +#ifndef TALK_MEDIA_DEVICES_YUVFRAMESCAPTURER_H_ +#define TALK_MEDIA_DEVICES_YUVFRAMESCAPTURER_H_ + +#include <string> +#include <vector> + +#include "talk/base/stream.h" +#include "talk/base/stringutils.h" +#include "talk/media/base/videocapturer.h" +#include "talk/media/base/yuvframegenerator.h" + + +namespace talk_base { +class FileStream; +} + +namespace cricket { + + +// Simulated video capturer that periodically reads frames from a file. +class YuvFramesCapturer : public VideoCapturer { + public: + YuvFramesCapturer(); + YuvFramesCapturer(int width, int height); + virtual ~YuvFramesCapturer(); + + static const char* kYuvFrameDeviceName; + static Device CreateYuvFramesCapturerDevice() { + std::stringstream id; + id << kYuvFrameDeviceName; + return Device(id.str(), id.str()); + } + static bool IsYuvFramesCapturerDevice(const Device& device) { + return talk_base::starts_with(device.id.c_str(), kYuvFrameDeviceName); + } + + void Init(); + // Override virtual methods of parent class VideoCapturer. + virtual CaptureState Start(const VideoFormat& capture_format); + virtual void Stop(); + virtual bool IsRunning(); + virtual bool IsScreencast() const { return false; } + + protected: + // Override virtual methods of parent class VideoCapturer. + virtual bool GetPreferredFourccs(std::vector<uint32>* fourccs); + + // Read a frame and determine how long to wait for the next frame. + void ReadFrame(bool first_frame); + + private: + class YuvFramesThread; // Forward declaration, defined in .cc. + + YuvFrameGenerator* frame_generator_; + CapturedFrame captured_frame_; + YuvFramesThread* frames_generator_thread; + int width_; + int height_; + uint32 frame_data_size_; + uint32 frame_index_; + + int64 barcode_reference_timestamp_millis_; + int32 barcode_interval_; + int32 GetBarcodeValue(); + + DISALLOW_COPY_AND_ASSIGN(YuvFramesCapturer); +}; + +} // namespace cricket + +#endif // TALK_MEDIA_DEVICES_YUVFRAMESCAPTURER_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/other/linphonemediaengine.h b/chromium/third_party/libjingle/source/talk/media/other/linphonemediaengine.h index 69b2d2f6a7a..db3e69f6bcc 100644 --- a/chromium/third_party/libjingle/source/talk/media/other/linphonemediaengine.h +++ b/chromium/third_party/libjingle/source/talk/media/other/linphonemediaengine.h @@ -145,7 +145,8 @@ class LinphoneVoiceChannel : public VoiceMediaChannel { virtual void SetSendSsrc(uint32 id) {} // TODO: change RTP packet? virtual bool SetRtcpCName(const std::string& cname) { return true; } virtual bool Mute(bool on) { return mute_; } - virtual bool SetSendBandwidth(bool autobw, int bps) { return true; } + virtual bool SetStartSendBandwidth(int bps) { return true; } + virtual bool SetMaxSendBandwidth(int bps) { return true; } virtual bool SetOptions(int options) { return true; } virtual bool SetRecvRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions) { return true; } diff --git a/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.cc b/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.cc index 653273bd2e3..3647d212923 100644 --- a/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.cc @@ -29,26 +29,82 @@ #include <stdarg.h> #include <stdio.h> +#include <sstream> #include <vector> -#include "talk/app/webrtc/datachannelinterface.h" #include "talk/base/buffer.h" #include "talk/base/helpers.h" #include "talk/base/logging.h" +#include "talk/base/safe_conversions.h" #include "talk/media/base/codec.h" #include "talk/media/base/constants.h" #include "talk/media/base/streamparams.h" -#include "talk/media/sctp/sctputils.h" #include "usrsctplib/usrsctp.h" +namespace { +typedef cricket::SctpDataMediaChannel::StreamSet StreamSet; +// Returns a comma-separated, human-readable list of the stream IDs in 's' +std::string ListStreams(const StreamSet& s) { + std::stringstream result; + bool first = true; + for (StreamSet::const_iterator it = s.begin(); it != s.end(); ++it) { + if (!first) { + result << ", " << *it; + } else { + result << *it; + first = false; + } + } + return result.str(); +} + +// Returns a pipe-separated, human-readable list of the SCTP_STREAM_RESET +// flags in 'flags' +std::string ListFlags(int flags) { + std::stringstream result; + bool first = true; + // Skip past the first 12 chars (strlen("SCTP_STREAM_")) +#define MAKEFLAG(X) { X, #X + 12} + struct flaginfo_t { + int value; + const char* name; + } flaginfo[] = { + MAKEFLAG(SCTP_STREAM_RESET_INCOMING_SSN), + MAKEFLAG(SCTP_STREAM_RESET_OUTGOING_SSN), + MAKEFLAG(SCTP_STREAM_RESET_DENIED), + MAKEFLAG(SCTP_STREAM_RESET_FAILED), + MAKEFLAG(SCTP_STREAM_CHANGE_DENIED) + }; +#undef MAKEFLAG + for (int i = 0; i < ARRAY_SIZE(flaginfo); ++i) { + if (flags & flaginfo[i].value) { + if (!first) result << " | "; + result << flaginfo[i].name; + first = false; + } + } + return result.str(); +} + +// Returns a comma-separated, human-readable list of the integers in 'array'. +// All 'num_elems' of them. +std::string ListArray(const uint16* array, int num_elems) { + std::stringstream result; + for (int i = 0; i < num_elems; ++i) { + if (i) { + result << ", " << array[i]; + } else { + result << array[i]; + } + } + return result.str(); +} +} // namespace + namespace cricket { +typedef talk_base::ScopedMessageData<SctpInboundPacket> InboundPacketMessage; +typedef talk_base::ScopedMessageData<talk_base::Buffer> OutboundPacketMessage; -// This is the SCTP port to use. It is passed along the wire and the listener -// and connector must be using the same port. It is not related to the ports at -// the IP level. (Corresponds to: sockaddr_conn.sconn_port in usrsctp.h) -// -// TODO(ldixon): Allow port to be set from higher level code. -static const int kSctpDefaultPort = 5001; // TODO(ldixon): Find where this is defined, and also check is Sctp really // respects this. static const size_t kSctpMtu = 1280; @@ -130,9 +186,9 @@ static int OnSctpOutboundPacket(void* addr, void* data, size_t length, << "; tos: " << std::hex << static_cast<int>(tos) << "; set_df: " << std::hex << static_cast<int>(set_df); // Note: We have to copy the data; the caller will delete it. - talk_base::Buffer* buffer = new talk_base::Buffer(data, length); - channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET, - talk_base::WrapMessageData(buffer)); + OutboundPacketMessage* msg = + new OutboundPacketMessage(new talk_base::Buffer(data, length)); + channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET, msg); return 0; } @@ -164,8 +220,9 @@ static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr, packet->params.timestamp = rcv.rcv_tsn; packet->params.type = type; packet->flags = flags; - channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET, - talk_base::WrapMessageData(packet)); + // The ownership of |packet| transfers to |msg|. + InboundPacketMessage* msg = new InboundPacketMessage(packet); + channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET, msg); } free(data); return 1; @@ -214,24 +271,26 @@ SctpDataEngine::SctpDataEngine() { } usrsctp_engines_count++; - // We don't put in a codec because we don't want one offered when we - // use the hybrid data engine. - // codecs_.push_back(cricket::DataCodec( kGoogleSctpDataCodecId, - // kGoogleSctpDataCodecName, 0)); + cricket::DataCodec codec(kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, 0); + codec.SetParam(kCodecParamPort, kSctpDefaultPort); + codecs_.push_back(codec); } SctpDataEngine::~SctpDataEngine() { - // TODO(ldixon): There is currently a bug in teardown of usrsctp that blocks - // indefintely if a finish call made too soon after close calls. So teardown - // has been skipped. Once the bug is fixed, retest and enable teardown. - // - // usrsctp_engines_count--; - // LOG(LS_VERBOSE) << "usrsctp_engines_count:" << usrsctp_engines_count; - // if (usrsctp_engines_count == 0) { - // if (usrsctp_finish() != 0) { - // LOG(LS_WARNING) << "usrsctp_finish."; - // } - // } + usrsctp_engines_count--; + LOG(LS_VERBOSE) << "usrsctp_engines_count:" << usrsctp_engines_count; + + if (usrsctp_engines_count == 0) { + // usrsctp_finish() may fail if it's called too soon after the channels are + // closed. Wait and try again until it succeeds for up to 3 seconds. + for (size_t i = 0; i < 300; ++i) { + if (usrsctp_finish() == 0) + return; + + talk_base::Thread::SleepMs(10); + } + LOG(LS_ERROR) << "Failed to shutdown usrsctp."; + } } DataMediaChannel* SctpDataEngine::CreateChannel( @@ -244,8 +303,8 @@ DataMediaChannel* SctpDataEngine::CreateChannel( SctpDataMediaChannel::SctpDataMediaChannel(talk_base::Thread* thread) : worker_thread_(thread), - local_port_(-1), - remote_port_(-1), + local_port_(kSctpDefaultPort), + remote_port_(kSctpDefaultPort), sock_(NULL), sending_(false), receiving_(false), @@ -300,6 +359,18 @@ bool SctpDataMediaChannel::OpenSctpSocket() { return false; } + // Enable stream ID resets. + struct sctp_assoc_value stream_rst; + stream_rst.assoc_id = SCTP_ALL_ASSOC; + stream_rst.assoc_value = 1; + if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, + &stream_rst, sizeof(stream_rst))) { + LOG_ERRNO(LS_ERROR) << debug_name_ + << "Failed to set SCTP_ENABLE_STREAM_RESET."; + return false; + } + + // Nagle. uint32_t nodelay = 1; if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay))) { @@ -311,7 +382,8 @@ bool SctpDataMediaChannel::OpenSctpSocket() { int event_types[] = {SCTP_ASSOC_CHANGE, SCTP_PEER_ADDR_CHANGE, SCTP_SEND_FAILED_EVENT, - SCTP_SENDER_DRY_EVENT}; + SCTP_SENDER_DRY_EVENT, + SCTP_STREAM_RESET_EVENT}; struct sctp_event event = {0}; event.se_assoc_id = SCTP_ALL_ASSOC; event.se_on = 1; @@ -346,12 +418,6 @@ void SctpDataMediaChannel::CloseSctpSocket() { bool SctpDataMediaChannel::Connect() { LOG(LS_VERBOSE) << debug_name_ << "->Connect()."; - if (remote_port_ < 0) { - remote_port_ = kSctpDefaultPort; - } - if (local_port_ < 0) { - local_port_ = kSctpDefaultPort; - } // If we already have a socket connection, just return. if (sock_) { @@ -412,59 +478,24 @@ bool SctpDataMediaChannel::SetReceive(bool receive) { } bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) { - if (!stream.has_ssrcs()) { - return false; - } - - StreamParams found_stream; - // TODO(lally): Consider keeping this sorted. - if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) { - LOG(LS_WARNING) << debug_name_ << "->AddSendStream(...): " - << "Not adding data send stream '" << stream.id - << "' with ssrc=" << stream.first_ssrc() - << " because stream already exists."; - return false; - } - - streams_.push_back(stream); - return true; + return AddStream(stream); } bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) { - StreamParams found_stream; - if (!GetStreamBySsrc(streams_, ssrc, &found_stream)) { - return false; - } - - RemoveStreamBySsrc(&streams_, ssrc); - return true; + return ResetStream(ssrc); } -// Note: expects exactly one ssrc. If none are given, it will fail. If more -// than one are given, it will use the first. bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) { - if (!stream.has_ssrcs()) { - return false; - } - - StreamParams found_stream; - if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) { - LOG(LS_WARNING) << debug_name_ << "->AddRecvStream(...): " - << "Not adding data recv stream '" << stream.id - << "' with ssrc=" << stream.first_ssrc() - << " because stream already exists."; - return false; - } - - streams_.push_back(stream); - LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): " - << "Added data recv stream '" << stream.id - << "' with ssrc=" << stream.first_ssrc(); + // SCTP DataChannels are always bi-directional and calling AddSendStream will + // enable both sending and receiving on the stream. So AddRecvStream is a + // no-op. return true; } bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) { - RemoveStreamBySsrc(&streams_, ssrc); + // SCTP DataChannels are always bi-directional and calling RemoveSendStream + // will disable both sending and receiving on the stream. So RemoveRecvStream + // is a no-op. return true; } @@ -485,9 +516,8 @@ bool SctpDataMediaChannel::SendData( return false; } - StreamParams found_stream; if (params.type != cricket::DMT_CONTROL && - !GetStreamBySsrc(streams_, params.ssrc, &found_stream)) { + open_streams_.find(params.ssrc) == open_streams_.end()) { LOG(LS_WARNING) << debug_name_ << "->SendData(...): " << "Not sending data because ssrc is unknown: " << params.ssrc; @@ -521,10 +551,10 @@ bool SctpDataMediaChannel::SendData( send_res = usrsctp_sendv(sock_, payload.data(), static_cast<size_t>(payload.length()), NULL, 0, &spa, - static_cast<socklen_t>(sizeof(spa)), + talk_base::checked_cast<socklen_t>(sizeof(spa)), SCTP_SENDV_SPA, 0); if (send_res < 0) { - if (errno == EWOULDBLOCK) { + if (errno == SCTP_EWOULDBLOCK) { *result = SDR_BLOCK; LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned"; } else { @@ -584,36 +614,12 @@ void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel( void SctpDataMediaChannel::OnDataFromSctpToChannel( const ReceiveDataParams& params, talk_base::Buffer* buffer) { - StreamParams found_stream; - if (!GetStreamBySsrc(streams_, params.ssrc, &found_stream)) { - if (params.type == DMT_CONTROL) { - std::string label; - webrtc::DataChannelInit config; - if (ParseDataChannelOpenMessage(*buffer, &label, &config)) { - config.id = params.ssrc; - // Do not send the OPEN message for this data channel. - config.negotiated = true; - SignalNewStreamReceived(label, config); - - // Add the stream immediately. - cricket::StreamParams sparams = - cricket::StreamParams::CreateLegacy(params.ssrc); - AddSendStream(sparams); - AddRecvStream(sparams); - } else { - LOG(LS_ERROR) << debug_name_ << "->OnDataFromSctpToChannel(...): " - << "Received malformed control message"; - } - } else { - LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): " - << "Received packet for unknown ssrc: " << params.ssrc; - } - return; - } - if (receiving_) { LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): " - << "Posting with length: " << buffer->length(); + << "Posting with length: " << buffer->length() + << " on stream " << params.ssrc; + // Reports all received messages to upper layers, no matter whether the sid + // is known. SignalDataReceived(params, buffer->data(), buffer->length()); } else { LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): " @@ -623,6 +629,59 @@ void SctpDataMediaChannel::OnDataFromSctpToChannel( } } +bool SctpDataMediaChannel::AddStream(const StreamParams& stream) { + if (!stream.has_ssrcs()) { + return false; + } + + const uint32 ssrc = stream.first_ssrc(); + if (open_streams_.find(ssrc) != open_streams_.end()) { + LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): " + << "Not adding data stream '" << stream.id + << "' with ssrc=" << ssrc + << " because stream is already open."; + return false; + } else if (queued_reset_streams_.find(ssrc) != queued_reset_streams_.end() + || sent_reset_streams_.find(ssrc) != sent_reset_streams_.end()) { + LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): " + << "Not adding data stream '" << stream.id + << "' with ssrc=" << ssrc + << " because stream is still closing."; + return false; + } + + open_streams_.insert(ssrc); + return true; +} + +bool SctpDataMediaChannel::ResetStream(uint32 ssrc) { + // We typically get this called twice for the same stream, once each for + // Send and Recv. + StreamSet::iterator found = open_streams_.find(ssrc); + + if (found == open_streams_.end()) { + LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): " + << "stream not found."; + return false; + } else { + LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): " + << "Removing and queuing RE-CONFIG chunk."; + open_streams_.erase(found); + } + + // SCTP won't let you have more than one stream reset pending at a time, but + // you can close multiple streams in a single reset. So, we keep an internal + // queue of streams-to-reset, and send them as one reset message in + // SendQueuedStreamResets(). + queued_reset_streams_.insert(ssrc); + + // Signal our stream-reset logic that it should try to send now, if it can. + SendQueuedStreamResets(); + + // The stream will actually get removed when we get the acknowledgment. + return true; +} + void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) { const sctp_notification& notification = reinterpret_cast<const sctp_notification&>(*buffer->data()); @@ -641,7 +700,7 @@ void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) { LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT"; break; case SCTP_ADAPTATION_INDICATION: - LOG(LS_INFO) << "SCTP_ADAPTATION_INIDICATION"; + LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION"; break; case SCTP_PARTIAL_DELIVERY_EVENT: LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT"; @@ -650,7 +709,7 @@ void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) { LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT"; break; case SCTP_SENDER_DRY_EVENT: - LOG(LS_INFO) << "SCTP_SENDER_DRY_EVENT"; + LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT"; SignalReadyToSend(true); break; // TODO(ldixon): Unblock after congestion. @@ -661,15 +720,18 @@ void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) { LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT"; break; case SCTP_STREAM_RESET_EVENT: - LOG(LS_INFO) << "SCTP_STREAM_RESET_EVENT"; - // TODO(ldixon): Notify up to channel that stream resent has happened, - // and write unit test for this case. + OnStreamResetEvent(¬ification.sn_strreset_event); break; case SCTP_ASSOC_RESET_EVENT: LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT"; break; case SCTP_STREAM_CHANGE_EVENT: LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT"; + // An acknowledgment we get after our stream resets have gone through, + // if they've failed. We log the message, but don't react -- we don't + // keep around the last-transmitted set of SSIDs we wanted to close for + // error recovery. It doesn't seem likely to occur, and if so, likely + // harmless within the lifetime of a single SCTP association. break; default: LOG(LS_WARNING) << "Unknown SCTP event: " @@ -702,6 +764,86 @@ void SctpDataMediaChannel::OnNotificationAssocChange( } } +void SctpDataMediaChannel::OnStreamResetEvent( + const struct sctp_stream_reset_event* evt) { + // A stream reset always involves two RE-CONFIG chunks for us -- we always + // simultaneously reset a sid's sequence number in both directions. The + // requesting side transmits a RE-CONFIG chunk and waits for the peer to send + // one back. Both sides get this SCTP_STREAM_RESET_EVENT when they receive + // RE-CONFIGs. + const int num_ssrcs = (evt->strreset_length - sizeof(*evt)) / + sizeof(evt->strreset_stream_list[0]); + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): Flags = 0x" + << std::hex << evt->strreset_flags << " (" + << ListFlags(evt->strreset_flags) << ")"; + LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = [" + << ListArray(evt->strreset_stream_list, num_ssrcs) + << "], Open: [" + << ListStreams(open_streams_) << "], Q'd: [" + << ListStreams(queued_reset_streams_) << "], Sent: [" + << ListStreams(sent_reset_streams_) << "]"; + + // If both sides try to reset some streams at the same time (even if they're + // disjoint sets), we can get reset failures. + if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) { + // OK, just try again. The stream IDs sent over when the RESET_FAILED flag + // is set seem to be garbage values. Ignore them. + queued_reset_streams_.insert( + sent_reset_streams_.begin(), + sent_reset_streams_.end()); + sent_reset_streams_.clear(); + + } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) { + // Each side gets an event for each direction of a stream. That is, + // closing sid k will make each side receive INCOMING and OUTGOING reset + // events for k. As per RFC6525, Section 5, paragraph 2, each side will + // get an INCOMING event first. + for (int i = 0; i < num_ssrcs; i++) { + const int stream_id = evt->strreset_stream_list[i]; + + // See if this stream ID was closed by our peer or ourselves. + StreamSet::iterator it = sent_reset_streams_.find(stream_id); + + // The reset was requested locally. + if (it != sent_reset_streams_.end()) { + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): local sid " << stream_id << " acknowledged."; + sent_reset_streams_.erase(it); + + } else if ((it = open_streams_.find(stream_id)) + != open_streams_.end()) { + // The peer requested the reset. + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): closing sid " << stream_id; + open_streams_.erase(it); + SignalStreamClosedRemotely(stream_id); + + } else if ((it = queued_reset_streams_.find(stream_id)) + != queued_reset_streams_.end()) { + // The peer requested the reset, but there was a local reset + // queued. + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): double-sided close for sid " << stream_id; + // Both sides want the stream closed, and the peer got to send the + // RE-CONFIG first. Treat it like the local Remove(Send|Recv)Stream + // finished quickly. + queued_reset_streams_.erase(it); + + } else { + // This stream is unknown. Sometimes this can be from an + // RESET_FAILED-related retransmit. + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): Unknown sid " << stream_id; + } + } + } + + // Always try to send the queued RESET because this call indicates that the + // last local RESET or remote RESET has made some progress. + SendQueuedStreamResets(); +} + // Puts the specified |param| from the codec identified by |id| into |dest| // and returns true. Or returns false if it wasn't there, leaving |dest| // untouched. @@ -739,31 +881,66 @@ void SctpDataMediaChannel::OnPacketFromSctpToNetwork( talk_base::Buffer* buffer) { if (buffer->length() > kSctpMtu) { LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): " - << "SCTP seems to have made a poacket that is bigger " + << "SCTP seems to have made a packet that is bigger " "than its official MTU."; } MediaChannel::SendPacket(buffer); } +bool SctpDataMediaChannel::SendQueuedStreamResets() { + if (!sent_reset_streams_.empty() || queued_reset_streams_.empty()) + return true; + + LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending [" + << ListStreams(queued_reset_streams_) << "], Open: [" + << ListStreams(open_streams_) << "], Sent: [" + << ListStreams(sent_reset_streams_) << "]"; + + const size_t num_streams = queued_reset_streams_.size(); + const size_t num_bytes = sizeof(struct sctp_reset_streams) + + (num_streams * sizeof(uint16)); + + std::vector<uint8> reset_stream_buf(num_bytes, 0); + struct sctp_reset_streams* resetp = reinterpret_cast<sctp_reset_streams*>( + &reset_stream_buf[0]); + resetp->srs_assoc_id = SCTP_ALL_ASSOC; + resetp->srs_flags = SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING; + resetp->srs_number_streams = talk_base::checked_cast<uint16_t>(num_streams); + int result_idx = 0; + for (StreamSet::iterator it = queued_reset_streams_.begin(); + it != queued_reset_streams_.end(); ++it) { + resetp->srs_stream_list[result_idx++] = *it; + } + + int ret = usrsctp_setsockopt( + sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp, + talk_base::checked_cast<socklen_t>(reset_stream_buf.size())); + if (ret < 0) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to send a stream reset for " + << num_streams << " streams"; + return false; + } + + // sent_reset_streams_ is empty, and all the queued_reset_streams_ go into + // it now. + queued_reset_streams_.swap(sent_reset_streams_); + return true; +} + void SctpDataMediaChannel::OnMessage(talk_base::Message* msg) { switch (msg->message_id) { case MSG_SCTPINBOUNDPACKET: { - SctpInboundPacket* packet = - static_cast<talk_base::TypedMessageData<SctpInboundPacket*>*>( - msg->pdata)->data(); - OnInboundPacketFromSctpToChannel(packet); - delete packet; + talk_base::scoped_ptr<InboundPacketMessage> pdata( + static_cast<InboundPacketMessage*>(msg->pdata)); + OnInboundPacketFromSctpToChannel(pdata->data().get()); break; } case MSG_SCTPOUTBOUNDPACKET: { - talk_base::Buffer* buffer = - static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>( - msg->pdata)->data(); - OnPacketFromSctpToNetwork(buffer); - delete buffer; + talk_base::scoped_ptr<OutboundPacketMessage> pdata( + static_cast<OutboundPacketMessage*>(msg->pdata)); + OnPacketFromSctpToNetwork(pdata->data().get()); break; } } } - } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.h b/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.h index 4d05cf36e02..2e8beecd137 100644 --- a/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.h +++ b/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.h @@ -50,14 +50,20 @@ enum PreservedErrno { // Defined by "usrsctplib/usrsctp.h" struct sockaddr_conn; struct sctp_assoc_change; +struct sctp_stream_reset_event; // Defined by <sys/socket.h> struct socket; - namespace cricket { // The highest stream ID (Sid) that SCTP allows, and the number of streams we // tell SCTP we're going to use. const uint32 kMaxSctpSid = 1023; +// This is the default SCTP port to use. It is passed along the wire and the +// connectee and connector must be using the same port. It is not related to the +// ports at the IP level. (Corresponds to: sockaddr_conn.sconn_port in +// usrsctp.h) +const int kSctpDefaultPort = 5000; + // A DataEngine that interacts with usrsctp. // // From channel calls, data flows like this: @@ -122,6 +128,8 @@ class SctpDataMediaChannel : public DataMediaChannel, PPID_TEXT_LAST = 51 }; + typedef std::set<uint32> StreamSet; + // Given a thread which will be used to post messages (received data) to this // SctpDataMediaChannel instance. explicit SctpDataMediaChannel(talk_base::Thread* thread); @@ -164,7 +172,8 @@ class SctpDataMediaChannel : public DataMediaChannel, // TODO(pthatcher): Cleanup MediaChannel interface, or at least // don't try calling these and return false. Right now, things // don't work if we return false. - virtual bool SetSendBandwidth(bool autobw, int bps) { return true; } + virtual bool SetStartSendBandwidth(int bps) { return true; } + virtual bool SetMaxSendBandwidth(int bps) { return true; } virtual bool SetRecvRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions) { return true; } virtual bool SetSendRtpHeaderExtensions( @@ -195,6 +204,14 @@ class SctpDataMediaChannel : public DataMediaChannel, // Sets sending_ to false and sock_ to NULL. void CloseSctpSocket(); + // Sends a SCTP_RESET_STREAM for all streams in closing_ssids_. + bool SendQueuedStreamResets(); + + // Adds a stream. + bool AddStream(const StreamParams &sp); + // Queues a stream for reset. + bool ResetStream(uint32 ssrc); + // Called by OnMessage to send packet on the network. void OnPacketFromSctpToNetwork(talk_base::Buffer* buffer); // Called by OnMessage to decide what to do with the packet. @@ -204,6 +221,8 @@ class SctpDataMediaChannel : public DataMediaChannel, void OnNotificationFromSctp(talk_base::Buffer* buffer); void OnNotificationAssocChange(const sctp_assoc_change& change); + void OnStreamResetEvent(const struct sctp_stream_reset_event* evt); + // Responsible for marshalling incoming data to the channels listeners, and // outgoing data to the network interface. talk_base::Thread* worker_thread_; @@ -219,8 +238,17 @@ class SctpDataMediaChannel : public DataMediaChannel, bool sending_; // receiving_ controls whether inbound packets are thrown away. bool receiving_; - // Unified send/receive streams, as each is bidirectional. - std::vector<StreamParams> streams_; + + // When a data channel opens a stream, it goes into open_streams_. When we + // want to close it, the stream's ID goes into queued_reset_streams_. When + // we actually transmit a RE-CONFIG chunk with that stream ID, the ID goes + // into sent_reset_streams_. When we get a response RE-CONFIG chunk back + // acknowledging the reset, we remove the stream ID from + // sent_reset_streams_. We use sent_reset_streams_ to differentiate + // between acknowledgment RE-CONFIG and peer-initiated RE-CONFIGs. + StreamSet open_streams_; + StreamSet queued_reset_streams_; + StreamSet sent_reset_streams_; // A human-readable name for debugging messages. std::string debug_name_; diff --git a/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine_unittest.cc b/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine_unittest.cc index b4ad6ce33e2..cf410e5acae 100644 --- a/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine_unittest.cc @@ -29,8 +29,9 @@ #include <stdarg.h> #include <stdio.h> #include <string> +#include <vector> -#include "talk/app/webrtc/datachannelinterface.h" +#include "talk/base/bind.h" #include "talk/base/buffer.h" #include "talk/base/criticalsection.h" #include "talk/base/gunit.h" @@ -38,11 +39,11 @@ #include "talk/base/messagehandler.h" #include "talk/base/messagequeue.h" #include "talk/base/scoped_ptr.h" +#include "talk/base/ssladapter.h" #include "talk/base/thread.h" #include "talk/media/base/constants.h" #include "talk/media/base/mediachannel.h" #include "talk/media/sctp/sctpdataengine.h" -#include "talk/media/sctp/sctputils.h" enum { MSG_PACKET = 1, @@ -80,13 +81,13 @@ class SctpFakeNetworkInterface : public cricket::MediaChannel::NetworkInterface, // an SCTP packet. virtual void OnMessage(talk_base::Message* msg) { LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::OnMessage"; - talk_base::Buffer* buffer = + talk_base::scoped_ptr<talk_base::Buffer> buffer( static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>( - msg->pdata)->data(); + msg->pdata)->data()); if (dest_) { - dest_->OnPacketReceived(buffer, talk_base::PacketTime()); + dest_->OnPacketReceived(buffer.get(), talk_base::PacketTime()); } - delete buffer; + delete msg->pdata; } // Unsupported functions required to exist by NetworkInterface. @@ -162,10 +163,69 @@ class SignalReadyToSendObserver : public sigslot::has_slots<> { bool writable_; }; +class SignalChannelClosedObserver : public sigslot::has_slots<> { + public: + SignalChannelClosedObserver() {} + void BindSelf(cricket::SctpDataMediaChannel* channel) { + channel->SignalStreamClosedRemotely.connect( + this, &SignalChannelClosedObserver::OnStreamClosed); + } + void OnStreamClosed(uint32 stream) { + streams_.push_back(stream); + } + + int StreamCloseCount(uint32 stream) { + return std::count(streams_.begin(), streams_.end(), stream); + } + + bool WasStreamClosed(uint32 stream) { + return std::find(streams_.begin(), streams_.end(), stream) + != streams_.end(); + } + + private: + std::vector<uint32> streams_; +}; + +class SignalChannelClosedReopener : public sigslot::has_slots<> { + public: + SignalChannelClosedReopener(cricket::SctpDataMediaChannel* channel, + cricket::SctpDataMediaChannel* peer) + : channel_(channel), peer_(peer) {} + + void OnStreamClosed(int stream) { + cricket::StreamParams p(cricket::StreamParams::CreateLegacy(stream)); + channel_->AddSendStream(p); + channel_->AddRecvStream(p); + peer_->AddSendStream(p); + peer_->AddRecvStream(p); + streams_.push_back(stream); + } + + int StreamCloseCount(int stream) { + return std::count(streams_.begin(), streams_.end(), stream); + } + + private: + cricket::SctpDataMediaChannel* channel_; + cricket::SctpDataMediaChannel* peer_; + std::vector<int> streams_; +}; + // SCTP Data Engine testing framework. class SctpDataMediaChannelTest : public testing::Test, public sigslot::has_slots<> { protected: + // usrsctp uses the NSS random number generator on non-Android platforms, + // so we need to initialize SSL. + static void SetUpTestCase() { + talk_base::InitializeSSL(); + } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } + virtual void SetUp() { engine_.reset(new cricket::SctpDataEngine()); } @@ -184,11 +244,8 @@ class SctpDataMediaChannelTest : public testing::Test, net2_->SetDestination(chan1_.get()); LOG(LS_VERBOSE) << "Channel setup ----------------------------- "; - chan1_->AddSendStream(cricket::StreamParams::CreateLegacy(1)); - chan2_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)); - - chan2_->AddSendStream(cricket::StreamParams::CreateLegacy(2)); - chan1_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)); + AddStream(1); + AddStream(2); LOG(LS_VERBOSE) << "Connect the channels -----------------------------"; // chan1 wants to setup a data connection. @@ -206,8 +263,21 @@ class SctpDataMediaChannelTest : public testing::Test, chan1_->SetSend(true); } + virtual void TearDown() { + channel1()->SetSend(false); + channel2()->SetSend(false); + } + + void AddStream(int ssrc) { + cricket::StreamParams p(cricket::StreamParams::CreateLegacy(ssrc)); + chan1_->AddSendStream(p); + chan1_->AddRecvStream(p); + chan2_->AddSendStream(p); + chan2_->AddRecvStream(p); + } + cricket::SctpDataMediaChannel* CreateChannel( - SctpFakeNetworkInterface* net, SctpFakeDataReceiver* recv) { + SctpFakeNetworkInterface* net, SctpFakeDataReceiver* recv) { cricket::SctpDataMediaChannel* channel = static_cast<cricket::SctpDataMediaChannel*>(engine_->CreateChannel( cricket::DCT_SCTP)); @@ -215,8 +285,6 @@ class SctpDataMediaChannelTest : public testing::Test, // When data is received, pass it to the SctpFakeDataReceiver. channel->SignalDataReceived.connect( recv, &SctpFakeDataReceiver::OnDataReceived); - channel->SignalNewStreamReceived.connect( - this, &SctpDataMediaChannelTest::OnNewStreamReceived); return channel; } @@ -225,7 +293,9 @@ class SctpDataMediaChannelTest : public testing::Test, cricket::SendDataResult* result) { cricket::SendDataParams params; params.ssrc = ssrc; - return chan->SendData(params, talk_base::Buffer(msg.data(), msg.length()), result); + + return chan->SendData(params, talk_base::Buffer( + &msg[0], msg.length()), result); } bool ReceivedData(const SctpFakeDataReceiver* recv, uint32 ssrc, @@ -251,14 +321,6 @@ class SctpDataMediaChannelTest : public testing::Test, SctpFakeDataReceiver* receiver1() { return recv1_.get(); } SctpFakeDataReceiver* receiver2() { return recv2_.get(); } - void OnNewStreamReceived(const std::string& label, - const webrtc::DataChannelInit& init) { - last_label_ = label; - last_dc_init_ = init; - } - std::string last_label() { return last_label_; } - webrtc::DataChannelInit last_dc_init() { return last_dc_init_; } - private: talk_base::scoped_ptr<cricket::SctpDataEngine> engine_; talk_base::scoped_ptr<SctpFakeNetworkInterface> net1_; @@ -267,8 +329,6 @@ class SctpDataMediaChannelTest : public testing::Test, talk_base::scoped_ptr<SctpFakeDataReceiver> recv2_; talk_base::scoped_ptr<cricket::SctpDataMediaChannel> chan1_; talk_base::scoped_ptr<cricket::SctpDataMediaChannel> chan2_; - std::string last_label_; - webrtc::DataChannelInit last_dc_init_; }; // Verifies that SignalReadyToSend is fired. @@ -304,57 +364,139 @@ TEST_F(SctpDataMediaChannelTest, SendData) { EXPECT_EQ(cricket::SDR_SUCCESS, result); EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received() - << "recv2.last_params.ssrc=" + << ", recv2.last_params.ssrc=" << receiver2()->last_params().ssrc - << "recv2.last_params.timestamp=" + << ", recv2.last_params.timestamp=" << receiver2()->last_params().ssrc - << "recv2.last_params.seq_num=" + << ", recv2.last_params.seq_num=" << receiver2()->last_params().seq_num - << "recv2.last_data=" << receiver2()->last_data(); + << ", recv2.last_data=" << receiver2()->last_data(); LOG(LS_VERBOSE) << "chan2 sending: 'hi chan1' -----------------------------"; ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); EXPECT_EQ(cricket::SDR_SUCCESS, result); EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received() - << "recv1.last_params.ssrc=" + << ", recv1.last_params.ssrc=" << receiver1()->last_params().ssrc - << "recv1.last_params.timestamp=" + << ", recv1.last_params.timestamp=" << receiver1()->last_params().ssrc - << "recv1.last_params.seq_num=" + << ", recv1.last_params.seq_num=" << receiver1()->last_params().seq_num - << "recv1.last_data=" << receiver1()->last_data(); + << ", recv1.last_data=" << receiver1()->last_data(); +} + +// Sends a lot of large messages at once and verifies SDR_BLOCK is returned. +TEST_F(SctpDataMediaChannelTest, SendDataBlocked) { + SetupConnectedChannels(); + + cricket::SendDataResult result; + cricket::SendDataParams params; + params.ssrc = 1; + + std::vector<char> buffer(1024 * 64, 0); + + for (size_t i = 0; i < 100; ++i) { + channel1()->SendData( + params, talk_base::Buffer(&buffer[0], buffer.size()), &result); + if (result == cricket::SDR_BLOCK) + break; + } + + EXPECT_EQ(cricket::SDR_BLOCK, result); +} + +TEST_F(SctpDataMediaChannelTest, ClosesRemoteStream) { + SetupConnectedChannels(); + SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver; + chan_1_sig_receiver.BindSelf(channel1()); + chan_2_sig_receiver.BindSelf(channel2()); + + cricket::SendDataResult result; + ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); + EXPECT_EQ(cricket::SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); + ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); + EXPECT_EQ(cricket::SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); - LOG(LS_VERBOSE) << "Closing down. -----------------------------"; - // Disconnects and closes socket, including setting receiving to false. - channel1()->SetSend(false); - channel2()->SetSend(false); - LOG(LS_VERBOSE) << "Cleaning up. -----------------------------"; + // Close channel 1. Channel 2 should notify us. + channel1()->RemoveSendStream(1); + EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000); } -TEST_F(SctpDataMediaChannelTest, SendReceiveOpenMessage) { +TEST_F(SctpDataMediaChannelTest, ClosesTwoRemoteStreams) { SetupConnectedChannels(); + AddStream(3); + SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver; + chan_1_sig_receiver.BindSelf(channel1()); + chan_2_sig_receiver.BindSelf(channel2()); - std::string label("x"); - webrtc::DataChannelInit config; - config.id = 10; + cricket::SendDataResult result; + ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); + EXPECT_EQ(cricket::SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); + ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); + EXPECT_EQ(cricket::SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); - // Send the OPEN message on a unknown ssrc. - channel1()->AddSendStream(cricket::StreamParams::CreateLegacy(config.id)); - cricket::SendDataParams params; - params.ssrc = config.id; - params.type = cricket::DMT_CONTROL; + // Close two streams on one side. + channel2()->RemoveSendStream(2); + channel2()->RemoveSendStream(3); + EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000); + EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000); +} + +TEST_F(SctpDataMediaChannelTest, ClosesStreamsOnBothSides) { + SetupConnectedChannels(); + AddStream(3); + AddStream(4); + SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver; + chan_1_sig_receiver.BindSelf(channel1()); + chan_2_sig_receiver.BindSelf(channel2()); + + cricket::SendDataResult result; + ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); + EXPECT_EQ(cricket::SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); + ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); + EXPECT_EQ(cricket::SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); + + // Close one stream on channel1(), while closing three streams on + // channel2(). They will conflict (only one side can close anything at a + // time, apparently). Test the resolution of the conflict. + channel1()->RemoveSendStream(1); + + channel2()->RemoveSendStream(2); + channel2()->RemoveSendStream(3); + channel2()->RemoveSendStream(4); + EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000); + EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000); + EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000); + EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(4), 1000); +} + +TEST_F(SctpDataMediaChannelTest, ReusesAStream) { + // Shut down channel 1, then open it up again for reuse. + SetupConnectedChannels(); cricket::SendDataResult result; - talk_base::Buffer buffer; - ASSERT_TRUE(cricket::WriteDataChannelOpenMessage(label, config, &buffer)); - ASSERT_TRUE(channel1()->SendData(params, buffer, &result)); - // Send data on the new ssrc immediately after sending the OPEN message. - ASSERT_TRUE(SendData(channel1(), config.id, "hi chan2", &result)); - - // Verifies the received OPEN message. - EXPECT_TRUE_WAIT(last_label() == label, 1000); - EXPECT_EQ(config.id, last_dc_init().id); - EXPECT_EQ(true, last_dc_init().negotiated); - // Verifies the received data. - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), config.id, "hi chan2"), 1000); + SignalChannelClosedObserver chan_2_sig_receiver; + chan_2_sig_receiver.BindSelf(channel2()); + + ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); + EXPECT_EQ(cricket::SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); + + channel1()->RemoveSendStream(1); + EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000); + // Channel 1 is gone now. + + // Create a new channel 1. + AddStream(1); + ASSERT_TRUE(SendData(channel1(), 1, "hi?", &result)); + EXPECT_EQ(cricket::SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), 1000); + channel1()->RemoveSendStream(1); + EXPECT_TRUE_WAIT(chan_2_sig_receiver.StreamCloseCount(1) == 2, 1000); } diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/OWNERS b/chromium/third_party/libjingle/source/talk/media/webrtc/OWNERS new file mode 100644 index 00000000000..9a3546ef8b8 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/OWNERS @@ -0,0 +1,3 @@ +mflodman@webrtc.org +pthatcher@webrtc.org +wu@webrtc.org diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideocapturemodule.h b/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideocapturemodule.h index b823bc18fe6..347e4b713ed 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideocapturemodule.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideocapturemodule.h @@ -59,21 +59,24 @@ class FakeWebRtcVideoCaptureModule : public webrtc::VideoCaptureModule { id_ = id; return 0; } - virtual int32_t RegisterCaptureDataCallback( + virtual void RegisterCaptureDataCallback( webrtc::VideoCaptureDataCallback& callback) { callback_ = &callback; - return 0; } - virtual int32_t DeRegisterCaptureDataCallback() { - callback_ = NULL; - return 0; + virtual void DeRegisterCaptureDataCallback() { callback_ = NULL; } + virtual void RegisterCaptureCallback(webrtc::VideoCaptureFeedBack& callback) { + // Not implemented. } - virtual int32_t RegisterCaptureCallback( - webrtc::VideoCaptureFeedBack& callback) { - return -1; // not implemented + virtual void DeRegisterCaptureCallback() { + // Not implemented. } - virtual int32_t DeRegisterCaptureCallback() { - return 0; + virtual void SetCaptureDelay(int32_t delay) { delay_ = delay; } + virtual int32_t CaptureDelay() { return delay_; } + virtual void EnableFrameRateCallback(const bool enable) { + // not implemented + } + virtual void EnableNoPictureAlarm(const bool enable) { + // not implemented } virtual int32_t StartCapture( const webrtc::VideoCaptureCapability& cap) { @@ -98,13 +101,7 @@ class FakeWebRtcVideoCaptureModule : public webrtc::VideoCaptureModule { settings = cap_; return 0; } - virtual int32_t SetCaptureDelay(int32_t delay) { - delay_ = delay; - return 0; - } - virtual int32_t CaptureDelay() { - return delay_; - } + virtual int32_t SetCaptureRotation( webrtc::VideoCaptureRotation rotation) { return -1; // not implemented @@ -113,12 +110,6 @@ class FakeWebRtcVideoCaptureModule : public webrtc::VideoCaptureModule { const webrtc::VideoCodec& codec) { return NULL; // not implemented } - virtual int32_t EnableFrameRateCallback(const bool enable) { - return -1; // not implemented - } - virtual int32_t EnableNoPictureAlarm(const bool enable) { - return -1; // not implemented - } virtual int32_t AddRef() { return 0; } diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideoengine.h b/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideoengine.h index bb75c2a7feb..85c59d83860 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideoengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideoengine.h @@ -41,14 +41,6 @@ #include "talk/media/webrtc/webrtcvideoencoderfactory.h" #include "talk/media/webrtc/webrtcvie.h" -namespace webrtc { - -bool operator==(const webrtc::VideoCodec& c1, const webrtc::VideoCodec& c2) { - return memcmp(&c1, &c2, sizeof(c1)) == 0; -} - -} - namespace cricket { #define WEBRTC_CHECK_CAPTURER(capturer) \ @@ -279,15 +271,16 @@ class FakeWebRtcVideoEngine can_transmit_(true), remote_rtx_ssrc_(-1), rtx_send_payload_type(-1), + rtx_recv_payload_type(-1), rtcp_status_(webrtc::kRtcpNone), key_frame_request_method_(webrtc::kViEKeyFrameRequestNone), tmmbr_(false), remb_contribute_(false), remb_bw_partition_(false), - rtp_offset_send_id_(0), - rtp_offset_receive_id_(0), - rtp_absolute_send_time_send_id_(0), - rtp_absolute_send_time_receive_id_(0), + rtp_offset_send_id_(-1), + rtp_offset_receive_id_(-1), + rtp_absolute_send_time_send_id_(-1), + rtp_absolute_send_time_receive_id_(-1), sender_target_delay_(0), receiver_target_delay_(0), transmission_smoothing_(false), @@ -297,9 +290,14 @@ class FakeWebRtcVideoEngine send_fec_bitrate_(0), send_nack_bitrate_(0), send_bandwidth_(0), - receive_bandwidth_(0) { + receive_bandwidth_(0), + reserved_transmit_bitrate_bps_(0), + suspend_below_min_bitrate_(false), + overuse_observer_(NULL), + last_recvd_payload_type_(-1) { ssrcs_[0] = 0; // default ssrc. memset(&send_codec, 0, sizeof(send_codec)); + memset(&overuse_options_, 0, sizeof(overuse_options_)); } int capture_id_; int original_channel_id_; @@ -312,6 +310,7 @@ class FakeWebRtcVideoEngine std::map<int, int> rtx_ssrcs_; int remote_rtx_ssrc_; int rtx_send_payload_type; + int rtx_recv_payload_type; std::string cname_; webrtc::ViERTCPMode rtcp_status_; webrtc::ViEKeyFrameRequestMethod key_frame_request_method_; @@ -336,6 +335,11 @@ class FakeWebRtcVideoEngine unsigned int send_nack_bitrate_; unsigned int send_bandwidth_; unsigned int receive_bandwidth_; + unsigned int reserved_transmit_bitrate_bps_; + bool suspend_below_min_bitrate_; + webrtc::CpuOveruseObserver* overuse_observer_; + webrtc::CpuOveruseOptions overuse_options_; + int last_recvd_payload_type_; }; class Capturer : public webrtc::ViEExternalCapture { public: @@ -425,7 +429,7 @@ class FakeWebRtcVideoEngine void set_fail_alloc_capturer(bool fail_alloc_capturer) { fail_alloc_capturer_ = fail_alloc_capturer; } - int num_set_send_codecs() const { return num_set_send_codecs_; } + int GetNumSetSendCodecs() const { return num_set_send_codecs_; } int GetCaptureId(int channel) const { WEBRTC_ASSERT_CHANNEL(channel); @@ -447,6 +451,10 @@ class FakeWebRtcVideoEngine WEBRTC_ASSERT_CHANNEL(channel); return channels_.find(channel)->second->send; } + bool GetReceive(int channel) const { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_.find(channel)->second->receive_; + } int GetCaptureChannelId(int capture_id) const { WEBRTC_ASSERT_CAPTURER(capture_id); return capturers_.find(capture_id)->second->channel_id(); @@ -479,21 +487,24 @@ class FakeWebRtcVideoEngine WEBRTC_ASSERT_CHANNEL(channel); return channels_.find(channel)->second->remb_contribute_; } - int GetSendRtpTimestampOffsetExtensionId(int channel) { - WEBRTC_ASSERT_CHANNEL(channel); - return channels_.find(channel)->second->rtp_offset_send_id_; - } - int GetReceiveRtpTimestampOffsetExtensionId(int channel) { + int GetSendRtpExtensionId(int channel, const std::string& extension) { WEBRTC_ASSERT_CHANNEL(channel); - return channels_.find(channel)->second->rtp_offset_receive_id_; - } - int GetSendAbsoluteSendTimeExtensionId(int channel) { - WEBRTC_ASSERT_CHANNEL(channel); - return channels_.find(channel)->second->rtp_absolute_send_time_send_id_; + if (extension == kRtpTimestampOffsetHeaderExtension) { + return channels_.find(channel)->second->rtp_offset_send_id_; + } else if (extension == kRtpAbsoluteSenderTimeHeaderExtension) { + return channels_.find(channel)->second->rtp_absolute_send_time_send_id_; + } + return -1; } - int GetReceiveAbsoluteSendTimeExtensionId(int channel) { + int GetReceiveRtpExtensionId(int channel, const std::string& extension) { WEBRTC_ASSERT_CHANNEL(channel); - return channels_.find(channel)->second->rtp_absolute_send_time_receive_id_; + if (extension == kRtpTimestampOffsetHeaderExtension) { + return channels_.find(channel)->second->rtp_offset_receive_id_; + } else if (extension == kRtpAbsoluteSenderTimeHeaderExtension) { + return + channels_.find(channel)->second->rtp_absolute_send_time_receive_id_; + } + return -1; } bool GetTransmissionSmoothingStatus(int channel) { WEBRTC_ASSERT_CHANNEL(channel); @@ -529,6 +540,14 @@ class FakeWebRtcVideoEngine WEBRTC_ASSERT_CHANNEL(channel); return channels_.find(channel)->second->can_transmit_; } + webrtc::CpuOveruseObserver* GetCpuOveruseObserver(int channel) const { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_.find(channel)->second->overuse_observer_; + } + webrtc::CpuOveruseOptions GetCpuOveruseOptions(int channel) const { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_.find(channel)->second->overuse_options_; + } int GetRtxSsrc(int channel, int simulcast_idx) const { WEBRTC_ASSERT_CHANNEL(channel); if (channels_.find(channel)->second->rtx_ssrcs_.find(simulcast_idx) == @@ -583,21 +602,38 @@ class FakeWebRtcVideoEngine } void SetSendBandwidthEstimate(int channel, unsigned int send_bandwidth) { WEBRTC_ASSERT_CHANNEL(channel); - channels_[channel]->send_bandwidth_ = send_bandwidth; + channels_[GetOriginalChannelId(channel)]->send_bandwidth_ = send_bandwidth; } void SetReceiveBandwidthEstimate(int channel, unsigned int receive_bandwidth) { WEBRTC_ASSERT_CHANNEL(channel); - channels_[channel]->receive_bandwidth_ = receive_bandwidth; + channels_[GetOriginalChannelId(channel)]->receive_bandwidth_ = + receive_bandwidth; }; int GetRtxSendPayloadType(int channel) { WEBRTC_CHECK_CHANNEL(channel); return channels_[channel]->rtx_send_payload_type; } + int GetRtxRecvPayloadType(int channel) { + WEBRTC_CHECK_CHANNEL(channel); + return channels_[channel]->rtx_recv_payload_type; + } int GetRemoteRtxSsrc(int channel) { WEBRTC_CHECK_CHANNEL(channel); return channels_.find(channel)->second->remote_rtx_ssrc_; } + bool GetSuspendBelowMinBitrateStatus(int channel) { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_.find(channel)->second->suspend_below_min_bitrate_; + } + int GetLastRecvdPayloadType(int channel) const { + WEBRTC_CHECK_CHANNEL(channel); + return channels_.find(channel)->second->last_recvd_payload_type_; + } + unsigned int GetReservedTransmitBitrate(int channel) { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_.find(channel)->second->reserved_transmit_bitrate_bps_; + } WEBRTC_STUB(Release, ()); @@ -615,7 +651,11 @@ class FakeWebRtcVideoEngine return -1; } Channel* ch = new Channel(); - channels_[++last_channel_] = ch; + ++last_channel_; + // The original channel of the first channel in a group refers to itself + // for code simplicity. + ch->original_channel_id_ = last_channel_; + channels_[last_channel_] = ch; channel = last_channel_; return 0; }; @@ -638,11 +678,19 @@ class FakeWebRtcVideoEngine channels_.erase(channel); return 0; } - WEBRTC_STUB(RegisterCpuOveruseObserver, - (int channel, webrtc::CpuOveruseObserver* observer)); -#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(RegisterCpuOveruseObserver, + (int channel, webrtc::CpuOveruseObserver* observer)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->overuse_observer_ = observer; + return 0; + } WEBRTC_STUB(CpuOveruseMeasures, (int, int*, int*, int*, int*)); -#endif + WEBRTC_FUNC(SetCpuOveruseOptions, + (int channel, const webrtc::CpuOveruseOptions& options)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->overuse_options_ = options; + return 0; + } WEBRTC_STUB(ConnectAudioChannel, (const int, const int)); WEBRTC_STUB(DisconnectAudioChannel, (const int)); WEBRTC_FUNC(StartSend, (const int channel)) { @@ -760,7 +808,10 @@ class FakeWebRtcVideoEngine WEBRTC_STUB(WaitForFirstKeyFrame, (const int, const bool)); WEBRTC_STUB(StartDebugRecording, (int, const char*)); WEBRTC_STUB(StopDebugRecording, (int)); - WEBRTC_VOID_STUB(SuspendBelowMinBitrate, (int)); + WEBRTC_VOID_FUNC(SuspendBelowMinBitrate, (int channel)) { + WEBRTC_ASSERT_CHANNEL(channel); + channels_[channel]->suspend_below_min_bitrate_ = true; + } // webrtc::ViECapture WEBRTC_STUB(NumberOfCaptureDevices, ()); @@ -827,16 +878,24 @@ class FakeWebRtcVideoEngine } WEBRTC_STUB(RegisterSendTransport, (const int, webrtc::Transport&)); WEBRTC_STUB(DeregisterSendTransport, (const int)); -#ifdef USE_WEBRTC_DEV_BRANCH - WEBRTC_STUB(ReceivedRTPPacket, (const int, const void*, const int, - const webrtc::PacketTime&)); -#else - WEBRTC_STUB(ReceivedRTPPacket, (const int, const void*, const int)); -#endif + + WEBRTC_FUNC(ReceivedRTPPacket, (const int channel, + const void* packet, + const int length, + const webrtc::PacketTime& packet_time)) { + WEBRTC_ASSERT_CHANNEL(channel); + ASSERT(length > 1); + uint8_t payload_type = static_cast<const uint8_t*>(packet)[1] & 0x7F; + channels_[channel]->last_recvd_payload_type_ = payload_type; + return 0; + } + WEBRTC_STUB(ReceivedRTCPPacket, (const int, const void*, const int)); // Not using WEBRTC_STUB due to bool return value virtual bool IsIPv6Enabled(int channel) { return true; } WEBRTC_STUB(SetMTU, (int, unsigned int)); + WEBRTC_STUB(ReceivedBWEPacket, (const int, int64_t, int, + const webrtc::RTPHeader&)); // webrtc::ViERender WEBRTC_STUB(RegisterVideoRenderModule, (webrtc::VideoRender&)); @@ -940,7 +999,17 @@ class FakeWebRtcVideoEngine channels_[channel]->rtx_send_payload_type = payload_type; return 0; } - WEBRTC_STUB(SetRtxReceivePayloadType, (const int, const uint8)); + +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_STUB(SetPadWithRedundantPayloads, (int, bool)); +#endif + + WEBRTC_FUNC(SetRtxReceivePayloadType, (const int channel, + const uint8 payload_type)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->rtx_recv_payload_type = payload_type; + return 0; + } WEBRTC_STUB(SetStartSequenceNumber, (const int, unsigned short)); WEBRTC_FUNC(SetRTCPStatus, @@ -1019,35 +1088,42 @@ class FakeWebRtcVideoEngine WEBRTC_FUNC(SetSendTimestampOffsetStatus, (int channel, bool enable, int id)) { WEBRTC_CHECK_CHANNEL(channel); - channels_[channel]->rtp_offset_send_id_ = (enable) ? id : 0; + channels_[channel]->rtp_offset_send_id_ = (enable) ? id : -1; return 0; } WEBRTC_FUNC(SetReceiveTimestampOffsetStatus, (int channel, bool enable, int id)) { WEBRTC_CHECK_CHANNEL(channel); - channels_[channel]->rtp_offset_receive_id_ = (enable) ? id : 0; + channels_[channel]->rtp_offset_receive_id_ = (enable) ? id : -1; return 0; } WEBRTC_FUNC(SetSendAbsoluteSendTimeStatus, (int channel, bool enable, int id)) { WEBRTC_CHECK_CHANNEL(channel); - channels_[channel]->rtp_absolute_send_time_send_id_ = (enable) ? id : 0; + channels_[channel]->rtp_absolute_send_time_send_id_ = (enable) ? id : -1; return 0; } WEBRTC_FUNC(SetReceiveAbsoluteSendTimeStatus, (int channel, bool enable, int id)) { WEBRTC_CHECK_CHANNEL(channel); - channels_[channel]->rtp_absolute_send_time_receive_id_ = (enable) ? id : 0; + channels_[channel]->rtp_absolute_send_time_receive_id_ = (enable) ? id : -1; return 0; } -#ifdef USE_WEBRTC_DEV_BRANCH WEBRTC_STUB(SetRtcpXrRrtrStatus, (int, bool)); -#endif WEBRTC_FUNC(SetTransmissionSmoothingStatus, (int channel, bool enable)) { WEBRTC_CHECK_CHANNEL(channel); channels_[channel]->transmission_smoothing_ = enable; return 0; } + WEBRTC_FUNC(SetReservedTransmitBitrate, (int channel, + unsigned int reserved_transmit_bitrate_bps)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->reserved_transmit_bitrate_bps_ = + reserved_transmit_bitrate_bps; + return 0; + } + WEBRTC_STUB_CONST(GetRtcpPacketTypeCounters, (int, + webrtc::RtcpPacketTypeCounter*, webrtc::RtcpPacketTypeCounter*)); WEBRTC_STUB_CONST(GetReceivedRTCPStatistics, (const int, unsigned short&, unsigned int&, unsigned int&, unsigned int&, int&)); WEBRTC_STUB_CONST(GetSentRTCPStatistics, (const int, unsigned short&, @@ -1084,6 +1160,7 @@ class FakeWebRtcVideoEngine std::map<int, Channel*>::const_iterator it = channels_.find(channel); // Assume the current video, fec and nack bitrate sums up to our estimate. if (it->second->send) { + it = channels_.find(GetOriginalChannelId(channel)); *send_bandwidth_estimate = it->second->send_bandwidth_; } else { *send_bandwidth_estimate = 0; @@ -1095,7 +1172,7 @@ class FakeWebRtcVideoEngine WEBRTC_CHECK_CHANNEL(channel); std::map<int, Channel*>::const_iterator it = channels_.find(channel); if (it->second->receive_) { - // For simplicity, assume all channels receive half of max send rate. + it = channels_.find(GetOriginalChannelId(channel)); *receive_bandwidth_estimate = it->second->receive_bandwidth_; } else { *receive_bandwidth_estimate = 0; diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvoiceengine.h b/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvoiceengine.h index a68d65ef750..25c952d33ec 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvoiceengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvoiceengine.h @@ -32,16 +32,18 @@ #include <map> #include <vector> - #include "talk/base/basictypes.h" #include "talk/base/gunit.h" #include "talk/base/stringutils.h" #include "talk/media/base/codec.h" +#include "talk/media/base/rtputils.h" #include "talk/media/base/voiceprocessor.h" #include "talk/media/webrtc/fakewebrtccommon.h" #include "talk/media/webrtc/webrtcvoe.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" -#include "webrtc/common.h" + +namespace webrtc { +class ViENetwork; +} namespace cricket { @@ -59,6 +61,14 @@ static const int kFakeDeviceId = 0; static const int kFakeDeviceId = 1; #endif +// Verify the header extension ID, if enabled, is within the bounds specified in +// [RFC5285]: 1-14 inclusive. +#define WEBRTC_CHECK_HEADER_EXTENSION_ID(enable, id) \ + do { \ + if (enable && (id < 1 || id > 14)) { \ + return -1; \ + } \ + } while (0); class FakeWebRtcVoiceEngine : public webrtc::VoEAudioProcessing, @@ -78,7 +88,7 @@ class FakeWebRtcVoiceEngine int dtmf_length_ms; }; struct Channel { - explicit Channel(bool use_experimental_acm) + explicit Channel() : external_transport(false), send(false), playout(false), @@ -87,7 +97,8 @@ class FakeWebRtcVoiceEngine volume_pan_right(1.0), file(false), vad(false), - fec(false), + codec_fec(false), + red(false), nack(false), media_processor_registered(false), rx_agc_enabled(false), @@ -95,11 +106,15 @@ class FakeWebRtcVoiceEngine cn8_type(13), cn16_type(105), dtmf_type(106), - fec_type(117), + red_type(117), nack_max_packets(0), + vie_network(NULL), + video_channel(-1), send_ssrc(0), - level_header_ext_(-1), - using_experimental_acm(use_experimental_acm) { + send_audio_level_ext_(-1), + receive_audio_level_ext_(-1), + send_absolute_sender_time_ext_(-1), + receive_absolute_sender_time_ext_(-1) { memset(&send_codec, 0, sizeof(send_codec)); memset(&rx_agc_config, 0, sizeof(rx_agc_config)); } @@ -111,7 +126,8 @@ class FakeWebRtcVoiceEngine float volume_pan_right; bool file; bool vad; - bool fec; + bool codec_fec; + bool red; bool nack; bool media_processor_registered; bool rx_agc_enabled; @@ -120,15 +136,20 @@ class FakeWebRtcVoiceEngine int cn8_type; int cn16_type; int dtmf_type; - int fec_type; + int red_type; int nack_max_packets; + webrtc::ViENetwork* vie_network; + int video_channel; uint32 send_ssrc; - int level_header_ext_; + int send_audio_level_ext_; + int receive_audio_level_ext_; + int send_absolute_sender_time_ext_; + int receive_absolute_sender_time_ext_; DtmfInfo dtmf_info; std::vector<webrtc::CodecInst> recv_codecs; webrtc::CodecInst send_codec; + webrtc::PacketTime last_rtp_packet_time; std::list<std::string> packets; - bool using_experimental_acm; }; FakeWebRtcVoiceEngine(const cricket::AudioCodec* const* codecs, @@ -138,6 +159,7 @@ class FakeWebRtcVoiceEngine fail_create_channel_(false), codecs_(codecs), num_codecs_(num_codecs), + num_set_send_codecs_(0), ec_enabled_(false), ec_metrics_enabled_(false), cng_enabled_(false), @@ -195,8 +217,11 @@ class FakeWebRtcVoiceEngine bool GetVAD(int channel) { return channels_[channel]->vad; } - bool GetFEC(int channel) { - return channels_[channel]->fec; + bool GetRED(int channel) { + return channels_[channel]->red; + } + bool GetCodecFEC(int channel) { + return channels_[channel]->codec_fec; } bool GetNACK(int channel) { return channels_[channel]->nack; @@ -204,9 +229,17 @@ class FakeWebRtcVoiceEngine int GetNACKMaxPackets(int channel) { return channels_[channel]->nack_max_packets; } - bool IsUsingExperimentalAcm(int channel) { + webrtc::ViENetwork* GetViENetwork(int channel) { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_[channel]->vie_network; + } + int GetVideoChannel(int channel) { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_[channel]->video_channel; + } + const webrtc::PacketTime& GetLastRtpPacketTime(int channel) { WEBRTC_ASSERT_CHANNEL(channel); - return channels_[channel]->using_experimental_acm; + return channels_[channel]->last_rtp_packet_time; } int GetSendCNPayloadType(int channel, bool wideband) { return (wideband) ? @@ -216,8 +249,8 @@ class FakeWebRtcVoiceEngine int GetSendTelephoneEventPayloadType(int channel) { return channels_[channel]->dtmf_type; } - int GetSendFECPayloadType(int channel) { - return channels_[channel]->fec_type; + int GetSendREDPayloadType(int channel) { + return channels_[channel]->red_type; } bool CheckPacket(int channel, const void* data, size_t len) { bool result = !CheckNoPacket(channel); @@ -261,11 +294,11 @@ class FakeWebRtcVoiceEngine true); } } - int AddChannel(bool use_experimental_acm) { + int AddChannel() { if (fail_create_channel_) { return -1; } - Channel* ch = new Channel(use_experimental_acm); + Channel* ch = new Channel(); for (int i = 0; i < NumOfCodecs(); ++i) { webrtc::CodecInst codec; GetCodec(i, codec); @@ -274,6 +307,26 @@ class FakeWebRtcVoiceEngine channels_[++last_channel_] = ch; return last_channel_; } + int GetSendRtpExtensionId(int channel, const std::string& extension) { + WEBRTC_ASSERT_CHANNEL(channel); + if (extension == kRtpAudioLevelHeaderExtension) { + return channels_[channel]->send_audio_level_ext_; + } else if (extension == kRtpAbsoluteSenderTimeHeaderExtension) { + return channels_[channel]->send_absolute_sender_time_ext_; + } + return -1; + } + int GetReceiveRtpExtensionId(int channel, const std::string& extension) { + WEBRTC_ASSERT_CHANNEL(channel); + if (extension == kRtpAudioLevelHeaderExtension) { + return channels_[channel]->receive_audio_level_ext_; + } else if (extension == kRtpAbsoluteSenderTimeHeaderExtension) { + return channels_[channel]->receive_absolute_sender_time_ext_; + } + return -1; + } + + int GetNumSetSendCodecs() const { return num_set_send_codecs_; } WEBRTC_STUB(Release, ()); @@ -297,13 +350,10 @@ class FakeWebRtcVoiceEngine return NULL; } WEBRTC_FUNC(CreateChannel, ()) { - return AddChannel(false); + return AddChannel(); } - WEBRTC_FUNC(CreateChannel, (const webrtc::Config& config)) { - talk_base::scoped_ptr<webrtc::AudioCodingModule> acm( - config.Get<webrtc::AudioCodingModuleFactory>().Create(0)); - return AddChannel(strcmp(acm->Version(), webrtc::kExperimentalAcmVersion) - == 0); + WEBRTC_FUNC(CreateChannel, (const webrtc::Config& /*config*/)) { + return AddChannel(); } WEBRTC_FUNC(DeleteChannel, (int channel)) { WEBRTC_CHECK_CHANNEL(channel); @@ -371,7 +421,15 @@ class FakeWebRtcVoiceEngine } WEBRTC_FUNC(SetSendCodec, (int channel, const webrtc::CodecInst& codec)) { WEBRTC_CHECK_CHANNEL(channel); + // To match the behavior of the real implementation. + if (_stricmp(codec.plname, "telephone-event") == 0 || + _stricmp(codec.plname, "audio/telephone-event") == 0 || + _stricmp(codec.plname, "CN") == 0 || + _stricmp(codec.plname, "red") == 0 ) { + return -1; + } channels_[channel]->send_codec = codec; + ++num_set_send_codecs_; return 0; } WEBRTC_FUNC(GetSendCodec, (int channel, webrtc::CodecInst& codec)) { @@ -385,7 +443,26 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(RemoveSecondarySendCodec, (int channel)); WEBRTC_STUB(GetSecondarySendCodec, (int channel, webrtc::CodecInst& codec)); - WEBRTC_STUB(GetRecCodec, (int channel, webrtc::CodecInst& codec)); + WEBRTC_FUNC(GetRecCodec, (int channel, webrtc::CodecInst& codec)) { + WEBRTC_CHECK_CHANNEL(channel); + const Channel* c = channels_[channel]; + for (std::list<std::string>::const_iterator it_packet = c->packets.begin(); + it_packet != c->packets.end(); ++it_packet) { + int pltype; + if (!GetRtpPayloadType(it_packet->data(), it_packet->length(), &pltype)) { + continue; + } + for (std::vector<webrtc::CodecInst>::const_iterator it_codec = + c->recv_codecs.begin(); it_codec != c->recv_codecs.end(); + ++it_codec) { + if (it_codec->pltype == pltype) { + codec = *it_codec; + return 0; + } + } + } + return -1; + } WEBRTC_STUB(SetAMREncFormat, (int channel, webrtc::AmrMode mode)); WEBRTC_STUB(SetAMRDecFormat, (int channel, webrtc::AmrMode mode)); WEBRTC_STUB(SetAMRWbEncFormat, (int channel, webrtc::AmrMode mode)); @@ -459,6 +536,18 @@ class FakeWebRtcVoiceEngine } WEBRTC_STUB(GetVADStatus, (int channel, bool& enabled, webrtc::VadModes& mode, bool& disabledDTX)); +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(SetFECStatus, (int channel, bool enable)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->codec_fec = enable; + return 0; + } + WEBRTC_FUNC(GetFECStatus, (int channel, bool& enable)) { + WEBRTC_CHECK_CHANNEL(channel); + enable = channels_[channel]->codec_fec; + return 0; + } +#endif // USE_WEBRTC_DEV_BRANCH // webrtc::VoEDtmf WEBRTC_FUNC(SendTelephoneEvent, (int channel, int event_code, @@ -482,7 +571,6 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(SetDtmfPlayoutStatus, (int channel, bool enable)); WEBRTC_STUB(GetDtmfPlayoutStatus, (int channel, bool& enabled)); - WEBRTC_FUNC(PlayDtmfTone, (int event_code, int length_ms = 200, int attenuation_db = 10)) { dtmf_info_.dtmf_event_code = event_code; @@ -631,13 +719,11 @@ class FakeWebRtcVoiceEngine // webrtc::VoENetEqStats WEBRTC_STUB(GetNetworkStatistics, (int, webrtc::NetworkStatistics&)); -#ifdef USE_WEBRTC_DEV_BRANCH WEBRTC_FUNC_CONST(GetDecodingCallStatistics, (int channel, webrtc::AudioDecodingCallStats*)) { WEBRTC_CHECK_CHANNEL(channel); return 0; } -#endif // webrtc::VoENetwork WEBRTC_FUNC(RegisterExternalTransport, (int channel, @@ -659,6 +745,17 @@ class FakeWebRtcVoiceEngine std::string(static_cast<const char*>(data), length)); return 0; } + WEBRTC_FUNC(ReceivedRTPPacket, (int channel, const void* data, + unsigned int length, + const webrtc::PacketTime& packet_time)) { + WEBRTC_CHECK_CHANNEL(channel); + if (ReceivedRTPPacket(channel, data, length) == -1) { + return -1; + } + channels_[channel]->last_rtp_packet_time = packet_time; + return 0; + } + WEBRTC_STUB(ReceivedRTCPPacket, (int channel, const void* data, unsigned int length)); @@ -680,24 +777,37 @@ class FakeWebRtcVoiceEngine return 0; } WEBRTC_STUB(GetRemoteSSRC, (int channel, unsigned int& ssrc)); - WEBRTC_FUNC(SetRTPAudioLevelIndicationStatus, (int channel, bool enable, + WEBRTC_FUNC(SetSendAudioLevelIndicationStatus, (int channel, bool enable, unsigned char id)) { WEBRTC_CHECK_CHANNEL(channel); - if (enable && (id < 1 || id > 14)) { - // [RFC5285] The 4-bit ID is the local identifier of this element in - // the range 1-14 inclusive. - return -1; - } - channels_[channel]->level_header_ext_ = (enable) ? id : -1; + WEBRTC_CHECK_HEADER_EXTENSION_ID(enable, id); + channels_[channel]->send_audio_level_ext_ = (enable) ? id : -1; + return 0; + } +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(SetReceiveAudioLevelIndicationStatus, (int channel, bool enable, + unsigned char id)) { + WEBRTC_CHECK_CHANNEL(channel); + WEBRTC_CHECK_HEADER_EXTENSION_ID(enable, id); + channels_[channel]->receive_audio_level_ext_ = (enable) ? id : -1; + return 0; + } +#endif // USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(SetSendAbsoluteSenderTimeStatus, (int channel, bool enable, + unsigned char id)) { + WEBRTC_CHECK_CHANNEL(channel); + WEBRTC_CHECK_HEADER_EXTENSION_ID(enable, id); + channels_[channel]->send_absolute_sender_time_ext_ = (enable) ? id : -1; return 0; } - WEBRTC_FUNC(GetRTPAudioLevelIndicationStatus, (int channel, bool& enabled, - unsigned char& id)) { + WEBRTC_FUNC(SetReceiveAbsoluteSenderTimeStatus, (int channel, bool enable, + unsigned char id)) { WEBRTC_CHECK_CHANNEL(channel); - enabled = (channels_[channel]->level_header_ext_ != -1); - id = channels_[channel]->level_header_ext_; + WEBRTC_CHECK_HEADER_EXTENSION_ID(enable, id); + channels_[channel]->receive_absolute_sender_time_ext_ = (enable) ? id : -1; return 0; } + WEBRTC_STUB(GetRemoteCSRCs, (int channel, unsigned int arrCSRC[15])); WEBRTC_STUB(SetRTCPStatus, (int channel, bool enable)); WEBRTC_STUB(GetRTCPStatus, (int channel, bool& enabled)); @@ -750,16 +860,24 @@ class FakeWebRtcVoiceEngine stats.packetsReceived = kIntStatValue; return 0; } +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(SetREDStatus, (int channel, bool enable, int redPayloadtype)) { +#else WEBRTC_FUNC(SetFECStatus, (int channel, bool enable, int redPayloadtype)) { +#endif // USE_WEBRTC_DEV_BRANCH WEBRTC_CHECK_CHANNEL(channel); - channels_[channel]->fec = enable; - channels_[channel]->fec_type = redPayloadtype; + channels_[channel]->red = enable; + channels_[channel]->red_type = redPayloadtype; return 0; } +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(GetREDStatus, (int channel, bool& enable, int& redPayloadtype)) { +#else WEBRTC_FUNC(GetFECStatus, (int channel, bool& enable, int& redPayloadtype)) { +#endif // USE_WEBRTC_DEV_BRANCH WEBRTC_CHECK_CHANNEL(channel); - enable = channels_[channel]->fec; - redPayloadtype = channels_[channel]->fec_type; + enable = channels_[channel]->red; + redPayloadtype = channels_[channel]->red_type; return 0; } WEBRTC_FUNC(SetNACKStatus, (int channel, bool enable, int maxNoPackets)) { @@ -777,6 +895,14 @@ class FakeWebRtcVoiceEngine unsigned short payloadSize)); WEBRTC_STUB(GetLastRemoteTimeStamp, (int channel, uint32_t* lastRemoteTimeStamp)); + WEBRTC_FUNC(SetVideoEngineBWETarget, (int channel, + webrtc::ViENetwork* vie_network, + int video_channel)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->vie_network = vie_network; + channels_[channel]->video_channel = video_channel; + return 0; + } // webrtc::VoEVideoSync WEBRTC_STUB(GetPlayoutBufferSize, (int& bufferMs)); @@ -923,9 +1049,7 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(GetEcDelayMetrics, (int& delay_median, int& delay_std)); WEBRTC_STUB(StartDebugRecording, (const char* fileNameUTF8)); -#ifdef USE_WEBRTC_DEV_BRANCH WEBRTC_STUB(StartDebugRecording, (FILE* handle)); -#endif WEBRTC_STUB(StopDebugRecording, ()); WEBRTC_FUNC(SetTypingDetectionStatus, (bool enable)) { @@ -1041,6 +1165,7 @@ class FakeWebRtcVoiceEngine bool fail_create_channel_; const cricket::AudioCodec* const* codecs_; int num_codecs_; + int num_set_send_codecs_; // how many times we call SetSendCodec(). bool ec_enabled_; bool ec_metrics_enabled_; bool cng_enabled_; @@ -1065,6 +1190,8 @@ class FakeWebRtcVoiceEngine webrtc::VoEMediaProcess* media_processor_; }; +#undef WEBRTC_CHECK_HEADER_EXTENSION_ID + } // namespace cricket #endif // TALK_SESSION_PHONE_FAKEWEBRTCVOICEENGINE_H_ diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/textchatreceivetask.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcmediaengine.cc index cbd019c7e35..445564c82ae 100644 --- a/chromium/third_party/libjingle/source/talk/examples/chat/textchatreceivetask.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcmediaengine.cc @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2013, Google Inc. + * Copyright 2014 Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,42 +25,34 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "talk/examples/chat/textchatreceivetask.h" - -#include "talk/xmpp/constants.h" - -namespace buzz { - -TextChatReceiveTask::TextChatReceiveTask(XmppTaskParentInterface* parent) - : XmppTask(parent, XmppEngine::HL_TYPE) { -} - -TextChatReceiveTask::~TextChatReceiveTask() { - Stop(); -} - -bool TextChatReceiveTask::HandleStanza(const XmlElement* stanza) { - // Make sure that this stanza is a message - if (stanza->Name() != QN_MESSAGE) { - return false; +#include "talk/media/webrtc/webrtcmediaengine.h" +#include "webrtc/system_wrappers/interface/field_trial.h" + +WRME_EXPORT +cricket::MediaEngineInterface* CreateWebRtcMediaEngine( + webrtc::AudioDeviceModule* adm, + webrtc::AudioDeviceModule* adm_sc, + cricket::WebRtcVideoEncoderFactory* encoder_factory, + cricket::WebRtcVideoDecoderFactory* decoder_factory) { +#ifdef WEBRTC_CHROMIUM_BUILD + if (webrtc::field_trial::FindFullName("WebRTC-NewVideoAPI") == "Enabled") { + return new cricket::WebRtcMediaEngine2( + adm, adm_sc, encoder_factory, decoder_factory); } - - // see if there is any body - const XmlElement* message_body = stanza->FirstNamed(QN_BODY); - if (message_body == NULL) { - return false; - } - - // Looks good, so send the message text along. - SignalTextChatReceived(Jid(stanza->Attr(QN_FROM)), Jid(stanza->Attr(QN_TO)), - message_body->BodyText()); - - return true; +#endif // WEBRTC_CHROMIUM_BUILD + return new cricket::WebRtcMediaEngine( + adm, adm_sc, encoder_factory, decoder_factory); } -int TextChatReceiveTask::ProcessStart() { - // not queuing messages, so just block. - return STATE_BLOCKED; +WRME_EXPORT +void DestroyWebRtcMediaEngine(cricket::MediaEngineInterface* media_engine) { +#ifdef WEBRTC_CHROMIUM_BUILD + if (webrtc::field_trial::FindFullName("WebRTC-NewVideoAPI") == "Enabled") { + delete static_cast<cricket::WebRtcMediaEngine2*>(media_engine); + } else { +#endif // WEBRTC_CHROMIUM_BUILD + delete static_cast<cricket::WebRtcMediaEngine*>(media_engine); +#ifdef WEBRTC_CHROMIUM_BUILD + } +#endif // WEBRTC_CHROMIUM_BUILD } - -} // namespace buzz diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcmediaengine.h b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcmediaengine.h index 94e7a99d826..6ca39e7dcf0 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcmediaengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcmediaengine.h @@ -145,6 +145,9 @@ class WebRtcMediaEngine : public cricket::MediaEngineInterface { virtual void SetVideoLogging(int min_sev, const char* filter) OVERRIDE { delegate_->SetVideoLogging(min_sev, filter); } + virtual bool StartAecDump(talk_base::PlatformFile file) OVERRIDE { + return delegate_->StartAecDump(file); + } virtual bool RegisterVoiceProcessor( uint32 ssrc, VoiceProcessor* video_processor, MediaProcessorDirection direction) OVERRIDE { @@ -172,6 +175,9 @@ class WebRtcMediaEngine : public cricket::MediaEngineInterface { #else #include "talk/media/webrtc/webrtcvideoengine.h" +#ifdef WEBRTC_CHROMIUM_BUILD +#include "talk/media/webrtc/webrtcvideoengine2.h" +#endif #include "talk/media/webrtc/webrtcvoiceengine.h" namespace cricket { @@ -192,6 +198,23 @@ class WebRtcMediaEngine : public WebRtcCompositeMediaEngine { } }; +#ifdef WEBRTC_CHROMIUM_BUILD +typedef CompositeMediaEngine<WebRtcVoiceEngine, WebRtcVideoEngine2> + WebRtcCompositeMediaEngine2; + +class WebRtcMediaEngine2 : public WebRtcCompositeMediaEngine2 { + public: + WebRtcMediaEngine2(webrtc::AudioDeviceModule* adm, + webrtc::AudioDeviceModule* adm_sc, + WebRtcVideoEncoderFactory* encoder_factory, + WebRtcVideoDecoderFactory* decoder_factory) { + voice_.SetAudioDeviceModule(adm, adm_sc); + video_.SetVoiceEngine(&voice_); + video_.EnableTimedRender(); + } +}; +#endif + } // namespace cricket #endif // !defined(LIBPEERCONNECTION_LIB) && diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.cc index 6e81b401613..d7fc1b2f242 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.cc @@ -32,6 +32,7 @@ #endif #ifdef HAVE_WEBRTC_VIDEO +#include "talk/base/criticalsection.h" #include "talk/base/logging.h" #include "talk/base/thread.h" #include "talk/base/timeutils.h" @@ -189,11 +190,15 @@ bool WebRtcVideoCapturer::Init(const Device& device) { } } factory_->DestroyDeviceInfo(info); +// TODO(fischman): Remove the following check +// when capabilities for iOS are implemented +// https://code.google.com/p/webrtc/issues/detail?id=2968 +#if !defined(IOS) if (supported.empty()) { LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id; return false; } - +#endif module_ = factory_->Create(0, vcm_id); if (!module_) { LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id; @@ -247,6 +252,7 @@ CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) { return CS_NO_DEVICE; } + talk_base::CritScope cs(&critical_section_stopping_); // TODO(hellner): weird to return failure when it is in fact actually running. if (IsRunning()) { LOG(LS_ERROR) << "The capturer is already running"; @@ -263,8 +269,8 @@ CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) { std::string camera_id(GetId()); uint32 start = talk_base::Time(); - if (module_->RegisterCaptureDataCallback(*this) != 0 || - module_->StartCapture(cap) != 0) { + module_->RegisterCaptureDataCallback(*this); + if (module_->StartCapture(cap) != 0) { LOG(LS_ERROR) << "Camera '" << camera_id << "' failed to start"; return CS_FAILED; } @@ -278,7 +284,13 @@ CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) { return CS_STARTING; } +// Critical section blocks Stop from shutting down during callbacks from capture +// thread to OnIncomingCapturedFrame. Note that the crit is try-locked in +// OnFrameCaptured, as the lock ordering between this and the system component +// controlling the camera is reversed: system frame -> OnIncomingCapturedFrame; +// Stop -> system stop camera). void WebRtcVideoCapturer::Stop() { + talk_base::CritScope cs(&critical_section_stopping_); if (IsRunning()) { talk_base::Thread::Current()->Clear(this); module_->StopCapture(); @@ -313,7 +325,17 @@ bool WebRtcVideoCapturer::GetPreferredFourccs( void WebRtcVideoCapturer::OnIncomingCapturedFrame(const int32_t id, webrtc::I420VideoFrame& sample) { - ASSERT(IsRunning()); + // This would be a normal CritScope, except that it's possible that: + // (1) whatever system component producing this frame has taken a lock, and + // (2) Stop() probably calls back into that system component, which may take + // the same lock. Due to the reversed order, we have to try-lock in order to + // avoid a potential deadlock. Besides, if we can't enter because we're + // stopping, we may as well drop the frame. + talk_base::TryCritScope cs(&critical_section_stopping_); + if (!cs.locked() || !IsRunning()) { + // Capturer has been stopped or is in the process of stopping. + return; + } ++captured_frames_; // Log the size and pixel aspect ratio of the first captured frame. diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.h b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.h index c20a05919e0..cefad5629f0 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.h @@ -31,6 +31,7 @@ #include <string> #include <vector> +#include "talk/base/criticalsection.h" #include "talk/base/messagehandler.h" #include "talk/media/base/videocapturer.h" #include "talk/media/webrtc/webrtcvideoframe.h" @@ -89,6 +90,9 @@ class WebRtcVideoCapturer : public VideoCapturer, webrtc::VideoCaptureModule* module_; int captured_frames_; std::vector<uint8_t> capture_buffer_; + + // Critical section to avoid Stop during an OnIncomingCapturedFrame callback. + talk_base::CriticalSection critical_section_stopping_; }; struct WebRtcCapturedFrame : public CapturedFrame { diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideochannelfactory.h b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideochannelfactory.h new file mode 100644 index 00000000000..646348cd55c --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideochannelfactory.h @@ -0,0 +1,44 @@ +/* + * libjingle + * Copyright 2004 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_MEDIA_WEBRTC_WEBRTCVIDEOCHANNEL_H_ +#define TALK_MEDIA_WEBRTC_WEBRTCVIDEOCHANNEL_H_ + +namespace cricket { +class VoiceMediaChannel; +class WebRtcVideoEngine2; +class WebRtcVideoChannel2; + +class WebRtcVideoChannelFactory { + public: + virtual ~WebRtcVideoChannelFactory() {} + virtual WebRtcVideoChannel2* Create(WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel) = 0; +}; +} // namespace cricket + +#endif // TALK_MEDIA_WEBRTC_WEBRTCVIDEOCHANNEL_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.cc index 88e09fc3842..fd609e9a4db 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.cc @@ -60,27 +60,8 @@ #include "talk/media/webrtc/webrtcvie.h" #include "talk/media/webrtc/webrtcvoe.h" #include "talk/media/webrtc/webrtcvoiceengine.h" - -#if !defined(LIBPEERCONNECTION_LIB) -#ifndef HAVE_WEBRTC_VIDEO -#error Need webrtc video -#endif -#include "talk/media/webrtc/webrtcmediaengine.h" - -WRME_EXPORT -cricket::MediaEngineInterface* CreateWebRtcMediaEngine( - webrtc::AudioDeviceModule* adm, webrtc::AudioDeviceModule* adm_sc, - cricket::WebRtcVideoEncoderFactory* encoder_factory, - cricket::WebRtcVideoDecoderFactory* decoder_factory) { - return new cricket::WebRtcMediaEngine(adm, adm_sc, encoder_factory, - decoder_factory); -} - -WRME_EXPORT -void DestroyWebRtcMediaEngine(cricket::MediaEngineInterface* media_engine) { - delete static_cast<cricket::WebRtcMediaEngine*>(media_engine); -} -#endif +#include "webrtc/experiments.h" +#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" namespace cricket { @@ -91,7 +72,6 @@ static const int kDefaultLogSeverity = talk_base::LS_WARNING; static const int kMinVideoBitrate = 50; static const int kStartVideoBitrate = 300; static const int kMaxVideoBitrate = 2000; -static const int kDefaultConferenceModeMaxVideoBitrate = 500; // Controlled by exp, try a super low minimum bitrate for poor connections. static const int kLowerMinBitrate = 30; @@ -106,14 +86,22 @@ static const char kFecPayloadName[] = "ulpfec"; static const int kDefaultNumberOfTemporalLayers = 1; // 1:1 -static const int kTimestampDeltaInSecondsForWarning = 2; - -static const int kMaxExternalVideoCodecs = 8; static const int kExternalVideoPayloadTypeBase = 120; +static bool BitrateIsSet(int value) { + return value > kAutoBandwidth; +} + +static int GetBitrate(int value, int deflt) { + return BitrateIsSet(value) ? value : deflt; +} + // Static allocation of payload type values for external video codec. static int GetExternalVideoPayloadType(int index) { +#if ENABLE_DEBUG + static const int kMaxExternalVideoCodecs = 8; ASSERT(index >= 0 && index < kMaxExternalVideoCodecs); +#endif return kExternalVideoPayloadTypeBase + index; } @@ -145,17 +133,6 @@ static const int kCpuMonitorPeriodMs = 2000; // 2 seconds. static const bool kNotSending = false; -// Extension header for RTP timestamp offset, see RFC 5450 for details: -// http://tools.ietf.org/html/rfc5450 -static const char kRtpTimestampOffsetHeaderExtension[] = - "urn:ietf:params:rtp-hdrext:toffset"; -static const int kRtpTimeOffsetExtensionId = 2; - -// Extension header for absolute send time, see url for details: -// http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time -static const char kRtpAbsoluteSendTimeHeaderExtension[] = - "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"; -static const int kRtpAbsoluteSendTimeExtensionId = 3; // Default video dscp value. // See http://tools.ietf.org/html/rfc2474 for details // See also http://tools.ietf.org/html/draft-jennings-rtcweb-qos-00 @@ -182,18 +159,18 @@ struct FlushBlackFrameData : public talk_base::MessageData { class WebRtcRenderAdapter : public webrtc::ExternalRenderer { public: - explicit WebRtcRenderAdapter(VideoRenderer* renderer) - : renderer_(renderer), width_(0), height_(0), watermark_enabled_(false) { + WebRtcRenderAdapter(VideoRenderer* renderer, int channel_id) + : renderer_(renderer), + channel_id_(channel_id), + width_(0), + height_(0), + capture_start_rtp_time_stamp_(-1), + capture_start_ntp_time_ms_(0) { } virtual ~WebRtcRenderAdapter() { } - void set_watermark_enabled(bool enable) { - talk_base::CritScope cs(&crit_); - watermark_enabled_ = enable; - } - void SetRenderer(VideoRenderer* renderer) { talk_base::CritScope cs(&crit_); renderer_ = renderer; @@ -205,7 +182,8 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { if (width_ > 0 && height_ > 0 && renderer_ != NULL) { if (!renderer_->SetSize(width_, height_, 0)) { LOG(LS_ERROR) - << "WebRtcRenderAdapter SetRenderer failed to SetSize to: " + << "WebRtcRenderAdapter (channel " << channel_id_ + << ") SetRenderer failed to SetSize to: " << width_ << "x" << height_; } } @@ -217,53 +195,75 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { talk_base::CritScope cs(&crit_); width_ = width; height_ = height; - LOG(LS_INFO) << "WebRtcRenderAdapter frame size changed to: " + LOG(LS_INFO) << "WebRtcRenderAdapter (channel " << channel_id_ + << ") frame size changed to: " << width << "x" << height; if (renderer_ == NULL) { - LOG(LS_VERBOSE) << "WebRtcRenderAdapter the renderer has not been set. " + LOG(LS_VERBOSE) << "WebRtcRenderAdapter (channel " << channel_id_ + << ") the renderer has not been set. " << "SetSize will be called later in SetRenderer."; return 0; } return renderer_->SetSize(width_, height_, 0) ? 0 : -1; } - virtual int DeliverFrame(unsigned char* buffer, int buffer_size, - uint32_t time_stamp, int64_t render_time, + virtual int DeliverFrame(unsigned char* buffer, + int buffer_size, + uint32_t rtp_time_stamp, +#ifdef USE_WEBRTC_DEV_BRANCH + int64_t ntp_time_ms, +#endif + int64_t render_time, void* handle) { talk_base::CritScope cs(&crit_); + if (capture_start_rtp_time_stamp_ < 0) { + capture_start_rtp_time_stamp_ = rtp_time_stamp; + } + + const int kVideoCodecClockratekHz = cricket::kVideoCodecClockrate / 1000; + + int64 elapsed_time_ms = + (rtp_ts_wraparound_handler_.Unwrap(rtp_time_stamp) - + capture_start_rtp_time_stamp_) / kVideoCodecClockratekHz; +#ifdef USE_WEBRTC_DEV_BRANCH + if (ntp_time_ms > 0) { + capture_start_ntp_time_ms_ = ntp_time_ms - elapsed_time_ms; + } +#endif frame_rate_tracker_.Update(1); if (renderer_ == NULL) { return 0; } - // Convert 90K rtp timestamp to ns timestamp. - int64 rtp_time_stamp_in_ns = (time_stamp / 90) * - talk_base::kNumNanosecsPerMillisec; + // Convert elapsed_time_ms to ns timestamp. + int64 elapsed_time_ns = + elapsed_time_ms * talk_base::kNumNanosecsPerMillisec; // Convert milisecond render time to ns timestamp. - int64 render_time_stamp_in_ns = render_time * + int64 render_time_ns = render_time * talk_base::kNumNanosecsPerMillisec; - // Send the rtp timestamp to renderer as the VideoFrame timestamp. - // and the render timestamp as the VideoFrame elapsed_time. + // Note that here we send the |elapsed_time_ns| to renderer as the + // cricket::VideoFrame's elapsed_time_ and the |render_time_ns| as the + // cricket::VideoFrame's time_stamp_. if (handle == NULL) { - return DeliverBufferFrame(buffer, buffer_size, render_time_stamp_in_ns, - rtp_time_stamp_in_ns); + return DeliverBufferFrame(buffer, buffer_size, render_time_ns, + elapsed_time_ns); } else { - return DeliverTextureFrame(handle, render_time_stamp_in_ns, - rtp_time_stamp_in_ns); + return DeliverTextureFrame(handle, render_time_ns, + elapsed_time_ns); } } virtual bool IsTextureSupported() { return true; } int DeliverBufferFrame(unsigned char* buffer, int buffer_size, - int64 elapsed_time, int64 time_stamp) { + int64 time_stamp, int64 elapsed_time) { WebRtcVideoFrame video_frame; video_frame.Alias(buffer, buffer_size, width_, height_, 1, 1, elapsed_time, time_stamp, 0); - // Sanity check on decoded frame size. if (buffer_size != static_cast<int>(VideoFrame::SizeOf(width_, height_))) { - LOG(LS_WARNING) << "WebRtcRenderAdapter received a strange frame size: " + LOG(LS_WARNING) << "WebRtcRenderAdapter (channel " << channel_id_ + << ") received a strange frame size: " << buffer_size; } @@ -271,7 +271,7 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { return ret; } - int DeliverTextureFrame(void* handle, int64 elapsed_time, int64 time_stamp) { + int DeliverTextureFrame(void* handle, int64 time_stamp, int64 elapsed_time) { WebRtcTextureVideoFrame video_frame( static_cast<webrtc::NativeHandle*>(handle), width_, height_, elapsed_time, time_stamp); @@ -298,13 +298,21 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { return renderer_; } + int64 capture_start_ntp_time_ms() { + talk_base::CritScope cs(&crit_); + return capture_start_ntp_time_ms_; + } + private: talk_base::CriticalSection crit_; VideoRenderer* renderer_; + int channel_id_; unsigned int width_; unsigned int height_; talk_base::RateTracker frame_rate_tracker_; - bool watermark_enabled_; + talk_base::TimestampWrapAroundHandler rtp_ts_wraparound_handler_; + int64 capture_start_rtp_time_stamp_; + int64 capture_start_ntp_time_ms_; }; class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver { @@ -319,8 +327,7 @@ class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver { target_delay_ms_(0), jitter_buffer_ms_(0), min_playout_delay_ms_(0), - render_delay_ms_(0), - firs_requested_(0) { + render_delay_ms_(0) { } // virtual functions from VieDecoderObserver. @@ -352,16 +359,11 @@ class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver { render_delay_ms_ = render_delay_ms; } - virtual void RequestNewKeyFrame(const int videoChannel) { - talk_base::CritScope cs(&crit_); - ASSERT(video_channel_ == videoChannel); - ++firs_requested_; - } + virtual void RequestNewKeyFrame(const int videoChannel) {} // Populate |rinfo| based on previously-set data in |*this|. void ExportTo(VideoReceiverInfo* rinfo) { talk_base::CritScope cs(&crit_); - rinfo->firs_sent = firs_requested_; rinfo->framerate_rcvd = framerate_; rinfo->decode_ms = decode_ms_; rinfo->max_decode_ms = max_decode_ms_; @@ -384,7 +386,6 @@ class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver { int jitter_buffer_ms_; int min_playout_delay_ms_; int render_delay_ms_; - int firs_requested_; }; class WebRtcEncoderObserver : public webrtc::ViEEncoderObserver { @@ -497,7 +498,7 @@ class WebRtcVideoChannelRecvInfo { typedef std::map<int, webrtc::VideoDecoder*> DecoderMap; // key: payload type explicit WebRtcVideoChannelRecvInfo(int channel_id) : channel_id_(channel_id), - render_adapter_(NULL), + render_adapter_(NULL, channel_id), decoder_observer_(channel_id) { } int channel_id() { return channel_id_; } @@ -557,10 +558,13 @@ class WebRtcOveruseObserver : public webrtc::CpuOveruseObserver { } void Enable(bool enable) { + LOG(LS_INFO) << "WebRtcOveruseObserver enable: " << enable; talk_base::CritScope cs(&crit_); enabled_ = enable; } + bool enabled() const { return enabled_; } + private: CoordinatedVideoAdapter* video_adapter_; bool enabled_; @@ -583,14 +587,7 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { external_capture_(external_capture), capturer_updated_(false), interval_(0), - video_adapter_(new CoordinatedVideoAdapter), cpu_monitor_(cpu_monitor) { - overuse_observer_.reset(new WebRtcOveruseObserver(video_adapter_.get())); - SignalCpuAdaptationUnable.repeat(video_adapter_->SignalCpuAdaptationUnable); - if (cpu_monitor) { - cpu_monitor->SignalUpdate.connect( - video_adapter_.get(), &CoordinatedVideoAdapter::OnCpuLoadUpdated); - } } int channel_id() const { return channel_id_; } @@ -599,7 +596,7 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { bool sending() const { return sending_; } void set_muted(bool on) { // TODO(asapersson): add support. - // video_adapter_->SetBlackOutput(on); + // video_adapter_.SetBlackOutput(on); muted_ = on; } bool muted() {return muted_; } @@ -614,7 +611,10 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { if (video_format_ != cricket::VideoFormat()) { interval_ = video_format_.interval; } - video_adapter_->OnOutputFormatRequest(video_format_); + CoordinatedVideoAdapter* adapter = video_adapter(); + if (adapter) { + adapter->OnOutputFormatRequest(video_format_); + } } void set_interval(int64 interval) { if (video_format() == cricket::VideoFormat()) { @@ -623,20 +623,12 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { } int64 interval() { return interval_; } - void InitializeAdapterOutputFormat(const webrtc::VideoCodec& codec) { - VideoFormat format(codec.width, codec.height, - VideoFormat::FpsToInterval(codec.maxFramerate), - FOURCC_I420); - if (video_adapter_->output_format().IsSize0x0()) { - video_adapter_->SetOutputFormat(format); - } - } - int CurrentAdaptReason() const { - return video_adapter_->adapt_reason(); - } - webrtc::CpuOveruseObserver* overuse_observer() { - return overuse_observer_.get(); + const CoordinatedVideoAdapter* adapter = video_adapter(); + if (!adapter) { + return CoordinatedVideoAdapter::ADAPTREASON_NONE; + } + return video_adapter()->adapt_reason(); } StreamParams* stream_params() { return stream_params_.get(); } @@ -654,65 +646,127 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { VideoCapturer* video_capturer() { return video_capturer_; } - void set_video_capturer(VideoCapturer* video_capturer) { + void set_video_capturer(VideoCapturer* video_capturer, + ViEWrapper* vie_wrapper) { if (video_capturer == video_capturer_) { return; } + + CoordinatedVideoAdapter* old_video_adapter = video_adapter(); + if (old_video_adapter) { + // Disconnect signals from old video adapter. + SignalCpuAdaptationUnable.disconnect(old_video_adapter); + if (cpu_monitor_) { + cpu_monitor_->SignalUpdate.disconnect(old_video_adapter); + } + } + capturer_updated_ = true; video_capturer_ = video_capturer; - if (video_capturer && !video_capturer->IsScreencast()) { - const VideoFormat* capture_format = video_capturer->GetCaptureFormat(); - if (capture_format) { - // TODO(thorcarpenter): This is broken. Video capturer doesn't have - // a capture format until the capturer is started. So, if - // the capturer is started immediately after calling set_video_capturer - // video adapter may not have the input format set, the interval may - // be zero, and all frames may be dropped. - // Consider fixing this by having video_adapter keep a pointer to the - // video capturer. - video_adapter_->SetInputFormat(*capture_format); - } - // TODO(thorcarpenter): When the adapter supports "only frame dropping" - // mode, also hook it up to screencast capturers. - video_capturer->SignalAdaptFrame.connect( - this, &WebRtcVideoChannelSendInfo::AdaptFrame); + + vie_wrapper->base()->RegisterCpuOveruseObserver(channel_id_, NULL); + if (!video_capturer) { + overuse_observer_.reset(); + return; } + + CoordinatedVideoAdapter* adapter = video_adapter(); + ASSERT(adapter && "Video adapter should not be null here."); + + UpdateAdapterCpuOptions(); + + overuse_observer_.reset(new WebRtcOveruseObserver(adapter)); + vie_wrapper->base()->RegisterCpuOveruseObserver(channel_id_, + overuse_observer_.get()); + // (Dis)connect the video adapter from the cpu monitor as appropriate. + SetCpuOveruseDetection( + video_options_.cpu_overuse_detection.GetWithDefaultIfUnset(false)); + + SignalCpuAdaptationUnable.repeat(adapter->SignalCpuAdaptationUnable); } - void AdaptFrame(VideoCapturer* capturer, const VideoFrame* input, - VideoFrame** adapted) { - video_adapter_->AdaptFrame(input, adapted); + CoordinatedVideoAdapter* video_adapter() { + if (!video_capturer_) { + return NULL; + } + return video_capturer_->video_adapter(); + } + const CoordinatedVideoAdapter* video_adapter() const { + if (!video_capturer_) { + return NULL; + } + return video_capturer_->video_adapter(); + } + + void ApplyCpuOptions(const VideoOptions& video_options) { + bool cpu_overuse_detection_changed = + video_options.cpu_overuse_detection.IsSet() && + (video_options.cpu_overuse_detection.GetWithDefaultIfUnset(false) != + video_options_.cpu_overuse_detection.GetWithDefaultIfUnset(false)); + // Use video_options_.SetAll() instead of assignment so that unset value in + // video_options will not overwrite the previous option value. + video_options_.SetAll(video_options); + UpdateAdapterCpuOptions(); + if (cpu_overuse_detection_changed) { + SetCpuOveruseDetection( + video_options_.cpu_overuse_detection.GetWithDefaultIfUnset(false)); + } } - void ApplyCpuOptions(const VideoOptions& options) { - bool cpu_adapt, cpu_smoothing, adapt_third; + void UpdateAdapterCpuOptions() { + if (!video_capturer_) { + return; + } + + bool cpu_smoothing, adapt_third; float low, med, high; - if (options.adapt_input_to_cpu_usage.Get(&cpu_adapt)) { - video_adapter_->set_cpu_adaptation(cpu_adapt); + bool cpu_adapt = + video_options_.adapt_input_to_cpu_usage.GetWithDefaultIfUnset(false); + bool cpu_overuse_detection = + video_options_.cpu_overuse_detection.GetWithDefaultIfUnset(false); + + // TODO(thorcarpenter): Have VideoAdapter be responsible for setting + // all these video options. + CoordinatedVideoAdapter* video_adapter = video_capturer_->video_adapter(); + if (video_options_.adapt_input_to_cpu_usage.IsSet() || + video_options_.cpu_overuse_detection.IsSet()) { + video_adapter->set_cpu_adaptation(cpu_adapt || cpu_overuse_detection); } - if (options.adapt_cpu_with_smoothing.Get(&cpu_smoothing)) { - video_adapter_->set_cpu_smoothing(cpu_smoothing); + if (video_options_.adapt_cpu_with_smoothing.Get(&cpu_smoothing)) { + video_adapter->set_cpu_smoothing(cpu_smoothing); } - if (options.process_adaptation_threshhold.Get(&med)) { - video_adapter_->set_process_threshold(med); + if (video_options_.process_adaptation_threshhold.Get(&med)) { + video_adapter->set_process_threshold(med); } - if (options.system_low_adaptation_threshhold.Get(&low)) { - video_adapter_->set_low_system_threshold(low); + if (video_options_.system_low_adaptation_threshhold.Get(&low)) { + video_adapter->set_low_system_threshold(low); } - if (options.system_high_adaptation_threshhold.Get(&high)) { - video_adapter_->set_high_system_threshold(high); + if (video_options_.system_high_adaptation_threshhold.Get(&high)) { + video_adapter->set_high_system_threshold(high); } - if (options.video_adapt_third.Get(&adapt_third)) { - video_adapter_->set_scale_third(adapt_third); + if (video_options_.video_adapt_third.Get(&adapt_third)) { + video_adapter->set_scale_third(adapt_third); } } void SetCpuOveruseDetection(bool enable) { - if (cpu_monitor_ && enable) { - cpu_monitor_->SignalUpdate.disconnect(video_adapter_.get()); + if (overuse_observer_) { + overuse_observer_->Enable(enable); + } + + // The video adapter is signaled by overuse detection if enabled; otherwise + // it will be signaled by cpu monitor. + CoordinatedVideoAdapter* adapter = video_adapter(); + if (adapter) { + if (cpu_monitor_) { + if (enable) { + cpu_monitor_->SignalUpdate.disconnect(adapter); + } else { + cpu_monitor_->SignalUpdate.connect( + adapter, &CoordinatedVideoAdapter::OnCpuLoadUpdated); + } + } } - overuse_observer_->Enable(enable); - video_adapter_->set_cpu_adaptation(enable); } void ProcessFrame(const VideoFrame& original_frame, bool mute, @@ -766,9 +820,10 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { int64 interval_; - talk_base::scoped_ptr<CoordinatedVideoAdapter> video_adapter_; talk_base::CpuMonitor* cpu_monitor_; talk_base::scoped_ptr<WebRtcOveruseObserver> overuse_observer_; + + VideoOptions video_options_; }; const WebRtcVideoEngine::VideoCodecPref @@ -817,6 +872,48 @@ static void UpdateVideoCodec(const cricket::VideoFormat& video_format, video_format.interval); } +static bool GetCpuOveruseOptions(const VideoOptions& options, + webrtc::CpuOveruseOptions* overuse_options) { + int underuse_threshold = 0; + int overuse_threshold = 0; + if (!options.cpu_underuse_threshold.Get(&underuse_threshold) || + !options.cpu_overuse_threshold.Get(&overuse_threshold)) { + return false; + } + if (underuse_threshold <= 0 || overuse_threshold <= 0) { + return false; + } + // Valid thresholds. + bool encode_usage = + options.cpu_overuse_encode_usage.GetWithDefaultIfUnset(false); + overuse_options->enable_capture_jitter_method = !encode_usage; + overuse_options->enable_encode_usage_method = encode_usage; + if (encode_usage) { + // Use method based on encode usage. + overuse_options->low_encode_usage_threshold_percent = underuse_threshold; + overuse_options->high_encode_usage_threshold_percent = overuse_threshold; +#ifdef USE_WEBRTC_DEV_BRANCH + // Set optional thresholds, if configured. + int underuse_rsd_threshold = 0; + if (options.cpu_underuse_encode_rsd_threshold.Get( + &underuse_rsd_threshold)) { + overuse_options->low_encode_time_rsd_threshold = underuse_rsd_threshold; + } + int overuse_rsd_threshold = 0; + if (options.cpu_overuse_encode_rsd_threshold.Get(&overuse_rsd_threshold)) { + overuse_options->high_encode_time_rsd_threshold = overuse_rsd_threshold; + } +#endif + } else { + // Use default method based on capture jitter. + overuse_options->low_capture_jitter_threshold_ms = + static_cast<float>(underuse_threshold); + overuse_options->high_capture_jitter_threshold_ms = + static_cast<float>(overuse_threshold); + } + return true; +} + WebRtcVideoEngine::WebRtcVideoEngine() { Construct(new ViEWrapper(), new ViETraceWrapper(), NULL, new talk_base::CpuMonitor(NULL)); @@ -878,10 +975,10 @@ void WebRtcVideoEngine::Construct(ViEWrapper* vie_wrapper, // Load our RTP Header extensions. rtp_header_extensions_.push_back( RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension, - kRtpTimeOffsetExtensionId)); + kRtpTimestampOffsetHeaderExtensionDefaultId)); rtp_header_extensions_.push_back( - RtpHeaderExtension(kRtpAbsoluteSendTimeHeaderExtension, - kRtpAbsoluteSendTimeExtensionId)); + RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension, + kRtpAbsoluteSenderTimeHeaderExtensionDefaultId)); } WebRtcVideoEngine::~WebRtcVideoEngine() { @@ -1173,8 +1270,15 @@ static void ConvertToCricketVideoCodec( out_codec->width = in_codec.width; out_codec->height = in_codec.height; out_codec->framerate = in_codec.maxFramerate; - out_codec->SetParam(kCodecParamMinBitrate, in_codec.minBitrate); - out_codec->SetParam(kCodecParamMaxBitrate, in_codec.maxBitrate); + if (BitrateIsSet(in_codec.minBitrate)) { + out_codec->SetParam(kCodecParamMinBitrate, in_codec.minBitrate); + } + if (BitrateIsSet(in_codec.maxBitrate)) { + out_codec->SetParam(kCodecParamMaxBitrate, in_codec.maxBitrate); + } + if (BitrateIsSet(in_codec.startBitrate)) { + out_codec->SetParam(kCodecParamStartBitrate, in_codec.startBitrate); + } if (in_codec.qpMax) { out_codec->SetParam(kCodecParamMaxQuantization, in_codec.qpMax); } @@ -1208,6 +1312,15 @@ bool WebRtcVideoEngine::ConvertFromCricketVideoCodec( } } + // Is this an RTX codec? Handled separately here since webrtc doesn't handle + // them as webrtc::VideoCodec internally. + if (!found && _stricmp(in_codec.name.c_str(), kRtxCodecName) == 0) { + talk_base::strcpyn(out_codec->plName, sizeof(out_codec->plName), + in_codec.name.c_str(), in_codec.name.length()); + out_codec->plType = in_codec.id; + found = true; + } + if (!found) { LOG(LS_ERROR) << "invalid codec type"; return false; @@ -1226,18 +1339,14 @@ bool WebRtcVideoEngine::ConvertFromCricketVideoCodec( out_codec->maxFramerate = in_codec.framerate; // Convert bitrate parameters. - int max_bitrate = kMaxVideoBitrate; - int min_bitrate = kMinVideoBitrate; - int start_bitrate = kStartVideoBitrate; + int max_bitrate = -1; + int min_bitrate = -1; + int start_bitrate = -1; in_codec.GetParam(kCodecParamMinBitrate, &min_bitrate); in_codec.GetParam(kCodecParamMaxBitrate, &max_bitrate); + in_codec.GetParam(kCodecParamStartBitrate, &start_bitrate); - if (max_bitrate < min_bitrate) { - return false; - } - start_bitrate = talk_base::_max(start_bitrate, min_bitrate); - start_bitrate = talk_base::_min(start_bitrate, max_bitrate); out_codec->minBitrate = min_bitrate; out_codec->startBitrate = start_bitrate; @@ -1310,6 +1419,8 @@ static void AddDefaultFeedbackParams(VideoCodec* codec) { codec->AddFeedbackParam(kFir); const FeedbackParam kNack(kRtcpFbParamNack, kParamValueEmpty); codec->AddFeedbackParam(kNack); + const FeedbackParam kPli(kRtcpFbParamNack, kRtcpFbNackParamPli); + codec->AddFeedbackParam(kPli); const FeedbackParam kRemb(kRtcpFbParamRemb, kParamValueEmpty); codec->AddFeedbackParam(kRemb); } @@ -1509,12 +1620,10 @@ WebRtcVideoMediaChannel::WebRtcVideoMediaChannel( remb_enabled_(false), render_started_(false), first_receive_ssrc_(0), + num_unsignalled_recv_channels_(0), send_rtx_type_(-1), send_red_type_(-1), send_fec_type_(-1), - send_min_bitrate_(kMinVideoBitrate), - send_start_bitrate_(kStartVideoBitrate), - send_max_bitrate_(kMaxVideoBitrate), sending_(false), ratio_w_(0), ratio_h_(0) { @@ -1543,7 +1652,7 @@ WebRtcVideoMediaChannel::~WebRtcVideoMediaChannel() { // Remove all receive streams and the default channel. while (!recv_channels_.empty()) { - RemoveRecvStream(recv_channels_.begin()->first); + RemoveRecvStreamInternal(recv_channels_.begin()->first); } // Unregister the channel from the engine. @@ -1556,12 +1665,17 @@ WebRtcVideoMediaChannel::~WebRtcVideoMediaChannel() { bool WebRtcVideoMediaChannel::SetRecvCodecs( const std::vector<VideoCodec>& codecs) { receive_codecs_.clear(); + associated_payload_types_.clear(); for (std::vector<VideoCodec>::const_iterator iter = codecs.begin(); iter != codecs.end(); ++iter) { if (engine()->FindCodec(*iter)) { webrtc::VideoCodec wcodec; if (engine()->ConvertFromCricketVideoCodec(*iter, &wcodec)) { receive_codecs_.push_back(wcodec); + int apt; + if (iter->GetParam(cricket::kCodecParamAssociatedPayloadType, &apt)) { + associated_payload_types_[wcodec.plType] = apt; + } } } else { LOG(LS_INFO) << "Unknown codec " << iter->name; @@ -1587,6 +1701,8 @@ bool WebRtcVideoMediaChannel::SetSendCodecs( ConvertToCricketVideoCodec(*send_codec_, ¤t); } std::map<int, int> primary_rtx_pt_mapping; + bool nack_enabled = nack_enabled_; + bool remb_enabled = remb_enabled_; for (std::vector<VideoCodec>::const_iterator iter = codecs.begin(); iter != codecs.end(); ++iter) { if (_stricmp(iter->name.c_str(), kRedPayloadName) == 0) { @@ -1603,8 +1719,8 @@ bool WebRtcVideoMediaChannel::SetSendCodecs( webrtc::VideoCodec wcodec; if (engine()->ConvertFromCricketVideoCodec(checked_codec, &wcodec)) { if (send_codecs.empty()) { - nack_enabled_ = IsNackEnabled(checked_codec); - remb_enabled_ = IsRembEnabled(checked_codec); + nack_enabled = IsNackEnabled(checked_codec); + remb_enabled = IsRembEnabled(checked_codec); } send_codecs.push_back(wcodec); } @@ -1620,35 +1736,43 @@ bool WebRtcVideoMediaChannel::SetSendCodecs( } // Recv protection. - for (RecvChannelMap::iterator it = recv_channels_.begin(); - it != recv_channels_.end(); ++it) { - int channel_id = it->second->channel_id(); - if (!SetNackFec(channel_id, send_red_type_, send_fec_type_, - nack_enabled_)) { - return false; - } - if (engine_->vie()->rtp()->SetRembStatus(channel_id, - kNotSending, - remb_enabled_) != 0) { - LOG_RTCERR3(SetRembStatus, channel_id, kNotSending, remb_enabled_); - return false; + // Do not update if the status is same as previously configured. + if (nack_enabled_ != nack_enabled) { + for (RecvChannelMap::iterator it = recv_channels_.begin(); + it != recv_channels_.end(); ++it) { + int channel_id = it->second->channel_id(); + if (!SetNackFec(channel_id, send_red_type_, send_fec_type_, + nack_enabled)) { + return false; + } + if (engine_->vie()->rtp()->SetRembStatus(channel_id, + kNotSending, + remb_enabled_) != 0) { + LOG_RTCERR3(SetRembStatus, channel_id, kNotSending, remb_enabled_); + return false; + } } + nack_enabled_ = nack_enabled; } // Send settings. - for (SendChannelMap::iterator iter = send_channels_.begin(); - iter != send_channels_.end(); ++iter) { - int channel_id = iter->second->channel_id(); - if (!SetNackFec(channel_id, send_red_type_, send_fec_type_, - nack_enabled_)) { - return false; - } - if (engine_->vie()->rtp()->SetRembStatus(channel_id, - remb_enabled_, - remb_enabled_) != 0) { - LOG_RTCERR3(SetRembStatus, channel_id, remb_enabled_, remb_enabled_); - return false; + // Do not update if the status is same as previously configured. + if (remb_enabled_ != remb_enabled) { + for (SendChannelMap::iterator iter = send_channels_.begin(); + iter != send_channels_.end(); ++iter) { + int channel_id = iter->second->channel_id(); + if (!SetNackFec(channel_id, send_red_type_, send_fec_type_, + nack_enabled_)) { + return false; + } + if (engine_->vie()->rtp()->SetRembStatus(channel_id, + remb_enabled, + remb_enabled) != 0) { + LOG_RTCERR3(SetRembStatus, channel_id, remb_enabled, remb_enabled); + return false; + } } + remb_enabled_ = remb_enabled; } // Select the first matched codec. @@ -1662,15 +1786,20 @@ bool WebRtcVideoMediaChannel::SetSendCodecs( send_rtx_type_ = rtx_it->second; } - if (!SetSendCodec( - codec, codec.minBitrate, codec.startBitrate, codec.maxBitrate)) { + if (BitrateIsSet(codec.minBitrate) && BitrateIsSet(codec.maxBitrate) && + codec.minBitrate > codec.maxBitrate) { + // TODO(pthatcher): This behavior contradicts other behavior in + // this file which will cause min > max to push the min down to + // the max. There are unit tests for both behaviors. We should + // pick one and do that. + LOG(LS_INFO) << "Rejecting codec with min bitrate (" + << codec.minBitrate << ") larger than max (" + << codec.maxBitrate << "). "; return false; } - for (SendChannelMap::iterator iter = send_channels_.begin(); - iter != send_channels_.end(); ++iter) { - WebRtcVideoChannelSendInfo* send_channel = iter->second; - send_channel->InitializeAdapterOutputFormat(codec); + if (!SetSendCodec(codec)) { + return false; } LogSendCodecChange("SetSendCodecs()"); @@ -1688,10 +1817,6 @@ bool WebRtcVideoMediaChannel::GetSendCodec(VideoCodec* send_codec) { bool WebRtcVideoMediaChannel::SetSendStreamFormat(uint32 ssrc, const VideoFormat& format) { - if (!send_codec_) { - LOG(LS_ERROR) << "The send codec has not been set yet."; - return false; - } WebRtcVideoChannelSendInfo* send_channel = GetSendChannel(ssrc); if (!send_channel) { LOG(LS_ERROR) << "The specified ssrc " << ssrc << " is not in use."; @@ -1761,6 +1886,11 @@ bool WebRtcVideoMediaChannel::SetSend(bool send) { } bool WebRtcVideoMediaChannel::AddSendStream(const StreamParams& sp) { + if (sp.first_ssrc() == 0) { + LOG(LS_ERROR) << "AddSendStream with 0 ssrc is not supported."; + return false; + } + LOG(LS_INFO) << "AddSendStream " << sp.ToString(); if (!IsOneSsrcStream(sp) && !IsSimulcastStream(sp)) { @@ -1828,8 +1958,7 @@ bool WebRtcVideoMediaChannel::AddSendStream(const StreamParams& sp) { // Reset send codec after stream parameters changed. if (send_codec_) { - if (!SetSendCodec(send_channel, *send_codec_, send_min_bitrate_, - send_start_bitrate_, send_max_bitrate_)) { + if (!SetSendCodec(send_channel, *send_codec_)) { return false; } LogSendCodecChange("SetSendStreamFormat()"); @@ -1842,6 +1971,11 @@ bool WebRtcVideoMediaChannel::AddSendStream(const StreamParams& sp) { } bool WebRtcVideoMediaChannel::RemoveSendStream(uint32 ssrc) { + if (ssrc == 0) { + LOG(LS_ERROR) << "RemoveSendStream with 0 ssrc is not supported."; + return false; + } + uint32 ssrc_key; if (!GetSendChannelKey(ssrc, &ssrc_key)) { LOG(LS_WARNING) << "Try to remove stream with ssrc " << ssrc @@ -1882,6 +2016,11 @@ bool WebRtcVideoMediaChannel::RemoveSendStream(uint32 ssrc) { } bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) { + if (sp.first_ssrc() == 0) { + LOG(LS_ERROR) << "AddRecvStream with 0 ssrc is not supported."; + return false; + } + // TODO(zhurunz) Remove this once BWE works properly across different send // and receive channels. // Reuse default channel for recv stream in 1:1 call. @@ -1890,6 +2029,9 @@ bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) { << " reuse default channel #" << vie_channel_; first_receive_ssrc_ = sp.first_ssrc(); + if (!MaybeSetRtxSsrc(sp, vie_channel_)) { + return false; + } if (render_started_) { if (engine()->vie()->render()->StartRender(vie_channel_) !=0) { LOG_RTCERR1(StartRender, vie_channel_); @@ -1898,36 +2040,37 @@ bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) { return true; } - if (recv_channels_.find(sp.first_ssrc()) != recv_channels_.end() || - first_receive_ssrc_ == sp.first_ssrc()) { - LOG(LS_ERROR) << "Stream already exists"; - return false; - } - - // TODO(perkj): Implement recv media from multiple media SSRCs per stream. - // NOTE: We have two SSRCs per stream when RTX is enabled. - if (!IsOneSsrcStream(sp)) { - LOG(LS_ERROR) << "WebRtcVideoMediaChannel supports one primary SSRC per" - << " stream and one FID SSRC per primary SSRC."; - return false; - } - - // Create a new channel for receiving video data. - // In order to get the bandwidth estimation work fine for - // receive only channels, we connect all receiving channels - // to our master send channel. int channel_id = -1; - if (!CreateChannel(sp.first_ssrc(), MD_RECV, &channel_id)) { - return false; + RecvChannelMap::iterator channel_iterator = + recv_channels_.find(sp.first_ssrc()); + if (channel_iterator == recv_channels_.end() && + first_receive_ssrc_ != sp.first_ssrc()) { + // TODO(perkj): Implement recv media from multiple media SSRCs per stream. + // NOTE: We have two SSRCs per stream when RTX is enabled. + if (!IsOneSsrcStream(sp)) { + LOG(LS_ERROR) << "WebRtcVideoMediaChannel supports one primary SSRC per" + << " stream and one FID SSRC per primary SSRC."; + return false; + } + + // Create a new channel for receiving video data. + // In order to get the bandwidth estimation work fine for + // receive only channels, we connect all receiving channels + // to our master send channel. + if (!CreateChannel(sp.first_ssrc(), MD_RECV, &channel_id)) { + return false; + } + } else { + // Already exists. + if (first_receive_ssrc_ == sp.first_ssrc()) { + return false; + } + // Early receive added channel. + channel_id = (*channel_iterator).second->channel_id(); } + channel_iterator = recv_channels_.find(sp.first_ssrc()); - // Set the corresponding RTX SSRC. - uint32 rtx_ssrc; - bool has_rtx = sp.GetFidSsrc(sp.first_ssrc(), &rtx_ssrc); - if (has_rtx && engine()->vie()->rtp()->SetRemoteSSRCType( - channel_id, webrtc::kViEStreamTypeRtx, rtx_ssrc) != 0) { - LOG_RTCERR3(SetRemoteSSRCType, channel_id, webrtc::kViEStreamTypeRtx, - rtx_ssrc); + if (!MaybeSetRtxSsrc(sp, channel_id)) { return false; } @@ -1957,9 +2100,34 @@ bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) { return true; } +bool WebRtcVideoMediaChannel::MaybeSetRtxSsrc(const StreamParams& sp, + int channel_id) { + uint32 rtx_ssrc; + bool has_rtx = sp.GetFidSsrc(sp.first_ssrc(), &rtx_ssrc); + if (has_rtx) { + LOG(LS_INFO) << "Setting rtx ssrc " << rtx_ssrc << " for stream " + << sp.first_ssrc(); + if (engine()->vie()->rtp()->SetRemoteSSRCType( + channel_id, webrtc::kViEStreamTypeRtx, rtx_ssrc) != 0) { + LOG_RTCERR3(SetRemoteSSRCType, channel_id, webrtc::kViEStreamTypeRtx, + rtx_ssrc); + return false; + } + rtx_to_primary_ssrc_[rtx_ssrc] = sp.first_ssrc(); + } + return true; +} + bool WebRtcVideoMediaChannel::RemoveRecvStream(uint32 ssrc) { - RecvChannelMap::iterator it = recv_channels_.find(ssrc); + if (ssrc == 0) { + LOG(LS_ERROR) << "RemoveRecvStream with 0 ssrc is not supported."; + return false; + } + return RemoveRecvStreamInternal(ssrc); +} +bool WebRtcVideoMediaChannel::RemoveRecvStreamInternal(uint32 ssrc) { + RecvChannelMap::iterator it = recv_channels_.find(ssrc); if (it == recv_channels_.end()) { // TODO(perkj): Remove this once BWE works properly across different send // and receive channels. @@ -1979,6 +2147,17 @@ bool WebRtcVideoMediaChannel::RemoveRecvStream(uint32 ssrc) { return false; } WebRtcVideoChannelRecvInfo* info = it->second; + + // Remove any RTX SSRC mappings to this stream. + SsrcMap::iterator rtx_it = rtx_to_primary_ssrc_.begin(); + while (rtx_it != rtx_to_primary_ssrc_.end()) { + if (rtx_it->second == ssrc) { + rtx_to_primary_ssrc_.erase(rtx_it++); + } else { + ++rtx_it; + } + } + int channel_id = info->channel_id(); if (engine()->vie()->render()->RemoveRenderer(channel_id) != 0) { LOG_RTCERR1(RemoveRenderer, channel_id); @@ -2175,7 +2354,7 @@ bool WebRtcVideoMediaChannel::DeleteSendChannel(uint32 ssrc_key) { } WebRtcVideoChannelSendInfo* send_channel = send_channels_[ssrc_key]; MaybeDisconnectCapturer(send_channel->video_capturer()); - send_channel->set_video_capturer(NULL); + send_channel->set_video_capturer(NULL, engine()->vie()); int channel_id = send_channel->channel_id(); int capture_id = send_channel->capture_id(); @@ -2215,7 +2394,7 @@ bool WebRtcVideoMediaChannel::RemoveCapturer(uint32 ssrc) { return false; } MaybeDisconnectCapturer(capturer); - send_channel->set_video_capturer(NULL); + send_channel->set_video_capturer(NULL, engine()->vie()); const int64 timestamp = send_channel->local_stream_info()->time_stamp(); if (send_codec_) { QueueBlackFrame(ssrc, timestamp, send_codec_->maxFramerate); @@ -2244,7 +2423,8 @@ bool WebRtcVideoMediaChannel::SetRenderer(uint32 ssrc, return true; } -bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { +bool WebRtcVideoMediaChannel::GetStats(const StatsOptions& options, + VideoMediaInfo* info) { // Get sender statistics and build VideoSenderInfo. unsigned int total_bitrate_sent = 0; unsigned int video_bitrate_sent = 0; @@ -2288,22 +2468,58 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { sinfo.packets_cached = -1; sinfo.packets_lost = -1; sinfo.fraction_lost = -1; - sinfo.firs_rcvd = -1; - sinfo.nacks_rcvd = -1; sinfo.rtt_ms = -1; - sinfo.frame_width = static_cast<int>(channel_stream_info->width()); - sinfo.frame_height = static_cast<int>(channel_stream_info->height()); + + VideoCapturer* video_capturer = send_channel->video_capturer(); + if (video_capturer) { + VideoFormat last_captured_frame_format; + video_capturer->GetStats(&sinfo.adapt_frame_drops, + &sinfo.effects_frame_drops, + &sinfo.capturer_frame_time, + &last_captured_frame_format); + sinfo.input_frame_width = last_captured_frame_format.width; + sinfo.input_frame_height = last_captured_frame_format.height; + } else { + sinfo.input_frame_width = 0; + sinfo.input_frame_height = 0; + } + + webrtc::VideoCodec vie_codec; + if (!video_capturer || video_capturer->IsMuted()) { + sinfo.send_frame_width = 0; + sinfo.send_frame_height = 0; + } else if (engine()->vie()->codec()->GetSendCodec(channel_id, + vie_codec) == 0) { + sinfo.send_frame_width = vie_codec.width; + sinfo.send_frame_height = vie_codec.height; + } else { + sinfo.send_frame_width = -1; + sinfo.send_frame_height = -1; + LOG_RTCERR1(GetSendCodec, channel_id); + } sinfo.framerate_input = channel_stream_info->framerate(); sinfo.framerate_sent = send_channel->encoder_observer()->framerate(); sinfo.nominal_bitrate = send_channel->encoder_observer()->bitrate(); - sinfo.preferred_bitrate = send_max_bitrate_; + if (send_codec_) { + sinfo.preferred_bitrate = GetBitrate( + send_codec_->maxBitrate, kMaxVideoBitrate); + } sinfo.adapt_reason = send_channel->CurrentAdaptReason(); + +#ifdef USE_WEBRTC_DEV_BRANCH + webrtc::CpuOveruseMetrics metrics; + engine()->vie()->base()->GetCpuOveruseMetrics(channel_id, &metrics); + sinfo.capture_jitter_ms = metrics.capture_jitter_ms; + sinfo.avg_encode_ms = metrics.avg_encode_time_ms; + sinfo.encode_usage_percent = metrics.encode_usage_percent; + sinfo.encode_rsd = metrics.encode_rsd; + sinfo.capture_queue_delay_ms_per_s = metrics.capture_queue_delay_ms_per_s; +#else sinfo.capture_jitter_ms = -1; sinfo.avg_encode_ms = -1; sinfo.encode_usage_percent = -1; sinfo.capture_queue_delay_ms_per_s = -1; -#ifdef USE_WEBRTC_DEV_BRANCH int capture_jitter_ms = 0; int avg_encode_time_ms = 0; int encode_usage_percent = 0; @@ -2321,6 +2537,20 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { } #endif + webrtc::RtcpPacketTypeCounter rtcp_sent; + webrtc::RtcpPacketTypeCounter rtcp_received; + if (engine()->vie()->rtp()->GetRtcpPacketTypeCounters( + channel_id, &rtcp_sent, &rtcp_received) == 0) { + sinfo.firs_rcvd = rtcp_received.fir_packets; + sinfo.plis_rcvd = rtcp_received.pli_packets; + sinfo.nacks_rcvd = rtcp_received.nack_packets; + } else { + sinfo.firs_rcvd = -1; + sinfo.plis_rcvd = -1; + sinfo.nacks_rcvd = -1; + LOG_RTCERR1(GetRtcpPacketTypeCounters, channel_id); + } + // Get received RTCP statistics for the sender (reported by the remote // client in a RTCP packet), if available. // It's not a fatal error if we can't, since RTCP may not have arrived @@ -2355,13 +2585,6 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { LOG_RTCERR1(GetBandwidthUsage, channel_id); } - unsigned int estimated_stream_send_bandwidth = 0; - if (engine_->vie()->rtp()->GetEstimatedSendBandwidth( - channel_id, &estimated_stream_send_bandwidth) == 0) { - estimated_send_bandwidth += estimated_stream_send_bandwidth; - } else { - LOG_RTCERR1(GetEstimatedSendBandwidth, channel_id); - } unsigned int target_enc_stream_bitrate = 0; if (engine_->vie()->codec()->GetCodecTargetBitrate( channel_id, &target_enc_stream_bitrate) == 0) { @@ -2370,21 +2593,27 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { LOG_RTCERR1(GetCodecTargetBitrate, channel_id); } } + if (!send_channels_.empty()) { + // GetEstimatedSendBandwidth returns the estimated bandwidth for all video + // engine channels in a channel group. Any valid channel id will do as it + // is only used to access the right group of channels. + const int channel_id = send_channels_.begin()->second->channel_id(); + // Get the send bandwidth available for this MediaChannel. + if (engine_->vie()->rtp()->GetEstimatedSendBandwidth( + channel_id, &estimated_send_bandwidth) != 0) { + LOG_RTCERR1(GetEstimatedSendBandwidth, channel_id); + } + } } else { LOG(LS_WARNING) << "GetStats: sender information not ready."; } // Get the SSRC and stats for each receiver, based on our own calculations. - unsigned int estimated_recv_bandwidth = 0; for (RecvChannelMap::const_iterator it = recv_channels_.begin(); it != recv_channels_.end(); ++it) { - // Don't report receive statistics from the default channel if we have - // specified receive channels. - if (it->first == 0 && recv_channels_.size() > 1) - continue; WebRtcVideoChannelRecvInfo* channel = it->second; - unsigned int ssrc; + unsigned int ssrc = 0; // Get receiver statistics and build VideoReceiverInfo, if we have data. // Skip the default channel (ssrc == 0). if (engine_->vie()->rtp()->GetRemoteSSRC( @@ -2406,14 +2635,29 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { rinfo.packets_lost = -1; rinfo.packets_concealed = -1; rinfo.fraction_lost = -1; // from SentRTCP - rinfo.nacks_sent = -1; rinfo.frame_width = channel->render_adapter()->width(); rinfo.frame_height = channel->render_adapter()->height(); int fps = channel->render_adapter()->framerate(); rinfo.framerate_decoded = fps; rinfo.framerate_output = fps; + rinfo.capture_start_ntp_time_ms = + channel->render_adapter()->capture_start_ntp_time_ms(); channel->decoder_observer()->ExportTo(&rinfo); + webrtc::RtcpPacketTypeCounter rtcp_sent; + webrtc::RtcpPacketTypeCounter rtcp_received; + if (engine()->vie()->rtp()->GetRtcpPacketTypeCounters( + channel->channel_id(), &rtcp_sent, &rtcp_received) == 0) { + rinfo.firs_sent = rtcp_sent.fir_packets; + rinfo.plis_sent = rtcp_sent.pli_packets; + rinfo.nacks_sent = rtcp_sent.nack_packets; + } else { + rinfo.firs_sent = -1; + rinfo.plis_sent = -1; + rinfo.nacks_sent = -1; + LOG_RTCERR1(GetRtcpPacketTypeCounters, channel->channel_id()); + } + // Get our locally created statistics of the received RTP stream. webrtc::RtcpStatistics incoming_stream_rtcp_stats; int incoming_stream_rtt_ms; @@ -2427,13 +2671,17 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { incoming_stream_rtcp_stats.fraction_lost) / (1 << 8); } info->receivers.push_back(rinfo); - - unsigned int estimated_recv_stream_bandwidth = 0; + } + unsigned int estimated_recv_bandwidth = 0; + if (!recv_channels_.empty()) { + // GetEstimatedReceiveBandwidth returns the estimated bandwidth for all + // video engine channels in a channel group. Any valid channel id will do as + // it is only used to access the right group of channels. + const int channel_id = recv_channels_.begin()->second->channel_id(); + // Gets the estimated receive bandwidth for the MediaChannel. if (engine_->vie()->rtp()->GetEstimatedReceiveBandwidth( - channel->channel_id(), &estimated_recv_stream_bandwidth) == 0) { - estimated_recv_bandwidth += estimated_recv_stream_bandwidth; - } else { - LOG_RTCERR1(GetEstimatedReceiveBandwidth, channel->channel_id()); + channel_id, &estimated_recv_bandwidth) != 0) { + LOG_RTCERR1(GetEstimatedReceiveBandwidth, channel_id); } } @@ -2441,6 +2689,26 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { // TODO(zhurunz): Add real unittest for this. BandwidthEstimationInfo bwe; + // TODO(jiayl): remove the condition when the necessary changes are available + // outside the dev branch. + if (options.include_received_propagation_stats) { + webrtc::ReceiveBandwidthEstimatorStats additional_stats; + // Only call for the default channel because the returned stats are + // collected for all the channels using the same estimator. + if (engine_->vie()->rtp()->GetReceiveBandwidthEstimatorStats( + recv_channels_[0]->channel_id(), &additional_stats) == 0) { + bwe.total_received_propagation_delta_ms = + additional_stats.total_propagation_time_delta_ms; + bwe.recent_received_propagation_delta_ms.swap( + additional_stats.recent_propagation_time_delta_ms); + bwe.recent_received_packet_group_arrival_time_ms.swap( + additional_stats.recent_arrival_time_ms); + } + } + + engine_->vie()->rtp()->GetPacerQueuingDelayMs( + recv_channels_[0]->channel_id(), &bwe.bucket_delay); + // Calculations done above per send/receive stream. bwe.actual_enc_bitrate = video_bitrate_sent; bwe.transmit_bitrate = total_bitrate_sent; @@ -2467,7 +2735,7 @@ bool WebRtcVideoMediaChannel::SetCapturer(uint32 ssrc, VideoCapturer* old_capturer = send_channel->video_capturer(); MaybeDisconnectCapturer(old_capturer); - send_channel->set_video_capturer(capturer); + send_channel->set_video_capturer(capturer, engine()->vie()); MaybeConnectCapturer(capturer); if (!capturer->IsScreencast() && ratio_w_ != 0 && ratio_h_ != 0) { capturer->UpdateAspectRatio(ratio_w_, ratio_h_); @@ -2493,20 +2761,24 @@ void WebRtcVideoMediaChannel::OnPacketReceived( uint32 ssrc = 0; if (!GetRtpSsrc(packet->data(), packet->length(), &ssrc)) return; - int which_channel = GetRecvChannelNum(ssrc); - if (which_channel == -1) { - which_channel = video_channel(); + int processing_channel = GetRecvChannelNum(ssrc); + if (processing_channel == -1) { + // Allocate an unsignalled recv channel for processing in conference mode. + if (!InConferenceMode()) { + // If we can't find or allocate one, use the default. + processing_channel = video_channel(); + } else if (!CreateUnsignalledRecvChannel(ssrc, &processing_channel)) { + // If we can't create an unsignalled recv channel, drop the packet in + // conference mode. + return; + } } engine()->vie()->network()->ReceivedRTPPacket( - which_channel, + processing_channel, packet->data(), -#ifdef USE_WEBRTC_DEV_BRANCH static_cast<int>(packet->length()), webrtc::PacketTime(packet_time.timestamp, packet_time.not_before)); -#else - static_cast<int>(packet->length())); -#endif } void WebRtcVideoMediaChannel::OnRtcpReceived( @@ -2570,12 +2842,11 @@ bool WebRtcVideoMediaChannel::SetRecvRtpHeaderExtensions( if (receive_extensions_ == extensions) { return true; } - receive_extensions_ = extensions; const RtpHeaderExtension* offset_extension = FindHeaderExtension(extensions, kRtpTimestampOffsetHeaderExtension); const RtpHeaderExtension* send_time_extension = - FindHeaderExtension(extensions, kRtpAbsoluteSendTimeHeaderExtension); + FindHeaderExtension(extensions, kRtpAbsoluteSenderTimeHeaderExtension); // Loop through all receive channels and enable/disable the extensions. for (RecvChannelMap::iterator channel_it = recv_channels_.begin(); @@ -2592,17 +2863,21 @@ bool WebRtcVideoMediaChannel::SetRecvRtpHeaderExtensions( return false; } } + + receive_extensions_ = extensions; return true; } bool WebRtcVideoMediaChannel::SetSendRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions) { - send_extensions_ = extensions; + if (send_extensions_ == extensions) { + return true; + } const RtpHeaderExtension* offset_extension = FindHeaderExtension(extensions, kRtpTimestampOffsetHeaderExtension); const RtpHeaderExtension* send_time_extension = - FindHeaderExtension(extensions, kRtpAbsoluteSendTimeHeaderExtension); + FindHeaderExtension(extensions, kRtpAbsoluteSenderTimeHeaderExtension); // Loop through all send channels and enable/disable the extensions. for (SendChannelMap::iterator channel_it = send_channels_.begin(); @@ -2619,44 +2894,64 @@ bool WebRtcVideoMediaChannel::SetSendRtpHeaderExtensions( return false; } } + + if (send_time_extension) { + // For video RTP packets, we would like to update AbsoluteSendTimeHeader + // Extension closer to the network, @ socket level before sending. + // Pushing the extension id to socket layer. + MediaChannel::SetOption(NetworkInterface::ST_RTP, + talk_base::Socket::OPT_RTP_SENDTIME_EXTN_ID, + send_time_extension->id); + } + + send_extensions_ = extensions; return true; } -bool WebRtcVideoMediaChannel::SetSendBandwidth(bool autobw, int bps) { - LOG(LS_INFO) << "WebRtcVideoMediaChanne::SetSendBandwidth"; +int WebRtcVideoMediaChannel::GetRtpSendTimeExtnId() const { + const RtpHeaderExtension* send_time_extension = FindHeaderExtension( + send_extensions_, kRtpAbsoluteSenderTimeHeaderExtension); + if (send_time_extension) { + return send_time_extension->id; + } + return -1; +} - if (InConferenceMode()) { - LOG(LS_INFO) << "Conference mode ignores SetSendBandWidth"; +bool WebRtcVideoMediaChannel::SetStartSendBandwidth(int bps) { + LOG(LS_INFO) << "WebRtcVideoMediaChannel::SetStartSendBandwidth"; + + if (!send_codec_) { + LOG(LS_INFO) << "The send codec has not been set up yet"; return true; } + // On success, SetSendCodec() will reset |send_start_bitrate_| to |bps/1000|, + // by calling MaybeChangeBitrates. That method will also clamp the + // start bitrate between min and max, consistent with the override behavior + // in SetMaxSendBandwidth. + webrtc::VideoCodec new_codec = *send_codec_; + if (BitrateIsSet(bps)) { + new_codec.startBitrate = bps / 1000; + } + return SetSendCodec(new_codec); +} + +bool WebRtcVideoMediaChannel::SetMaxSendBandwidth(int bps) { + LOG(LS_INFO) << "WebRtcVideoMediaChannel::SetMaxSendBandwidth"; + if (!send_codec_) { LOG(LS_INFO) << "The send codec has not been set up yet"; return true; } - int min_bitrate; - int start_bitrate; - int max_bitrate; - if (autobw) { - // Use the default values for min bitrate. - min_bitrate = send_min_bitrate_; - // Use the default value or the bps for the max - max_bitrate = (bps <= 0) ? send_max_bitrate_ : (bps / 1000); - // Maximum start bitrate can be kStartVideoBitrate. - start_bitrate = talk_base::_min(kStartVideoBitrate, max_bitrate); - } else { - // Use the default start or the bps as the target bitrate. - int target_bitrate = (bps <= 0) ? kStartVideoBitrate : (bps / 1000); - min_bitrate = target_bitrate; - start_bitrate = target_bitrate; - max_bitrate = target_bitrate; + webrtc::VideoCodec new_codec = *send_codec_; + if (BitrateIsSet(bps)) { + new_codec.maxBitrate = bps / 1000; } - - if (!SetSendCodec(*send_codec_, min_bitrate, start_bitrate, max_bitrate)) { + if (!SetSendCodec(new_codec)) { return false; } - LogSendCodecChange("SetSendBandwidth()"); + LogSendCodecChange("SetMaxSendBandwidth()"); return true; } @@ -2678,9 +2973,6 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { bool buffer_latency_changed = options.buffered_mode_latency.IsSet() && (options_.buffered_mode_latency != options.buffered_mode_latency); - bool cpu_overuse_detection_changed = options.cpu_overuse_detection.IsSet() && - (options_.cpu_overuse_detection != options.cpu_overuse_detection); - bool dscp_option_changed = (options_.dscp != options.dscp); bool suspend_below_min_bitrate_changed = @@ -2694,6 +2986,17 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { conference_mode_turned_off = true; } + bool improved_wifi_bwe_changed = + options.use_improved_wifi_bandwidth_estimator.IsSet() && + options_.use_improved_wifi_bandwidth_estimator != + options.use_improved_wifi_bandwidth_estimator; + +#ifdef USE_WEBRTC_DEV_BRANCH + bool payload_padding_changed = options.use_payload_padding.IsSet() && + options_.use_payload_padding != options.use_payload_padding; +#endif + + // Save the options, to be interpreted where appropriate. // Use options_.SetAll() instead of assignment so that unset value in options // will not overwrite the previous option value. @@ -2706,48 +3009,52 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { send_channel->ApplyCpuOptions(options_); } - // Adjust send codec bitrate if needed. - int conf_max_bitrate = kDefaultConferenceModeMaxVideoBitrate; + if (send_codec_) { + bool reset_send_codec_needed = denoiser_changed; + webrtc::VideoCodec new_codec = *send_codec_; + + // TODO(pthatcher): Remove this. We don't need 4 ways to set bitrates. + bool lower_min_bitrate; + if (options.lower_min_bitrate.Get(&lower_min_bitrate)) { + new_codec.minBitrate = kLowerMinBitrate; + reset_send_codec_needed = true; + } - // Save altered min_bitrate level and apply if necessary. - bool adjusted_min_bitrate = false; - if (options.lower_min_bitrate.IsSet()) { - bool lower; - options.lower_min_bitrate.Get(&lower); + if (conference_mode_turned_off) { + // This is a special case for turning conference mode off. + // Max bitrate should go back to the default maximum value instead + // of the current maximum. + new_codec.maxBitrate = kAutoBandwidth; + reset_send_codec_needed = true; + } - int new_send_min_bitrate = lower ? kLowerMinBitrate : kMinVideoBitrate; - adjusted_min_bitrate = (new_send_min_bitrate != send_min_bitrate_); - send_min_bitrate_ = new_send_min_bitrate; - } + // TODO(pthatcher): Remove this. We don't need 4 ways to set bitrates. + int new_start_bitrate; + if (options.video_start_bitrate.Get(&new_start_bitrate)) { + new_codec.startBitrate = new_start_bitrate; + reset_send_codec_needed = true; + } - int expected_bitrate = send_max_bitrate_; - if (InConferenceMode()) { - expected_bitrate = conf_max_bitrate; - } else if (conference_mode_turned_off) { - // This is a special case for turning conference mode off. - // Max bitrate should go back to the default maximum value instead - // of the current maximum. - expected_bitrate = kMaxVideoBitrate; - } - - if (send_codec_ && - (send_max_bitrate_ != expected_bitrate || denoiser_changed || - adjusted_min_bitrate)) { - // On success, SetSendCodec() will reset send_max_bitrate_ to - // expected_bitrate. - if (!SetSendCodec(*send_codec_, - send_min_bitrate_, - send_start_bitrate_, - expected_bitrate)) { - return false; + + LOG(LS_INFO) << "Reset send codec needed is enabled? " + << reset_send_codec_needed; + if (reset_send_codec_needed) { + if (!SetSendCodec(new_codec)) { + return false; + } + LogSendCodecChange("SetOptions()"); } - LogSendCodecChange("SetOptions()"); } + if (leaky_bucket_changed) { bool enable_leaky_bucket = - options_.video_leaky_bucket.GetWithDefaultIfUnset(false); + options_.video_leaky_bucket.GetWithDefaultIfUnset(true); + LOG(LS_INFO) << "Leaky bucket is enabled? " << enable_leaky_bucket; for (SendChannelMap::iterator it = send_channels_.begin(); it != send_channels_.end(); ++it) { + // TODO(holmer): This API will be removed as we move to the new + // webrtc::Call API. We should clean up this experiment when that is + // happening. if (engine()->vie()->rtp()->SetTransmissionSmoothingStatus( it->second->channel_id(), enable_leaky_bucket) != 0) { LOG_RTCERR2(SetTransmissionSmoothingStatus, it->second->channel_id(), @@ -2759,6 +3066,7 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { int buffer_latency = options_.buffered_mode_latency.GetWithDefaultIfUnset( cricket::kBufferedModeDisabled); + LOG(LS_INFO) << "Buffer latency is " << buffer_latency; for (SendChannelMap::iterator it = send_channels_.begin(); it != send_channels_.end(); ++it) { if (engine()->vie()->rtp()->SetSenderBufferingMode( @@ -2776,25 +3084,18 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { } } } - if (cpu_overuse_detection_changed) { - bool cpu_overuse_detection = - options_.cpu_overuse_detection.GetWithDefaultIfUnset(false); - for (SendChannelMap::iterator iter = send_channels_.begin(); - iter != send_channels_.end(); ++iter) { - WebRtcVideoChannelSendInfo* send_channel = iter->second; - send_channel->SetCpuOveruseDetection(cpu_overuse_detection); - } - } if (dscp_option_changed) { talk_base::DiffServCodePoint dscp = talk_base::DSCP_DEFAULT; - if (options.dscp.GetWithDefaultIfUnset(false)) + if (options_.dscp.GetWithDefaultIfUnset(false)) dscp = kVideoDscpValue; + LOG(LS_INFO) << "DSCP is " << dscp; if (MediaChannel::SetDscp(dscp) != 0) { LOG(LS_WARNING) << "Failed to set DSCP settings for video channel"; } } if (suspend_below_min_bitrate_changed) { if (options_.suspend_below_min_bitrate.GetWithDefaultIfUnset(false)) { + LOG(LS_INFO) << "Suspend below min bitrate enabled."; for (SendChannelMap::iterator it = send_channels_.begin(); it != send_channels_.end(); ++it) { engine()->vie()->codec()->SuspendBelowMinBitrate( @@ -2804,6 +3105,39 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { LOG(LS_WARNING) << "Cannot disable video suspension once it is enabled"; } } + if (improved_wifi_bwe_changed) { + LOG(LS_INFO) << "Improved WIFI BWE called."; + webrtc::Config config; + config.Set(new webrtc::AimdRemoteRateControl( + options_.use_improved_wifi_bandwidth_estimator + .GetWithDefaultIfUnset(false))); + for (SendChannelMap::iterator it = send_channels_.begin(); + it != send_channels_.end(); ++it) { + engine()->vie()->network()->SetBandwidthEstimationConfig( + it->second->channel_id(), config); + } + } +#ifdef USE_WEBRTC_DEV_BRANCH + if (payload_padding_changed) { + LOG(LS_INFO) << "Payload-based padding called."; + for (SendChannelMap::iterator it = send_channels_.begin(); + it != send_channels_.end(); ++it) { + engine()->vie()->rtp()->SetPadWithRedundantPayloads( + it->second->channel_id(), + options_.use_payload_padding.GetWithDefaultIfUnset(false)); + } + } +#endif + webrtc::CpuOveruseOptions overuse_options; + if (GetCpuOveruseOptions(options_, &overuse_options)) { + for (SendChannelMap::iterator it = send_channels_.begin(); + it != send_channels_.end(); ++it) { + if (engine()->vie()->base()->SetCpuOveruseOptions( + it->second->channel_id(), overuse_options) != 0) { + LOG_RTCERR1(SetCpuOveruseOptions, it->second->channel_id()); + } + } + } return true; } @@ -2858,6 +3192,16 @@ bool WebRtcVideoMediaChannel::GetRenderer(uint32 ssrc, return true; } +bool WebRtcVideoMediaChannel::GetVideoAdapter( + uint32 ssrc, CoordinatedVideoAdapter** video_adapter) { + SendChannelMap::iterator it = send_channels_.find(ssrc); + if (it == send_channels_.end()) { + return false; + } + *video_adapter = it->second->video_adapter(); + return true; +} + void WebRtcVideoMediaChannel::SendFrame(VideoCapturer* capturer, const VideoFrame* frame) { // If the |capturer| is registered to any send channel, then send the frame @@ -2939,6 +3283,8 @@ bool WebRtcVideoMediaChannel::SendFrame( // TODO(justinlin): Reenable after Windows issues with clock drift are fixed. // Currently reverted to old behavior of discarding capture timestamp. #if 0 + static const int kTimestampDeltaInSecondsForWarning = 2; + // If the frame timestamp is 0, we will use the deliver time. const int64 frame_timestamp = frame->GetTimeStamp(); if (frame_timestamp != 0) { @@ -3007,6 +3353,22 @@ bool WebRtcVideoMediaChannel::CreateChannel(uint32 ssrc_key, return true; } +bool WebRtcVideoMediaChannel::CreateUnsignalledRecvChannel( + uint32 ssrc_key, int* out_channel_id) { + int unsignalled_recv_channel_limit = + options_.unsignalled_recv_stream_limit.GetWithDefaultIfUnset( + kNumDefaultUnsignalledVideoRecvStreams); + if (num_unsignalled_recv_channels_ >= unsignalled_recv_channel_limit) { + return false; + } + if (!CreateChannel(ssrc_key, MD_RECV, out_channel_id)) { + return false; + } + // TODO(tvsriram): Support dynamic sizing of unsignalled recv channels. + num_unsignalled_recv_channels_++; + return true; +} + bool WebRtcVideoMediaChannel::ConfigureChannel(int channel_id, MediaDirection direction, uint32 ssrc_key) { @@ -3055,6 +3417,13 @@ bool WebRtcVideoMediaChannel::ConfigureChannel(int channel_id, } } + // Start receiving for both receive and send channels so that we get incoming + // RTP (if receiving) as well as RTCP feedback (if sending). + if (engine()->vie()->base()->StartReceive(channel_id) != 0) { + LOG_RTCERR1(StartReceive, channel_id); + return false; + } + return true; } @@ -3104,10 +3473,9 @@ bool WebRtcVideoMediaChannel::ConfigureReceiving(int channel_id, channel_id, receive_extensions_, kRtpTimestampOffsetHeaderExtension)) { return false; } - if (!SetHeaderExtension( &webrtc::ViERTP_RTCP::SetReceiveAbsoluteSendTimeStatus, channel_id, - receive_extensions_, kRtpAbsoluteSendTimeHeaderExtension)) { + receive_extensions_, kRtpAbsoluteSenderTimeHeaderExtension)) { return false; } @@ -3196,17 +3564,16 @@ bool WebRtcVideoMediaChannel::ConfigureSending(int channel_id, new WebRtcVideoChannelSendInfo(channel_id, vie_capture, external_capture, engine()->cpu_monitor())); - if (engine()->vie()->base()->RegisterCpuOveruseObserver( - channel_id, send_channel->overuse_observer())) { - LOG_RTCERR1(RegisterCpuOveruseObserver, channel_id); - return false; - } send_channel->ApplyCpuOptions(options_); send_channel->SignalCpuAdaptationUnable.connect(this, &WebRtcVideoMediaChannel::OnCpuAdaptationUnable); - if (options_.cpu_overuse_detection.GetWithDefaultIfUnset(false)) { - send_channel->SetCpuOveruseDetection(true); + webrtc::CpuOveruseOptions overuse_options; + if (GetCpuOveruseOptions(options_, &overuse_options)) { + if (engine()->vie()->base()->SetCpuOveruseOptions(channel_id, + overuse_options) != 0) { + LOG_RTCERR1(SetCpuOveruseOptions, channel_id); + } } // Register encoder observer for outgoing framerate and bitrate. @@ -3222,11 +3589,11 @@ bool WebRtcVideoMediaChannel::ConfigureSending(int channel_id, } if (!SetHeaderExtension(&webrtc::ViERTP_RTCP::SetSendAbsoluteSendTimeStatus, - channel_id, send_extensions_, kRtpAbsoluteSendTimeHeaderExtension)) { + channel_id, send_extensions_, kRtpAbsoluteSenderTimeHeaderExtension)) { return false; } - if (options_.video_leaky_bucket.GetWithDefaultIfUnset(false)) { + if (options_.video_leaky_bucket.GetWithDefaultIfUnset(true)) { if (engine()->vie()->rtp()->SetTransmissionSmoothingStatus(channel_id, true) != 0) { LOG_RTCERR2(SetTransmissionSmoothingStatus, channel_id, true); @@ -3243,6 +3610,11 @@ bool WebRtcVideoMediaChannel::ConfigureSending(int channel_id, LOG_RTCERR2(SetSenderBufferingMode, channel_id, buffer_latency); } } + + if (options_.suspend_below_min_bitrate.GetWithDefaultIfUnset(false)) { + engine()->vie()->codec()->SuspendBelowMinBitrate(channel_id); + } + // The remb status direction correspond to the RTP stream (and not the RTCP // stream). I.e. if send remb is enabled it means it is receiving remote // rembs and should use them to estimate bandwidth. Receive remb mean that @@ -3291,32 +3663,24 @@ bool WebRtcVideoMediaChannel::SetNackFec(int channel_id, return true; } -bool WebRtcVideoMediaChannel::SetSendCodec(const webrtc::VideoCodec& codec, - int min_bitrate, - int start_bitrate, - int max_bitrate) { +bool WebRtcVideoMediaChannel::SetSendCodec(const webrtc::VideoCodec& codec) { bool ret_val = true; for (SendChannelMap::iterator iter = send_channels_.begin(); iter != send_channels_.end(); ++iter) { WebRtcVideoChannelSendInfo* send_channel = iter->second; - ret_val = SetSendCodec(send_channel, codec, min_bitrate, start_bitrate, - max_bitrate) && ret_val; + ret_val = SetSendCodec(send_channel, codec) && ret_val; } if (ret_val) { // All SetSendCodec calls were successful. Update the global state // accordingly. send_codec_.reset(new webrtc::VideoCodec(codec)); - send_min_bitrate_ = min_bitrate; - send_start_bitrate_ = start_bitrate; - send_max_bitrate_ = max_bitrate; } else { // At least one SetSendCodec call failed, rollback. for (SendChannelMap::iterator iter = send_channels_.begin(); iter != send_channels_.end(); ++iter) { WebRtcVideoChannelSendInfo* send_channel = iter->second; if (send_codec_) { - SetSendCodec(send_channel, *send_codec_.get(), send_min_bitrate_, - send_start_bitrate_, send_max_bitrate_); + SetSendCodec(send_channel, *send_codec_); } } } @@ -3325,19 +3689,14 @@ bool WebRtcVideoMediaChannel::SetSendCodec(const webrtc::VideoCodec& codec, bool WebRtcVideoMediaChannel::SetSendCodec( WebRtcVideoChannelSendInfo* send_channel, - const webrtc::VideoCodec& codec, - int min_bitrate, - int start_bitrate, - int max_bitrate) { + const webrtc::VideoCodec& codec) { if (!send_channel) { return false; } + const int channel_id = send_channel->channel_id(); // Make a copy of the codec webrtc::VideoCodec target_codec = codec; - target_codec.startBitrate = start_bitrate; - target_codec.minBitrate = min_bitrate; - target_codec.maxBitrate = max_bitrate; // Set the default number of temporal layers for VP8. if (webrtc::kVideoCodecVP8 == codec.codecType) { @@ -3348,7 +3707,7 @@ bool WebRtcVideoMediaChannel::SetSendCodec( target_codec.codecSpecific.VP8.resilience = webrtc::kResilienceOff; bool enable_denoising = - options_.video_noise_reduction.GetWithDefaultIfUnset(false); + options_.video_noise_reduction.GetWithDefaultIfUnset(true); target_codec.codecSpecific.VP8.denoisingOn = enable_denoising; } @@ -3377,7 +3736,16 @@ bool WebRtcVideoMediaChannel::SetSendCodec( LOG(LS_INFO) << "0x0 resolution selected. Captured frames will be dropped " << "for ssrc: " << ssrc << "."; } else { - MaybeChangeStartBitrate(channel_id, &target_codec); + MaybeChangeBitrates(channel_id, &target_codec); + webrtc::VideoCodec current_codec; + if (!engine()->vie()->codec()->GetSendCodec(channel_id, current_codec)) { + // Compare against existing configured send codec. + if (current_codec == target_codec) { + // Codec is already configured on channel. no need to apply. + return true; + } + } + if (0 != engine()->vie()->codec()->SetSendCodec(channel_id, target_codec)) { LOG_RTCERR2(SetSendCodec, channel_id, target_codec.plName); return false; @@ -3477,6 +3845,13 @@ bool WebRtcVideoMediaChannel::SetReceiveCodecs( int red_type = -1; int fec_type = -1; int channel_id = info->channel_id(); + // Build a map from payload types to video codecs so that we easily can find + // out if associated payload types are referring to valid codecs. + std::map<int, webrtc::VideoCodec*> pt_to_codec; + for (std::vector<webrtc::VideoCodec>::iterator it = receive_codecs_.begin(); + it != receive_codecs_.end(); ++it) { + pt_to_codec[it->plType] = &(*it); + } for (std::vector<webrtc::VideoCodec>::iterator it = receive_codecs_.begin(); it != receive_codecs_.end(); ++it) { if (it->codecType == webrtc::kVideoCodecRED) { @@ -3484,6 +3859,32 @@ bool WebRtcVideoMediaChannel::SetReceiveCodecs( } else if (it->codecType == webrtc::kVideoCodecULPFEC) { fec_type = it->plType; } + // If this is an RTX codec we have to verify that it is associated with + // a valid video codec which we have RTX support for. + if (_stricmp(it->plName, kRtxCodecName) == 0) { + std::map<int, int>::iterator apt_it = associated_payload_types_.find( + it->plType); + bool valid_apt = false; + if (apt_it != associated_payload_types_.end()) { + std::map<int, webrtc::VideoCodec*>::iterator codec_it = + pt_to_codec.find(apt_it->second); + // We currently only support RTX associated with VP8 due to limitations + // in webrtc where only one RTX payload type can be registered. + valid_apt = codec_it != pt_to_codec.end() && + _stricmp(codec_it->second->plName, kVp8PayloadName) == 0; + } + if (!valid_apt) { + LOG(LS_ERROR) << "The RTX codec isn't associated with a known and " + "supported payload type"; + return false; + } + if (engine()->vie()->rtp()->SetRtxReceivePayloadType( + channel_id, it->plType) != 0) { + LOG_RTCERR2(SetRtxReceivePayloadType, channel_id, it->plType); + return false; + } + continue; + } if (engine()->vie()->codec()->SetReceiveCodec(channel_id, *it) != 0) { LOG_RTCERR2(SetReceiveCodec, channel_id, it->plName); return false; @@ -3504,14 +3905,6 @@ bool WebRtcVideoMediaChannel::SetReceiveCodecs( } } } - - // Start receiving packets if at least one receive codec has been set. - if (!receive_codecs_.empty()) { - if (engine()->vie()->base()->StartReceive(channel_id) != 0) { - LOG_RTCERR1(StartReceive, channel_id); - return false; - } - } return true; } @@ -3519,14 +3912,32 @@ int WebRtcVideoMediaChannel::GetRecvChannelNum(uint32 ssrc) { if (ssrc == first_receive_ssrc_) { return vie_channel_; } + int recv_channel = -1; RecvChannelMap::iterator it = recv_channels_.find(ssrc); - return (it != recv_channels_.end()) ? it->second->channel_id() : -1; + if (it == recv_channels_.end()) { + // Check if we have an RTX stream registered on this SSRC. + SsrcMap::iterator rtx_it = rtx_to_primary_ssrc_.find(ssrc); + if (rtx_it != rtx_to_primary_ssrc_.end()) { + if (rtx_it->second == first_receive_ssrc_) { + recv_channel = vie_channel_; + } else { + it = recv_channels_.find(rtx_it->second); + assert(it != recv_channels_.end()); + recv_channel = it->second->channel_id(); + } + } + } else { + recv_channel = it->second->channel_id(); + } + return recv_channel; } // If the new frame size is different from the send codec size we set on vie, // we need to reset the send codec on vie. // The new send codec size should not exceed send_codec_ which is controlled // only by the 'jec' logic. +// TODO(pthatcher): Get rid of this function, so we only ever set up +// codecs in a single place. bool WebRtcVideoMediaChannel::MaybeResetVieSendCodec( WebRtcVideoChannelSendInfo* send_channel, int new_width, @@ -3538,7 +3949,7 @@ bool WebRtcVideoMediaChannel::MaybeResetVieSendCodec( } ASSERT(send_codec_.get() != NULL); - webrtc::VideoCodec target_codec = *send_codec_.get(); + webrtc::VideoCodec target_codec = *send_codec_; const VideoFormat& video_format = send_channel->video_format(); UpdateVideoCodec(video_format, &target_codec); @@ -3569,14 +3980,21 @@ bool WebRtcVideoMediaChannel::MaybeResetVieSendCodec( // Turn off VP8 frame dropping when screensharing as the current model does // not work well at low fps. bool vp8_frame_dropping = !is_screencast; - // Disable denoising for screencasting. + // TODO(pbos): Remove |video_noise_reduction| and enable it for all + // non-screencast. bool enable_denoising = - options_.video_noise_reduction.GetWithDefaultIfUnset(false); - bool denoising = !is_screencast && enable_denoising; + options_.video_noise_reduction.GetWithDefaultIfUnset(true); + // Disable denoising for screencasting. + if (is_screencast) { + enable_denoising = false; + } + int screencast_min_bitrate = + options_.screencast_min_bitrate.GetWithDefaultIfUnset(0); + bool leaky_bucket = options_.video_leaky_bucket.GetWithDefaultIfUnset(true); bool reset_send_codec = target_width != cur_width || target_height != cur_height || automatic_resize != vie_codec.codecSpecific.VP8.automaticResizeOn || - denoising != vie_codec.codecSpecific.VP8.denoisingOn || + enable_denoising != vie_codec.codecSpecific.VP8.denoisingOn || vp8_frame_dropping != vie_codec.codecSpecific.VP8.frameDroppingOn; if (reset_send_codec) { @@ -3585,18 +4003,34 @@ bool WebRtcVideoMediaChannel::MaybeResetVieSendCodec( vie_codec.height = target_height; vie_codec.maxFramerate = target_codec.maxFramerate; vie_codec.startBitrate = target_codec.startBitrate; + vie_codec.minBitrate = target_codec.minBitrate; + vie_codec.maxBitrate = target_codec.maxBitrate; + vie_codec.targetBitrate = 0; vie_codec.codecSpecific.VP8.automaticResizeOn = automatic_resize; - vie_codec.codecSpecific.VP8.denoisingOn = denoising; + vie_codec.codecSpecific.VP8.denoisingOn = enable_denoising; vie_codec.codecSpecific.VP8.frameDroppingOn = vp8_frame_dropping; - // TODO(mflodman): Remove 'is_screencast' check when screen cast settings - // are treated correctly in WebRTC. - if (!is_screencast) - MaybeChangeStartBitrate(channel_id, &vie_codec); + MaybeChangeBitrates(channel_id, &vie_codec); if (engine()->vie()->codec()->SetSendCodec(channel_id, vie_codec) != 0) { LOG_RTCERR1(SetSendCodec, channel_id); return false; } + + if (is_screencast) { + engine()->vie()->rtp()->SetMinTransmitBitrate(channel_id, + screencast_min_bitrate); + // If screencast and min bitrate set, force enable pacer. + if (screencast_min_bitrate > 0) { + engine()->vie()->rtp()->SetTransmissionSmoothingStatus(channel_id, + true); + } + } else { + // In case of switching from screencast to regular capture, set + // min bitrate padding and pacer back to defaults. + engine()->vie()->rtp()->SetMinTransmitBitrate(channel_id, 0); + engine()->vie()->rtp()->SetTransmissionSmoothingStatus(channel_id, + leaky_bucket); + } if (reset) { *reset = true; } @@ -3606,12 +4040,28 @@ bool WebRtcVideoMediaChannel::MaybeResetVieSendCodec( return true; } -void WebRtcVideoMediaChannel::MaybeChangeStartBitrate( - int channel_id, webrtc::VideoCodec* video_codec) { - if (video_codec->startBitrate < video_codec->minBitrate) { - video_codec->startBitrate = video_codec->minBitrate; - } else if (video_codec->startBitrate > video_codec->maxBitrate) { - video_codec->startBitrate = video_codec->maxBitrate; +void WebRtcVideoMediaChannel::MaybeChangeBitrates( + int channel_id, webrtc::VideoCodec* codec) { + codec->minBitrate = GetBitrate(codec->minBitrate, kMinVideoBitrate); + codec->startBitrate = GetBitrate(codec->startBitrate, kStartVideoBitrate); + codec->maxBitrate = GetBitrate(codec->maxBitrate, kMaxVideoBitrate); + + if (codec->minBitrate > codec->maxBitrate) { + LOG(LS_INFO) << "Decreasing codec min bitrate to the max (" + << codec->maxBitrate << ") because the min (" + << codec->minBitrate << ") exceeds the max."; + codec->minBitrate = codec->maxBitrate; + } + if (codec->startBitrate < codec->minBitrate) { + LOG(LS_INFO) << "Increasing codec start bitrate to the min (" + << codec->minBitrate << ") because the start (" + << codec->startBitrate << ") is less than the min."; + codec->startBitrate = codec->minBitrate; + } else if (codec->startBitrate > codec->maxBitrate) { + LOG(LS_INFO) << "Decreasing codec start bitrate to the max (" + << codec->maxBitrate << ") because the start (" + << codec->startBitrate << ") exceeds the max."; + codec->startBitrate = codec->maxBitrate; } // Use a previous target bitrate, if there is one. @@ -3620,11 +4070,11 @@ void WebRtcVideoMediaChannel::MaybeChangeStartBitrate( channel_id, ¤t_target_bitrate) == 0) { // Convert to kbps. current_target_bitrate /= 1000; - if (current_target_bitrate > video_codec->maxBitrate) { - current_target_bitrate = video_codec->maxBitrate; + if (current_target_bitrate > codec->maxBitrate) { + current_target_bitrate = codec->maxBitrate; } - if (current_target_bitrate > video_codec->startBitrate) { - video_codec->startBitrate = current_target_bitrate; + if (current_target_bitrate > codec->startBitrate) { + codec->startBitrate = current_target_bitrate; } } } diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.h b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.h index 289903a0722..775f4e46de1 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.h @@ -45,6 +45,7 @@ #error "Bogus include." #endif + namespace webrtc { class VideoCaptureModule; class VideoDecoder; @@ -60,12 +61,13 @@ class CpuMonitor; namespace cricket { +class CoordinatedVideoAdapter; +class ViETraceWrapper; +class ViEWrapper; class VideoCapturer; class VideoFrame; class VideoProcessor; class VideoRenderer; -class ViETraceWrapper; -class ViEWrapper; class VoiceMediaChannel; class WebRtcDecoderObserver; class WebRtcEncoderObserver; @@ -199,6 +201,7 @@ class WebRtcVideoEngine : public sigslot::has_slots<>, void SetTraceFilter(int filter); void SetTraceOptions(const std::string& options); bool InitVideoEngine(); + bool VerifyApt(const VideoCodec& in, int expected_apt) const; // webrtc::TraceCallback implementation. virtual void Print(webrtc::TraceLevel level, const char* trace, int length); @@ -227,10 +230,6 @@ class WebRtcVideoEngine : public sigslot::has_slots<>, int local_renderer_h_; VideoRenderer* local_renderer_; - // Critical section to protect the media processor register/unregister - // while processing a frame - talk_base::CriticalSection signal_media_critical_; - talk_base::scoped_ptr<talk_base::CpuMonitor> cpu_monitor_; }; @@ -248,6 +247,9 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, int video_channel() const { return vie_channel_; } bool sending() const { return sending_; } + // Public for testing purpose. + uint32 GetDefaultChannelSsrc(); + // VideoMediaChannel implementation virtual bool SetRecvCodecs(const std::vector<VideoCodec> &codecs); virtual bool SetSendCodecs(const std::vector<VideoCodec> &codecs); @@ -261,7 +263,7 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, virtual bool AddRecvStream(const StreamParams& sp); virtual bool RemoveRecvStream(uint32 ssrc); virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer); - virtual bool GetStats(VideoMediaInfo* info); + virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info); virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer); virtual bool SendIntraFrame(); virtual bool RequestIntraFrame(); @@ -276,7 +278,9 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, const std::vector<RtpHeaderExtension>& extensions); virtual bool SetSendRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions); - virtual bool SetSendBandwidth(bool autobw, int bps); + virtual int GetRtpSendTimeExtnId() const; + virtual bool SetStartSendBandwidth(int bps); + virtual bool SetMaxSendBandwidth(int bps); virtual bool SetOptions(const VideoOptions &options); virtual bool GetOptions(VideoOptions *options) const { *options = options_; @@ -288,6 +292,7 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, // Public functions for use by tests and other specialized code. uint32 send_ssrc() const { return 0; } bool GetRenderer(uint32 ssrc, VideoRenderer** renderer); + bool GetVideoAdapter(uint32 ssrc, CoordinatedVideoAdapter** video_adapter); void SendFrame(VideoCapturer* capturer, const VideoFrame* frame); bool SendFrame(WebRtcVideoChannelSendInfo* channel_info, const VideoFrame* frame, bool is_screencast); @@ -309,6 +314,7 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, private: typedef std::map<uint32, WebRtcVideoChannelRecvInfo*> RecvChannelMap; typedef std::map<uint32, WebRtcVideoChannelSendInfo*> SendChannelMap; + typedef std::map<uint32, uint32> SsrcMap; typedef int (webrtc::ViERTP_RTCP::* ExtensionSetterFunction)(int, bool, int); enum MediaDirection { MD_RECV, MD_SEND, MD_SENDRECV }; @@ -323,32 +329,32 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, // returning false. bool CreateChannel(uint32 ssrc_key, MediaDirection direction, int* channel_id); + bool CreateUnsignalledRecvChannel(uint32 ssrc_key, int* channel_id); bool ConfigureChannel(int channel_id, MediaDirection direction, uint32 ssrc_key); bool ConfigureReceiving(int channel_id, uint32 remote_ssrc_key); bool ConfigureSending(int channel_id, uint32 local_ssrc_key); bool SetNackFec(int channel_id, int red_payload_type, int fec_payload_type, bool nack_enabled); - bool SetSendCodec(const webrtc::VideoCodec& codec, int min_bitrate, - int start_bitrate, int max_bitrate); + bool SetSendCodec(const webrtc::VideoCodec& codec); bool SetSendCodec(WebRtcVideoChannelSendInfo* send_channel, - const webrtc::VideoCodec& codec, int min_bitrate, - int start_bitrate, int max_bitrate); + const webrtc::VideoCodec& codec); void LogSendCodecChange(const std::string& reason); // Prepares the channel with channel id |info->channel_id()| to receive all // codecs in |receive_codecs_| and start receive packets. bool SetReceiveCodecs(WebRtcVideoChannelRecvInfo* info); // Returns the channel number that receives the stream with SSRC |ssrc|. int GetRecvChannelNum(uint32 ssrc); + bool MaybeSetRtxSsrc(const StreamParams& sp, int channel_id); // Given captured video frame size, checks if we need to reset vie send codec. // |reset| is set to whether resetting has happened on vie or not. // Returns false on error. bool MaybeResetVieSendCodec(WebRtcVideoChannelSendInfo* send_channel, int new_width, int new_height, bool is_screencast, bool* reset); - // Checks the current bitrate estimate and modifies the start bitrate - // accordingly. - void MaybeChangeStartBitrate(int channel_id, webrtc::VideoCodec* video_codec); + // Checks the current bitrate estimate and modifies the bitrates + // accordingly, including converting kAutoBandwidth to the correct defaults. + void MaybeChangeBitrates(int channel_id, webrtc::VideoCodec* video_codec); // Helper function for starting the sending of media on all channels or // |channel_id|. Note that these two function do not change |sending_|. bool StartSend(); @@ -376,7 +382,6 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, bool IsDefaultChannel(int channel_id) const { return channel_id == vie_channel_; } - uint32 GetDefaultChannelSsrc(); bool DeleteSendChannel(uint32 ssrc_key); @@ -412,6 +417,8 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, // to one send channel, i.e. the last send channel. void MaybeDisconnectCapturer(VideoCapturer* capturer); + bool RemoveRecvStreamInternal(uint32 ssrc); + // Global state. WebRtcVideoEngine* engine_; VoiceMediaChannel* voice_channel_; @@ -427,10 +434,20 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, // work properly), resides in both recv_channels_ and send_channels_ with the // ssrc key 0. RecvChannelMap recv_channels_; // Contains all receive channels. + // A map from the SSRCs on which RTX packets are received to the media SSRCs + // the RTX packets are associated with. RTX packets will be delivered to the + // streams matching the primary SSRC. + SsrcMap rtx_to_primary_ssrc_; std::vector<webrtc::VideoCodec> receive_codecs_; + // A map from codec payload types to their associated payload types, if any. + // TODO(holmer): This is a temporary solution until webrtc::VideoCodec has + // an associated payload type member, when it does we can rely on + // receive_codecs_. + std::map<int, int> associated_payload_types_; bool render_started_; uint32 first_receive_ssrc_; std::vector<RtpHeaderExtension> receive_extensions_; + int num_unsignalled_recv_channels_; // Global send side state. SendChannelMap send_channels_; @@ -438,9 +455,6 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, int send_rtx_type_; int send_red_type_; int send_fec_type_; - int send_min_bitrate_; - int send_start_bitrate_; - int send_max_bitrate_; bool sending_; std::vector<RtpHeaderExtension> send_extensions_; diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2.cc new file mode 100644 index 00000000000..716c5a88554 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2.cc @@ -0,0 +1,1778 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_WEBRTC_VIDEO +#include "talk/media/webrtc/webrtcvideoengine2.h" + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <math.h> + +#include <string> + +#include "libyuv/convert_from.h" +#include "talk/base/buffer.h" +#include "talk/base/logging.h" +#include "talk/base/stringutils.h" +#include "talk/media/base/videocapturer.h" +#include "talk/media/base/videorenderer.h" +#include "talk/media/webrtc/webrtcvideocapturer.h" +#include "talk/media/webrtc/webrtcvideoframe.h" +#include "talk/media/webrtc/webrtcvoiceengine.h" +#include "webrtc/call.h" +// TODO(pbos): Move codecs out of modules (webrtc:3070). +#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" + +#define UNIMPLEMENTED \ + LOG(LS_ERROR) << "Call to unimplemented function " << __FUNCTION__; \ + ASSERT(false) + +namespace cricket { + +static const int kCpuMonitorPeriodMs = 2000; // 2 seconds. + +// This constant is really an on/off, lower-level configurable NACK history +// duration hasn't been implemented. +static const int kNackHistoryMs = 1000; + +static const int kDefaultFramerate = 30; +static const int kMinVideoBitrate = 50; +static const int kMaxVideoBitrate = 2000; + +static const int kVideoMtu = 1200; +static const int kVideoRtpBufferSize = 65536; + +static const char kVp8PayloadName[] = "VP8"; + +static const int kDefaultRtcpReceiverReportSsrc = 1; + +struct VideoCodecPref { + int payload_type; + const char* name; + int rtx_payload_type; +} kDefaultVideoCodecPref = {100, kVp8PayloadName, 96}; + +VideoCodecPref kRedPref = {116, kRedCodecName, -1}; +VideoCodecPref kUlpfecPref = {117, kUlpfecCodecName, -1}; + +// The formats are sorted by the descending order of width. We use the order to +// find the next format for CPU and bandwidth adaptation. +const VideoFormatPod kDefaultVideoFormat = { + 640, 400, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}; +const VideoFormatPod kVideoFormats[] = { + {1280, 800, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {1280, 720, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {960, 600, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {960, 540, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + kDefaultVideoFormat, + {640, 360, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {640, 480, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {480, 300, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {480, 270, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {480, 360, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {320, 200, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {320, 180, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {320, 240, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {240, 150, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {240, 135, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {240, 180, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {160, 100, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {160, 90, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, + {160, 120, FPS_TO_INTERVAL(kDefaultFramerate), FOURCC_ANY}, }; + +static bool FindFirstMatchingCodec(const std::vector<VideoCodec>& codecs, + const VideoCodec& requested_codec, + VideoCodec* matching_codec) { + for (size_t i = 0; i < codecs.size(); ++i) { + if (requested_codec.Matches(codecs[i])) { + *matching_codec = codecs[i]; + return true; + } + } + return false; +} +static bool FindBestVideoFormat(int max_width, + int max_height, + int aspect_width, + int aspect_height, + VideoFormat* video_format) { + assert(max_width > 0); + assert(max_height > 0); + assert(aspect_width > 0); + assert(aspect_height > 0); + VideoFormat best_format; + for (int i = 0; i < ARRAY_SIZE(kVideoFormats); ++i) { + const VideoFormat format(kVideoFormats[i]); + + // Skip any format that is larger than the local or remote maximums, or + // smaller than the current best match + if (format.width > max_width || format.height > max_height || + (format.width < best_format.width && + format.height < best_format.height)) { + continue; + } + + // If we don't have any matches yet, this is the best so far. + if (best_format.width == 0) { + best_format = format; + continue; + } + + // Prefer closer aspect ratios i.e: + // |format| aspect - requested aspect < + // |best_format| aspect - requested aspect + if (abs(format.width * aspect_height * best_format.height - + aspect_width * format.height * best_format.height) < + abs(best_format.width * aspect_height * format.height - + aspect_width * format.height * best_format.height)) { + best_format = format; + } + } + if (best_format.width != 0) { + *video_format = best_format; + return true; + } + return false; +} + +static void AddDefaultFeedbackParams(VideoCodec* codec) { + const FeedbackParam kFir(kRtcpFbParamCcm, kRtcpFbCcmParamFir); + codec->AddFeedbackParam(kFir); + const FeedbackParam kNack(kRtcpFbParamNack, kParamValueEmpty); + codec->AddFeedbackParam(kNack); + const FeedbackParam kPli(kRtcpFbParamNack, kRtcpFbNackParamPli); + codec->AddFeedbackParam(kPli); + const FeedbackParam kRemb(kRtcpFbParamRemb, kParamValueEmpty); + codec->AddFeedbackParam(kRemb); +} + +static bool IsNackEnabled(const VideoCodec& codec) { + return codec.HasFeedbackParam( + FeedbackParam(kRtcpFbParamNack, kParamValueEmpty)); +} + +static VideoCodec DefaultVideoCodec() { + VideoCodec default_codec(kDefaultVideoCodecPref.payload_type, + kDefaultVideoCodecPref.name, + kDefaultVideoFormat.width, + kDefaultVideoFormat.height, + kDefaultFramerate, + 0); + AddDefaultFeedbackParams(&default_codec); + return default_codec; +} + +static VideoCodec DefaultRedCodec() { + return VideoCodec(kRedPref.payload_type, kRedPref.name, 0, 0, 0, 0); +} + +static VideoCodec DefaultUlpfecCodec() { + return VideoCodec(kUlpfecPref.payload_type, kUlpfecPref.name, 0, 0, 0, 0); +} + +static std::vector<VideoCodec> DefaultVideoCodecs() { + std::vector<VideoCodec> codecs; + codecs.push_back(DefaultVideoCodec()); + codecs.push_back(DefaultRedCodec()); + codecs.push_back(DefaultUlpfecCodec()); + if (kDefaultVideoCodecPref.rtx_payload_type != -1) { + codecs.push_back( + VideoCodec::CreateRtxCodec(kDefaultVideoCodecPref.rtx_payload_type, + kDefaultVideoCodecPref.payload_type)); + } + return codecs; +} + +WebRtcVideoEncoderFactory2::~WebRtcVideoEncoderFactory2() { +} + +std::vector<webrtc::VideoStream> WebRtcVideoEncoderFactory2::CreateVideoStreams( + const VideoCodec& codec, + const VideoOptions& options, + size_t num_streams) { + assert(SupportsCodec(codec)); + if (num_streams != 1) { + LOG(LS_ERROR) << "Unsupported number of streams: " << num_streams; + return std::vector<webrtc::VideoStream>(); + } + + webrtc::VideoStream stream; + stream.width = codec.width; + stream.height = codec.height; + stream.max_framerate = + codec.framerate != 0 ? codec.framerate : kDefaultFramerate; + + int min_bitrate = kMinVideoBitrate; + codec.GetParam(kCodecParamMinBitrate, &min_bitrate); + int max_bitrate = kMaxVideoBitrate; + codec.GetParam(kCodecParamMaxBitrate, &max_bitrate); + stream.min_bitrate_bps = min_bitrate * 1000; + stream.target_bitrate_bps = stream.max_bitrate_bps = max_bitrate * 1000; + + int max_qp = 56; + codec.GetParam(kCodecParamMaxQuantization, &max_qp); + stream.max_qp = max_qp; + std::vector<webrtc::VideoStream> streams; + streams.push_back(stream); + return streams; +} + +webrtc::VideoEncoder* WebRtcVideoEncoderFactory2::CreateVideoEncoder( + const VideoCodec& codec, + const VideoOptions& options) { + assert(SupportsCodec(codec)); + return webrtc::VP8Encoder::Create(); +} + +bool WebRtcVideoEncoderFactory2::SupportsCodec(const VideoCodec& codec) { + return _stricmp(codec.name.c_str(), kVp8PayloadName) == 0; +} + +WebRtcVideoEngine2::WebRtcVideoEngine2() { + // Construct without a factory or voice engine. + Construct(NULL, NULL, new talk_base::CpuMonitor(NULL)); +} + +WebRtcVideoEngine2::WebRtcVideoEngine2( + WebRtcVideoChannelFactory* channel_factory) { + // Construct without a voice engine. + Construct(channel_factory, NULL, new talk_base::CpuMonitor(NULL)); +} + +void WebRtcVideoEngine2::Construct(WebRtcVideoChannelFactory* channel_factory, + WebRtcVoiceEngine* voice_engine, + talk_base::CpuMonitor* cpu_monitor) { + LOG(LS_INFO) << "WebRtcVideoEngine2::WebRtcVideoEngine2"; + worker_thread_ = NULL; + voice_engine_ = voice_engine; + initialized_ = false; + capture_started_ = false; + cpu_monitor_.reset(cpu_monitor); + channel_factory_ = channel_factory; + + video_codecs_ = DefaultVideoCodecs(); + default_codec_format_ = VideoFormat(kDefaultVideoFormat); + + rtp_header_extensions_.push_back( + RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension, + kRtpTimestampOffsetHeaderExtensionDefaultId)); + rtp_header_extensions_.push_back( + RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension, + kRtpAbsoluteSenderTimeHeaderExtensionDefaultId)); +} + +WebRtcVideoEngine2::~WebRtcVideoEngine2() { + LOG(LS_INFO) << "WebRtcVideoEngine2::~WebRtcVideoEngine2"; + + if (initialized_) { + Terminate(); + } +} + +bool WebRtcVideoEngine2::Init(talk_base::Thread* worker_thread) { + LOG(LS_INFO) << "WebRtcVideoEngine2::Init"; + worker_thread_ = worker_thread; + ASSERT(worker_thread_ != NULL); + + cpu_monitor_->set_thread(worker_thread_); + if (!cpu_monitor_->Start(kCpuMonitorPeriodMs)) { + LOG(LS_ERROR) << "Failed to start CPU monitor."; + cpu_monitor_.reset(); + } + + initialized_ = true; + return true; +} + +void WebRtcVideoEngine2::Terminate() { + LOG(LS_INFO) << "WebRtcVideoEngine2::Terminate"; + + cpu_monitor_->Stop(); + + initialized_ = false; +} + +int WebRtcVideoEngine2::GetCapabilities() { return VIDEO_RECV | VIDEO_SEND; } + +bool WebRtcVideoEngine2::SetOptions(const VideoOptions& options) { + // TODO(pbos): Do we need this? This is a no-op in the existing + // WebRtcVideoEngine implementation. + LOG(LS_VERBOSE) << "SetOptions: " << options.ToString(); + // options_ = options; + return true; +} + +bool WebRtcVideoEngine2::SetDefaultEncoderConfig( + const VideoEncoderConfig& config) { + // TODO(pbos): Implement. Should be covered by corresponding unit tests. + LOG(LS_VERBOSE) << "SetDefaultEncoderConfig()"; + return true; +} + +VideoEncoderConfig WebRtcVideoEngine2::GetDefaultEncoderConfig() const { + return VideoEncoderConfig(DefaultVideoCodec()); +} + +WebRtcVideoChannel2* WebRtcVideoEngine2::CreateChannel( + VoiceMediaChannel* voice_channel) { + LOG(LS_INFO) << "CreateChannel: " + << (voice_channel != NULL ? "With" : "Without") + << " voice channel."; + WebRtcVideoChannel2* channel = + channel_factory_ != NULL + ? channel_factory_->Create(this, voice_channel) + : new WebRtcVideoChannel2( + this, voice_channel, GetVideoEncoderFactory()); + if (!channel->Init()) { + delete channel; + return NULL; + } + channel->SetRecvCodecs(video_codecs_); + return channel; +} + +const std::vector<VideoCodec>& WebRtcVideoEngine2::codecs() const { + return video_codecs_; +} + +const std::vector<RtpHeaderExtension>& +WebRtcVideoEngine2::rtp_header_extensions() const { + return rtp_header_extensions_; +} + +void WebRtcVideoEngine2::SetLogging(int min_sev, const char* filter) { + // TODO(pbos): Set up logging. + LOG(LS_VERBOSE) << "SetLogging: " << min_sev << '"' << filter << '"'; + // if min_sev == -1, we keep the current log level. + if (min_sev < 0) { + assert(min_sev == -1); + return; + } +} + +bool WebRtcVideoEngine2::EnableTimedRender() { + // TODO(pbos): Figure out whether this can be removed. + return true; +} + +bool WebRtcVideoEngine2::SetLocalRenderer(VideoRenderer* renderer) { + // TODO(pbos): Implement or remove. Unclear which stream should be rendered + // locally even. + return true; +} + +// Checks to see whether we comprehend and could receive a particular codec +bool WebRtcVideoEngine2::FindCodec(const VideoCodec& in) { + // TODO(pbos): Probe encoder factory to figure out that the codec is supported + // if supported by the encoder factory. Add a corresponding test that fails + // with this code (that doesn't ask the factory). + for (int i = 0; i < ARRAY_SIZE(kVideoFormats); ++i) { + const VideoFormat fmt(kVideoFormats[i]); + if ((in.width != 0 || in.height != 0) && + (fmt.width != in.width || fmt.height != in.height)) { + continue; + } + for (size_t j = 0; j < video_codecs_.size(); ++j) { + VideoCodec codec(video_codecs_[j].id, video_codecs_[j].name, 0, 0, 0, 0); + if (codec.Matches(in)) { + return true; + } + } + } + return false; +} + +// Tells whether the |requested| codec can be transmitted or not. If it can be +// transmitted |out| is set with the best settings supported. Aspect ratio will +// be set as close to |current|'s as possible. If not set |requested|'s +// dimensions will be used for aspect ratio matching. +bool WebRtcVideoEngine2::CanSendCodec(const VideoCodec& requested, + const VideoCodec& current, + VideoCodec* out) { + assert(out != NULL); + // TODO(pbos): Implement. + + if (requested.width != requested.height && + (requested.height == 0 || requested.width == 0)) { + // 0xn and nx0 are invalid resolutions. + return false; + } + + VideoCodec matching_codec; + if (!FindFirstMatchingCodec(video_codecs_, requested, &matching_codec)) { + // Codec not supported. + return false; + } + + // Pick the best quality that is within their and our bounds and has the + // correct aspect ratio. + VideoFormat format; + if (requested.width == 0 && requested.height == 0) { + // Special case with resolution 0. The channel should not send frames. + } else { + int max_width = talk_base::_min(requested.width, matching_codec.width); + int max_height = talk_base::_min(requested.height, matching_codec.height); + int aspect_width = max_width; + int aspect_height = max_height; + if (current.width > 0 && current.height > 0) { + aspect_width = current.width; + aspect_height = current.height; + } + if (!FindBestVideoFormat( + max_width, max_height, aspect_width, aspect_height, &format)) { + return false; + } + } + + out->id = requested.id; + out->name = requested.name; + out->preference = requested.preference; + out->params = requested.params; + out->framerate = + talk_base::_min(requested.framerate, matching_codec.framerate); + out->width = format.width; + out->height = format.height; + out->params = requested.params; + out->feedback_params = requested.feedback_params; + return true; +} + +bool WebRtcVideoEngine2::SetVoiceEngine(WebRtcVoiceEngine* voice_engine) { + if (initialized_) { + LOG(LS_WARNING) << "SetVoiceEngine can not be called after Init"; + return false; + } + voice_engine_ = voice_engine; + return true; +} + +// Ignore spammy trace messages, mostly from the stats API when we haven't +// gotten RTCP info yet from the remote side. +bool WebRtcVideoEngine2::ShouldIgnoreTrace(const std::string& trace) { + static const char* const kTracesToIgnore[] = {NULL}; + for (const char* const* p = kTracesToIgnore; *p; ++p) { + if (trace.find(*p) == 0) { + return true; + } + } + return false; +} + +WebRtcVideoEncoderFactory2* WebRtcVideoEngine2::GetVideoEncoderFactory() { + return &default_video_encoder_factory_; +} + +// Thin map between VideoFrame and an existing webrtc::I420VideoFrame +// to avoid having to copy the rendered VideoFrame prematurely. +// This implementation is only safe to use in a const context and should never +// be written to. +class WebRtcVideoRenderFrame : public VideoFrame { + public: + explicit WebRtcVideoRenderFrame(const webrtc::I420VideoFrame* frame) + : frame_(frame) {} + + virtual bool InitToBlack(int w, + int h, + size_t pixel_width, + size_t pixel_height, + int64 elapsed_time, + int64 time_stamp) OVERRIDE { + UNIMPLEMENTED; + return false; + } + + virtual bool Reset(uint32 fourcc, + int w, + int h, + int dw, + int dh, + uint8* sample, + size_t sample_size, + size_t pixel_width, + size_t pixel_height, + int64 elapsed_time, + int64 time_stamp, + int rotation) OVERRIDE { + UNIMPLEMENTED; + return false; + } + + virtual size_t GetWidth() const OVERRIDE { + return static_cast<size_t>(frame_->width()); + } + virtual size_t GetHeight() const OVERRIDE { + return static_cast<size_t>(frame_->height()); + } + + virtual const uint8* GetYPlane() const OVERRIDE { + return frame_->buffer(webrtc::kYPlane); + } + virtual const uint8* GetUPlane() const OVERRIDE { + return frame_->buffer(webrtc::kUPlane); + } + virtual const uint8* GetVPlane() const OVERRIDE { + return frame_->buffer(webrtc::kVPlane); + } + + virtual uint8* GetYPlane() OVERRIDE { + UNIMPLEMENTED; + return NULL; + } + virtual uint8* GetUPlane() OVERRIDE { + UNIMPLEMENTED; + return NULL; + } + virtual uint8* GetVPlane() OVERRIDE { + UNIMPLEMENTED; + return NULL; + } + + virtual int32 GetYPitch() const OVERRIDE { + return frame_->stride(webrtc::kYPlane); + } + virtual int32 GetUPitch() const OVERRIDE { + return frame_->stride(webrtc::kUPlane); + } + virtual int32 GetVPitch() const OVERRIDE { + return frame_->stride(webrtc::kVPlane); + } + + virtual void* GetNativeHandle() const OVERRIDE { return NULL; } + + virtual size_t GetPixelWidth() const OVERRIDE { return 1; } + virtual size_t GetPixelHeight() const OVERRIDE { return 1; } + + virtual int64 GetElapsedTime() const OVERRIDE { + // Convert millisecond render time to ns timestamp. + return frame_->render_time_ms() * talk_base::kNumNanosecsPerMillisec; + } + virtual int64 GetTimeStamp() const OVERRIDE { + // Convert 90K rtp timestamp to ns timestamp. + return (frame_->timestamp() / 90) * talk_base::kNumNanosecsPerMillisec; + } + virtual void SetElapsedTime(int64 elapsed_time) OVERRIDE { UNIMPLEMENTED; } + virtual void SetTimeStamp(int64 time_stamp) OVERRIDE { UNIMPLEMENTED; } + + virtual int GetRotation() const OVERRIDE { + UNIMPLEMENTED; + return ROTATION_0; + } + + virtual VideoFrame* Copy() const OVERRIDE { + UNIMPLEMENTED; + return NULL; + } + + virtual bool MakeExclusive() OVERRIDE { + UNIMPLEMENTED; + return false; + } + + virtual size_t CopyToBuffer(uint8* buffer, size_t size) const { + UNIMPLEMENTED; + return 0; + } + + // TODO(fbarchard): Refactor into base class and share with LMI + virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, + uint8* buffer, + size_t size, + int stride_rgb) const OVERRIDE { + size_t width = GetWidth(); + size_t height = GetHeight(); + size_t needed = (stride_rgb >= 0 ? stride_rgb : -stride_rgb) * height; + if (size < needed) { + LOG(LS_WARNING) << "RGB buffer is not large enough"; + return needed; + } + + if (libyuv::ConvertFromI420(GetYPlane(), + GetYPitch(), + GetUPlane(), + GetUPitch(), + GetVPlane(), + GetVPitch(), + buffer, + stride_rgb, + static_cast<int>(width), + static_cast<int>(height), + to_fourcc)) { + LOG(LS_ERROR) << "RGB type not supported: " << to_fourcc; + return 0; // 0 indicates error + } + return needed; + } + + protected: + virtual VideoFrame* CreateEmptyFrame(int w, + int h, + size_t pixel_width, + size_t pixel_height, + int64 elapsed_time, + int64 time_stamp) const OVERRIDE { + // TODO(pbos): Remove WebRtcVideoFrame dependency, and have a non-const + // version of I420VideoFrame wrapped. + WebRtcVideoFrame* frame = new WebRtcVideoFrame(); + frame->InitToBlack( + w, h, pixel_width, pixel_height, elapsed_time, time_stamp); + return frame; + } + + private: + const webrtc::I420VideoFrame* const frame_; +}; + +WebRtcVideoRenderer::WebRtcVideoRenderer() + : last_width_(-1), last_height_(-1), renderer_(NULL) {} + +void WebRtcVideoRenderer::RenderFrame(const webrtc::I420VideoFrame& frame, + int time_to_render_ms) { + talk_base::CritScope crit(&lock_); + if (renderer_ == NULL) { + LOG(LS_WARNING) << "VideoReceiveStream not connected to a VideoRenderer."; + return; + } + + if (frame.width() != last_width_ || frame.height() != last_height_) { + SetSize(frame.width(), frame.height()); + } + + LOG(LS_VERBOSE) << "RenderFrame: (" << frame.width() << "x" << frame.height() + << ")"; + + const WebRtcVideoRenderFrame render_frame(&frame); + renderer_->RenderFrame(&render_frame); +} + +void WebRtcVideoRenderer::SetRenderer(cricket::VideoRenderer* renderer) { + talk_base::CritScope crit(&lock_); + renderer_ = renderer; + if (renderer_ != NULL && last_width_ != -1) { + SetSize(last_width_, last_height_); + } +} + +VideoRenderer* WebRtcVideoRenderer::GetRenderer() { + talk_base::CritScope crit(&lock_); + return renderer_; +} + +void WebRtcVideoRenderer::SetSize(int width, int height) { + talk_base::CritScope crit(&lock_); + if (!renderer_->SetSize(width, height, 0)) { + LOG(LS_ERROR) << "Could not set renderer size."; + } + last_width_ = width; + last_height_ = height; +} + +// WebRtcVideoChannel2 + +WebRtcVideoChannel2::WebRtcVideoChannel2( + WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel, + WebRtcVideoEncoderFactory2* encoder_factory) + : encoder_factory_(encoder_factory) { + // TODO(pbos): Connect the video and audio with |voice_channel|. + webrtc::Call::Config config(this); + Construct(webrtc::Call::Create(config), engine); +} + +WebRtcVideoChannel2::WebRtcVideoChannel2( + webrtc::Call* call, + WebRtcVideoEngine2* engine, + WebRtcVideoEncoderFactory2* encoder_factory) + : encoder_factory_(encoder_factory) { + Construct(call, engine); +} + +void WebRtcVideoChannel2::Construct(webrtc::Call* call, + WebRtcVideoEngine2* engine) { + rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc; + sending_ = false; + call_.reset(call); + default_renderer_ = NULL; + default_send_ssrc_ = 0; + default_recv_ssrc_ = 0; +} + +WebRtcVideoChannel2::~WebRtcVideoChannel2() { + for (std::map<uint32, WebRtcVideoSendStream*>::iterator it = + send_streams_.begin(); + it != send_streams_.end(); + ++it) { + delete it->second; + } + + for (std::map<uint32, webrtc::VideoReceiveStream*>::iterator it = + receive_streams_.begin(); + it != receive_streams_.end(); + ++it) { + assert(it->second != NULL); + call_->DestroyVideoReceiveStream(it->second); + } + + for (std::map<uint32, WebRtcVideoRenderer*>::iterator it = renderers_.begin(); + it != renderers_.end(); + ++it) { + assert(it->second != NULL); + delete it->second; + } +} + +bool WebRtcVideoChannel2::Init() { return true; } + +namespace { + +static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) { + std::stringstream out; + out << '{'; + for (size_t i = 0; i < codecs.size(); ++i) { + out << codecs[i].ToString(); + if (i != codecs.size() - 1) { + out << ", "; + } + } + out << '}'; + return out.str(); +} + +static bool ValidateCodecFormats(const std::vector<VideoCodec>& codecs) { + bool has_video = false; + for (size_t i = 0; i < codecs.size(); ++i) { + if (!codecs[i].ValidateCodecFormat()) { + return false; + } + if (codecs[i].GetCodecType() == VideoCodec::CODEC_VIDEO) { + has_video = true; + } + } + if (!has_video) { + LOG(LS_ERROR) << "Setting codecs without a video codec is invalid: " + << CodecVectorToString(codecs); + return false; + } + return true; +} + +static std::string RtpExtensionsToString( + const std::vector<RtpHeaderExtension>& extensions) { + std::stringstream out; + out << '{'; + for (size_t i = 0; i < extensions.size(); ++i) { + out << "{" << extensions[i].uri << ": " << extensions[i].id << "}"; + if (i != extensions.size() - 1) { + out << ", "; + } + } + out << '}'; + return out.str(); +} + +} // namespace + +bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector<VideoCodec>& codecs) { + // TODO(pbos): Must these receive codecs propagate to existing receive + // streams? + LOG(LS_INFO) << "SetRecvCodecs: " << CodecVectorToString(codecs); + if (!ValidateCodecFormats(codecs)) { + return false; + } + + const std::vector<VideoCodecSettings> mapped_codecs = MapCodecs(codecs); + if (mapped_codecs.empty()) { + LOG(LS_ERROR) << "SetRecvCodecs called without video codec payloads."; + return false; + } + + // TODO(pbos): Add a decoder factory which controls supported codecs. + // Blocked on webrtc:2854. + for (size_t i = 0; i < mapped_codecs.size(); ++i) { + if (_stricmp(mapped_codecs[i].codec.name.c_str(), kVp8PayloadName) != 0) { + LOG(LS_ERROR) << "SetRecvCodecs called with unsupported codec: '" + << mapped_codecs[i].codec.name << "'"; + return false; + } + } + + recv_codecs_ = mapped_codecs; + return true; +} + +bool WebRtcVideoChannel2::SetSendCodecs(const std::vector<VideoCodec>& codecs) { + LOG(LS_INFO) << "SetSendCodecs: " << CodecVectorToString(codecs); + if (!ValidateCodecFormats(codecs)) { + return false; + } + + const std::vector<VideoCodecSettings> supported_codecs = + FilterSupportedCodecs(MapCodecs(codecs)); + + if (supported_codecs.empty()) { + LOG(LS_ERROR) << "No video codecs supported by encoder factory."; + return false; + } + + send_codec_.Set(supported_codecs.front()); + LOG(LS_INFO) << "Using codec: " << supported_codecs.front().codec.ToString(); + + SetCodecForAllSendStreams(supported_codecs.front()); + + return true; +} + +bool WebRtcVideoChannel2::GetSendCodec(VideoCodec* codec) { + VideoCodecSettings codec_settings; + if (!send_codec_.Get(&codec_settings)) { + LOG(LS_VERBOSE) << "GetSendCodec: No send codec set."; + return false; + } + *codec = codec_settings.codec; + return true; +} + +bool WebRtcVideoChannel2::SetSendStreamFormat(uint32 ssrc, + const VideoFormat& format) { + LOG(LS_VERBOSE) << "SetSendStreamFormat:" << ssrc << " -> " + << format.ToString(); + if (send_streams_.find(ssrc) == send_streams_.end()) { + return false; + } + return send_streams_[ssrc]->SetVideoFormat(format); +} + +bool WebRtcVideoChannel2::SetRender(bool render) { + // TODO(pbos): Implement. Or refactor away as it shouldn't be needed. + LOG(LS_VERBOSE) << "SetRender: " << (render ? "true" : "false"); + return true; +} + +bool WebRtcVideoChannel2::SetSend(bool send) { + LOG(LS_VERBOSE) << "SetSend: " << (send ? "true" : "false"); + if (send && !send_codec_.IsSet()) { + LOG(LS_ERROR) << "SetSend(true) called before setting codec."; + return false; + } + if (send) { + StartAllSendStreams(); + } else { + StopAllSendStreams(); + } + sending_ = send; + return true; +} + +static bool ConfigureSendSsrcs(webrtc::VideoSendStream::Config* config, + const StreamParams& sp) { + if (!sp.has_ssrc_groups()) { + config->rtp.ssrcs = sp.ssrcs; + return true; + } + + if (sp.get_ssrc_group(kFecSsrcGroupSemantics) != NULL) { + LOG(LS_ERROR) << "Standalone FEC SSRCs not supported."; + return false; + } + + // Map RTX SSRCs. + std::vector<uint32_t> ssrcs; + std::vector<uint32_t> rtx_ssrcs; + const SsrcGroup* sim_group = sp.get_ssrc_group(kSimSsrcGroupSemantics); + if (sim_group == NULL) { + ssrcs.push_back(sp.first_ssrc()); + uint32_t rtx_ssrc; + if (!sp.GetFidSsrc(sp.first_ssrc(), &rtx_ssrc)) { + LOG(LS_ERROR) << "Could not find FID ssrc for primary SSRC '" + << sp.first_ssrc() << "':" << sp.ToString(); + return false; + } + rtx_ssrcs.push_back(rtx_ssrc); + } else { + ssrcs = sim_group->ssrcs; + for (size_t i = 0; i < sim_group->ssrcs.size(); ++i) { + uint32_t rtx_ssrc; + if (!sp.GetFidSsrc(sim_group->ssrcs[i], &rtx_ssrc)) { + continue; + } + rtx_ssrcs.push_back(rtx_ssrc); + } + } + if (!rtx_ssrcs.empty() && ssrcs.size() != rtx_ssrcs.size()) { + LOG(LS_ERROR) + << "RTX SSRCs exist, but don't cover all SSRCs (unsupported): " + << sp.ToString(); + return false; + } + config->rtp.rtx.ssrcs = rtx_ssrcs; + config->rtp.ssrcs = ssrcs; + return true; +} + +bool WebRtcVideoChannel2::AddSendStream(const StreamParams& sp) { + LOG(LS_INFO) << "AddSendStream: " << sp.ToString(); + if (sp.ssrcs.empty()) { + LOG(LS_ERROR) << "No SSRCs in stream parameters."; + return false; + } + + uint32 ssrc = sp.first_ssrc(); + assert(ssrc != 0); + // TODO(pbos): Make sure none of sp.ssrcs are used, not just the identifying + // ssrc. + if (send_streams_.find(ssrc) != send_streams_.end()) { + LOG(LS_ERROR) << "Send stream with ssrc '" << ssrc << "' already exists."; + return false; + } + + webrtc::VideoSendStream::Config config = call_->GetDefaultSendConfig(); + + if (!ConfigureSendSsrcs(&config, sp)) { + return false; + } + + VideoCodecSettings codec_settings; + if (!send_codec_.Get(&codec_settings)) { + // TODO(pbos): Set up a temporary fake encoder for VideoSendStream instead + // of setting default codecs not to break CreateEncoderSettings. + SetSendCodecs(DefaultVideoCodecs()); + assert(send_codec_.IsSet()); + send_codec_.Get(&codec_settings); + // This is only to bring up defaults to make VideoSendStream setup easier + // and avoid complexity. We still don't want to allow sending with the + // default codec. + send_codec_.Clear(); + } + + // CreateEncoderSettings will allocate a suitable VideoEncoder instance + // matching current settings. + std::vector<webrtc::VideoStream> video_streams = + encoder_factory_->CreateVideoStreams( + codec_settings.codec, options_, config.rtp.ssrcs.size()); + if (video_streams.empty()) { + return false; + } + + config.encoder_settings.encoder = + encoder_factory_->CreateVideoEncoder(codec_settings.codec, options_); + config.encoder_settings.payload_name = codec_settings.codec.name; + config.encoder_settings.payload_type = codec_settings.codec.id; + config.rtp.c_name = sp.cname; + config.rtp.fec = codec_settings.fec; + if (!config.rtp.rtx.ssrcs.empty()) { + config.rtp.rtx.payload_type = codec_settings.rtx_payload_type; + } + + config.rtp.extensions = send_rtp_extensions_; + + if (IsNackEnabled(codec_settings.codec)) { + config.rtp.nack.rtp_history_ms = kNackHistoryMs; + } + config.rtp.max_packet_size = kVideoMtu; + + WebRtcVideoSendStream* stream = + new WebRtcVideoSendStream(call_.get(), + config, + options_, + codec_settings.codec, + video_streams, + encoder_factory_); + send_streams_[ssrc] = stream; + + if (rtcp_receiver_report_ssrc_ == kDefaultRtcpReceiverReportSsrc) { + rtcp_receiver_report_ssrc_ = ssrc; + } + if (default_send_ssrc_ == 0) { + default_send_ssrc_ = ssrc; + } + if (sending_) { + stream->Start(); + } + + return true; +} + +bool WebRtcVideoChannel2::RemoveSendStream(uint32 ssrc) { + LOG(LS_INFO) << "RemoveSendStream: " << ssrc; + + if (ssrc == 0) { + if (default_send_ssrc_ == 0) { + LOG(LS_ERROR) << "No default send stream active."; + return false; + } + + LOG(LS_VERBOSE) << "Removing default stream: " << default_send_ssrc_; + ssrc = default_send_ssrc_; + } + + std::map<uint32, WebRtcVideoSendStream*>::iterator it = + send_streams_.find(ssrc); + if (it == send_streams_.end()) { + return false; + } + + delete it->second; + send_streams_.erase(it); + + if (ssrc == default_send_ssrc_) { + default_send_ssrc_ = 0; + } + + return true; +} + +bool WebRtcVideoChannel2::AddRecvStream(const StreamParams& sp) { + LOG(LS_INFO) << "AddRecvStream: " << sp.ToString(); + assert(sp.ssrcs.size() > 0); + + uint32 ssrc = sp.first_ssrc(); + assert(ssrc != 0); // TODO(pbos): Is this ever valid? + if (default_recv_ssrc_ == 0) { + default_recv_ssrc_ = ssrc; + } + + // TODO(pbos): Check if any of the SSRCs overlap. + if (receive_streams_.find(ssrc) != receive_streams_.end()) { + LOG(LS_ERROR) << "Receive stream for SSRC " << ssrc << "already exists."; + return false; + } + + webrtc::VideoReceiveStream::Config config = call_->GetDefaultReceiveConfig(); + config.rtp.remote_ssrc = ssrc; + config.rtp.local_ssrc = rtcp_receiver_report_ssrc_; + + if (IsNackEnabled(recv_codecs_.begin()->codec)) { + config.rtp.nack.rtp_history_ms = kNackHistoryMs; + } + config.rtp.remb = true; + config.rtp.extensions = recv_rtp_extensions_; + // TODO(pbos): This protection is against setting the same local ssrc as + // remote which is not permitted by the lower-level API. RTCP requires a + // corresponding sender SSRC. Figure out what to do when we don't have + // (receive-only) or know a good local SSRC. + if (config.rtp.remote_ssrc == config.rtp.local_ssrc) { + if (config.rtp.local_ssrc != kDefaultRtcpReceiverReportSsrc) { + config.rtp.local_ssrc = kDefaultRtcpReceiverReportSsrc; + } else { + config.rtp.local_ssrc = kDefaultRtcpReceiverReportSsrc + 1; + } + } + bool default_renderer_used = false; + for (std::map<uint32, WebRtcVideoRenderer*>::iterator it = renderers_.begin(); + it != renderers_.end(); + ++it) { + if (it->second->GetRenderer() == default_renderer_) { + default_renderer_used = true; + break; + } + } + + assert(renderers_[ssrc] == NULL); + renderers_[ssrc] = new WebRtcVideoRenderer(); + if (!default_renderer_used) { + renderers_[ssrc]->SetRenderer(default_renderer_); + } + config.renderer = renderers_[ssrc]; + + { + // TODO(pbos): Base receive codecs off recv_codecs_ and set up using a + // DecoderFactory similar to send side. Pending webrtc:2854. + // Also set up default codecs if there's nothing in recv_codecs_. + webrtc::VideoCodec codec; + memset(&codec, 0, sizeof(codec)); + + codec.plType = kDefaultVideoCodecPref.payload_type; + strcpy(codec.plName, kDefaultVideoCodecPref.name); + codec.codecType = webrtc::kVideoCodecVP8; + codec.codecSpecific.VP8.resilience = webrtc::kResilientStream; + codec.codecSpecific.VP8.numberOfTemporalLayers = 1; + codec.codecSpecific.VP8.denoisingOn = true; + codec.codecSpecific.VP8.errorConcealmentOn = false; + codec.codecSpecific.VP8.automaticResizeOn = false; + codec.codecSpecific.VP8.frameDroppingOn = true; + codec.codecSpecific.VP8.keyFrameInterval = 3000; + // Bitrates don't matter and are ignored for the receiver. This is put in to + // have the current underlying implementation accept the VideoCodec. + codec.minBitrate = codec.startBitrate = codec.maxBitrate = 300; + config.codecs.push_back(codec); + for (size_t i = 0; i < recv_codecs_.size(); ++i) { + if (recv_codecs_[i].codec.id == codec.plType) { + config.rtp.fec = recv_codecs_[i].fec; + uint32 rtx_ssrc; + if (recv_codecs_[i].rtx_payload_type != -1 && + sp.GetFidSsrc(ssrc, &rtx_ssrc)) { + config.rtp.rtx[codec.plType].ssrc = rtx_ssrc; + config.rtp.rtx[codec.plType].payload_type = + recv_codecs_[i].rtx_payload_type; + } + break; + } + } + } + + webrtc::VideoReceiveStream* receive_stream = + call_->CreateVideoReceiveStream(config); + assert(receive_stream != NULL); + + receive_streams_[ssrc] = receive_stream; + receive_stream->Start(); + + return true; +} + +bool WebRtcVideoChannel2::RemoveRecvStream(uint32 ssrc) { + LOG(LS_INFO) << "RemoveRecvStream: " << ssrc; + if (ssrc == 0) { + ssrc = default_recv_ssrc_; + } + + std::map<uint32, webrtc::VideoReceiveStream*>::iterator stream = + receive_streams_.find(ssrc); + if (stream == receive_streams_.end()) { + LOG(LS_ERROR) << "Stream not found for ssrc: " << ssrc; + return false; + } + call_->DestroyVideoReceiveStream(stream->second); + receive_streams_.erase(stream); + + std::map<uint32, WebRtcVideoRenderer*>::iterator renderer = + renderers_.find(ssrc); + assert(renderer != renderers_.end()); + delete renderer->second; + renderers_.erase(renderer); + + if (ssrc == default_recv_ssrc_) { + default_recv_ssrc_ = 0; + } + + return true; +} + +bool WebRtcVideoChannel2::SetRenderer(uint32 ssrc, VideoRenderer* renderer) { + LOG(LS_INFO) << "SetRenderer: ssrc:" << ssrc << " " + << (renderer ? "(ptr)" : "NULL"); + bool is_default_ssrc = false; + if (ssrc == 0) { + is_default_ssrc = true; + ssrc = default_recv_ssrc_; + default_renderer_ = renderer; + } + + std::map<uint32, WebRtcVideoRenderer*>::iterator it = renderers_.find(ssrc); + if (it == renderers_.end()) { + return is_default_ssrc; + } + + it->second->SetRenderer(renderer); + return true; +} + +bool WebRtcVideoChannel2::GetRenderer(uint32 ssrc, VideoRenderer** renderer) { + if (ssrc == 0) { + if (default_renderer_ == NULL) { + return false; + } + *renderer = default_renderer_; + return true; + } + + std::map<uint32, WebRtcVideoRenderer*>::iterator it = renderers_.find(ssrc); + if (it == renderers_.end()) { + return false; + } + *renderer = it->second->GetRenderer(); + return true; +} + +bool WebRtcVideoChannel2::GetStats(const StatsOptions& options, + VideoMediaInfo* info) { + // TODO(pbos): Implement. + return true; +} + +bool WebRtcVideoChannel2::SetCapturer(uint32 ssrc, VideoCapturer* capturer) { + LOG(LS_INFO) << "SetCapturer: " << ssrc << " -> " + << (capturer != NULL ? "(capturer)" : "NULL"); + assert(ssrc != 0); + if (send_streams_.find(ssrc) == send_streams_.end()) { + LOG(LS_ERROR) << "No sending stream on ssrc " << ssrc; + return false; + } + return send_streams_[ssrc]->SetCapturer(capturer); +} + +bool WebRtcVideoChannel2::SendIntraFrame() { + // TODO(pbos): Implement. + LOG(LS_VERBOSE) << "SendIntraFrame()."; + return true; +} + +bool WebRtcVideoChannel2::RequestIntraFrame() { + // TODO(pbos): Implement. + LOG(LS_VERBOSE) << "SendIntraFrame()."; + return true; +} + +void WebRtcVideoChannel2::OnPacketReceived( + talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) { + const webrtc::PacketReceiver::DeliveryStatus delivery_result = + call_->Receiver()->DeliverPacket( + reinterpret_cast<const uint8_t*>(packet->data()), packet->length()); + switch (delivery_result) { + case webrtc::PacketReceiver::DELIVERY_OK: + return; + case webrtc::PacketReceiver::DELIVERY_PACKET_ERROR: + return; + case webrtc::PacketReceiver::DELIVERY_UNKNOWN_SSRC: + break; + } + + uint32 ssrc = 0; + if (default_recv_ssrc_ != 0) { // Already one default stream. + LOG(LS_WARNING) << "Unknown SSRC, but default receive stream already set."; + return; + } + + if (!GetRtpSsrc(packet->data(), packet->length(), &ssrc)) { + return; + } + + StreamParams sp; + sp.ssrcs.push_back(ssrc); + LOG(LS_INFO) << "Creating default receive stream for SSRC=" << ssrc << "."; + AddRecvStream(sp); + + if (call_->Receiver()->DeliverPacket( + reinterpret_cast<const uint8_t*>(packet->data()), packet->length()) != + webrtc::PacketReceiver::DELIVERY_OK) { + LOG(LS_WARNING) << "Failed to deliver RTP packet after creating default " + "receiver."; + return; + } +} + +void WebRtcVideoChannel2::OnRtcpReceived( + talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) { + if (call_->Receiver()->DeliverPacket( + reinterpret_cast<const uint8_t*>(packet->data()), packet->length()) != + webrtc::PacketReceiver::DELIVERY_OK) { + LOG(LS_WARNING) << "Failed to deliver RTCP packet."; + } +} + +void WebRtcVideoChannel2::OnReadyToSend(bool ready) { + LOG(LS_VERBOSE) << "OnReadySend: " << (ready ? "Ready." : "Not ready."); +} + +bool WebRtcVideoChannel2::MuteStream(uint32 ssrc, bool mute) { + LOG(LS_VERBOSE) << "MuteStream: " << ssrc << " -> " + << (mute ? "mute" : "unmute"); + assert(ssrc != 0); + if (send_streams_.find(ssrc) == send_streams_.end()) { + LOG(LS_ERROR) << "No sending stream on ssrc " << ssrc; + return false; + } + return send_streams_[ssrc]->MuteStream(mute); +} + +bool WebRtcVideoChannel2::SetRecvRtpHeaderExtensions( + const std::vector<RtpHeaderExtension>& extensions) { + LOG(LS_INFO) << "SetRecvRtpHeaderExtensions: " + << RtpExtensionsToString(extensions); + std::vector<webrtc::RtpExtension> webrtc_extensions; + for (size_t i = 0; i < extensions.size(); ++i) { + // TODO(pbos): Make sure we don't pass unsupported extensions! + webrtc::RtpExtension webrtc_extension(extensions[i].uri.c_str(), + extensions[i].id); + webrtc_extensions.push_back(webrtc_extension); + } + recv_rtp_extensions_ = webrtc_extensions; + return true; +} + +bool WebRtcVideoChannel2::SetSendRtpHeaderExtensions( + const std::vector<RtpHeaderExtension>& extensions) { + LOG(LS_INFO) << "SetSendRtpHeaderExtensions: " + << RtpExtensionsToString(extensions); + std::vector<webrtc::RtpExtension> webrtc_extensions; + for (size_t i = 0; i < extensions.size(); ++i) { + // TODO(pbos): Make sure we don't pass unsupported extensions! + webrtc::RtpExtension webrtc_extension(extensions[i].uri.c_str(), + extensions[i].id); + webrtc_extensions.push_back(webrtc_extension); + } + send_rtp_extensions_ = webrtc_extensions; + return true; +} + +bool WebRtcVideoChannel2::SetStartSendBandwidth(int bps) { + // TODO(pbos): Implement. + LOG(LS_VERBOSE) << "SetStartSendBandwidth: " << bps; + return true; +} + +bool WebRtcVideoChannel2::SetMaxSendBandwidth(int bps) { + // TODO(pbos): Implement. + LOG(LS_VERBOSE) << "SetMaxSendBandwidth: " << bps; + return true; +} + +bool WebRtcVideoChannel2::SetOptions(const VideoOptions& options) { + LOG(LS_VERBOSE) << "SetOptions: " << options.ToString(); + options_.SetAll(options); + return true; +} + +void WebRtcVideoChannel2::SetInterface(NetworkInterface* iface) { + MediaChannel::SetInterface(iface); + // Set the RTP recv/send buffer to a bigger size + MediaChannel::SetOption(NetworkInterface::ST_RTP, + talk_base::Socket::OPT_RCVBUF, + kVideoRtpBufferSize); + + // TODO(sriniv): Remove or re-enable this. + // As part of b/8030474, send-buffer is size now controlled through + // portallocator flags. + // network_interface_->SetOption(NetworkInterface::ST_RTP, + // talk_base::Socket::OPT_SNDBUF, + // kVideoRtpBufferSize); +} + +void WebRtcVideoChannel2::UpdateAspectRatio(int ratio_w, int ratio_h) { + // TODO(pbos): Implement. +} + +void WebRtcVideoChannel2::OnMessage(talk_base::Message* msg) { + // Ignored. +} + +bool WebRtcVideoChannel2::SendRtp(const uint8_t* data, size_t len) { + talk_base::Buffer packet(data, len, kMaxRtpPacketLen); + return MediaChannel::SendPacket(&packet); +} + +bool WebRtcVideoChannel2::SendRtcp(const uint8_t* data, size_t len) { + talk_base::Buffer packet(data, len, kMaxRtpPacketLen); + return MediaChannel::SendRtcp(&packet); +} + +void WebRtcVideoChannel2::StartAllSendStreams() { + for (std::map<uint32, WebRtcVideoSendStream*>::iterator it = + send_streams_.begin(); + it != send_streams_.end(); + ++it) { + it->second->Start(); + } +} + +void WebRtcVideoChannel2::StopAllSendStreams() { + for (std::map<uint32, WebRtcVideoSendStream*>::iterator it = + send_streams_.begin(); + it != send_streams_.end(); + ++it) { + it->second->Stop(); + } +} + +void WebRtcVideoChannel2::SetCodecForAllSendStreams( + const WebRtcVideoChannel2::VideoCodecSettings& codec) { + for (std::map<uint32, WebRtcVideoSendStream*>::iterator it = + send_streams_.begin(); + it != send_streams_.end(); + ++it) { + assert(it->second != NULL); + it->second->SetCodec(options_, codec); + } +} + +WebRtcVideoChannel2::WebRtcVideoSendStream::VideoSendStreamParameters:: + VideoSendStreamParameters( + const webrtc::VideoSendStream::Config& config, + const VideoOptions& options, + const VideoCodec& codec, + const std::vector<webrtc::VideoStream>& video_streams) + : config(config), + options(options), + codec(codec), + video_streams(video_streams) { +} + +WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream( + webrtc::Call* call, + const webrtc::VideoSendStream::Config& config, + const VideoOptions& options, + const VideoCodec& codec, + const std::vector<webrtc::VideoStream>& video_streams, + WebRtcVideoEncoderFactory2* encoder_factory) + : call_(call), + parameters_(config, options, codec, video_streams), + encoder_factory_(encoder_factory), + capturer_(NULL), + stream_(NULL), + sending_(false), + muted_(false), + format_(static_cast<int>(video_streams.back().height), + static_cast<int>(video_streams.back().width), + VideoFormat::FpsToInterval(video_streams.back().max_framerate), + FOURCC_I420) { + RecreateWebRtcStream(); +} + +WebRtcVideoChannel2::WebRtcVideoSendStream::~WebRtcVideoSendStream() { + DisconnectCapturer(); + call_->DestroyVideoSendStream(stream_); + delete parameters_.config.encoder_settings.encoder; +} + +static void SetWebRtcFrameToBlack(webrtc::I420VideoFrame* video_frame) { + assert(video_frame != NULL); + memset(video_frame->buffer(webrtc::kYPlane), + 16, + video_frame->allocated_size(webrtc::kYPlane)); + memset(video_frame->buffer(webrtc::kUPlane), + 128, + video_frame->allocated_size(webrtc::kUPlane)); + memset(video_frame->buffer(webrtc::kVPlane), + 128, + video_frame->allocated_size(webrtc::kVPlane)); +} + +static void CreateBlackFrame(webrtc::I420VideoFrame* video_frame, + int width, + int height) { + video_frame->CreateEmptyFrame( + width, height, width, (width + 1) / 2, (width + 1) / 2); + SetWebRtcFrameToBlack(video_frame); +} + +static void ConvertToI420VideoFrame(const VideoFrame& frame, + webrtc::I420VideoFrame* i420_frame) { + i420_frame->CreateFrame( + static_cast<int>(frame.GetYPitch() * frame.GetHeight()), + frame.GetYPlane(), + static_cast<int>(frame.GetUPitch() * ((frame.GetHeight() + 1) / 2)), + frame.GetUPlane(), + static_cast<int>(frame.GetVPitch() * ((frame.GetHeight() + 1) / 2)), + frame.GetVPlane(), + static_cast<int>(frame.GetWidth()), + static_cast<int>(frame.GetHeight()), + static_cast<int>(frame.GetYPitch()), + static_cast<int>(frame.GetUPitch()), + static_cast<int>(frame.GetVPitch())); +} + +void WebRtcVideoChannel2::WebRtcVideoSendStream::InputFrame( + VideoCapturer* capturer, + const VideoFrame* frame) { + LOG(LS_VERBOSE) << "InputFrame: " << frame->GetWidth() << "x" + << frame->GetHeight(); + bool is_screencast = capturer->IsScreencast(); + // Lock before copying, can be called concurrently when swapping input source. + talk_base::CritScope frame_cs(&frame_lock_); + if (!muted_) { + ConvertToI420VideoFrame(*frame, &video_frame_); + } else { + // Create a tiny black frame to transmit instead. + CreateBlackFrame(&video_frame_, 1, 1); + is_screencast = false; + } + talk_base::CritScope cs(&lock_); + if (format_.width == 0) { // Dropping frames. + assert(format_.height == 0); + LOG(LS_VERBOSE) << "VideoFormat 0x0 set, Dropping frame."; + return; + } + // Reconfigure codec if necessary. + if (is_screencast) { + SetDimensions(video_frame_.width(), video_frame_.height()); + } + LOG(LS_VERBOSE) << "SwapFrame: " << video_frame_.width() << "x" + << video_frame_.height() << " -> (codec) " + << parameters_.video_streams.back().width << "x" + << parameters_.video_streams.back().height; + stream_->Input()->SwapFrame(&video_frame_); +} + +bool WebRtcVideoChannel2::WebRtcVideoSendStream::SetCapturer( + VideoCapturer* capturer) { + if (!DisconnectCapturer() && capturer == NULL) { + return false; + } + + { + talk_base::CritScope cs(&lock_); + + if (capturer == NULL) { + LOG(LS_VERBOSE) << "Disabling capturer, sending black frame."; + webrtc::I420VideoFrame black_frame; + + int width = format_.width; + int height = format_.height; + int half_width = (width + 1) / 2; + black_frame.CreateEmptyFrame( + width, height, width, half_width, half_width); + SetWebRtcFrameToBlack(&black_frame); + SetDimensions(width, height); + stream_->Input()->SwapFrame(&black_frame); + + capturer_ = NULL; + return true; + } + + capturer_ = capturer; + } + // Lock cannot be held while connecting the capturer to prevent lock-order + // violations. + capturer->SignalVideoFrame.connect(this, &WebRtcVideoSendStream::InputFrame); + return true; +} + +bool WebRtcVideoChannel2::WebRtcVideoSendStream::SetVideoFormat( + const VideoFormat& format) { + if ((format.width == 0 || format.height == 0) && + format.width != format.height) { + LOG(LS_ERROR) << "Can't set VideoFormat, width or height is zero (but not " + "both, 0x0 drops frames)."; + return false; + } + + talk_base::CritScope cs(&lock_); + if (format.width == 0 && format.height == 0) { + LOG(LS_INFO) + << "0x0 resolution selected. Captured frames will be dropped for ssrc: " + << parameters_.config.rtp.ssrcs[0] << "."; + } else { + // TODO(pbos): Fix me, this only affects the last stream! + parameters_.video_streams.back().max_framerate = + VideoFormat::IntervalToFps(format.interval); + SetDimensions(format.width, format.height); + } + + format_ = format; + return true; +} + +bool WebRtcVideoChannel2::WebRtcVideoSendStream::MuteStream(bool mute) { + talk_base::CritScope cs(&lock_); + bool was_muted = muted_; + muted_ = mute; + return was_muted != mute; +} + +bool WebRtcVideoChannel2::WebRtcVideoSendStream::DisconnectCapturer() { + talk_base::CritScope cs(&lock_); + if (capturer_ == NULL) { + return false; + } + capturer_->SignalVideoFrame.disconnect(this); + capturer_ = NULL; + return true; +} + +void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodec( + const VideoOptions& options, + const VideoCodecSettings& codec) { + talk_base::CritScope cs(&lock_); + + std::vector<webrtc::VideoStream> video_streams = + encoder_factory_->CreateVideoStreams( + codec.codec, options, parameters_.video_streams.size()); + if (video_streams.empty()) { + return; + } + parameters_.video_streams = video_streams; + format_ = VideoFormat(codec.codec.width, + codec.codec.height, + VideoFormat::FpsToInterval(30), + FOURCC_I420); + + webrtc::VideoEncoder* old_encoder = + parameters_.config.encoder_settings.encoder; + parameters_.config.encoder_settings.encoder = + encoder_factory_->CreateVideoEncoder(codec.codec, options); + parameters_.config.rtp.fec = codec.fec; + // TODO(pbos): Should changing RTX payload type be allowed? + parameters_.codec = codec.codec; + parameters_.options = options; + RecreateWebRtcStream(); + delete old_encoder; +} + +void WebRtcVideoChannel2::WebRtcVideoSendStream::SetDimensions(int width, + int height) { + assert(!parameters_.video_streams.empty()); + LOG(LS_VERBOSE) << "SetDimensions: " << width << "x" << height; + if (parameters_.video_streams.back().width == width && + parameters_.video_streams.back().height == height) { + return; + } + + // TODO(pbos): Fix me, this only affects the last stream! + parameters_.video_streams.back().width = width; + parameters_.video_streams.back().height = height; + + // TODO(pbos): Wire up encoder_parameters, webrtc:3424. + if (!stream_->ReconfigureVideoEncoder(parameters_.video_streams, NULL)) { + LOG(LS_WARNING) << "Failed to reconfigure video encoder for dimensions: " + << width << "x" << height; + return; + } +} + +void WebRtcVideoChannel2::WebRtcVideoSendStream::Start() { + talk_base::CritScope cs(&lock_); + stream_->Start(); + sending_ = true; +} + +void WebRtcVideoChannel2::WebRtcVideoSendStream::Stop() { + talk_base::CritScope cs(&lock_); + stream_->Stop(); + sending_ = false; +} + +void WebRtcVideoChannel2::WebRtcVideoSendStream::RecreateWebRtcStream() { + if (stream_ != NULL) { + call_->DestroyVideoSendStream(stream_); + } + + // TODO(pbos): Wire up encoder_parameters, webrtc:3424. + stream_ = call_->CreateVideoSendStream( + parameters_.config, parameters_.video_streams, NULL); + if (sending_) { + stream_->Start(); + } +} + +WebRtcVideoChannel2::VideoCodecSettings::VideoCodecSettings() + : rtx_payload_type(-1) {} + +std::vector<WebRtcVideoChannel2::VideoCodecSettings> +WebRtcVideoChannel2::MapCodecs(const std::vector<VideoCodec>& codecs) { + assert(!codecs.empty()); + + std::vector<VideoCodecSettings> video_codecs; + std::map<int, bool> payload_used; + std::map<int, VideoCodec::CodecType> payload_codec_type; + std::map<int, int> rtx_mapping; // video payload type -> rtx payload type. + + webrtc::FecConfig fec_settings; + + for (size_t i = 0; i < codecs.size(); ++i) { + const VideoCodec& in_codec = codecs[i]; + int payload_type = in_codec.id; + + if (payload_used[payload_type]) { + LOG(LS_ERROR) << "Payload type already registered: " + << in_codec.ToString(); + return std::vector<VideoCodecSettings>(); + } + payload_used[payload_type] = true; + payload_codec_type[payload_type] = in_codec.GetCodecType(); + + switch (in_codec.GetCodecType()) { + case VideoCodec::CODEC_RED: { + // RED payload type, should not have duplicates. + assert(fec_settings.red_payload_type == -1); + fec_settings.red_payload_type = in_codec.id; + continue; + } + + case VideoCodec::CODEC_ULPFEC: { + // ULPFEC payload type, should not have duplicates. + assert(fec_settings.ulpfec_payload_type == -1); + fec_settings.ulpfec_payload_type = in_codec.id; + continue; + } + + case VideoCodec::CODEC_RTX: { + int associated_payload_type; + if (!in_codec.GetParam(kCodecParamAssociatedPayloadType, + &associated_payload_type)) { + LOG(LS_ERROR) << "RTX codec without associated payload type: " + << in_codec.ToString(); + return std::vector<VideoCodecSettings>(); + } + rtx_mapping[associated_payload_type] = in_codec.id; + continue; + } + + case VideoCodec::CODEC_VIDEO: + break; + } + + video_codecs.push_back(VideoCodecSettings()); + video_codecs.back().codec = in_codec; + } + + // One of these codecs should have been a video codec. Only having FEC + // parameters into this code is a logic error. + assert(!video_codecs.empty()); + + for (std::map<int, int>::const_iterator it = rtx_mapping.begin(); + it != rtx_mapping.end(); + ++it) { + if (!payload_used[it->first]) { + LOG(LS_ERROR) << "RTX mapped to payload not in codec list."; + return std::vector<VideoCodecSettings>(); + } + if (payload_codec_type[it->first] != VideoCodec::CODEC_VIDEO) { + LOG(LS_ERROR) << "RTX not mapped to regular video codec."; + return std::vector<VideoCodecSettings>(); + } + } + + // TODO(pbos): Write tests that figure out that I have not verified that RTX + // codecs aren't mapped to bogus payloads. + for (size_t i = 0; i < video_codecs.size(); ++i) { + video_codecs[i].fec = fec_settings; + if (rtx_mapping[video_codecs[i].codec.id] != 0) { + video_codecs[i].rtx_payload_type = rtx_mapping[video_codecs[i].codec.id]; + } + } + + return video_codecs; +} + +std::vector<WebRtcVideoChannel2::VideoCodecSettings> +WebRtcVideoChannel2::FilterSupportedCodecs( + const std::vector<WebRtcVideoChannel2::VideoCodecSettings>& mapped_codecs) { + std::vector<VideoCodecSettings> supported_codecs; + for (size_t i = 0; i < mapped_codecs.size(); ++i) { + if (encoder_factory_->SupportsCodec(mapped_codecs[i].codec)) { + supported_codecs.push_back(mapped_codecs[i]); + } + } + return supported_codecs; +} + +} // namespace cricket + +#endif // HAVE_WEBRTC_VIDEO diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2.h b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2.h new file mode 100644 index 00000000000..81466eb6579 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2.h @@ -0,0 +1,367 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_H_ +#define TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_H_ + +#include <map> +#include <vector> +#include <string> + +#include "talk/base/cpumonitor.h" +#include "talk/base/scoped_ptr.h" +#include "talk/media/base/mediaengine.h" +#include "talk/media/webrtc/webrtcvideochannelfactory.h" +#include "webrtc/common_video/interface/i420_video_frame.h" +#include "webrtc/system_wrappers/interface/thread_annotations.h" +#include "webrtc/transport.h" +#include "webrtc/video_renderer.h" +#include "webrtc/video_send_stream.h" + +namespace webrtc { +class Call; +class VideoCaptureModule; +class VideoDecoder; +class VideoEncoder; +class VideoRender; +class VideoSendStreamInput; +class VideoReceiveStream; +} + +namespace talk_base { +class CpuMonitor; +class Thread; +} // namespace talk_base + +namespace cricket { + +class VideoCapturer; +class VideoFrame; +class VideoProcessor; +class VideoRenderer; +class VoiceMediaChannel; +class WebRtcVideoChannel2; +class WebRtcDecoderObserver; +class WebRtcEncoderObserver; +class WebRtcLocalStreamInfo; +class WebRtcRenderAdapter; +class WebRtcVideoChannelRecvInfo; +class WebRtcVideoChannelSendInfo; +class WebRtcVideoDecoderFactory; +class WebRtcVoiceEngine; + +struct CapturedFrame; +struct Device; + +class WebRtcVideoEngine2; +class WebRtcVideoChannel2; + +class WebRtcVideoEncoderFactory2 { + public: + virtual ~WebRtcVideoEncoderFactory2(); + virtual std::vector<webrtc::VideoStream> CreateVideoStreams( + const VideoCodec& codec, + const VideoOptions& options, + size_t num_streams); + + virtual webrtc::VideoEncoder* CreateVideoEncoder( + const VideoCodec& codec, + const VideoOptions& options); + + virtual bool SupportsCodec(const cricket::VideoCodec& codec); +}; + +// WebRtcVideoEngine2 is used for the new native WebRTC Video API (webrtc:1667). +class WebRtcVideoEngine2 : public sigslot::has_slots<> { + public: + // Creates the WebRtcVideoEngine2 with internal VideoCaptureModule. + WebRtcVideoEngine2(); + // Custom WebRtcVideoChannelFactory for testing purposes. + explicit WebRtcVideoEngine2(WebRtcVideoChannelFactory* channel_factory); + ~WebRtcVideoEngine2(); + + // Basic video engine implementation. + bool Init(talk_base::Thread* worker_thread); + void Terminate(); + + int GetCapabilities(); + bool SetOptions(const VideoOptions& options); + bool SetDefaultEncoderConfig(const VideoEncoderConfig& config); + VideoEncoderConfig GetDefaultEncoderConfig() const; + + WebRtcVideoChannel2* CreateChannel(VoiceMediaChannel* voice_channel); + + const std::vector<VideoCodec>& codecs() const; + const std::vector<RtpHeaderExtension>& rtp_header_extensions() const; + void SetLogging(int min_sev, const char* filter); + + bool EnableTimedRender(); + // No-op, never used. + bool SetLocalRenderer(VideoRenderer* renderer); + // This is currently ignored. + sigslot::repeater2<VideoCapturer*, CaptureState> SignalCaptureStateChange; + + // Set the VoiceEngine for A/V sync. This can only be called before Init. + bool SetVoiceEngine(WebRtcVoiceEngine* voice_engine); + + // Functions called by WebRtcVideoChannel2. + const VideoFormat& default_codec_format() const { + return default_codec_format_; + } + + bool FindCodec(const VideoCodec& in); + bool CanSendCodec(const VideoCodec& in, + const VideoCodec& current, + VideoCodec* out); + // Check whether the supplied trace should be ignored. + bool ShouldIgnoreTrace(const std::string& trace); + + VideoFormat GetStartCaptureFormat() const { return default_codec_format_; } + + talk_base::CpuMonitor* cpu_monitor() { return cpu_monitor_.get(); } + + virtual WebRtcVideoEncoderFactory2* GetVideoEncoderFactory(); + + private: + void Construct(WebRtcVideoChannelFactory* channel_factory, + WebRtcVoiceEngine* voice_engine, + talk_base::CpuMonitor* cpu_monitor); + + talk_base::Thread* worker_thread_; + WebRtcVoiceEngine* voice_engine_; + std::vector<VideoCodec> video_codecs_; + std::vector<RtpHeaderExtension> rtp_header_extensions_; + VideoFormat default_codec_format_; + + bool initialized_; + + bool capture_started_; + + // Critical section to protect the media processor register/unregister + // while processing a frame + talk_base::CriticalSection signal_media_critical_; + + talk_base::scoped_ptr<talk_base::CpuMonitor> cpu_monitor_; + WebRtcVideoChannelFactory* channel_factory_; + WebRtcVideoEncoderFactory2 default_video_encoder_factory_; +}; + +// Adapter between webrtc::VideoRenderer and cricket::VideoRenderer. +// The webrtc::VideoRenderer is set once, whereas the cricket::VideoRenderer can +// be set after initialization. This adapter will also convert the incoming +// webrtc::I420VideoFrame to a frame type that cricket::VideoRenderer can +// render. +class WebRtcVideoRenderer : public webrtc::VideoRenderer { + public: + WebRtcVideoRenderer(); + + virtual void RenderFrame(const webrtc::I420VideoFrame& frame, + int time_to_render_ms) OVERRIDE; + + void SetRenderer(cricket::VideoRenderer* renderer); + cricket::VideoRenderer* GetRenderer(); + + private: + void SetSize(int width, int height); + int last_width_; + int last_height_; + talk_base::CriticalSection lock_; + cricket::VideoRenderer* renderer_ GUARDED_BY(lock_); +}; + +class WebRtcVideoChannel2 : public talk_base::MessageHandler, + public VideoMediaChannel, + public webrtc::newapi::Transport { + public: + WebRtcVideoChannel2(WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel, + WebRtcVideoEncoderFactory2* encoder_factory); + // For testing purposes insert a pre-constructed call to verify that + // WebRtcVideoChannel2 calls the correct corresponding methods. + WebRtcVideoChannel2(webrtc::Call* call, + WebRtcVideoEngine2* engine, + WebRtcVideoEncoderFactory2* encoder_factory); + ~WebRtcVideoChannel2(); + bool Init(); + + // VideoMediaChannel implementation + virtual bool SetRecvCodecs(const std::vector<VideoCodec>& codecs) OVERRIDE; + virtual bool SetSendCodecs(const std::vector<VideoCodec>& codecs) OVERRIDE; + virtual bool GetSendCodec(VideoCodec* send_codec) OVERRIDE; + virtual bool SetSendStreamFormat(uint32 ssrc, + const VideoFormat& format) OVERRIDE; + virtual bool SetRender(bool render) OVERRIDE; + virtual bool SetSend(bool send) OVERRIDE; + + virtual bool AddSendStream(const StreamParams& sp) OVERRIDE; + virtual bool RemoveSendStream(uint32 ssrc) OVERRIDE; + virtual bool AddRecvStream(const StreamParams& sp) OVERRIDE; + virtual bool RemoveRecvStream(uint32 ssrc) OVERRIDE; + virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer) OVERRIDE; + virtual bool GetStats(const StatsOptions& options, + VideoMediaInfo* info) OVERRIDE; + virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer) OVERRIDE; + virtual bool SendIntraFrame() OVERRIDE; + virtual bool RequestIntraFrame() OVERRIDE; + + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) + OVERRIDE; + virtual void OnRtcpReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) + OVERRIDE; + virtual void OnReadyToSend(bool ready) OVERRIDE; + virtual bool MuteStream(uint32 ssrc, bool mute) OVERRIDE; + + // Set send/receive RTP header extensions. This must be done before creating + // streams as it only has effect on future streams. + virtual bool SetRecvRtpHeaderExtensions( + const std::vector<RtpHeaderExtension>& extensions) OVERRIDE; + virtual bool SetSendRtpHeaderExtensions( + const std::vector<RtpHeaderExtension>& extensions) OVERRIDE; + virtual bool SetStartSendBandwidth(int bps) OVERRIDE; + virtual bool SetMaxSendBandwidth(int bps) OVERRIDE; + virtual bool SetOptions(const VideoOptions& options) OVERRIDE; + virtual bool GetOptions(VideoOptions* options) const OVERRIDE { + *options = options_; + return true; + } + virtual void SetInterface(NetworkInterface* iface) OVERRIDE; + virtual void UpdateAspectRatio(int ratio_w, int ratio_h) OVERRIDE; + + virtual void OnMessage(talk_base::Message* msg) OVERRIDE; + + // Implemented for VideoMediaChannelTest. + bool sending() const { return sending_; } + uint32 GetDefaultChannelSsrc() { return default_send_ssrc_; } + bool GetRenderer(uint32 ssrc, VideoRenderer** renderer); + + private: + struct VideoCodecSettings { + VideoCodecSettings(); + + VideoCodec codec; + webrtc::FecConfig fec; + int rtx_payload_type; + }; + + class WebRtcVideoSendStream : public sigslot::has_slots<> { + public: + WebRtcVideoSendStream(webrtc::Call* call, + const webrtc::VideoSendStream::Config& config, + const VideoOptions& options, + const VideoCodec& codec, + const std::vector<webrtc::VideoStream>& video_streams, + WebRtcVideoEncoderFactory2* encoder_factory); + ~WebRtcVideoSendStream(); + void SetCodec(const VideoOptions& options, const VideoCodecSettings& codec); + + void InputFrame(VideoCapturer* capturer, const VideoFrame* frame); + bool SetCapturer(VideoCapturer* capturer); + bool SetVideoFormat(const VideoFormat& format); + bool MuteStream(bool mute); + bool DisconnectCapturer(); + + void Start(); + void Stop(); + + private: + // Parameters needed to reconstruct the underlying stream. + // webrtc::VideoSendStream doesn't support setting a lot of options on the + // fly, so when those need to be changed we tear down and reconstruct with + // similar parameters depending on which options changed etc. + struct VideoSendStreamParameters { + VideoSendStreamParameters( + const webrtc::VideoSendStream::Config& config, + const VideoOptions& options, + const VideoCodec& codec, + const std::vector<webrtc::VideoStream>& video_streams); + webrtc::VideoSendStream::Config config; + VideoOptions options; + VideoCodec codec; + // Sent resolutions + bitrates etc. by the underlying VideoSendStream, + // typically changes when setting a new resolution or reconfiguring + // bitrates. + std::vector<webrtc::VideoStream> video_streams; + }; + + void RecreateWebRtcStream(); + void SetDimensions(int width, int height); + + webrtc::Call* const call_; + WebRtcVideoEncoderFactory2* const encoder_factory_; + + talk_base::CriticalSection lock_; + webrtc::VideoSendStream* stream_ GUARDED_BY(lock_); + VideoSendStreamParameters parameters_ GUARDED_BY(lock_); + + VideoCapturer* capturer_ GUARDED_BY(lock_); + bool sending_ GUARDED_BY(lock_); + bool muted_ GUARDED_BY(lock_); + VideoFormat format_ GUARDED_BY(lock_); + + talk_base::CriticalSection frame_lock_; + webrtc::I420VideoFrame video_frame_ GUARDED_BY(frame_lock_); + }; + + void Construct(webrtc::Call* call, WebRtcVideoEngine2* engine); + + virtual bool SendRtp(const uint8_t* data, size_t len) OVERRIDE; + virtual bool SendRtcp(const uint8_t* data, size_t len) OVERRIDE; + + void StartAllSendStreams(); + void StopAllSendStreams(); + void SetCodecForAllSendStreams(const VideoCodecSettings& codec); + static std::vector<VideoCodecSettings> MapCodecs( + const std::vector<VideoCodec>& codecs); + std::vector<VideoCodecSettings> FilterSupportedCodecs( + const std::vector<VideoCodecSettings>& mapped_codecs); + + uint32_t rtcp_receiver_report_ssrc_; + bool sending_; + talk_base::scoped_ptr<webrtc::Call> call_; + std::map<uint32, WebRtcVideoRenderer*> renderers_; + VideoRenderer* default_renderer_; + uint32_t default_send_ssrc_; + uint32_t default_recv_ssrc_; + + // Using primary-ssrc (first ssrc) as key. + std::map<uint32, WebRtcVideoSendStream*> send_streams_; + std::map<uint32, webrtc::VideoReceiveStream*> receive_streams_; + + Settable<VideoCodecSettings> send_codec_; + std::vector<webrtc::RtpExtension> send_rtp_extensions_; + + WebRtcVideoEncoderFactory2* const encoder_factory_; + std::vector<VideoCodecSettings> recv_codecs_; + std::vector<webrtc::RtpExtension> recv_rtp_extensions_; + VideoOptions options_; +}; + +} // namespace cricket + +#endif // TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2_unittest.cc new file mode 100644 index 00000000000..6886300234d --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2_unittest.cc @@ -0,0 +1,1367 @@ +/* + * libjingle + * Copyright 2004 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <map> +#include <vector> + +#include "talk/base/gunit.h" +#include "talk/media/base/testutils.h" +#include "talk/media/base/videoengine_unittest.h" +#include "talk/media/webrtc/webrtcvideoengine2.h" +#include "talk/media/webrtc/webrtcvideoengine2_unittest.h" +#include "talk/media/webrtc/webrtcvideochannelfactory.h" + +namespace { +static const cricket::VideoCodec kVp8Codec720p(100, "VP8", 1280, 720, 30, 0); +static const cricket::VideoCodec kVp8Codec360p(100, "VP8", 640, 360, 30, 0); +static const cricket::VideoCodec kVp8Codec270p(100, "VP8", 480, 270, 30, 0); +static const cricket::VideoCodec kVp8Codec180p(100, "VP8", 320, 180, 30, 0); + +static const cricket::VideoCodec kVp8Codec(100, "VP8", 640, 400, 30, 0); +static const cricket::VideoCodec kVp9Codec(101, "VP9", 640, 400, 30, 0); +static const cricket::VideoCodec kRedCodec(116, "red", 0, 0, 0, 0); +static const cricket::VideoCodec kUlpfecCodec(117, "ulpfec", 0, 0, 0, 0); + +static const uint32 kSsrcs1[] = {1}; +static const uint32 kRtxSsrcs1[] = {4}; + +void VerifyCodecHasDefaultFeedbackParams(const cricket::VideoCodec& codec) { + EXPECT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam( + cricket::kRtcpFbParamNack, cricket::kParamValueEmpty))); + EXPECT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam( + cricket::kRtcpFbParamNack, cricket::kRtcpFbNackParamPli))); + EXPECT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam( + cricket::kRtcpFbParamRemb, cricket::kParamValueEmpty))); + EXPECT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam( + cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir))); +} + +} // namespace + +namespace cricket { +FakeVideoSendStream::FakeVideoSendStream( + const webrtc::VideoSendStream::Config& config, + const std::vector<webrtc::VideoStream>& video_streams) + : sending_(false), config_(config), video_streams_(video_streams) { +} + +webrtc::VideoSendStream::Config FakeVideoSendStream::GetConfig() { + return config_; +} + +std::vector<webrtc::VideoStream> FakeVideoSendStream::GetVideoStreams() { + return video_streams_; +} + +bool FakeVideoSendStream::IsSending() { + return sending_; +} + +webrtc::VideoSendStream::Stats FakeVideoSendStream::GetStats() const { + return webrtc::VideoSendStream::Stats(); +} + +bool FakeVideoSendStream::ReconfigureVideoEncoder( + const std::vector<webrtc::VideoStream>& streams, + const void* encoder_specific) { + video_streams_ = streams; + return true; +} + +webrtc::VideoSendStreamInput* FakeVideoSendStream::Input() { + // TODO(pbos): Fix. + return NULL; +} + +void FakeVideoSendStream::Start() { + sending_ = true; +} + +void FakeVideoSendStream::Stop() { + sending_ = false; +} + +FakeVideoReceiveStream::FakeVideoReceiveStream( + const webrtc::VideoReceiveStream::Config& config) + : config_(config), receiving_(false) { +} + +webrtc::VideoReceiveStream::Config FakeVideoReceiveStream::GetConfig() { + return config_; +} + +webrtc::VideoReceiveStream::Stats FakeVideoReceiveStream::GetStats() const { + return webrtc::VideoReceiveStream::Stats(); +} + +void FakeVideoReceiveStream::Start() { + receiving_ = true; +} +void FakeVideoReceiveStream::Stop() { + receiving_ = false; +} +void FakeVideoReceiveStream::GetCurrentReceiveCodec(webrtc::VideoCodec* codec) { +} + +FakeCall::FakeCall() { SetVideoCodecs(GetDefaultVideoCodecs()); } + +FakeCall::~FakeCall() { + EXPECT_EQ(0u, video_send_streams_.size()); + EXPECT_EQ(0u, video_receive_streams_.size()); +} + +void FakeCall::SetVideoCodecs(const std::vector<webrtc::VideoCodec> codecs) { + codecs_ = codecs; +} + +std::vector<FakeVideoSendStream*> FakeCall::GetVideoSendStreams() { + return video_send_streams_; +} + +std::vector<FakeVideoReceiveStream*> FakeCall::GetVideoReceiveStreams() { + return video_receive_streams_; +} + +webrtc::VideoCodec FakeCall::GetEmptyVideoCodec() { + webrtc::VideoCodec codec; + codec.minBitrate = 300; + codec.startBitrate = 800; + codec.maxBitrate = 1500; + codec.maxFramerate = 10; + codec.width = 640; + codec.height = 480; + codec.qpMax = 56; + + return codec; +} + +webrtc::VideoCodec FakeCall::GetVideoCodecVp8() { + webrtc::VideoCodec vp8_codec = GetEmptyVideoCodec(); + vp8_codec.codecType = webrtc::kVideoCodecVP8; + strcpy(vp8_codec.plName, kVp8Codec.name.c_str()); + vp8_codec.plType = kVp8Codec.id; + + return vp8_codec; +} + +webrtc::VideoCodec FakeCall::GetVideoCodecVp9() { + webrtc::VideoCodec vp9_codec = GetEmptyVideoCodec(); + // TODO(pbos): Add a correct codecType when webrtc has one. + vp9_codec.codecType = webrtc::kVideoCodecVP8; + strcpy(vp9_codec.plName, kVp9Codec.name.c_str()); + vp9_codec.plType = kVp9Codec.id; + + return vp9_codec; +} + +std::vector<webrtc::VideoCodec> FakeCall::GetDefaultVideoCodecs() { + std::vector<webrtc::VideoCodec> codecs; + codecs.push_back(GetVideoCodecVp8()); + // codecs.push_back(GetVideoCodecVp9()); + + return codecs; +} + +webrtc::VideoSendStream::Config FakeCall::GetDefaultSendConfig() { + webrtc::VideoSendStream::Config config; + // TODO(pbos): Encoder settings. + // config.codec = GetVideoCodecVp8(); + return config; +} + +webrtc::VideoSendStream* FakeCall::CreateVideoSendStream( + const webrtc::VideoSendStream::Config& config, + const std::vector<webrtc::VideoStream>& video_streams, + const void* encoder_settings) { + FakeVideoSendStream* fake_stream = + new FakeVideoSendStream(config, video_streams); + video_send_streams_.push_back(fake_stream); + return fake_stream; +} + +void FakeCall::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) { + FakeVideoSendStream* fake_stream = + static_cast<FakeVideoSendStream*>(send_stream); + for (size_t i = 0; i < video_send_streams_.size(); ++i) { + if (video_send_streams_[i] == fake_stream) { + delete video_send_streams_[i]; + video_send_streams_.erase(video_send_streams_.begin() + i); + return; + } + } + ADD_FAILURE() << "DestroyVideoSendStream called with unknown paramter."; +} + +webrtc::VideoReceiveStream::Config FakeCall::GetDefaultReceiveConfig() { + return webrtc::VideoReceiveStream::Config(); +} + +webrtc::VideoReceiveStream* FakeCall::CreateVideoReceiveStream( + const webrtc::VideoReceiveStream::Config& config) { + video_receive_streams_.push_back(new FakeVideoReceiveStream(config)); + return video_receive_streams_[video_receive_streams_.size() - 1]; +} + +void FakeCall::DestroyVideoReceiveStream( + webrtc::VideoReceiveStream* receive_stream) { + FakeVideoReceiveStream* fake_stream = + static_cast<FakeVideoReceiveStream*>(receive_stream); + for (size_t i = 0; i < video_receive_streams_.size(); ++i) { + if (video_receive_streams_[i] == fake_stream) { + delete video_receive_streams_[i]; + video_receive_streams_.erase(video_receive_streams_.begin() + i); + return; + } + } + ADD_FAILURE() << "DestroyVideoReceiveStream called with unknown paramter."; +} + +webrtc::PacketReceiver* FakeCall::Receiver() { + // TODO(pbos): Fix this. + return NULL; +} + +uint32_t FakeCall::SendBitrateEstimate() { + return 0; +} + +uint32_t FakeCall::ReceiveBitrateEstimate() { + return 0; +} + +FakeWebRtcVideoChannel2::FakeWebRtcVideoChannel2( + FakeCall* call, + WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel) + : WebRtcVideoChannel2(call, engine, engine->GetVideoEncoderFactory()), + fake_call_(call), + voice_channel_(voice_channel) { +} + +FakeWebRtcVideoChannel2::~FakeWebRtcVideoChannel2() { +} + +VoiceMediaChannel* FakeWebRtcVideoChannel2::GetVoiceChannel() { + return voice_channel_; +} +FakeCall* FakeWebRtcVideoChannel2::GetFakeCall() { + return fake_call_; +} + +FakeWebRtcVideoChannel2* FakeWebRtcVideoMediaChannelFactory::GetFakeChannel( + VideoMediaChannel* channel) { + return channel_map_[channel]; +} + +WebRtcVideoChannel2* FakeWebRtcVideoMediaChannelFactory::Create( + WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel) { + FakeWebRtcVideoChannel2* channel = + new FakeWebRtcVideoChannel2(new FakeCall(), engine, voice_channel); + channel_map_[channel] = channel; + return channel; +} + +class WebRtcVideoEngine2Test : public testing::Test { + public: + WebRtcVideoEngine2Test() : engine_(&factory_) { + std::vector<VideoCodec> engine_codecs = engine_.codecs(); + assert(!engine_codecs.empty()); + bool codec_set = false; + for (size_t i = 0; i < engine_codecs.size(); ++i) { + if (engine_codecs[i].name == "red") { + default_red_codec_ = engine_codecs[i]; + } else if (engine_codecs[i].name == "ulpfec") { + default_ulpfec_codec_ = engine_codecs[i]; + } else if (engine_codecs[i].name == "rtx") { + default_rtx_codec_ = engine_codecs[i]; + } else if (!codec_set) { + default_codec_ = engine_codecs[i]; + codec_set = true; + } + } + + assert(codec_set); + } + + protected: + FakeWebRtcVideoMediaChannelFactory factory_; + WebRtcVideoEngine2 engine_; + VideoCodec default_codec_; + VideoCodec default_red_codec_; + VideoCodec default_ulpfec_codec_; + VideoCodec default_rtx_codec_; +}; + +TEST_F(WebRtcVideoEngine2Test, CreateChannel) { + talk_base::scoped_ptr<VideoMediaChannel> channel(engine_.CreateChannel(NULL)); + ASSERT_TRUE(channel.get() != NULL) << "Could not create channel."; + EXPECT_TRUE(factory_.GetFakeChannel(channel.get()) != NULL) + << "Channel not created through factory."; +} + +TEST_F(WebRtcVideoEngine2Test, CreateChannelWithVoiceEngine) { + VoiceMediaChannel* voice_channel = reinterpret_cast<VoiceMediaChannel*>(0x42); + talk_base::scoped_ptr<VideoMediaChannel> channel( + engine_.CreateChannel(voice_channel)); + ASSERT_TRUE(channel.get() != NULL) << "Could not create channel."; + + FakeWebRtcVideoChannel2* fake_channel = + factory_.GetFakeChannel(channel.get()); + ASSERT_TRUE(fake_channel != NULL) << "Channel not created through factory."; + + EXPECT_TRUE(fake_channel->GetVoiceChannel() != NULL) + << "VoiceChannel not set."; + EXPECT_EQ(voice_channel, fake_channel->GetVoiceChannel()) + << "Different VoiceChannel set than the provided one."; +} + +TEST_F(WebRtcVideoEngine2Test, FindCodec) { + const std::vector<cricket::VideoCodec>& c = engine_.codecs(); + EXPECT_EQ(4U, c.size()); + + cricket::VideoCodec vp8(104, "VP8", 320, 200, 30, 0); + EXPECT_TRUE(engine_.FindCodec(vp8)); + + cricket::VideoCodec vp8_ci(104, "vp8", 320, 200, 30, 0); + EXPECT_TRUE(engine_.FindCodec(vp8)); + + cricket::VideoCodec vp8_diff_fr_diff_pref(104, "VP8", 320, 200, 50, 50); + EXPECT_TRUE(engine_.FindCodec(vp8_diff_fr_diff_pref)); + + cricket::VideoCodec vp8_diff_id(95, "VP8", 320, 200, 30, 0); + EXPECT_FALSE(engine_.FindCodec(vp8_diff_id)); + vp8_diff_id.id = 97; + EXPECT_TRUE(engine_.FindCodec(vp8_diff_id)); + + cricket::VideoCodec vp8_diff_res(104, "VP8", 320, 111, 30, 0); + EXPECT_FALSE(engine_.FindCodec(vp8_diff_res)); + + // PeerConnection doesn't negotiate the resolution at this point. + // Test that FindCodec can handle the case when width/height is 0. + cricket::VideoCodec vp8_zero_res(104, "VP8", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(vp8_zero_res)); + + cricket::VideoCodec red(101, "RED", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(red)); + + cricket::VideoCodec red_ci(101, "red", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(red)); + + cricket::VideoCodec fec(102, "ULPFEC", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(fec)); + + cricket::VideoCodec fec_ci(102, "ulpfec", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(fec)); + + cricket::VideoCodec rtx(96, "rtx", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(rtx)); +} + +TEST_F(WebRtcVideoEngine2Test, DefaultRtxCodecHasAssociatedPayloadTypeSet) { + std::vector<VideoCodec> engine_codecs = engine_.codecs(); + for (size_t i = 0; i < engine_codecs.size(); ++i) { + if (engine_codecs[i].name != kRtxCodecName) + continue; + int associated_payload_type; + EXPECT_TRUE(engine_codecs[i].GetParam(kCodecParamAssociatedPayloadType, + &associated_payload_type)); + EXPECT_EQ(default_codec_.id, associated_payload_type); + return; + } + FAIL() << "No RTX codec found among default codecs."; +} + +TEST_F(WebRtcVideoEngine2Test, SupportsTimestampOffsetHeaderExtension) { + std::vector<RtpHeaderExtension> extensions = engine_.rtp_header_extensions(); + ASSERT_FALSE(extensions.empty()); + for (size_t i = 0; i < extensions.size(); ++i) { + if (extensions[i].uri == kRtpTimestampOffsetHeaderExtension) { + EXPECT_EQ(kRtpTimestampOffsetHeaderExtensionDefaultId, extensions[i].id); + return; + } + } + FAIL() << "Timestamp offset extension not in header-extension list."; +} + +TEST_F(WebRtcVideoEngine2Test, SupportsAbsoluteSenderTimeHeaderExtension) { + std::vector<RtpHeaderExtension> extensions = engine_.rtp_header_extensions(); + ASSERT_FALSE(extensions.empty()); + for (size_t i = 0; i < extensions.size(); ++i) { + if (extensions[i].uri == kRtpAbsoluteSenderTimeHeaderExtension) { + EXPECT_EQ(kRtpAbsoluteSenderTimeHeaderExtensionDefaultId, + extensions[i].id); + return; + } + } + FAIL() << "Absolute Sender Time extension not in header-extension list."; +} + +class WebRtcVideoChannel2BaseTest + : public VideoMediaChannelTest<WebRtcVideoEngine2, WebRtcVideoChannel2> { + protected: + virtual cricket::VideoCodec DefaultCodec() OVERRIDE { return kVp8Codec; } + typedef VideoMediaChannelTest<WebRtcVideoEngine2, WebRtcVideoChannel2> Base; +}; + +// TODO(pbos): Fix WebRtcVideoEngine2BaseTest, where we want CheckCoInitialize. +#if 0 +// TODO(juberti): Figure out why ViE is munging the COM refcount. +#ifdef WIN32 +TEST_F(WebRtcVideoChannel2BaseTest, DISABLED_CheckCoInitialize) { + Base::CheckCoInitialize(); +} +#endif +#endif + +TEST_F(WebRtcVideoChannel2BaseTest, SetSend) { Base::SetSend(); } + +TEST_F(WebRtcVideoChannel2BaseTest, SetSendWithoutCodecs) { + Base::SetSendWithoutCodecs(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, SetSendSetsTransportBufferSizes) { + Base::SetSendSetsTransportBufferSizes(); +} + +// TODO(juberti): Fix this test to tolerate missing stats. +TEST_F(WebRtcVideoChannel2BaseTest, DISABLED_GetStats) { Base::GetStats(); } + +// TODO(juberti): Fix this test to tolerate missing stats. +TEST_F(WebRtcVideoChannel2BaseTest, DISABLED_GetStatsMultipleRecvStreams) { + Base::GetStatsMultipleRecvStreams(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, DISABLED_GetStatsMultipleSendStreams) { + Base::GetStatsMultipleSendStreams(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, SetSendBandwidth) { + Base::SetSendBandwidth(); +} +TEST_F(WebRtcVideoChannel2BaseTest, SetSendSsrc) { Base::SetSendSsrc(); } +TEST_F(WebRtcVideoChannel2BaseTest, SetSendSsrcAfterSetCodecs) { + Base::SetSendSsrcAfterSetCodecs(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, SetRenderer) { Base::SetRenderer(); } + +TEST_F(WebRtcVideoChannel2BaseTest, AddRemoveRecvStreams) { + Base::AddRemoveRecvStreams(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, DISABLED_AddRemoveRecvStreamAndRender) { + Base::AddRemoveRecvStreamAndRender(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, AddRemoveRecvStreamsNoConference) { + Base::AddRemoveRecvStreamsNoConference(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, AddRemoveSendStreams) { + Base::AddRemoveSendStreams(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, SimulateConference) { + Base::SimulateConference(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, AddRemoveCapturer) { + Base::AddRemoveCapturer(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, RemoveCapturerWithoutAdd) { + Base::RemoveCapturerWithoutAdd(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, AddRemoveCapturerMultipleSources) { + Base::AddRemoveCapturerMultipleSources(); +} + +// TODO(pbos): Figure out why this fails so often. +TEST_F(WebRtcVideoChannel2BaseTest, DISABLED_HighAspectHighHeightCapturer) { + Base::HighAspectHighHeightCapturer(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, RejectEmptyStreamParams) { + Base::RejectEmptyStreamParams(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, AdaptResolution16x10) { + Base::AdaptResolution16x10(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, AdaptResolution4x3) { + Base::AdaptResolution4x3(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, MuteStream) { Base::MuteStream(); } + +TEST_F(WebRtcVideoChannel2BaseTest, MultipleSendStreams) { + Base::MultipleSendStreams(); +} + +// TODO(juberti): Restore this test once we support sending 0 fps. +TEST_F(WebRtcVideoChannel2BaseTest, DISABLED_AdaptDropAllFrames) { + Base::AdaptDropAllFrames(); +} +// TODO(juberti): Understand why we get decode errors on this test. +TEST_F(WebRtcVideoChannel2BaseTest, DISABLED_AdaptFramerate) { + Base::AdaptFramerate(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, SetSendStreamFormat0x0) { + Base::SetSendStreamFormat0x0(); +} + +// TODO(zhurunz): Fix the flakey test. +TEST_F(WebRtcVideoChannel2BaseTest, DISABLED_SetSendStreamFormat) { + Base::SetSendStreamFormat(); +} + +TEST_F(WebRtcVideoChannel2BaseTest, TwoStreamsSendAndReceive) { + Base::TwoStreamsSendAndReceive(kVp8Codec); +} + +TEST_F(WebRtcVideoChannel2BaseTest, TwoStreamsReUseFirstStream) { + Base::TwoStreamsReUseFirstStream(kVp8Codec); +} + +class WebRtcVideoChannel2Test : public WebRtcVideoEngine2Test { + public: + virtual void SetUp() OVERRIDE { + channel_.reset(engine_.CreateChannel(NULL)); + fake_channel_ = factory_.GetFakeChannel(channel_.get()); + last_ssrc_ = 123; + ASSERT_TRUE(fake_channel_ != NULL) + << "Channel not created through factory."; + } + + protected: + FakeVideoSendStream* AddSendStream() { + return AddSendStream(StreamParams::CreateLegacy(last_ssrc_++)); + } + + FakeVideoSendStream* AddSendStream(const StreamParams& sp) { + size_t num_streams = + fake_channel_->GetFakeCall()->GetVideoSendStreams().size(); + EXPECT_TRUE(channel_->AddSendStream(sp)); + std::vector<FakeVideoSendStream*> streams = + fake_channel_->GetFakeCall()->GetVideoSendStreams(); + EXPECT_EQ(num_streams + 1, streams.size()); + return streams[streams.size() - 1]; + } + + std::vector<FakeVideoSendStream*> GetFakeSendStreams() { + return fake_channel_->GetFakeCall()->GetVideoSendStreams(); + } + + FakeVideoReceiveStream* AddRecvStream() { + return AddRecvStream(StreamParams::CreateLegacy(last_ssrc_++)); + } + + FakeVideoReceiveStream* AddRecvStream(const StreamParams& sp) { + size_t num_streams = + fake_channel_->GetFakeCall()->GetVideoReceiveStreams().size(); + EXPECT_TRUE(channel_->AddRecvStream(sp)); + std::vector<FakeVideoReceiveStream*> streams = + fake_channel_->GetFakeCall()->GetVideoReceiveStreams(); + EXPECT_EQ(num_streams + 1, streams.size()); + return streams[streams.size() - 1]; + } + + void SetSendCodecsShouldWorkForBitrates(const char* min_bitrate, + const char* max_bitrate) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + codecs[0].params[kCodecParamMinBitrate] = min_bitrate; + codecs[0].params[kCodecParamMaxBitrate] = max_bitrate; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + + FakeVideoSendStream* stream = AddSendStream(); + + std::vector<webrtc::VideoStream> video_streams = stream->GetVideoStreams(); + ASSERT_EQ(1u, video_streams.size()); + EXPECT_EQ(atoi(min_bitrate), video_streams.back().min_bitrate_bps / 1000); + EXPECT_EQ(atoi(max_bitrate), video_streams.back().max_bitrate_bps / 1000); + + VideoCodec codec; + EXPECT_TRUE(channel_->GetSendCodec(&codec)); + EXPECT_EQ(min_bitrate, codec.params[kCodecParamMinBitrate]); + EXPECT_EQ(max_bitrate, codec.params[kCodecParamMaxBitrate]); + } + + void ExpectEqualCodecs(const VideoCodec video_codec, + const webrtc::VideoCodec& webrtc_codec) { + EXPECT_STREQ(video_codec.name.c_str(), webrtc_codec.plName); + EXPECT_EQ(video_codec.id, webrtc_codec.plType); + EXPECT_EQ(video_codec.width, webrtc_codec.width); + EXPECT_EQ(video_codec.height, webrtc_codec.height); + EXPECT_EQ(video_codec.framerate, webrtc_codec.maxFramerate); + } + + void TestSetSendRtpHeaderExtensions(const std::string& cricket_ext, + const std::string& webrtc_ext) { + // Enable extension. + const int id = 1; + std::vector<cricket::RtpHeaderExtension> extensions; + extensions.push_back(cricket::RtpHeaderExtension(cricket_ext, id)); + EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); + + FakeVideoSendStream* send_stream = + AddSendStream(cricket::StreamParams::CreateLegacy(123)); + + // Verify the send extension id. + ASSERT_EQ(1u, send_stream->GetConfig().rtp.extensions.size()); + EXPECT_EQ(id, send_stream->GetConfig().rtp.extensions[0].id); + EXPECT_EQ(webrtc_ext, send_stream->GetConfig().rtp.extensions[0].name); + // Verify call with same set of extensions returns true. + EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); + // Verify that SetSendRtpHeaderExtensions doesn't implicitly add them for + // receivers. + EXPECT_TRUE(AddRecvStream(cricket::StreamParams::CreateLegacy(123)) + ->GetConfig() + .rtp.extensions.empty()); + + // Remove the extension id, verify that this doesn't reset extensions as + // they should be set before creating channels. + std::vector<cricket::RtpHeaderExtension> empty_extensions; + EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(empty_extensions)); + EXPECT_FALSE(send_stream->GetConfig().rtp.extensions.empty()); + } + + void TestSetRecvRtpHeaderExtensions(const std::string& cricket_ext, + const std::string& webrtc_ext) { + // Enable extension. + const int id = 1; + std::vector<cricket::RtpHeaderExtension> extensions; + extensions.push_back(cricket::RtpHeaderExtension(cricket_ext, id)); + EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); + + FakeVideoReceiveStream* recv_stream = + AddRecvStream(cricket::StreamParams::CreateLegacy(123)); + + // Verify the recv extension id. + ASSERT_EQ(1u, recv_stream->GetConfig().rtp.extensions.size()); + EXPECT_EQ(id, recv_stream->GetConfig().rtp.extensions[0].id); + EXPECT_EQ(webrtc_ext, recv_stream->GetConfig().rtp.extensions[0].name); + // Verify call with same set of extensions returns true. + EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); + // Verify that SetRecvRtpHeaderExtensions doesn't implicitly add them for + // senders. + EXPECT_TRUE(AddSendStream(cricket::StreamParams::CreateLegacy(123)) + ->GetConfig() + .rtp.extensions.empty()); + + // Remove the extension id, verify that this doesn't reset extensions as + // they should be set before creating channels. + std::vector<cricket::RtpHeaderExtension> empty_extensions; + EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(empty_extensions)); + EXPECT_FALSE(recv_stream->GetConfig().rtp.extensions.empty()); + } + + talk_base::scoped_ptr<VideoMediaChannel> channel_; + FakeWebRtcVideoChannel2* fake_channel_; + uint32 last_ssrc_; +}; + +TEST_F(WebRtcVideoChannel2Test, DISABLED_MaxBitrateResetsWithConferenceMode) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_StartSendBitrate) { + // TODO(pbos): Is this test testing vie_ ? this is confusing. No API to set + // start send bitrate from outside? Add defaults here that should be kept? + std::vector<cricket::VideoCodec> codec_list; + codec_list.push_back(kVp8Codec); + EXPECT_TRUE(channel_->SetSendCodecs(codec_list)); + const unsigned int kVideoMinSendBitrateKbps = 50; + const unsigned int kVideoTargetSendBitrateKbps = 300; + const unsigned int kVideoMaxSendBitrateKbps = 2000; + FakeVideoSendStream* stream = AddSendStream(); + std::vector<webrtc::VideoStream> video_streams = stream->GetVideoStreams(); + ASSERT_EQ(1u, video_streams.size()); + EXPECT_EQ(kVideoMinSendBitrateKbps, + video_streams.back().min_bitrate_bps / 1000); + EXPECT_EQ(kVideoTargetSendBitrateKbps, + video_streams.back().target_bitrate_bps / 1000); + EXPECT_EQ(kVideoMaxSendBitrateKbps, + video_streams.back().max_bitrate_bps / 1000); +#if 0 + // TODO(pbos): un-#if + VerifyVP8SendCodec(send_channel, kVP8Codec.width, kVP8Codec.height, 0, + kVideoMaxSendBitrateKbps, kVideoMinSendBitrateKbps, + kVideoDefaultStartSendBitrateKbps); + EXPECT_EQ(0, vie_.StartSend(send_channel)); + + // Increase the send bitrate and verify it is used as start bitrate. + const unsigned int kVideoSendBitrateBps = 768000; + vie_.SetSendBitrates(send_channel, kVideoSendBitrateBps, 0, 0); + EXPECT_TRUE(channel_->SetSendCodecs(codec_list)); + VerifyVP8SendCodec(send_channel, kVP8Codec.width, kVP8Codec.height, 0, + kVideoMaxSendBitrateKbps, kVideoMinSendBitrateKbps, + kVideoSendBitrateBps / 1000); + + // Never set a start bitrate higher than the max bitrate. + vie_.SetSendBitrates(send_channel, kVideoMaxSendBitrateKbps + 500, 0, 0); + EXPECT_TRUE(channel_->SetSendCodecs(codec_list)); + VerifyVP8SendCodec(send_channel, kVP8Codec.width, kVP8Codec.height, 0, + kVideoMaxSendBitrateKbps, kVideoMinSendBitrateKbps, + kVideoDefaultStartSendBitrateKbps); + + // Use the default start bitrate if the send bitrate is lower. + vie_.SetSendBitrates(send_channel, kVideoDefaultStartSendBitrateKbps - 50, 0, + 0); + EXPECT_TRUE(channel_->SetSendCodecs(codec_list)); + VerifyVP8SendCodec(send_channel, kVP8Codec.width, kVP8Codec.height, 0, + kVideoMaxSendBitrateKbps, kVideoMinSendBitrateKbps, + kVideoDefaultStartSendBitrateKbps); +#endif +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_RtcpEnabled) { + // Note(pbos): This is a receiver-side setting, dumbo. + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_KeyFrameRequestEnabled) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, RembIsEnabledByDefault) { + FakeVideoReceiveStream* stream = AddRecvStream(); + EXPECT_TRUE(stream->GetConfig().rtp.remb); +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_RembEnabledOnReceiveChannels) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, RecvStreamWithSimAndRtx) { + EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); + EXPECT_TRUE(channel_->SetSend(true)); + cricket::VideoOptions options; + options.conference_mode.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + + // Send side. + const std::vector<uint32> ssrcs = MAKE_VECTOR(kSsrcs1); + const std::vector<uint32> rtx_ssrcs = MAKE_VECTOR(kRtxSsrcs1); + FakeVideoSendStream* send_stream = AddSendStream( + cricket::CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs)); + + ASSERT_EQ(rtx_ssrcs.size(), send_stream->GetConfig().rtp.rtx.ssrcs.size()); + for (size_t i = 0; i < rtx_ssrcs.size(); ++i) + EXPECT_EQ(rtx_ssrcs[i], send_stream->GetConfig().rtp.rtx.ssrcs[i]); + + // Receiver side. + FakeVideoReceiveStream* recv_stream = AddRecvStream( + cricket::CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs)); + ASSERT_GT(recv_stream->GetConfig().rtp.rtx.size(), 0u) + << "No SSRCs for RTX configured by AddRecvStream."; + ASSERT_EQ(1u, recv_stream->GetConfig().rtp.rtx.size()) + << "This test only works with one receive codec. Please update the test."; + EXPECT_EQ(rtx_ssrcs[0], + recv_stream->GetConfig().rtp.rtx.begin()->second.ssrc); + // TODO(pbos): Make sure we set the RTX for correct payloads etc. +} + +TEST_F(WebRtcVideoChannel2Test, RecvStreamWithRtx) { + // Setup one channel with an associated RTX stream. + cricket::StreamParams params = + cricket::StreamParams::CreateLegacy(kSsrcs1[0]); + params.AddFidSsrc(kSsrcs1[0], kRtxSsrcs1[0]); + FakeVideoReceiveStream* recv_stream = AddRecvStream(params); + ASSERT_EQ(1u, recv_stream->GetConfig().rtp.rtx.size()); + EXPECT_EQ(kRtxSsrcs1[0], + recv_stream->GetConfig().rtp.rtx.begin()->second.ssrc); +} + +TEST_F(WebRtcVideoChannel2Test, RecvStreamNoRtx) { + // Setup one channel without an associated RTX stream. + cricket::StreamParams params = + cricket::StreamParams::CreateLegacy(kSsrcs1[0]); + FakeVideoReceiveStream* recv_stream = AddRecvStream(params); + ASSERT_TRUE(recv_stream->GetConfig().rtp.rtx.empty()); +} + +TEST_F(WebRtcVideoChannel2Test, NoHeaderExtesionsByDefault) { + FakeVideoSendStream* send_stream = + AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcs1[0])); + ASSERT_TRUE(send_stream->GetConfig().rtp.extensions.empty()); + + FakeVideoReceiveStream* recv_stream = + AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrcs1[0])); + ASSERT_TRUE(recv_stream->GetConfig().rtp.extensions.empty()); +} + +// Test support for RTP timestamp offset header extension. +TEST_F(WebRtcVideoChannel2Test, SendRtpTimestampOffsetHeaderExtensions) { + TestSetSendRtpHeaderExtensions(kRtpTimestampOffsetHeaderExtension, + webrtc::RtpExtension::kTOffset); +} +TEST_F(WebRtcVideoChannel2Test, RecvRtpTimestampOffsetHeaderExtensions) { + TestSetRecvRtpHeaderExtensions(kRtpTimestampOffsetHeaderExtension, + webrtc::RtpExtension::kTOffset); +} + +// Test support for absolute send time header extension. +TEST_F(WebRtcVideoChannel2Test, SendAbsoluteSendTimeHeaderExtensions) { + TestSetSendRtpHeaderExtensions(kRtpAbsoluteSenderTimeHeaderExtension, + webrtc::RtpExtension::kAbsSendTime); +} +TEST_F(WebRtcVideoChannel2Test, RecvAbsoluteSendTimeHeaderExtensions) { + TestSetRecvRtpHeaderExtensions(kRtpAbsoluteSenderTimeHeaderExtension, + webrtc::RtpExtension::kAbsSendTime); +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_LeakyBucketTest) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_BufferedModeLatency) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_AdditiveVideoOptions) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, AddRecvStreamOnlyUsesOneReceiveStream) { + EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1))); + EXPECT_EQ(1u, fake_channel_->GetFakeCall()->GetVideoReceiveStreams().size()); +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_NoRembChangeAfterAddRecvStream) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_RembOnOff) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, NackIsEnabledByDefault) { + VerifyCodecHasDefaultFeedbackParams(default_codec_); + + EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); + EXPECT_TRUE(channel_->SetSend(true)); + + // Send side. + FakeVideoSendStream* send_stream = + AddSendStream(cricket::StreamParams::CreateLegacy(1)); + EXPECT_GT(send_stream->GetConfig().rtp.nack.rtp_history_ms, 0); + + // Receiver side. + FakeVideoReceiveStream* recv_stream = + AddRecvStream(cricket::StreamParams::CreateLegacy(1)); + EXPECT_GT(recv_stream->GetConfig().rtp.nack.rtp_history_ms, 0); + + // Nack history size should match between sender and receiver. + EXPECT_EQ(send_stream->GetConfig().rtp.nack.rtp_history_ms, + recv_stream->GetConfig().rtp.nack.rtp_history_ms); +} + +TEST_F(WebRtcVideoChannel2Test, NackCanBeDisabled) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + + // Send side. + ASSERT_TRUE(channel_->SetSendCodecs(codecs)); + FakeVideoSendStream* send_stream = + AddSendStream(cricket::StreamParams::CreateLegacy(1)); + EXPECT_EQ(0, send_stream->GetConfig().rtp.nack.rtp_history_ms); + + // Receiver side. + ASSERT_TRUE(channel_->SetRecvCodecs(codecs)); + FakeVideoReceiveStream* recv_stream = + AddRecvStream(cricket::StreamParams::CreateLegacy(1)); + EXPECT_EQ(0, recv_stream->GetConfig().rtp.nack.rtp_history_ms); +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_VideoProtectionInterop) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_VideoProtectionInteropReversed) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_HybridNackFecConference) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_AddRemoveRecvStreamConference) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetRender) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetBandwidthAuto) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetBandwidthAutoCapped) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetBandwidthFixed) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetBandwidthInConference) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetBandwidthScreencast) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetSendSsrcAndCname) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, + DISABLED_SetSendSsrcAfterCreatingReceiveChannel) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetOptionsWithDenoising) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_MultipleSendStreamsWithOneCapturer) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_DISABLED_SendReceiveBitratesStats) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_TestSetAdaptInputToCpuUsage) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_TestSetCpuThreshold) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_TestSetInvalidCpuThreshold) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_WebRtcShouldLog) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_WebRtcShouldNotLog) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, SetDefaultSendCodecs) { + ASSERT_TRUE(channel_->SetSendCodecs(engine_.codecs())); + + VideoCodec codec; + EXPECT_TRUE(channel_->GetSendCodec(&codec)); + EXPECT_TRUE(codec.Matches(engine_.codecs()[0])); + + // Using a RTX setup to verify that the default RTX payload type is good. + const std::vector<uint32> ssrcs = MAKE_VECTOR(kSsrcs1); + const std::vector<uint32> rtx_ssrcs = MAKE_VECTOR(kRtxSsrcs1); + FakeVideoSendStream* stream = AddSendStream( + cricket::CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs)); + webrtc::VideoSendStream::Config config = stream->GetConfig(); + // TODO(pbos): Replace ExpectEqualCodecs. + // ExpectEqualCodecs(engine_.codecs()[0], config.codec); + + // Make sure NACK and FEC are enabled on the correct payload types. + EXPECT_EQ(1000, config.rtp.nack.rtp_history_ms); + EXPECT_EQ(default_ulpfec_codec_.id, config.rtp.fec.ulpfec_payload_type); + EXPECT_EQ(default_red_codec_.id, config.rtp.fec.red_payload_type); + // TODO(pbos): Verify that the rtx ssrc is set, correct, not taken by anything + // else. + // ASSERT_EQ(1u, config.rtp.rtx.ssrcs.size()); + EXPECT_EQ(static_cast<int>(default_rtx_codec_.id), + config.rtp.rtx.payload_type); + // TODO(juberti): Check RTCP, PLI, TMMBR. +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithoutFec) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + ASSERT_TRUE(channel_->SetSendCodecs(codecs)); + + FakeVideoSendStream* stream = AddSendStream(); + webrtc::VideoSendStream::Config config = stream->GetConfig(); + + EXPECT_EQ(-1, config.rtp.fec.ulpfec_payload_type); + EXPECT_EQ(-1, config.rtp.fec.red_payload_type); +} + +TEST_F(WebRtcVideoChannel2Test, + DISABLED_SetSendCodecRejectsRtxWithoutAssociatedPayloadType) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, + DISABLED_SetSendCodecRejectsRtxWithoutMatchingVideoCodec) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, + DISABLED_SetCodecsWithoutFecDisablesCurrentFec) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetSendCodecsChangesExistingStreams) { + FAIL(); // TODO(pbos): Implement, make sure that it's changing running + // streams. Should it? +} + +TEST_F(WebRtcVideoChannel2Test, + DISABLED_ConstrainsSetCodecsAccordingToEncoderConfig) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithMinMaxBitrate) { + SetSendCodecsShouldWorkForBitrates("10", "20"); +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectsMaxLessThanMinBitrate) { + std::vector<VideoCodec> video_codecs = engine_.codecs(); + video_codecs[0].params[kCodecParamMinBitrate] = "30"; + video_codecs[0].params[kCodecParamMaxBitrate] = "20"; + EXPECT_FALSE(channel_->SetSendCodecs(video_codecs)); +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsAcceptLargeMinMaxBitrate) { + SetSendCodecsShouldWorkForBitrates("1000", "2000"); +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithMaxQuantization) { + static const char* kMaxQuantization = "21"; + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + codecs[0].params[kCodecParamMaxQuantization] = kMaxQuantization; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_EQ(static_cast<unsigned int>(atoi(kMaxQuantization)), + AddSendStream()->GetVideoStreams().back().max_qp); + + VideoCodec codec; + EXPECT_TRUE(channel_->GetSendCodec(&codec)); + EXPECT_EQ(kMaxQuantization, codec.params[kCodecParamMaxQuantization]); +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectBadDimensions) { + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVp8Codec); + + codecs[0].width = 0; + EXPECT_FALSE(channel_->SetSendCodecs(codecs)) + << "Codec set though codec width is zero."; + + codecs[0].width = kVp8Codec.width; + codecs[0].height = 0; + EXPECT_FALSE(channel_->SetSendCodecs(codecs)) + << "Codec set though codec height is zero."; +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectBadPayloadTypes) { + // TODO(pbos): Should we only allow the dynamic range? + static const size_t kNumIncorrectPayloads = 4; + static const int kIncorrectPayloads[kNumIncorrectPayloads] = {-2, -1, 128, + 129}; + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVp8Codec); + for (size_t i = 0; i < kNumIncorrectPayloads; ++i) { + int payload_type = kIncorrectPayloads[i]; + codecs[0].id = payload_type; + EXPECT_FALSE(channel_->SetSendCodecs(codecs)) + << "Bad payload type '" << payload_type << "' accepted."; + } +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsAcceptAllValidPayloadTypes) { + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVp8Codec); + for (int payload_type = 0; payload_type <= 127; ++payload_type) { + codecs[0].id = payload_type; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)) + << "Payload type '" << payload_type << "' rejected."; + } +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_ResetVieSendCodecOnNewFrameSize) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, SetRecvCodecsWithOnlyVp8) { + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVp8Codec); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); +} + +// Test that we set our inbound RTX codecs properly. +TEST_F(WebRtcVideoChannel2Test, SetRecvCodecsWithRtx) { + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVp8Codec); + cricket::VideoCodec rtx_codec(96, "rtx", 0, 0, 0, 0); + codecs.push_back(rtx_codec); + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)) + << "RTX codec without associated payload should be rejected."; + + codecs[1].SetParam("apt", kVp8Codec.id + 1); + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)) + << "RTX codec with invalid associated payload type should be rejected."; + + codecs[1].SetParam("apt", kVp8Codec.id); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); + + cricket::VideoCodec rtx_codec2(97, "rtx", 0, 0, 0, 0); + rtx_codec2.SetParam("apt", rtx_codec.id); + codecs.push_back(rtx_codec2); + + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)) << "RTX codec with another RTX " + "as associated payload type " + "should be rejected."; +} + +TEST_F(WebRtcVideoChannel2Test, SetRecvCodecsDifferentPayloadType) { + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVp8Codec); + codecs[0].id = 99; + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetRecvCodecsAcceptDefaultCodecs) { + EXPECT_TRUE(channel_->SetRecvCodecs(engine_.codecs())); + // (I've added this one.) Make sure they propagate down to VideoReceiveStream! + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, SetRecvCodecsRejectUnsupportedCodec) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + codecs.push_back(VideoCodec(101, "WTF3", 640, 400, 30, 0)); + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)); +} + +// TODO(pbos): Enable VP9 through external codec support +TEST_F(WebRtcVideoChannel2Test, + DISABLED_SetRecvCodecsAcceptsMultipleVideoCodecs) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + codecs.push_back(kVp9Codec); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); +} + +TEST_F(WebRtcVideoChannel2Test, + DISABLED_SetRecvCodecsSetsFecForAllVideoCodecs) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + codecs.push_back(kVp9Codec); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); + FAIL(); // TODO(pbos): Verify that the FEC parameters are set for all codecs. +} + +TEST_F(WebRtcVideoChannel2Test, SetSendCodecsRejectDuplicateFecPayloads) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + codecs.push_back(kRedCodec); + codecs[1].id = codecs[0].id; + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)); +} + +TEST_F(WebRtcVideoChannel2Test, SetRecvCodecsRejectDuplicateCodecPayloads) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + codecs.push_back(kVp9Codec); + codecs[1].id = codecs[0].id; + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)); +} + +TEST_F(WebRtcVideoChannel2Test, + SetRecvCodecsAcceptSameCodecOnMultiplePayloadTypes) { + std::vector<VideoCodec> codecs; + codecs.push_back(kVp8Codec); + codecs.push_back(kVp8Codec); + codecs[1].id += 1; + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); +} + +TEST_F(WebRtcVideoChannel2Test, SendStreamNotSendingByDefault) { + EXPECT_FALSE(AddSendStream()->IsSending()); +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_ReceiveStreamReceivingByDefault) { + // Is this test correct though? Auto-receive? Enable receive on first packet? + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, SetSend) { + AddSendStream(); + EXPECT_FALSE(channel_->SetSend(true)) + << "Channel should not start without codecs."; + EXPECT_TRUE(channel_->SetSend(false)) + << "Channel should be stoppable even without set codecs."; + + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVp8Codec); + channel_->SetSendCodecs(codecs); + std::vector<FakeVideoSendStream*> streams = GetFakeSendStreams(); + ASSERT_EQ(1u, streams.size()); + FakeVideoSendStream* stream = streams.back(); + + EXPECT_FALSE(stream->IsSending()); + + // false->true + EXPECT_TRUE(channel_->SetSend(true)); + EXPECT_TRUE(stream->IsSending()); + // true->true + EXPECT_TRUE(channel_->SetSend(true)); + EXPECT_TRUE(stream->IsSending()); + // true->false + EXPECT_TRUE(channel_->SetSend(false)); + EXPECT_FALSE(stream->IsSending()); + // false->false + EXPECT_TRUE(channel_->SetSend(false)); + EXPECT_FALSE(stream->IsSending()); + + EXPECT_TRUE(channel_->SetSend(true)); + FakeVideoSendStream* new_stream = AddSendStream(); + EXPECT_TRUE(new_stream->IsSending()) + << "Send stream created after SetSend(true) not sending initially."; +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SendAndReceiveVp8Vga) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SendAndReceiveVp8Qvga) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SendAndReceiveH264SvcQqvga) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SendManyResizeOnce) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SendVp8HdAndReceiveAdaptedVp8Vga) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_TestSetDscpOptions) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetOptionsWithMaxBitrate) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetOptionsWithLoweredBitrate) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_SetOptionsSucceedsWhenSending) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_ResetCodecOnScreencast) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_DontResetCodecOnSendFrame) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, + DISABLED_DontRegisterDecoderIfFactoryIsNotGiven) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_RegisterDecoderIfFactoryIsGiven) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterDecoderMultipleTimes) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterDecoderForNonVP8) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, + DISABLED_DontRegisterEncoderIfFactoryIsNotGiven) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_RegisterEncoderIfFactoryIsGiven) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterEncoderMultipleTimes) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, + DISABLED_RegisterEncoderWithMultipleSendStreams) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterEncoderForNonVP8) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_FeedbackParamsForNonVP8) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_ExternalCodecAddedToTheEnd) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_ExternalCodecIgnored) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_UpdateEncoderCodecsAfterSetFactory) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_OnReadyToSend) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} + +TEST_F(WebRtcVideoChannel2Test, DISABLED_CaptureFrameTimestampToNtpTimestamp) { + FAIL() << "Not implemented."; // TODO(pbos): Implement. +} +} // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2_unittest.h b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2_unittest.h new file mode 100644 index 00000000000..879b4f40448 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine2_unittest.h @@ -0,0 +1,157 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_UNITTEST_H_ +#define TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_UNITTEST_H_ + +#include <map> +#include <vector> + +#include "webrtc/call.h" +#include "webrtc/video_receive_stream.h" +#include "webrtc/video_send_stream.h" + +namespace cricket { +class FakeVideoSendStream : public webrtc::VideoSendStream { + public: + FakeVideoSendStream(const webrtc::VideoSendStream::Config& config, + const std::vector<webrtc::VideoStream>& video_streams); + webrtc::VideoSendStream::Config GetConfig(); + std::vector<webrtc::VideoStream> GetVideoStreams(); + + bool IsSending(); + + private: + virtual webrtc::VideoSendStream::Stats GetStats() const OVERRIDE; + + virtual bool ReconfigureVideoEncoder( + const std::vector<webrtc::VideoStream>& streams, + const void* encoder_specific); + + virtual webrtc::VideoSendStreamInput* Input() OVERRIDE; + + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; + + bool sending_; + webrtc::VideoSendStream::Config config_; + std::vector<webrtc::VideoStream> video_streams_; +}; + +class FakeVideoReceiveStream : public webrtc::VideoReceiveStream { + public: + explicit FakeVideoReceiveStream( + const webrtc::VideoReceiveStream::Config& config); + + webrtc::VideoReceiveStream::Config GetConfig(); + + private: + virtual webrtc::VideoReceiveStream::Stats GetStats() const OVERRIDE; + + virtual void Start() OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void GetCurrentReceiveCodec(webrtc::VideoCodec* codec); + + webrtc::VideoReceiveStream::Config config_; + bool receiving_; +}; + +class FakeCall : public webrtc::Call { + public: + FakeCall(); + ~FakeCall(); + + void SetVideoCodecs(const std::vector<webrtc::VideoCodec> codecs); + + std::vector<FakeVideoSendStream*> GetVideoSendStreams(); + std::vector<FakeVideoReceiveStream*> GetVideoReceiveStreams(); + + webrtc::VideoCodec GetEmptyVideoCodec(); + + webrtc::VideoCodec GetVideoCodecVp8(); + webrtc::VideoCodec GetVideoCodecVp9(); + + std::vector<webrtc::VideoCodec> GetDefaultVideoCodecs(); + + private: + virtual webrtc::VideoSendStream::Config GetDefaultSendConfig() OVERRIDE; + + virtual webrtc::VideoSendStream* CreateVideoSendStream( + const webrtc::VideoSendStream::Config& config, + const std::vector<webrtc::VideoStream>& video_streams, + const void* encoder_settings) OVERRIDE; + + virtual void DestroyVideoSendStream( + webrtc::VideoSendStream* send_stream) OVERRIDE; + + virtual webrtc::VideoReceiveStream::Config GetDefaultReceiveConfig() OVERRIDE; + + virtual webrtc::VideoReceiveStream* CreateVideoReceiveStream( + const webrtc::VideoReceiveStream::Config& config) OVERRIDE; + + virtual void DestroyVideoReceiveStream( + webrtc::VideoReceiveStream* receive_stream) OVERRIDE; + virtual webrtc::PacketReceiver* Receiver() OVERRIDE; + + virtual uint32_t SendBitrateEstimate() OVERRIDE; + virtual uint32_t ReceiveBitrateEstimate() OVERRIDE; + + std::vector<webrtc::VideoCodec> codecs_; + std::vector<FakeVideoSendStream*> video_send_streams_; + std::vector<FakeVideoReceiveStream*> video_receive_streams_; +}; + +class FakeWebRtcVideoChannel2 : public WebRtcVideoChannel2 { + public: + FakeWebRtcVideoChannel2(FakeCall* call, + WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel); + virtual ~FakeWebRtcVideoChannel2(); + + VoiceMediaChannel* GetVoiceChannel(); + FakeCall* GetFakeCall(); + + private: + FakeCall* fake_call_; + VoiceMediaChannel* voice_channel_; +}; + +class FakeWebRtcVideoMediaChannelFactory : public WebRtcVideoChannelFactory { + public: + FakeWebRtcVideoChannel2* GetFakeChannel(VideoMediaChannel* channel); + + private: + virtual WebRtcVideoChannel2* Create( + WebRtcVideoEngine2* engine, + VoiceMediaChannel* voice_channel) OVERRIDE; + + std::map<VideoMediaChannel*, FakeWebRtcVideoChannel2*> channel_map_; +}; + + +} // namespace cricket +#endif // TALK_MEDIA_WEBRTC_WEBRTCVIDEOENGINE2_UNITTEST_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine_unittest.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine_unittest.cc index d5886a13822..307e594b926 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine_unittest.cc @@ -36,6 +36,7 @@ #include "talk/media/base/fakevideorenderer.h" #include "talk/media/base/mediachannel.h" #include "talk/media/base/testutils.h" +#include "talk/media/base/videoadapter.h" #include "talk/media/base/videoengine_unittest.h" #include "talk/media/webrtc/fakewebrtcvideocapturemodule.h" #include "talk/media/webrtc/fakewebrtcvideoengine.h" @@ -49,6 +50,9 @@ // Tests for the WebRtcVideoEngine/VideoChannel code. +using cricket::kRtpTimestampOffsetHeaderExtension; +using cricket::kRtpAbsoluteSenderTimeHeaderExtension; + static const cricket::VideoCodec kVP8Codec720p(100, "VP8", 1280, 720, 30, 0); static const cricket::VideoCodec kVP8Codec360p(100, "VP8", 640, 360, 30, 0); static const cricket::VideoCodec kVP8Codec270p(100, "VP8", 480, 270, 30, 0); @@ -63,16 +67,14 @@ static const cricket::VideoCodec* const kVideoCodecs[] = { &kUlpFecCodec }; -static const unsigned int kMinBandwidthKbps = 50; static const unsigned int kStartBandwidthKbps = 300; +static const unsigned int kMinBandwidthKbps = 50; static const unsigned int kMaxBandwidthKbps = 2000; -static const unsigned int kNumberOfTemporalLayers = 1; - static const uint32 kSsrcs1[] = {1}; static const uint32 kSsrcs2[] = {1, 2}; static const uint32 kSsrcs3[] = {1, 2, 3}; -static const uint32 kRtxSsrc1[] = {4}; +static const uint32 kRtxSsrcs1[] = {4}; static const uint32 kRtxSsrcs3[] = {4, 5, 6}; @@ -147,6 +149,90 @@ class WebRtcVideoEngineTestFake : public testing::Test, channel_->SendFrame(&capturer, &frame); return true; } + void TestSetSendRtpHeaderExtensions(const std::string& ext) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + + // Verify extensions are off by default. + EXPECT_EQ(-1, vie_.GetSendRtpExtensionId(channel_num, ext)); + + // Enable extension. + const int id = 1; + std::vector<cricket::RtpHeaderExtension> extensions; + extensions.push_back(cricket::RtpHeaderExtension(ext, id)); + + // Verify the send extension id. + EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); + EXPECT_EQ(id, vie_.GetSendRtpExtensionId(channel_num, ext)); + // Verify call with same set of extensions returns true. + EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); + EXPECT_EQ(id, vie_.GetSendRtpExtensionId(channel_num, ext)); + + // Add a new send stream and verify the extension is set. + // The first send stream to occupy the default channel. + EXPECT_TRUE( + channel_->AddSendStream(cricket::StreamParams::CreateLegacy(123))); + EXPECT_TRUE( + channel_->AddSendStream(cricket::StreamParams::CreateLegacy(234))); + int new_send_channel_num = vie_.GetLastChannel(); + EXPECT_NE(channel_num, new_send_channel_num); + EXPECT_EQ(id, vie_.GetSendRtpExtensionId(new_send_channel_num, ext)); + + // Remove the extension id. + std::vector<cricket::RtpHeaderExtension> empty_extensions; + EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(empty_extensions)); + EXPECT_EQ(-1, vie_.GetSendRtpExtensionId(channel_num, ext)); + EXPECT_EQ(-1, vie_.GetSendRtpExtensionId(new_send_channel_num, ext)); + } + void TestSetRecvRtpHeaderExtensions(const std::string& ext) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + + // Verify extensions are off by default. + EXPECT_EQ(-1, vie_.GetReceiveRtpExtensionId(channel_num, ext)); + + // Enable extension. + const int id = 2; + std::vector<cricket::RtpHeaderExtension> extensions; + extensions.push_back(cricket::RtpHeaderExtension(ext, id)); + + // Verify receive extension id. + EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); + EXPECT_EQ(id, vie_.GetReceiveRtpExtensionId(channel_num, ext)); + // Verify call with same set of extensions returns true. + EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); + EXPECT_EQ(id, vie_.GetReceiveRtpExtensionId(channel_num, ext)); + + // Add a new receive stream and verify the extension is set. + // The first send stream to occupy the default channel. + EXPECT_TRUE( + channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(345))); + EXPECT_TRUE( + channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(456))); + int new_recv_channel_num = vie_.GetLastChannel(); + EXPECT_NE(channel_num, new_recv_channel_num); + EXPECT_EQ(id, vie_.GetReceiveRtpExtensionId(new_recv_channel_num, ext)); + + // Remove the extension id. + std::vector<cricket::RtpHeaderExtension> empty_extensions; + EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(empty_extensions)); + EXPECT_EQ(-1, vie_.GetReceiveRtpExtensionId(channel_num, ext)); + EXPECT_EQ(-1, vie_.GetReceiveRtpExtensionId(new_recv_channel_num, ext)); + } + void VerifyCodecFeedbackParams(const cricket::VideoCodec& codec) { + EXPECT_TRUE(codec.HasFeedbackParam( + cricket::FeedbackParam(cricket::kRtcpFbParamNack, + cricket::kParamValueEmpty))); + EXPECT_TRUE(codec.HasFeedbackParam( + cricket::FeedbackParam(cricket::kRtcpFbParamNack, + cricket::kRtcpFbNackParamPli))); + EXPECT_TRUE(codec.HasFeedbackParam( + cricket::FeedbackParam(cricket::kRtcpFbParamRemb, + cricket::kParamValueEmpty))); + EXPECT_TRUE(codec.HasFeedbackParam( + cricket::FeedbackParam(cricket::kRtcpFbParamCcm, + cricket::kRtcpFbCcmParamFir))); + } void VerifyVP8SendCodec(int channel_num, unsigned int width, unsigned int height, @@ -237,8 +323,10 @@ TEST_F(WebRtcVideoEngineTest, WebRtcShouldLog) { EXPECT_EQ(talk_base::LS_INFO, talk_base::LogMessage::GetLogToStream(&stream)); webrtc::Trace::Add(webrtc::kTraceStateInfo, webrtc::kTraceUndefined, 0, webrtc_log); - EXPECT_TRUE_WAIT(std::string::npos != str.find(webrtc_log), 10); + talk_base::Thread::Current()->ProcessMessages(100); talk_base::LogMessage::RemoveLogToStream(&stream); + // Access |str| after LogMessage is done with it to avoid data racing. + EXPECT_NE(std::string::npos, str.find(webrtc_log)); } // Tests that webrtc logs are not logged when they should't be. @@ -296,9 +384,28 @@ TEST_F(WebRtcVideoEngineTestFake, SetSendCodecs) { VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height); EXPECT_TRUE(vie_.GetHybridNackFecStatus(channel_num)); EXPECT_FALSE(vie_.GetNackStatus(channel_num)); + EXPECT_EQ(1, vie_.GetNumSetSendCodecs()); // TODO(juberti): Check RTCP, PLI, TMMBR. } +// Test that ViE Channel doesn't call SetSendCodec again if same codec is tried +// to apply. +TEST_F(WebRtcVideoEngineTestFake, DontResetSetSendCodec) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + std::vector<cricket::VideoCodec> codecs(engine_.codecs()); + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height); + EXPECT_TRUE(vie_.GetHybridNackFecStatus(channel_num)); + EXPECT_FALSE(vie_.GetNackStatus(channel_num)); + EXPECT_EQ(1, vie_.GetNumSetSendCodecs()); + // Try setting same code again. + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + // Since it's exact same codec which is already set, media channel shouldn't + // send the codec to ViE. + EXPECT_EQ(1, vie_.GetNumSetSendCodecs()); +} + TEST_F(WebRtcVideoEngineTestFake, SetSendCodecsWithMinMaxBitrate) { EXPECT_TRUE(SetupEngine()); int channel_num = vie_.GetLastChannel(); @@ -316,6 +423,40 @@ TEST_F(WebRtcVideoEngineTestFake, SetSendCodecsWithMinMaxBitrate) { EXPECT_EQ("20", codec.params[cricket::kCodecParamMaxBitrate]); } +TEST_F(WebRtcVideoEngineTestFake, SetSendCodecsWithStartBitrate) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + std::vector<cricket::VideoCodec> codecs(engine_.codecs()); + codecs[0].params[cricket::kCodecParamStartBitrate] = "450"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + + VerifyVP8SendCodec( + channel_num, kVP8Codec.width, kVP8Codec.height, 0, 2000, 50, 450); + + cricket::VideoCodec codec; + EXPECT_TRUE(channel_->GetSendCodec(&codec)); + EXPECT_EQ("450", codec.params[cricket::kCodecParamStartBitrate]); +} + +TEST_F(WebRtcVideoEngineTestFake, SetSendCodecsWithMinMaxStartBitrate) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + std::vector<cricket::VideoCodec> codecs(engine_.codecs()); + codecs[0].params[cricket::kCodecParamMinBitrate] = "10"; + codecs[0].params[cricket::kCodecParamMaxBitrate] = "20"; + codecs[0].params[cricket::kCodecParamStartBitrate] = "14"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + + VerifyVP8SendCodec( + channel_num, kVP8Codec.width, kVP8Codec.height, 0, 20, 10, 14); + + cricket::VideoCodec codec; + EXPECT_TRUE(channel_->GetSendCodec(&codec)); + EXPECT_EQ("10", codec.params[cricket::kCodecParamMinBitrate]); + EXPECT_EQ("20", codec.params[cricket::kCodecParamMaxBitrate]); + EXPECT_EQ("14", codec.params[cricket::kCodecParamStartBitrate]); +} + TEST_F(WebRtcVideoEngineTestFake, SetSendCodecsWithMinMaxBitrateInvalid) { EXPECT_TRUE(SetupEngine()); std::vector<cricket::VideoCodec> codecs(engine_.codecs()); @@ -415,7 +556,7 @@ TEST_F(WebRtcVideoEngineTestFake, MaxBitrateResetWithConferenceMode) { EXPECT_TRUE(channel_->SetOptions(options)); VerifyVP8SendCodec( channel_num, kVP8Codec.width, kVP8Codec.height, 0, - kMaxBandwidthKbps, 10, 20); + kMaxBandwidthKbps, 10, kStartBandwidthKbps); } // Verify the current send bitrate is used as start bitrate when reconfiguring @@ -576,6 +717,112 @@ TEST_F(WebRtcVideoEngineTestFake, SetRecvCodecs) { EXPECT_TRUE(vie_.ReceiveCodecRegistered(channel_num, wcodec)); } +// Test that we set our inbound RTX codecs properly. +TEST_F(WebRtcVideoEngineTestFake, SetRecvCodecsWithRtx) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + + std::vector<cricket::VideoCodec> codecs; + cricket::VideoCodec rtx_codec(96, "rtx", 0, 0, 0, 0); + codecs.push_back(rtx_codec); + // Should fail since there's no associated payload type set. + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)); + + codecs[0].SetParam("apt", 97); + // Should still fail since the we don't support RTX on this APT. + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)); + + codecs[0].SetParam("apt", kVP8Codec.id); + // Should still fail since the associated payload type is unknown. + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)); + + codecs.push_back(kVP8Codec); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); + + webrtc::VideoCodec wcodec; + // Should not have been registered as a WebRTC codec. + EXPECT_TRUE(engine_.ConvertFromCricketVideoCodec(rtx_codec, &wcodec)); + EXPECT_STREQ("rtx", wcodec.plName); + EXPECT_FALSE(vie_.ReceiveCodecRegistered(channel_num, wcodec)); + + // The RTX payload type should have been set. + EXPECT_EQ(rtx_codec.id, vie_.GetRtxRecvPayloadType(channel_num)); +} + +// Test that RTX packets are routed to the default video channel if +// there's only one recv stream. +TEST_F(WebRtcVideoEngineTestFake, TestReceiveRtxOneStream) { + EXPECT_TRUE(SetupEngine()); + + // Setup one channel with an associated RTX stream. + cricket::StreamParams params = + cricket::StreamParams::CreateLegacy(kSsrcs1[0]); + params.AddFidSsrc(kSsrcs1[0], kRtxSsrcs1[0]); + EXPECT_TRUE(channel_->AddRecvStream(params)); + int channel_num = vie_.GetLastChannel(); + EXPECT_EQ(static_cast<int>(kRtxSsrcs1[0]), + vie_.GetRemoteRtxSsrc(channel_num)); + + // Register codecs. + std::vector<cricket::VideoCodec> codec_list; + codec_list.push_back(kVP8Codec720p); + cricket::VideoCodec rtx_codec(96, "rtx", 0, 0, 0, 0); + rtx_codec.SetParam("apt", kVP8Codec.id); + codec_list.push_back(rtx_codec); + EXPECT_TRUE(channel_->SetRecvCodecs(codec_list)); + + // Construct a fake RTX packet and verify that it is passed to the + // right WebRTC channel. + const size_t kDataLength = 12; + uint8_t data[kDataLength]; + memset(data, 0, sizeof(data)); + data[0] = 0x80; + data[1] = rtx_codec.id; + talk_base::SetBE32(&data[8], kRtxSsrcs1[0]); + talk_base::Buffer packet(data, kDataLength); + talk_base::PacketTime packet_time; + channel_->OnPacketReceived(&packet, packet_time); + EXPECT_EQ(rtx_codec.id, vie_.GetLastRecvdPayloadType(channel_num)); +} + +// Test that RTX packets are routed to the correct video channel. +TEST_F(WebRtcVideoEngineTestFake, TestReceiveRtxThreeStreams) { + EXPECT_TRUE(SetupEngine()); + + // Setup three channels with associated RTX streams. + int channel_num[ARRAY_SIZE(kSsrcs3)]; + for (size_t i = 0; i < ARRAY_SIZE(kSsrcs3); ++i) { + cricket::StreamParams params = + cricket::StreamParams::CreateLegacy(kSsrcs3[i]); + params.AddFidSsrc(kSsrcs3[i], kRtxSsrcs3[i]); + EXPECT_TRUE(channel_->AddRecvStream(params)); + channel_num[i] = vie_.GetLastChannel(); + } + + // Register codecs. + std::vector<cricket::VideoCodec> codec_list; + codec_list.push_back(kVP8Codec720p); + cricket::VideoCodec rtx_codec(96, "rtx", 0, 0, 0, 0); + rtx_codec.SetParam("apt", kVP8Codec.id); + codec_list.push_back(rtx_codec); + EXPECT_TRUE(channel_->SetRecvCodecs(codec_list)); + + // Construct a fake RTX packet and verify that it is passed to the + // right WebRTC channel. + const size_t kDataLength = 12; + uint8_t data[kDataLength]; + memset(data, 0, sizeof(data)); + data[0] = 0x80; + data[1] = rtx_codec.id; + talk_base::SetBE32(&data[8], kRtxSsrcs3[1]); + talk_base::Buffer packet(data, kDataLength); + talk_base::PacketTime packet_time; + channel_->OnPacketReceived(&packet, packet_time); + EXPECT_NE(rtx_codec.id, vie_.GetLastRecvdPayloadType(channel_num[0])); + EXPECT_EQ(rtx_codec.id, vie_.GetLastRecvdPayloadType(channel_num[1])); + EXPECT_NE(rtx_codec.id, vie_.GetLastRecvdPayloadType(channel_num[2])); +} + // Test that channel connects and disconnects external capturer correctly. TEST_F(WebRtcVideoEngineTestFake, HasExternalCapturer) { EXPECT_TRUE(SetupEngine()); @@ -669,7 +916,7 @@ TEST_F(WebRtcVideoEngineTestFake, RecvStreamWithRtx) { EXPECT_TRUE(channel_->AddRecvStream( cricket::CreateSimWithRtxStreamParams("cname", MAKE_VECTOR(kSsrcs1), - MAKE_VECTOR(kRtxSsrc1)))); + MAKE_VECTOR(kRtxSsrcs1)))); int new_channel_num = vie_.GetLastChannel(); EXPECT_NE(default_channel, new_channel_num); EXPECT_EQ(4, vie_.GetRemoteRtxSsrc(new_channel_num)); @@ -694,105 +941,35 @@ TEST_F(WebRtcVideoEngineTestFake, RecvStreamNoRtx) { } // Test support for RTP timestamp offset header extension. -TEST_F(WebRtcVideoEngineTestFake, RtpTimestampOffsetHeaderExtensions) { - EXPECT_TRUE(SetupEngine()); - int channel_num = vie_.GetLastChannel(); - cricket::VideoOptions options; - options.conference_mode.Set(true); - EXPECT_TRUE(channel_->SetOptions(options)); - - // Verify extensions are off by default. - EXPECT_EQ(0, vie_.GetSendRtpTimestampOffsetExtensionId(channel_num)); - EXPECT_EQ(0, vie_.GetReceiveRtpTimestampOffsetExtensionId(channel_num)); - - // Enable RTP timestamp extension. - const int id = 14; - std::vector<cricket::RtpHeaderExtension> extensions; - extensions.push_back(cricket::RtpHeaderExtension( - "urn:ietf:params:rtp-hdrext:toffset", id)); - - // Verify the send extension id. - EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); - EXPECT_EQ(id, vie_.GetSendRtpTimestampOffsetExtensionId(channel_num)); - - // Remove the extension id. - std::vector<cricket::RtpHeaderExtension> empty_extensions; - EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(empty_extensions)); - EXPECT_EQ(0, vie_.GetSendRtpTimestampOffsetExtensionId(channel_num)); - - // Verify receive extension id. - EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); - EXPECT_EQ(id, vie_.GetReceiveRtpTimestampOffsetExtensionId(channel_num)); - - // Add a new receive stream and verify the extension is set. - EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2))); - int new_channel_num = vie_.GetLastChannel(); - EXPECT_NE(channel_num, new_channel_num); - EXPECT_EQ(id, vie_.GetReceiveRtpTimestampOffsetExtensionId(new_channel_num)); - - // Remove the extension id. - EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(empty_extensions)); - EXPECT_EQ(0, vie_.GetReceiveRtpTimestampOffsetExtensionId(channel_num)); - EXPECT_EQ(0, vie_.GetReceiveRtpTimestampOffsetExtensionId(new_channel_num)); +TEST_F(WebRtcVideoEngineTestFake, SendRtpTimestampOffsetHeaderExtensions) { + TestSetSendRtpHeaderExtensions(kRtpTimestampOffsetHeaderExtension); +} +TEST_F(WebRtcVideoEngineTestFake, RecvRtpTimestampOffsetHeaderExtensions) { + TestSetRecvRtpHeaderExtensions(kRtpTimestampOffsetHeaderExtension); } // Test support for absolute send time header extension. -TEST_F(WebRtcVideoEngineTestFake, AbsoluteSendTimeHeaderExtensions) { - EXPECT_TRUE(SetupEngine()); - int channel_num = vie_.GetLastChannel(); - cricket::VideoOptions options; - options.conference_mode.Set(true); - EXPECT_TRUE(channel_->SetOptions(options)); - - // Verify extensions are off by default. - EXPECT_EQ(0, vie_.GetSendAbsoluteSendTimeExtensionId(channel_num)); - EXPECT_EQ(0, vie_.GetReceiveAbsoluteSendTimeExtensionId(channel_num)); - - // Enable RTP timestamp extension. - const int id = 12; - std::vector<cricket::RtpHeaderExtension> extensions; - extensions.push_back(cricket::RtpHeaderExtension( - "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", id)); - - // Verify the send extension id. - EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); - EXPECT_EQ(id, vie_.GetSendAbsoluteSendTimeExtensionId(channel_num)); - - // Remove the extension id. - std::vector<cricket::RtpHeaderExtension> empty_extensions; - EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(empty_extensions)); - EXPECT_EQ(0, vie_.GetSendAbsoluteSendTimeExtensionId(channel_num)); - - // Verify receive extension id. - EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); - EXPECT_EQ(id, vie_.GetReceiveAbsoluteSendTimeExtensionId(channel_num)); - - // Add a new receive stream and verify the extension is set. - EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2))); - int new_channel_num = vie_.GetLastChannel(); - EXPECT_NE(channel_num, new_channel_num); - EXPECT_EQ(id, vie_.GetReceiveAbsoluteSendTimeExtensionId(new_channel_num)); - - // Remove the extension id. - EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(empty_extensions)); - EXPECT_EQ(0, vie_.GetReceiveAbsoluteSendTimeExtensionId(channel_num)); - EXPECT_EQ(0, vie_.GetReceiveAbsoluteSendTimeExtensionId(new_channel_num)); +TEST_F(WebRtcVideoEngineTestFake, SendAbsoluteSendTimeHeaderExtensions) { + TestSetSendRtpHeaderExtensions(kRtpAbsoluteSenderTimeHeaderExtension); +} +TEST_F(WebRtcVideoEngineTestFake, RecvAbsoluteSendTimeHeaderExtensions) { + TestSetRecvRtpHeaderExtensions(kRtpAbsoluteSenderTimeHeaderExtension); } TEST_F(WebRtcVideoEngineTestFake, LeakyBucketTest) { EXPECT_TRUE(SetupEngine()); - // Verify this is off by default. + // Verify this is on by default. EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1))); int first_send_channel = vie_.GetLastChannel(); - EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); - // Enable the experiment and verify. + // Disable the experiment and verify. cricket::VideoOptions options; options.conference_mode.Set(true); - options.video_leaky_bucket.Set(true); + options.video_leaky_bucket.Set(false); EXPECT_TRUE(channel_->SetOptions(options)); - EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); // Add a receive channel and verify leaky bucket isn't enabled. EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2))); @@ -800,13 +977,41 @@ TEST_F(WebRtcVideoEngineTestFake, LeakyBucketTest) { EXPECT_NE(first_send_channel, recv_channel_num); EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(recv_channel_num)); - // Add a new send stream and verify leaky bucket is enabled from start. + // Add a new send stream and verify leaky bucket is disabled from start. EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(3))); int second_send_channel = vie_.GetLastChannel(); EXPECT_NE(first_send_channel, second_send_channel); + EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(second_send_channel)); + + // Reenable leaky bucket. + options.video_leaky_bucket.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(second_send_channel)); } +// Verify that SuspendBelowMinBitrate is enabled if it is set in the options. +TEST_F(WebRtcVideoEngineTestFake, SuspendBelowMinBitrateTest) { + EXPECT_TRUE(SetupEngine()); + + // Verify this is off by default. + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1))); + int first_send_channel = vie_.GetLastChannel(); + EXPECT_FALSE(vie_.GetSuspendBelowMinBitrateStatus(first_send_channel)); + + // Enable the experiment and verify. + cricket::VideoOptions options; + options.suspend_below_min_bitrate.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + EXPECT_TRUE(vie_.GetSuspendBelowMinBitrateStatus(first_send_channel)); + + // Add a new send stream and verify suspend_below_min_bitrate is enabled. + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(2))); + int second_send_channel = vie_.GetLastChannel(); + EXPECT_NE(first_send_channel, second_send_channel); + EXPECT_TRUE(vie_.GetSuspendBelowMinBitrateStatus(second_send_channel)); +} + TEST_F(WebRtcVideoEngineTestFake, BufferedModeLatency) { EXPECT_TRUE(SetupEngine()); @@ -863,12 +1068,12 @@ TEST_F(WebRtcVideoEngineTestFake, AdditiveVideoOptions) { EXPECT_TRUE(channel_->SetOptions(options1)); EXPECT_EQ(100, vie_.GetSenderTargetDelay(first_send_channel)); EXPECT_EQ(100, vie_.GetReceiverTargetDelay(first_send_channel)); - EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); cricket::VideoOptions options2; - options2.video_leaky_bucket.Set(true); + options2.video_leaky_bucket.Set(false); EXPECT_TRUE(channel_->SetOptions(options2)); - EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); // The buffered_mode_latency still takes effect. EXPECT_EQ(100, vie_.GetSenderTargetDelay(first_send_channel)); EXPECT_EQ(100, vie_.GetReceiverTargetDelay(first_send_channel)); @@ -878,7 +1083,169 @@ TEST_F(WebRtcVideoEngineTestFake, AdditiveVideoOptions) { EXPECT_EQ(50, vie_.GetSenderTargetDelay(first_send_channel)); EXPECT_EQ(50, vie_.GetReceiverTargetDelay(first_send_channel)); // The video_leaky_bucket still takes effect. - EXPECT_TRUE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); + EXPECT_FALSE(vie_.GetTransmissionSmoothingStatus(first_send_channel)); +} + +TEST_F(WebRtcVideoEngineTestFake, SetCpuOveruseOptionsWithCaptureJitterMethod) { + EXPECT_TRUE(SetupEngine()); + + // Verify this is off by default. + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1))); + int first_send_channel = vie_.GetLastChannel(); + webrtc::CpuOveruseOptions cpu_option = + vie_.GetCpuOveruseOptions(first_send_channel); + EXPECT_EQ(0, cpu_option.low_capture_jitter_threshold_ms); + EXPECT_EQ(0, cpu_option.high_capture_jitter_threshold_ms); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_FALSE(cpu_option.enable_encode_usage_method); + + // Set low and high threshold and verify that cpu options are set. + cricket::VideoOptions options; + options.conference_mode.Set(true); + options.cpu_underuse_threshold.Set(10); + options.cpu_overuse_threshold.Set(20); + EXPECT_TRUE(channel_->SetOptions(options)); + cpu_option = vie_.GetCpuOveruseOptions(first_send_channel); + EXPECT_EQ(10, cpu_option.low_capture_jitter_threshold_ms); + EXPECT_EQ(20, cpu_option.high_capture_jitter_threshold_ms); + EXPECT_TRUE(cpu_option.enable_capture_jitter_method); + EXPECT_FALSE(cpu_option.enable_encode_usage_method); + + // Add a receive channel and verify that cpu options are not set. + EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2))); + int recv_channel_num = vie_.GetLastChannel(); + EXPECT_NE(first_send_channel, recv_channel_num); + cpu_option = vie_.GetCpuOveruseOptions(recv_channel_num); + EXPECT_EQ(0, cpu_option.low_capture_jitter_threshold_ms); + EXPECT_EQ(0, cpu_option.high_capture_jitter_threshold_ms); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_FALSE(cpu_option.enable_encode_usage_method); + + // Add a new send stream and verify that cpu options are set from start. + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(3))); + int second_send_channel = vie_.GetLastChannel(); + EXPECT_NE(first_send_channel, second_send_channel); + cpu_option = vie_.GetCpuOveruseOptions(second_send_channel); + EXPECT_EQ(10, cpu_option.low_capture_jitter_threshold_ms); + EXPECT_EQ(20, cpu_option.high_capture_jitter_threshold_ms); + EXPECT_TRUE(cpu_option.enable_capture_jitter_method); + EXPECT_FALSE(cpu_option.enable_encode_usage_method); +} + +TEST_F(WebRtcVideoEngineTestFake, SetInvalidCpuOveruseThresholds) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1))); + int channel_num = vie_.GetLastChannel(); + + // Only low threshold set. Verify that cpu options are not set. + cricket::VideoOptions options; + options.conference_mode.Set(true); + options.cpu_underuse_threshold.Set(10); + EXPECT_TRUE(channel_->SetOptions(options)); + webrtc::CpuOveruseOptions cpu_option = vie_.GetCpuOveruseOptions(channel_num); + EXPECT_EQ(0, cpu_option.low_capture_jitter_threshold_ms); + EXPECT_EQ(0, cpu_option.high_capture_jitter_threshold_ms); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_FALSE(cpu_option.enable_encode_usage_method); + + // Set high threshold to a negative value. Verify that options are not set. + options.cpu_overuse_threshold.Set(-1); + EXPECT_TRUE(channel_->SetOptions(options)); + cpu_option = vie_.GetCpuOveruseOptions(channel_num); + EXPECT_EQ(0, cpu_option.low_capture_jitter_threshold_ms); + EXPECT_EQ(0, cpu_option.high_capture_jitter_threshold_ms); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_FALSE(cpu_option.enable_encode_usage_method); + + // Low and high threshold valid. Verify that cpu options are set. + options.cpu_overuse_threshold.Set(20); + EXPECT_TRUE(channel_->SetOptions(options)); + cpu_option = vie_.GetCpuOveruseOptions(channel_num); + EXPECT_EQ(10, cpu_option.low_capture_jitter_threshold_ms); + EXPECT_EQ(20, cpu_option.high_capture_jitter_threshold_ms); + EXPECT_TRUE(cpu_option.enable_capture_jitter_method); + EXPECT_FALSE(cpu_option.enable_encode_usage_method); +} + +TEST_F(WebRtcVideoEngineTestFake, SetCpuOveruseOptionsWithEncodeUsageMethod) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1))); + int first_send_channel = vie_.GetLastChannel(); + + // Set low and high threshold and enable encode usage method. + // Verify that cpu options are set. + cricket::VideoOptions options; + options.conference_mode.Set(true); + options.cpu_underuse_threshold.Set(10); + options.cpu_overuse_threshold.Set(20); + options.cpu_overuse_encode_usage.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + webrtc::CpuOveruseOptions cpu_option = + vie_.GetCpuOveruseOptions(first_send_channel); + EXPECT_EQ(10, cpu_option.low_encode_usage_threshold_percent); + EXPECT_EQ(20, cpu_option.high_encode_usage_threshold_percent); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_TRUE(cpu_option.enable_encode_usage_method); +#ifdef USE_WEBRTC_DEV_BRANCH + // Verify that optional encode rsd thresholds are not set. + EXPECT_EQ(-1, cpu_option.low_encode_time_rsd_threshold); + EXPECT_EQ(-1, cpu_option.high_encode_time_rsd_threshold); +#endif + + // Add a new send stream and verify that cpu options are set from start. + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(3))); + int second_send_channel = vie_.GetLastChannel(); + EXPECT_NE(first_send_channel, second_send_channel); + cpu_option = vie_.GetCpuOveruseOptions(second_send_channel); + EXPECT_EQ(10, cpu_option.low_encode_usage_threshold_percent); + EXPECT_EQ(20, cpu_option.high_encode_usage_threshold_percent); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_TRUE(cpu_option.enable_encode_usage_method); +#ifdef USE_WEBRTC_DEV_BRANCH + // Verify that optional encode rsd thresholds are not set. + EXPECT_EQ(-1, cpu_option.low_encode_time_rsd_threshold); + EXPECT_EQ(-1, cpu_option.high_encode_time_rsd_threshold); +#endif +} + +TEST_F(WebRtcVideoEngineTestFake, SetCpuOveruseOptionsWithEncodeRsdThresholds) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1))); + int first_send_channel = vie_.GetLastChannel(); + + // Set optional encode rsd thresholds and verify cpu options. + cricket::VideoOptions options; + options.conference_mode.Set(true); + options.cpu_underuse_threshold.Set(10); + options.cpu_overuse_threshold.Set(20); + options.cpu_underuse_encode_rsd_threshold.Set(30); + options.cpu_overuse_encode_rsd_threshold.Set(40); + options.cpu_overuse_encode_usage.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + webrtc::CpuOveruseOptions cpu_option = + vie_.GetCpuOveruseOptions(first_send_channel); + EXPECT_EQ(10, cpu_option.low_encode_usage_threshold_percent); + EXPECT_EQ(20, cpu_option.high_encode_usage_threshold_percent); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_TRUE(cpu_option.enable_encode_usage_method); +#ifdef USE_WEBRTC_DEV_BRANCH + EXPECT_EQ(30, cpu_option.low_encode_time_rsd_threshold); + EXPECT_EQ(40, cpu_option.high_encode_time_rsd_threshold); +#endif + + // Add a new send stream and verify that cpu options are set from start. + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(3))); + int second_send_channel = vie_.GetLastChannel(); + EXPECT_NE(first_send_channel, second_send_channel); + cpu_option = vie_.GetCpuOveruseOptions(second_send_channel); + EXPECT_EQ(10, cpu_option.low_encode_usage_threshold_percent); + EXPECT_EQ(20, cpu_option.high_encode_usage_threshold_percent); + EXPECT_FALSE(cpu_option.enable_capture_jitter_method); + EXPECT_TRUE(cpu_option.enable_encode_usage_method); +#ifdef USE_WEBRTC_DEV_BRANCH + EXPECT_EQ(30, cpu_option.low_encode_time_rsd_threshold); + EXPECT_EQ(40, cpu_option.high_encode_time_rsd_threshold); +#endif } // Test that AddRecvStream doesn't create new channel for 1:1 call. @@ -889,6 +1256,17 @@ TEST_F(WebRtcVideoEngineTestFake, AddRecvStream1On1) { EXPECT_EQ(channel_num, vie_.GetLastChannel()); } +// Test that NACK, PLI and REMB are enabled for internal codec. +TEST_F(WebRtcVideoEngineTestFake, InternalCodecFeedbackParams) { + EXPECT_TRUE(SetupEngine()); + + std::vector<cricket::VideoCodec> codecs(engine_.codecs()); + // Vp8 will appear at the beginning. + size_t pos = 0; + EXPECT_EQ("VP8", codecs[pos].name); + VerifyCodecFeedbackParams(codecs[pos]); +} + // Test that AddRecvStream doesn't change remb for 1:1 call. TEST_F(WebRtcVideoEngineTestFake, NoRembChangeAfterAddRecvStream) { EXPECT_TRUE(SetupEngine()); @@ -1029,6 +1407,24 @@ TEST_F(WebRtcVideoEngineTestFake, AddRemoveRecvStreamConference) { EXPECT_FALSE(vie_.IsChannel(receive_channel_num)); } +// Test that adding/removing stream with 0 ssrc should fail (and not crash). +// For crbug/351699 and 350988. +TEST_F(WebRtcVideoEngineTestFake, AddRemoveRecvStreamWith0Ssrc) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1))); + EXPECT_FALSE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(0))); + EXPECT_FALSE(channel_->RemoveRecvStream(0)); + EXPECT_TRUE(channel_->RemoveRecvStream(1)); +} + +TEST_F(WebRtcVideoEngineTestFake, AddRemoveSendStreamWith0Ssrc) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(1))); + EXPECT_FALSE(channel_->AddSendStream(cricket::StreamParams::CreateLegacy(0))); + EXPECT_FALSE(channel_->RemoveSendStream(0)); + EXPECT_TRUE(channel_->RemoveSendStream(1)); +} + // Test that we can create a channel and start/stop rendering out on it. TEST_F(WebRtcVideoEngineTestFake, SetRender) { EXPECT_TRUE(SetupEngine()); @@ -1049,6 +1445,8 @@ TEST_F(WebRtcVideoEngineTestFake, SetRender) { TEST_F(WebRtcVideoEngineTestFake, SetSend) { EXPECT_TRUE(SetupEngine()); int channel_num = vie_.GetLastChannel(); + // Verify receiving is also started. + EXPECT_TRUE(vie_.GetReceive(channel_num)); // Set send codecs on the channel. std::vector<cricket::VideoCodec> codecs; @@ -1073,30 +1471,84 @@ TEST_F(WebRtcVideoEngineTestFake, SetBandwidthAuto) { EXPECT_TRUE(SetupEngine()); int channel_num = vie_.GetLastChannel(); EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); - EXPECT_TRUE(channel_->SetSendBandwidth(true, cricket::kAutoBandwidth)); + EXPECT_TRUE(channel_->SetMaxSendBandwidth(cricket::kAutoBandwidth)); VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height); } // Test that we set bandwidth properly when using auto with upper bound. -TEST_F(WebRtcVideoEngineTestFake, SetBandwidthAutoCapped) { +TEST_F(WebRtcVideoEngineTestFake, SetBandwidthCapped) { EXPECT_TRUE(SetupEngine()); int channel_num = vie_.GetLastChannel(); EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); - EXPECT_TRUE(channel_->SetSendBandwidth(true, 768000)); + EXPECT_TRUE(channel_->SetMaxSendBandwidth(768000)); VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, 768U); } -// Test that we set bandwidth properly when using a fixed bandwidth. -TEST_F(WebRtcVideoEngineTestFake, SetBandwidthFixed) { +// Test that we reduce the start bandwidth when the requested max is less than +// the default start bandwidth. +TEST_F(WebRtcVideoEngineTestFake, SetMaxBandwidthBelowDefaultStart) { EXPECT_TRUE(SetupEngine()); int channel_num = vie_.GetLastChannel(); EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); - EXPECT_TRUE(channel_->SetSendBandwidth(false, 768000)); + int max_bandwidth_kbps = (kMinBandwidthKbps + kStartBandwidthKbps) / 2; + EXPECT_TRUE(channel_->SetMaxSendBandwidth(max_bandwidth_kbps * 1000)); VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, - 768U, 768U, 768U); + max_bandwidth_kbps, kMinBandwidthKbps, max_bandwidth_kbps); } -// Test that SetSendBandwidth is ignored in conference mode. +// Test that we reduce the min bandwidth when the requested max is less than +// the min bandwidth. +TEST_F(WebRtcVideoEngineTestFake, SetMaxBandwidthBelowMin) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); + int max_bandwidth_kbps = kMinBandwidthKbps / 2; + EXPECT_TRUE(channel_->SetMaxSendBandwidth(max_bandwidth_kbps * 1000)); + VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, + max_bandwidth_kbps, max_bandwidth_kbps, max_bandwidth_kbps); +} + +// Test that the start bandwidth can be controlled separately from the max +// bandwidth. +TEST_F(WebRtcVideoEngineTestFake, SetStartBandwidth) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); + int start_bandwidth_kbps = kStartBandwidthKbps + 1; + EXPECT_TRUE(channel_->SetStartSendBandwidth(start_bandwidth_kbps * 1000)); + VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, + kMaxBandwidthKbps, kMinBandwidthKbps, start_bandwidth_kbps); + + // Check that SetMaxSendBandwidth doesn't overwrite the start bandwidth. + int max_bandwidth_kbps = kMaxBandwidthKbps + 1; + EXPECT_TRUE(channel_->SetMaxSendBandwidth(max_bandwidth_kbps * 1000)); + VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, + max_bandwidth_kbps, kMinBandwidthKbps, start_bandwidth_kbps); +} + +// Test that the start bandwidth can be controlled by experiment. +TEST_F(WebRtcVideoEngineTestFake, SetStartBandwidthOption) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); + int start_bandwidth_kbps = kStartBandwidthKbps; + EXPECT_TRUE(channel_->SetStartSendBandwidth(start_bandwidth_kbps * 1000)); + VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, + kMaxBandwidthKbps, kMinBandwidthKbps, start_bandwidth_kbps); + + // Set the start bitrate option. + start_bandwidth_kbps = 1000; + cricket::VideoOptions options; + options.video_start_bitrate.Set( + start_bandwidth_kbps); + EXPECT_TRUE(channel_->SetOptions(options)); + + // Check that start bitrate has changed to the new value. + VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, + kMaxBandwidthKbps, kMinBandwidthKbps, start_bandwidth_kbps); +} + +// Test that SetMaxSendBandwidth works as expected in conference mode. TEST_F(WebRtcVideoEngineTestFake, SetBandwidthInConference) { EXPECT_TRUE(SetupEngine()); int channel_num = vie_.GetLastChannel(); @@ -1107,19 +1559,14 @@ TEST_F(WebRtcVideoEngineTestFake, SetBandwidthInConference) { VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height); // Set send bandwidth. - EXPECT_TRUE(channel_->SetSendBandwidth(false, 768000)); + EXPECT_TRUE(channel_->SetMaxSendBandwidth(768000)); - // Verify bitrate not changed. - webrtc::VideoCodec gcodec; - EXPECT_EQ(0, vie_.GetSendCodec(channel_num, gcodec)); - EXPECT_EQ(kMinBandwidthKbps, gcodec.minBitrate); - EXPECT_EQ(kStartBandwidthKbps, gcodec.startBitrate); - EXPECT_EQ(kMaxBandwidthKbps, gcodec.maxBitrate); - EXPECT_NE(768U, gcodec.minBitrate); - EXPECT_NE(768U, gcodec.startBitrate); - EXPECT_NE(768U, gcodec.maxBitrate); + // Verify that the max bitrate has changed. + VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, + 768, kMinBandwidthKbps, kStartBandwidthKbps); } + // Test that sending screencast frames doesn't change bitrate. TEST_F(WebRtcVideoEngineTestFake, SetBandwidthScreencast) { EXPECT_TRUE(SetupEngine()); @@ -1132,12 +1579,11 @@ TEST_F(WebRtcVideoEngineTestFake, SetBandwidthScreencast) { EXPECT_TRUE(channel_->AddSendStream( cricket::StreamParams::CreateLegacy(123))); EXPECT_TRUE(channel_->SetSendCodecs(codec_list)); - EXPECT_TRUE(channel_->SetSendBandwidth(false, 111000)); + EXPECT_TRUE(channel_->SetMaxSendBandwidth(111000)); EXPECT_TRUE(channel_->SetSend(true)); SendI420ScreencastFrame(kVP8Codec.width, kVP8Codec.height); - VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, - 111, 111, 111); + VerifyVP8SendCodec(channel_num, kVP8Codec.width, kVP8Codec.height, 0, 111); } @@ -1239,12 +1685,20 @@ TEST_F(WebRtcVideoEngineTestFake, MultipleSendStreamsWithOneCapturer) { ASSERT_NE(-1, channel1); ASSERT_NE(channel0, channel1); + // Both channels should have started receiving after created. + EXPECT_TRUE(vie_.GetReceive(channel0)); + EXPECT_TRUE(vie_.GetReceive(channel1)); + // Set send codec. std::vector<cricket::VideoCodec> codecs; cricket::VideoCodec send_codec(100, "VP8", 640, 480, 30, 0); codecs.push_back(send_codec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->SetSend(true)); + EXPECT_TRUE(vie_.GetSend(channel0)); + EXPECT_TRUE(vie_.GetSend(channel1)); + EXPECT_TRUE(capturer.CaptureFrame()); EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0)); EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel1)); @@ -1264,31 +1718,33 @@ TEST_F(WebRtcVideoEngineTestFake, MultipleSendStreamsWithOneCapturer) { } -// Disabled since its flaky: b/11288120 -TEST_F(WebRtcVideoEngineTestFake, DISABLED_SendReceiveBitratesStats) { +TEST_F(WebRtcVideoEngineTestFake, SendReceiveBitratesStats) { EXPECT_TRUE(SetupEngine()); cricket::VideoOptions options; options.conference_mode.Set(true); EXPECT_TRUE(channel_->SetOptions(options)); EXPECT_TRUE(channel_->AddSendStream( cricket::StreamParams::CreateLegacy(1))); - int send_channel = vie_.GetLastChannel(); + int first_send_channel = vie_.GetLastChannel(); + EXPECT_TRUE(channel_->AddSendStream( + cricket::StreamParams::CreateLegacy(2))); + int second_send_channel = vie_.GetLastChannel(); cricket::VideoCodec codec(kVP8Codec720p); std::vector<cricket::VideoCodec> codec_list; codec_list.push_back(codec); EXPECT_TRUE(channel_->SetSendCodecs(codec_list)); EXPECT_TRUE(channel_->AddRecvStream( - cricket::StreamParams::CreateLegacy(2))); + cricket::StreamParams::CreateLegacy(3))); int first_receive_channel = vie_.GetLastChannel(); - EXPECT_NE(send_channel, first_receive_channel); + EXPECT_NE(first_send_channel, first_receive_channel); EXPECT_TRUE(channel_->AddRecvStream( - cricket::StreamParams::CreateLegacy(3))); + cricket::StreamParams::CreateLegacy(4))); int second_receive_channel = vie_.GetLastChannel(); EXPECT_NE(first_receive_channel, second_receive_channel); cricket::VideoMediaInfo info; - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.bw_estimations.size()); ASSERT_EQ(0, info.bw_estimations[0].actual_enc_bitrate); ASSERT_EQ(0, info.bw_estimations[0].transmit_bitrate); @@ -1298,49 +1754,48 @@ TEST_F(WebRtcVideoEngineTestFake, DISABLED_SendReceiveBitratesStats) { ASSERT_EQ(0, info.bw_estimations[0].target_enc_bitrate); // Start sending and receiving on one of the channels and verify bitrates. - EXPECT_EQ(0, vie_.StartSend(send_channel)); + EXPECT_EQ(0, vie_.StartSend(first_send_channel)); int send_video_bitrate = 800; int send_fec_bitrate = 100; int send_nack_bitrate = 20; int send_total_bitrate = send_video_bitrate + send_fec_bitrate + send_nack_bitrate; - int send_bandwidth = 950; - vie_.SetSendBitrates(send_channel, send_video_bitrate, send_fec_bitrate, + int send_bandwidth = 1900; + vie_.SetSendBitrates(first_send_channel, send_video_bitrate, send_fec_bitrate, send_nack_bitrate); - vie_.SetSendBandwidthEstimate(send_channel, send_bandwidth); + vie_.SetSendBandwidthEstimate(first_send_channel, send_bandwidth); EXPECT_EQ(0, vie_.StartReceive(first_receive_channel)); - int first_channel_receive_bandwidth = 600; - vie_.SetReceiveBandwidthEstimate(first_receive_channel, - first_channel_receive_bandwidth); + int receive_bandwidth = 600; + vie_.SetReceiveBandwidthEstimate(first_receive_channel, receive_bandwidth); info.Clear(); - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.bw_estimations.size()); ASSERT_EQ(send_video_bitrate, info.bw_estimations[0].actual_enc_bitrate); ASSERT_EQ(send_total_bitrate, info.bw_estimations[0].transmit_bitrate); ASSERT_EQ(send_nack_bitrate, info.bw_estimations[0].retransmit_bitrate); ASSERT_EQ(send_bandwidth, info.bw_estimations[0].available_send_bandwidth); - ASSERT_EQ(first_channel_receive_bandwidth, - info.bw_estimations[0].available_recv_bandwidth); + ASSERT_EQ(receive_bandwidth, info.bw_estimations[0].available_recv_bandwidth); ASSERT_EQ(send_video_bitrate, info.bw_estimations[0].target_enc_bitrate); // Start receiving on the second channel and verify received rate. + EXPECT_EQ(0, vie_.StartSend(second_send_channel)); + vie_.SetSendBitrates(second_send_channel, + send_video_bitrate, + send_fec_bitrate, + send_nack_bitrate); EXPECT_EQ(0, vie_.StartReceive(second_receive_channel)); - int second_channel_receive_bandwidth = 100; - vie_.SetReceiveBandwidthEstimate(second_receive_channel, - second_channel_receive_bandwidth); info.Clear(); - EXPECT_TRUE(channel_->GetStats(&info)); + EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info)); ASSERT_EQ(1U, info.bw_estimations.size()); - ASSERT_EQ(send_video_bitrate, info.bw_estimations[0].actual_enc_bitrate); - ASSERT_EQ(send_total_bitrate, info.bw_estimations[0].transmit_bitrate); - ASSERT_EQ(send_nack_bitrate, info.bw_estimations[0].retransmit_bitrate); + ASSERT_EQ(2 * send_video_bitrate, info.bw_estimations[0].actual_enc_bitrate); + ASSERT_EQ(2 * send_total_bitrate, info.bw_estimations[0].transmit_bitrate); + ASSERT_EQ(2 * send_nack_bitrate, info.bw_estimations[0].retransmit_bitrate); ASSERT_EQ(send_bandwidth, info.bw_estimations[0].available_send_bandwidth); - ASSERT_EQ(first_channel_receive_bandwidth + second_channel_receive_bandwidth, - info.bw_estimations[0].available_recv_bandwidth); - ASSERT_EQ(send_video_bitrate, info.bw_estimations[0].target_enc_bitrate); + ASSERT_EQ(receive_bandwidth, info.bw_estimations[0].available_recv_bandwidth); + ASSERT_EQ(2 * send_video_bitrate, info.bw_estimations[0].target_enc_bitrate); } TEST_F(WebRtcVideoEngineTestFake, TestSetAdaptInputToCpuUsage) { @@ -1395,309 +1850,6 @@ TEST_F(WebRtcVideoEngineTestFake, TestSetInvalidCpuThreshold) { } -///////////////////////// -// Tests with real ViE // -///////////////////////// - -// Tests that we can find codecs by name or id. -TEST_F(WebRtcVideoEngineTest, FindCodec) { - // We should not need to init engine in order to get codecs. - const std::vector<cricket::VideoCodec>& c = engine_.codecs(); - EXPECT_EQ(4U, c.size()); - - cricket::VideoCodec vp8(104, "VP8", 320, 200, 30, 0); - EXPECT_TRUE(engine_.FindCodec(vp8)); - - cricket::VideoCodec vp8_ci(104, "vp8", 320, 200, 30, 0); - EXPECT_TRUE(engine_.FindCodec(vp8)); - - cricket::VideoCodec vp8_diff_fr_diff_pref(104, "VP8", 320, 200, 50, 50); - EXPECT_TRUE(engine_.FindCodec(vp8_diff_fr_diff_pref)); - - cricket::VideoCodec vp8_diff_id(95, "VP8", 320, 200, 30, 0); - EXPECT_FALSE(engine_.FindCodec(vp8_diff_id)); - vp8_diff_id.id = 97; - EXPECT_TRUE(engine_.FindCodec(vp8_diff_id)); - - cricket::VideoCodec vp8_diff_res(104, "VP8", 320, 111, 30, 0); - EXPECT_FALSE(engine_.FindCodec(vp8_diff_res)); - - // PeerConnection doesn't negotiate the resolution at this point. - // Test that FindCodec can handle the case when width/height is 0. - cricket::VideoCodec vp8_zero_res(104, "VP8", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(vp8_zero_res)); - - cricket::VideoCodec red(101, "RED", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(red)); - - cricket::VideoCodec red_ci(101, "red", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(red)); - - cricket::VideoCodec fec(102, "ULPFEC", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(fec)); - - cricket::VideoCodec fec_ci(102, "ulpfec", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(fec)); - - cricket::VideoCodec rtx(96, "rtx", 0, 0, 30, 0); - EXPECT_TRUE(engine_.FindCodec(rtx)); -} - -TEST_F(WebRtcVideoEngineTest, RtxCodecHasAptSet) { - std::vector<cricket::VideoCodec>::const_iterator it; - bool apt_checked = false; - for (it = engine_.codecs().begin(); it != engine_.codecs().end(); ++it) { - if (_stricmp(cricket::kRtxCodecName, it->name.c_str()) && it->id != 96) { - continue; - } - int apt; - EXPECT_TRUE(it->GetParam("apt", &apt)); - EXPECT_EQ(100, apt); - apt_checked = true; - } - EXPECT_TRUE(apt_checked); -} - -TEST_F(WebRtcVideoEngineTest, StartupShutdown) { - EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); - engine_.Terminate(); -} - -TEST_PRE_VIDEOENGINE_INIT(WebRtcVideoEngineTest, ConstrainNewCodec) -TEST_POST_VIDEOENGINE_INIT(WebRtcVideoEngineTest, ConstrainNewCodec) - -TEST_PRE_VIDEOENGINE_INIT(WebRtcVideoEngineTest, ConstrainRunningCodec) -TEST_POST_VIDEOENGINE_INIT(WebRtcVideoEngineTest, ConstrainRunningCodec) - -// TODO(juberti): Figure out why ViE is munging the COM refcount. -#ifdef WIN32 -TEST_F(WebRtcVideoEngineTest, DISABLED_CheckCoInitialize) { - Base::CheckCoInitialize(); -} -#endif - -TEST_F(WebRtcVideoEngineTest, CreateChannel) { - EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); - cricket::VideoMediaChannel* channel = engine_.CreateChannel(NULL); - EXPECT_TRUE(channel != NULL); - delete channel; -} - -TEST_F(WebRtcVideoMediaChannelTest, SetRecvCodecs) { - std::vector<cricket::VideoCodec> codecs; - codecs.push_back(kVP8Codec); - EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); -} -TEST_F(WebRtcVideoMediaChannelTest, SetRecvCodecsWrongPayloadType) { - std::vector<cricket::VideoCodec> codecs; - codecs.push_back(kVP8Codec); - codecs[0].id = 99; - EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); -} -TEST_F(WebRtcVideoMediaChannelTest, SetRecvCodecsUnsupportedCodec) { - std::vector<cricket::VideoCodec> codecs; - codecs.push_back(kVP8Codec); - codecs.push_back(cricket::VideoCodec(101, "VP1", 640, 400, 30, 0)); - EXPECT_FALSE(channel_->SetRecvCodecs(codecs)); -} - -TEST_F(WebRtcVideoMediaChannelTest, SetSend) { - Base::SetSend(); -} -TEST_F(WebRtcVideoMediaChannelTest, SetSendWithoutCodecs) { - Base::SetSendWithoutCodecs(); -} -TEST_F(WebRtcVideoMediaChannelTest, SetSendSetsTransportBufferSizes) { - Base::SetSendSetsTransportBufferSizes(); -} - -TEST_F(WebRtcVideoMediaChannelTest, SendAndReceiveVp8Vga) { - SendAndReceive(cricket::VideoCodec(100, "VP8", 640, 400, 30, 0)); -} -TEST_F(WebRtcVideoMediaChannelTest, SendAndReceiveVp8Qvga) { - SendAndReceive(cricket::VideoCodec(100, "VP8", 320, 200, 30, 0)); -} -TEST_F(WebRtcVideoMediaChannelTest, SendAndReceiveH264SvcQqvga) { - SendAndReceive(cricket::VideoCodec(100, "VP8", 160, 100, 30, 0)); -} -TEST_F(WebRtcVideoMediaChannelTest, SendManyResizeOnce) { - SendManyResizeOnce(); -} - -TEST_F(WebRtcVideoMediaChannelTest, SendVp8HdAndReceiveAdaptedVp8Vga) { - EXPECT_TRUE(channel_->SetCapturer(kSsrc, NULL)); - channel_->UpdateAspectRatio(1280, 720); - video_capturer_.reset(new cricket::FakeVideoCapturer); - const std::vector<cricket::VideoFormat>* formats = - video_capturer_->GetSupportedFormats(); - cricket::VideoFormat capture_format_hd = (*formats)[0]; - EXPECT_EQ(cricket::CS_RUNNING, video_capturer_->Start(capture_format_hd)); - EXPECT_TRUE(channel_->SetCapturer(kSsrc, video_capturer_.get())); - - // Capture format HD -> adapt (OnOutputFormatRequest VGA) -> VGA. - cricket::VideoCodec codec(100, "VP8", 1280, 720, 30, 0); - EXPECT_TRUE(SetOneCodec(codec)); - codec.width /= 2; - codec.height /= 2; - EXPECT_TRUE(channel_->SetSendStreamFormat(kSsrc, cricket::VideoFormat( - codec.width, codec.height, - cricket::VideoFormat::FpsToInterval(codec.framerate), - cricket::FOURCC_ANY))); - EXPECT_TRUE(SetSend(true)); - EXPECT_TRUE(channel_->SetRender(true)); - EXPECT_EQ(0, renderer_.num_rendered_frames()); - EXPECT_TRUE(SendFrame()); - EXPECT_FRAME_WAIT(1, codec.width, codec.height, kTimeout); -} - -// TODO(juberti): Fix this test to tolerate missing stats. -TEST_F(WebRtcVideoMediaChannelTest, DISABLED_GetStats) { - Base::GetStats(); -} - -// TODO(juberti): Fix this test to tolerate missing stats. -TEST_F(WebRtcVideoMediaChannelTest, DISABLED_GetStatsMultipleRecvStreams) { - Base::GetStatsMultipleRecvStreams(); -} - -TEST_F(WebRtcVideoMediaChannelTest, GetStatsMultipleSendStreams) { - Base::GetStatsMultipleSendStreams(); -} - -TEST_F(WebRtcVideoMediaChannelTest, SetSendBandwidth) { - Base::SetSendBandwidth(); -} -TEST_F(WebRtcVideoMediaChannelTest, SetSendSsrc) { - Base::SetSendSsrc(); -} -TEST_F(WebRtcVideoMediaChannelTest, SetSendSsrcAfterSetCodecs) { - Base::SetSendSsrcAfterSetCodecs(); -} - -TEST_F(WebRtcVideoMediaChannelTest, SetRenderer) { - Base::SetRenderer(); -} - -TEST_F(WebRtcVideoMediaChannelTest, AddRemoveRecvStreams) { - Base::AddRemoveRecvStreams(); -} - -TEST_F(WebRtcVideoMediaChannelTest, AddRemoveRecvStreamAndRender) { - Base::AddRemoveRecvStreamAndRender(); -} - -TEST_F(WebRtcVideoMediaChannelTest, AddRemoveRecvStreamsNoConference) { - Base::AddRemoveRecvStreamsNoConference(); -} - -TEST_F(WebRtcVideoMediaChannelTest, AddRemoveSendStreams) { - Base::AddRemoveSendStreams(); -} - -TEST_F(WebRtcVideoMediaChannelTest, SimulateConference) { - Base::SimulateConference(); -} - -TEST_F(WebRtcVideoMediaChannelTest, AddRemoveCapturer) { - Base::AddRemoveCapturer(); -} - -TEST_F(WebRtcVideoMediaChannelTest, RemoveCapturerWithoutAdd) { - Base::RemoveCapturerWithoutAdd(); -} - -TEST_F(WebRtcVideoMediaChannelTest, AddRemoveCapturerMultipleSources) { - Base::AddRemoveCapturerMultipleSources(); -} - -// This test verifies DSCP settings are properly applied on video media channel. -TEST_F(WebRtcVideoMediaChannelTest, TestSetDscpOptions) { - talk_base::scoped_ptr<cricket::FakeNetworkInterface> network_interface( - new cricket::FakeNetworkInterface); - channel_->SetInterface(network_interface.get()); - cricket::VideoOptions options; - options.dscp.Set(true); - EXPECT_TRUE(channel_->SetOptions(options)); - EXPECT_EQ(talk_base::DSCP_AF41, network_interface->dscp()); - options.dscp.Set(false); - EXPECT_TRUE(channel_->SetOptions(options)); - EXPECT_EQ(talk_base::DSCP_DEFAULT, network_interface->dscp()); - channel_->SetInterface(NULL); -} - - -TEST_F(WebRtcVideoMediaChannelTest, SetOptionsSucceedsWhenSending) { - cricket::VideoOptions options; - options.conference_mode.Set(true); - EXPECT_TRUE(channel_->SetOptions(options)); - - // Verify SetOptions returns true on a different options. - cricket::VideoOptions options2; - options2.adapt_input_to_cpu_usage.Set(true); - EXPECT_TRUE(channel_->SetOptions(options2)); - - // Set send codecs on the channel and start sending. - std::vector<cricket::VideoCodec> codecs; - codecs.push_back(kVP8Codec); - EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - EXPECT_TRUE(channel_->SetSend(true)); - - // Verify SetOptions returns true if channel is already sending. - cricket::VideoOptions options3; - options3.conference_mode.Set(true); - EXPECT_TRUE(channel_->SetOptions(options3)); -} - -// Tests empty StreamParams is rejected. -TEST_F(WebRtcVideoMediaChannelTest, RejectEmptyStreamParams) { - Base::RejectEmptyStreamParams(); -} - - -TEST_F(WebRtcVideoMediaChannelTest, AdaptResolution16x10) { - Base::AdaptResolution16x10(); -} - -TEST_F(WebRtcVideoMediaChannelTest, AdaptResolution4x3) { - Base::AdaptResolution4x3(); -} - -TEST_F(WebRtcVideoMediaChannelTest, MuteStream) { - Base::MuteStream(); -} - -TEST_F(WebRtcVideoMediaChannelTest, MultipleSendStreams) { - Base::MultipleSendStreams(); -} - -// TODO(juberti): Restore this test once we support sending 0 fps. -TEST_F(WebRtcVideoMediaChannelTest, DISABLED_AdaptDropAllFrames) { - Base::AdaptDropAllFrames(); -} -// TODO(juberti): Understand why we get decode errors on this test. -TEST_F(WebRtcVideoMediaChannelTest, DISABLED_AdaptFramerate) { - Base::AdaptFramerate(); -} - -TEST_F(WebRtcVideoMediaChannelTest, SetSendStreamFormat0x0) { - Base::SetSendStreamFormat0x0(); -} - -// TODO(zhurunz): Fix the flakey test. -TEST_F(WebRtcVideoMediaChannelTest, DISABLED_SetSendStreamFormat) { - Base::SetSendStreamFormat(); -} - -TEST_F(WebRtcVideoMediaChannelTest, TwoStreamsSendAndReceive) { - Base::TwoStreamsSendAndReceive(cricket::VideoCodec(100, "VP8", 640, 400, 30, - 0)); -} - -TEST_F(WebRtcVideoMediaChannelTest, TwoStreamsReUseFirstStream) { - Base::TwoStreamsReUseFirstStream(cricket::VideoCodec(100, "VP8", 640, 400, 30, - 0)); -} - TEST_F(WebRtcVideoEngineTestFake, ResetCodecOnScreencast) { EXPECT_TRUE(SetupEngine()); cricket::VideoOptions options; @@ -1712,7 +1864,7 @@ TEST_F(WebRtcVideoEngineTestFake, ResetCodecOnScreencast) { cricket::StreamParams::CreateLegacy(123))); EXPECT_TRUE(channel_->SetSendCodecs(codec_list)); EXPECT_TRUE(channel_->SetSend(true)); - EXPECT_EQ(1, vie_.num_set_send_codecs()); + EXPECT_EQ(1, vie_.GetNumSetSendCodecs()); webrtc::VideoCodec gcodec; memset(&gcodec, 0, sizeof(gcodec)); @@ -1723,7 +1875,7 @@ TEST_F(WebRtcVideoEngineTestFake, ResetCodecOnScreencast) { // Send a screencast frame with the same size. // Verify that denoising is turned off. SendI420ScreencastFrame(kVP8Codec.width, kVP8Codec.height); - EXPECT_EQ(2, vie_.num_set_send_codecs()); + EXPECT_EQ(2, vie_.GetNumSetSendCodecs()); EXPECT_EQ(0, vie_.GetSendCodec(channel_num, gcodec)); EXPECT_FALSE(gcodec.codecSpecific.VP8.denoisingOn); } @@ -1834,11 +1986,9 @@ TEST_F(WebRtcVideoEngineTestFake, DontRegisterEncoderMultipleTimes) { EXPECT_TRUE(vie_.ExternalEncoderRegistered(channel_num, 100)); EXPECT_EQ(1, vie_.GetNumExternalEncoderRegistered(channel_num)); - EXPECT_EQ(1, encoder_factory_.GetNumCreatedEncoders()); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); EXPECT_EQ(1, vie_.GetNumExternalEncoderRegistered(channel_num)); - EXPECT_EQ(1, encoder_factory_.GetNumCreatedEncoders()); // Remove stream previously added to free the external encoder instance. EXPECT_TRUE(channel_->RemoveSendStream(kSsrc)); @@ -1898,8 +2048,8 @@ TEST_F(WebRtcVideoEngineTestFake, DontRegisterEncoderForNonVP8) { EXPECT_EQ(0, vie_.GetNumExternalEncoderRegistered(channel_num)); } -// Test that NACK and REMB are enabled for external codec. -TEST_F(WebRtcVideoEngineTestFake, FeedbackParamsForNonVP8) { +// Test that NACK, PLI and REMB are enabled for external codec. +TEST_F(WebRtcVideoEngineTestFake, ExternalCodecFeedbackParams) { encoder_factory_.AddSupportedVideoCodecType(webrtc::kVideoCodecGeneric, "GENERIC"); engine_.SetExternalEncoderFactory(&encoder_factory_); @@ -1910,15 +2060,7 @@ TEST_F(WebRtcVideoEngineTestFake, FeedbackParamsForNonVP8) { // The external codec will appear at last. size_t pos = codecs.size() - 1; EXPECT_EQ("GENERIC", codecs[pos].name); - EXPECT_TRUE(codecs[pos].HasFeedbackParam( - cricket::FeedbackParam(cricket::kRtcpFbParamNack, - cricket::kParamValueEmpty))); - EXPECT_TRUE(codecs[pos].HasFeedbackParam( - cricket::FeedbackParam(cricket::kRtcpFbParamRemb, - cricket::kParamValueEmpty))); - EXPECT_TRUE(codecs[pos].HasFeedbackParam( - cricket::FeedbackParam(cricket::kRtcpFbParamCcm, - cricket::kRtcpFbCcmParamFir))); + VerifyCodecFeedbackParams(codecs[pos]); } // Test external codec with be added to the end of the supported codec list. @@ -1978,7 +2120,6 @@ TEST_F(WebRtcVideoEngineTestFake, UpdateEncoderCodecsAfterSetFactory) { EXPECT_TRUE(vie_.ExternalEncoderRegistered(channel_num, 100)); EXPECT_EQ(1, vie_.GetNumExternalEncoderRegistered(channel_num)); - EXPECT_EQ(1, encoder_factory_.GetNumCreatedEncoders()); // Remove stream previously added to free the external encoder instance. EXPECT_TRUE(channel_->RemoveSendStream(kSsrc)); @@ -2021,3 +2162,350 @@ TEST_F(WebRtcVideoEngineTestFake, CaptureFrameTimestampToNtpTimestamp) { EXPECT_EQ(0, vie_.GetCaptureLastTimestamp(capture_id)); } #endif + +///////////////////////// +// Tests with real ViE // +///////////////////////// + +// Tests that we can find codecs by name or id. +TEST_F(WebRtcVideoEngineTest, FindCodec) { + // We should not need to init engine in order to get codecs. + const std::vector<cricket::VideoCodec>& c = engine_.codecs(); + EXPECT_EQ(4U, c.size()); + + cricket::VideoCodec vp8(104, "VP8", 320, 200, 30, 0); + EXPECT_TRUE(engine_.FindCodec(vp8)); + + cricket::VideoCodec vp8_ci(104, "vp8", 320, 200, 30, 0); + EXPECT_TRUE(engine_.FindCodec(vp8)); + + cricket::VideoCodec vp8_diff_fr_diff_pref(104, "VP8", 320, 200, 50, 50); + EXPECT_TRUE(engine_.FindCodec(vp8_diff_fr_diff_pref)); + + cricket::VideoCodec vp8_diff_id(95, "VP8", 320, 200, 30, 0); + EXPECT_FALSE(engine_.FindCodec(vp8_diff_id)); + vp8_diff_id.id = 97; + EXPECT_TRUE(engine_.FindCodec(vp8_diff_id)); + + cricket::VideoCodec vp8_diff_res(104, "VP8", 320, 111, 30, 0); + EXPECT_FALSE(engine_.FindCodec(vp8_diff_res)); + + // PeerConnection doesn't negotiate the resolution at this point. + // Test that FindCodec can handle the case when width/height is 0. + cricket::VideoCodec vp8_zero_res(104, "VP8", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(vp8_zero_res)); + + cricket::VideoCodec red(101, "RED", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(red)); + + cricket::VideoCodec red_ci(101, "red", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(red)); + + cricket::VideoCodec fec(102, "ULPFEC", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(fec)); + + cricket::VideoCodec fec_ci(102, "ulpfec", 0, 0, 30, 0); + EXPECT_TRUE(engine_.FindCodec(fec)); + + cricket::VideoCodec rtx(96, "rtx", 0, 0, 30, 0); + rtx.SetParam("apt", kVP8Codec.id); + EXPECT_TRUE(engine_.FindCodec(rtx)); +} + +TEST_F(WebRtcVideoEngineTest, RtxCodecHasAptSet) { + std::vector<cricket::VideoCodec>::const_iterator it; + bool apt_checked = false; + for (it = engine_.codecs().begin(); it != engine_.codecs().end(); ++it) { + if (_stricmp(cricket::kRtxCodecName, it->name.c_str()) && it->id != 96) { + continue; + } + int apt; + EXPECT_TRUE(it->GetParam("apt", &apt)); + EXPECT_EQ(100, apt); + apt_checked = true; + } + EXPECT_TRUE(apt_checked); +} + +TEST_F(WebRtcVideoEngineTest, StartupShutdown) { + EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); + engine_.Terminate(); +} + +TEST_PRE_VIDEOENGINE_INIT(WebRtcVideoEngineTest, ConstrainNewCodec) +TEST_POST_VIDEOENGINE_INIT(WebRtcVideoEngineTest, ConstrainNewCodec) + +TEST_PRE_VIDEOENGINE_INIT(WebRtcVideoEngineTest, ConstrainRunningCodec) +TEST_POST_VIDEOENGINE_INIT(WebRtcVideoEngineTest, ConstrainRunningCodec) + +// TODO(juberti): Figure out why ViE is munging the COM refcount. +#ifdef WIN32 +TEST_F(WebRtcVideoEngineTest, DISABLED_CheckCoInitialize) { + Base::CheckCoInitialize(); +} +#endif + +TEST_F(WebRtcVideoEngineTest, CreateChannel) { + EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); + cricket::VideoMediaChannel* channel = engine_.CreateChannel(NULL); + EXPECT_TRUE(channel != NULL); + delete channel; +} + +TEST_F(WebRtcVideoMediaChannelTest, SetRecvCodecs) { + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVP8Codec); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); +} +TEST_F(WebRtcVideoMediaChannelTest, SetRecvCodecsWrongPayloadType) { + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVP8Codec); + codecs[0].id = 99; + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); +} +TEST_F(WebRtcVideoMediaChannelTest, SetRecvCodecsUnsupportedCodec) { + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVP8Codec); + codecs.push_back(cricket::VideoCodec(101, "VP1", 640, 400, 30, 0)); + EXPECT_FALSE(channel_->SetRecvCodecs(codecs)); +} + +TEST_F(WebRtcVideoMediaChannelTest, GetRtpSendTimeExtension) { + // Enable RTP timestamp extension. + const int id = 12; + std::vector<cricket::RtpHeaderExtension> extensions; + extensions.push_back(cricket::RtpHeaderExtension( + "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", id)); + + // Verify the send extension id. + EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); + EXPECT_EQ(id, channel_->GetRtpSendTimeExtnId()); +} + +TEST_F(WebRtcVideoMediaChannelTest, SetSend) { + Base::SetSend(); +} +TEST_F(WebRtcVideoMediaChannelTest, SetSendWithoutCodecs) { + Base::SetSendWithoutCodecs(); +} +TEST_F(WebRtcVideoMediaChannelTest, SetSendSetsTransportBufferSizes) { + Base::SetSendSetsTransportBufferSizes(); +} + +TEST_F(WebRtcVideoMediaChannelTest, SendAndReceiveVp8Vga) { + SendAndReceive(cricket::VideoCodec(100, "VP8", 640, 400, 30, 0)); +} +TEST_F(WebRtcVideoMediaChannelTest, SendAndReceiveVp8Qvga) { + SendAndReceive(cricket::VideoCodec(100, "VP8", 320, 200, 30, 0)); +} +TEST_F(WebRtcVideoMediaChannelTest, SendAndReceiveH264SvcQqvga) { + SendAndReceive(cricket::VideoCodec(100, "VP8", 160, 100, 30, 0)); +} +TEST_F(WebRtcVideoMediaChannelTest, SendManyResizeOnce) { + SendManyResizeOnce(); +} + +TEST_F(WebRtcVideoMediaChannelTest, DISABLED_SendVp8HdAndReceiveAdaptedVp8Vga) { + EXPECT_TRUE(channel_->SetCapturer(kSsrc, NULL)); + channel_->UpdateAspectRatio(1280, 720); + video_capturer_.reset(new cricket::FakeVideoCapturer); + const std::vector<cricket::VideoFormat>* formats = + video_capturer_->GetSupportedFormats(); + cricket::VideoFormat capture_format_hd = (*formats)[0]; + EXPECT_EQ(cricket::CS_RUNNING, video_capturer_->Start(capture_format_hd)); + EXPECT_TRUE(channel_->SetCapturer(kSsrc, video_capturer_.get())); + + // Capture format HD -> adapt (OnOutputFormatRequest VGA) -> VGA. + cricket::VideoCodec codec(100, "VP8", 1280, 720, 30, 0); + EXPECT_TRUE(SetOneCodec(codec)); + codec.width /= 2; + codec.height /= 2; + EXPECT_TRUE(SetSend(true)); + EXPECT_TRUE(channel_->SetRender(true)); + EXPECT_EQ(0, renderer_.num_rendered_frames()); + EXPECT_TRUE(SendFrame()); + EXPECT_FRAME_WAIT(1, codec.width, codec.height, kTimeout); +} + +#ifdef USE_WEBRTC_DEV_BRANCH +TEST_F(WebRtcVideoMediaChannelTest, GetStats) { +#else +TEST_F(WebRtcVideoMediaChannelTest, DISABLED_GetStats) { +#endif + Base::GetStats(); +} + +#ifdef USE_WEBRTC_DEV_BRANCH +TEST_F(WebRtcVideoMediaChannelTest, GetStatsMultipleRecvStreams) { +#else +TEST_F(WebRtcVideoMediaChannelTest, DISABLED_GetStatsMultipleRecvStreams) { +#endif + Base::GetStatsMultipleRecvStreams(); +} + +TEST_F(WebRtcVideoMediaChannelTest, GetStatsMultipleSendStreams) { + Base::GetStatsMultipleSendStreams(); +} + +TEST_F(WebRtcVideoMediaChannelTest, SetSendBandwidth) { + Base::SetSendBandwidth(); +} +TEST_F(WebRtcVideoMediaChannelTest, SetSendSsrc) { + Base::SetSendSsrc(); +} +TEST_F(WebRtcVideoMediaChannelTest, SetSendSsrcAfterSetCodecs) { + Base::SetSendSsrcAfterSetCodecs(); +} + +TEST_F(WebRtcVideoMediaChannelTest, SetRenderer) { + Base::SetRenderer(); +} + +TEST_F(WebRtcVideoMediaChannelTest, AddRemoveRecvStreams) { + Base::AddRemoveRecvStreams(); +} + +TEST_F(WebRtcVideoMediaChannelTest, AddRemoveRecvStreamAndRender) { + Base::AddRemoveRecvStreamAndRender(); +} + +TEST_F(WebRtcVideoMediaChannelTest, AddRemoveRecvStreamsNoConference) { + Base::AddRemoveRecvStreamsNoConference(); +} + +TEST_F(WebRtcVideoMediaChannelTest, AddRemoveSendStreams) { + Base::AddRemoveSendStreams(); +} + +TEST_F(WebRtcVideoMediaChannelTest, SimulateConference) { + Base::SimulateConference(); +} + +TEST_F(WebRtcVideoMediaChannelTest, AddRemoveCapturer) { + Base::AddRemoveCapturer(); +} + +TEST_F(WebRtcVideoMediaChannelTest, RemoveCapturerWithoutAdd) { + Base::RemoveCapturerWithoutAdd(); +} + +TEST_F(WebRtcVideoMediaChannelTest, AddRemoveCapturerMultipleSources) { + Base::AddRemoveCapturerMultipleSources(); +} + +// This test verifies DSCP settings are properly applied on video media channel. +TEST_F(WebRtcVideoMediaChannelTest, TestSetDscpOptions) { + talk_base::scoped_ptr<cricket::FakeNetworkInterface> network_interface( + new cricket::FakeNetworkInterface); + channel_->SetInterface(network_interface.get()); + cricket::VideoOptions options; + options.dscp.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + EXPECT_EQ(talk_base::DSCP_AF41, network_interface->dscp()); + // Verify previous value is not modified if dscp option is not set. + cricket::VideoOptions options1; + EXPECT_TRUE(channel_->SetOptions(options1)); + EXPECT_EQ(talk_base::DSCP_AF41, network_interface->dscp()); + options.dscp.Set(false); + EXPECT_TRUE(channel_->SetOptions(options)); + EXPECT_EQ(talk_base::DSCP_DEFAULT, network_interface->dscp()); + channel_->SetInterface(NULL); +} + + +TEST_F(WebRtcVideoMediaChannelTest, SetOptionsSucceedsWhenSending) { + cricket::VideoOptions options; + options.conference_mode.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + + // Verify SetOptions returns true on a different options. + cricket::VideoOptions options2; + options2.adapt_input_to_cpu_usage.Set(true); + EXPECT_TRUE(channel_->SetOptions(options2)); + + // Set send codecs on the channel and start sending. + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVP8Codec); + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->SetSend(true)); + + // Verify SetOptions returns true if channel is already sending. + cricket::VideoOptions options3; + options3.conference_mode.Set(true); + EXPECT_TRUE(channel_->SetOptions(options3)); +} + +// Tests empty StreamParams is rejected. +TEST_F(WebRtcVideoMediaChannelTest, RejectEmptyStreamParams) { + Base::RejectEmptyStreamParams(); +} + + +TEST_F(WebRtcVideoMediaChannelTest, AdaptResolution16x10) { + Base::AdaptResolution16x10(); +} + +TEST_F(WebRtcVideoMediaChannelTest, AdaptResolution4x3) { + Base::AdaptResolution4x3(); +} + +TEST_F(WebRtcVideoMediaChannelTest, MuteStream) { + Base::MuteStream(); +} + +TEST_F(WebRtcVideoMediaChannelTest, MultipleSendStreams) { + Base::MultipleSendStreams(); +} + +// TODO(juberti): Restore this test once we support sending 0 fps. +TEST_F(WebRtcVideoMediaChannelTest, DISABLED_AdaptDropAllFrames) { + Base::AdaptDropAllFrames(); +} +// TODO(juberti): Understand why we get decode errors on this test. +TEST_F(WebRtcVideoMediaChannelTest, DISABLED_AdaptFramerate) { + Base::AdaptFramerate(); +} + +TEST_F(WebRtcVideoMediaChannelTest, SetSendStreamFormat0x0) { + Base::SetSendStreamFormat0x0(); +} + +// TODO(zhurunz): Fix the flakey test. +TEST_F(WebRtcVideoMediaChannelTest, DISABLED_SetSendStreamFormat) { + Base::SetSendStreamFormat(); +} + +TEST_F(WebRtcVideoMediaChannelTest, TwoStreamsSendAndReceive) { + Base::TwoStreamsSendAndReceive(cricket::VideoCodec(100, "VP8", 640, 400, 30, + 0)); +} + +TEST_F(WebRtcVideoMediaChannelTest, TwoStreamsReUseFirstStream) { + Base::TwoStreamsReUseFirstStream(cricket::VideoCodec(100, "VP8", 640, 400, 30, + 0)); +} + +TEST_F(WebRtcVideoMediaChannelTest, DISABLED_TwoStreamsSendAndUnsignalledRecv) { + Base::TwoStreamsSendAndUnsignalledRecv(cricket::VideoCodec(100, "VP8", 640, + 400, 30, 0)); +} + +TEST_F(WebRtcVideoMediaChannelTest, + TwoStreamsSendAndFailUnsignalledRecv) { + webrtc::Trace::set_level_filter(webrtc::kTraceAll); + Base::TwoStreamsSendAndFailUnsignalledRecv( + cricket::VideoCodec(100, "VP8", 640, 400, 30, 0)); +} + +TEST_F(WebRtcVideoMediaChannelTest, + TwoStreamsSendAndFailUnsignalledRecvInOneToOne) { + Base::TwoStreamsSendAndFailUnsignalledRecvInOneToOne( + cricket::VideoCodec(100, "VP8", 640, 400, 30, 0)); +} + +TEST_F(WebRtcVideoMediaChannelTest, + TwoStreamsAddAndRemoveUnsignalledRecv) { + Base::TwoStreamsAddAndRemoveUnsignalledRecv(cricket::VideoCodec(100, "VP8", + 640, 400, 30, + 0)); +} diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.cc index 16aa4cdcebc..1cc6fe97122 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.cc @@ -36,12 +36,6 @@ namespace cricket { -static const int kWatermarkWidth = 8; -static const int kWatermarkHeight = 8; -static const int kWatermarkOffsetFromLeft = 8; -static const int kWatermarkOffsetFromBottom = 8; -static const unsigned char kWatermarkMaxYValue = 64; - // Class that wraps ownerhip semantics of a buffer passed to it. // * Buffers passed using Attach() become owned by this FrameBuffer and will be // destroyed on FrameBuffer destruction. @@ -296,29 +290,6 @@ void WebRtcVideoFrame::Attach( rotation_ = rotation; } -// Add a square watermark near the left-low corner. clamp Y. -// Returns false on error. -bool WebRtcVideoFrame::AddWatermark() { - size_t w = GetWidth(); - size_t h = GetHeight(); - - if (w < kWatermarkWidth + kWatermarkOffsetFromLeft || - h < kWatermarkHeight + kWatermarkOffsetFromBottom) { - return false; - } - - uint8* buffer = GetYPlane(); - for (size_t x = kWatermarkOffsetFromLeft; - x < kWatermarkOffsetFromLeft + kWatermarkWidth; ++x) { - for (size_t y = h - kWatermarkOffsetFromBottom - kWatermarkHeight; - y < h - kWatermarkOffsetFromBottom; ++y) { - buffer[y * w + x] = - talk_base::_min(buffer[y * w + x], kWatermarkMaxYValue); - } - } - return true; -} - webrtc::VideoFrame* WebRtcVideoFrame::frame() { return video_buffer_->frame(); } diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.h b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.h index 8191a58d875..4ba7ab65b57 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.h @@ -67,7 +67,6 @@ class WebRtcVideoFrame : public VideoFrame { size_t pixel_width, size_t pixel_height, int64 elapsed_time, int64 time_stamp, int rotation); - bool AddWatermark(); webrtc::VideoFrame* frame(); const webrtc::VideoFrame* frame() const; diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.cc index 51da9ac6b14..f1460a6cb8a 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.cc @@ -107,12 +107,6 @@ static const int kDefaultSoundclipDeviceId = -2; static const int kDefaultAudioDeviceId = 0; #endif -// extension header for audio levels, as defined in -// http://tools.ietf.org/html/draft-ietf-avtext-client-to-mixer-audio-level-03 -static const char kRtpAudioLevelHeaderExtension[] = - "urn:ietf:params:rtp-hdrext:ssrc-audio-level"; -static const int kRtpAudioLevelHeaderExtensionId = 1; - static const char kIsacCodecName[] = "ISAC"; static const char kL16CodecName[] = "L16"; // Codec parameters for Opus. @@ -194,6 +188,18 @@ static bool IsCodecMultiRate(const webrtc::CodecInst& codec) { return false; } +static bool IsTelephoneEventCodec(const std::string& name) { + return _stricmp(name.c_str(), "telephone-event") == 0; +} + +static bool IsCNCodec(const std::string& name) { + return _stricmp(name.c_str(), "CN") == 0; +} + +static bool IsRedCodec(const std::string& name) { + return _stricmp(name.c_str(), "red") == 0; +} + static bool FindCodec(const std::vector<AudioCodec>& codecs, const AudioCodec& codec, AudioCodec* found_codec) { @@ -229,8 +235,9 @@ static AudioOptions GetDefaultEngineOptions() { options.adjust_agc_delta.Set(0); options.experimental_agc.Set(false); options.experimental_aec.Set(false); + options.experimental_ns.Set(false); options.aec_dump.Set(false); - options.experimental_acm.Set(false); + options.opus_fec.Set(false); return options; } @@ -334,7 +341,6 @@ WebRtcVoiceEngine::WebRtcVoiceEngine() log_filter_(SeverityToFilter(kDefaultLogSeverity)), is_dumping_aec_(false), desired_local_monitor_enable_(false), - use_experimental_acm_(false), tx_processor_ssrc_(0), rx_processor_ssrc_(0) { Construct(); @@ -352,7 +358,6 @@ WebRtcVoiceEngine::WebRtcVoiceEngine(VoEWrapper* voe_wrapper, log_filter_(SeverityToFilter(kDefaultLogSeverity)), is_dumping_aec_(false), desired_local_monitor_enable_(false), - use_experimental_acm_(false), tx_processor_ssrc_(0), rx_processor_ssrc_(0) { Construct(); @@ -378,12 +383,11 @@ void WebRtcVoiceEngine::Construct() { // Load our RTP Header extensions. rtp_header_extensions_.push_back( RtpHeaderExtension(kRtpAudioLevelHeaderExtension, - kRtpAudioLevelHeaderExtensionId)); + kRtpAudioLevelHeaderExtensionDefaultId)); + rtp_header_extensions_.push_back( + RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension, + kRtpAbsoluteSenderTimeHeaderExtensionDefaultId)); options_ = GetDefaultEngineOptions(); - - // Initialize the VoE Configuration to the default ACM. - voe_config_.Set<webrtc::AudioCodingModuleFactory>( - new webrtc::AudioCodingModuleFactory); } static bool IsOpus(const AudioCodec& codec) { @@ -396,12 +400,8 @@ static bool IsIsac(const AudioCodec& codec) { // True if params["stereo"] == "1" static bool IsOpusStereoEnabled(const AudioCodec& codec) { - CodecParameterMap::const_iterator param = - codec.params.find(kCodecParamStereo); - if (param == codec.params.end()) { - return false; - } - return param->second == kParamValueTrue; + int value; + return codec.GetParam(kCodecParamStereo, &value) && value == 1; } static bool IsValidOpusBitrate(int bitrate) { @@ -423,6 +423,22 @@ static int GetOpusBitrateFromParams(const AudioCodec& codec) { return bitrate; } +// Return true params[kCodecParamUseInbandFec] == kParamValueTrue, false +// otherwise. +static bool IsOpusFecEnabled(const AudioCodec& codec) { + int value; + return codec.GetParam(kCodecParamUseInbandFec, &value) && value == 1; +} + +// Set params[kCodecParamUseInbandFec]. Caller should make sure codec is Opus. +static void SetOpusFec(AudioCodec *codec, bool opus_fec) { + if (opus_fec) { + codec->params[kCodecParamUseInbandFec] = kParamValueTrue; + } else { + codec->params.erase(kCodecParamUseInbandFec); + } +} + void WebRtcVoiceEngine::ConstructCodecs() { LOG(LS_INFO) << "WebRtc VoiceEngine codecs:"; int ncodecs = voe_wrapper_->codec()->NumOfCodecs(); @@ -467,6 +483,7 @@ void WebRtcVoiceEngine::ConstructCodecs() { } // TODO(hellner): Add ptime, sprop-stereo, stereo and useinbandfec // when they can be set to values other than the default. + SetOpusFec(&codec, false); } codecs_.push_back(codec); } else { @@ -717,16 +734,11 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { options.typing_detection.Set(false); options.experimental_agc.Set(false); options.experimental_aec.Set(false); + options.experimental_ns.Set(false); #endif LOG(LS_INFO) << "Applying audio options: " << options.ToString(); - // Configure whether ACM1 or ACM2 is used. - bool enable_acm2 = false; - if (options.experimental_acm.Get(&enable_acm2)) { - EnableExperimentalAcm(enable_acm2); - } - webrtc::VoEAudioProcessing* voep = voe_wrapper_->processing(); bool echo_cancellation; @@ -804,8 +816,26 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { } } + bool experimental_ns; + if (options.experimental_ns.Get(&experimental_ns)) { + webrtc::AudioProcessing* audioproc = + voe_wrapper_->base()->audio_processing(); + // We check audioproc for the benefit of tests, since FakeWebRtcVoiceEngine + // returns NULL on audio_processing(). + if (audioproc) { + if (audioproc->EnableExperimentalNs(experimental_ns) == -1) { + LOG_RTCERR1(EnableExperimentalNs, experimental_ns); + return false; + } + } else { + LOG(LS_VERBOSE) << "Experimental noise suppression set to " + << experimental_ns; + } + } + bool highpass_filter; if (options.highpass_filter.Get(&highpass_filter)) { + LOG(LS_INFO) << "High pass filter enabled? " << highpass_filter; if (voep->EnableHighPassFilter(highpass_filter) == -1) { LOG_RTCERR1(SetHighpassFilterStatus, highpass_filter); return false; @@ -814,6 +844,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { bool stereo_swapping; if (options.stereo_swapping.Get(&stereo_swapping)) { + LOG(LS_INFO) << "Stereo swapping enabled? " << stereo_swapping; voep->EnableStereoChannelSwapping(stereo_swapping); if (voep->IsStereoChannelSwappingEnabled() != stereo_swapping) { LOG_RTCERR1(EnableStereoChannelSwapping, stereo_swapping); @@ -823,6 +854,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { bool typing_detection; if (options.typing_detection.Get(&typing_detection)) { + LOG(LS_INFO) << "Typing detection is enabled? " << typing_detection; if (voep->SetTypingDetectionStatus(typing_detection) == -1) { // In case of error, log the info and continue LOG_RTCERR1(SetTypingDetectionStatus, typing_detection); @@ -831,6 +863,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { int adjust_agc_delta; if (options.adjust_agc_delta.Get(&adjust_agc_delta)) { + LOG(LS_INFO) << "Adjust agc delta is " << adjust_agc_delta; if (!AdjustAgcLevel(adjust_agc_delta)) { return false; } @@ -838,6 +871,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { bool aec_dump; if (options.aec_dump.Get(&aec_dump)) { + LOG(LS_INFO) << "Aec dump is enabled? " << aec_dump; if (aec_dump) StartAecDump(kAecDumpByAudioOptionFilename); else @@ -846,6 +880,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { bool experimental_aec; if (options.experimental_aec.Get(&experimental_aec)) { + LOG(LS_INFO) << "Experimental aec is " << experimental_aec; webrtc::AudioProcessing* audioproc = voe_wrapper_->base()->audio_processing(); // We check audioproc for the benefit of tests, since FakeWebRtcVoiceEngine @@ -860,6 +895,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { uint32 recording_sample_rate; if (options.recording_sample_rate.Get(&recording_sample_rate)) { + LOG(LS_INFO) << "Recording sample rate is " << recording_sample_rate; if (voe_wrapper_->hw()->SetRecordingSampleRate(recording_sample_rate)) { LOG_RTCERR1(SetRecordingSampleRate, recording_sample_rate); } @@ -867,11 +903,21 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { uint32 playout_sample_rate; if (options.playout_sample_rate.Get(&playout_sample_rate)) { + LOG(LS_INFO) << "Playout sample rate is " << playout_sample_rate; if (voe_wrapper_->hw()->SetPlayoutSampleRate(playout_sample_rate)) { LOG_RTCERR1(SetPlayoutSampleRate, playout_sample_rate); } } + bool opus_fec = false; + if (options.opus_fec.Get(&opus_fec)) { + LOG(LS_INFO) << "Opus FEC is enabled? " << opus_fec; + for (std::vector<AudioCodec>::iterator it = codecs_.begin(); + it != codecs_.end(); ++it) { + if (IsOpus(*it)) + SetOpusFec(&(*it), opus_fec); + } + } return true; } @@ -1261,21 +1307,6 @@ bool WebRtcVoiceEngine::ShouldIgnoreTrace(const std::string& trace) { return false; } -void WebRtcVoiceEngine::EnableExperimentalAcm(bool enable) { - if (enable == use_experimental_acm_) - return; - if (enable) { - LOG(LS_INFO) << "VoiceEngine is set to use new ACM (ACM2 + NetEq4)."; - voe_config_.Set<webrtc::AudioCodingModuleFactory>( - new webrtc::NewAudioCodingModuleFactory()); - } else { - LOG(LS_INFO) << "VoiceEngine is set to use legacy ACM (ACM1 + Neteq3)."; - voe_config_.Set<webrtc::AudioCodingModuleFactory>( - new webrtc::AudioCodingModuleFactory()); - } - use_experimental_acm_ = enable; -} - void WebRtcVoiceEngine::Print(webrtc::TraceLevel level, const char* trace, int length) { talk_base::LoggingSeverity sev = talk_base::LS_VERBOSE; @@ -1433,6 +1464,25 @@ bool WebRtcVoiceEngine::SetAudioDeviceModule(webrtc::AudioDeviceModule* adm, return true; } +bool WebRtcVoiceEngine::StartAecDump(talk_base::PlatformFile file) { + FILE* aec_dump_file_stream = talk_base::FdopenPlatformFileForWriting(file); + if (!aec_dump_file_stream) { + LOG(LS_ERROR) << "Could not open AEC dump file stream."; + if (!talk_base::ClosePlatformFile(file)) + LOG(LS_WARNING) << "Could not close file."; + return false; + } + StopAecDump(); + if (voe_wrapper_->processing()->StartDebugRecording(aec_dump_file_stream) != + webrtc::AudioProcessing::kNoError) { + LOG_RTCERR0(StartDebugRecording); + fclose(aec_dump_file_stream); + return false; + } + is_dumping_aec_ = true; + return true; +} + bool WebRtcVoiceEngine::RegisterProcessor( uint32 ssrc, VoiceProcessor* voice_processor, @@ -1590,7 +1640,7 @@ void WebRtcVoiceEngine::StartAecDump(const std::string& filename) { // Start dumping AEC when we are not dumping. if (voe_wrapper_->processing()->StartDebugRecording( filename.c_str()) != webrtc::AudioProcessing::kNoError) { - LOG_RTCERR0(StartDebugRecording); + LOG_RTCERR1(StartDebugRecording, filename.c_str()); } else { is_dumping_aec_ = true; } @@ -1620,17 +1670,89 @@ int WebRtcVoiceEngine::CreateSoundclipVoiceChannel() { return CreateVoiceChannel(voe_wrapper_sc_.get()); } -// This struct relies on the generated copy constructor and assignment operator -// since it is used in an stl::map. -struct WebRtcVoiceMediaChannel::WebRtcVoiceChannelInfo { - WebRtcVoiceChannelInfo() : channel(-1), renderer(NULL) {} - WebRtcVoiceChannelInfo(int ch, AudioRenderer* r) - : channel(ch), - renderer(r) {} - ~WebRtcVoiceChannelInfo() {} +class WebRtcVoiceMediaChannel::WebRtcVoiceChannelRenderer + : public AudioRenderer::Sink { + public: + WebRtcVoiceChannelRenderer(int ch, + webrtc::AudioTransport* voe_audio_transport) + : channel_(ch), + voe_audio_transport_(voe_audio_transport), + renderer_(NULL) { + } + virtual ~WebRtcVoiceChannelRenderer() { + Stop(); + } + + // Starts the rendering by setting a sink to the renderer to get data + // callback. + // This method is called on the libjingle worker thread. + // TODO(xians): Make sure Start() is called only once. + void Start(AudioRenderer* renderer) { + talk_base::CritScope lock(&lock_); + ASSERT(renderer != NULL); + if (renderer_ != NULL) { + ASSERT(renderer_ == renderer); + return; + } + + // TODO(xians): Remove AddChannel() call after Chrome turns on APM + // in getUserMedia by default. + renderer->AddChannel(channel_); + renderer->SetSink(this); + renderer_ = renderer; + } + + // Stops rendering by setting the sink of the renderer to NULL. No data + // callback will be received after this method. + // This method is called on the libjingle worker thread. + void Stop() { + talk_base::CritScope lock(&lock_); + if (renderer_ == NULL) + return; + + renderer_->RemoveChannel(channel_); + renderer_->SetSink(NULL); + renderer_ = NULL; + } + + // AudioRenderer::Sink implementation. + // This method is called on the audio thread. + virtual void OnData(const void* audio_data, + int bits_per_sample, + int sample_rate, + int number_of_channels, + int number_of_frames) OVERRIDE { + voe_audio_transport_->OnData(channel_, + audio_data, + bits_per_sample, + sample_rate, + number_of_channels, + number_of_frames); + } + + // Callback from the |renderer_| when it is going away. In case Start() has + // never been called, this callback won't be triggered. + virtual void OnClose() OVERRIDE { + talk_base::CritScope lock(&lock_); + // Set |renderer_| to NULL to make sure no more callback will get into + // the renderer. + renderer_ = NULL; + } + + // Accessor to the VoE channel ID. + int channel() const { return channel_; } + + private: + const int channel_; + webrtc::AudioTransport* const voe_audio_transport_; + + // Raw pointer to AudioRenderer owned by LocalAudioTrackHandler. + // PeerConnection will make sure invalidating the pointer before the object + // goes away. + AudioRenderer* renderer_; - int channel; - AudioRenderer* renderer; + // Protects |renderer_| in Start(), Stop() and OnClose(). + talk_base::CriticalSection lock_; }; // WebRtcVoiceMediaChannel @@ -1639,7 +1761,6 @@ WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel(WebRtcVoiceEngine *engine) engine, engine->CreateMediaVoiceChannel()), send_bw_setting_(false), - send_autobw_(false), send_bw_bps_(0), options_(), dtmf_allowed_(false), @@ -1753,7 +1874,7 @@ bool WebRtcVoiceMediaChannel::SetOptions(const AudioOptions& options) { } if (dscp_option_changed) { talk_base::DiffServCodePoint dscp = talk_base::DSCP_DEFAULT; - if (options.dscp.GetWithDefaultIfUnset(false)) + if (options_.dscp.GetWithDefaultIfUnset(false)) dscp = kAudioDscpValue; if (MediaChannel::SetDscp(dscp) != 0) { LOG(LS_WARNING) << "Failed to set DSCP settings for audio channel"; @@ -1823,8 +1944,8 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( for (ChannelMap::iterator it = receive_channels_.begin(); it != receive_channels_.end() && ret; ++it) { if (engine()->voe()->codec()->SetRecPayloadType( - it->second.channel, voe_codec) == -1) { - LOG_RTCERR2(SetRecPayloadType, it->second.channel, + it->second->channel(), voe_codec) == -1) { + LOG_RTCERR2(SetRecPayloadType, it->second->channel(), ToString(voe_codec)); ret = false; } @@ -1846,24 +1967,38 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( bool WebRtcVoiceMediaChannel::SetSendCodecs( int channel, const std::vector<AudioCodec>& codecs) { - // Disable VAD, and FEC unless we know the other side wants them. + // Disable VAD, FEC, and RED unless we know the other side wants them. engine()->voe()->codec()->SetVADStatus(channel, false); engine()->voe()->rtp()->SetNACKStatus(channel, false, 0); +#ifdef USE_WEBRTC_DEV_BRANCH + engine()->voe()->rtp()->SetREDStatus(channel, false); + engine()->voe()->codec()->SetFECStatus(channel, false); +#else + // TODO(minyue): Remove code under #else case after new WebRTC roll. engine()->voe()->rtp()->SetFECStatus(channel, false); +#endif // USE_WEBRTC_DEV_BRANCH // Scan through the list to figure out the codec to use for sending, along // with the proper configuration for VAD and DTMF. - bool first = true; + bool found_send_codec = false; webrtc::CodecInst send_codec; memset(&send_codec, 0, sizeof(send_codec)); + bool nack_enabled = nack_enabled_; + + // Set send codec (the first non-telephone-event/CN codec) for (std::vector<AudioCodec>::const_iterator it = codecs.begin(); it != codecs.end(); ++it) { // Ignore codecs we don't know about. The negotiation step should prevent // this, but double-check to be sure. webrtc::CodecInst voe_codec; if (!engine()->FindWebRtcCodec(*it, &voe_codec)) { - LOG(LS_WARNING) << "Unknown codec " << ToString(voe_codec); + LOG(LS_WARNING) << "Unknown codec " << ToString(*it); + continue; + } + + if (IsTelephoneEventCodec(it->name) || IsCNCodec(it->name)) { + // Skip telephone-event/CN codec, which will be handled later. continue; } @@ -1900,23 +2035,99 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( if (bitrate_from_params != 0) { voe_codec.rate = bitrate_from_params; } + + // For Opus, we also enable inband FEC if it is requested. + if (IsOpusFecEnabled(*it)) { + LOG(LS_INFO) << "Enabling Opus FEC on channel " << channel; +#ifdef USE_WEBRTC_DEV_BRANCH + if (engine()->voe()->codec()->SetFECStatus(channel, true) == -1) { + // Enable in-band FEC of the Opus codec. Treat any failure as a fatal + // internal error. + LOG_RTCERR2(SetFECStatus, channel, true); + return false; + } +#endif // USE_WEBRTC_DEV_BRANCH + } + } + + // We'll use the first codec in the list to actually send audio data. + // Be sure to use the payload type requested by the remote side. + // "red", for RED audio, is a special case where the actual codec to be + // used is specified in params. + if (IsRedCodec(it->name)) { + // Parse out the RED parameters. If we fail, just ignore RED; + // we don't support all possible params/usage scenarios. + if (!GetRedSendCodec(*it, codecs, &send_codec)) { + continue; + } + + // Enable redundant encoding of the specified codec. Treat any + // failure as a fatal internal error. +#ifdef USE_WEBRTC_DEV_BRANCH + LOG(LS_INFO) << "Enabling RED on channel " << channel; + if (engine()->voe()->rtp()->SetREDStatus(channel, true, it->id) == -1) { + LOG_RTCERR3(SetREDStatus, channel, true, it->id); +#else + // TODO(minyue): Remove code under #else case after new WebRTC roll. + LOG(LS_INFO) << "Enabling FEC"; + if (engine()->voe()->rtp()->SetFECStatus(channel, true, it->id) == -1) { + LOG_RTCERR3(SetFECStatus, channel, true, it->id); +#endif // USE_WEBRTC_DEV_BRANCH + return false; + } + } else { + send_codec = voe_codec; + nack_enabled = IsNackEnabled(*it); + } + found_send_codec = true; + break; + } + + if (nack_enabled_ != nack_enabled) { + SetNack(channel, nack_enabled); + nack_enabled_ = nack_enabled; + } + + if (!found_send_codec) { + LOG(LS_WARNING) << "Received empty list of codecs."; + return false; + } + + // Set the codec immediately, since SetVADStatus() depends on whether + // the current codec is mono or stereo. + if (!SetSendCodec(channel, send_codec)) + return false; + + // Always update the |send_codec_| to the currently set send codec. + send_codec_.reset(new webrtc::CodecInst(send_codec)); + + if (send_bw_setting_) { + SetSendBandwidthInternal(send_bw_bps_); + } + + // Loop through the codecs list again to config the telephone-event/CN codec. + for (std::vector<AudioCodec>::const_iterator it = codecs.begin(); + it != codecs.end(); ++it) { + // Ignore codecs we don't know about. The negotiation step should prevent + // this, but double-check to be sure. + webrtc::CodecInst voe_codec; + if (!engine()->FindWebRtcCodec(*it, &voe_codec)) { + LOG(LS_WARNING) << "Unknown codec " << ToString(*it); + continue; } // Find the DTMF telephone event "codec" and tell VoiceEngine channels // about it. - if (_stricmp(it->name.c_str(), "telephone-event") == 0 || - _stricmp(it->name.c_str(), "audio/telephone-event") == 0) { + if (IsTelephoneEventCodec(it->name)) { if (engine()->voe()->dtmf()->SetSendTelephoneEventPayloadType( channel, it->id) == -1) { LOG_RTCERR2(SetSendTelephoneEventPayloadType, channel, it->id); return false; } - } - - // Turn voice activity detection/comfort noise on if supported. - // Set the wideband CN payload type appropriately. - // (narrowband always uses the static payload type 13). - if (_stricmp(it->name.c_str(), "CN") == 0) { + } else if (IsCNCodec(it->name)) { + // Turn voice activity detection/comfort noise on if supported. + // Set the wideband CN payload type appropriately. + // (narrowband always uses the static payload type 13). webrtc::PayloadFrequencies cn_freq; switch (it->clockrate) { case 8000: @@ -1949,7 +2160,6 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( // send the offer. } } - // Only turn on VAD if we have a CN payload type that matches the // clockrate for the codec we are going to use. if (it->clockrate == send_codec.plfreq) { @@ -1960,56 +2170,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( } } } - - // We'll use the first codec in the list to actually send audio data. - // Be sure to use the payload type requested by the remote side. - // "red", for FEC audio, is a special case where the actual codec to be - // used is specified in params. - if (first) { - if (_stricmp(it->name.c_str(), "red") == 0) { - // Parse out the RED parameters. If we fail, just ignore RED; - // we don't support all possible params/usage scenarios. - if (!GetRedSendCodec(*it, codecs, &send_codec)) { - continue; - } - - // Enable redundant encoding of the specified codec. Treat any - // failure as a fatal internal error. - LOG(LS_INFO) << "Enabling FEC"; - if (engine()->voe()->rtp()->SetFECStatus(channel, true, it->id) == -1) { - LOG_RTCERR3(SetFECStatus, channel, true, it->id); - return false; - } - } else { - send_codec = voe_codec; - nack_enabled_ = IsNackEnabled(*it); - SetNack(channel, nack_enabled_); - } - first = false; - // Set the codec immediately, since SetVADStatus() depends on whether - // the current codec is mono or stereo. - if (!SetSendCodec(channel, send_codec)) - return false; - } } - - // If we're being asked to set an empty list of codecs, due to a buggy client, - // choose the most common format: PCMU - if (first) { - LOG(LS_WARNING) << "Received empty list of codecs; using PCMU/8000"; - AudioCodec codec(0, "PCMU", 8000, 0, 1, 0); - engine()->FindWebRtcCodec(codec, &send_codec); - if (!SetSendCodec(channel, send_codec)) - return false; - } - - // Always update the |send_codec_| to the currently set send codec. - send_codec_.reset(new webrtc::CodecInst(send_codec)); - - if (send_bw_setting_) { - SetSendBandwidthInternal(send_autobw_, send_bw_bps_); - } - return true; } @@ -2029,13 +2190,13 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( send_codecs_ = codecs; for (ChannelMap::iterator iter = send_channels_.begin(); iter != send_channels_.end(); ++iter) { - if (!SetSendCodecs(iter->second.channel, codecs)) { + if (!SetSendCodecs(iter->second->channel(), codecs)) { return false; } } + // Set nack status on receive channels and update |nack_enabled_|. SetNack(receive_channels_, nack_enabled_); - return true; } @@ -2043,7 +2204,7 @@ void WebRtcVoiceMediaChannel::SetNack(const ChannelMap& channels, bool nack_enabled) { for (ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it) { - SetNack(it->second.channel, nack_enabled); + SetNack(it->second->channel(), nack_enabled); } } @@ -2063,7 +2224,7 @@ bool WebRtcVoiceMediaChannel::SetSendCodec( << ", bitrate=" << send_codec.rate; for (ChannelMap::iterator iter = send_channels_.begin(); iter != send_channels_.end(); ++iter) { - if (!SetSendCodec(iter->second.channel, send_codec)) + if (!SetSendCodec(iter->second->channel(), send_codec)) return false; } @@ -2075,6 +2236,13 @@ bool WebRtcVoiceMediaChannel::SetSendCodec( LOG(LS_INFO) << "Send channel " << channel << " selected voice codec " << ToString(send_codec) << ", bitrate=" << send_codec.rate; + webrtc::CodecInst current_codec; + if (engine()->voe()->codec()->GetSendCodec(channel, current_codec) == 0 && + (send_codec == current_codec)) { + // Codec is already configured, we can return without setting it again. + return true; + } + if (engine()->voe()->codec()->SetSendCodec(channel, send_codec) == -1) { LOG_RTCERR2(SetSendCodec, channel, ToString(send_codec)); return false; @@ -2084,43 +2252,96 @@ bool WebRtcVoiceMediaChannel::SetSendCodec( bool WebRtcVoiceMediaChannel::SetRecvRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions) { - // We don't support any incoming extensions headers right now. + if (receive_extensions_ == extensions) { + return true; + } + + // The default channel may or may not be in |receive_channels_|. Set the rtp + // header extensions for default channel regardless. + if (!SetChannelRecvRtpHeaderExtensions(voe_channel(), extensions)) { + return false; + } + + // Loop through all receive channels and enable/disable the extensions. + for (ChannelMap::const_iterator channel_it = receive_channels_.begin(); + channel_it != receive_channels_.end(); ++channel_it) { + if (!SetChannelRecvRtpHeaderExtensions(channel_it->second->channel(), + extensions)) { + return false; + } + } + + receive_extensions_ = extensions; + return true; +} + +bool WebRtcVoiceMediaChannel::SetChannelRecvRtpHeaderExtensions( + int channel_id, const std::vector<RtpHeaderExtension>& extensions) { +#ifdef USE_WEBRTC_DEV_BRANCH + const RtpHeaderExtension* audio_level_extension = + FindHeaderExtension(extensions, kRtpAudioLevelHeaderExtension); + if (!SetHeaderExtension( + &webrtc::VoERTP_RTCP::SetReceiveAudioLevelIndicationStatus, channel_id, + audio_level_extension)) { + return false; + } +#endif // USE_WEBRTC_DEV_BRANCH + + const RtpHeaderExtension* send_time_extension = + FindHeaderExtension(extensions, kRtpAbsoluteSenderTimeHeaderExtension); + if (!SetHeaderExtension( + &webrtc::VoERTP_RTCP::SetReceiveAbsoluteSenderTimeStatus, channel_id, + send_time_extension)) { + return false; + } return true; } bool WebRtcVoiceMediaChannel::SetSendRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions) { - // Enable the audio level extension header if requested. - std::vector<RtpHeaderExtension>::const_iterator it; - for (it = extensions.begin(); it != extensions.end(); ++it) { - if (it->uri == kRtpAudioLevelHeaderExtension) { - break; - } + if (send_extensions_ == extensions) { + return true; } - bool enable = (it != extensions.end()); - int id = 0; + // The default channel may or may not be in |send_channels_|. Set the rtp + // header extensions for default channel regardless. - if (enable) { - id = it->id; - if (id < kMinRtpHeaderExtensionId || - id > kMaxRtpHeaderExtensionId) { - LOG(LS_WARNING) << "Invalid RTP header extension id " << id; - return false; - } + if (!SetChannelSendRtpHeaderExtensions(voe_channel(), extensions)) { + return false; } - LOG(LS_INFO) << "Enabling audio level header extension with ID " << id; - for (ChannelMap::const_iterator iter = send_channels_.begin(); - iter != send_channels_.end(); ++iter) { - if (engine()->voe()->rtp()->SetRTPAudioLevelIndicationStatus( - iter->second.channel, enable, id) == -1) { - LOG_RTCERR3(SetRTPAudioLevelIndicationStatus, - iter->second.channel, enable, id); + // Loop through all send channels and enable/disable the extensions. + for (ChannelMap::const_iterator channel_it = send_channels_.begin(); + channel_it != send_channels_.end(); ++channel_it) { + if (!SetChannelSendRtpHeaderExtensions(channel_it->second->channel(), + extensions)) { return false; } } + send_extensions_ = extensions; + return true; +} + +bool WebRtcVoiceMediaChannel::SetChannelSendRtpHeaderExtensions( + int channel_id, const std::vector<RtpHeaderExtension>& extensions) { + const RtpHeaderExtension* audio_level_extension = + FindHeaderExtension(extensions, kRtpAudioLevelHeaderExtension); + + if (!SetHeaderExtension( + &webrtc::VoERTP_RTCP::SetSendAudioLevelIndicationStatus, channel_id, + audio_level_extension)) { + return false; + } + + const RtpHeaderExtension* send_time_extension = + FindHeaderExtension(extensions, kRtpAbsoluteSenderTimeHeaderExtension); + if (!SetHeaderExtension( + &webrtc::VoERTP_RTCP::SetSendAbsoluteSenderTimeStatus, channel_id, + send_time_extension)) { + return false; + } + return true; } @@ -2150,9 +2371,9 @@ bool WebRtcVoiceMediaChannel::ChangePlayout(bool playout) { } for (ChannelMap::iterator it = receive_channels_.begin(); it != receive_channels_.end() && result; ++it) { - if (!SetPlayout(it->second.channel, playout)) { + if (!SetPlayout(it->second->channel(), playout)) { LOG(LS_ERROR) << "SetPlayout " << playout << " on channel " - << it->second.channel << " failed"; + << it->second->channel() << " failed"; result = false; } } @@ -2190,7 +2411,7 @@ bool WebRtcVoiceMediaChannel::ChangeSend(SendFlags send) { // Change the settings on each send channel. for (ChannelMap::iterator iter = send_channels_.begin(); iter != send_channels_.end(); ++iter) { - if (!ChangeSend(iter->second.channel, send)) + if (!ChangeSend(iter->second->channel(), send)) return false; } @@ -2224,6 +2445,7 @@ bool WebRtcVoiceMediaChannel::ChangeSend(int channel, SendFlags send) { return true; } +// TODO(ronghuawu): Change this method to return bool. void WebRtcVoiceMediaChannel::ConfigureSendChannel(int channel) { if (engine()->voe()->network()->RegisterExternalTransport( channel, *this) == -1) { @@ -2235,6 +2457,9 @@ void WebRtcVoiceMediaChannel::ConfigureSendChannel(int channel) { // Reset all recv codecs; they will be enabled via SetRecvCodecs. ResetRecvCodecs(channel); + + // Set RTP header extension for the new channel. + SetChannelSendRtpHeaderExtensions(channel, send_extensions_); } bool WebRtcVoiceMediaChannel::DeleteChannel(int channel) { @@ -2262,7 +2487,7 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) { bool default_channel_is_available = true; for (ChannelMap::const_iterator iter = send_channels_.begin(); iter != send_channels_.end(); ++iter) { - if (IsDefaultChannel(iter->second.channel)) { + if (IsDefaultChannel(iter->second->channel())) { default_channel_is_available = false; break; } @@ -2282,7 +2507,11 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) { // Save the channel to send_channels_, so that RemoveSendStream() can still // delete the channel in case failure happens below. - send_channels_[sp.first_ssrc()] = WebRtcVoiceChannelInfo(channel, NULL); + webrtc::AudioTransport* audio_transport = + engine()->voe()->base()->audio_transport(); + send_channels_.insert(std::make_pair( + sp.first_ssrc(), + new WebRtcVoiceChannelRenderer(channel, audio_transport))); // Set the send (local) SSRC. // If there are multiple send SSRCs, we can only set the first one here, and @@ -2301,10 +2530,10 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) { for (ChannelMap::const_iterator it = receive_channels_.begin(); it != receive_channels_.end(); ++it) { // Only update the SSRC for non-default channels. - if (!IsDefaultChannel(it->second.channel)) { - if (engine()->voe()->rtp()->SetLocalSSRC(it->second.channel, + if (!IsDefaultChannel(it->second->channel())) { + if (engine()->voe()->rtp()->SetLocalSSRC(it->second->channel(), sp.first_ssrc()) != 0) { - LOG_RTCERR2(SetLocalSSRC, it->second.channel, sp.first_ssrc()); + LOG_RTCERR2(SetLocalSSRC, it->second->channel(), sp.first_ssrc()); return false; } } @@ -2331,12 +2560,13 @@ bool WebRtcVoiceMediaChannel::RemoveSendStream(uint32 ssrc) { return false; } - int channel = it->second.channel; + int channel = it->second->channel(); ChangeSend(channel, SEND_NOTHING); - // Notify the audio renderer that the send channel is going away. - if (it->second.renderer) - it->second.renderer->RemoveChannel(channel); + // Delete the WebRtcVoiceChannelRenderer object connected to the channel, + // this will disconnect the audio renderer with the send channel. + delete it->second; + send_channels_.erase(it); if (IsDefaultChannel(channel)) { // Do not delete the default channel since the receive channels depend on @@ -2350,7 +2580,6 @@ bool WebRtcVoiceMediaChannel::RemoveSendStream(uint32 ssrc) { return false; } - send_channels_.erase(it); if (send_channels_.empty()) ChangeSend(SEND_NOTHING); @@ -2376,12 +2605,15 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) { // Reuse default channel for recv stream in non-conference mode call // when the default channel is not being used. + webrtc::AudioTransport* audio_transport = + engine()->voe()->base()->audio_transport(); if (!InConferenceMode() && default_receive_ssrc_ == 0) { LOG(LS_INFO) << "Recv stream " << sp.first_ssrc() << " reuse default channel"; default_receive_ssrc_ = sp.first_ssrc(); receive_channels_.insert(std::make_pair( - default_receive_ssrc_, WebRtcVoiceChannelInfo(voe_channel(), NULL))); + default_receive_ssrc_, + new WebRtcVoiceChannelRenderer(voe_channel(), audio_transport))); return SetPlayout(voe_channel(), playout_); } @@ -2398,7 +2630,8 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) { } receive_channels_.insert( - std::make_pair(ssrc, WebRtcVoiceChannelInfo(channel, NULL))); + std::make_pair( + ssrc, new WebRtcVoiceChannelRenderer(channel, audio_transport))); LOG(LS_INFO) << "New audio stream " << ssrc << " registered to VoiceEngine channel #" @@ -2415,14 +2648,14 @@ bool WebRtcVoiceMediaChannel::ConfigureRecvChannel(int channel) { } // Use the same SSRC as our default channel (so the RTCP reports are correct). - unsigned int send_ssrc; + unsigned int send_ssrc = 0; webrtc::VoERTP_RTCP* rtp = engine()->voe()->rtp(); if (rtp->GetLocalSSRC(voe_channel(), send_ssrc) == -1) { - LOG_RTCERR2(GetSendSSRC, channel, send_ssrc); + LOG_RTCERR1(GetSendSSRC, channel); return false; } if (rtp->SetLocalSSRC(channel, send_ssrc) == -1) { - LOG_RTCERR2(SetSendSSRC, channel, send_ssrc); + LOG_RTCERR1(SetSendSSRC, channel); return false; } @@ -2463,6 +2696,11 @@ bool WebRtcVoiceMediaChannel::ConfigureRecvChannel(int channel) { } SetNack(channel, nack_enabled_); + // Set RTP header extension for the new channel. + if (!SetChannelRecvRtpHeaderExtensions(channel, receive_extensions_)) { + return false; + } + return SetPlayout(channel, playout_); } @@ -2475,34 +2713,28 @@ bool WebRtcVoiceMediaChannel::RemoveRecvStream(uint32 ssrc) { return false; } + // Delete the WebRtcVoiceChannelRenderer object connected to the channel, this + // will disconnect the audio renderer with the receive channel. + // Cache the channel before the deletion. + const int channel = it->second->channel(); + delete it->second; + receive_channels_.erase(it); + if (ssrc == default_receive_ssrc_) { - ASSERT(IsDefaultChannel(it->second.channel)); + ASSERT(IsDefaultChannel(channel)); // Recycle the default channel is for recv stream. if (playout_) SetPlayout(voe_channel(), false); - if (it->second.renderer) - it->second.renderer->RemoveChannel(voe_channel()); - default_receive_ssrc_ = 0; - receive_channels_.erase(it); return true; } - // Non default channel. - // Notify the renderer that channel is going away. - if (it->second.renderer) - it->second.renderer->RemoveChannel(it->second.channel); - LOG(LS_INFO) << "Removing audio stream " << ssrc - << " with VoiceEngine channel #" << it->second.channel << "."; - if (!DeleteChannel(it->second.channel)) { - // Erase the entry anyhow. - receive_channels_.erase(it); + << " with VoiceEngine channel #" << channel << "."; + if (!DeleteChannel(channel)) return false; - } - receive_channels_.erase(it); bool enable_default_channel_playout = false; if (receive_channels_.empty()) { // The last stream was removed. We can now enable the default @@ -2540,19 +2772,11 @@ bool WebRtcVoiceMediaChannel::SetRemoteRenderer(uint32 ssrc, return true; } - AudioRenderer* remote_renderer = it->second.renderer; - if (renderer) { - ASSERT(remote_renderer == NULL || remote_renderer == renderer); - if (!remote_renderer) { - renderer->AddChannel(it->second.channel); - } - } else if (remote_renderer) { - // |renderer| == NULL, remove the channel from the renderer. - remote_renderer->RemoveChannel(it->second.channel); - } + if (renderer) + it->second->Start(renderer); + else + it->second->Stop(); - // Assign the new value to the struct. - it->second.renderer = renderer; return true; } @@ -2570,17 +2794,11 @@ bool WebRtcVoiceMediaChannel::SetLocalRenderer(uint32 ssrc, return true; } - AudioRenderer* local_renderer = it->second.renderer; - if (renderer) { - ASSERT(local_renderer == NULL || local_renderer == renderer); - if (!local_renderer) - renderer->AddChannel(it->second.channel); - } else if (local_renderer) { - local_renderer->RemoveChannel(it->second.channel); - } + if (renderer) + it->second->Start(renderer); + else + it->second->Stop(); - // Assign the new value to the struct. - it->second.renderer = renderer; return true; } @@ -2591,7 +2809,7 @@ bool WebRtcVoiceMediaChannel::GetActiveStreams( actives->clear(); for (ChannelMap::iterator it = receive_channels_.begin(); it != receive_channels_.end(); ++it) { - int level = GetOutputLevel(it->second.channel); + int level = GetOutputLevel(it->second->channel()); if (level > 0) { actives->push_back(std::make_pair(it->first, level)); } @@ -2604,7 +2822,7 @@ int WebRtcVoiceMediaChannel::GetOutputLevel() { int highest = GetOutputLevel(voe_channel()); for (ChannelMap::iterator it = receive_channels_.begin(); it != receive_channels_.end(); ++it) { - int level = GetOutputLevel(it->second.channel); + int level = GetOutputLevel(it->second->channel()); highest = talk_base::_max(level, highest); } return highest; @@ -2647,7 +2865,7 @@ bool WebRtcVoiceMediaChannel::SetOutputScaling( channels.push_back(voe_channel()); for (ChannelMap::const_iterator it = receive_channels_.begin(); it != receive_channels_.end(); ++it) { - channels.push_back(it->second.channel); + channels.push_back(it->second->channel()); } } else { // Collect only the channel of the specified ssrc. int channel = GetReceiveChannelNum(ssrc); @@ -2783,7 +3001,7 @@ bool WebRtcVoiceMediaChannel::InsertDtmf(uint32 ssrc, int event, bool default_channel_is_inuse = false; for (ChannelMap::const_iterator iter = send_channels_.begin(); iter != send_channels_.end(); ++iter) { - if (IsDefaultChannel(iter->second.channel)) { + if (IsDefaultChannel(iter->second->channel())) { default_channel_is_inuse = true; break; } @@ -2791,7 +3009,7 @@ bool WebRtcVoiceMediaChannel::InsertDtmf(uint32 ssrc, int event, if (default_channel_is_inuse) { channel = voe_channel(); } else if (!send_channels_.empty()) { - channel = send_channels_.begin()->second.channel; + channel = send_channels_.begin()->second->channel(); } } else { channel = GetSendChannelNum(ssrc); @@ -2889,11 +3107,12 @@ void WebRtcVoiceMediaChannel::OnRtcpReceived( for (ChannelMap::iterator iter = send_channels_.begin(); iter != send_channels_.end(); ++iter) { // Make sure not sending the same packet to default channel more than once. - if (IsDefaultChannel(iter->second.channel) && has_sent_to_default_channel) + if (IsDefaultChannel(iter->second->channel()) && + has_sent_to_default_channel) continue; engine()->voe()->network()->ReceivedRTCPPacket( - iter->second.channel, + iter->second->channel(), packet->data(), static_cast<unsigned int>(packet->length())); } @@ -2912,18 +3131,23 @@ bool WebRtcVoiceMediaChannel::MuteStream(uint32 ssrc, bool muted) { return true; } -bool WebRtcVoiceMediaChannel::SetSendBandwidth(bool autobw, int bps) { - LOG(LS_INFO) << "WebRtcVoiceMediaChanne::SetSendBandwidth."; +bool WebRtcVoiceMediaChannel::SetStartSendBandwidth(int bps) { + // TODO(andresp): Add support for setting an independent start bandwidth when + // bandwidth estimation is enabled for voice engine. + return false; +} - send_bw_setting_ = true; - send_autobw_ = autobw; - send_bw_bps_ = bps; +bool WebRtcVoiceMediaChannel::SetMaxSendBandwidth(int bps) { + LOG(LS_INFO) << "WebRtcVoiceMediaChanne::SetSendBandwidth."; - return SetSendBandwidthInternal(send_autobw_, send_bw_bps_); + return SetSendBandwidthInternal(bps); } -bool WebRtcVoiceMediaChannel::SetSendBandwidthInternal(bool autobw, int bps) { - LOG(LS_INFO) << "WebRtcVoiceMediaChanne::SetSendBandwidthInternal."; +bool WebRtcVoiceMediaChannel::SetSendBandwidthInternal(int bps) { + LOG(LS_INFO) << "WebRtcVoiceMediaChannel::SetSendBandwidthInternal."; + + send_bw_setting_ = true; + send_bw_bps_ = bps; if (!send_codec_) { LOG(LS_INFO) << "The send codec has not been set up yet. " @@ -2932,7 +3156,9 @@ bool WebRtcVoiceMediaChannel::SetSendBandwidthInternal(bool autobw, int bps) { } // Bandwidth is auto by default. - if (autobw || bps <= 0) + // TODO(bemasc): Fix this so that if SetMaxSendBandwidth(50) is followed by + // SetMaxSendBandwith(0), the second call removes the previous limit. + if (bps <= 0) return true; webrtc::CodecInst codec = *send_codec_; @@ -2997,7 +3223,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { for (ChannelMap::const_iterator channel_iter = send_channels_.begin(); channel_iter != send_channels_.end(); ++channel_iter) { - const int channel = channel_iter->second.channel; + const int channel = channel_iter->second->channel(); // Fill in the sender info, based on what we know, and what the // remote side told us it got from its RTCP report. @@ -3069,7 +3295,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { std::vector<int> channels; for (ChannelMap::const_iterator it = receive_channels_.begin(); it != receive_channels_.end(); ++it) { - channels.push_back(it->second.channel); + channels.push_back(it->second->channel()); } if (channels.empty()) { channels.push_back(voe_channel()); @@ -3091,6 +3317,12 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { rinfo.fraction_lost = static_cast<float>(cs.fractionLost) / (1 << 8); rinfo.packets_lost = cs.cumulativeLost; rinfo.ext_seqnum = cs.extendedMax; +#ifdef USE_WEBRTC_DEV_BRANCH + rinfo.capture_start_ntp_time_ms = cs.capture_start_ntp_time_ms_; +#endif + if (codec.pltype != -1) { + rinfo.codec_name = codec.plname; + } // Convert samples to milliseconds. if (codec.plfreq / 1000 > 0) { rinfo.jitter_ms = cs.jitterSamples / (codec.plfreq / 1000); @@ -3106,6 +3338,20 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { rinfo.expand_rate = static_cast<float>(ns.currentExpandRate) / (1 << 14); } + + webrtc::AudioDecodingCallStats ds; + if (engine()->voe()->neteq() && + engine()->voe()->neteq()->GetDecodingCallStatistics( + *it, &ds) != -1) { + rinfo.decoding_calls_to_silence_generator = + ds.calls_to_silence_generator; + rinfo.decoding_calls_to_neteq = ds.calls_to_neteq; + rinfo.decoding_normal = ds.decoded_normal; + rinfo.decoding_plc = ds.decoded_plc; + rinfo.decoding_cng = ds.decoded_cng; + rinfo.decoding_plc_cng = ds.decoded_plc_cng; + } + if (engine()->voe()->sync()) { int jitter_buffer_delay_ms = 0; int playout_buffer_delay_ms = 0; @@ -3147,7 +3393,7 @@ bool WebRtcVoiceMediaChannel::FindSsrc(int channel_num, uint32* ssrc) { // Check whether this is a sending channel. for (ChannelMap::const_iterator it = send_channels_.begin(); it != send_channels_.end(); ++it) { - if (it->second.channel == channel_num) { + if (it->second->channel() == channel_num) { // This is a sending channel. uint32 local_ssrc = 0; if (engine()->voe()->rtp()->GetLocalSSRC( @@ -3161,7 +3407,7 @@ bool WebRtcVoiceMediaChannel::FindSsrc(int channel_num, uint32* ssrc) { // Check whether this is a receiving channel. for (ChannelMap::const_iterator it = receive_channels_.begin(); it != receive_channels_.end(); ++it) { - if (it->second.channel == channel_num) { + if (it->second->channel() == channel_num) { *ssrc = it->first; return true; } @@ -3189,14 +3435,14 @@ int WebRtcVoiceMediaChannel::GetOutputLevel(int channel) { int WebRtcVoiceMediaChannel::GetReceiveChannelNum(uint32 ssrc) { ChannelMap::iterator it = receive_channels_.find(ssrc); if (it != receive_channels_.end()) - return it->second.channel; + return it->second->channel(); return (ssrc == default_receive_ssrc_) ? voe_channel() : -1; } int WebRtcVoiceMediaChannel::GetSendChannelNum(uint32 ssrc) { ChannelMap::iterator it = send_channels_.find(ssrc); if (it != send_channels_.end()) - return it->second.channel; + return it->second->channel(); return -1; } @@ -3333,6 +3579,23 @@ VoiceMediaChannel::Error } } +bool WebRtcVoiceMediaChannel::SetHeaderExtension(ExtensionSetterFunction setter, + int channel_id, const RtpHeaderExtension* extension) { + bool enable = false; + int id = 0; + std::string uri; + if (extension) { + enable = true; + id = extension->id; + uri = extension->uri; + } + if ((engine()->voe()->rtp()->*setter)(channel_id, enable, id) != 0) { + LOG_RTCERR4(*setter, uri, channel_id, enable, id); + return false; + } + return true; +} + int WebRtcSoundclipStream::Read(void *buf, int len) { size_t res = 0; mem_.Read(buf, len, &res, NULL); diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.h b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.h index 23d97f5e103..efc388fc336 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.h @@ -44,14 +44,12 @@ #include "talk/media/webrtc/webrtcvoe.h" #include "talk/session/media/channel.h" #include "webrtc/common.h" -#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #if !defined(LIBPEERCONNECTION_LIB) && \ !defined(LIBPEERCONNECTION_IMPLEMENTATION) #error "Bogus include." #endif - namespace cricket { // WebRtcSoundclipStream is an adapter object that allows a memory stream to be @@ -174,6 +172,9 @@ class WebRtcVoiceEngine bool SetAudioDeviceModule(webrtc::AudioDeviceModule* adm, webrtc::AudioDeviceModule* adm_sc); + // Starts AEC dump using existing file. + bool StartAecDump(talk_base::PlatformFile file); + // Check whether the supplied trace should be ignored. bool ShouldIgnoreTrace(const std::string& trace); @@ -198,9 +199,6 @@ class WebRtcVoiceEngine // allows us to selectively turn on and off different options easily // at any time. bool ApplyOptions(const AudioOptions& options); - // Configure for using ACM2, if |enable| is true, otherwise configure for - // ACM1. - void EnableExperimentalAcm(bool enable); virtual void Print(webrtc::TraceLevel level, const char* trace, int length); virtual void CallbackOnError(int channel, int errCode); // Given the device type, name, and id, find device id. Return true and @@ -258,7 +256,6 @@ class WebRtcVoiceEngine webrtc::AgcConfig default_agc_config_; webrtc::Config voe_config_; - bool use_experimental_acm_; bool initialized_; // See SetOptions and SetOptionOverrides for a description of the @@ -362,7 +359,8 @@ class WebRtcVoiceMediaChannel const talk_base::PacketTime& packet_time); virtual void OnReadyToSend(bool ready) {} virtual bool MuteStream(uint32 ssrc, bool on); - virtual bool SetSendBandwidth(bool autobw, int bps); + virtual bool SetStartSendBandwidth(int bps); + virtual bool SetMaxSendBandwidth(int bps); virtual bool GetStats(VoiceMediaInfo* info); // Gets last reported error from WebRtc voice engine. This should be only // called in response a failure. @@ -388,8 +386,13 @@ class WebRtcVoiceMediaChannel static Error WebRtcErrorToChannelError(int err_code); private: - struct WebRtcVoiceChannelInfo; - typedef std::map<uint32, WebRtcVoiceChannelInfo> ChannelMap; + class WebRtcVoiceChannelRenderer; + // Map of ssrc to WebRtcVoiceChannelRenderer object. A new object of + // WebRtcVoiceChannelRenderer will be created for every new stream and + // will be destroyed when the stream goes away. + typedef std::map<uint32, WebRtcVoiceChannelRenderer*> ChannelMap; + typedef int (webrtc::VoERTP_RTCP::* ExtensionSetterFunction)(int, bool, + unsigned char); void SetNack(int channel, bool nack_enabled); void SetNack(const ChannelMap& channels, bool nack_enabled); @@ -408,7 +411,17 @@ class WebRtcVoiceMediaChannel return channel_id == voe_channel(); } bool SetSendCodecs(int channel, const std::vector<AudioCodec>& codecs); - bool SetSendBandwidthInternal(bool autobw, int bps); + bool SetSendBandwidthInternal(int bps); + + bool SetHeaderExtension(ExtensionSetterFunction setter, int channel_id, + const RtpHeaderExtension* extension); + + bool SetChannelRecvRtpHeaderExtensions( + int channel_id, + const std::vector<RtpHeaderExtension>& extensions); + bool SetChannelSendRtpHeaderExtensions( + int channel_id, + const std::vector<RtpHeaderExtension>& extensions); talk_base::scoped_ptr<WebRtcSoundclipStream> ringback_tone_; std::set<int> ringback_channels_; // channels playing ringback @@ -416,7 +429,6 @@ class WebRtcVoiceMediaChannel std::vector<AudioCodec> send_codecs_; talk_base::scoped_ptr<webrtc::CodecInst> send_codec_; bool send_bw_setting_; - bool send_autobw_; int send_bw_bps_; AudioOptions options_; bool dtmf_allowed_; @@ -431,6 +443,7 @@ class WebRtcVoiceMediaChannel // When the default channel (voe_channel) is used for sending, it is // contained in send_channels_, otherwise not. ChannelMap send_channels_; + std::vector<RtpHeaderExtension> send_extensions_; uint32 default_receive_ssrc_; // Note the default channel (voe_channel()) can reside in both // receive_channels_ and send_channels_ in non-conference mode and in that @@ -440,6 +453,7 @@ class WebRtcVoiceMediaChannel // the WebRtc thread must be synchronized with edits on the worker thread. // Reads on the worker thread are ok. // + std::vector<RtpHeaderExtension> receive_extensions_; // Do not lock this on the VoE media processor thread; potential for deadlock // exists. mutable talk_base::CriticalSection receive_channels_cs_; diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine_unittest.cc index 9bb681a895c..58893b98ac5 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine_unittest.cc @@ -1,6 +1,29 @@ -// Copyright 2008 Google Inc. -// -// Author: Justin Uberti (juberti@google.com) +/* + * libjingle + * Copyright 2008 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #ifdef WIN32 #include "talk/base/win32.h" @@ -21,6 +44,9 @@ // Tests for the WebRtcVoiceEngine/VoiceChannel code. +using cricket::kRtpAudioLevelHeaderExtension; +using cricket::kRtpAbsoluteSenderTimeHeaderExtension; + static const cricket::AudioCodec kPcmuCodec(0, "PCMU", 8000, 64000, 1, 0); static const cricket::AudioCodec kIsacCodec(103, "ISAC", 16000, 32000, 1, 0); static const cricket::AudioCodec kCeltCodec(110, "CELT", 32000, 64000, 2, 0); @@ -113,17 +139,19 @@ class WebRtcVoiceEngineTestFake : public testing::Test { options_conference_.conference_mode.Set(true); options_adjust_agc_.adjust_agc_delta.Set(-10); } - bool SetupEngine() { - bool result = engine_.Init(talk_base::Thread::Current()); - if (result) { - channel_ = engine_.CreateChannel(); - result = (channel_ != NULL); + bool SetupEngineWithoutStream() { + if (!engine_.Init(talk_base::Thread::Current())) { + return false; } - if (result) { - result = channel_->AddSendStream( - cricket::StreamParams::CreateLegacy(kSsrc1)); + channel_ = engine_.CreateChannel(); + return (channel_ != NULL); + } + bool SetupEngine() { + if (!SetupEngineWithoutStream()) { + return false; } - return result; + return channel_->AddSendStream( + cricket::StreamParams::CreateLegacy(kSsrc1)); } void SetupForMultiSendStream() { EXPECT_TRUE(SetupEngine()); @@ -201,81 +229,110 @@ class WebRtcVoiceEngineTestFake : public testing::Test { // Test that send bandwidth is set correctly. // |codec| is the codec under test. - // |default_bitrate| is the default bitrate for the codec. - // |auto_bitrate| is a parameter to set to SetSendBandwidth(). - // |desired_bitrate| is a parameter to set to SetSendBandwidth(). - // |expected_result| is expected results from SetSendBandwidth(). + // |max_bitrate| is a parameter to set to SetMaxSendBandwidth(). + // |expected_result| is the expected result from SetMaxSendBandwidth(). + // |expected_bitrate| is the expected audio bitrate afterward. void TestSendBandwidth(const cricket::AudioCodec& codec, - int default_bitrate, - bool auto_bitrate, - int desired_bitrate, - bool expected_result) { + int max_bitrate, + bool expected_result, + int expected_bitrate) { int channel_num = voe_.GetLastChannel(); std::vector<cricket::AudioCodec> codecs; codecs.push_back(codec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - bool result = channel_->SetSendBandwidth(auto_bitrate, desired_bitrate); + bool result = channel_->SetMaxSendBandwidth(max_bitrate); EXPECT_EQ(expected_result, result); webrtc::CodecInst temp_codec; EXPECT_FALSE(voe_.GetSendCodec(channel_num, temp_codec)); - if (result) { - // If SetSendBandwidth() returns true then bitrate is set correctly. - if (auto_bitrate) { - EXPECT_EQ(default_bitrate, temp_codec.rate); - } else { - EXPECT_EQ(desired_bitrate, temp_codec.rate); - } - } else { - // If SetSendBandwidth() returns false then bitrate is set to the - // default value. - EXPECT_EQ(default_bitrate, temp_codec.rate); - } + EXPECT_EQ(expected_bitrate, temp_codec.rate); } + void TestSetSendRtpHeaderExtensions(const std::string& ext) { + EXPECT_TRUE(SetupEngineWithoutStream()); + int channel_num = voe_.GetLastChannel(); - void TestSetSendRtpHeaderExtensions(int channel_id) { - std::vector<cricket::RtpHeaderExtension> extensions; - bool enable = false; - unsigned char id = 0; - - // Ensure audio levels are off by default. - EXPECT_EQ(0, voe_.GetRTPAudioLevelIndicationStatus( - channel_id, enable, id)); - EXPECT_FALSE(enable); + // Ensure extensions are off by default. + EXPECT_EQ(-1, voe_.GetSendRtpExtensionId(channel_num, ext)); + std::vector<cricket::RtpHeaderExtension> extensions; // Ensure unknown extensions won't cause an error. extensions.push_back(cricket::RtpHeaderExtension( - "urn:ietf:params:unknowextention", 1)); + "urn:ietf:params:unknownextention", 1)); EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); - EXPECT_EQ(0, voe_.GetRTPAudioLevelIndicationStatus( - channel_id, enable, id)); - EXPECT_FALSE(enable); + EXPECT_EQ(-1, voe_.GetSendRtpExtensionId(channel_num, ext)); - // Ensure audio levels stay off with an empty list of headers. + // Ensure extensions stay off with an empty list of headers. + extensions.clear(); EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); - EXPECT_EQ(0, voe_.GetRTPAudioLevelIndicationStatus( - channel_id, enable, id)); - EXPECT_FALSE(enable); + EXPECT_EQ(-1, voe_.GetSendRtpExtensionId(channel_num, ext)); - // Ensure audio levels are enabled if the audio-level header is specified. - extensions.push_back(cricket::RtpHeaderExtension( - "urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8)); + // Ensure extension is set properly. + const int id = 1; + extensions.push_back(cricket::RtpHeaderExtension(ext, id)); EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); - EXPECT_EQ(0, voe_.GetRTPAudioLevelIndicationStatus( - channel_id, enable, id)); - EXPECT_TRUE(enable); - EXPECT_EQ(8, id); + EXPECT_EQ(id, voe_.GetSendRtpExtensionId(channel_num, ext)); + + // Ensure extension is set properly on new channel. + // The first stream to occupy the default channel. + EXPECT_TRUE(channel_->AddSendStream( + cricket::StreamParams::CreateLegacy(123))); + EXPECT_TRUE(channel_->AddSendStream( + cricket::StreamParams::CreateLegacy(234))); + int new_channel_num = voe_.GetLastChannel(); + EXPECT_NE(channel_num, new_channel_num); + EXPECT_EQ(id, voe_.GetSendRtpExtensionId(new_channel_num, ext)); - // Ensure audio levels go back off with an empty list. + // Ensure all extensions go back off with an empty list. extensions.clear(); EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions)); - EXPECT_EQ(0, voe_.GetRTPAudioLevelIndicationStatus( - channel_id, enable, id)); - EXPECT_FALSE(enable); + EXPECT_EQ(-1, voe_.GetSendRtpExtensionId(channel_num, ext)); + EXPECT_EQ(-1, voe_.GetSendRtpExtensionId(new_channel_num, ext)); + } + + void TestSetRecvRtpHeaderExtensions(const std::string& ext) { + EXPECT_TRUE(SetupEngineWithoutStream()); + int channel_num = voe_.GetLastChannel(); + + // Ensure extensions are off by default. + EXPECT_EQ(-1, voe_.GetReceiveRtpExtensionId(channel_num, ext)); + + std::vector<cricket::RtpHeaderExtension> extensions; + // Ensure unknown extensions won't cause an error. + extensions.push_back(cricket::RtpHeaderExtension( + "urn:ietf:params:unknownextention", 1)); + EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); + EXPECT_EQ(-1, voe_.GetReceiveRtpExtensionId(channel_num, ext)); + + // Ensure extensions stay off with an empty list of headers. + extensions.clear(); + EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); + EXPECT_EQ(-1, voe_.GetReceiveRtpExtensionId(channel_num, ext)); + + // Ensure extension is set properly. + const int id = 2; + extensions.push_back(cricket::RtpHeaderExtension(ext, id)); + EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); + EXPECT_EQ(id, voe_.GetReceiveRtpExtensionId(channel_num, ext)); + + // Ensure extension is set properly on new channel. + // The first stream to occupy the default channel. + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(345))); + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(456))); + int new_channel_num = voe_.GetLastChannel(); + EXPECT_NE(channel_num, new_channel_num); + EXPECT_EQ(id, voe_.GetReceiveRtpExtensionId(new_channel_num, ext)); + + // Ensure all extensions go back off with an empty list. + extensions.clear(); + EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); + EXPECT_EQ(-1, voe_.GetReceiveRtpExtensionId(channel_num, ext)); + EXPECT_EQ(-1, voe_.GetReceiveRtpExtensionId(new_channel_num, ext)); } protected: @@ -575,47 +632,67 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthAuto) { EXPECT_TRUE(SetupEngine()); EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); - // Test that when autobw is true, bitrate is kept as the default - // value. autobw is true for the following tests. + // Test that when autobw is enabled, bitrate is kept as the default + // value. autobw is enabled for the following tests because the target + // bitrate is <= 0. // ISAC, default bitrate == 32000. - TestSendBandwidth(kIsacCodec, 32000, true, 96000, true); + TestSendBandwidth(kIsacCodec, 0, true, 32000); // PCMU, default bitrate == 64000. - TestSendBandwidth(kPcmuCodec, 64000, true, 96000, true); + TestSendBandwidth(kPcmuCodec, -1, true, 64000); // CELT, default bitrate == 64000. - TestSendBandwidth(kCeltCodec, 64000, true, 96000, true); + TestSendBandwidth(kCeltCodec, 0, true, 64000); // opus, default bitrate == 64000. - TestSendBandwidth(kOpusCodec, 64000, true, 96000, true); + TestSendBandwidth(kOpusCodec, -1, true, 64000); } -TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthFixedMultiRateAsCaller) { +TEST_F(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthMultiRateAsCaller) { EXPECT_TRUE(SetupEngine()); EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); - // Test that we can set bitrate if a multi-rate codec is used. - // autobw is false for the following tests. + // Test that the bitrate of a multi-rate codec is always the maximum. // ISAC, default bitrate == 32000. - TestSendBandwidth(kIsacCodec, 32000, false, 128000, true); + TestSendBandwidth(kIsacCodec, 128000, true, 128000); + TestSendBandwidth(kIsacCodec, 16000, true, 16000); // CELT, default bitrate == 64000. - TestSendBandwidth(kCeltCodec, 64000, false, 96000, true); + TestSendBandwidth(kCeltCodec, 96000, true, 96000); + TestSendBandwidth(kCeltCodec, 32000, true, 32000); // opus, default bitrate == 64000. - TestSendBandwidth(kOpusCodec, 64000, false, 96000, true); + TestSendBandwidth(kOpusCodec, 96000, true, 96000); + TestSendBandwidth(kOpusCodec, 48000, true, 48000); } -TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthFixedMultiRateAsCallee) { +TEST_F(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthFixedRateAsCaller) { + EXPECT_TRUE(SetupEngine()); + EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); + + // Test that we can only set a maximum bitrate for a fixed-rate codec + // if it's bigger than the fixed rate. + + // PCMU, fixed bitrate == 64000. + TestSendBandwidth(kPcmuCodec, 0, true, 64000); + TestSendBandwidth(kPcmuCodec, 1, false, 64000); + TestSendBandwidth(kPcmuCodec, 128000, true, 64000); + TestSendBandwidth(kPcmuCodec, 32000, false, 64000); + TestSendBandwidth(kPcmuCodec, 64000, true, 64000); + TestSendBandwidth(kPcmuCodec, 63999, false, 64000); + TestSendBandwidth(kPcmuCodec, 64001, true, 64000); +} + +TEST_F(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthMultiRateAsCallee) { EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); channel_ = engine_.CreateChannel(); EXPECT_TRUE(channel_ != NULL); EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); int desired_bitrate = 128000; - EXPECT_TRUE(channel_->SetSendBandwidth(false, desired_bitrate)); + EXPECT_TRUE(channel_->SetMaxSendBandwidth(desired_bitrate)); EXPECT_TRUE(channel_->AddSendStream( cricket::StreamParams::CreateLegacy(kSsrc1))); @@ -629,7 +706,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthFixedMultiRateAsCallee) { // Test that bitrate cannot be set for CBR codecs. // Bitrate is ignored if it is higher than the fixed bitrate. // Bitrate less then the fixed bitrate is an error. -TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthFixedCbr) { +TEST_F(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthCbr) { EXPECT_TRUE(SetupEngine()); EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); @@ -642,10 +719,10 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthFixedCbr) { EXPECT_TRUE(channel_->SetSendCodecs(codecs)); EXPECT_EQ(0, voe_.GetSendCodec(channel_num, codec)); EXPECT_EQ(64000, codec.rate); - EXPECT_TRUE(channel_->SetSendBandwidth(false, 128000)); + EXPECT_TRUE(channel_->SetMaxSendBandwidth(128000)); EXPECT_EQ(0, voe_.GetSendCodec(channel_num, codec)); EXPECT_EQ(64000, codec.rate); - EXPECT_FALSE(channel_->SetSendBandwidth(false, 128)); + EXPECT_FALSE(channel_->SetMaxSendBandwidth(128)); EXPECT_EQ(0, voe_.GetSendCodec(channel_num, codec)); EXPECT_EQ(64000, codec.rate); } @@ -661,105 +738,98 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecs) { codecs[0].id = 96; codecs[0].bitrate = 48000; EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_EQ(1, voe_.GetNumSetSendCodecs()); webrtc::CodecInst gcodec; EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_EQ(48000, gcodec.rate); EXPECT_STREQ("ISAC", gcodec.plname); EXPECT_FALSE(voe_.GetVAD(channel_num)); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); EXPECT_EQ(13, voe_.GetSendCNPayloadType(channel_num, false)); EXPECT_EQ(105, voe_.GetSendCNPayloadType(channel_num, true)); EXPECT_EQ(106, voe_.GetSendTelephoneEventPayloadType(channel_num)); } -// TODO(pthatcher): Change failure behavior to returning false rather -// than defaulting to PCMU. -// Test that if clockrate is not 48000 for opus, we fail by fallback to PCMU. +// Test that VoE Channel doesn't call SetSendCodec again if same codec is tried +// to apply. +TEST_F(WebRtcVoiceEngineTestFake, DontResetSetSendCodec) { + EXPECT_TRUE(SetupEngine()); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kIsacCodec); + codecs.push_back(kPcmuCodec); + codecs.push_back(kRedCodec); + codecs[0].id = 96; + codecs[0].bitrate = 48000; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_EQ(1, voe_.GetNumSetSendCodecs()); + // Calling SetSendCodec again with same codec which is already set. + // In this case media channel shouldn't send codec to VoE. + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_EQ(1, voe_.GetNumSetSendCodecs()); +} + +// Test that if clockrate is not 48000 for opus, we fail. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusBadClockrate) { EXPECT_TRUE(SetupEngine()); - int channel_num = voe_.GetLastChannel(); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kOpusCodec); codecs[0].bitrate = 0; codecs[0].clockrate = 50000; - EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - webrtc::CodecInst gcodec; - EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); - EXPECT_STREQ("PCMU", gcodec.plname); + EXPECT_FALSE(channel_->SetSendCodecs(codecs)); } -// Test that if channels=0 for opus, we fail by falling back to PCMU. +// Test that if channels=0 for opus, we fail. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusBad0ChannelsNoStereo) { EXPECT_TRUE(SetupEngine()); - int channel_num = voe_.GetLastChannel(); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kOpusCodec); codecs[0].bitrate = 0; codecs[0].channels = 0; - EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - webrtc::CodecInst gcodec; - EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); - EXPECT_STREQ("PCMU", gcodec.plname); + EXPECT_FALSE(channel_->SetSendCodecs(codecs)); } -// Test that if channels=0 for opus, we fail by falling back to PCMU. +// Test that if channels=0 for opus, we fail. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusBad0Channels1Stereo) { EXPECT_TRUE(SetupEngine()); - int channel_num = voe_.GetLastChannel(); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kOpusCodec); codecs[0].bitrate = 0; codecs[0].channels = 0; codecs[0].params["stereo"] = "1"; - EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - webrtc::CodecInst gcodec; - EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); - EXPECT_STREQ("PCMU", gcodec.plname); + EXPECT_FALSE(channel_->SetSendCodecs(codecs)); } // Test that if channel is 1 for opus and there's no stereo, we fail. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpus1ChannelNoStereo) { EXPECT_TRUE(SetupEngine()); - int channel_num = voe_.GetLastChannel(); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kOpusCodec); codecs[0].bitrate = 0; codecs[0].channels = 1; - EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - webrtc::CodecInst gcodec; - EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); - EXPECT_STREQ("PCMU", gcodec.plname); + EXPECT_FALSE(channel_->SetSendCodecs(codecs)); } // Test that if channel is 1 for opus and stereo=0, we fail. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusBad1Channel0Stereo) { EXPECT_TRUE(SetupEngine()); - int channel_num = voe_.GetLastChannel(); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kOpusCodec); codecs[0].bitrate = 0; codecs[0].channels = 1; codecs[0].params["stereo"] = "0"; - EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - webrtc::CodecInst gcodec; - EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); - EXPECT_STREQ("PCMU", gcodec.plname); + EXPECT_FALSE(channel_->SetSendCodecs(codecs)); } // Test that if channel is 1 for opus and stereo=1, we fail. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusBad1Channel1Stereo) { EXPECT_TRUE(SetupEngine()); - int channel_num = voe_.GetLastChannel(); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kOpusCodec); codecs[0].bitrate = 0; codecs[0].channels = 1; codecs[0].params["stereo"] = "1"; - EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - webrtc::CodecInst gcodec; - EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); - EXPECT_STREQ("PCMU", gcodec.plname); + EXPECT_FALSE(channel_->SetSendCodecs(codecs)); } // Test that with bitrate=0 and no stereo, @@ -1074,17 +1144,117 @@ TEST_F(WebRtcVoiceEngineTestFake, AddRecvStreamEnableNack) { EXPECT_TRUE(voe_.GetNACK(channel_num)); } +#ifdef USE_WEBRTC_DEV_BRANCH +// Test that without useinbandfec, Opus FEC is off. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecNoOpusFec) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kOpusCodec); + codecs[0].bitrate = 0; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_FALSE(voe_.GetCodecFEC(channel_num)); +} + +// Test that with useinbandfec=0, Opus FEC is off. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusDisableFec) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kOpusCodec); + codecs[0].bitrate = 0; + codecs[0].params["useinbandfec"] = "0"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_FALSE(voe_.GetCodecFEC(channel_num)); + webrtc::CodecInst gcodec; + EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); + EXPECT_STREQ("opus", gcodec.plname); + EXPECT_EQ(1, gcodec.channels); + EXPECT_EQ(32000, gcodec.rate); +} + +// Test that with useinbandfec=1, Opus FEC is on. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusEnableFec) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kOpusCodec); + codecs[0].bitrate = 0; + codecs[0].params["useinbandfec"] = "1"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(voe_.GetCodecFEC(channel_num)); + webrtc::CodecInst gcodec; + EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); + EXPECT_STREQ("opus", gcodec.plname); + EXPECT_EQ(1, gcodec.channels); + EXPECT_EQ(32000, gcodec.rate); +} + +// Test that with useinbandfec=1, stereo=1, Opus FEC is on. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecOpusEnableFecStereo) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kOpusCodec); + codecs[0].bitrate = 0; + codecs[0].params["stereo"] = "1"; + codecs[0].params["useinbandfec"] = "1"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(voe_.GetCodecFEC(channel_num)); + webrtc::CodecInst gcodec; + EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); + EXPECT_STREQ("opus", gcodec.plname); + EXPECT_EQ(2, gcodec.channels); + EXPECT_EQ(64000, gcodec.rate); +} + +// Test that with non-Opus, codec FEC is off. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecIsacNoFec) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kIsacCodec); + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_FALSE(voe_.GetCodecFEC(channel_num)); +} +#endif // USE_WEBRTC_DEV_BRANCH + +// Test AudioOptions controls whether opus FEC is supported in codec list. +TEST_F(WebRtcVoiceEngineTestFake, OpusFecViaOptions) { + EXPECT_TRUE(SetupEngine()); + std::vector<cricket::AudioCodec> codecs = engine_.codecs(); + int value; + for (std::vector<cricket::AudioCodec>::const_iterator it = codecs.begin(); + it != codecs.end(); ++it) { + if (_stricmp(it->name.c_str(), cricket::kOpusCodecName) == 0) { + EXPECT_FALSE(it->GetParam(cricket::kCodecParamUseInbandFec, &value)); + } + } + + cricket::AudioOptions options; + options.opus_fec.Set(true); + EXPECT_TRUE(engine_.SetOptions(options)); + codecs = engine_.codecs(); + for (std::vector<cricket::AudioCodec>::const_iterator it = codecs.begin(); + it != codecs.end(); ++it) { + if (_stricmp(it->name.c_str(), cricket::kOpusCodecName) == 0) { + EXPECT_TRUE(it->GetParam(cricket::kCodecParamUseInbandFec, &value)); + EXPECT_EQ(1, value); + } + } +} + // Test that we can apply CELT with stereo mode but fail with mono mode. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCelt) { EXPECT_TRUE(SetupEngine()); int channel_num = voe_.GetLastChannel(); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kCeltCodec); - codecs.push_back(kPcmuCodec); + codecs.push_back(kIsacCodec); codecs[0].id = 96; codecs[0].channels = 2; codecs[0].bitrate = 96000; - codecs[1].bitrate = 96000; + codecs[1].bitrate = 64000; EXPECT_TRUE(channel_->SetSendCodecs(codecs)); webrtc::CodecInst gcodec; EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); @@ -1096,10 +1266,10 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCelt) { codecs[0].channels = 1; EXPECT_TRUE(channel_->SetSendCodecs(codecs)); EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); - EXPECT_EQ(0, gcodec.pltype); + EXPECT_EQ(103, gcodec.pltype); EXPECT_EQ(1, gcodec.channels); EXPECT_EQ(64000, gcodec.rate); - EXPECT_STREQ("PCMU", gcodec.plname); + EXPECT_STREQ("ISAC", gcodec.plname); } // Test that we can switch back and forth between CELT and ISAC with CN. @@ -1179,21 +1349,49 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBitrate) { EXPECT_EQ(32000, gcodec.rate); } -// Test that we fall back to PCMU if no codecs are specified. +// Test that we fail if no codecs are specified. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsNoCodecs) { EXPECT_TRUE(SetupEngine()); + std::vector<cricket::AudioCodec> codecs; + EXPECT_FALSE(channel_->SetSendCodecs(codecs)); +} + +// Test that we can set send codecs even with telephone-event codec as the first +// one on the list. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsDTMFOnTop) { + EXPECT_TRUE(SetupEngine()); int channel_num = voe_.GetLastChannel(); std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kTelephoneEventCodec); + codecs.push_back(kIsacCodec); + codecs.push_back(kPcmuCodec); + codecs[0].id = 98; // DTMF + codecs[1].id = 96; EXPECT_TRUE(channel_->SetSendCodecs(codecs)); webrtc::CodecInst gcodec; EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); - EXPECT_EQ(0, gcodec.pltype); - EXPECT_STREQ("PCMU", gcodec.plname); - EXPECT_FALSE(voe_.GetVAD(channel_num)); - EXPECT_FALSE(voe_.GetFEC(channel_num)); - EXPECT_EQ(13, voe_.GetSendCNPayloadType(channel_num, false)); - EXPECT_EQ(105, voe_.GetSendCNPayloadType(channel_num, true)); - EXPECT_EQ(106, voe_.GetSendTelephoneEventPayloadType(channel_num)); + EXPECT_EQ(96, gcodec.pltype); + EXPECT_STREQ("ISAC", gcodec.plname); + EXPECT_EQ(98, voe_.GetSendTelephoneEventPayloadType(channel_num)); +} + +// Test that we can set send codecs even with CN codec as the first +// one on the list. +TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCNOnTop) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + std::vector<cricket::AudioCodec> codecs; + codecs.push_back(kCn16000Codec); + codecs.push_back(kIsacCodec); + codecs.push_back(kPcmuCodec); + codecs[0].id = 98; // wideband CN + codecs[1].id = 96; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + webrtc::CodecInst gcodec; + EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); + EXPECT_EQ(96, gcodec.pltype); + EXPECT_STREQ("ISAC", gcodec.plname); + EXPECT_EQ(98, voe_.GetSendCNPayloadType(channel_num, true)); } // Test that we set VAD and DTMF types correctly as caller. @@ -1217,7 +1415,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCNandDTMFAsCaller) { EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); EXPECT_TRUE(voe_.GetVAD(channel_num)); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); EXPECT_EQ(13, voe_.GetSendCNPayloadType(channel_num, false)); EXPECT_EQ(97, voe_.GetSendCNPayloadType(channel_num, true)); EXPECT_EQ(98, voe_.GetSendTelephoneEventPayloadType(channel_num)); @@ -1250,7 +1448,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCNandDTMFAsCallee) { EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); EXPECT_TRUE(voe_.GetVAD(channel_num)); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); EXPECT_EQ(13, voe_.GetSendCNPayloadType(channel_num, false)); EXPECT_EQ(97, voe_.GetSendCNPayloadType(channel_num, true)); EXPECT_EQ(98, voe_.GetSendTelephoneEventPayloadType(channel_num)); @@ -1314,13 +1512,13 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsCaseInsensitive) { EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); EXPECT_TRUE(voe_.GetVAD(channel_num)); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); EXPECT_EQ(13, voe_.GetSendCNPayloadType(channel_num, false)); EXPECT_EQ(97, voe_.GetSendCNPayloadType(channel_num, true)); EXPECT_EQ(98, voe_.GetSendTelephoneEventPayloadType(channel_num)); } -// Test that we set up FEC correctly as caller. +// Test that we set up RED correctly as caller. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCaller) { EXPECT_TRUE(SetupEngine()); int channel_num = voe_.GetLastChannel(); @@ -1336,11 +1534,11 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCaller) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_TRUE(voe_.GetFEC(channel_num)); - EXPECT_EQ(127, voe_.GetSendFECPayloadType(channel_num)); + EXPECT_TRUE(voe_.GetRED(channel_num)); + EXPECT_EQ(127, voe_.GetSendREDPayloadType(channel_num)); } -// Test that we set up FEC correctly as callee. +// Test that we set up RED correctly as callee. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCallee) { EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); channel_ = engine_.CreateChannel(); @@ -1361,11 +1559,11 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDAsCallee) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_TRUE(voe_.GetFEC(channel_num)); - EXPECT_EQ(127, voe_.GetSendFECPayloadType(channel_num)); + EXPECT_TRUE(voe_.GetRED(channel_num)); + EXPECT_EQ(127, voe_.GetSendREDPayloadType(channel_num)); } -// Test that we set up FEC correctly if params are omitted. +// Test that we set up RED correctly if params are omitted. TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDNoParams) { EXPECT_TRUE(SetupEngine()); int channel_num = voe_.GetLastChannel(); @@ -1380,8 +1578,8 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsREDNoParams) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_TRUE(voe_.GetFEC(channel_num)); - EXPECT_EQ(127, voe_.GetSendFECPayloadType(channel_num)); + EXPECT_TRUE(voe_.GetRED(channel_num)); + EXPECT_EQ(127, voe_.GetSendREDPayloadType(channel_num)); } // Test that we ignore RED if the parameters aren't named the way we expect. @@ -1400,7 +1598,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED1) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } // Test that we ignore RED if it uses different primary/secondary encoding. @@ -1419,7 +1617,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED2) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } // Test that we ignore RED if it uses more than 2 encodings. @@ -1438,7 +1636,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED3) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } // Test that we ignore RED if it has bogus codec ids. @@ -1457,7 +1655,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED4) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } // Test that we ignore RED if it refers to a codec that is not present. @@ -1476,38 +1674,25 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecsBadRED5) { EXPECT_EQ(0, voe_.GetSendCodec(channel_num, gcodec)); EXPECT_EQ(96, gcodec.pltype); EXPECT_STREQ("ISAC", gcodec.plname); - EXPECT_FALSE(voe_.GetFEC(channel_num)); + EXPECT_FALSE(voe_.GetRED(channel_num)); } -// Test that we support setting an empty list of recv header extensions. -TEST_F(WebRtcVoiceEngineTestFake, SetRecvRtpHeaderExtensions) { - EXPECT_TRUE(SetupEngine()); - std::vector<cricket::RtpHeaderExtension> extensions; - int channel_num = voe_.GetLastChannel(); - bool enable = false; - unsigned char id = 0; - - // An empty list shouldn't cause audio-level headers to be enabled. - EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); - EXPECT_EQ(0, voe_.GetRTPAudioLevelIndicationStatus( - channel_num, enable, id)); - EXPECT_FALSE(enable); - - // Nor should indicating we can receive the audio-level header. - extensions.push_back(cricket::RtpHeaderExtension( - "urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8)); - EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions)); - EXPECT_EQ(0, voe_.GetRTPAudioLevelIndicationStatus( - channel_num, enable, id)); - EXPECT_FALSE(enable); +// Test support for audio level header extension. +TEST_F(WebRtcVoiceEngineTestFake, SendAudioLevelHeaderExtensions) { + TestSetSendRtpHeaderExtensions(kRtpAudioLevelHeaderExtension); +} +#ifdef USE_WEBRTC_DEV_BRANCH +TEST_F(WebRtcVoiceEngineTestFake, RecvAudioLevelHeaderExtensions) { + TestSetRecvRtpHeaderExtensions(kRtpAudioLevelHeaderExtension); } +#endif // USE_WEBRTC_DEV_BRANCH -// Test that we support setting certain send header extensions. -TEST_F(WebRtcVoiceEngineTestFake, SetSendRtpHeaderExtensions) { - EXPECT_TRUE(SetupEngine()); - std::vector<cricket::RtpHeaderExtension> extensions; - int channel_num = voe_.GetLastChannel(); - TestSetSendRtpHeaderExtensions(channel_num); +// Test support for absolute send time header extension. +TEST_F(WebRtcVoiceEngineTestFake, SendAbsoluteSendTimeHeaderExtensions) { + TestSetSendRtpHeaderExtensions(kRtpAbsoluteSenderTimeHeaderExtension); +} +TEST_F(WebRtcVoiceEngineTestFake, RecvAbsoluteSendTimeHeaderExtensions) { + TestSetRecvRtpHeaderExtensions(kRtpAbsoluteSenderTimeHeaderExtension); } // Test that we can create a channel and start sending/playing out on it. @@ -1641,11 +1826,15 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStatsWithMultipleSendStreams) { EXPECT_TRUE(channel_->AddSendStream( cricket::StreamParams::CreateLegacy(kSsrcs4[i]))); } - + // Create a receive stream to check that none of the send streams end up in + // the receive stream stats. + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(kSsrc2))); // We need send codec to be set to get all stats. std::vector<cricket::AudioCodec> codecs; codecs.push_back(kPcmuCodec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); cricket::VoiceMediaInfo info; EXPECT_EQ(true, channel_->GetStats(&info)); @@ -1662,29 +1851,19 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStatsWithMultipleSendStreams) { EXPECT_EQ(cricket::kIntStatValue, info.senders[i].ext_seqnum); EXPECT_EQ(cricket::kIntStatValue, info.senders[i].rtt_ms); EXPECT_EQ(cricket::kIntStatValue, info.senders[i].jitter_ms); + EXPECT_EQ(kPcmuCodec.name, info.senders[i].codec_name); } - EXPECT_EQ(1u, info.receivers.size()); -} - -// Test that we support setting certain send header extensions on multiple -// send streams. -TEST_F(WebRtcVoiceEngineTestFake, - SetSendRtpHeaderExtensionsWithMultpleSendStreams) { - SetupForMultiSendStream(); - - static const uint32 kSsrcs4[] = {1, 2, 3, 4}; - // Create send streams. - for (unsigned int i = 0; i < ARRAY_SIZE(kSsrcs4); ++i) { - EXPECT_TRUE(channel_->AddSendStream( - cricket::StreamParams::CreateLegacy(kSsrcs4[i]))); - } + EXPECT_EQ(0u, info.receivers.size()); + DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame)); + EXPECT_EQ(true, channel_->GetStats(&info)); - // Test SendRtpHeaderExtensions on each send channel. - for (unsigned int i = 0; i < ARRAY_SIZE(kSsrcs4); ++i) { - int channel_num = voe_.GetChannelFromLocalSsrc(kSsrcs4[i]); - TestSetSendRtpHeaderExtensions(channel_num); - } + EXPECT_EQ(1u, info.receivers.size()); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].bytes_rcvd); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].packets_rcvd); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].packets_lost); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].ext_seqnum); + EXPECT_EQ(kPcmuCodec.name, info.receivers[0].codec_name); } // Test that we can add and remove receive streams, and do proper send/playout. @@ -1971,9 +2150,14 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendSsrc) { TEST_F(WebRtcVoiceEngineTestFake, GetStats) { // Setup. We need send codec to be set to get all stats. EXPECT_TRUE(SetupEngine()); + // SetupEngine adds a send stream with kSsrc1, so the receive stream has to + // use a different SSRC. + EXPECT_TRUE(channel_->AddRecvStream( + cricket::StreamParams::CreateLegacy(kSsrc2))); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kPcmuCodec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->SetRecvCodecs(codecs)); cricket::VoiceMediaInfo info; EXPECT_EQ(true, channel_->GetStats(&info)); @@ -1987,6 +2171,7 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStats) { EXPECT_EQ(cricket::kIntStatValue, info.senders[0].ext_seqnum); EXPECT_EQ(cricket::kIntStatValue, info.senders[0].rtt_ms); EXPECT_EQ(cricket::kIntStatValue, info.senders[0].jitter_ms); + EXPECT_EQ(kPcmuCodec.name, info.senders[0].codec_name); // TODO(sriniv): Add testing for more fields. These are not populated // in FakeWebrtcVoiceEngine yet. // EXPECT_EQ(cricket::kIntStatValue, info.senders[0].audio_level); @@ -1996,8 +2181,17 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStats) { // EXPECT_EQ(cricket::kIntStatValue, // info.senders[0].echo_return_loss_enhancement); + EXPECT_EQ(0u, info.receivers.size()); + DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame)); + EXPECT_EQ(true, channel_->GetStats(&info)); EXPECT_EQ(1u, info.receivers.size()); - // TODO(sriniv): Add testing for receiver fields. + + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].bytes_rcvd); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].packets_rcvd); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].packets_lost); + EXPECT_EQ(cricket::kIntStatValue, info.receivers[0].ext_seqnum); + EXPECT_EQ(kPcmuCodec.name, info.receivers[0].codec_name); + // TODO(sriniv): Add testing for more receiver fields. } // Test that we can set the outgoing SSRC properly with multiple streams. @@ -2586,7 +2780,6 @@ TEST_F(WebRtcVoiceEngineTestFake, InitDoesNotOverwriteDefaultAgcConfig) { EXPECT_EQ(set_config.limiterEnable, config.limiterEnable); } - TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { EXPECT_TRUE(SetupEngine()); talk_base::scoped_ptr<cricket::VoiceMediaChannel> channel1( @@ -2718,6 +2911,10 @@ TEST_F(WebRtcVoiceEngineTestFake, TestSetDscpOptions) { options.dscp.Set(true); EXPECT_TRUE(channel->SetOptions(options)); EXPECT_EQ(talk_base::DSCP_EF, network_interface->dscp()); + // Verify previous value is not modified if dscp option is not set. + cricket::AudioOptions options1; + EXPECT_TRUE(channel->SetOptions(options1)); + EXPECT_EQ(talk_base::DSCP_EF, network_interface->dscp()); options.dscp.Set(false); EXPECT_TRUE(channel->SetOptions(options)); EXPECT_EQ(talk_base::DSCP_DEFAULT, network_interface->dscp()); @@ -2782,7 +2979,6 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOutputScaling) { EXPECT_DOUBLE_EQ(1, right); } - // Tests for the actual WebRtc VoE library. // Tests that the library initializes and shuts down properly. @@ -2891,8 +3087,6 @@ TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) { EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(0, "", 0, 0, 2, 0))); EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(0, "", 5000, 0, 1, 0))); EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(0, "", 0, 5000, 1, 0))); - // Check that there aren't any extra codecs lying around. - EXPECT_EQ(13U, engine.codecs().size()); // Verify the payload id of common audio codecs, including CN, ISAC, and G722. for (std::vector<cricket::AudioCodec>::const_iterator it = engine.codecs().begin(); it != engine.codecs().end(); ++it) { @@ -2983,39 +3177,3 @@ TEST(WebRtcVoiceEngineTest, CoInitialize) { } #endif - -TEST_F(WebRtcVoiceEngineTestFake, SetExperimentalAcm) { - EXPECT_TRUE(SetupEngine()); - - // By default experimental ACM should not be used. - int media_channel = engine_.CreateMediaVoiceChannel(); - ASSERT_GE(media_channel, 0); - EXPECT_FALSE(voe_.IsUsingExperimentalAcm(media_channel)); - - int soundclip_channel = engine_.CreateSoundclipVoiceChannel(); - ASSERT_GE(soundclip_channel, 0); - EXPECT_FALSE(voe_sc_.IsUsingExperimentalAcm(soundclip_channel)); - - // Set options to use experimental ACM. - cricket::AudioOptions options; - options.experimental_acm.Set(true); - ASSERT_TRUE(engine_.SetOptions(options)); - media_channel = engine_.CreateMediaVoiceChannel(); - ASSERT_GE(media_channel, 0); - EXPECT_TRUE(voe_.IsUsingExperimentalAcm(media_channel)); - - soundclip_channel = engine_.CreateSoundclipVoiceChannel(); - ASSERT_GE(soundclip_channel, 0); - EXPECT_TRUE(voe_sc_.IsUsingExperimentalAcm(soundclip_channel)); - - // Set option to use legacy ACM. - options.experimental_acm.Set(false); - ASSERT_TRUE(engine_.SetOptions(options)); - media_channel = engine_.CreateMediaVoiceChannel(); - ASSERT_GE(media_channel, 0); - EXPECT_FALSE(voe_.IsUsingExperimentalAcm(media_channel)); - - soundclip_channel = engine_.CreateSoundclipVoiceChannel(); - ASSERT_GE(soundclip_channel, 0); - EXPECT_FALSE(voe_sc_.IsUsingExperimentalAcm(soundclip_channel)); -} diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.cc b/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.cc index 67178f4985e..8bcfa3ad272 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.cc @@ -27,7 +27,7 @@ #include "talk/p2p/base/asyncstuntcpsocket.h" -#include <cstring> +#include <string.h> #include "talk/base/common.h" #include "talk/base/logging.h" @@ -65,9 +65,8 @@ AsyncStunTCPSocket::AsyncStunTCPSocket( : talk_base::AsyncTCPSocketBase(socket, listen, kBufSize) { } -// TODO(mallinath) - Add support of setting DSCP code on AsyncSocket. int AsyncStunTCPSocket::Send(const void *pv, size_t cb, - talk_base::DiffServCodePoint dscp) { + const talk_base::PacketOptions& options) { if (cb > kBufSize || cb < kPacketLenSize + kPacketLenOffset) { SetError(EMSGSIZE); return -1; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.h b/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.h index ff748d1f287..bef8e980900 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.h @@ -48,7 +48,7 @@ class AsyncStunTCPSocket : public talk_base::AsyncTCPSocketBase { virtual ~AsyncStunTCPSocket() {} virtual int Send(const void* pv, size_t cb, - talk_base::DiffServCodePoint dscp); + const talk_base::PacketOptions& options); virtual void ProcessInput(char* data, size_t* len); virtual void HandleIncomingConnection(talk_base::AsyncSocket* socket); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket_unittest.cc index c6a7b1b6fbc..f3261df52dc 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket_unittest.cc @@ -122,8 +122,9 @@ class AsyncStunTCPSocketTest : public testing::Test, } bool Send(const void* data, size_t len) { + talk_base::PacketOptions options; size_t ret = send_socket_->Send( - reinterpret_cast<const char*>(data), len, talk_base::DSCP_NO_CHANGE); + reinterpret_cast<const char*>(data), len, options); vss_->ProcessMessagesUntilIdle(); return (ret == len); } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/candidate.h b/chromium/third_party/libjingle/source/talk/p2p/base/candidate.h index 19eed8cc320..547725df830 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/candidate.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/candidate.h @@ -28,11 +28,13 @@ #ifndef TALK_P2P_BASE_CANDIDATE_H_ #define TALK_P2P_BASE_CANDIDATE_H_ -#include <climits> -#include <cmath> +#include <limits.h> +#include <math.h> + #include <string> #include <sstream> #include <iomanip> + #include "talk/base/basictypes.h" #include "talk/base/socketaddress.h" #include "talk/p2p/base/constants.h" @@ -163,13 +165,30 @@ class Candidate { return ToStringInternal(true); } - uint32 GetPriority(uint32 type_preference) const { + uint32 GetPriority(uint32 type_preference, + int network_adapter_preference) const { // RFC 5245 - 4.1.2.1. // priority = (2^24)*(type preference) + // (2^8)*(local preference) + // (2^0)*(256 - component ID) + + // |local_preference| length is 2 bytes, 0-65535 inclusive. + // In our implemenation we will partion local_preference into + // 0 1 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | NIC Pref | Addr Pref | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // NIC Type - Type of the network adapter e.g. 3G/Wifi/Wired. + // Addr Pref - Address preference value as per RFC 3484. + // local preference is calculated as - NIC Type << 8 | Addr_Pref. + int addr_pref = IPAddressPrecedence(address_.ipaddr()); - return (type_preference << 24) | (addr_pref << 8) | (256 - component_); + int local_preference = (network_adapter_preference << 8) | addr_pref; + + return (type_preference << 24) | + (local_preference << 8) | + (256 - component_); } private: @@ -177,9 +196,9 @@ class Candidate { std::ostringstream ost; std::string address = sensitive ? address_.ToSensitiveString() : address_.ToString(); - ost << "Cand[" << id_ << ":" << component_ << ":" - << type_ << ":" << protocol_ << ":" - << network_name_ << ":" << address << ":" + ost << "Cand[" << foundation_ << ":" << component_ << ":" + << protocol_ << ":" << priority_ << ":" + << address << ":" << type_ << ":" << related_address_ << ":" << username_ << ":" << password_ << "]"; return ost.str(); } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/constants.cc b/chromium/third_party/libjingle/source/talk/p2p/base/constants.cc index 27d43096b9d..2e57d9d18d6 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/constants.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/constants.cc @@ -176,6 +176,10 @@ const int ICE_UFRAG_LENGTH = 16; // Minimum password length of 22 characters as per RFC5245. We chose 24 because // some internal systems expect password to be multiple of 4. const int ICE_PWD_LENGTH = 24; +const size_t ICE_UFRAG_MIN_LENGTH = 4; +const size_t ICE_PWD_MIN_LENGTH = 22; +const size_t ICE_UFRAG_MAX_LENGTH = 255; +const size_t ICE_PWD_MAX_LENGTH = 256; // TODO: This is media-specific, so might belong // somewhere like media/base/constants.h const int ICE_CANDIDATE_COMPONENT_RTP = 1; @@ -202,7 +206,6 @@ const buzz::StaticQName QN_ADDRESS = { cricket::NS_EMPTY, "address" }; const buzz::StaticQName QN_USERNAME = { cricket::NS_EMPTY, "username" }; const buzz::StaticQName QN_PASSWORD = { cricket::NS_EMPTY, "password" }; const buzz::StaticQName QN_PREFERENCE = { cricket::NS_EMPTY, "preference" }; -const char GICE_CANDIDATE_TYPE_STUN[] = "stun"; const char GICE_CHANNEL_NAME_RTP[] = "rtp"; const char GICE_CHANNEL_NAME_RTCP[] = "rtcp"; const char GICE_CHANNEL_NAME_VIDEO_RTP[] = "video_rtp"; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/constants.h b/chromium/third_party/libjingle/source/talk/p2p/base/constants.h index 99e006a0185..61dd815b381 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/constants.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/constants.h @@ -178,6 +178,10 @@ extern const char ICE_CANDIDATE_TYPE_PEER_STUN[]; extern const char ICE_CANDIDATE_TYPE_SERVER_STUN[]; extern const int ICE_UFRAG_LENGTH; extern const int ICE_PWD_LENGTH; +extern const size_t ICE_UFRAG_MIN_LENGTH; +extern const size_t ICE_PWD_MIN_LENGTH; +extern const size_t ICE_UFRAG_MAX_LENGTH; +extern const size_t ICE_PWD_MAX_LENGTH; extern const int ICE_CANDIDATE_COMPONENT_RTP; extern const int ICE_CANDIDATE_COMPONENT_RTCP; extern const int ICE_CANDIDATE_COMPONENT_DEFAULT; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransport.h b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransport.h index 7492171ee1e..6bef185ce89 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransport.h @@ -66,8 +66,8 @@ class DtlsTransport : public Base { return true; } - virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* - channel) { + virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel, + std::string* error_desc) { talk_base::SSLFingerprint* local_fp = Base::local_description()->identity_fingerprint.get(); @@ -79,30 +79,36 @@ class DtlsTransport : public Base { identity_)); ASSERT(local_fp_tmp.get() != NULL); if (!(*local_fp_tmp == *local_fp)) { - LOG(LS_WARNING) << "Local fingerprint does not match identity"; - return false; + std::ostringstream desc; + desc << "Local fingerprint does not match identity. Expected: "; + desc << local_fp_tmp->ToString(); + desc << " Got: " << local_fp->ToString(); + return BadTransportDescription(desc.str(), error_desc); } } else { - LOG(LS_WARNING) - << "Local fingerprint provided but no identity available"; - return false; + return BadTransportDescription( + "Local fingerprint provided but no identity available.", + error_desc); } } else { identity_ = NULL; } - if (!channel->SetLocalIdentity(identity_)) - return false; + if (!channel->SetLocalIdentity(identity_)) { + return BadTransportDescription("Failed to set local identity.", + error_desc); + } // Apply the description in the base class. - return Base::ApplyLocalTransportDescription_w(channel); + return Base::ApplyLocalTransportDescription_w(channel, error_desc); } - virtual bool NegotiateTransportDescription_w(ContentAction local_role) { + virtual bool NegotiateTransportDescription_w(ContentAction local_role, + std::string* error_desc) { if (!Base::local_description() || !Base::remote_description()) { - LOG(LS_INFO) << "Local and Remote description must be set before " - << "transport descriptions are negotiated"; - return false; + const std::string msg = "Local and Remote description must be set before " + "transport descriptions are negotiated"; + return BadTransportDescription(msg, error_desc); } talk_base::SSLFingerprint* local_fp = @@ -144,8 +150,9 @@ class DtlsTransport : public Base { bool is_remote_server = false; if (local_role == CA_OFFER) { if (local_connection_role != CONNECTIONROLE_ACTPASS) { - LOG(LS_ERROR) << "Offerer must use actpass value for setup attribute"; - return false; + return BadTransportDescription( + "Offerer must use actpass value for setup attribute.", + error_desc); } if (remote_connection_role == CONNECTIONROLE_ACTIVE || @@ -153,25 +160,28 @@ class DtlsTransport : public Base { remote_connection_role == CONNECTIONROLE_NONE) { is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE); } else { - LOG(LS_ERROR) << "Answerer must use either active or passive value " - << "for setup attribute"; - return false; + const std::string msg = + "Answerer must use either active or passive value " + "for setup attribute."; + return BadTransportDescription(msg, error_desc); } // If remote is NONE or ACTIVE it will act as client. } else { if (remote_connection_role != CONNECTIONROLE_ACTPASS && remote_connection_role != CONNECTIONROLE_NONE) { - LOG(LS_ERROR) << "Offerer must use actpass value for setup attribute"; - return false; + return BadTransportDescription( + "Offerer must use actpass value for setup attribute.", + error_desc); } if (local_connection_role == CONNECTIONROLE_ACTIVE || local_connection_role == CONNECTIONROLE_PASSIVE) { is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE); } else { - LOG(LS_ERROR) << "Answerer must use either active or passive value " - << "for setup attribute"; - return false; + const std::string msg = + "Answerer must use either active or passive value " + "for setup attribute."; + return BadTransportDescription(msg, error_desc); } // If local is passive, local will act as server. @@ -181,9 +191,9 @@ class DtlsTransport : public Base { talk_base::SSL_SERVER; } else if (local_fp && (local_role == CA_ANSWER)) { - LOG(LS_ERROR) - << "Local fingerprint supplied when caller didn't offer DTLS"; - return false; + return BadTransportDescription( + "Local fingerprint supplied when caller didn't offer DTLS.", + error_desc); } else { // We are not doing DTLS remote_fingerprint_.reset(new talk_base::SSLFingerprint( @@ -191,7 +201,7 @@ class DtlsTransport : public Base { } // Now run the negotiation for the base class. - return Base::NegotiateTransportDescription_w(local_role); + return Base::NegotiateTransportDescription_w(local_role, error_desc); } virtual DtlsTransportChannelWrapper* CreateTransportChannel(int component) { @@ -216,12 +226,13 @@ class DtlsTransport : public Base { private: virtual bool ApplyNegotiatedTransportDescription_w( - TransportChannelImpl* channel) { + TransportChannelImpl* channel, + std::string* error_desc) { // Set ssl role. Role must be set before fingerprint is applied, which // initiates DTLS setup. if (!channel->SetSslRole(secure_role_)) { - LOG(LS_INFO) << "Failed to set ssl role for the channel."; - return false; + return BadTransportDescription("Failed to set ssl role for the channel.", + error_desc); } // Apply remote fingerprint. if (!channel->SetRemoteFingerprint( @@ -229,9 +240,10 @@ class DtlsTransport : public Base { reinterpret_cast<const uint8 *>(remote_fingerprint_-> digest.data()), remote_fingerprint_->digest.length())) { - return false; + return BadTransportDescription("Failed to apply remote fingerprint.", + error_desc); } - return Base::ApplyNegotiatedTransportDescription_w(channel); + return Base::ApplyNegotiatedTransportDescription_w(channel, error_desc); } talk_base::SSLIdentity* identity_; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.cc b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.cc index 472299959a0..416e6e93286 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.cc @@ -42,7 +42,6 @@ namespace cricket { static const size_t kDtlsRecordHeaderLen = 13; static const size_t kMaxDtlsPacketLen = 2048; static const size_t kMinRtpPacketLen = 12; -static const size_t kDefaultVideoAndDataCryptos = 1; static bool IsDtlsPacket(const char* data, size_t len) { const uint8* u = reinterpret_cast<const uint8*>(data); @@ -71,8 +70,9 @@ talk_base::StreamResult StreamInterfaceChannel::Write(const void* data, int* error) { // Always succeeds, since this is an unreliable transport anyway. // TODO: Should this block if channel_'s temporarily unwritable? - channel_->SendPacket( - static_cast<const char*>(data), data_len, talk_base::DSCP_NO_CHANGE); + talk_base::PacketOptions packet_options; + channel_->SendPacket(static_cast<const char*>(data), data_len, + packet_options); if (written) { *written = data_len; } @@ -124,6 +124,8 @@ DtlsTransportChannelWrapper::DtlsTransportChannelWrapper( &DtlsTransportChannelWrapper::OnRoleConflict); channel_->SignalRouteChange.connect(this, &DtlsTransportChannelWrapper::OnRouteChange); + channel_->SignalConnectionRemoved.connect(this, + &DtlsTransportChannelWrapper::OnConnectionRemoved); } DtlsTransportChannelWrapper::~DtlsTransportChannelWrapper() { @@ -154,14 +156,15 @@ void DtlsTransportChannelWrapper::Reset() { bool DtlsTransportChannelWrapper::SetLocalIdentity( talk_base::SSLIdentity* identity) { - if (dtls_state_ == STATE_OPEN && identity == local_identity_) { - return true; - } - - // TODO(ekr@rtfm.com): Forbid this if Connect() has been called. if (dtls_state_ != STATE_NONE) { - LOG_J(LS_ERROR, this) << "Can't set DTLS local identity in this state"; - return false; + if (identity == local_identity_) { + // This may happen during renegotiation. + LOG_J(LS_INFO, this) << "Ignoring identical DTLS identity"; + return true; + } else { + LOG_J(LS_ERROR, this) << "Can't change DTLS local identity in this state"; + return false; + } } if (identity) { @@ -208,8 +211,11 @@ bool DtlsTransportChannelWrapper::SetRemoteFingerprint( talk_base::Buffer remote_fingerprint_value(digest, digest_len); - if ((dtls_state_ == STATE_OPEN) && - (remote_fingerprint_value_ == remote_fingerprint_value)) { + if (dtls_state_ != STATE_NONE && + remote_fingerprint_value_ == remote_fingerprint_value && + !digest_alg.empty()) { + // This may happen during renegotiation. + LOG_J(LS_INFO, this) << "Ignoring identical remote DTLS fingerprint"; return true; } @@ -339,9 +345,9 @@ bool DtlsTransportChannelWrapper::GetSrtpCipher(std::string* cipher) { // Called from upper layers to send a media packet. -int DtlsTransportChannelWrapper::SendPacket(const char* data, size_t size, - talk_base::DiffServCodePoint dscp, - int flags) { +int DtlsTransportChannelWrapper::SendPacket( + const char* data, size_t size, + const talk_base::PacketOptions& options, int flags) { int result = -1; switch (dtls_state_) { @@ -361,11 +367,11 @@ int DtlsTransportChannelWrapper::SendPacket(const char* data, size_t size, if (flags & PF_SRTP_BYPASS) { ASSERT(!srtp_ciphers_.empty()); if (!IsRtpPacket(data, size)) { - result = false; + result = -1; break; } - result = channel_->SendPacket(data, size, dscp); + result = channel_->SendPacket(data, size, options); } else { result = (dtls_->WriteAll(data, size, NULL, NULL) == talk_base::SR_SUCCESS) ? static_cast<int>(size) : -1; @@ -373,7 +379,7 @@ int DtlsTransportChannelWrapper::SendPacket(const char* data, size_t size, break; // Not doing DTLS. case STATE_NONE: - result = channel_->SendPacket(data, size, dscp); + result = channel_->SendPacket(data, size, options); break; case STATE_CLOSED: // Can't send anything when we're closed. @@ -621,4 +627,10 @@ void DtlsTransportChannelWrapper::OnRouteChange( SignalRouteChange(this, candidate); } +void DtlsTransportChannelWrapper::OnConnectionRemoved( + TransportChannelImpl* channel) { + ASSERT(channel == channel_); + SignalConnectionRemoved(this); +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.h b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.h index d6b73467486..232d400c652 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.h @@ -127,6 +127,9 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { virtual IceRole GetIceRole() const { return channel_->GetIceRole(); } + virtual size_t GetConnectionCount() const { + return channel_->GetConnectionCount(); + } virtual bool SetLocalIdentity(talk_base::SSLIdentity *identity); virtual bool GetLocalIdentity(talk_base::SSLIdentity** identity) const; @@ -137,7 +140,7 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { // Called to send a packet (via DTLS, if turned on). virtual int SendPacket(const char* data, size_t size, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, int flags); // TransportChannel calls that we forward to the wrapped transport. @@ -193,6 +196,9 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { virtual void SetIceTiebreaker(uint64 tiebreaker) { channel_->SetIceTiebreaker(tiebreaker); } + virtual bool GetIceProtocolType(IceProtocolType* type) const { + return channel_->GetIceProtocolType(type); + } virtual void SetIceProtocolType(IceProtocolType type) { channel_->SetIceProtocolType(type); } @@ -236,6 +242,7 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { void OnCandidatesAllocationDone(TransportChannelImpl* channel); void OnRoleConflict(TransportChannelImpl* channel); void OnRouteChange(TransportChannel* channel, const Candidate& candidate); + void OnConnectionRemoved(TransportChannelImpl* channel); Transport* transport_; // The transport_ that created us. talk_base::Thread* worker_thread_; // Everything should occur on this thread. diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel_unittest.cc index 1fd82d71073..5727ac4cf7b 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel_unittest.cc @@ -185,14 +185,14 @@ class DtlsTestClient : public sigslot::has_slots<> { // content action is CA_ANSWER. if (action == cricket::CA_OFFER) { ASSERT_TRUE(transport_->SetLocalTransportDescription( - local_desc, cricket::CA_OFFER)); + local_desc, cricket::CA_OFFER, NULL)); ASSERT_EQ(expect_success, transport_->SetRemoteTransportDescription( - remote_desc, cricket::CA_ANSWER)); + remote_desc, cricket::CA_ANSWER, NULL)); } else { ASSERT_TRUE(transport_->SetRemoteTransportDescription( - remote_desc, cricket::CA_OFFER)); + remote_desc, cricket::CA_OFFER, NULL)); ASSERT_EQ(expect_success, transport_->SetLocalTransportDescription( - local_desc, cricket::CA_ANSWER)); + local_desc, cricket::CA_ANSWER, NULL)); } negotiated_dtls_ = (local_identity && remote_identity); } @@ -245,14 +245,26 @@ class DtlsTestClient : public sigslot::has_slots<> { // Only set the bypass flag if we've activated DTLS. int flags = (identity_.get() && srtp) ? cricket::PF_SRTP_BYPASS : 0; + talk_base::PacketOptions packet_options; int rv = channels_[channel]->SendPacket( - packet.get(), size, talk_base::DSCP_NO_CHANGE, flags); + packet.get(), size, packet_options, flags); ASSERT_GT(rv, 0); ASSERT_EQ(size, static_cast<size_t>(rv)); ++sent; } while (sent < count); } + int SendInvalidSrtpPacket(size_t channel, size_t size) { + ASSERT(channel < channels_.size()); + talk_base::scoped_ptr<char[]> packet(new char[size]); + // Fill the packet with 0 to form an invalid SRTP packet. + memset(packet.get(), 0, size); + + talk_base::PacketOptions packet_options; + return channels_[channel]->SendPacket( + packet.get(), size, packet_options, cricket::PF_SRTP_BYPASS); + } + void ExpectPackets(size_t channel, size_t size) { packet_size_ = size; received_.clear(); @@ -623,6 +635,16 @@ TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtp) { TestTransfer(0, 1000, 100, true); } +// Connect with DTLS-SRTP, transfer an invalid SRTP packet, and expects -1 +// returned. +TEST_F(DtlsTransportChannelTest, TestTransferDtlsInvalidSrtpPacket) { + MAYBE_SKIP_TEST(HaveDtls); + PrepareDtls(true, true); + PrepareDtlsSrtp(true, true); + ASSERT_TRUE(Connect()); + int result = client1_.SendInvalidSrtpPacket(0, 100); + ASSERT_EQ(-1, result); +} // Connect with DTLS. A does DTLS-SRTP but B does not. TEST_F(DtlsTransportChannelTest, TestTransferDtlsSrtpRejected) { @@ -754,6 +776,25 @@ TEST_F(DtlsTransportChannelTest, TestDtlsReOfferWithDifferentSetupAttr) { TestTransfer(1, 1000, 100, true); } +// Test that re-negotiation can be started before the clients become connected +// in the first negotiation. +TEST_F(DtlsTransportChannelTest, TestRenegotiateBeforeConnect) { + MAYBE_SKIP_TEST(HaveDtlsSrtp); + SetChannelCount(2); + PrepareDtls(true, true); + PrepareDtlsSrtp(true, true); + Negotiate(); + + Renegotiate(&client1_, cricket::CONNECTIONROLE_ACTPASS, + cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER); + bool rv = client1_.Connect(&client2_); + EXPECT_TRUE(rv); + EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000); + + TestTransfer(0, 1000, 100, true); + TestTransfer(1, 1000, 100, true); +} + // Test Certificates state after negotiation but before connection. TEST_F(DtlsTransportChannelTest, TestCertificatesBeforeConnect) { MAYBE_SKIP_TEST(HaveDtls); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/fakesession.h b/chromium/third_party/libjingle/source/talk/p2p/base/fakesession.h index 2615f50dffe..f2c5b84d763 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/fakesession.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/fakesession.h @@ -73,7 +73,8 @@ class FakeTransportChannel : public TransportChannelImpl, ice_proto_(ICEPROTO_HYBRID), remote_ice_mode_(ICEMODE_FULL), dtls_fingerprint_("", NULL, 0), - ssl_role_(talk_base::SSL_CLIENT) { + ssl_role_(talk_base::SSL_CLIENT), + connection_count_(0) { } ~FakeTransportChannel() { Reset(); @@ -100,7 +101,12 @@ class FakeTransportChannel : public TransportChannelImpl, virtual void SetIceRole(IceRole role) { role_ = role; } virtual IceRole GetIceRole() const { return role_; } + virtual size_t GetConnectionCount() const { return connection_count_; } virtual void SetIceTiebreaker(uint64 tiebreaker) { tiebreaker_ = tiebreaker; } + virtual bool GetIceProtocolType(IceProtocolType* type) const { + *type = ice_proto_; + return true; + } virtual void SetIceProtocolType(IceProtocolType type) { ice_proto_ = type; } virtual void SetIceCredentials(const std::string& ice_ufrag, const std::string& ice_pwd) { @@ -170,8 +176,15 @@ class FakeTransportChannel : public TransportChannelImpl, } } + void SetConnectionCount(size_t connection_count) { + size_t old_connection_count = connection_count_; + connection_count_ = connection_count; + if (connection_count_ < old_connection_count) + SignalConnectionRemoved(this); + } + virtual int SendPacket(const char* data, size_t len, - talk_base::DiffServCodePoint dscp, int flags) { + const talk_base::PacketOptions& options, int flags) { if (state_ != STATE_CONNECTED) { return -1; } @@ -309,6 +322,7 @@ class FakeTransportChannel : public TransportChannelImpl, IceMode remote_ice_mode_; talk_base::SSLFingerprint dtls_fingerprint_; talk_base::SSLRole ssl_role_; + size_t connection_count_; }; // Fake transport class, which can be passed to anything that needs a Transport. diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc b/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc index 38cc35445ea..887fc45efae 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc @@ -167,7 +167,7 @@ P2PTransportChannel::P2PTransportChannel(const std::string& content_name, pending_best_connection_(NULL), sort_dirty_(false), was_writable_(false), - protocol_type_(ICEPROTO_GOOGLE), + protocol_type_(ICEPROTO_HYBRID), remote_ice_mode_(ICEMODE_FULL), ice_role_(ICEROLE_UNKNOWN), tiebreaker_(0), @@ -237,6 +237,11 @@ void P2PTransportChannel::SetIceTiebreaker(uint64 tiebreaker) { tiebreaker_ = tiebreaker; } +bool P2PTransportChannel::GetIceProtocolType(IceProtocolType* type) const { + *type = protocol_type_; + return true; +} + void P2PTransportChannel::SetIceProtocolType(IceProtocolType type) { ASSERT(worker_thread_ == talk_base::Thread::Current()); @@ -465,9 +470,8 @@ void P2PTransportChannel::OnUnknownAddress( } } else { // Create a new candidate with this address. - std::string type; - if (protocol_type_ == ICEPROTO_RFC5245) { + if (port->IceProtocol() == ICEPROTO_RFC5245) { type = PRFLX_PORT_TYPE; } else { // G-ICE doesn't support prflx candidate. @@ -488,10 +492,11 @@ void P2PTransportChannel::OnUnknownAddress( port->Network()->name(), 0U, talk_base::ToString<uint32>(talk_base::ComputeCrc32(id))); new_remote_candidate.set_priority( - new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX)); + new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX, + port->Network()->preference())); } - if (protocol_type_ == ICEPROTO_RFC5245) { + if (port->IceProtocol() == ICEPROTO_RFC5245) { // RFC 5245 // If the source transport address of the request does not match any // existing remote candidates, it represents a new peer reflexive remote @@ -623,7 +628,7 @@ void P2PTransportChannel::OnCandidate(const Candidate& candidate) { // Creates connections from all of the ports that we care about to the given // remote candidate. The return value is true if we created a connection from // the origin port. -bool P2PTransportChannel::CreateConnections(const Candidate &remote_candidate, +bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate, PortInterface* origin_port, bool readable) { ASSERT(worker_thread_ == talk_base::Thread::Current()); @@ -642,14 +647,25 @@ bool P2PTransportChannel::CreateConnections(const Candidate &remote_candidate, new_remote_candidate.set_password(remote_ice_pwd_); } + // If we've already seen the new remote candidate (in the current candidate + // generation), then we shouldn't try creating connections for it. + // We either already have a connection for it, or we previously created one + // and then later pruned it. If we don't return, the channel will again + // re-create any connections that were previously pruned, which will then + // immediately be re-pruned, churning the network for no purpose. + // This only applies to candidates received over signaling (i.e. origin_port + // is NULL). + if (!origin_port && IsDuplicateRemoteCandidate(new_remote_candidate)) { + // return true to indicate success, without creating any new connections. + return true; + } + // Add a new connection for this candidate to every port that allows such a // connection (i.e., if they have compatible protocols) and that does not // already have a connection to an equivalent candidate. We must be careful // to make sure that the origin port is included, even if it was pruned, // since that may be the only port that can create this connection. - bool created = false; - std::vector<PortInterface *>::reverse_iterator it; for (it = ports_.rbegin(); it != ports_.rend(); ++it) { if (CreateConnection(*it, new_remote_candidate, origin_port, readable)) { @@ -684,7 +700,11 @@ bool P2PTransportChannel::CreateConnection(PortInterface* port, // It is not legal to try to change any of the parameters of an existing // connection; however, the other side can send a duplicate candidate. if (!remote_candidate.IsEquivalent(connection->remote_candidate())) { - LOG(INFO) << "Attempt to change a remote candidate"; + LOG(INFO) << "Attempt to change a remote candidate." + << " Existing remote candidate: " + << connection->remote_candidate().ToString() + << "New remote candidate: " + << remote_candidate.ToString(); return false; } } else { @@ -734,6 +754,17 @@ uint32 P2PTransportChannel::GetRemoteCandidateGeneration( return remote_candidate_generation_; } +// Check if remote candidate is already cached. +bool P2PTransportChannel::IsDuplicateRemoteCandidate( + const Candidate& candidate) { + for (uint32 i = 0; i < remote_candidates_.size(); ++i) { + if (remote_candidates_[i].IsEquivalent(candidate)) { + return true; + } + } + return false; +} + // Maintain our remote candidate list, adding this new remote one. void P2PTransportChannel::RememberRemoteCandidate( const Candidate& remote_candidate, PortInterface* origin_port) { @@ -751,12 +782,9 @@ void P2PTransportChannel::RememberRemoteCandidate( } // Make sure this candidate is not a duplicate. - for (uint32 i = 0; i < remote_candidates_.size(); ++i) { - if (remote_candidates_[i].IsEquivalent(remote_candidate)) { - LOG(INFO) << "Duplicate candidate: " - << remote_candidate.address().ToSensitiveString(); - return; - } + if (IsDuplicateRemoteCandidate(remote_candidate)) { + LOG(INFO) << "Duplicate candidate: " << remote_candidate.ToString(); + return; } // Try this candidate for all future ports. @@ -789,7 +817,7 @@ int P2PTransportChannel::SetOption(talk_base::Socket::Option opt, int value) { // Send data to the other side, using our best connection. int P2PTransportChannel::SendPacket(const char *data, size_t len, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, int flags) { ASSERT(worker_thread_ == talk_base::Thread::Current()); if (flags != 0) { @@ -801,7 +829,7 @@ int P2PTransportChannel::SendPacket(const char *data, size_t len, return -1; } - int sent = best_connection_->Send(data, len, dscp); + int sent = best_connection_->Send(data, len, options); if (sent <= 0) { ASSERT(sent < 0); error_ = best_connection_->GetError(); @@ -884,6 +912,15 @@ void P2PTransportChannel::SortConnections() { // will be sorted. UpdateConnectionStates(); + if (protocol_type_ == ICEPROTO_HYBRID) { + // If we are in hybrid mode, we are not sending any ping requests, so there + // is no point in sorting the connections. In hybrid state, ports can have + // different protocol than hybrid and protocol may differ from one another. + // Instead just update the state of this channel + UpdateChannelState(); + return; + } + // Any changes after this point will require a re-sort. sort_dirty_ = false; @@ -995,8 +1032,10 @@ void P2PTransportChannel::UpdateChannelState() { bool readable = false; for (uint32 i = 0; i < connections_.size(); ++i) { - if (connections_[i]->read_state() == Connection::STATE_READABLE) + if (connections_[i]->read_state() == Connection::STATE_READABLE) { readable = true; + break; + } } set_readable(readable); } @@ -1209,6 +1248,8 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) { SwitchBestConnectionTo(NULL); RequestSort(); } + + SignalConnectionRemoved(this); } // When a port is destroyed remove it from our list of ports to use for diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h b/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h index 6f287f369c6..09dabd56770 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h @@ -79,6 +79,8 @@ class P2PTransportChannel : public TransportChannelImpl, virtual void SetIceRole(IceRole role); virtual IceRole GetIceRole() const { return ice_role_; } virtual void SetIceTiebreaker(uint64 tiebreaker); + virtual size_t GetConnectionCount() const { return connections_.size(); } + virtual bool GetIceProtocolType(IceProtocolType* type) const; virtual void SetIceProtocolType(IceProtocolType type); virtual void SetIceCredentials(const std::string& ice_ufrag, const std::string& ice_pwd); @@ -92,7 +94,7 @@ class P2PTransportChannel : public TransportChannelImpl, // From TransportChannel: virtual int SendPacket(const char *data, size_t len, - talk_base::DiffServCodePoint dscp, int flags); + const talk_base::PacketOptions& options, int flags); virtual int SetOption(talk_base::Socket::Option opt, int value); virtual int GetError() { return error_; } virtual bool GetStats(std::vector<ConnectionInfo>* stats); @@ -187,6 +189,7 @@ class P2PTransportChannel : public TransportChannelImpl, bool FindConnection(cricket::Connection* connection) const; uint32 GetRemoteCandidateGeneration(const Candidate& candidate); + bool IsDuplicateRemoteCandidate(const Candidate& candidate); void RememberRemoteCandidate(const Candidate& remote_candidate, PortInterface* origin_port); bool IsPingable(Connection* conn); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel_unittest.cc index 3c24ded632a..498fde9dcaf 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel_unittest.cc @@ -36,11 +36,13 @@ #include "talk/base/physicalsocketserver.h" #include "talk/base/proxyserver.h" #include "talk/base/socketaddress.h" +#include "talk/base/ssladapter.h" #include "talk/base/thread.h" #include "talk/base/virtualsocketserver.h" #include "talk/p2p/base/p2ptransportchannel.h" #include "talk/p2p/base/testrelayserver.h" #include "talk/p2p/base/teststunserver.h" +#include "talk/p2p/base/testturnserver.h" #include "talk/p2p/client/basicportallocator.h" using cricket::kDefaultPortAllocatorFlags; @@ -92,6 +94,11 @@ static const SocketAddress kRelayTcpIntAddr("99.99.99.2", 5002); static const SocketAddress kRelayTcpExtAddr("99.99.99.3", 5003); static const SocketAddress kRelaySslTcpIntAddr("99.99.99.2", 5004); static const SocketAddress kRelaySslTcpExtAddr("99.99.99.3", 5005); +// The addresses for the public turn server. +static const SocketAddress kTurnUdpIntAddr("99.99.99.4", + cricket::STUN_SERVER_PORT); +static const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0); +static const cricket::RelayCredentials kRelayCredentials("test", "test"); // Based on ICE_UFRAG_LENGTH static const char* kIceUfrag[4] = {"TESTICEUFRAG0000", "TESTICEUFRAG0001", @@ -132,6 +139,7 @@ class P2PTransportChannelTestBase : public testing::Test, ss_(new talk_base::FirewallSocketServer(nss_.get())), ss_scope_(ss_.get()), stun_server_(main_, kStunAddr), + turn_server_(main_, kTurnUdpIntAddr, kTurnUdpExtAddr), relay_server_(main_, kRelayUdpIntAddr, kRelayUdpExtAddr, kRelayTcpIntAddr, kRelayTcpExtAddr, kRelaySslTcpIntAddr, kRelaySslTcpExtAddr), @@ -139,9 +147,11 @@ class P2PTransportChannelTestBase : public testing::Test, ss_.get(), kSocksProxyAddrs[0]), socks_server2_(ss_.get(), kSocksProxyAddrs[1], ss_.get(), kSocksProxyAddrs[1]), - clear_remote_candidates_ufrag_pwd_(false) { + clear_remote_candidates_ufrag_pwd_(false), + force_relay_(false) { ep1_.role_ = cricket::ICEROLE_CONTROLLING; ep2_.role_ = cricket::ICEROLE_CONTROLLED; + ep1_.allocator_.reset(new cricket::BasicPortAllocator( &ep1_.network_manager_, kStunAddr, kRelayUdpIntAddr, kRelayTcpIntAddr, kRelaySslTcpIntAddr)); @@ -237,7 +247,7 @@ class P2PTransportChannelTestBase : public testing::Test, } talk_base::FakeNetworkManager network_manager_; - talk_base::scoped_ptr<cricket::PortAllocator> allocator_; + talk_base::scoped_ptr<cricket::BasicPortAllocator> allocator_; ChannelData cd1_; ChannelData cd2_; int signaling_delay_; @@ -354,8 +364,11 @@ class P2PTransportChannelTestBase : public testing::Test, static const Result kPrflxTcpToLocalTcp; static void SetUpTestCase() { - // Ensure the RNG is inited. - talk_base::InitRandom(NULL, 0); + talk_base::InitializeSSL(); + } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); } talk_base::NATSocketServer* nat() { return nss_.get(); } @@ -478,13 +491,14 @@ class P2PTransportChannelTestBase : public testing::Test, // i.e. when don't match its remote type is either local or stun. // TODO(ronghuawu): Refine the test criteria. // https://code.google.com/p/webrtc/issues/detail?id=1953 - if (expected.remote_type2 != RemoteCandidate(ep2_ch1())->type()) + if (expected.remote_type2 != RemoteCandidate(ep2_ch1())->type()) { EXPECT_TRUE(expected.remote_type2 == cricket::LOCAL_PORT_TYPE || expected.remote_type2 == cricket::STUN_PORT_TYPE); EXPECT_TRUE( RemoteCandidate(ep2_ch1())->type() == cricket::LOCAL_PORT_TYPE || RemoteCandidate(ep2_ch1())->type() == cricket::STUN_PORT_TYPE || RemoteCandidate(ep2_ch1())->type() == cricket::PRFLX_PORT_TYPE); + } } converge_time = talk_base::TimeSince(converge_start); @@ -588,12 +602,55 @@ class P2PTransportChannelTestBase : public testing::Test, TestSendRecv(1); } + void TestHybridConnectivity(cricket::IceProtocolType proto) { + AddAddress(0, kPublicAddrs[0]); + AddAddress(1, kPublicAddrs[1]); + + SetAllocationStepDelay(0, kMinimumStepDelay); + SetAllocationStepDelay(1, kMinimumStepDelay); + + SetIceRole(0, cricket::ICEROLE_CONTROLLING); + SetIceProtocol(0, cricket::ICEPROTO_HYBRID); + SetIceTiebreaker(0, kTiebreaker1); + SetIceRole(1, cricket::ICEROLE_CONTROLLED); + SetIceProtocol(1, proto); + SetIceTiebreaker(1, kTiebreaker2); + + CreateChannels(1); + // When channel is in hybrid and it's controlling agent, channel will + // receive ping request from the remote. Hence connection is readable. + // Since channel is in hybrid, it will not send any pings, so no writable + // connection. Since channel2 is in controlled state, it will not have + // any connections which are readable or writable, as it didn't received + // pings (or none) with USE-CANDIDATE attribute. + EXPECT_TRUE_WAIT(ep1_ch1()->readable(), 1000); + + // Set real protocol type. + ep1_ch1()->SetIceProtocolType(proto); + + // Channel should able to send ping requests and connections become writable + // in both directions. + EXPECT_TRUE_WAIT(ep1_ch1()->readable() && ep1_ch1()->writable() && + ep2_ch1()->readable() && ep2_ch1()->writable(), + 1000); + EXPECT_TRUE( + ep1_ch1()->best_connection() && ep2_ch1()->best_connection() && + LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) && + RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1])); + + TestSendRecv(1); + DestroyChannels(); + } + void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) { channel->OnSignalingReady(); } // We pass the candidates directly to the other side. void OnCandidate(cricket::TransportChannelImpl* ch, const cricket::Candidate& c) { + if (force_relay_ && c.type() != cricket::RELAY_PORT_TYPE) + return; + main_->PostDelayed(GetEndpoint(ch)->signaling_delay_, this, 0, new CandidateData(ch, c)); } @@ -627,7 +684,8 @@ class P2PTransportChannelTestBase : public testing::Test, } int SendData(cricket::TransportChannel* channel, const char* data, size_t len) { - return channel->SendPacket(data, len, talk_base::DSCP_NO_CHANGE, 0); + talk_base::PacketOptions options; + return channel->SendPacket(data, len, options, 0); } bool CheckDataOnChannel(cricket::TransportChannel* channel, const char* data, int len) { @@ -673,6 +731,10 @@ class P2PTransportChannelTestBase : public testing::Test, clear_remote_candidates_ufrag_pwd_ = clear; } + void set_force_relay(bool relay) { + force_relay_ = relay; + } + private: talk_base::Thread* main_; talk_base::scoped_ptr<talk_base::PhysicalSocketServer> pss_; @@ -681,12 +743,14 @@ class P2PTransportChannelTestBase : public testing::Test, talk_base::scoped_ptr<talk_base::FirewallSocketServer> ss_; talk_base::SocketServerScope ss_scope_; cricket::TestStunServer stun_server_; + cricket::TestTurnServer turn_server_; cricket::TestRelayServer relay_server_; talk_base::SocksProxyServer socks_server1_; talk_base::SocksProxyServer socks_server2_; Endpoint ep1_; Endpoint ep2_; bool clear_remote_candidates_ufrag_pwd_; + bool force_relay_; }; // The tests have only a few outcomes, which we predefine. @@ -739,6 +803,35 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase { int allocator_flags1, int allocator_flags2, int delay1, int delay2, cricket::IceProtocolType type) { + // Ideally we want to use TURN server for both GICE and ICE, but in case + // of GICE, TURN server usage is not producing results reliabally. + // TODO(mallinath): Remove Relay and use TURN server for all tests. + GetEndpoint(0)->allocator_.reset( + new cricket::BasicPortAllocator(&(GetEndpoint(0)->network_manager_), + kStunAddr, talk_base::SocketAddress(), talk_base::SocketAddress(), + talk_base::SocketAddress())); + GetEndpoint(1)->allocator_.reset( + new cricket::BasicPortAllocator(&(GetEndpoint(1)->network_manager_), + kStunAddr, talk_base::SocketAddress(), talk_base::SocketAddress(), + talk_base::SocketAddress())); + + cricket::RelayServerConfig relay_server(cricket::RELAY_GTURN); + if (type == cricket::ICEPROTO_RFC5245) { + relay_server.type = cricket::RELAY_TURN; + relay_server.credentials = kRelayCredentials; + relay_server.ports.push_back(cricket::ProtocolAddress( + kTurnUdpIntAddr, cricket::PROTO_UDP, false)); + } else { + relay_server.ports.push_back(cricket::ProtocolAddress( + kRelayUdpIntAddr, cricket::PROTO_UDP, false)); + relay_server.ports.push_back(cricket::ProtocolAddress( + kRelayTcpIntAddr, cricket::PROTO_TCP, false)); + relay_server.ports.push_back(cricket::ProtocolAddress( + kRelaySslTcpIntAddr, cricket::PROTO_SSLTCP, false)); + } + GetEndpoint(0)->allocator_->AddRelay(relay_server); + GetEndpoint(1)->allocator_->AddRelay(relay_server); + ConfigureEndpoint(0, config1); SetIceProtocol(0, type); SetAllocatorFlags(0, allocator_flags1); @@ -1035,7 +1128,7 @@ const P2PTransportChannelTest::Result* P2P_TEST(x, OPEN) \ FLAKY_P2P_TEST(x, NAT_FULL_CONE) \ FLAKY_P2P_TEST(x, NAT_ADDR_RESTRICTED) \ - P2P_TEST(x, NAT_PORT_RESTRICTED) \ + FLAKY_P2P_TEST(x, NAT_PORT_RESTRICTED) \ P2P_TEST(x, NAT_SYMMETRIC) \ FLAKY_P2P_TEST(x, NAT_DOUBLE_CONE) \ P2P_TEST(x, NAT_SYMMETRIC_THEN_CONE) \ @@ -1082,6 +1175,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeAsIce) { cricket::ICEPROTO_RFC5245); CreateChannels(1); TestHandleIceUfragPasswordChanged(); + DestroyChannels(); } // Test that we restart candidate allocation when local ufrag&pwd changed. @@ -1097,6 +1191,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeBundleAsIce) { CreateChannels(2); TestHandleIceUfragPasswordChanged(); + DestroyChannels(); } // Test that we restart candidate allocation when local ufrag&pwd changed. @@ -1109,6 +1204,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeAsGice) { cricket::ICEPROTO_GOOGLE); CreateChannels(1); TestHandleIceUfragPasswordChanged(); + DestroyChannels(); } // Test that ICE restart works when bundle is enabled. @@ -1124,6 +1220,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeBundleAsGice) { CreateChannels(2); TestHandleIceUfragPasswordChanged(); + DestroyChannels(); } // Test the operation of GetStats. @@ -1389,6 +1486,19 @@ TEST_F(P2PTransportChannelTest, TestIceConfigWillPassDownToPort) { ep2_ch1()->best_connection()); TestSendRecv(1); + DestroyChannels(); +} + +// This test verifies channel can handle ice messages when channel is in +// hybrid mode. +TEST_F(P2PTransportChannelTest, TestConnectivityBetweenHybridandIce) { + TestHybridConnectivity(cricket::ICEPROTO_RFC5245); +} + +// This test verifies channel can handle Gice messages when channel is in +// hybrid mode. +TEST_F(P2PTransportChannelTest, TestConnectivityBetweenHybridandGice) { + TestHybridConnectivity(cricket::ICEPROTO_GOOGLE); } // Verify that we can set DSCP value and retrieve properly from P2PTC. @@ -1420,7 +1530,8 @@ TEST_F(P2PTransportChannelTest, TestDefaultDscpValue) { } // Verify IPv6 connection is preferred over IPv4. -TEST_F(P2PTransportChannelTest, TestIPv6Connections) { +// Flaky: https://code.google.com/p/webrtc/issues/detail?id=3317 +TEST_F(P2PTransportChannelTest, DISABLED_TestIPv6Connections) { AddAddress(0, kIPv6PublicAddrs[0]); AddAddress(0, kPublicAddrs[0]); AddAddress(1, kIPv6PublicAddrs[1]); @@ -1447,6 +1558,42 @@ TEST_F(P2PTransportChannelTest, TestIPv6Connections) { DestroyChannels(); } +// Testing forceful TURN connections. +TEST_F(P2PTransportChannelTest, TestForceTurn) { + ConfigureEndpoints(NAT_PORT_RESTRICTED, NAT_SYMMETRIC, + kDefaultPortAllocatorFlags | + cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | + cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG, + kDefaultPortAllocatorFlags | + cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | + cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG, + kDefaultStepDelay, kDefaultStepDelay, + cricket::ICEPROTO_RFC5245); + set_force_relay(true); + + SetAllocationStepDelay(0, kMinimumStepDelay); + SetAllocationStepDelay(1, kMinimumStepDelay); + + CreateChannels(1); + + EXPECT_TRUE_WAIT(ep1_ch1()->readable() && + ep1_ch1()->writable() && + ep2_ch1()->readable() && + ep2_ch1()->writable(), + 1000); + + EXPECT_TRUE(ep1_ch1()->best_connection() && + ep2_ch1()->best_connection()); + + EXPECT_EQ("relay", RemoteCandidate(ep1_ch1())->type()); + EXPECT_EQ("relay", LocalCandidate(ep1_ch1())->type()); + EXPECT_EQ("relay", RemoteCandidate(ep2_ch1())->type()); + EXPECT_EQ("relay", LocalCandidate(ep2_ch1())->type()); + + TestSendRecv(1); + DestroyChannels(); +} + // Test what happens when we have 2 users behind the same NAT. This can lead // to interesting behavior because the STUN server will only give out the // address of the outermost NAT. @@ -1498,8 +1645,11 @@ TEST_F(P2PTransportChannelMultihomedTest, DISABLED_TestBasic) { // Test that we can quickly switch links if an interface goes down. TEST_F(P2PTransportChannelMultihomedTest, TestFailover) { AddAddress(0, kPublicAddrs[0]); - AddAddress(1, kPublicAddrs[1]); + // Adding alternate address will make sure |kPublicAddrs| has the higher + // priority than others. This is due to FakeNetwork::AddInterface method. AddAddress(1, kAlternateAddrs[1]); + AddAddress(1, kPublicAddrs[1]); + // Use only local ports for simplicity. SetAllocatorFlags(0, kOnlyLocalPorts); SetAllocatorFlags(1, kOnlyLocalPorts); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/port.cc b/chromium/third_party/libjingle/source/talk/p2p/base/port.cc index 24ef4271fb6..ad692cec1be 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/port.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/port.cc @@ -176,13 +176,12 @@ Port::Port(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, generation_(0), ice_username_fragment_(username_fragment), password_(password), - lifetime_(LT_PRESTART), + timeout_delay_(kPortTimeoutDelay), enable_port_packets_(false), - ice_protocol_(ICEPROTO_GOOGLE), + ice_protocol_(ICEPROTO_HYBRID), ice_role_(ICEROLE_UNKNOWN), tiebreaker_(0), - shared_socket_(true), - default_dscp_(talk_base::DSCP_NO_CHANGE) { + shared_socket_(true) { Construct(); } @@ -203,13 +202,12 @@ Port::Port(talk_base::Thread* thread, const std::string& type, generation_(0), ice_username_fragment_(username_fragment), password_(password), - lifetime_(LT_PRESTART), + timeout_delay_(kPortTimeoutDelay), enable_port_packets_(false), - ice_protocol_(ICEPROTO_GOOGLE), + ice_protocol_(ICEPROTO_HYBRID), ice_role_(ICEROLE_UNKNOWN), tiebreaker_(0), - shared_socket_(false), - default_dscp_(talk_base::DSCP_NO_CHANGE) { + shared_socket_(false) { ASSERT(factory_ != NULL); Construct(); } @@ -250,6 +248,7 @@ Connection* Port::GetConnection(const talk_base::SocketAddress& remote_addr) { void Port::AddAddress(const talk_base::SocketAddress& address, const talk_base::SocketAddress& base_address, + const talk_base::SocketAddress& related_address, const std::string& protocol, const std::string& type, uint32 type_preference, @@ -260,12 +259,12 @@ void Port::AddAddress(const talk_base::SocketAddress& address, c.set_type(type); c.set_protocol(protocol); c.set_address(address); - c.set_priority(c.GetPriority(type_preference)); + c.set_priority(c.GetPriority(type_preference, network_->preference())); c.set_username(username_fragment()); c.set_password(password_); c.set_network_name(network_->name()); c.set_generation(generation_); - c.set_related_address(related_address_); + c.set_related_address(related_address); c.set_foundation(ComputeFoundation(type, protocol, base_address)); candidates_.push_back(c); SignalCandidateReady(this, c); @@ -341,6 +340,10 @@ bool Port::IsGoogleIce() const { return (ice_protocol_ == ICEPROTO_GOOGLE); } +bool Port::IsHybridIce() const { + return (ice_protocol_ == ICEPROTO_HYBRID); +} + bool Port::GetStunMessage(const char* data, size_t size, const talk_base::SocketAddress& addr, IceMessage** out_msg, std::string* out_username) { @@ -382,7 +385,9 @@ bool Port::GetStunMessage(const char* data, size_t size, // If the username is bad or unknown, fail with a 401 Unauthorized. std::string local_ufrag; std::string remote_ufrag; - if (!ParseStunUsername(stun_msg.get(), &local_ufrag, &remote_ufrag) || + IceProtocolType remote_protocol_type; + if (!ParseStunUsername(stun_msg.get(), &local_ufrag, &remote_ufrag, + &remote_protocol_type) || local_ufrag != username_fragment()) { LOG_J(LS_ERROR, this) << "Received STUN request with bad local username " << local_ufrag << " from " @@ -392,6 +397,15 @@ bool Port::GetStunMessage(const char* data, size_t size, return true; } + // Port is initialized to GOOGLE-ICE protocol type. If pings from remote + // are received before the signal message, protocol type may be different. + // Based on the STUN username, we can determine what's the remote protocol. + // This also enables us to send the response back using the same protocol + // as the request. + if (IsHybridIce()) { + SetIceProtocolType(remote_protocol_type); + } + // If ICE, and the MESSAGE-INTEGRITY is bad, fail with a 401 Unauthorized if (IsStandardIce() && !stun_msg->ValidateMessageIntegrity(data, size, password_)) { @@ -453,7 +467,8 @@ bool Port::IsCompatibleAddress(const talk_base::SocketAddress& addr) { bool Port::ParseStunUsername(const StunMessage* stun_msg, std::string* local_ufrag, - std::string* remote_ufrag) const { + std::string* remote_ufrag, + IceProtocolType* remote_protocol_type) const { // The packet must include a username that either begins or ends with our // fragment. It should begin with our fragment if it is a request and it // should end with our fragment if it is a response. @@ -465,8 +480,16 @@ bool Port::ParseStunUsername(const StunMessage* stun_msg, return false; const std::string username_attr_str = username_attr->GetString(); - if (IsStandardIce()) { - size_t colon_pos = username_attr_str.find(":"); + size_t colon_pos = username_attr_str.find(":"); + // If we are in hybrid mode set the appropriate ice protocol type based on + // the username argument style. + if (IsHybridIce()) { + *remote_protocol_type = (colon_pos != std::string::npos) ? + ICEPROTO_RFC5245 : ICEPROTO_GOOGLE; + } else { + *remote_protocol_type = ice_protocol_; + } + if (*remote_protocol_type == ICEPROTO_RFC5245) { if (colon_pos != std::string::npos) { // RFRAG:LFRAG *local_ufrag = username_attr_str.substr(0, colon_pos); *remote_ufrag = username_attr_str.substr( @@ -474,7 +497,7 @@ bool Port::ParseStunUsername(const StunMessage* stun_msg, } else { return false; } - } else if (IsGoogleIce()) { + } else if (*remote_protocol_type == ICEPROTO_GOOGLE) { int remote_frag_len = static_cast<int>(username_attr_str.size()); remote_frag_len -= static_cast<int>(username_fragment().size()); if (remote_frag_len < 0) @@ -608,7 +631,8 @@ void Port::SendBindingResponse(StunMessage* request, // Send the response message. talk_base::ByteBuffer buf; response.Write(&buf); - if (SendTo(buf.Data(), buf.Length(), addr, DefaultDscpValue(), false) < 0) { + talk_base::PacketOptions options(DefaultDscpValue()); + if (SendTo(buf.Data(), buf.Length(), addr, options, false) < 0) { LOG_J(LS_ERROR, this) << "Failed to send STUN ping response to " << addr.ToSensitiveString(); } @@ -662,15 +686,14 @@ void Port::SendBindingErrorResponse(StunMessage* request, // Send the response message. talk_base::ByteBuffer buf; response.Write(&buf); - SendTo(buf.Data(), buf.Length(), addr, DefaultDscpValue(), false); + talk_base::PacketOptions options(DefaultDscpValue()); + SendTo(buf.Data(), buf.Length(), addr, options, false); LOG_J(LS_INFO, this) << "Sending STUN binding error: reason=" << reason << " to " << addr.ToSensitiveString(); } void Port::OnMessage(talk_base::Message *pmsg) { ASSERT(pmsg->message_id == MSG_CHECKTIMEOUT); - ASSERT(lifetime_ == LT_PRETIMEOUT); - lifetime_ = LT_POSTTIMEOUT; CheckTimeout(); } @@ -686,24 +709,18 @@ void Port::EnablePortPackets() { enable_port_packets_ = true; } -void Port::Start() { - // The port sticks around for a minimum lifetime, after which - // we destroy it when it drops to zero connections. - if (lifetime_ == LT_PRESTART) { - lifetime_ = LT_PRETIMEOUT; - thread_->PostDelayed(kPortTimeoutDelay, this, MSG_CHECKTIMEOUT); - } else { - LOG_J(LS_WARNING, this) << "Port restart attempted"; - } -} - void Port::OnConnectionDestroyed(Connection* conn) { AddressMap::iterator iter = connections_.find(conn->remote_candidate().address()); ASSERT(iter != connections_.end()); connections_.erase(iter); - CheckTimeout(); + // On the controlled side, ports time out, but only after all connections + // fail. Note: If a new connection is added after this message is posted, + // but it fails and is removed before kPortTimeoutDelay, then this message + // will still cause the Port to be destroyed. + if (ice_role_ == ICEROLE_CONTROLLED) + thread_->PostDelayed(timeout_delay_, this, MSG_CHECKTIMEOUT); } void Port::Destroy() { @@ -714,17 +731,17 @@ void Port::Destroy() { } void Port::CheckTimeout() { + ASSERT(ice_role_ == ICEROLE_CONTROLLED); // If this port has no connections, then there's no reason to keep it around. // When the connections time out (both read and write), they will delete // themselves, so if we have any connections, they are either readable or // writable (or still connecting). - if ((lifetime_ == LT_POSTTIMEOUT) && connections_.empty()) { + if (connections_.empty()) Destroy(); - } } const std::string Port::username_fragment() const { - if (IsGoogleIce() && + if (!IsStandardIce() && component_ == ICE_CANDIDATE_COMPONENT_RTCP) { // In GICE mode, we should adjust username fragment for rtcp component. return GetRtcpUfragFromRtpUfrag(ice_username_fragment_); @@ -918,8 +935,9 @@ void Connection::set_use_candidate_attr(bool enable) { void Connection::OnSendStunPacket(const void* data, size_t size, StunRequest* req) { + talk_base::PacketOptions options(port_->DefaultDscpValue()); if (port_->SendTo(data, size, remote_candidate_.address(), - port_->DefaultDscpValue(), false) < 0) { + options, false) < 0) { LOG_J(LS_WARNING, this) << "Failed to send STUN ping " << req->id(); } } @@ -1005,7 +1023,7 @@ void Connection::OnReadPacket( // id's match. case STUN_BINDING_RESPONSE: case STUN_BINDING_ERROR_RESPONSE: - if (port_->IceProtocol() == ICEPROTO_GOOGLE || + if (port_->IsGoogleIce() || msg->ValidateMessageIntegrity( data, size, remote_candidate().password())) { requests_.CheckResponse(msg.get()); @@ -1180,15 +1198,14 @@ std::string Connection::ToString() const { << ":" << local.type() << ":" << local.protocol() << ":" << local.address().ToSensitiveString() << "->" << remote.id() << ":" << remote.component() - << ":" << remote.generation() + << ":" << remote.preference() << ":" << remote.type() << ":" - << remote.protocol() << ":" << remote.address().ToSensitiveString() - << "|" + << remote.protocol() << ":" << remote.address().ToSensitiveString() << "|" << CONNECT_STATE_ABBREV[connected()] << READ_STATE_ABBREV[read_state()] << WRITE_STATE_ABBREV[write_state()] - << ICESTATE[state()] - << "|"; + << ICESTATE[state()] << "|" + << priority() << "|"; if (rtt_ < DEFAULT_RTT) { ss << rtt_ << "]"; } else { @@ -1394,12 +1411,13 @@ ProxyConnection::ProxyConnection(Port* port, size_t index, } int ProxyConnection::Send(const void* data, size_t size, - talk_base::DiffServCodePoint dscp) { + const talk_base::PacketOptions& options) { if (write_state_ == STATE_WRITE_INIT || write_state_ == STATE_WRITE_TIMEOUT) { error_ = EWOULDBLOCK; return SOCKET_ERROR; } - int sent = port_->SendTo(data, size, remote_candidate_.address(), dscp, true); + int sent = port_->SendTo(data, size, remote_candidate_.address(), + options, true); if (sent <= 0) { ASSERT(sent < 0); error_ = port_->GetError(); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/port.h b/chromium/third_party/libjingle/source/talk/p2p/base/port.h index 9ea3f0c37aa..6e5c383b79a 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/port.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/port.h @@ -172,13 +172,6 @@ class Port : public PortInterface, public talk_base::MessageHandler, send_retransmit_count_attribute_ = enable; } - const talk_base::SocketAddress& related_address() const { - return related_address_; - } - void set_related_address(const talk_base::SocketAddress& address) { - related_address_ = address; - } - // Identifies the generation that this port was created in. uint32 generation() { return generation_; } void set_generation(uint32 generation) { generation_ = generation; } @@ -262,10 +255,6 @@ class Port : public PortInterface, public talk_base::MessageHandler, virtual void EnablePortPackets(); - // Indicates to the port that its official use has now begun. This will - // start the timer that checks to see if the port is being used. - void Start(); - // Called if the port has no connections and is no longer useful. void Destroy(); @@ -277,11 +266,15 @@ class Port : public PortInterface, public talk_base::MessageHandler, int min_port() { return min_port_; } int max_port() { return max_port_; } + // Timeout shortening function to speed up unit tests. + void set_timeout_delay(int delay) { timeout_delay_ = delay; } + // This method will return local and remote username fragements from the // stun username attribute if present. bool ParseStunUsername(const StunMessage* stun_msg, std::string* local_username, - std::string* remote_username) const; + std::string* remote_username, + IceProtocolType* remote_protocol_type) const; void CreateStunUsername(const std::string& remote_username, std::string* stun_username_attr_str) const; @@ -302,10 +295,8 @@ class Port : public PortInterface, public talk_base::MessageHandler, // Returns if Google ICE protocol is used. bool IsGoogleIce() const; - // Returns default DSCP value. - talk_base::DiffServCodePoint DefaultDscpValue() const { - return default_dscp_; - } + // Returns if Hybrid ICE protocol is used. + bool IsHybridIce() const; protected: enum { @@ -317,6 +308,7 @@ class Port : public PortInterface, public talk_base::MessageHandler, // Fills in the local address of the port. void AddAddress(const talk_base::SocketAddress& address, const talk_base::SocketAddress& base_address, + const talk_base::SocketAddress& related_address, const std::string& protocol, const std::string& type, uint32 type_preference, bool final); @@ -342,9 +334,10 @@ class Port : public PortInterface, public talk_base::MessageHandler, // Checks if the address in addr is compatible with the port's ip. bool IsCompatibleAddress(const talk_base::SocketAddress& addr); - // Default DSCP value for this port. Set by TransportChannel. - void SetDefaultDscpValue(talk_base::DiffServCodePoint dscp) { - default_dscp_ = dscp; + // Returns default DSCP value. + talk_base::DiffServCodePoint DefaultDscpValue() const { + // No change from what MediaChannel set. + return talk_base::DSCP_NO_CHANGE; } private: @@ -366,7 +359,6 @@ class Port : public PortInterface, public talk_base::MessageHandler, std::string content_name_; int component_; uint32 generation_; - talk_base::SocketAddress related_address_; // In order to establish a connection to this Port (so that real data can be // sent through), the other side must send us a STUN binding request that is // authenticated with this username_fragment and password. @@ -379,15 +371,12 @@ class Port : public PortInterface, public talk_base::MessageHandler, std::string password_; std::vector<Candidate> candidates_; AddressMap connections_; - enum Lifetime { LT_PRESTART, LT_PRETIMEOUT, LT_POSTTIMEOUT } lifetime_; + int timeout_delay_; bool enable_port_packets_; IceProtocolType ice_protocol_; IceRole ice_role_; uint64 tiebreaker_; bool shared_socket_; - // DSCP value for ICE/STUN messages. Set by the P2PTransportChannel after - // port becomes ready. - talk_base::DiffServCodePoint default_dscp_; // Information to use when going through a proxy. std::string user_agent_; talk_base::ProxyInfo proxy_; @@ -463,7 +452,7 @@ class Connection : public talk_base::MessageHandler, // the interface of AsyncPacketSocket, which may use UDP or TCP under the // covers. virtual int Send(const void* data, size_t size, - talk_base::DiffServCodePoint dscp) = 0; + const talk_base::PacketOptions& options) = 0; // Error if Send() returns < 0 virtual int GetError() = 0; @@ -595,7 +584,7 @@ class ProxyConnection : public Connection { ProxyConnection(Port* port, size_t index, const Candidate& candidate); virtual int Send(const void* data, size_t size, - talk_base::DiffServCodePoint dscp); + const talk_base::PacketOptions& options); virtual int GetError() { return error_; } private: diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/port_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/port_unittest.cc index 1122d8aea01..fc6d48c9a5d 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/port_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/port_unittest.cc @@ -34,6 +34,7 @@ #include "talk/base/physicalsocketserver.h" #include "talk/base/scoped_ptr.h" #include "talk/base/socketaddress.h" +#include "talk/base/ssladapter.h" #include "talk/base/stringutils.h" #include "talk/base/thread.h" #include "talk/base/virtualsocketserver.h" @@ -145,19 +146,21 @@ class TestPort : public Port { virtual void PrepareAddress() { talk_base::SocketAddress addr(ip(), min_port()); - AddAddress(addr, addr, "udp", Type(), ICE_TYPE_PREFERENCE_HOST, true); + AddAddress(addr, addr, talk_base::SocketAddress(), "udp", Type(), + ICE_TYPE_PREFERENCE_HOST, true); } // Exposed for testing candidate building. void AddCandidateAddress(const talk_base::SocketAddress& addr) { - AddAddress(addr, addr, "udp", Type(), type_preference_, false); + AddAddress(addr, addr, talk_base::SocketAddress(), "udp", Type(), + type_preference_, false); } void AddCandidateAddress(const talk_base::SocketAddress& addr, const talk_base::SocketAddress& base_address, const std::string& type, int type_preference, bool final) { - AddAddress(addr, base_address, "udp", type, + AddAddress(addr, base_address, talk_base::SocketAddress(), "udp", type, type_preference, final); } @@ -172,7 +175,7 @@ class TestPort : public Port { } virtual int SendTo( const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, bool payload) { + const talk_base::PacketOptions& options, bool payload) { if (!payload) { IceMessage* msg = new IceMessage; ByteBuffer* buf = new ByteBuffer(static_cast<const char*>(data), size); @@ -213,12 +216,14 @@ class TestPort : public Port { class TestChannel : public sigslot::has_slots<> { public: + // Takes ownership of |p1| (but not |p2|). TestChannel(Port* p1, Port* p2) : ice_mode_(ICEMODE_FULL), src_(p1), dst_(p2), complete_count_(0), conn_(NULL), remote_request_(), nominated_(false) { src_->SignalPortComplete.connect( this, &TestChannel::OnPortComplete); src_->SignalUnknownAddress.connect(this, &TestChannel::OnUnknownAddress); + src_->SignalDestroyed.connect(this, &TestChannel::OnSrcPortDestroyed); } int complete_count() { return complete_count_; } @@ -305,6 +310,11 @@ class TestChannel : public sigslot::has_slots<> { conn_ = NULL; } + void OnSrcPortDestroyed(PortInterface* port) { + Port* destroyed_src = src_.release(); + ASSERT_EQ(destroyed_src, port); + } + bool nominated() const { return nominated_; } private: @@ -341,16 +351,21 @@ class PortTest : public testing::Test, public sigslot::has_slots<> { username_(talk_base::CreateRandomString(ICE_UFRAG_LENGTH)), password_(talk_base::CreateRandomString(ICE_PWD_LENGTH)), ice_protocol_(cricket::ICEPROTO_GOOGLE), - role_conflict_(false) { + role_conflict_(false), + destroyed_(false) { network_.AddIP(talk_base::IPAddress(INADDR_ANY)); } protected: static void SetUpTestCase() { - // Ensure the RNG is inited. - talk_base::InitRandom(NULL, 0); + talk_base::InitializeSSL(); + } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); } + void TestLocalToLocal() { Port* port1 = CreateUdpPort(kLocalAddr1); Port* port2 = CreateUdpPort(kLocalAddr2); @@ -455,10 +470,17 @@ class PortTest : public testing::Test, public sigslot::has_slots<> { TurnPort* CreateTurnPort(const SocketAddress& addr, PacketSocketFactory* socket_factory, ProtocolType int_proto, ProtocolType ext_proto) { + return CreateTurnPort(addr, socket_factory, + int_proto, ext_proto, kTurnUdpIntAddr); + } + TurnPort* CreateTurnPort(const SocketAddress& addr, + PacketSocketFactory* socket_factory, + ProtocolType int_proto, ProtocolType ext_proto, + const talk_base::SocketAddress& server_addr) { TurnPort* port = TurnPort::Create(main_, socket_factory, &network_, addr.ipaddr(), 0, 0, username_, password_, ProtocolAddress( - kTurnUdpIntAddr, PROTO_UDP), + server_addr, PROTO_UDP), kRelayCredentials); port->SetIceProtocolType(ice_protocol_); return port; @@ -513,12 +535,17 @@ class PortTest : public testing::Test, public sigslot::has_slots<> { void TestCrossFamilyPorts(int type); - // this does all the work + // This does all the work and then deletes |port1| and |port2|. void TestConnectivity(const char* name1, Port* port1, const char* name2, Port* port2, bool accept, bool same_addr1, bool same_addr2, bool possible); + // This connects and disconnects the provided channels in the same sequence as + // TestConnectivity with all options set to |true|. It does not delete either + // channel. + void ConnectAndDisconnectChannels(TestChannel* ch1, TestChannel* ch2); + void SetIceProtocolType(cricket::IceProtocolType protocol) { ice_protocol_ = protocol; } @@ -562,6 +589,15 @@ class PortTest : public testing::Test, public sigslot::has_slots<> { } bool role_conflict() const { return role_conflict_; } + void ConnectToSignalDestroyed(PortInterface* port) { + port->SignalDestroyed.connect(this, &PortTest::OnDestroyed); + } + + void OnDestroyed(PortInterface* port) { + destroyed_ = true; + } + bool destroyed() const { return destroyed_; } + talk_base::BasicPacketSocketFactory* nat_socket_factory1() { return &nat_socket_factory1_; } @@ -586,6 +622,7 @@ class PortTest : public testing::Test, public sigslot::has_slots<> { std::string password_; cricket::IceProtocolType ice_protocol_; bool role_conflict_; + bool destroyed_; }; void PortTest::TestConnectivity(const char* name1, Port* port1, @@ -596,7 +633,7 @@ void PortTest::TestConnectivity(const char* name1, Port* port1, port1->set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT); port2->set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT); - // Set up channels. + // Set up channels and ensure both ports will be deleted. TestChannel ch1(port1, port2); TestChannel ch2(port2, port1); EXPECT_EQ(0, ch1.complete_count()); @@ -719,6 +756,29 @@ void PortTest::TestConnectivity(const char* name1, Port* port1, EXPECT_TRUE_WAIT(ch2.conn() == NULL, kTimeout); } +void PortTest::ConnectAndDisconnectChannels(TestChannel* ch1, + TestChannel* ch2) { + // Acquire addresses. + ch1->Start(); + ch2->Start(); + + // Send a ping from src to dst. + ch1->CreateConnection(); + EXPECT_TRUE_WAIT(ch1->conn()->connected(), kTimeout); // for TCP connect + ch1->Ping(); + WAIT(!ch2->remote_address().IsNil(), kTimeout); + + // Send a ping from dst to src. + ch2->AcceptConnection(); + ch2->Ping(); + EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, ch2->conn()->write_state(), + kTimeout); + + // Destroy the connections. + ch1->Stop(); + ch2->Stop(); +} + class FakePacketSocketFactory : public talk_base::PacketSocketFactory { public: FakePacketSocketFactory() @@ -791,11 +851,11 @@ class FakeAsyncPacketSocket : public AsyncPacketSocket { // Send a packet. virtual int Send(const void *pv, size_t cb, - talk_base::DiffServCodePoint dscp) { + const talk_base::PacketOptions& options) { return static_cast<int>(cb); } virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr, - talk_base::DiffServCodePoint dscp) { + const talk_base::PacketOptions& options) { return static_cast<int>(cb); } virtual int Close() { @@ -835,7 +895,8 @@ TEST_F(PortTest, TestLocalToSymNat) { TestLocalToStun(NAT_SYMMETRIC); } -TEST_F(PortTest, TestLocalToTurn) { +// Flaky: https://code.google.com/p/webrtc/issues/detail?id=3316. +TEST_F(PortTest, DISABLED_TestLocalToTurn) { TestLocalToRelay(RELAY_TURN, PROTO_UDP); } @@ -1234,21 +1295,37 @@ TEST_F(PortTest, TestSkipCrossFamilyUdp) { // This test verifies DSCP value set through SetOption interface can be // get through DefaultDscpValue. TEST_F(PortTest, TestDefaultDscpValue) { + int dscp; talk_base::scoped_ptr<UDPPort> udpport(CreateUdpPort(kLocalAddr1)); - udpport->SetOption(talk_base::Socket::OPT_DSCP, talk_base::DSCP_CS6); - EXPECT_EQ(talk_base::DSCP_CS6, udpport->DefaultDscpValue()); + EXPECT_EQ(0, udpport->SetOption(talk_base::Socket::OPT_DSCP, + talk_base::DSCP_CS6)); + EXPECT_EQ(0, udpport->GetOption(talk_base::Socket::OPT_DSCP, &dscp)); talk_base::scoped_ptr<TCPPort> tcpport(CreateTcpPort(kLocalAddr1)); - tcpport->SetOption(talk_base::Socket::OPT_DSCP, talk_base::DSCP_AF31); - EXPECT_EQ(talk_base::DSCP_AF31, tcpport->DefaultDscpValue()); + EXPECT_EQ(0, tcpport->SetOption(talk_base::Socket::OPT_DSCP, + talk_base::DSCP_AF31)); + EXPECT_EQ(0, tcpport->GetOption(talk_base::Socket::OPT_DSCP, &dscp)); + EXPECT_EQ(talk_base::DSCP_AF31, dscp); talk_base::scoped_ptr<StunPort> stunport( CreateStunPort(kLocalAddr1, nat_socket_factory1())); - stunport->SetOption(talk_base::Socket::OPT_DSCP, talk_base::DSCP_AF41); - EXPECT_EQ(talk_base::DSCP_AF41, stunport->DefaultDscpValue()); - talk_base::scoped_ptr<TurnPort> turnport(CreateTurnPort( + EXPECT_EQ(0, stunport->SetOption(talk_base::Socket::OPT_DSCP, + talk_base::DSCP_AF41)); + EXPECT_EQ(0, stunport->GetOption(talk_base::Socket::OPT_DSCP, &dscp)); + EXPECT_EQ(talk_base::DSCP_AF41, dscp); + talk_base::scoped_ptr<TurnPort> turnport1(CreateTurnPort( kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP)); - turnport->SetOption(talk_base::Socket::OPT_DSCP, talk_base::DSCP_CS7); - EXPECT_EQ(talk_base::DSCP_CS7, turnport->DefaultDscpValue()); - // TODO(mallinath) - Test DSCP through GetOption. + // Socket is created in PrepareAddress. + turnport1->PrepareAddress(); + EXPECT_EQ(0, turnport1->SetOption(talk_base::Socket::OPT_DSCP, + talk_base::DSCP_CS7)); + EXPECT_EQ(0, turnport1->GetOption(talk_base::Socket::OPT_DSCP, &dscp)); + EXPECT_EQ(talk_base::DSCP_CS7, dscp); + // This will verify correct value returned without the socket. + talk_base::scoped_ptr<TurnPort> turnport2(CreateTurnPort( + kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP)); + EXPECT_EQ(0, turnport2->SetOption(talk_base::Socket::OPT_DSCP, + talk_base::DSCP_CS6)); + EXPECT_EQ(0, turnport2->GetOption(talk_base::Socket::OPT_DSCP, &dscp)); + EXPECT_EQ(talk_base::DSCP_CS6, dscp); } // Test sending STUN messages in GICE format. @@ -1619,6 +1696,81 @@ TEST_F(PortTest, TestHandleStunMessageAsIce) { out_msg->GetErrorCode()->reason()); } +// This test verifies port can handle ICE messages in Hybrid mode and switches +// ICEPROTO_RFC5245 mode after successfully handling the message. +TEST_F(PortTest, TestHandleStunMessageAsIceInHybridMode) { + // Our port will act as the "remote" port. + talk_base::scoped_ptr<TestPort> port( + CreateTestPort(kLocalAddr2, "rfrag", "rpass")); + port->SetIceProtocolType(ICEPROTO_HYBRID); + + talk_base::scoped_ptr<IceMessage> in_msg, out_msg; + talk_base::scoped_ptr<ByteBuffer> buf(new ByteBuffer()); + talk_base::SocketAddress addr(kLocalAddr1); + std::string username; + + // BINDING-REQUEST from local to remote with valid ICE username, + // MESSAGE-INTEGRITY, and FINGERPRINT. + in_msg.reset(CreateStunMessageWithUsername(STUN_BINDING_REQUEST, + "rfrag:lfrag")); + in_msg->AddMessageIntegrity("rpass"); + in_msg->AddFingerprint(); + WriteStunMessage(in_msg.get(), buf.get()); + EXPECT_TRUE(port->GetStunMessage(buf->Data(), buf->Length(), addr, + out_msg.accept(), &username)); + EXPECT_TRUE(out_msg.get() != NULL); + EXPECT_EQ("lfrag", username); + EXPECT_EQ(ICEPROTO_RFC5245, port->IceProtocol()); +} + +// This test verifies port can handle GICE messages in Hybrid mode and switches +// ICEPROTO_GOOGLE mode after successfully handling the message. +TEST_F(PortTest, TestHandleStunMessageAsGiceInHybridMode) { + // Our port will act as the "remote" port. + talk_base::scoped_ptr<TestPort> port( + CreateTestPort(kLocalAddr2, "rfrag", "rpass")); + port->SetIceProtocolType(ICEPROTO_HYBRID); + + talk_base::scoped_ptr<IceMessage> in_msg, out_msg; + talk_base::scoped_ptr<ByteBuffer> buf(new ByteBuffer()); + talk_base::SocketAddress addr(kLocalAddr1); + std::string username; + + // BINDING-REQUEST from local to remote with valid GICE username and no M-I. + in_msg.reset(CreateStunMessageWithUsername(STUN_BINDING_REQUEST, + "rfraglfrag")); + WriteStunMessage(in_msg.get(), buf.get()); + EXPECT_TRUE(port->GetStunMessage(buf->Data(), buf->Length(), addr, + out_msg.accept(), &username)); + EXPECT_TRUE(out_msg.get() != NULL); // Succeeds, since this is GICE. + EXPECT_EQ("lfrag", username); + EXPECT_EQ(ICEPROTO_GOOGLE, port->IceProtocol()); +} + +// Verify port is not switched out of RFC5245 mode if GICE message is received +// in that mode. +TEST_F(PortTest, TestHandleStunMessageAsGiceInIceMode) { + // Our port will act as the "remote" port. + talk_base::scoped_ptr<TestPort> port( + CreateTestPort(kLocalAddr2, "rfrag", "rpass")); + port->SetIceProtocolType(ICEPROTO_RFC5245); + + talk_base::scoped_ptr<IceMessage> in_msg, out_msg; + talk_base::scoped_ptr<ByteBuffer> buf(new ByteBuffer()); + talk_base::SocketAddress addr(kLocalAddr1); + std::string username; + + // BINDING-REQUEST from local to remote with valid GICE username and no M-I. + in_msg.reset(CreateStunMessageWithUsername(STUN_BINDING_REQUEST, + "rfraglfrag")); + WriteStunMessage(in_msg.get(), buf.get()); + // Should fail as there is no MI and fingerprint. + EXPECT_FALSE(port->GetStunMessage(buf->Data(), buf->Length(), addr, + out_msg.accept(), &username)); + EXPECT_EQ(ICEPROTO_RFC5245, port->IceProtocol()); +} + + // Tests handling of GICE binding requests with missing or incorrect usernames. TEST_F(PortTest, TestHandleStunMessageAsGiceBadUsername) { talk_base::scoped_ptr<TestPort> port( @@ -2024,16 +2176,35 @@ TEST_F(PortTest, TestCandidateFoundation) { EXPECT_NE(udpport2->Candidates()[0].foundation(), relayport->Candidates()[0].foundation()); // Verifying TURN candidate foundation. - talk_base::scoped_ptr<Port> turnport(CreateTurnPort( + talk_base::scoped_ptr<Port> turnport1(CreateTurnPort( kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP)); - turnport->PrepareAddress(); - ASSERT_EQ_WAIT(1U, turnport->Candidates().size(), kTimeout); + turnport1->PrepareAddress(); + ASSERT_EQ_WAIT(1U, turnport1->Candidates().size(), kTimeout); EXPECT_NE(udpport1->Candidates()[0].foundation(), - turnport->Candidates()[0].foundation()); + turnport1->Candidates()[0].foundation()); EXPECT_NE(udpport2->Candidates()[0].foundation(), - turnport->Candidates()[0].foundation()); + turnport1->Candidates()[0].foundation()); EXPECT_NE(stunport->Candidates()[0].foundation(), - turnport->Candidates()[0].foundation()); + turnport1->Candidates()[0].foundation()); + talk_base::scoped_ptr<Port> turnport2(CreateTurnPort( + kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP)); + turnport2->PrepareAddress(); + ASSERT_EQ_WAIT(1U, turnport2->Candidates().size(), kTimeout); + EXPECT_EQ(turnport1->Candidates()[0].foundation(), + turnport2->Candidates()[0].foundation()); + + // Running a second turn server, to get different base IP address. + SocketAddress kTurnUdpIntAddr2("99.99.98.4", STUN_SERVER_PORT); + SocketAddress kTurnUdpExtAddr2("99.99.98.5", 0); + TestTurnServer turn_server2( + talk_base::Thread::Current(), kTurnUdpIntAddr2, kTurnUdpExtAddr2); + talk_base::scoped_ptr<Port> turnport3(CreateTurnPort( + kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP, + kTurnUdpIntAddr2)); + turnport3->PrepareAddress(); + ASSERT_EQ_WAIT(1U, turnport3->Candidates().size(), kTimeout); + EXPECT_NE(turnport3->Candidates()[0].foundation(), + turnport2->Candidates()[0].foundation()); } // This test verifies the related addresses of different types of @@ -2155,16 +2326,15 @@ TEST_F(PortTest, TestWritableState) { // Data should be unsendable until the connection is accepted. char data[] = "abcd"; int data_size = ARRAY_SIZE(data); - EXPECT_EQ(SOCKET_ERROR, - ch1.conn()->Send(data, data_size, talk_base::DSCP_NO_CHANGE)); + talk_base::PacketOptions options; + EXPECT_EQ(SOCKET_ERROR, ch1.conn()->Send(data, data_size, options)); // Accept the connection to return the binding response, transition to // writable, and allow data to be sent. ch2.AcceptConnection(); EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, ch1.conn()->write_state(), kTimeout); - EXPECT_EQ(data_size, - ch1.conn()->Send(data, data_size, talk_base::DSCP_NO_CHANGE)); + EXPECT_EQ(data_size, ch1.conn()->Send(data, data_size, options)); // Ask the connection to update state as if enough time has passed to lose // full writability and 5 pings went unresponded to. We'll accomplish the @@ -2177,8 +2347,7 @@ TEST_F(PortTest, TestWritableState) { EXPECT_EQ(Connection::STATE_WRITE_UNRELIABLE, ch1.conn()->write_state()); // Data should be able to be sent in this state. - EXPECT_EQ(data_size, - ch1.conn()->Send(data, data_size, talk_base::DSCP_NO_CHANGE)); + EXPECT_EQ(data_size, ch1.conn()->Send(data, data_size, options)); // And now allow the other side to process the pings and send binding // responses. @@ -2195,8 +2364,7 @@ TEST_F(PortTest, TestWritableState) { EXPECT_EQ(Connection::STATE_WRITE_TIMEOUT, ch1.conn()->write_state()); // Now that the connection has completely timed out, data send should fail. - EXPECT_EQ(SOCKET_ERROR, - ch1.conn()->Send(data, data_size, talk_base::DSCP_NO_CHANGE)); + EXPECT_EQ(SOCKET_ERROR, ch1.conn()->Send(data, data_size, options)); ch1.Stop(); ch2.Stop(); @@ -2292,3 +2460,59 @@ TEST_F(PortTest, TestIceLiteConnectivity) { EXPECT_TRUE(msg->GetByteString(STUN_ATTR_USE_CANDIDATE) != NULL); ch1.Stop(); } + +// This test case verifies that the CONTROLLING port does not time out. +TEST_F(PortTest, TestControllingNoTimeout) { + SetIceProtocolType(cricket::ICEPROTO_RFC5245); + UDPPort* port1 = CreateUdpPort(kLocalAddr1); + ConnectToSignalDestroyed(port1); + port1->set_timeout_delay(10); // milliseconds + port1->SetIceRole(cricket::ICEROLE_CONTROLLING); + port1->SetIceTiebreaker(kTiebreaker1); + + UDPPort* port2 = CreateUdpPort(kLocalAddr2); + port2->SetIceRole(cricket::ICEROLE_CONTROLLED); + port2->SetIceTiebreaker(kTiebreaker2); + + // Set up channels and ensure both ports will be deleted. + TestChannel ch1(port1, port2); + TestChannel ch2(port2, port1); + + // Simulate a connection that succeeds, and then is destroyed. + ConnectAndDisconnectChannels(&ch1, &ch2); + + // After the connection is destroyed, the port should not be destroyed. + talk_base::Thread::Current()->ProcessMessages(kTimeout); + EXPECT_FALSE(destroyed()); +} + +// This test case verifies that the CONTROLLED port does time out, but only +// after connectivity is lost. +TEST_F(PortTest, TestControlledTimeout) { + SetIceProtocolType(cricket::ICEPROTO_RFC5245); + UDPPort* port1 = CreateUdpPort(kLocalAddr1); + port1->SetIceRole(cricket::ICEROLE_CONTROLLING); + port1->SetIceTiebreaker(kTiebreaker1); + + UDPPort* port2 = CreateUdpPort(kLocalAddr2); + ConnectToSignalDestroyed(port2); + port2->set_timeout_delay(10); // milliseconds + port2->SetIceRole(cricket::ICEROLE_CONTROLLED); + port2->SetIceTiebreaker(kTiebreaker2); + + // The connection must not be destroyed before a connection is attempted. + EXPECT_FALSE(destroyed()); + + port1->set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT); + port2->set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT); + + // Set up channels and ensure both ports will be deleted. + TestChannel ch1(port1, port2); + TestChannel ch2(port2, port1); + + // Simulate a connection that succeeds, and then is destroyed. + ConnectAndDisconnectChannels(&ch1, &ch2); + + // The controlled port should be destroyed after 10 milliseconds. + EXPECT_TRUE_WAIT(destroyed(), kTimeout); +} diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/portallocator.h b/chromium/third_party/libjingle/source/talk/p2p/base/portallocator.h index 348102165f7..e2cb3fd2087 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/portallocator.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/portallocator.h @@ -54,7 +54,6 @@ const uint32 PORTALLOCATOR_ENABLE_IPV6 = 0x40; const uint32 PORTALLOCATOR_ENABLE_SHARED_UFRAG = 0x80; const uint32 PORTALLOCATOR_ENABLE_SHARED_SOCKET = 0x100; const uint32 PORTALLOCATOR_ENABLE_STUN_RETRANSMIT_ATTRIBUTE = 0x200; -const uint32 PORTALLOCATOR_USE_LARGE_SOCKET_SEND_BUFFERS = 0x400; const uint32 kDefaultPortAllocatorFlags = 0; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/portinterface.h b/chromium/third_party/libjingle/source/talk/p2p/base/portinterface.h index 6ea63466c9d..5ebf6539873 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/portinterface.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/portinterface.h @@ -30,13 +30,12 @@ #include <string> -#include "talk/base/dscp.h" #include "talk/base/socketaddress.h" #include "talk/p2p/base/transport.h" namespace talk_base { class Network; -class PacketSocketFactory; +struct PacketOptions; } namespace cricket { @@ -100,7 +99,7 @@ class PortInterface { // that of a connection or an address that has sent to us already. virtual int SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, bool payload) = 0; + const talk_base::PacketOptions& options, bool payload) = 0; // Indicates that we received a successful STUN binding request from an // address that doesn't correspond to any current connection. To turn this diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/portproxy.cc b/chromium/third_party/libjingle/source/talk/p2p/base/portproxy.cc index eae39f1612d..43bb747c964 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/portproxy.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/portproxy.cc @@ -97,10 +97,10 @@ Connection* PortProxy::CreateConnection(const Candidate& remote_candidate, int PortProxy::SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload) { ASSERT(impl_ != NULL); - return impl_->SendTo(data, size, addr, dscp, payload); + return impl_->SendTo(data, size, addr, options, payload); } int PortProxy::SetOption(talk_base::Socket::Option opt, diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/portproxy.h b/chromium/third_party/libjingle/source/talk/p2p/base/portproxy.h index da326646dcc..d138dc3614a 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/portproxy.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/portproxy.h @@ -69,7 +69,7 @@ class PortProxy : public PortInterface, public sigslot::has_slots<> { virtual int SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload); virtual int SetOption(talk_base::Socket::Option opt, int value); virtual int GetOption(talk_base::Socket::Option opt, int* value); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc b/chromium/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc index 56aa5b019a0..3925637a305 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc @@ -27,8 +27,9 @@ #include "talk/p2p/base/pseudotcp.h" -#include <cstdio> -#include <cstdlib> +#include <stdio.h> +#include <stdlib.h> + #include <set> #include "talk/base/basictypes.h" @@ -81,7 +82,6 @@ const uint32 MAX_PACKET = 65535; const uint32 MIN_PACKET = 296; const uint32 IP_HEADER_SIZE = 20; // (+ up to 40 bytes of options?) -const uint32 ICMP_HEADER_SIZE = 8; const uint32 UDP_HEADER_SIZE = 8; // TODO: Make JINGLE_HEADER_SIZE transparent to this code? const uint32 JINGLE_HEADER_SIZE = 64; // when relay framing is in use @@ -118,7 +118,6 @@ const uint32 DEFAULT_SND_BUF_SIZE = 90 * 1024; #define PSEUDO_KEEPALIVE 0 -const uint32 MAX_SEQ = 0xFFFFFFFF; const uint32 HEADER_SIZE = 24; const uint32 PACKET_OVERHEAD = HEADER_SIZE + UDP_HEADER_SIZE + IP_HEADER_SIZE + JINGLE_HEADER_SIZE; @@ -131,8 +130,6 @@ const uint8 FLAG_CTL = 0x02; const uint8 FLAG_RST = 0x04; const uint8 CTL_CONNECT = 0; -//const uint8 CTL_REDIRECT = 1; -const uint8 CTL_EXTRA = 255; // TCP options. const uint8 TCP_OPT_EOL = 0; // End of list. @@ -140,14 +137,6 @@ const uint8 TCP_OPT_NOOP = 1; // No-op. const uint8 TCP_OPT_MSS = 2; // Maximum segment size. const uint8 TCP_OPT_WND_SCALE = 3; // Window scale factor. -/* -const uint8 FLAG_FIN = 0x01; -const uint8 FLAG_SYN = 0x02; -const uint8 FLAG_ACK = 0x10; -*/ - -const uint32 CTRL_BOUND = 0x80000000; - const long DEFAULT_TIMEOUT = 4000; // If there are no pending clocks, wake up every 4 seconds const long CLOSED_TIMEOUT = 60 * 1000; // If the connection is closed, once per minute @@ -728,13 +717,16 @@ bool PseudoTcp::process(Segment& seg) { if ((seg.ack > m_snd_una) && (seg.ack <= m_snd_nxt)) { // Calculate round-trip time if (seg.tsecr) { - long rtt = talk_base::TimeDiff(now, seg.tsecr); + int32 rtt = talk_base::TimeDiff(now, seg.tsecr); if (rtt >= 0) { if (m_rx_srtt == 0) { m_rx_srtt = rtt; m_rx_rttvar = rtt / 2; } else { - m_rx_rttvar = (3 * m_rx_rttvar + abs(long(rtt - m_rx_srtt))) / 4; + uint32 unsigned_rtt = static_cast<uint32>(rtt); + uint32 abs_err = unsigned_rtt > m_rx_srtt ? unsigned_rtt - m_rx_srtt + : m_rx_srtt - unsigned_rtt; + m_rx_rttvar = (3 * m_rx_rttvar + abs_err) / 4; m_rx_srtt = (7 * m_rx_srtt + rtt) / 8; } m_rx_rto = bound(MIN_RTO, m_rx_srtt + diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc b/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc index 2baef4245ab..37478ca4b50 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc @@ -75,7 +75,7 @@ RawTransportChannel::~RawTransportChannel() { } int RawTransportChannel::SendPacket(const char *data, size_t size, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, int flags) { if (port_ == NULL) return -1; @@ -83,7 +83,7 @@ int RawTransportChannel::SendPacket(const char *data, size_t size, return -1; if (flags != 0) return -1; - return port_->SendTo(data, size, remote_address_, dscp, true); + return port_->SendTo(data, size, remote_address_, options, true); } int RawTransportChannel::SetOption(talk_base::Socket::Option opt, int value) { diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h b/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h index ed38952d561..52085c04fa3 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h @@ -65,7 +65,7 @@ class RawTransportChannel : public TransportChannelImpl, // Implementation of normal channel packet sending. virtual int SendPacket(const char *data, size_t len, - talk_base::DiffServCodePoint dscp, int flags); + const talk_base::PacketOptions& options, int flags); virtual int SetOption(talk_base::Socket::Option opt, int value); virtual int GetError(); @@ -97,10 +97,14 @@ class RawTransportChannel : public TransportChannelImpl, virtual IceRole GetIceRole() const { return ICEROLE_UNKNOWN; } virtual void SetIceRole(IceRole role) {} virtual void SetIceTiebreaker(uint64 tiebreaker) {} + + virtual bool GetIceProtocolType(IceProtocolType* type) const { return false; } virtual void SetIceProtocolType(IceProtocolType type) {} + virtual void SetIceUfrag(const std::string& ice_ufrag) {} virtual void SetIcePwd(const std::string& ice_pwd) {} virtual void SetRemoteIceMode(IceMode mode) {} + virtual size_t GetConnectionCount() const { return 1; } virtual bool GetStats(ConnectionInfos* infos) { return false; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/relayport.cc b/chromium/third_party/libjingle/source/talk/p2p/base/relayport.cc index ddfca7114ca..23571ea5fa7 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/relayport.cc @@ -67,7 +67,7 @@ class RelayConnection : public sigslot::has_slots<> { bool CheckResponse(StunMessage* msg); // Sends data to the relay server. - int Send(const void* pv, size_t cb, talk_base::DiffServCodePoint dscp); + int Send(const void* pv, size_t cb, const talk_base::PacketOptions& options); // Sends a STUN allocate request message to the relay server. void SendAllocateRequest(RelayEntry* entry, int delay); @@ -124,7 +124,7 @@ class RelayEntry : public talk_base::MessageHandler, // entry. This will wrap the packet in STUN if necessary. int SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp); + const talk_base::PacketOptions& options); // Schedules a keep-alive allocate request. void ScheduleKeepAlive(); @@ -166,7 +166,7 @@ class RelayEntry : public talk_base::MessageHandler, // Sends the given data on the socket to the server with no wrapping. This // returns the number of bytes written or -1 if an error occurred. int SendPacket(const void* data, size_t size, - talk_base::DiffServCodePoint dscp); + const talk_base::PacketOptions& options); }; // Handles an allocate request for a particular RelayEntry. @@ -240,8 +240,11 @@ void RelayPort::SetReady() { for (iter = external_addr_.begin(); iter != external_addr_.end(); ++iter) { std::string proto_name = ProtoToString(iter->proto); - AddAddress(iter->address, iter->address, proto_name, - RELAY_PORT_TYPE, ICE_TYPE_PREFERENCE_RELAY, false); + // In case of Gturn, related address is set to null socket address. + // This is due to as mapped address stun attribute is used for allocated + // address. + AddAddress(iter->address, iter->address, talk_base::SocketAddress(), + proto_name, RELAY_PORT_TYPE, ICE_TYPE_PREFERENCE_RELAY, false); } ready_ = true; SignalPortComplete(this); @@ -258,8 +261,9 @@ bool RelayPort::HasMagicCookie(const char* data, size_t size) { if (size < 24 + sizeof(TURN_MAGIC_COOKIE_VALUE)) { return false; } else { - return 0 == std::memcmp(data + 24, TURN_MAGIC_COOKIE_VALUE, - sizeof(TURN_MAGIC_COOKIE_VALUE)); + return memcmp(data + 24, + TURN_MAGIC_COOKIE_VALUE, + sizeof(TURN_MAGIC_COOKIE_VALUE)) == 0; } } @@ -304,7 +308,7 @@ Connection* RelayPort::CreateConnection(const Candidate& address, int RelayPort::SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload) { // Try to find an entry for this specific address. Note that the first entry // created was not given an address initially, so it can be set to the first @@ -346,7 +350,7 @@ int RelayPort::SendTo(const void* data, size_t size, } // Send the actual contents to the server using the usual mechanism. - int sent = entry->SendTo(data, size, addr, dscp); + int sent = entry->SendTo(data, size, addr, options); if (sent <= 0) { ASSERT(sent < 0); error_ = entry->GetError(); @@ -359,14 +363,6 @@ int RelayPort::SendTo(const void* data, size_t size, int RelayPort::SetOption(talk_base::Socket::Option opt, int value) { int result = 0; - // DSCP option is not passed to the socket. - // TODO(mallinath) - After we have the support on socket, - // remove this specialization. - if (opt == talk_base::Socket::OPT_DSCP) { - SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value)); - return result; - } - for (size_t i = 0; i < entries_.size(); ++i) { if (entries_[i]->SetSocketOption(opt, value) < 0) { result = -1; @@ -434,18 +430,18 @@ bool RelayConnection::CheckResponse(StunMessage* msg) { void RelayConnection::OnSendPacket(const void* data, size_t size, StunRequest* req) { // TODO(mallinath) Find a way to get DSCP value from Port. - int sent = socket_->SendTo( - data, size, GetAddress(), talk_base::DSCP_NO_CHANGE); + talk_base::PacketOptions options; // Default dscp set to NO_CHANGE. + int sent = socket_->SendTo(data, size, GetAddress(), options); if (sent <= 0) { LOG(LS_VERBOSE) << "OnSendPacket: failed sending to " << GetAddress() << - std::strerror(socket_->GetError()); + strerror(socket_->GetError()); ASSERT(sent < 0); } } int RelayConnection::Send(const void* pv, size_t cb, - talk_base::DiffServCodePoint dscp) { - return socket_->SendTo(pv, cb, GetAddress(), dscp); + const talk_base::PacketOptions& options) { + return socket_->SendTo(pv, cb, GetAddress(), options); } void RelayConnection::SendAllocateRequest(RelayEntry* entry, int delay) { @@ -555,21 +551,17 @@ void RelayEntry::OnConnect(const talk_base::SocketAddress& mapped_addr, << " @ " << mapped_addr.ToSensitiveString(); connected_ = true; - // In case of Gturn related address is set to null socket address. - // This is due to mapped address stun attribute is used for allocated - // address. - port_->set_related_address(talk_base::SocketAddress()); port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto)); port_->SetReady(); } int RelayEntry::SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp) { + const talk_base::PacketOptions& options) { // If this connection is locked to the address given, then we can send the // packet with no wrapper. if (locked_ && (ext_addr_ == addr)) - return SendPacket(data, size, dscp); + return SendPacket(data, size, options); // Otherwise, we must wrap the given data in a STUN SEND request so that we // can communicate the destination address to the server. @@ -617,7 +609,7 @@ int RelayEntry::SendTo(const void* data, size_t size, talk_base::ByteBuffer buf; request.Write(&buf); - return SendPacket(buf.Data(), buf.Length(), dscp); + return SendPacket(buf.Data(), buf.Length(), options); } void RelayEntry::ScheduleKeepAlive() { @@ -766,12 +758,12 @@ void RelayEntry::OnReadyToSend(talk_base::AsyncPacketSocket* socket) { } int RelayEntry::SendPacket(const void* data, size_t size, - talk_base::DiffServCodePoint dscp) { + const talk_base::PacketOptions& options) { int sent = 0; if (current_connection_) { // We are connected, no need to send packets anywere else than to // the current connection. - sent = current_connection_->Send(data, size, dscp); + sent = current_connection_->Send(data, size, options); } return sent; } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/relayport.h b/chromium/third_party/libjingle/source/talk/p2p/base/relayport.h index 08df12f9d28..140c80fe065 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/relayport.h @@ -93,7 +93,7 @@ class RelayPort : public Port { virtual int SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload); // Dispatches the given packet to the port or connection as appropriate. diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/relayport_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/relayport_unittest.cc index bd00af86de0..987fd1e3966 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayport_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/relayport_unittest.cc @@ -32,6 +32,7 @@ #include "talk/base/scoped_ptr.h" #include "talk/base/socketadapters.h" #include "talk/base/socketaddress.h" +#include "talk/base/ssladapter.h" #include "talk/base/thread.h" #include "talk/base/virtualsocketserver.h" #include "talk/p2p/base/basicpacketsocketfactory.h" @@ -93,10 +94,14 @@ class RelayPortTest : public testing::Test, protected: static void SetUpTestCase() { - // Ensure the RNG is inited. - talk_base::InitRandom(NULL, 0); + talk_base::InitializeSSL(); } + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } + + virtual void SetUp() { // The relay server needs an external socket to work properly. talk_base::AsyncUDPSocket* ext_socket = diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.cc b/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.cc index c2619c03feb..3dd85065714 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.cc @@ -46,12 +46,11 @@ const int MAX_LIFETIME = 15 * 60 * 1000; // The number of bytes in each of the usernames we use. const uint32 USERNAME_LENGTH = 16; -static const uint32 kMessageAcceptConnection = 1; - // Calls SendTo on the given socket and logs any bad results. void Send(talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size, const talk_base::SocketAddress& addr) { - int result = socket->SendTo(bytes, size, addr, talk_base::DSCP_NO_CHANGE); + talk_base::PacketOptions options; + int result = socket->SendTo(bytes, size, addr, options); if (result < static_cast<int>(size)) { LOG(LS_ERROR) << "SendTo wrote only " << result << " of " << size << " bytes"; @@ -547,7 +546,10 @@ void RelayServer::RemoveBinding(RelayServerBinding* binding) { } void RelayServer::OnMessage(talk_base::Message *pmsg) { +#if ENABLE_DEBUG + static const uint32 kMessageAcceptConnection = 1; ASSERT(pmsg->message_id == kMessageAcceptConnection); +#endif talk_base::MessageData* data = pmsg->pdata; talk_base::AsyncSocket* socket = static_cast <talk_base::TypedMessageData<talk_base::AsyncSocket*>*> @@ -712,8 +714,7 @@ bool RelayServerBinding::HasMagicCookie(const char* bytes, size_t size) const { if (size < 24 + magic_cookie_.size()) { return false; } else { - return 0 == std::memcmp( - bytes + 24, magic_cookie_.c_str(), magic_cookie_.size()); + return memcmp(bytes + 24, magic_cookie_.c_str(), magic_cookie_.size()) == 0; } } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/relayserver_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/relayserver_unittest.cc index 86d2eef6926..239f644b475 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayserver_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/relayserver_unittest.cc @@ -32,6 +32,7 @@ #include "talk/base/logging.h" #include "talk/base/physicalsocketserver.h" #include "talk/base/socketaddress.h" +#include "talk/base/ssladapter.h" #include "talk/base/testclient.h" #include "talk/base/thread.h" #include "talk/p2p/base/relayserver.h" @@ -53,8 +54,13 @@ static const char* msg2 = "Lobster Thermidor a Crevette with a mornay sauce..."; class RelayServerTest : public testing::Test { public: static void SetUpTestCase() { - talk_base::InitRandom(NULL, 0); + talk_base::InitializeSSL(); } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } + RelayServerTest() : main_(talk_base::Thread::Current()), ss_(main_->socketserver()), username_(talk_base::CreateRandomString(12)), @@ -191,7 +197,7 @@ class RelayServerTest : public testing::Test { TEST_F(RelayServerTest, TestBadRequest) { talk_base::scoped_ptr<StunMessage> res; - SendRaw1(bad, static_cast<int>(std::strlen(bad))); + SendRaw1(bad, static_cast<int>(strlen(bad))); res.reset(Receive1()); ASSERT_TRUE(!res); @@ -334,7 +340,7 @@ TEST_F(RelayServerTest, TestRemoteBadRequest) { Allocate(); Bind(); - SendRaw1(bad, static_cast<int>(std::strlen(bad))); + SendRaw1(bad, static_cast<int>(strlen(bad))); EXPECT_TRUE(Receive1() == NULL); EXPECT_TRUE(Receive2() == NULL); } @@ -480,7 +486,7 @@ TEST_F(RelayServerTest, TestSendRaw) { Send1(req.get()); EXPECT_EQ(msg1, ReceiveRaw2()); - SendRaw2(msg2, static_cast<int>(std::strlen(msg2))); + SendRaw2(msg2, static_cast<int>(strlen(msg2))); res.reset(Receive1()); ASSERT_TRUE(res); @@ -533,6 +539,6 @@ TEST_F(RelayServerTest, TestExpiration) { EXPECT_EQ("Operation Not Supported", err->reason()); // Also verify that traffic from the external client is ignored. - SendRaw2(msg2, static_cast<int>(std::strlen(msg2))); + SendRaw2(msg2, static_cast<int>(strlen(msg2))); EXPECT_TRUE(ReceiveRaw1().empty()); } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/session.cc b/chromium/third_party/libjingle/source/talk/p2p/base/session.cc index e0f8dd3f4f8..a48f3cb0f66 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/session.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/session.cc @@ -277,21 +277,35 @@ void TransportProxy::SetIceRole(IceRole role) { } bool TransportProxy::SetLocalTransportDescription( - const TransportDescription& description, ContentAction action) { + const TransportDescription& description, + ContentAction action, + std::string* error_desc) { // If this is an answer, finalize the negotiation. if (action == CA_ANSWER) { CompleteNegotiation(); } - return transport_->get()->SetLocalTransportDescription(description, action); + bool result = transport_->get()->SetLocalTransportDescription(description, + action, + error_desc); + if (result) + local_description_set_ = true; + return result; } bool TransportProxy::SetRemoteTransportDescription( - const TransportDescription& description, ContentAction action) { + const TransportDescription& description, + ContentAction action, + std::string* error_desc) { // If this is an answer, finalize the negotiation. if (action == CA_ANSWER) { CompleteNegotiation(); } - return transport_->get()->SetRemoteTransportDescription(description, action); + bool result = transport_->get()->SetRemoteTransportDescription(description, + action, + error_desc); + if (result) + remote_description_set_ = true; + return result; } void TransportProxy::OnSignalingReady() { @@ -418,16 +432,22 @@ bool BaseSession::SetIdentity(talk_base::SSLIdentity* identity) { } bool BaseSession::PushdownTransportDescription(ContentSource source, - ContentAction action) { + ContentAction action, + std::string* error_desc) { if (source == CS_LOCAL) { - return PushdownLocalTransportDescription(local_description_, action); + return PushdownLocalTransportDescription(local_description_, + action, + error_desc); } - return PushdownRemoteTransportDescription(remote_description_, action); + return PushdownRemoteTransportDescription(remote_description_, + action, + error_desc); } bool BaseSession::PushdownLocalTransportDescription( const SessionDescription* sdesc, - ContentAction action) { + ContentAction action, + std::string* error_desc) { // Update the Transports with the right information, and trigger them to // start connecting. for (TransportMap::iterator iter = transports_.begin(); @@ -438,7 +458,8 @@ bool BaseSession::PushdownLocalTransportDescription( bool ret = GetTransportDescription( sdesc, iter->second->content_name(), &tdesc); if (ret) { - if (!iter->second->SetLocalTransportDescription(tdesc, action)) { + if (!iter->second->SetLocalTransportDescription(tdesc, action, + error_desc)) { return false; } @@ -451,7 +472,8 @@ bool BaseSession::PushdownLocalTransportDescription( bool BaseSession::PushdownRemoteTransportDescription( const SessionDescription* sdesc, - ContentAction action) { + ContentAction action, + std::string* error_desc) { // Update the Transports with the right information. for (TransportMap::iterator iter = transports_.begin(); iter != transports_.end(); ++iter) { @@ -462,7 +484,8 @@ bool BaseSession::PushdownRemoteTransportDescription( bool ret = GetTransportDescription( sdesc, iter->second->content_name(), &tdesc); if (ret) { - if (!iter->second->SetRemoteTransportDescription(tdesc, action)) { + if (!iter->second->SetRemoteTransportDescription(tdesc, action, + error_desc)) { return false; } } @@ -522,11 +545,17 @@ TransportProxy* BaseSession::GetOrCreateTransportProxy( this, &BaseSession::OnTransportCandidatesAllocationDone); transport->SignalRoleConflict.connect( this, &BaseSession::OnRoleConflict); + transport->SignalCompleted.connect( + this, &BaseSession::OnTransportCompleted); + transport->SignalFailed.connect( + this, &BaseSession::OnTransportFailed); transproxy = new TransportProxy(worker_thread_, sid_, content_name, new TransportWrapper(transport)); transproxy->SignalCandidatesReady.connect( this, &BaseSession::OnTransportProxyCandidatesReady); + if (identity_) + transproxy->SetIdentity(identity_); transports_[content_name] = transproxy; return transproxy; @@ -611,10 +640,11 @@ void BaseSession::SetState(State state) { SignalNewDescription(); } -void BaseSession::SetError(Error error) { +void BaseSession::SetError(Error error, const std::string& error_desc) { ASSERT(signaling_thread_->IsCurrent()); if (error != error_) { error_ = error; + error_desc_ = error_desc; SignalError(this, error); } } @@ -738,9 +768,9 @@ void BaseSession::OnTransportCandidatesAllocationDone(Transport* transport) { // Transport, since this removes the need to manually iterate over all // the transports, as is needed to make sure signals are handled properly // when BUNDLEing. -#if 0 - ASSERT(!IsCandidateAllocationDone()); -#endif + // TODO(juberti): Per b/7998978, devs and QA are hitting this assert in ways + // that make it prohibitively difficult to run dbg builds. Disabled for now. + //ASSERT(!IsCandidateAllocationDone()); for (TransportMap::iterator iter = transports_.begin(); iter != transports_.end(); ++iter) { if (iter->second->impl() == transport) { @@ -856,7 +886,7 @@ void BaseSession::OnMessage(talk_base::Message *pmsg) { switch (pmsg->message_id) { case MSG_TIMEOUT: // Session timeout has occured. - SetError(ERROR_TIME); + SetError(ERROR_TIME, "Session timeout has occured."); break; case MSG_STATE: @@ -925,7 +955,7 @@ bool Session::Initiate(const std::string &to, // the TransportDescriptions. SpeculativelyConnectAllTransportChannels(); - PushdownTransportDescription(CS_LOCAL, CA_OFFER); + PushdownTransportDescription(CS_LOCAL, CA_OFFER, NULL); SetState(Session::STATE_SENTINITIATE); return true; } @@ -946,7 +976,7 @@ bool Session::Accept(const SessionDescription* sdesc) { return false; } // TODO(juberti): Add BUNDLE support to transport-info messages. - PushdownTransportDescription(CS_LOCAL, CA_ANSWER); + PushdownTransportDescription(CS_LOCAL, CA_ANSWER, NULL); MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. SetState(Session::STATE_SENTACCEPT); return true; @@ -1261,8 +1291,10 @@ void Session::OnFailedSend(const buzz::XmlElement* orig_stanza, if (!OnRedirectError(redirect, &error)) { // TODO: Should we send a message back? The standard // says nothing about it. - LOG(LS_ERROR) << "Failed to redirect: " << error.text; - SetError(ERROR_RESPONSE); + std::ostringstream desc; + desc << "Failed to redirect: " << error.text; + LOG(LS_ERROR) << desc.str(); + SetError(ERROR_RESPONSE, desc.str()); } return; } @@ -1290,7 +1322,7 @@ void Session::OnFailedSend(const buzz::XmlElement* orig_stanza, } else if ((error_type != "continue") && (error_type != "wait")) { // We do not set an error if the other side said it is okay to continue // (possibly after waiting). These errors can be ignored. - SetError(ERROR_RESPONSE); + SetError(ERROR_RESPONSE, ""); } } @@ -1318,7 +1350,7 @@ bool Session::OnInitiateMessage(const SessionMessage& msg, init.transports, init.groups)); // Updating transport with TransportDescription. - PushdownTransportDescription(CS_REMOTE, CA_OFFER); + PushdownTransportDescription(CS_REMOTE, CA_OFFER, NULL); SetState(STATE_RECEIVEDINITIATE); // Users of Session may listen to state change and call Reject(). @@ -1352,7 +1384,7 @@ bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) { accept.transports, accept.groups)); // Updating transport with TransportDescription. - PushdownTransportDescription(CS_REMOTE, CA_ANSWER); + PushdownTransportDescription(CS_REMOTE, CA_ANSWER, NULL); MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. SetState(STATE_RECEIVEDACCEPT); @@ -1496,8 +1528,8 @@ bool Session::CheckState(State expected, MessageError* error) { return true; } -void Session::SetError(Error error) { - BaseSession::SetError(error); +void Session::SetError(Error error, const std::string& error_desc) { + BaseSession::SetError(error, error_desc); if (error != ERROR_NONE) signaling_thread()->Post(this, MSG_ERROR); } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/session.h b/chromium/third_party/libjingle/source/talk/p2p/base/session.h index 637c9424b7b..504187f6213 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/session.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/session.h @@ -102,7 +102,9 @@ class TransportProxy : public sigslot::has_slots<>, connecting_(false), negotiated_(false), sent_candidates_(false), - candidates_allocated_(false) { + candidates_allocated_(false), + local_description_set_(false), + remote_description_set_(false) { transport_->get()->SignalCandidatesReady.connect( this, &TransportProxy::OnTransportCandidatesReady); } @@ -145,9 +147,11 @@ class TransportProxy : public sigslot::has_slots<>, void SetIceRole(IceRole role); void SetIdentity(talk_base::SSLIdentity* identity); bool SetLocalTransportDescription(const TransportDescription& description, - ContentAction action); + ContentAction action, + std::string* error_desc); bool SetRemoteTransportDescription(const TransportDescription& description, - ContentAction action); + ContentAction action, + std::string* error_desc); void OnSignalingReady(); bool OnRemoteCandidates(const Candidates& candidates, std::string* error); @@ -163,6 +167,13 @@ class TransportProxy : public sigslot::has_slots<>, SignalCandidatesReady(this, candidates); } + bool local_description_set() const { + return local_description_set_; + } + bool remote_description_set() const { + return remote_description_set_; + } + // Handles sending of ready candidates and receiving of remote candidates. sigslot::signal2<TransportProxy*, const std::vector<Candidate>&> SignalCandidatesReady; @@ -194,6 +205,8 @@ class TransportProxy : public sigslot::has_slots<>, Candidates sent_candidates_; Candidates unsent_candidates_; bool candidates_allocated_; + bool local_description_set_; + bool remote_description_set_; }; typedef std::map<std::string, TransportProxy*> TransportMap; @@ -327,13 +340,15 @@ class BaseSession : public sigslot::has_slots<>, // Returns the last error in the session. See the enum above for details. // Each time the an error occurs, we will fire this signal. Error error() const { return error_; } + const std::string& error_desc() const { return error_desc_; } sigslot::signal2<BaseSession* , Error> SignalError; // Updates the state, signaling if necessary. virtual void SetState(State state); // Updates the error state, signaling if necessary. - virtual void SetError(Error error); + // TODO(ronghuawu): remove the SetError method that doesn't take |error_desc|. + virtual void SetError(Error error, const std::string& error_desc); // Fired when the remote description is updated, with the updated // contents. @@ -384,7 +399,8 @@ class BaseSession : public sigslot::has_slots<>, bool SetIdentity(talk_base::SSLIdentity* identity); bool PushdownTransportDescription(ContentSource source, - ContentAction action); + ContentAction action, + std::string* error_desc); void set_initiator(bool initiator) { initiator_ = initiator; } const TransportMap& transport_proxies() const { return transports_; } @@ -427,6 +443,14 @@ class BaseSession : public sigslot::has_slots<>, virtual void OnTransportReadable(Transport* transport) { } + // Called when a transport has found its steady-state connections. + virtual void OnTransportCompleted(Transport* transport) { + } + + // Called when a transport has failed permanently. + virtual void OnTransportFailed(Transport* transport) { + } + // Called when a transport signals that it has new candidates. virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy, const Candidates& candidates) { @@ -466,13 +490,16 @@ class BaseSession : public sigslot::has_slots<>, protected: State state_; Error error_; + std::string error_desc_; private: // Helper methods to push local and remote transport descriptions. bool PushdownLocalTransportDescription( - const SessionDescription* sdesc, ContentAction action); + const SessionDescription* sdesc, ContentAction action, + std::string* error_desc); bool PushdownRemoteTransportDescription( - const SessionDescription* sdesc, ContentAction action); + const SessionDescription* sdesc, ContentAction action, + std::string* error_desc); bool IsCandidateAllocationDone() const; void MaybeCandidateAllocationDone(); @@ -553,7 +580,7 @@ class Session : public BaseSession { } // Updates the error state, signaling if necessary. - virtual void SetError(Error error); + virtual void SetError(Error error, const std::string& error_desc); // When the session needs to send signaling messages, it beings by requesting // signaling. The client should handle this by calling OnSignalingReady once diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/session_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/session_unittest.cc index ab4620f879e..1c08bf18dd0 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/session_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/session_unittest.cc @@ -25,14 +25,14 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <cstring> +#include <string.h> + #include <sstream> #include <deque> #include <map> #include "talk/base/base64.h" #include "talk/base/common.h" -#include "talk/base/dscp.h" #include "talk/base/gunit.h" #include "talk/base/helpers.h" #include "talk/base/logging.h" @@ -77,19 +77,6 @@ static const int kNumPorts = 2; static const int kPort0 = 28653; static const int kPortStep = 5; -static const std::string kNotifyNick1 = "derekcheng_google.com^59422C27"; -static const std::string kNotifyNick2 = "someoneelses_google.com^7abd6a7a20"; -static const uint32 kNotifyAudioSsrc1 = 2625839801U; -static const uint32 kNotifyAudioSsrc2 = 2529430427U; -static const uint32 kNotifyVideoSsrc1 = 3; -static const uint32 kNotifyVideoSsrc2 = 2; - -static const std::string kViewRequestNick = "param_google.com^16A3CDBE"; -static const uint32 kViewRequestSsrc = 4; -static const int kViewRequestWidth = 320; -static const int kViewRequestHeight = 200; -static const int kViewRequestFrameRate = 15; - int GetPort(int port_index) { return kPort0 + (port_index * kPortStep); } @@ -824,14 +811,15 @@ struct ChannelHandler : sigslot::has_slots<> { EXPECT_LE(size, sizeof(last_data)); data_count += 1; last_size = size; - std::memcpy(last_data, buf, size); + memcpy(last_data, buf, size); } void Send(const char* data, size_t size) { + talk_base::PacketOptions options; std::string data_with_id(name); data_with_id += data; int result = channel->SendPacket(data_with_id.c_str(), data_with_id.size(), - talk_base::DSCP_NO_CHANGE, 0); + options, 0); EXPECT_EQ(static_cast<int>(data_with_id.size()), result); } @@ -1170,14 +1158,10 @@ class SessionTest : public testing::Test { EXPECT_EQ(strlen(dat1a), chan2a->last_size); EXPECT_EQ(strlen(dat1b), chan2b->last_size); - EXPECT_EQ(0, std::memcmp(chan1a->last_data, dat2a, - strlen(dat2a))); - EXPECT_EQ(0, std::memcmp(chan1b->last_data, dat2b, - strlen(dat2b))); - EXPECT_EQ(0, std::memcmp(chan2a->last_data, dat1a, - strlen(dat1a))); - EXPECT_EQ(0, std::memcmp(chan2b->last_data, dat1b, - strlen(dat1b))); + EXPECT_EQ(0, memcmp(chan1a->last_data, dat2a, strlen(dat2a))); + EXPECT_EQ(0, memcmp(chan1b->last_data, dat2b, strlen(dat2b))); + EXPECT_EQ(0, memcmp(chan2a->last_data, dat1a, strlen(dat1a))); + EXPECT_EQ(0, memcmp(chan2b->last_data, dat1b, strlen(dat1b))); } } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stun.cc b/chromium/third_party/libjingle/source/talk/p2p/base/stun.cc index 38fc96ee1a4..6331ba9ebd2 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stun.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stun.cc @@ -27,7 +27,7 @@ #include "talk/p2p/base/stun.h" -#include <cstring> +#include <string.h> #include "talk/base/byteorder.h" #include "talk/base/common.h" @@ -217,8 +217,9 @@ bool StunMessage::ValidateMessageIntegrity(const char* data, size_t size, return false; // Comparing the calculated HMAC with the one present in the message. - return (std::memcmp(data + current_pos + kStunAttributeHeaderSize, - hmac, sizeof(hmac)) == 0); + return memcmp(data + current_pos + kStunAttributeHeaderSize, + hmac, + sizeof(hmac)) == 0; } bool StunMessage::AddMessageIntegrity(const std::string& password) { @@ -734,7 +735,7 @@ void StunByteStringAttribute::CopyBytes(const char* bytes) { void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) { char* new_bytes = new char[length]; - std::memcpy(new_bytes, bytes, length); + memcpy(new_bytes, bytes, length); SetBytes(new_bytes, length); } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stun_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/stun_unittest.cc index 6a5bcd9bbb9..71d87500e99 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stun_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stun_unittest.cc @@ -50,8 +50,7 @@ class StunTest : public ::testing::Test { ASSERT_EQ(length, msg.transaction_id().size()); ASSERT_EQ(length == kStunTransactionIdLength + 4, msg.IsLegacy()); ASSERT_EQ(length == kStunTransactionIdLength, !msg.IsLegacy()); - ASSERT_EQ(0, std::memcmp(msg.transaction_id().c_str(), - expectedID, length)); + ASSERT_EQ(0, memcmp(msg.transaction_id().c_str(), expectedID, length)); } void CheckStunAddressAttribute(const StunAddressAttribute* addr, @@ -64,13 +63,11 @@ class StunTest : public ::testing::Test { if (addr->family() == STUN_ADDRESS_IPV4) { in_addr v4_address = expected_address.ipv4_address(); in_addr stun_address = addr->ipaddr().ipv4_address(); - ASSERT_EQ(0, std::memcmp(&v4_address, &stun_address, - sizeof(stun_address))); + ASSERT_EQ(0, memcmp(&v4_address, &stun_address, sizeof(stun_address))); } else if (addr->family() == STUN_ADDRESS_IPV6) { in6_addr v6_address = expected_address.ipv6_address(); in6_addr stun_address = addr->ipaddr().ipv6_address(); - ASSERT_EQ(0, std::memcmp(&v6_address, &stun_address, - sizeof(stun_address))); + ASSERT_EQ(0, memcmp(&v6_address, &stun_address, sizeof(stun_address))); } else { ASSERT_TRUE(addr->family() == STUN_ADDRESS_IPV6 || addr->family() == STUN_ADDRESS_IPV4); @@ -211,37 +208,6 @@ static const unsigned char kStunMessageWithErrorAttribute[] = { 0x69, 0x7a, 0x65, 0x64 }; -// Message with an address attribute with an unknown address family, -// and a byte string attribute. Check that we quit reading after the -// bogus address family and don't read the username attribute. -static const unsigned char kStunMessageWithInvalidAddressFamily[] = { - 0x01, 0x01, 0x00, 0x18, // binding response, length 24 - 0x21, 0x12, 0xa4, 0x42, // magic cookie - 0x29, 0x1f, 0xcd, 0x7c, // transaction ID - 0xba, 0x58, 0xab, 0xd7, - 0xf2, 0x41, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x08, // Mapped address, 4 byte length - 0x00, 0x09, 0xfe, 0xed, // Bogus address family (port unimportant). - 0xac, 0x17, 0x44, 0xe6, // Should be skipped. - 0x00, 0x06, 0x00, 0x08, // Username attribute (length 8) - 0x61, 0x62, 0x63, 0x64, // abcdefgh - 0x65, 0x66, 0x67, 0x68 -}; - -// Message with an address attribute with an invalid address length. -// Should fail to be read. -static const unsigned char kStunMessageWithInvalidAddressLength[] = { - 0x01, 0x01, 0x00, 0x18, // binding response, length 24 - 0x21, 0x12, 0xa4, 0x42, // magic cookie - 0x29, 0x1f, 0xcd, 0x7c, // transaction ID - 0xba, 0x58, 0xab, 0xd7, - 0xf2, 0x41, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x0c, // Mapped address, 12 byte length - 0x00, 0x01, 0xfe, 0xed, // Claims to be AF_INET. - 0xac, 0x17, 0x44, 0xe6, - 0x00, 0x06, 0x00, 0x08 -}; - // Sample messages with an invalid length Field // The actual length in bytes of the invalid messages (including STUN header) @@ -514,19 +480,10 @@ const in6_addr kIPv6TestAddress2 = { { { 0x24, 0x01, 0xfa, 0x00, 0x06, 0x0c, 0xce, 0xff, 0xfe, 0x1f, 0x61, 0xa4 } } }; -// This is kIPv6TestAddress1 xor-ed with kTestTransactionID2. -const in6_addr kIPv6XoredTestAddress = { { { 0x05, 0x13, 0x5e, 0x42, - 0xe3, 0xad, 0x56, 0xe1, - 0xc2, 0x30, 0x99, 0x9d, - 0xaa, 0xed, 0x01, 0xc3 } } }; - #ifdef POSIX const in_addr kIPv4TestAddress1 = { 0xe64417ac }; -// This is kIPv4TestAddress xored with the STUN magic cookie. -const in_addr kIPv4XoredTestAddress = { 0x8d05e0a4 }; #elif defined WIN32 // Windows in_addr has a union with a uchar[] array first. -const in_addr kIPv4XoredTestAddress = { { 0x8d, 0x05, 0xe0, 0xa4 } }; const in_addr kIPv4TestAddress1 = { { 0x0ac, 0x017, 0x044, 0x0e6 } }; #endif const char kTestUserName1[] = "abcdefgh"; @@ -788,9 +745,8 @@ TEST_F(StunTest, SetIPv6XorAddressAttributeOwner) { EXPECT_TRUE(addr->Write(&correct_buf)); EXPECT_TRUE(addr2.Write(&wrong_buf)); // But when written out, the buffers should look different. - ASSERT_NE(0, std::memcmp(correct_buf.Data(), - wrong_buf.Data(), - wrong_buf.Length())); + ASSERT_NE(0, + memcmp(correct_buf.Data(), wrong_buf.Data(), wrong_buf.Length())); // And when reading a known good value, the address should be wrong. addr2.Read(&correct_buf); ASSERT_NE(addr->ipaddr(), addr2.ipaddr()); @@ -836,9 +792,8 @@ TEST_F(StunTest, SetIPv4XorAddressAttributeOwner) { EXPECT_TRUE(addr->Write(&correct_buf)); EXPECT_TRUE(addr2.Write(&wrong_buf)); // The same address data should be written. - ASSERT_EQ(0, std::memcmp(correct_buf.Data(), - wrong_buf.Data(), - wrong_buf.Length())); + ASSERT_EQ(0, + memcmp(correct_buf.Data(), wrong_buf.Data(), wrong_buf.Length())); // And an attribute should be able to un-XOR an address belonging to a message // with a different transaction ID. EXPECT_TRUE(addr2.Read(&correct_buf)); @@ -927,9 +882,7 @@ TEST_F(StunTest, WriteMessageWithIPv6AddressAttribute) { int len1 = static_cast<int>(out.Length()); std::string bytes; out.ReadString(&bytes, len1); - ASSERT_EQ(0, std::memcmp(bytes.c_str(), - kStunMessageWithIPv6MappedAddress, - len1)); + ASSERT_EQ(0, memcmp(bytes.c_str(), kStunMessageWithIPv6MappedAddress, len1)); } TEST_F(StunTest, WriteMessageWithIPv4AddressAttribute) { @@ -958,9 +911,7 @@ TEST_F(StunTest, WriteMessageWithIPv4AddressAttribute) { int len1 = static_cast<int>(out.Length()); std::string bytes; out.ReadString(&bytes, len1); - ASSERT_EQ(0, std::memcmp(bytes.c_str(), - kStunMessageWithIPv4MappedAddress, - len1)); + ASSERT_EQ(0, memcmp(bytes.c_str(), kStunMessageWithIPv4MappedAddress, len1)); } TEST_F(StunTest, WriteMessageWithIPv6XorAddressAttribute) { @@ -989,9 +940,8 @@ TEST_F(StunTest, WriteMessageWithIPv6XorAddressAttribute) { int len1 = static_cast<int>(out.Length()); std::string bytes; out.ReadString(&bytes, len1); - ASSERT_EQ(0, std::memcmp(bytes.c_str(), - kStunMessageWithIPv6XorMappedAddress, - len1)); + ASSERT_EQ(0, + memcmp(bytes.c_str(), kStunMessageWithIPv6XorMappedAddress, len1)); } TEST_F(StunTest, WriteMessageWithIPv4XoreAddressAttribute) { @@ -1020,9 +970,8 @@ TEST_F(StunTest, WriteMessageWithIPv4XoreAddressAttribute) { int len1 = static_cast<int>(out.Length()); std::string bytes; out.ReadString(&bytes, len1); - ASSERT_EQ(0, std::memcmp(bytes.c_str(), - kStunMessageWithIPv4XorMappedAddress, - len1)); + ASSERT_EQ(0, + memcmp(bytes.c_str(), kStunMessageWithIPv4XorMappedAddress, len1)); } TEST_F(StunTest, ReadByteStringAttribute) { @@ -1107,7 +1056,7 @@ TEST_F(StunTest, WriteMessageWithAnErrorCodeAttribute) { EXPECT_TRUE(msg.Write(&out)); ASSERT_EQ(size, out.Length()); // No padding. - ASSERT_EQ(0, std::memcmp(out.Data(), kStunMessageWithErrorAttribute, size)); + ASSERT_EQ(0, memcmp(out.Data(), kStunMessageWithErrorAttribute, size)); } TEST_F(StunTest, WriteMessageWithAUInt16ListAttribute) { @@ -1130,8 +1079,8 @@ TEST_F(StunTest, WriteMessageWithAUInt16ListAttribute) { EXPECT_TRUE(msg.Write(&out)); ASSERT_EQ(size, out.Length()); // Check everything up to the padding. - ASSERT_EQ(0, std::memcmp(out.Data(), kStunMessageWithUInt16ListAttribute, - size - 2)); + ASSERT_EQ(0, + memcmp(out.Data(), kStunMessageWithUInt16ListAttribute, size - 2)); } // Test that we fail to read messages with invalid lengths. @@ -1238,7 +1187,7 @@ TEST_F(StunTest, AddMessageIntegrity) { const StunByteStringAttribute* mi_attr = msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY); EXPECT_EQ(20U, mi_attr->length()); - EXPECT_EQ(0, std::memcmp( + EXPECT_EQ(0, memcmp( mi_attr->bytes(), kCalculatedHmac1, sizeof(kCalculatedHmac1))); talk_base::ByteBuffer buf1; @@ -1256,8 +1205,8 @@ TEST_F(StunTest, AddMessageIntegrity) { const StunByteStringAttribute* mi_attr2 = msg2.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY); EXPECT_EQ(20U, mi_attr2->length()); - EXPECT_EQ(0, std::memcmp( - mi_attr2->bytes(), kCalculatedHmac2, sizeof(kCalculatedHmac2))); + EXPECT_EQ( + 0, memcmp(mi_attr2->bytes(), kCalculatedHmac2, sizeof(kCalculatedHmac2))); talk_base::ByteBuffer buf3; EXPECT_TRUE(msg2.Write(&buf3)); @@ -1401,8 +1350,10 @@ TEST_F(StunTest, ReadRelayMessage) { bytes = msg.GetByteString(STUN_ATTR_MAGIC_COOKIE); ASSERT_TRUE(bytes != NULL); EXPECT_EQ(4U, bytes->length()); - EXPECT_EQ(0, std::memcmp(bytes->bytes(), TURN_MAGIC_COOKIE_VALUE, - sizeof(TURN_MAGIC_COOKIE_VALUE))); + EXPECT_EQ(0, + memcmp(bytes->bytes(), + TURN_MAGIC_COOKIE_VALUE, + sizeof(TURN_MAGIC_COOKIE_VALUE))); bytes2 = StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); bytes2->CopyBytes(reinterpret_cast<const char*>(TURN_MAGIC_COOKIE_VALUE), @@ -1454,7 +1405,7 @@ TEST_F(StunTest, ReadRelayMessage) { size_t len1 = out.Length(); std::string outstring; out.ReadString(&outstring, len1); - EXPECT_EQ(0, std::memcmp(outstring.c_str(), input, len1)); + EXPECT_EQ(0, memcmp(outstring.c_str(), input, len1)); talk_base::ByteBuffer out2; EXPECT_TRUE(msg2.Write(&out2)); @@ -1462,7 +1413,7 @@ TEST_F(StunTest, ReadRelayMessage) { size_t len2 = out2.Length(); std::string outstring2; out2.ReadString(&outstring2, len2); - EXPECT_EQ(0, std::memcmp(outstring2.c_str(), input, len2)); + EXPECT_EQ(0, memcmp(outstring2.c_str(), input, len2)); } } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stunport.cc b/chromium/third_party/libjingle/source/talk/p2p/base/stunport.cc index 913f9af5f5b..6e18fc50569 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunport.cc @@ -173,7 +173,7 @@ bool UDPPort::Init() { UDPPort::~UDPPort() { if (resolver_) { - resolver_->Destroy(false); + resolver_->Destroy(true); } if (!SharedSocket()) delete socket_; @@ -192,7 +192,7 @@ void UDPPort::MaybePrepareStunCandidate() { if (!server_addr_.IsNil()) { SendStunBindingRequest(); } else { - // Processing host candidate address. + // Port is done allocating candidates. SetResult(true); } } @@ -218,9 +218,9 @@ Connection* UDPPort::CreateConnection(const Candidate& address, int UDPPort::SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload) { - int sent = socket_->SendTo(data, size, addr, dscp); + int sent = socket_->SendTo(data, size, addr, options); if (sent < 0) { error_ = socket_->GetError(); LOG_J(LS_ERROR, this) << "UDP send of " << size @@ -230,12 +230,6 @@ int UDPPort::SendTo(const void* data, size_t size, } int UDPPort::SetOption(talk_base::Socket::Option opt, int value) { - // TODO(mallinath) - After we have the support on socket, - // remove this specialization. - if (opt == talk_base::Socket::OPT_DSCP) { - SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value)); - return 0; - } return socket_->SetOption(opt, value); } @@ -249,7 +243,8 @@ int UDPPort::GetError() { void UDPPort::OnLocalAddressReady(talk_base::AsyncPacketSocket* socket, const talk_base::SocketAddress& address) { - AddAddress(address, address, UDP_PROTOCOL_NAME, LOCAL_PORT_TYPE, + AddAddress(address, address, talk_base::SocketAddress(), + UDP_PROTOCOL_NAME, LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST, false); MaybePrepareStunCandidate(); } @@ -330,11 +325,10 @@ void UDPPort::OnStunBindingRequestSucceeded( if (!SharedSocket() || stun_addr != socket_->GetLocalAddress()) { // If socket is shared and |stun_addr| is equal to local socket // address then discarding the stun address. - // Setting related address before STUN candidate is added. For STUN - // related address is local socket address. - set_related_address(socket_->GetLocalAddress()); - AddAddress(stun_addr, socket_->GetLocalAddress(), UDP_PROTOCOL_NAME, - STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_PRFLX, false); + // For STUN related address is local socket address. + AddAddress(stun_addr, socket_->GetLocalAddress(), + socket_->GetLocalAddress(), UDP_PROTOCOL_NAME, + STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, false); } SetResult(true); } @@ -360,7 +354,8 @@ void UDPPort::SetResult(bool success) { // TODO: merge this with SendTo above. void UDPPort::OnSendPacket(const void* data, size_t size, StunRequest* req) { StunBindingRequest* sreq = static_cast<StunBindingRequest*>(req); - if (socket_->SendTo(data, size, sreq->server_addr(), DefaultDscpValue()) < 0) + talk_base::PacketOptions options(DefaultDscpValue()); + if (socket_->SendTo(data, size, sreq->server_addr(), options) < 0) PLOG(LERROR, socket_->GetError()) << "sendto"; } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stunport.h b/chromium/third_party/libjingle/source/talk/p2p/base/stunport.h index a8b89c3be5d..c45d6af334c 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunport.h @@ -125,7 +125,7 @@ class UDPPort : public Port { virtual int SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload); void OnLocalAddressReady(talk_base::AsyncPacketSocket* socket, @@ -142,7 +142,6 @@ class UDPPort : public Port { void SendStunBindingRequest(); - private: // DNS resolution of the STUN server. void ResolveStunAddress(); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stunport_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/stunport_unittest.cc index 2a98a9fdb8b..5850027ec8d 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunport_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunport_unittest.cc @@ -43,6 +43,8 @@ static const SocketAddress kBadAddr("0.0.0.1", 5000); static const SocketAddress kStunHostnameAddr("localhost", 5000); static const SocketAddress kBadHostnameAddr("not-a-real-hostname", 5000); static const int kTimeoutMs = 10000; +// stun prio = 100 << 24 | 30 (IPV4) << 8 | 256 - 0 +static const uint32 kStunCandidatePriority = 1677729535; // Tests connecting a StunPort to a fake STUN server (cricket::StunServer) // TODO: Use a VirtualSocketServer here. We have to use a @@ -178,6 +180,7 @@ TEST_F(StunPortTest, TestPrepareAddressHostname) { EXPECT_TRUE_WAIT(done(), kTimeoutMs); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address())); + EXPECT_EQ(kStunCandidatePriority, port()->Candidates()[0].priority()); } // Test that we handle hostname lookup failures properly. diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stunrequest_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/stunrequest_unittest.cc index b641585a70b..508660c8499 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunrequest_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunrequest_unittest.cc @@ -28,6 +28,7 @@ #include "talk/base/gunit.h" #include "talk/base/helpers.h" #include "talk/base/logging.h" +#include "talk/base/ssladapter.h" #include "talk/base/timeutils.h" #include "talk/p2p/base/stunrequest.h" @@ -37,8 +38,13 @@ class StunRequestTest : public testing::Test, public sigslot::has_slots<> { public: static void SetUpTestCase() { - talk_base::InitRandom(NULL, 0); + talk_base::InitializeSSL(); } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } + StunRequestTest() : manager_(talk_base::Thread::Current()), request_count_(0), response_(NULL), diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.cc b/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.cc index 062be206876..ee6c64376bc 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.cc @@ -103,8 +103,8 @@ void StunServer::SendResponse( const StunMessage& msg, const talk_base::SocketAddress& addr) { talk_base::ByteBuffer buf; msg.Write(&buf); - if (socket_->SendTo( - buf.Data(), buf.Length(), addr, talk_base::DSCP_NO_CHANGE) < 0) + talk_base::PacketOptions options; + if (socket_->SendTo(buf.Data(), buf.Length(), addr, options) < 0) LOG_ERR(LS_ERROR) << "sendto"; } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stunserver_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/stunserver_unittest.cc index abb19578696..a6f56a5176e 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunserver_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunserver_unittest.cc @@ -119,7 +119,7 @@ TEST_F(StunServerTest, TestBad) { const char* bad = "this is a completely nonsensical message whose only " "purpose is to make the parser go 'ack'. it doesn't " "look anything like a normal stun message"; - Send(bad, static_cast<int>(std::strlen(bad))); + Send(bad, static_cast<int>(strlen(bad))); StunMessage* msg = Receive(); ASSERT_TRUE(msg == NULL); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.cc b/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.cc index 2cca82f1946..069323a3cd3 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.cc @@ -121,6 +121,7 @@ void TCPPort::PrepareAddress() { if (socket_->GetState() == talk_base::AsyncPacketSocket::STATE_BOUND || socket_->GetState() == talk_base::AsyncPacketSocket::STATE_CLOSED) AddAddress(socket_->GetLocalAddress(), socket_->GetLocalAddress(), + talk_base::SocketAddress(), TCP_PROTOCOL_NAME, LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, true); } else { @@ -128,14 +129,15 @@ void TCPPort::PrepareAddress() { // Note: We still add the address, since otherwise the remote side won't // recognize our incoming TCP connections. AddAddress(talk_base::SocketAddress(ip(), 0), - talk_base::SocketAddress(ip(), 0), TCP_PROTOCOL_NAME, - LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, true); + talk_base::SocketAddress(ip(), 0), talk_base::SocketAddress(), + TCP_PROTOCOL_NAME, LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, + true); } } int TCPPort::SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload) { talk_base::AsyncPacketSocket * socket = NULL; if (TCPConnection * conn = static_cast<TCPConnection*>(GetConnection(addr))) { @@ -149,7 +151,7 @@ int TCPPort::SendTo(const void* data, size_t size, return -1; // TODO: Set error_ } - int sent = socket->Send(data, size, dscp); + int sent = socket->Send(data, size, options); if (sent < 0) { error_ = socket->GetError(); LOG_J(LS_ERROR, this) << "TCP send of " << size @@ -167,14 +169,6 @@ int TCPPort::GetOption(talk_base::Socket::Option opt, int* value) { } int TCPPort::SetOption(talk_base::Socket::Option opt, int value) { - // If we are setting DSCP value, pass value to base Port and return. - // TODO(mallinath) - After we have the support on socket, - // remove this specialization. - if (opt == talk_base::Socket::OPT_DSCP) { - SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value)); - return 0; - } - if (socket_) { return socket_->SetOption(opt, value); } else { @@ -229,7 +223,7 @@ void TCPPort::OnReadyToSend(talk_base::AsyncPacketSocket* socket) { void TCPPort::OnAddressReady(talk_base::AsyncPacketSocket* socket, const talk_base::SocketAddress& address) { - AddAddress(address, address, "tcp", + AddAddress(address, address, talk_base::SocketAddress(), "tcp", LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, true); } @@ -243,7 +237,7 @@ TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate, int opts = (candidate.protocol() == SSLTCP_PROTOCOL_NAME) ? talk_base::PacketSocketFactory::OPT_SSLTCP : 0; socket_ = port->socket_factory()->CreateClientTcpSocket( - talk_base::SocketAddress(port_->Network()->ip(), 0), + talk_base::SocketAddress(port->ip(), 0), candidate.address(), port->proxy(), port->user_agent(), opts); if (socket_) { LOG_J(LS_VERBOSE, this) << "Connecting from " @@ -273,7 +267,7 @@ TCPConnection::~TCPConnection() { } int TCPConnection::Send(const void* data, size_t size, - talk_base::DiffServCodePoint dscp) { + const talk_base::PacketOptions& options) { if (!socket_) { error_ = ENOTCONN; return SOCKET_ERROR; @@ -284,7 +278,7 @@ int TCPConnection::Send(const void* data, size_t size, error_ = EWOULDBLOCK; return SOCKET_ERROR; } - int sent = socket_->Send(data, size, dscp); + int sent = socket_->Send(data, size, options); if (sent < 0) { error_ = socket_->GetError(); } else { @@ -299,9 +293,19 @@ int TCPConnection::GetError() { void TCPConnection::OnConnect(talk_base::AsyncPacketSocket* socket) { ASSERT(socket == socket_); - LOG_J(LS_VERBOSE, this) << "Connection established to " - << socket->GetRemoteAddress().ToSensitiveString(); - set_connected(true); + // Do not use this connection if the socket bound to a different address than + // the one we asked for. This is seen in Chrome, where TCP sockets cannot be + // given a binding address, and the platform is expected to pick the + // correct local address. + if (socket->GetLocalAddress().ipaddr() == port()->ip()) { + LOG_J(LS_VERBOSE, this) << "Connection established to " + << socket->GetRemoteAddress().ToSensitiveString(); + set_connected(true); + } else { + LOG_J(LS_WARNING, this) << "Dropping connection as TCP socket bound to a " + << "different address from the local candidate."; + socket_->Close(); + } } void TCPConnection::OnClose(talk_base::AsyncPacketSocket* socket, int error) { diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.h b/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.h index 77b177a2d51..c152ec0d386 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.h @@ -83,7 +83,7 @@ class TCPPort : public Port { // Handles sending using the local TCP socket. virtual int SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload); // Accepts incoming TCP connection. @@ -128,7 +128,7 @@ class TCPConnection : public Connection { virtual ~TCPConnection(); virtual int Send(const void* data, size_t size, - talk_base::DiffServCodePoint dscp); + const talk_base::PacketOptions& options); virtual int GetError(); talk_base::AsyncPacketSocket* socket() { return socket_; } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transport.cc b/chromium/third_party/libjingle/source/talk/p2p/base/transport.cc index 4404c081a83..16087e3f069 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transport.cc @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2005, Google Inc. + * Copyright 2004, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -53,6 +53,8 @@ enum { MSG_CONNECTING, MSG_CANDIDATEALLOCATIONCOMPLETE, MSG_ROLECONFLICT, + MSG_COMPLETED, + MSG_FAILED, }; struct ChannelParams : public talk_base::MessageData { @@ -73,6 +75,49 @@ struct ChannelParams : public talk_base::MessageData { Candidate* candidate; }; +static std::string IceProtoToString(TransportProtocol proto) { + std::string proto_str; + switch (proto) { + case ICEPROTO_GOOGLE: + proto_str = "gice"; + break; + case ICEPROTO_HYBRID: + proto_str = "hybrid"; + break; + case ICEPROTO_RFC5245: + proto_str = "ice"; + break; + default: + ASSERT(false); + break; + } + return proto_str; +} + +static bool VerifyIceParams(const TransportDescription& desc) { + // For legacy protocols. + if (desc.ice_ufrag.empty() && desc.ice_pwd.empty()) + return true; + + if (desc.ice_ufrag.length() < ICE_UFRAG_MIN_LENGTH || + desc.ice_ufrag.length() > ICE_UFRAG_MAX_LENGTH) { + return false; + } + if (desc.ice_pwd.length() < ICE_PWD_MIN_LENGTH || + desc.ice_pwd.length() > ICE_PWD_MAX_LENGTH) { + return false; + } + return true; +} + +bool BadTransportDescription(const std::string& desc, std::string* err_desc) { + if (err_desc) { + *err_desc = desc; + } + LOG(LS_ERROR) << desc; + return false; +} + Transport::Transport(talk_base::Thread* signaling_thread, talk_base::Thread* worker_thread, const std::string& content_name, @@ -131,15 +176,21 @@ bool Transport::GetRemoteCertificate_w(talk_base::SSLCertificate** cert) { } bool Transport::SetLocalTransportDescription( - const TransportDescription& description, ContentAction action) { + const TransportDescription& description, + ContentAction action, + std::string* error_desc) { return worker_thread_->Invoke<bool>(Bind( - &Transport::SetLocalTransportDescription_w, this, description, action)); + &Transport::SetLocalTransportDescription_w, this, + description, action, error_desc)); } bool Transport::SetRemoteTransportDescription( - const TransportDescription& description, ContentAction action) { + const TransportDescription& description, + ContentAction action, + std::string* error_desc) { return worker_thread_->Invoke<bool>(Bind( - &Transport::SetRemoteTransportDescription_w, this, description, action)); + &Transport::SetRemoteTransportDescription_w, this, + description, action, error_desc)); } TransportChannelImpl* Transport::CreateChannel(int component) { @@ -175,13 +226,14 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) { // Push down our transport state to the new channel. impl->SetIceRole(ice_role_); impl->SetIceTiebreaker(tiebreaker_); - if (local_description_) { - ApplyLocalTransportDescription_w(impl); - if (remote_description_) { - ApplyRemoteTransportDescription_w(impl); - ApplyNegotiatedTransportDescription_w(impl); - } - } + // TODO(ronghuawu): Change CreateChannel_w to be able to return error since + // below Apply**Description_w calls can fail. + if (local_description_) + ApplyLocalTransportDescription_w(impl, NULL); + if (remote_description_) + ApplyRemoteTransportDescription_w(impl, NULL); + if (local_description_ && remote_description_) + ApplyNegotiatedTransportDescription_w(impl, NULL); impl->SignalReadableState.connect(this, &Transport::OnChannelReadableState); impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState); @@ -192,6 +244,8 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) { impl->SignalCandidatesAllocationDone.connect( this, &Transport::OnChannelCandidatesAllocationDone); impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict); + impl->SignalConnectionRemoved.connect( + this, &Transport::OnChannelConnectionRemoved); if (connect_requested_) { impl->Connect(); @@ -276,7 +330,7 @@ void Transport::ConnectChannels_w() { talk_base::CreateRandomString(ICE_PWD_LENGTH), ICEMODE_FULL, CONNECTIONROLE_NONE, NULL, Candidates()); - SetLocalTransportDescription_w(desc, CA_OFFER); + SetLocalTransportDescription_w(desc, CA_OFFER, NULL); } CallChannels_w(&TransportChannelImpl::Connect); @@ -367,6 +421,12 @@ bool Transport::VerifyCandidate(const Candidate& cand, std::string* error) { // Disallow all ports below 1024, except for 80 and 443 on public addresses. int port = cand.address().port(); + if (port == 0) { + // Expected for active-only candidates per + // http://tools.ietf.org/html/rfc6544#section-4.5 so no error. + *error = ""; + return false; + } if (port < 1024) { if ((port != 80) && (port != 443)) { *error = "candidate has port below 1024, but not 80 or 443"; @@ -459,6 +519,8 @@ void Transport::OnChannelReadableState_s() { void Transport::OnChannelWritableState(TransportChannel* channel) { ASSERT(worker_thread()->IsCurrent()); signaling_thread()->Post(this, MSG_WRITESTATE, NULL); + + MaybeCompleted_w(); } void Transport::OnChannelWritableState_s() { @@ -574,6 +636,8 @@ void Transport::OnChannelCandidatesAllocationDone( return; } signaling_thread_->Post(this, MSG_CANDIDATEALLOCATIONCOMPLETE); + + MaybeCompleted_w(); } void Transport::OnChannelCandidatesAllocationDone_s() { @@ -586,6 +650,51 @@ void Transport::OnRoleConflict(TransportChannelImpl* channel) { signaling_thread_->Post(this, MSG_ROLECONFLICT); } +void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) { + ASSERT(worker_thread()->IsCurrent()); + MaybeCompleted_w(); + + // Check if the state is now Failed. + // Failed is only available in the Controlling ICE role. + if (channel->GetIceRole() != ICEROLE_CONTROLLING) { + return; + } + + ChannelMap::iterator iter = channels_.find(channel->component()); + ASSERT(iter != channels_.end()); + // Failed can only occur after candidate allocation has stopped. + if (!iter->second.candidates_allocated()) { + return; + } + + size_t connections = channel->GetConnectionCount(); + if (connections == 0) { + // A Transport has failed if any of its channels have no remaining + // connections. + signaling_thread_->Post(this, MSG_FAILED); + } +} + +void Transport::MaybeCompleted_w() { + ASSERT(worker_thread()->IsCurrent()); + + // A Transport's ICE process is completed if all of its channels are writable, + // have finished allocating candidates, and have pruned all but one of their + // connections. + ChannelMap::const_iterator iter; + for (iter = channels_.begin(); iter != channels_.end(); ++iter) { + const TransportChannelImpl* channel = iter->second.get(); + if (!(channel->writable() && + channel->GetConnectionCount() == 1 && + channel->GetIceRole() == ICEROLE_CONTROLLING && + iter->second.candidates_allocated())) { + return; + } + } + + signaling_thread_->Post(this, MSG_COMPLETED); +} + void Transport::SetIceRole_w(IceRole role) { talk_base::CritScope cs(&crit_); ice_role_ = role; @@ -606,63 +715,95 @@ void Transport::SetRemoteIceMode_w(IceMode mode) { } bool Transport::SetLocalTransportDescription_w( - const TransportDescription& desc, ContentAction action) { + const TransportDescription& desc, + ContentAction action, + std::string* error_desc) { bool ret = true; talk_base::CritScope cs(&crit_); - local_description_.reset(new TransportDescription(desc)); + if (!VerifyIceParams(desc)) { + return BadTransportDescription("Invalid ice-ufrag or ice-pwd length", + error_desc); + } + + local_description_.reset(new TransportDescription(desc)); for (ChannelMap::iterator iter = channels_.begin(); iter != channels_.end(); ++iter) { - ret &= ApplyLocalTransportDescription_w(iter->second.get()); + ret &= ApplyLocalTransportDescription_w(iter->second.get(), error_desc); } if (!ret) return false; // If PRANSWER/ANSWER is set, we should decide transport protocol type. if (action == CA_PRANSWER || action == CA_ANSWER) { - ret &= NegotiateTransportDescription_w(action); + ret &= NegotiateTransportDescription_w(action, error_desc); } return ret; } bool Transport::SetRemoteTransportDescription_w( - const TransportDescription& desc, ContentAction action) { + const TransportDescription& desc, + ContentAction action, + std::string* error_desc) { bool ret = true; talk_base::CritScope cs(&crit_); - remote_description_.reset(new TransportDescription(desc)); + if (!VerifyIceParams(desc)) { + return BadTransportDescription("Invalid ice-ufrag or ice-pwd length", + error_desc); + } + + remote_description_.reset(new TransportDescription(desc)); for (ChannelMap::iterator iter = channels_.begin(); iter != channels_.end(); ++iter) { - ret &= ApplyRemoteTransportDescription_w(iter->second.get()); + ret &= ApplyRemoteTransportDescription_w(iter->second.get(), error_desc); } // If PRANSWER/ANSWER is set, we should decide transport protocol type. if (action == CA_PRANSWER || action == CA_ANSWER) { - ret = NegotiateTransportDescription_w(CA_OFFER); + ret = NegotiateTransportDescription_w(CA_OFFER, error_desc); } return ret; } -bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch) { +bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch, + std::string* error_desc) { + // If existing protocol_type is HYBRID, we may have not chosen the final + // protocol type, so update the channel protocol type from the + // local description. Otherwise, skip updating the protocol type. + // We check for HYBRID to avoid accidental changes; in the case of a + // session renegotiation, the new offer will have the google-ice ICE option, + // so we need to make sure we don't switch back from ICE mode to HYBRID + // when this happens. + // There are some other ways we could have solved this, but this is the + // simplest. The ultimate solution will be to get rid of GICE altogether. + IceProtocolType protocol_type; + if (ch->GetIceProtocolType(&protocol_type) && + protocol_type == ICEPROTO_HYBRID) { + ch->SetIceProtocolType( + TransportProtocolFromDescription(local_description())); + } ch->SetIceCredentials(local_description_->ice_ufrag, local_description_->ice_pwd); return true; } -bool Transport::ApplyRemoteTransportDescription_w(TransportChannelImpl* ch) { +bool Transport::ApplyRemoteTransportDescription_w(TransportChannelImpl* ch, + std::string* error_desc) { ch->SetRemoteIceCredentials(remote_description_->ice_ufrag, remote_description_->ice_pwd); return true; } bool Transport::ApplyNegotiatedTransportDescription_w( - TransportChannelImpl* channel) { + TransportChannelImpl* channel, std::string* error_desc) { channel->SetIceProtocolType(protocol_); channel->SetRemoteIceMode(remote_ice_mode_); return true; } -bool Transport::NegotiateTransportDescription_w(ContentAction local_role) { +bool Transport::NegotiateTransportDescription_w(ContentAction local_role, + std::string* error_desc) { // TODO(ekr@rtfm.com): This is ICE-specific stuff. Refactor into // P2PTransport. const TransportDescription* offer; @@ -690,7 +831,12 @@ bool Transport::NegotiateTransportDescription_w(ContentAction local_role) { // answer must be treated as error. if ((offer_proto == ICEPROTO_GOOGLE || offer_proto == ICEPROTO_RFC5245) && (offer_proto != answer_proto)) { - return false; + std::ostringstream desc; + desc << "Offer and answer protocol mismatch: " + << IceProtoToString(offer_proto) + << " vs " + << IceProtoToString(answer_proto); + return BadTransportDescription(desc.str(), error_desc); } protocol_ = answer_proto == ICEPROTO_HYBRID ? ICEPROTO_GOOGLE : answer_proto; @@ -712,7 +858,7 @@ bool Transport::NegotiateTransportDescription_w(ContentAction local_role) { for (ChannelMap::iterator iter = channels_.begin(); iter != channels_.end(); ++iter) { - if (!ApplyNegotiatedTransportDescription_w(iter->second.get())) + if (!ApplyNegotiatedTransportDescription_w(iter->second.get(), error_desc)) return false; } return true; @@ -759,6 +905,12 @@ void Transport::OnMessage(talk_base::Message* msg) { case MSG_ROLECONFLICT: SignalRoleConflict(); break; + case MSG_COMPLETED: + SignalCompleted(this); + break; + case MSG_FAILED: + SignalFailed(this); + break; } } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transport.h b/chromium/third_party/libjingle/source/talk/p2p/base/transport.h index f9e9d887457..7f460d1127b 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transport.h @@ -187,6 +187,8 @@ struct TransportStats { TransportChannelStatsList channel_stats; }; +bool BadTransportDescription(const std::string& desc, std::string* err_desc); + class Transport : public talk_base::MessageHandler, public sigslot::has_slots<> { public: @@ -234,6 +236,8 @@ class Transport : public talk_base::MessageHandler, } sigslot::signal1<Transport*> SignalReadableState; sigslot::signal1<Transport*> SignalWritableState; + sigslot::signal1<Transport*> SignalCompleted; + sigslot::signal1<Transport*> SignalFailed; // Returns whether the client has requested the channels to connect. bool connect_requested() const { return connect_requested_; } @@ -270,11 +274,13 @@ class Transport : public talk_base::MessageHandler, // Set the local TransportDescription to be used by TransportChannels. // This should be called before ConnectChannels(). bool SetLocalTransportDescription(const TransportDescription& description, - ContentAction action); + ContentAction action, + std::string* error_desc); // Set the remote TransportDescription to be used by TransportChannels. bool SetRemoteTransportDescription(const TransportDescription& description, - ContentAction action); + ContentAction action, + std::string* error_desc); // Tells all current and future channels to start connecting. When the first // channel begins connecting, the following signal is raised. @@ -362,25 +368,27 @@ class Transport : public talk_base::MessageHandler, // Pushes down the transport parameters from the local description, such // as the ICE ufrag and pwd. // Derived classes can override, but must call the base as well. - virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* - channel); + virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel, + std::string* error_desc); // Pushes down remote ice credentials from the remote description to the // transport channel. - virtual bool ApplyRemoteTransportDescription_w(TransportChannelImpl* ch); + virtual bool ApplyRemoteTransportDescription_w(TransportChannelImpl* ch, + std::string* error_desc); // Negotiates the transport parameters based on the current local and remote // transport description, such at the version of ICE to use, and whether DTLS // should be activated. // Derived classes can negotiate their specific parameters here, but must call // the base as well. - virtual bool NegotiateTransportDescription_w(ContentAction local_role); + virtual bool NegotiateTransportDescription_w(ContentAction local_role, + std::string* error_desc); // Pushes down the transport parameters obtained via negotiation. // Derived classes can set their specific parameters here, but must call the // base as well. virtual bool ApplyNegotiatedTransportDescription_w( - TransportChannelImpl* channel); + TransportChannelImpl* channel, std::string* error_desc); virtual bool GetSslRole_w(talk_base::SSLRole* ssl_role) const { return false; @@ -435,6 +443,8 @@ class Transport : public talk_base::MessageHandler, void OnChannelCandidatesAllocationDone(TransportChannelImpl* channel); // Called when there is ICE role change. void OnRoleConflict(TransportChannelImpl* channel); + // Called when the channel removes a connection. + void OnChannelConnectionRemoved(TransportChannelImpl* channel); // Dispatches messages to the appropriate handler (below). void OnMessage(talk_base::Message* msg); @@ -468,12 +478,16 @@ class Transport : public talk_base::MessageHandler, void SetIceRole_w(IceRole role); void SetRemoteIceMode_w(IceMode mode); bool SetLocalTransportDescription_w(const TransportDescription& desc, - ContentAction action); + ContentAction action, + std::string* error_desc); bool SetRemoteTransportDescription_w(const TransportDescription& desc, - ContentAction action); + ContentAction action, + std::string* error_desc); bool GetStats_w(TransportStats* infos); bool GetRemoteCertificate_w(talk_base::SSLCertificate** cert); + // Sends SignalCompleted if we are now in that state. + void MaybeCompleted_w(); talk_base::Thread* signaling_thread_; talk_base::Thread* worker_thread_; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transport_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/transport_unittest.cc index e3b7badfff7..b91b1a0d0aa 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transport_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transport_unittest.cc @@ -60,8 +60,12 @@ class TransportTest : public testing::Test, transport_(new FakeTransport( thread_, thread_, "test content name", NULL)), channel_(NULL), - connecting_signalled_(false) { + connecting_signalled_(false), + completed_(false), + failed_(false) { transport_->SignalConnecting.connect(this, &TransportTest::OnConnecting); + transport_->SignalCompleted.connect(this, &TransportTest::OnCompleted); + transport_->SignalFailed.connect(this, &TransportTest::OnFailed); } ~TransportTest() { transport_->DestroyAllChannels(); @@ -83,11 +87,19 @@ class TransportTest : public testing::Test, void OnConnecting(Transport* transport) { connecting_signalled_ = true; } + void OnCompleted(Transport* transport) { + completed_ = true; + } + void OnFailed(Transport* transport) { + failed_ = true; + } talk_base::Thread* thread_; talk_base::scoped_ptr<FakeTransport> transport_; FakeTransportChannel* channel_; bool connecting_signalled_; + bool completed_; + bool failed_; }; class FakeCandidateTranslator : public cricket::CandidateTranslator { @@ -147,7 +159,8 @@ TEST_F(TransportTest, TestChannelIceParameters) { cricket::TransportDescription local_desc( cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc, - cricket::CA_OFFER)); + cricket::CA_OFFER, + NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); EXPECT_TRUE(SetupChannel()); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); @@ -158,7 +171,8 @@ TEST_F(TransportTest, TestChannelIceParameters) { cricket::TransportDescription remote_desc( cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc, - cricket::CA_ANSWER)); + cricket::CA_ANSWER, + NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); EXPECT_EQ(99U, channel_->IceTiebreaker()); EXPECT_EQ(cricket::ICEMODE_FULL, channel_->remote_ice_mode()); @@ -170,6 +184,43 @@ TEST_F(TransportTest, TestChannelIceParameters) { EXPECT_EQ(kIcePwd1, channel_->remote_ice_pwd()); } +// This test verifies that the Completed and Failed states can be reached. +TEST_F(TransportTest, TestChannelCompletedAndFailed) { + transport_->SetIceRole(cricket::ICEROLE_CONTROLLING); + cricket::TransportDescription local_desc( + cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1); + ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc, + cricket::CA_OFFER, + NULL)); + EXPECT_TRUE(SetupChannel()); + + cricket::TransportDescription remote_desc( + cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1); + ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc, + cricket::CA_ANSWER, + NULL)); + + channel_->SetConnectionCount(2); + channel_->SignalCandidatesAllocationDone(channel_); + channel_->SetWritable(true); + EXPECT_TRUE_WAIT(transport_->all_channels_writable(), 100); + // ICE is not yet completed because there is still more than one connection. + EXPECT_FALSE(completed_); + EXPECT_FALSE(failed_); + + // When the connection count drops to 1, SignalCompleted should be emitted, + // and completed() should be true. + channel_->SetConnectionCount(1); + EXPECT_TRUE_WAIT(completed_, 100); + completed_ = false; + + // When the connection count drops to 0, SignalFailed should be emitted, and + // completed() should be false. + channel_->SetConnectionCount(0); + EXPECT_TRUE_WAIT(failed_, 100); + EXPECT_FALSE(completed_); +} + // Tests channel role is reversed after receiving ice-lite from remote. TEST_F(TransportTest, TestSetRemoteIceLiteInOffer) { transport_->SetIceRole(cricket::ICEROLE_CONTROLLED); @@ -178,11 +229,13 @@ TEST_F(TransportTest, TestSetRemoteIceLiteInOffer) { kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS, NULL, cricket::Candidates()); ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc, - cricket::CA_OFFER)); + cricket::CA_OFFER, + NULL)); cricket::TransportDescription local_desc( cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc, - cricket::CA_ANSWER)); + cricket::CA_ANSWER, + NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); EXPECT_TRUE(SetupChannel()); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); @@ -195,7 +248,8 @@ TEST_F(TransportTest, TestSetRemoteIceLiteInAnswer) { cricket::TransportDescription local_desc( cricket::NS_JINGLE_ICE_UDP, kIceUfrag1, kIcePwd1); ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc, - cricket::CA_OFFER)); + cricket::CA_OFFER, + NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_->ice_role()); EXPECT_TRUE(SetupChannel()); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); @@ -206,7 +260,8 @@ TEST_F(TransportTest, TestSetRemoteIceLiteInAnswer) { kIceUfrag1, kIcePwd1, cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_NONE, NULL, cricket::Candidates()); ASSERT_TRUE(transport_->SetRemoteTransportDescription(remote_desc, - cricket::CA_ANSWER)); + cricket::CA_ANSWER, + NULL)); EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel_->GetIceRole()); // After receiving remote description with ICEMODE_LITE, channel should // have mode set to ICEMODE_LITE. diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannel.h b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannel.h index 47ba990f35f..c548c1c8c1e 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannel.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannel.h @@ -83,7 +83,7 @@ class TransportChannel : public sigslot::has_slots<> { // Attempts to send the given packet. The return value is < 0 on failure. // TODO: Remove the default argument once channel code is updated. virtual int SendPacket(const char* data, size_t len, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, int flags = 0) = 0; // Sets a socket option on this channel. Note that not all options are diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelimpl.h b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelimpl.h index d8432b73233..25c3121886f 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelimpl.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelimpl.h @@ -53,7 +53,9 @@ class TransportChannelImpl : public TransportChannel { virtual IceRole GetIceRole() const = 0; virtual void SetIceRole(IceRole role) = 0; virtual void SetIceTiebreaker(uint64 tiebreaker) = 0; + virtual size_t GetConnectionCount() const = 0; // To toggle G-ICE/ICE. + virtual bool GetIceProtocolType(IceProtocolType* type) const = 0; virtual void SetIceProtocolType(IceProtocolType type) = 0; // SetIceCredentials only need to be implemented by the ICE // transport channels. Non-ICE transport channels can just ignore. @@ -113,6 +115,10 @@ class TransportChannelImpl : public TransportChannel { // agents. sigslot::signal1<TransportChannelImpl*> SignalRoleConflict; + // Emitted whenever the number of connections available to the transport + // channel decreases. + sigslot::signal1<TransportChannelImpl*> SignalConnectionRemoved; + private: DISALLOW_EVIL_CONSTRUCTORS(TransportChannelImpl); }; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc index 0d8cace2a86..fdcc509d34c 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc @@ -58,7 +58,6 @@ void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) { ASSERT(talk_base::Thread::Current() == worker_thread_); if (impl == impl_) { - ASSERT(false); // Ignore if the |impl| has already been set. LOG(LS_WARNING) << "Ignored TransportChannelProxy::SetImplementation call " << "with a same impl as the existing one."; @@ -102,14 +101,14 @@ void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) { } int TransportChannelProxy::SendPacket(const char* data, size_t len, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, int flags) { ASSERT(talk_base::Thread::Current() == worker_thread_); // Fail if we don't have an impl yet. if (!impl_) { return -1; } - return impl_->SendPacket(data, len, dscp, flags); + return impl_->SendPacket(data, len, options, flags); } int TransportChannelProxy::SetOption(talk_base::Socket::Option opt, int value) { diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h index 196d0f6cfab..cb38c7bc495 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h @@ -64,7 +64,7 @@ class TransportChannelProxy : public TransportChannel, // Implementation of the TransportChannel interface. These simply forward to // the implementation. virtual int SendPacket(const char* data, size_t len, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, int flags); virtual int SetOption(talk_base::Socket::Option opt, int value); virtual int GetError(); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/turnport.cc b/chromium/third_party/libjingle/source/talk/p2p/base/turnport.cc index 01d7f9c8998..195e9707bd6 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/turnport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/turnport.cc @@ -43,7 +43,6 @@ namespace cricket { // TODO(juberti): Move to stun.h when relay messages have been renamed. static const int TURN_ALLOCATE_REQUEST = STUN_ALLOCATE_REQUEST; -static const int TURN_ALLOCATE_ERROR_RESPONSE = STUN_ALLOCATE_ERROR_RESPONSE; // TODO(juberti): Extract to turnmessage.h static const int TURN_DEFAULT_PORT = 3478; @@ -153,7 +152,7 @@ class TurnEntry : public sigslot::has_slots<> { // Sends a packet to the given destination address. // This will wrap the packet in STUN if necessary. int Send(const void* data, size_t size, bool payload, - talk_base::DiffServCodePoint dscp); + const talk_base::PacketOptions& options); void OnCreatePermissionSuccess(); void OnCreatePermissionError(StunMessage* response, int code); @@ -172,6 +171,27 @@ class TurnEntry : public sigslot::has_slots<> { TurnPort::TurnPort(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, talk_base::Network* network, + talk_base::AsyncPacketSocket* socket, + const std::string& username, + const std::string& password, + const ProtocolAddress& server_address, + const RelayCredentials& credentials) + : Port(thread, factory, network, socket->GetLocalAddress().ipaddr(), + username, password), + server_address_(server_address), + credentials_(credentials), + socket_(socket), + resolver_(NULL), + error_(0), + request_manager_(thread), + next_channel_number_(TURN_CHANNEL_NUMBER_START), + connected_(false) { + request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket); +} + +TurnPort::TurnPort(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, + talk_base::Network* network, const talk_base::IPAddress& ip, int min_port, int max_port, const std::string& username, @@ -182,6 +202,7 @@ TurnPort::TurnPort(talk_base::Thread* thread, username, password), server_address_(server_address), credentials_(credentials), + socket_(NULL), resolver_(NULL), error_(0), request_manager_(thread), @@ -195,6 +216,12 @@ TurnPort::~TurnPort() { while (!entries_.empty()) { DestroyEntry(entries_.front()->address()); } + if (resolver_) { + resolver_->Destroy(false); + } + if (!SharedSocket()) { + delete socket_; + } } void TurnPort::PrepareAddress() { @@ -225,19 +252,19 @@ void TurnPort::PrepareAddress() { LOG_J(LS_INFO, this) << "Trying to connect to TURN server via " << ProtoToString(server_address_.proto) << " @ " << server_address_.address.ToSensitiveString(); - if (server_address_.proto == PROTO_UDP) { - socket_.reset(socket_factory()->CreateUdpSocket( - talk_base::SocketAddress(ip(), 0), min_port(), max_port())); + if (server_address_.proto == PROTO_UDP && !SharedSocket()) { + socket_ = socket_factory()->CreateUdpSocket( + talk_base::SocketAddress(ip(), 0), min_port(), max_port()); } else if (server_address_.proto == PROTO_TCP) { + ASSERT(!SharedSocket()); int opts = talk_base::PacketSocketFactory::OPT_STUN; // If secure bit is enabled in server address, use TLS over TCP. if (server_address_.secure) { opts |= talk_base::PacketSocketFactory::OPT_TLS; } - - socket_.reset(socket_factory()->CreateClientTcpSocket( + socket_ = socket_factory()->CreateClientTcpSocket( talk_base::SocketAddress(ip(), 0), server_address_.address, - proxy(), user_agent(), opts)); + proxy(), user_agent(), opts); } if (!socket_) { @@ -251,7 +278,11 @@ void TurnPort::PrepareAddress() { socket_->SetOption(iter->first, iter->second); } - socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket); + if (!SharedSocket()) { + // If socket is shared, AllocationSequence will receive the packet. + socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket); + } + socket_->SignalReadyToSend.connect(this, &TurnPort::OnReadyToSend); if (server_address_.proto == PROTO_TCP) { @@ -266,6 +297,18 @@ void TurnPort::PrepareAddress() { } void TurnPort::OnSocketConnect(talk_base::AsyncPacketSocket* socket) { + ASSERT(server_address_.proto == PROTO_TCP); + // Do not use this port if the socket bound to a different address than + // the one we asked for. This is seen in Chrome, where TCP sockets cannot be + // given a binding address, and the platform is expected to pick the + // correct local address. + if (socket->GetLocalAddress().ipaddr() != ip()) { + LOG(LS_WARNING) << "Socket is bound to a different address then the " + << "local port. Discarding TURN port."; + OnAllocateError(); + return; + } + LOG(LS_INFO) << "TurnPort connected to " << socket->GetRemoteAddress() << " using tcp."; SendRequest(new TurnAllocateRequest(this), 0); @@ -292,23 +335,21 @@ Connection* TurnPort::CreateConnection(const Candidate& address, // Create an entry, if needed, so we can get our permissions set up correctly. CreateEntry(address.address()); - // TODO(juberti): The '0' index will need to change if we start gathering STUN - // candidates on this port. - ProxyConnection* conn = new ProxyConnection(this, 0, address); - conn->SignalDestroyed.connect(this, &TurnPort::OnConnectionDestroyed); - AddConnection(conn); - return conn; + // A TURN port will have two candiates, STUN and TURN. STUN may not + // present in all cases. If present stun candidate will be added first + // and TURN candidate later. + for (size_t index = 0; index < Candidates().size(); ++index) { + if (Candidates()[index].type() == RELAY_PORT_TYPE) { + ProxyConnection* conn = new ProxyConnection(this, index, address); + conn->SignalDestroyed.connect(this, &TurnPort::OnConnectionDestroyed); + AddConnection(conn); + return conn; + } + } + return NULL; } int TurnPort::SetOption(talk_base::Socket::Option opt, int value) { - // DSCP option is not passed to the socket. - // TODO(mallinath) - After we have the support on socket, - // remove this specialization. - if (opt == talk_base::Socket::OPT_DSCP) { - SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value)); - return 0; - } - if (!socket_) { // If socket is not created yet, these options will be applied during socket // creation. @@ -319,8 +360,14 @@ int TurnPort::SetOption(talk_base::Socket::Option opt, int value) { } int TurnPort::GetOption(talk_base::Socket::Option opt, int* value) { - if (!socket_) - return -1; + if (!socket_) { + SocketOptionsMap::const_iterator it = socket_options_.find(opt); + if (it == socket_options_.end()) { + return -1; + } + *value = it->second; + return 0; + } return socket_->GetOption(opt, value); } @@ -331,7 +378,7 @@ int TurnPort::GetError() { int TurnPort::SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload) { // Try to find an entry for this specific address; we should have one. TurnEntry* entry = FindEntry(addr); @@ -346,7 +393,7 @@ int TurnPort::SendTo(const void* data, size_t size, } // Send the actual contents to the server using the usual mechanism. - int sent = entry->Send(data, size, payload, dscp); + int sent = entry->Send(data, size, payload, options); if (sent <= 0) { return SOCKET_ERROR; } @@ -360,7 +407,7 @@ void TurnPort::OnReadPacket( talk_base::AsyncPacketSocket* socket, const char* data, size_t size, const talk_base::SocketAddress& remote_addr, const talk_base::PacketTime& packet_time) { - ASSERT(socket == socket_.get()); + ASSERT(socket == socket_); ASSERT(remote_addr == server_address_.address); // The message must be at least the size of a channel header. @@ -407,35 +454,51 @@ void TurnPort::ResolveTurnAddress(const talk_base::SocketAddress& address) { void TurnPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) { ASSERT(resolver == resolver_); + // Copy the original server address in |resolved_address|. For TLS based + // sockets we need hostname along with resolved address. + talk_base::SocketAddress resolved_address = server_address_.address; if (resolver_->GetError() != 0 || - !resolver_->GetResolvedAddress(ip().family(), &server_address_.address)) { + !resolver_->GetResolvedAddress(ip().family(), &resolved_address)) { LOG_J(LS_WARNING, this) << "TURN host lookup received error " << resolver_->GetError(); OnAllocateError(); return; } - + // Signal needs both resolved and unresolved address. After signal is sent + // we can copy resolved address back into |server_address_|. + SignalResolvedServerAddress(this, server_address_.address, + resolved_address); + server_address_.address = resolved_address; PrepareAddress(); } void TurnPort::OnSendStunPacket(const void* data, size_t size, StunRequest* request) { - if (Send(data, size, DefaultDscpValue()) < 0) { + talk_base::PacketOptions options(DefaultDscpValue()); + if (Send(data, size, options) < 0) { LOG_J(LS_ERROR, this) << "Failed to send TURN message, err=" << socket_->GetError(); } } void TurnPort::OnStunAddress(const talk_base::SocketAddress& address) { - // For relay, mapped address is rel-addr. - set_related_address(address); + // STUN Port will discover STUN candidate, as it's supplied with first TURN + // server address. + // Why not using this address? - P2PTransportChannel will start creating + // connections after first candidate, which means it could start creating the + // connections before TURN candidate added. For that to handle, we need to + // supply STUN candidate from this port to UDPPort, and TurnPort should have + // handle to UDPPort to pass back the address. } -void TurnPort::OnAllocateSuccess(const talk_base::SocketAddress& address) { +void TurnPort::OnAllocateSuccess(const talk_base::SocketAddress& address, + const talk_base::SocketAddress& stun_address) { connected_ = true; - AddAddress(address, - socket_->GetLocalAddress(), - "udp", + // For relayed candidate, Base is the candidate itself. + AddAddress(address, // Candidate address. + address, // Base address. + stun_address, // Related address. + UDP_PROTOCOL_NAME, RELAY_PORT_TYPE, GetRelayPreference(server_address_.proto, server_address_.secure), true); @@ -577,8 +640,8 @@ void TurnPort::AddRequestAuthInfo(StunMessage* msg) { } int TurnPort::Send(const void* data, size_t len, - talk_base::DiffServCodePoint dscp) { - return socket_->SendTo(data, len, server_address_.address, dscp); + const talk_base::PacketOptions& options) { + return socket_->SendTo(data, len, server_address_.address, options); } void TurnPort::UpdateHash() { @@ -682,8 +745,7 @@ void TurnAllocateRequest::OnResponse(StunMessage* response) { << "attribute in allocate success response"; return; } - - // TODO(mallinath) - Use mapped address for STUN candidate. + // Using XOR-Mapped-Address for stun. port_->OnStunAddress(mapped_attr->GetAddress()); const StunAddressAttribute* relayed_attr = @@ -702,7 +764,8 @@ void TurnAllocateRequest::OnResponse(StunMessage* response) { return; } // Notify the port the allocate succeeded, and schedule a refresh request. - port_->OnAllocateSuccess(relayed_attr->GetAddress()); + port_->OnAllocateSuccess(relayed_attr->GetAddress(), + mapped_attr->GetAddress()); port_->ScheduleRefresh(lifetime_attr->value()); } @@ -911,7 +974,7 @@ void TurnEntry::SendChannelBindRequest(int delay) { } int TurnEntry::Send(const void* data, size_t size, bool payload, - talk_base::DiffServCodePoint dscp) { + const talk_base::PacketOptions& options) { talk_base::ByteBuffer buf; if (state_ != STATE_BOUND) { // If we haven't bound the channel yet, we have to use a Send Indication. @@ -936,7 +999,7 @@ int TurnEntry::Send(const void* data, size_t size, bool payload, buf.WriteUInt16(static_cast<uint16>(size)); buf.WriteBytes(reinterpret_cast<const char*>(data), size); } - return port_->Send(buf.Data(), buf.Length(), dscp); + return port_->Send(buf.Data(), buf.Length(), options); } void TurnEntry::OnCreatePermissionSuccess() { diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/turnport.h b/chromium/third_party/libjingle/source/talk/p2p/base/turnport.h index e380a8912f7..2f5e8c4ab1b 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/turnport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/turnport.h @@ -52,6 +52,18 @@ class TurnPort : public Port { static TurnPort* Create(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, talk_base::Network* network, + talk_base::AsyncPacketSocket* socket, + const std::string& username, // ice username. + const std::string& password, // ice password. + const ProtocolAddress& server_address, + const RelayCredentials& credentials) { + return new TurnPort(thread, factory, network, socket, + username, password, server_address, credentials); + } + + static TurnPort* Create(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, + talk_base::Network* network, const talk_base::IPAddress& ip, int min_port, int max_port, const std::string& username, // ice username. @@ -74,15 +86,24 @@ class TurnPort : public Port { const Candidate& c, PortInterface::CandidateOrigin origin); virtual int SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, - talk_base::DiffServCodePoint dscp, + const talk_base::PacketOptions& options, bool payload); virtual int SetOption(talk_base::Socket::Option opt, int value); virtual int GetOption(talk_base::Socket::Option opt, int* value); virtual int GetError(); - virtual void OnReadPacket( + + virtual bool HandleIncomingPacket( talk_base::AsyncPacketSocket* socket, const char* data, size_t size, const talk_base::SocketAddress& remote_addr, - const talk_base::PacketTime& packet_time); + const talk_base::PacketTime& packet_time) { + OnReadPacket(socket, data, size, remote_addr, packet_time); + return true; + } + virtual void OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); + virtual void OnReadyToSend(talk_base::AsyncPacketSocket* socket); void OnSocketConnect(talk_base::AsyncPacketSocket* socket); @@ -92,6 +113,13 @@ class TurnPort : public Port { const std::string& hash() const { return hash_; } const std::string& nonce() const { return nonce_; } + // Signal with resolved server address. + // Parameters are port, server address and resolved server address. + // This signal will be sent only if server address is resolved successfully. + sigslot::signal3<TurnPort*, + const talk_base::SocketAddress&, + const talk_base::SocketAddress&> SignalResolvedServerAddress; + // This signal is only for testing purpose. sigslot::signal3<TurnPort*, const talk_base::SocketAddress&, int> SignalCreatePermissionResult; @@ -100,6 +128,15 @@ class TurnPort : public Port { TurnPort(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, talk_base::Network* network, + talk_base::AsyncPacketSocket* socket, + const std::string& username, + const std::string& password, + const ProtocolAddress& server_address, + const RelayCredentials& credentials); + + TurnPort(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, + talk_base::Network* network, const talk_base::IPAddress& ip, int min_port, int max_port, const std::string& username, @@ -131,7 +168,8 @@ class TurnPort : public Port { // Stun address from allocate success response. // Currently used only for testing. void OnStunAddress(const talk_base::SocketAddress& address); - void OnAllocateSuccess(const talk_base::SocketAddress& address); + void OnAllocateSuccess(const talk_base::SocketAddress& address, + const talk_base::SocketAddress& stun_address); void OnAllocateError(); void OnAllocateRequestTimeout(); @@ -145,7 +183,8 @@ class TurnPort : public Port { bool ScheduleRefresh(int lifetime); void SendRequest(StunRequest* request, int delay); - int Send(const void* data, size_t size, talk_base::DiffServCodePoint dscp); + int Send(const void* data, size_t size, + const talk_base::PacketOptions& options); void UpdateHash(); bool UpdateNonce(StunMessage* response); @@ -159,7 +198,7 @@ class TurnPort : public Port { ProtocolAddress server_address_; RelayCredentials credentials_; - talk_base::scoped_ptr<talk_base::AsyncPacketSocket> socket_; + talk_base::AsyncPacketSocket* socket_; SocketOptionsMap socket_options_; talk_base::AsyncResolverInterface* resolver_; int error_; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/turnport_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/turnport_unittest.cc index d559894ac56..12a19aa4dba 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/turnport_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/turnport_unittest.cc @@ -24,6 +24,9 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if defined(POSIX) +#include <dirent.h> +#endif #include "talk/base/asynctcpsocket.h" #include "talk/base/buffer.h" @@ -71,7 +74,7 @@ static const char kIcePwd1[] = "TESTICEPWD00000000000001"; static const char kIcePwd2[] = "TESTICEPWD00000000000002"; static const char kTurnUsername[] = "test"; static const char kTurnPassword[] = "test"; -static const int kTimeout = 1000; +static const unsigned int kTimeout = 1000; static const cricket::ProtocolAddress kTurnUdpProtoAddr( kTurnUdpIntAddr, cricket::PROTO_UDP); @@ -80,8 +83,26 @@ static const cricket::ProtocolAddress kTurnTcpProtoAddr( static const cricket::ProtocolAddress kTurnUdpIPv6ProtoAddr( kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); +static const unsigned int MSG_TESTFINISH = 0; + +#if defined(LINUX) +static int GetFDCount() { + struct dirent *dp; + int fd_count = 0; + DIR *dir = opendir("/proc/self/fd/"); + while ((dp = readdir(dir)) != NULL) { + if (dp->d_name[0] == '.') + continue; + ++fd_count; + } + closedir(dir); + return fd_count; +} +#endif + class TurnPortTest : public testing::Test, - public sigslot::has_slots<> { + public sigslot::has_slots<>, + public talk_base::MessageHandler { public: TurnPortTest() : main_(talk_base::Thread::Current()), @@ -95,10 +116,17 @@ class TurnPortTest : public testing::Test, turn_error_(false), turn_unknown_address_(false), turn_create_permission_success_(false), - udp_ready_(false) { + udp_ready_(false), + test_finish_(false) { network_.AddIP(talk_base::IPAddress(INADDR_ANY)); } + virtual void OnMessage(talk_base::Message* msg) { + ASSERT(msg->message_id == MSG_TESTFINISH); + if (msg->message_id == MSG_TESTFINISH) + test_finish_ = true; + } + void OnTurnPortComplete(Port* port) { turn_ready_ = true; } @@ -129,7 +157,13 @@ class TurnPortTest : public testing::Test, const talk_base::PacketTime& packet_time) { udp_packets_.push_back(talk_base::Buffer(data, size)); } - + void OnSocketReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { + turn_port_->HandleIncomingPacket(socket, data, size, remote_addr, + packet_time); + } talk_base::AsyncSocket* CreateServerSocket(const SocketAddress addr) { talk_base::AsyncSocket* socket = ss_->CreateAsyncSocket(SOCK_STREAM); EXPECT_GE(socket->Bind(addr), 0); @@ -151,6 +185,39 @@ class TurnPortTest : public testing::Test, local_address.ipaddr(), 0, 0, kIceUfrag1, kIcePwd1, server_address, credentials)); + // Set ICE protocol type to ICEPROTO_RFC5245, as port by default will be + // in Hybrid mode. Protocol type is necessary to send correct type STUN ping + // messages. + // This TURN port will be the controlling. + turn_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245); + turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING); + ConnectSignals(); + } + + void CreateSharedTurnPort(const std::string& username, + const std::string& password, + const cricket::ProtocolAddress& server_address) { + ASSERT(server_address.proto == cricket::PROTO_UDP); + + socket_.reset(socket_factory_.CreateUdpSocket( + talk_base::SocketAddress(kLocalAddr1.ipaddr(), 0), 0, 0)); + ASSERT_TRUE(socket_ != NULL); + socket_->SignalReadPacket.connect(this, &TurnPortTest::OnSocketReadPacket); + + cricket::RelayCredentials credentials(username, password); + turn_port_.reset(cricket::TurnPort::Create( + main_, &socket_factory_, &network_, socket_.get(), + kIceUfrag1, kIcePwd1, server_address, credentials)); + // Set ICE protocol type to ICEPROTO_RFC5245, as port by default will be + // in Hybrid mode. Protocol type is necessary to send correct type STUN ping + // messages. + // This TURN port will be the controlling. + turn_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245); + turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING); + ConnectSignals(); + } + + void ConnectSignals() { turn_port_->SignalPortComplete.connect(this, &TurnPortTest::OnTurnPortComplete); turn_port_->SignalPortError.connect(this, @@ -164,6 +231,10 @@ class TurnPortTest : public testing::Test, udp_port_.reset(UDPPort::Create(main_, &socket_factory_, &network_, kLocalAddr2.ipaddr(), 0, 0, kIceUfrag2, kIcePwd2)); + // Set protocol type to RFC5245, as turn port is also in same mode. + // UDP port will be controlled. + udp_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245); + udp_port_->SetIceRole(cricket::ICEROLE_CONTROLLED); udp_port_->SignalPortComplete.connect( this, &TurnPortTest::OnUdpPortComplete); } @@ -234,8 +305,8 @@ class TurnPortTest : public testing::Test, for (size_t j = 0; j < i + 1; ++j) { buf[j] = 0xFF - j; } - conn1->Send(buf, i + 1, talk_base::DSCP_NO_CHANGE); - conn2->Send(buf, i + 1, talk_base::DSCP_NO_CHANGE); + conn1->Send(buf, i + 1, options); + conn2->Send(buf, i + 1, options); main_->ProcessMessages(0); } @@ -256,6 +327,7 @@ class TurnPortTest : public testing::Test, talk_base::SocketServerScope ss_scope_; talk_base::Network network_; talk_base::BasicPacketSocketFactory socket_factory_; + talk_base::scoped_ptr<talk_base::AsyncPacketSocket> socket_; cricket::TestTurnServer turn_server_; talk_base::scoped_ptr<TurnPort> turn_port_; talk_base::scoped_ptr<UDPPort> udp_port_; @@ -264,8 +336,10 @@ class TurnPortTest : public testing::Test, bool turn_unknown_address_; bool turn_create_permission_success_; bool udp_ready_; + bool test_finish_; std::vector<talk_base::Buffer> turn_packets_; std::vector<talk_base::Buffer> udp_packets_; + talk_base::PacketOptions options; }; // Do a normal TURN allocation. @@ -309,6 +383,12 @@ TEST_F(TurnPortTest, TestTurnConnection) { TestTurnConnection(); } +// Similar to above, except that this test will use the shared socket. +TEST_F(TurnPortTest, TestTurnConnectionUsingSharedSocket) { + CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + TestTurnConnection(); +} + // Test that we can establish a TCP connection with TURN server. TEST_F(TurnPortTest, TestTurnTcpConnection) { turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); @@ -378,3 +458,24 @@ TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv6ExtenalIPv4) { EXPECT_NE(0, turn_port_->Candidates()[0].address().port()); } +// This test verifies any FD's are not leaked after TurnPort is destroyed. +// https://code.google.com/p/webrtc/issues/detail?id=2651 +#if defined(LINUX) +TEST_F(TurnPortTest, TestResolverShutdown) { + turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); + int last_fd_count = GetFDCount(); + // Need to supply unresolved address to kick off resolver. + CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, + cricket::ProtocolAddress(talk_base::SocketAddress( + "stun.l.google.com", 3478), cricket::PROTO_UDP)); + turn_port_->PrepareAddress(); + ASSERT_TRUE_WAIT(turn_error_, kTimeout); + EXPECT_TRUE(turn_port_->Candidates().empty()); + turn_port_.reset(); + talk_base::Thread::Current()->Post(this, MSG_TESTFINISH); + // Waiting for above message to be processed. + ASSERT_TRUE_WAIT(test_finish_, kTimeout); + EXPECT_EQ(last_fd_count, GetFDCount()); +} +#endif + diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.cc b/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.cc index 0bd903abe22..4d7f39e8f1c 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.cc @@ -57,8 +57,6 @@ static const size_t kNonceSize = 40; static const size_t TURN_CHANNEL_HEADER_SIZE = 4U; // TODO(mallinath) - Move these to a common place. -static const size_t kMaxPacketSize = 64 * 1024; - inline bool IsTurnChannelData(uint16 msg_type) { // The first two bits of a channel data message are 0b01. return ((msg_type & 0xC000) == 0x4000); @@ -566,8 +564,8 @@ void TurnServer::SendStun(Connection* conn, StunMessage* msg) { void TurnServer::Send(Connection* conn, const talk_base::ByteBuffer& buf) { - conn->socket()->SendTo(buf.Data(), buf.Length(), conn->src(), - talk_base::DSCP_NO_CHANGE); + talk_base::PacketOptions options; + conn->socket()->SendTo(buf.Data(), buf.Length(), conn->src(), options); } void TurnServer::OnAllocationDestroyed(Allocation* allocation) { @@ -940,7 +938,8 @@ void TurnServer::Allocation::SendErrorResponse(const TurnMessage* req, int code, void TurnServer::Allocation::SendExternal(const void* data, size_t size, const talk_base::SocketAddress& peer) { - external_socket_->SendTo(data, size, peer, talk_base::DSCP_NO_CHANGE); + talk_base::PacketOptions options; + external_socket_->SendTo(data, size, peer, options); } void TurnServer::Allocation::OnMessage(talk_base::Message* msg) { diff --git a/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc b/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc index dbc2e3342ee..762726fbb24 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc @@ -56,7 +56,6 @@ const uint32 MSG_SEQUENCEOBJECTS_CREATED = 6; const uint32 MSG_CONFIG_STOP = 7; const uint32 ALLOCATE_DELAY = 250; -const uint32 ALLOCATION_STEP_DELAY = 1 * 1000; const int PHASE_UDP = 0; const int PHASE_RELAY = 1; @@ -65,10 +64,6 @@ const int PHASE_SSLTCP = 3; const int kNumPhases = 4; -// Both these values are in bytes. -const int kLargeSocketSendBufferSize = 128 * 1024; -const int kNormalSocketSendBufferSize = 64 * 1024; - const int SHAKE_MIN_DELAY = 45 * 1000; // 45 seconds const int SHAKE_MAX_DELAY = 90 * 1000; // 90 seconds @@ -107,6 +102,7 @@ class AllocationSequence : public talk_base::MessageHandler, uint32 flags); ~AllocationSequence(); bool Init(); + void Clear(); State state() const { return state_; } @@ -153,6 +149,9 @@ class AllocationSequence : public talk_base::MessageHandler, const talk_base::PacketTime& packet_time); void OnPortDestroyed(PortInterface* port); + void OnResolvedTurnServerAddress( + TurnPort* port, const talk_base::SocketAddress& server_address, + const talk_base::SocketAddress& resolved_server_address); BasicPortAllocatorSession* session_; talk_base::Network* network_; @@ -162,8 +161,10 @@ class AllocationSequence : public talk_base::MessageHandler, uint32 flags_; ProtocolList protocols_; talk_base::scoped_ptr<talk_base::AsyncPacketSocket> udp_socket_; - // Keeping a list of all UDP based ports. - std::deque<Port*> ports; + // There will be only one udp port per AllocationSequence. + UDPPort* udp_port_; + // Keeping a map for turn ports keyed with server addresses. + std::map<talk_base::SocketAddress, Port*> turn_ports_; int phase_; }; @@ -206,13 +207,15 @@ BasicPortAllocator::BasicPortAllocator( stun_address_(stun_address) { RelayServerConfig config(RELAY_GTURN); - if (!relay_address_udp.IsAny()) + if (!relay_address_udp.IsNil()) config.ports.push_back(ProtocolAddress(relay_address_udp, PROTO_UDP)); - if (!relay_address_tcp.IsAny()) + if (!relay_address_tcp.IsNil()) config.ports.push_back(ProtocolAddress(relay_address_tcp, PROTO_TCP)); - if (!relay_address_ssl.IsAny()) + if (!relay_address_ssl.IsNil()) config.ports.push_back(ProtocolAddress(relay_address_ssl, PROTO_SSLTCP)); - AddRelay(config); + + if (!config.ports.empty()) + AddRelay(config); Construct(); } @@ -242,7 +245,6 @@ BasicPortAllocatorSession::BasicPortAllocatorSession( ice_ufrag, ice_pwd, allocator->flags()), allocator_(allocator), network_thread_(NULL), socket_factory_(allocator->socket_factory()), - configuration_done_(false), allocation_started_(false), network_manager_started_(false), running_(false), @@ -257,6 +259,12 @@ BasicPortAllocatorSession::~BasicPortAllocatorSession() { if (network_thread_ != NULL) network_thread_->Clear(this); + for (uint32 i = 0; i < sequences_.size(); ++i) { + // AllocationSequence should clear it's map entry for turn ports before + // ports are destroyed. + sequences_[i]->Clear(); + } + std::vector<PortData>::iterator it; for (it = ports_.begin(); it != ports_.end(); it++) delete it->port(); @@ -490,16 +498,6 @@ void BasicPortAllocatorSession::AddAllocatedPort(Port* port, port->set_send_retransmit_count_attribute((allocator_->flags() & PORTALLOCATOR_ENABLE_STUN_RETRANSMIT_ATTRIBUTE) != 0); - if (content_name().compare(CN_VIDEO) == 0 && - component_ == cricket::ICE_CANDIDATE_COMPONENT_RTP) { - // For video RTP alone, we set send-buffer sizes. This used to be set in the - // engines/channels. - int sendBufSize = (flags() & PORTALLOCATOR_USE_LARGE_SOCKET_SEND_BUFFERS) - ? kLargeSocketSendBufferSize - : kNormalSocketSendBufferSize; - port->SetOption(talk_base::Socket::OPT_SNDBUF, sendBufSize); - } - PortData data(port, seq); ports_.push_back(data); @@ -515,8 +513,6 @@ void BasicPortAllocatorSession::AddAllocatedPort(Port* port, if (prepare_address) port->PrepareAddress(); - if (running_) - port->Start(); } void BasicPortAllocatorSession::OnAllocationSequenceObjectsCreated() { @@ -711,6 +707,7 @@ AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, state_(kInit), flags_(flags), udp_socket_(), + udp_port_(NULL), phase_(0) { } @@ -737,6 +734,11 @@ bool AllocationSequence::Init() { return true; } +void AllocationSequence::Clear() { + udp_port_ = NULL; + turn_ports_.clear(); +} + AllocationSequence::~AllocationSequence() { session_->network_thread()->Clear(this); } @@ -873,18 +875,27 @@ void AllocationSequence::CreateUDPPorts() { } if (port) { - ports.push_back(port); // If shared socket is enabled, STUN candidate will be allocated by the // UDPPort. - if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET) && - !IsFlagSet(PORTALLOCATOR_DISABLE_STUN)) { - ASSERT(config_ && !config_->stun_address.IsNil()); - if (!(config_ && !config_->stun_address.IsNil())) { - LOG(LS_WARNING) - << "AllocationSequence: No STUN server configured, skipping."; - return; + if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET)) { + udp_port_ = port; + + // If STUN is not disabled, setting stun server address to port. + if (!IsFlagSet(PORTALLOCATOR_DISABLE_STUN)) { + // If config has stun_address, use it to get server reflexive candidate + // otherwise use first TURN server which supports UDP. + if (config_ && !config_->stun_address.IsNil()) { + LOG(LS_INFO) << "AllocationSequence: UDPPort will be handling the " + << "STUN candidate generation."; + port->set_server_addr(config_->stun_address); + } else if (config_ && + config_->SupportsProtocol(RELAY_TURN, PROTO_UDP)) { + port->set_server_addr(config_->GetFirstRelayServerAddress( + RELAY_TURN, PROTO_UDP)); + LOG(LS_INFO) << "AllocationSequence: TURN Server address will be " + << " used for generating STUN candidate."; + } } - port->set_server_addr(config_->stun_address); } session_->AddAllocatedPort(port, this, true); @@ -919,8 +930,6 @@ void AllocationSequence::CreateStunPorts() { } if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET)) { - LOG(LS_INFO) << "AllocationSequence: " - << "UDPPort will be handling the STUN candidate generation."; return; } @@ -1010,17 +1019,44 @@ void AllocationSequence::CreateTurnPort(const RelayServerConfig& config) { PortList::const_iterator relay_port; for (relay_port = config.ports.begin(); relay_port != config.ports.end(); ++relay_port) { - TurnPort* port = TurnPort::Create(session_->network_thread(), - session_->socket_factory(), - network_, ip_, - session_->allocator()->min_port(), - session_->allocator()->max_port(), - session_->username(), - session_->password(), - *relay_port, config.credentials); - if (port) { - session_->AddAllocatedPort(port, this, true); + TurnPort* port = NULL; + // Shared socket mode must be enabled only for UDP based ports. Hence + // don't pass shared socket for ports which will create TCP sockets. + if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET) && + relay_port->proto == PROTO_UDP) { + port = TurnPort::Create(session_->network_thread(), + session_->socket_factory(), + network_, udp_socket_.get(), + session_->username(), session_->password(), + *relay_port, config.credentials); + // If we are using shared socket for TURN and udp ports, we need to + // find a way to demux the packets to the correct port when received. + // Mapping against server_address is one way of doing this. When packet + // is received the remote_address will be checked against the map. + // If server address is not resolved, a signal will be sent from the port + // after the address is resolved. The map entry will updated with the + // resolved address when the signal is received from the port. + if ((*relay_port).address.IsUnresolved()) { + // If server address is not resolved then listen for signal from port. + port->SignalResolvedServerAddress.connect( + this, &AllocationSequence::OnResolvedTurnServerAddress); + } + turn_ports_[(*relay_port).address] = port; + // Listen to the port destroyed signal, to allow AllocationSequence to + // remove entrt from it's map. + port->SignalDestroyed.connect(this, &AllocationSequence::OnPortDestroyed); + } else { + port = TurnPort::Create(session_->network_thread(), + session_->socket_factory(), + network_, ip_, + session_->allocator()->min_port(), + session_->allocator()->max_port(), + session_->username(), + session_->password(), + *relay_port, config.credentials); } + ASSERT(port != NULL); + session_->AddAllocatedPort(port, this, true); } } @@ -1029,22 +1065,51 @@ void AllocationSequence::OnReadPacket( const talk_base::SocketAddress& remote_addr, const talk_base::PacketTime& packet_time) { ASSERT(socket == udp_socket_.get()); - for (std::deque<Port*>::iterator iter = ports.begin(); - iter != ports.end(); ++iter) { - // We have only one port in the queue. - // TODO(mallinath) - Add shared socket support to Relay and Turn ports. - if ((*iter)->HandleIncomingPacket( - socket, data, size, remote_addr, packet_time)) { - break; - } + // If the packet is received from one of the TURN server in the config, then + // pass down the packet to that port, otherwise it will be handed down to + // the local udp port. + Port* port = NULL; + std::map<talk_base::SocketAddress, Port*>::iterator iter = + turn_ports_.find(remote_addr); + if (iter != turn_ports_.end()) { + port = iter->second; + } else if (udp_port_) { + port = udp_port_; + } + ASSERT(port != NULL); + if (port) { + port->HandleIncomingPacket(socket, data, size, remote_addr, packet_time); } } void AllocationSequence::OnPortDestroyed(PortInterface* port) { - std::deque<Port*>::iterator iter = - std::find(ports.begin(), ports.end(), port); - ASSERT(iter != ports.end()); - ports.erase(iter); + if (udp_port_ == port) { + udp_port_ = NULL; + } else { + std::map<talk_base::SocketAddress, Port*>::iterator iter; + for (iter = turn_ports_.begin(); iter != turn_ports_.end(); ++iter) { + if (iter->second == port) { + turn_ports_.erase(iter); + break; + } + } + } +} + +void AllocationSequence::OnResolvedTurnServerAddress( + TurnPort* port, const talk_base::SocketAddress& server_address, + const talk_base::SocketAddress& resolved_server_address) { + std::map<talk_base::SocketAddress, Port*>::iterator iter; + iter = turn_ports_.find(server_address); + if (iter == turn_ports_.end()) { + LOG(LS_INFO) << "TurnPort entry is not found in the map."; + return; + } + + ASSERT(iter->second == port); + // Remove old entry and then insert using the resolved address as key. + turn_ports_.erase(iter); + turn_ports_[resolved_server_address] = port; } // PortConfiguration @@ -1062,7 +1127,7 @@ void PortConfiguration::AddRelay(const RelayServerConfig& config) { } bool PortConfiguration::SupportsProtocol( - const RelayServerConfig& relay, ProtocolType type) { + const RelayServerConfig& relay, ProtocolType type) const { PortList::const_iterator relay_port; for (relay_port = relay.ports.begin(); relay_port != relay.ports.end(); @@ -1073,4 +1138,24 @@ bool PortConfiguration::SupportsProtocol( return false; } +bool PortConfiguration::SupportsProtocol(RelayType turn_type, + ProtocolType type) const { + for (size_t i = 0; i < relays.size(); ++i) { + if (relays[i].type == turn_type && + SupportsProtocol(relays[i], type)) + return true; + } + return false; +} + +talk_base::SocketAddress PortConfiguration::GetFirstRelayServerAddress( + RelayType turn_type, ProtocolType type) const { + for (size_t i = 0; i < relays.size(); ++i) { + if (relays[i].type == turn_type && SupportsProtocol(relays[i], type)) { + return relays[i].ports.front().address; + } + } + return talk_base::SocketAddress(); +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.h b/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.h index 1fc6ebe10fb..8a60c425ca6 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.h +++ b/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.h @@ -204,7 +204,6 @@ class BasicPortAllocatorSession : public PortAllocatorSession, talk_base::Thread* network_thread_; talk_base::scoped_ptr<talk_base::PacketSocketFactory> owned_socket_factory_; talk_base::PacketSocketFactory* socket_factory_; - bool configuration_done_; bool allocation_started_; bool network_manager_started_; bool running_; // set when StartGetAllPorts is called @@ -233,8 +232,13 @@ struct PortConfiguration : public talk_base::MessageData { void AddRelay(const RelayServerConfig& config); // Determines whether the given relay server supports the given protocol. - static bool SupportsProtocol(const RelayServerConfig& relay, - ProtocolType type); + bool SupportsProtocol(const RelayServerConfig& relay, + ProtocolType type) const; + bool SupportsProtocol(RelayType turn_type, ProtocolType type) const; + // Helper method returns the first server address for the matching + // RelayType and Protocol type. + talk_base::SocketAddress GetFirstRelayServerAddress( + RelayType turn_type, ProtocolType type) const; }; } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/p2p/client/connectivitychecker.cc b/chromium/third_party/libjingle/source/talk/p2p/client/connectivitychecker.cc index 1075bd6947b..1b599430ed1 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/client/connectivitychecker.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/client/connectivitychecker.cc @@ -22,10 +22,6 @@ namespace cricket { -static const char kSessionTypeVideo[] = - "http://www.google.com/session/video"; -static const char kSessionNameRtp[] = "rtp"; - static const char kDefaultStunHostname[] = "stun.l.google.com"; static const int kDefaultStunPort = 19302; diff --git a/chromium/third_party/libjingle/source/talk/p2p/client/connectivitychecker_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/client/connectivitychecker_unittest.cc index fe1cb9b5391..c62120beeb7 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/client/connectivitychecker_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/client/connectivitychecker_unittest.cc @@ -23,8 +23,6 @@ static const talk_base::SocketAddress kStunAddr("44.44.44.44", 4444); static const talk_base::SocketAddress kRelayAddr("55.55.55.55", 5555); static const talk_base::SocketAddress kProxyAddr("66.66.66.66", 6666); static const talk_base::ProxyType kProxyType = talk_base::PROXY_HTTPS; -static const char kChannelName[] = "rtp_test"; -static const int kComponent = 1; static const char kRelayHost[] = "relay.google.com"; static const char kRelayToken[] = "CAESFwoOb2phQGdvb2dsZS5jb20Q043h47MmGhBTB1rbfIXkhuarDCZe+xF6"; @@ -75,7 +73,7 @@ class FakeStunPort : public StunPort { // Just set external address and signal that we are done. virtual void PrepareAddress() { - AddAddress(kExternalAddr, kExternalAddr, "udp", + AddAddress(kExternalAddr, kExternalAddr, talk_base::SocketAddress(), "udp", STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, true); SignalPortComplete(this); } diff --git a/chromium/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc b/chromium/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc index e54acba5c4d..b881d439f18 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc @@ -41,9 +41,6 @@ namespace { -const uint32 MSG_TIMEOUT = 100; // must not conflict - // with BasicPortAllocator.cpp - // Helper routine to remove whitespace from the ends of a string. void Trim(std::string& str) { size_t first = str.find_first_not_of(" \t\r\n"); diff --git a/chromium/third_party/libjingle/source/talk/p2p/client/portallocator_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/client/portallocator_unittest.cc index 6966e44d04e..44a8f2725d8 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/client/portallocator_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/client/portallocator_unittest.cc @@ -35,6 +35,7 @@ #include "talk/base/network.h" #include "talk/base/physicalsocketserver.h" #include "talk/base/socketaddress.h" +#include "talk/base/ssladapter.h" #include "talk/base/thread.h" #include "talk/base/virtualsocketserver.h" #include "talk/p2p/base/basicpacketsocketfactory.h" @@ -43,6 +44,7 @@ #include "talk/p2p/base/portallocatorsessionproxy.h" #include "talk/p2p/base/testrelayserver.h" #include "talk/p2p/base/teststunserver.h" +#include "talk/p2p/base/testturnserver.h" #include "talk/p2p/client/basicportallocator.h" #include "talk/p2p/client/httpportallocator.h" @@ -52,6 +54,7 @@ using talk_base::Thread; static const SocketAddress kClientAddr("11.11.11.11", 0); static const SocketAddress kClientIPv6Addr( "2401:fa00:4:1000:be30:5bff:fee5:c3", 0); +static const SocketAddress kClientAddr2("22.22.22.22", 0); static const SocketAddress kNatAddr("77.77.77.77", talk_base::NAT_SERVER_PORT); static const SocketAddress kRemoteClientAddr("22.22.22.22", 0); static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT); @@ -61,6 +64,9 @@ static const SocketAddress kRelayTcpIntAddr("99.99.99.2", 5002); static const SocketAddress kRelayTcpExtAddr("99.99.99.3", 5003); static const SocketAddress kRelaySslTcpIntAddr("99.99.99.2", 5004); static const SocketAddress kRelaySslTcpExtAddr("99.99.99.3", 5005); +static const SocketAddress kTurnUdpIntAddr("99.99.99.4", 3478); +static const SocketAddress kTurnTcpIntAddr("99.99.99.5", 3478); +static const SocketAddress kTurnUdpExtAddr("99.99.99.6", 0); // Minimum and maximum port for port range tests. static const int kMinPort = 10000; @@ -74,6 +80,8 @@ static const char kIcePwd0[] = "TESTICEPWD00000000000000"; static const char kContentName[] = "test content"; static const int kDefaultAllocationTimeout = 1000; +static const char kTurnUsername[] = "test"; +static const char kTurnPassword[] = "test"; namespace cricket { @@ -88,9 +96,13 @@ std::ostream& operator<<(std::ostream& os, const cricket::Candidate& c) { class PortAllocatorTest : public testing::Test, public sigslot::has_slots<> { public: static void SetUpTestCase() { - // Ensure the RNG is inited. - talk_base::InitRandom(NULL, 0); + talk_base::InitializeSSL(); } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } + PortAllocatorTest() : pss_(new talk_base::PhysicalSocketServer), vss_(new talk_base::VirtualSocketServer(pss_.get())), @@ -102,6 +114,7 @@ class PortAllocatorTest : public testing::Test, public sigslot::has_slots<> { relay_server_(Thread::Current(), kRelayUdpIntAddr, kRelayUdpExtAddr, kRelayTcpIntAddr, kRelayTcpExtAddr, kRelaySslTcpIntAddr, kRelaySslTcpExtAddr), + turn_server_(Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr), allocator_(new cricket::BasicPortAllocator( &network_manager_, kStunAddr, kRelayUdpIntAddr, kRelayTcpIntAddr, kRelaySslTcpIntAddr)), @@ -240,6 +253,7 @@ class PortAllocatorTest : public testing::Test, public sigslot::has_slots<> { talk_base::BasicPacketSocketFactory nat_socket_factory_; cricket::TestStunServer stun_server_; cricket::TestRelayServer relay_server_; + cricket::TestTurnServer turn_server_; talk_base::FakeNetworkManager network_manager_; talk_base::scoped_ptr<cricket::BasicPortAllocator> allocator_; talk_base::scoped_ptr<cricket::PortAllocatorSession> session_; @@ -331,56 +345,7 @@ TEST_F(PortAllocatorTest, TestSetupVideoRtpPortsWithNormalSendBuffers) { // If we Stop gathering now, we shouldn't get a second "done" callback. session_->StopGettingPorts(); - // All ports should have normal send-buffer sizes (64KB). - CheckSendBufferSizesOfAllPorts(64 * 1024); -} - -TEST_F(PortAllocatorTest, TestSetupVideoRtpPortsWithLargeSendBuffers) { - AddInterface(kClientAddr); - allocator_->set_flags(allocator_->flags() | - cricket::PORTALLOCATOR_USE_LARGE_SOCKET_SEND_BUFFERS); - EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP, - cricket::CN_VIDEO)); - session_->StartGettingPorts(); - ASSERT_EQ_WAIT(7U, candidates_.size(), kDefaultAllocationTimeout); - EXPECT_TRUE(candidate_allocation_done_); - // If we Stop gathering now, we shouldn't get a second "done" callback. - session_->StopGettingPorts(); - - // All ports should have large send-buffer sizes (128KB). - CheckSendBufferSizesOfAllPorts(128 * 1024); -} - -TEST_F(PortAllocatorTest, TestSetupVideoRtcpPortsAndCheckSendBuffers) { - AddInterface(kClientAddr); - allocator_->set_flags(allocator_->flags() | - cricket::PORTALLOCATOR_USE_LARGE_SOCKET_SEND_BUFFERS); - EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTCP, - cricket::CN_DATA)); - session_->StartGettingPorts(); - ASSERT_EQ_WAIT(7U, candidates_.size(), kDefaultAllocationTimeout); - EXPECT_TRUE(candidate_allocation_done_); - // If we Stop gathering now, we shouldn't get a second "done" callback. - session_->StopGettingPorts(); - - // No ports should have send-buffer size set. - CheckSendBufferSizesOfAllPorts(-1); -} - - -TEST_F(PortAllocatorTest, TestSetupNonVideoPortsAndCheckSendBuffers) { - AddInterface(kClientAddr); - allocator_->set_flags(allocator_->flags() | - cricket::PORTALLOCATOR_USE_LARGE_SOCKET_SEND_BUFFERS); - EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP, - cricket::CN_DATA)); - session_->StartGettingPorts(); - ASSERT_EQ_WAIT(7U, candidates_.size(), kDefaultAllocationTimeout); - EXPECT_TRUE(candidate_allocation_done_); - // If we Stop gathering now, we shouldn't get a second "done" callback. - session_->StopGettingPorts(); - - // No ports should have send-buffer size set. + // All ports should have unset send-buffer sizes. CheckSendBufferSizesOfAllPorts(-1); } @@ -536,6 +501,23 @@ TEST_F(PortAllocatorTest, TestGetAllPortsNoUdpAllowed) { EXPECT_TRUE_WAIT(candidate_allocation_done_, 9000); } +TEST_F(PortAllocatorTest, TestCandidatePriorityOfMultipleInterfaces) { + AddInterface(kClientAddr); + AddInterface(kClientAddr2); + // Allocating only host UDP ports. This is done purely for testing + // convenience. + allocator().set_flags(cricket::PORTALLOCATOR_DISABLE_TCP | + cricket::PORTALLOCATOR_DISABLE_STUN | + cricket::PORTALLOCATOR_DISABLE_RELAY); + EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + ASSERT_EQ(2U, candidates_.size()); + EXPECT_EQ(2U, ports_.size()); + // Candidates priorities should be different. + EXPECT_NE(candidates_[0].priority(), candidates_[1].priority()); +} + // Test to verify ICE restart process. TEST_F(PortAllocatorTest, TestGetAllPortsRestarts) { AddInterface(kClientAddr); @@ -680,7 +662,7 @@ TEST_F(PortAllocatorTest, TestDisableSharedUfrag) { // is allocated for udp and stun. Also verify there is only one candidate // (local) if stun candidate is same as local candidate, which will be the case // in a public network like the below test. -TEST_F(PortAllocatorTest, TestEnableSharedSocketWithoutNat) { +TEST_F(PortAllocatorTest, TestSharedSocketWithoutNat) { AddInterface(kClientAddr); allocator_->set_flags(allocator().flags() | cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | @@ -697,7 +679,7 @@ TEST_F(PortAllocatorTest, TestEnableSharedSocketWithoutNat) { // Test that when PORTALLOCATOR_ENABLE_SHARED_SOCKET is enabled only one port // is allocated for udp and stun. In this test we should expect both stun and // local candidates as client behind a nat. -TEST_F(PortAllocatorTest, TestEnableSharedSocketWithNat) { +TEST_F(PortAllocatorTest, TestSharedSocketWithNat) { AddInterface(kClientAddr); talk_base::scoped_ptr<talk_base::NATServer> nat_server( CreateNatServer(kNatAddr, talk_base::NAT_OPEN_CONE)); @@ -720,10 +702,116 @@ TEST_F(PortAllocatorTest, TestEnableSharedSocketWithNat) { EXPECT_EQ(3U, candidates_.size()); } +// Test TURN port in shared socket mode with UDP and TCP TURN server adderesses. +TEST_F(PortAllocatorTest, TestSharedSocketWithoutNatUsingTurn) { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); + AddInterface(kClientAddr); + allocator_.reset(new cricket::BasicPortAllocator(&network_manager_)); + cricket::RelayServerConfig relay_server(cricket::RELAY_TURN); + cricket::RelayCredentials credentials(kTurnUsername, kTurnPassword); + relay_server.credentials = credentials; + relay_server.ports.push_back(cricket::ProtocolAddress( + kTurnUdpIntAddr, cricket::PROTO_UDP, false)); + relay_server.ports.push_back(cricket::ProtocolAddress( + kTurnTcpIntAddr, cricket::PROTO_TCP, false)); + allocator_->AddRelay(relay_server); + + allocator_->set_step_delay(cricket::kMinimumStepDelay); + allocator_->set_flags(allocator().flags() | + cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | + cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | + cricket::PORTALLOCATOR_DISABLE_TCP); + + EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + + ASSERT_EQ_WAIT(3U, candidates_.size(), kDefaultAllocationTimeout); + ASSERT_EQ(3U, ports_.size()); + EXPECT_PRED5(CheckCandidate, candidates_[0], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "udp", kClientAddr); + EXPECT_PRED5(CheckCandidate, candidates_[1], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "relay", "udp", + talk_base::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + EXPECT_PRED5(CheckCandidate, candidates_[2], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "relay", "udp", + talk_base::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + EXPECT_EQ(3U, candidates_.size()); +} + +// Testing DNS resolve for the TURN server, this will test AllocationSequence +// handling the unresolved address signal from TurnPort. +TEST_F(PortAllocatorTest, TestSharedSocketWithServerAddressResolve) { + turn_server_.AddInternalSocket(talk_base::SocketAddress("127.0.0.1", 3478), + cricket::PROTO_UDP); + AddInterface(kClientAddr); + allocator_.reset(new cricket::BasicPortAllocator(&network_manager_)); + cricket::RelayServerConfig relay_server(cricket::RELAY_TURN); + cricket::RelayCredentials credentials(kTurnUsername, kTurnPassword); + relay_server.credentials = credentials; + relay_server.ports.push_back(cricket::ProtocolAddress( + talk_base::SocketAddress("localhost", 3478), + cricket::PROTO_UDP, false)); + allocator_->AddRelay(relay_server); + + allocator_->set_step_delay(cricket::kMinimumStepDelay); + allocator_->set_flags(allocator().flags() | + cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | + cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | + cricket::PORTALLOCATOR_DISABLE_TCP); + + EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + + EXPECT_EQ_WAIT(2U, ports_.size(), kDefaultAllocationTimeout); +} + +// Test that when PORTALLOCATOR_ENABLE_SHARED_SOCKET is enabled only one port +// is allocated for udp/stun/turn. In this test we should expect all local, +// stun and turn candidates. +TEST_F(PortAllocatorTest, TestSharedSocketWithNatUsingTurn) { + AddInterface(kClientAddr); + talk_base::scoped_ptr<talk_base::NATServer> nat_server( + CreateNatServer(kNatAddr, talk_base::NAT_OPEN_CONE)); + allocator_.reset(new cricket::BasicPortAllocator( + &network_manager_, &nat_socket_factory_, kStunAddr)); + cricket::RelayServerConfig relay_server(cricket::RELAY_TURN); + cricket::RelayCredentials credentials(kTurnUsername, kTurnPassword); + relay_server.credentials = credentials; + relay_server.ports.push_back(cricket::ProtocolAddress( + kTurnUdpIntAddr, cricket::PROTO_UDP, false)); + allocator_->AddRelay(relay_server); + + allocator_->set_step_delay(cricket::kMinimumStepDelay); + allocator_->set_flags(allocator().flags() | + cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | + cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | + cricket::PORTALLOCATOR_DISABLE_TCP); + + EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + + ASSERT_EQ_WAIT(3U, candidates_.size(), kDefaultAllocationTimeout); + ASSERT_EQ(2U, ports_.size()); + EXPECT_PRED5(CheckCandidate, candidates_[0], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "udp", kClientAddr); + EXPECT_PRED5(CheckCandidate, candidates_[1], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "stun", "udp", + talk_base::SocketAddress(kNatAddr.ipaddr(), 0)); + EXPECT_PRED5(CheckCandidate, candidates_[2], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "relay", "udp", + talk_base::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + EXPECT_EQ(3U, candidates_.size()); + // Local port will be created first and then TURN port. + EXPECT_EQ(2U, ports_[0]->Candidates().size()); + EXPECT_EQ(1U, ports_[1]->Candidates().size()); +} + // This test verifies when PORTALLOCATOR_ENABLE_SHARED_SOCKET flag is enabled // and fail to generate STUN candidate, local UDP candidate is generated // properly. -TEST_F(PortAllocatorTest, TestEnableSharedSocketNoUdpAllowed) { +TEST_F(PortAllocatorTest, TestSharedSocketNoUdpAllowed) { allocator().set_flags(allocator().flags() | cricket::PORTALLOCATOR_DISABLE_RELAY | cricket::PORTALLOCATOR_DISABLE_TCP | diff --git a/chromium/third_party/libjingle/source/talk/session/media/audiomonitor.cc b/chromium/third_party/libjingle/source/talk/session/media/audiomonitor.cc index 385702f75f7..c3a2eb0cb44 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/audiomonitor.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/audiomonitor.cc @@ -27,7 +27,7 @@ #include "talk/session/media/audiomonitor.h" #include "talk/session/media/voicechannel.h" -#include <cassert> +#include <assert.h> namespace cricket { diff --git a/chromium/third_party/libjingle/source/talk/session/media/ssrcmuxfilter.cc b/chromium/third_party/libjingle/source/talk/session/media/bundlefilter.cc index 638167d18e7..0d7927c2e95 100644..100755 --- a/chromium/third_party/libjingle/source/talk/session/media/ssrcmuxfilter.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/bundlefilter.cc @@ -25,9 +25,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "talk/session/media/ssrcmuxfilter.h" - -#include <algorithm> +#include "talk/session/media/bundlefilter.h" #include "talk/base/logging.h" #include "talk/media/base/rtputils.h" @@ -36,41 +34,52 @@ namespace cricket { static const uint32 kSsrc01 = 0x01; -SsrcMuxFilter::SsrcMuxFilter() { +BundleFilter::BundleFilter() { } -SsrcMuxFilter::~SsrcMuxFilter() { +BundleFilter::~BundleFilter() { } -bool SsrcMuxFilter::IsActive() const { - return !streams_.empty(); -} +bool BundleFilter::DemuxPacket(const char* data, size_t len, bool rtcp) { + // For rtp packets, we check whether the payload type can be found. + // For rtcp packets, we check whether the ssrc can be found or is the special + // value 1 except for SDES packets which always pass through. Plus, if + // |streams_| is empty, we will allow all rtcp packets pass through provided + // that they are valid rtcp packets in case that they are for early media. + if (!rtcp) { + int payload_type = 0; + if (!GetRtpPayloadType(data, len, &payload_type)) { + return false; + } + return FindPayloadType(payload_type); + } -bool SsrcMuxFilter::DemuxPacket(const char* data, size_t len, bool rtcp) { + // Rtcp packets using ssrc filter. + int pl_type = 0; uint32 ssrc = 0; - if (!rtcp) { - GetRtpSsrc(data, len, &ssrc); + if (!GetRtcpType(data, len, &pl_type)) return false; + if (pl_type == kRtcpTypeSDES) { + // SDES packet parsing not supported. + LOG(LS_INFO) << "SDES packet received for demux."; + return true; } else { - int pl_type = 0; - if (!GetRtcpType(data, len, &pl_type)) return false; - if (pl_type == kRtcpTypeSDES) { - // SDES packet parsing not supported. - LOG(LS_INFO) << "SDES packet received for demux."; + if (!GetRtcpSsrc(data, len, &ssrc)) return false; + if (ssrc == kSsrc01) { + // SSRC 1 has a special meaning and indicates generic feedback on + // some systems and should never be dropped. If it is forwarded + // incorrectly it will be ignored by lower layers anyway. return true; - } else { - if (!GetRtcpSsrc(data, len, &ssrc)) return false; - if (ssrc == kSsrc01) { - // SSRC 1 has a special meaning and indicates generic feedback on - // some systems and should never be dropped. If it is forwarded - // incorrectly it will be ignored by lower layers anyway. - return true; - } } } - return FindStream(ssrc); + // Pass through if |streams_| is empty to allow early rtcp packets in. + return !HasStreams() || FindStream(ssrc); } -bool SsrcMuxFilter::AddStream(const StreamParams& stream) { +void BundleFilter::AddPayloadType(int payload_type) { + payload_types_.insert(payload_type); +} + +bool BundleFilter::AddStream(const StreamParams& stream) { if (GetStreamBySsrc(streams_, stream.first_ssrc(), NULL)) { LOG(LS_WARNING) << "Stream already added to filter"; return false; @@ -79,15 +88,27 @@ bool SsrcMuxFilter::AddStream(const StreamParams& stream) { return true; } -bool SsrcMuxFilter::RemoveStream(uint32 ssrc) { +bool BundleFilter::RemoveStream(uint32 ssrc) { return RemoveStreamBySsrc(&streams_, ssrc); } -bool SsrcMuxFilter::FindStream(uint32 ssrc) const { +bool BundleFilter::HasStreams() const { + return !streams_.empty(); +} + +bool BundleFilter::FindStream(uint32 ssrc) const { if (ssrc == 0) { return false; } return (GetStreamBySsrc(streams_, ssrc, NULL)); } +bool BundleFilter::FindPayloadType(int pl_type) const { + return payload_types_.find(pl_type) != payload_types_.end(); +} + +void BundleFilter::ClearAllPayloadTypes() { + payload_types_.clear(); +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/session/media/ssrcmuxfilter.h b/chromium/third_party/libjingle/source/talk/session/media/bundlefilter.h index 9420f54cceb..34bc3307345 100644..100755 --- a/chromium/third_party/libjingle/source/talk/session/media/ssrcmuxfilter.h +++ b/chromium/third_party/libjingle/source/talk/session/media/bundlefilter.h @@ -25,9 +25,10 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_SESSION_MEDIA_SSRCMUXFILTER_H_ -#define TALK_SESSION_MEDIA_SSRCMUXFILTER_H_ +#ifndef TALK_SESSION_MEDIA_BUNDLEFILTER_H_ +#define TALK_SESSION_MEDIA_BUNDLEFILTER_H_ +#include <set> #include <vector> #include "talk/base/basictypes.h" @@ -35,33 +36,45 @@ namespace cricket { -// This class maintains list of recv SSRC's destined for cricket::BaseChannel. // In case of single RTP session and single transport channel, all session // ( or media) channels share a common transport channel. Hence they all get // SignalReadPacket when packet received on transport channel. This requires // cricket::BaseChannel to know all the valid sources, else media channel // will decode invalid packets. -class SsrcMuxFilter { +// +// This class determines whether a packet is destined for cricket::BaseChannel. +// For rtp packets, this is decided based on the payload type. For rtcp packets, +// this is decided based on the sender ssrc values. +class BundleFilter { public: - SsrcMuxFilter(); - ~SsrcMuxFilter(); + BundleFilter(); + ~BundleFilter(); - // Whether the rtp mux is active for a sdp session. - // Returns true if the filter contains a stream. - bool IsActive() const; // Determines packet belongs to valid cricket::BaseChannel. bool DemuxPacket(const char* data, size_t len, bool rtcp); + + // Adds the supported payload type. + void AddPayloadType(int payload_type); + // Adding a valid source to the filter. bool AddStream(const StreamParams& stream); + // Removes source from the filter. bool RemoveStream(uint32 ssrc); - // Utility method added for unitest. + + // Utility methods added for unitest. + // True if |streams_| is not empty. + bool HasStreams() const; bool FindStream(uint32 ssrc) const; + bool FindPayloadType(int pl_type) const; + void ClearAllPayloadTypes(); + private: + std::set<int> payload_types_; std::vector<StreamParams> streams_; }; } // namespace cricket -#endif // TALK_SESSION_MEDIA_SSRCMUXFILTER_H_ +#endif // TALK_SESSION_MEDIA_BUNDLEFILTER_H_ diff --git a/chromium/third_party/libjingle/source/talk/session/media/ssrcmuxfilter_unittest.cc b/chromium/third_party/libjingle/source/talk/session/media/bundlefilter_unittest.cc index 85a4dbe50da..0386666c099 100644..100755 --- a/chromium/third_party/libjingle/source/talk/session/media/ssrcmuxfilter_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/bundlefilter_unittest.cc @@ -25,34 +25,34 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "talk/base/gunit.h" -#include "talk/session/media/ssrcmuxfilter.h" +#include "talk/session/media/bundlefilter.h" + +using cricket::StreamParams; static const int kSsrc1 = 0x1111; static const int kSsrc2 = 0x2222; static const int kSsrc3 = 0x3333; - -using cricket::StreamParams; - -// SSRC = 0x1111 -static const unsigned char kRtpPacketSsrc1[] = { - 0x80, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, -}; - -// SSRC = 0x2222 -static const unsigned char kRtpPacketSsrc2[] = { - 0x80, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, +static const int kPayloadType1 = 0x11; +static const int kPayloadType2 = 0x22; +static const int kPayloadType3 = 0x33; + +// SSRC = 0x1111, Payload type = 0x11 +static const unsigned char kRtpPacketPt1Ssrc1[] = { + 0x80, kPayloadType1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x11, }; -// SSRC = 0 -static const unsigned char kRtpPacketInvalidSsrc[] = { - 0x80, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// SSRC = 0x2222, Payload type = 0x22 +static const unsigned char kRtpPacketPt2Ssrc2[] = { + 0x80, 0x80 + kPayloadType2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, }; -// invalid size -static const unsigned char kRtpPacketTooSmall[] = { - 0x80, 0x80, 0x00, 0x00, +// SSRC = 0x2222, Payload type = 0x33 +static const unsigned char kRtpPacketPt3Ssrc2[] = { + 0x80, kPayloadType3, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, + 0x22, }; // PT = 200 = SR, len = 28, SSRC of sender = 0x0001 @@ -105,80 +105,92 @@ static const unsigned char kRtcpPacketNonCompoundRtcpPliFeedback[] = { 0x81, 0xCE, 0x00, 0x0C, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x11, 0x11, }; -TEST(SsrcMuxFilterTest, AddRemoveStreamTest) { - cricket::SsrcMuxFilter ssrc_filter; - EXPECT_FALSE(ssrc_filter.IsActive()); - EXPECT_TRUE(ssrc_filter.AddStream(StreamParams::CreateLegacy(kSsrc1))); +TEST(BundleFilterTest, AddRemoveStreamTest) { + cricket::BundleFilter bundle_filter; + EXPECT_FALSE(bundle_filter.HasStreams()); + EXPECT_TRUE(bundle_filter.AddStream(StreamParams::CreateLegacy(kSsrc1))); StreamParams stream2; stream2.ssrcs.push_back(kSsrc2); stream2.ssrcs.push_back(kSsrc3); - EXPECT_TRUE(ssrc_filter.AddStream(stream2)); - - EXPECT_TRUE(ssrc_filter.IsActive()); - EXPECT_TRUE(ssrc_filter.FindStream(kSsrc1)); - EXPECT_TRUE(ssrc_filter.FindStream(kSsrc2)); - EXPECT_TRUE(ssrc_filter.FindStream(kSsrc3)); - EXPECT_TRUE(ssrc_filter.RemoveStream(kSsrc1)); - EXPECT_FALSE(ssrc_filter.FindStream(kSsrc1)); - EXPECT_TRUE(ssrc_filter.RemoveStream(kSsrc3)); - EXPECT_FALSE(ssrc_filter.RemoveStream(kSsrc2)); // Already removed. - EXPECT_FALSE(ssrc_filter.IsActive()); + EXPECT_TRUE(bundle_filter.AddStream(stream2)); + + EXPECT_TRUE(bundle_filter.HasStreams()); + EXPECT_TRUE(bundle_filter.FindStream(kSsrc1)); + EXPECT_TRUE(bundle_filter.FindStream(kSsrc2)); + EXPECT_TRUE(bundle_filter.FindStream(kSsrc3)); + EXPECT_TRUE(bundle_filter.RemoveStream(kSsrc1)); + EXPECT_FALSE(bundle_filter.FindStream(kSsrc1)); + EXPECT_TRUE(bundle_filter.RemoveStream(kSsrc3)); + EXPECT_FALSE(bundle_filter.RemoveStream(kSsrc2)); // Already removed. + EXPECT_FALSE(bundle_filter.HasStreams()); } -TEST(SsrcMuxFilterTest, RtpPacketTest) { - cricket::SsrcMuxFilter ssrc_filter; - EXPECT_TRUE(ssrc_filter.AddStream(StreamParams::CreateLegacy(kSsrc1))); - EXPECT_TRUE(ssrc_filter.DemuxPacket( - reinterpret_cast<const char*>(kRtpPacketSsrc1), - sizeof(kRtpPacketSsrc1), false)); - EXPECT_TRUE(ssrc_filter.AddStream(StreamParams::CreateLegacy(kSsrc2))); - EXPECT_TRUE(ssrc_filter.DemuxPacket( - reinterpret_cast<const char*>(kRtpPacketSsrc2), - sizeof(kRtpPacketSsrc2), false)); - EXPECT_TRUE(ssrc_filter.RemoveStream(kSsrc2)); - EXPECT_FALSE(ssrc_filter.DemuxPacket( - reinterpret_cast<const char*>(kRtpPacketSsrc2), - sizeof(kRtpPacketSsrc2), false)); - EXPECT_FALSE(ssrc_filter.DemuxPacket( - reinterpret_cast<const char*>(kRtpPacketInvalidSsrc), - sizeof(kRtpPacketInvalidSsrc), false)); - EXPECT_FALSE(ssrc_filter.DemuxPacket( - reinterpret_cast<const char*>(kRtpPacketTooSmall), - sizeof(kRtpPacketTooSmall), false)); +TEST(BundleFilterTest, RtpPacketTest) { + cricket::BundleFilter bundle_filter; + bundle_filter.AddPayloadType(kPayloadType1); + EXPECT_TRUE(bundle_filter.DemuxPacket( + reinterpret_cast<const char*>(kRtpPacketPt1Ssrc1), + sizeof(kRtpPacketPt1Ssrc1), false)); + bundle_filter.AddPayloadType(kPayloadType2); + EXPECT_TRUE(bundle_filter.DemuxPacket( + reinterpret_cast<const char*>(kRtpPacketPt2Ssrc2), + sizeof(kRtpPacketPt2Ssrc2), false)); + + // Payload type 0x33 is not added. + EXPECT_FALSE(bundle_filter.DemuxPacket( + reinterpret_cast<const char*>(kRtpPacketPt3Ssrc2), + sizeof(kRtpPacketPt3Ssrc2), false)); + // Size is too small. + EXPECT_FALSE(bundle_filter.DemuxPacket( + reinterpret_cast<const char*>(kRtpPacketPt1Ssrc1), 11, false)); + + bundle_filter.ClearAllPayloadTypes(); + EXPECT_FALSE(bundle_filter.DemuxPacket( + reinterpret_cast<const char*>(kRtpPacketPt1Ssrc1), + sizeof(kRtpPacketPt1Ssrc1), false)); + EXPECT_FALSE(bundle_filter.DemuxPacket( + reinterpret_cast<const char*>(kRtpPacketPt2Ssrc2), + sizeof(kRtpPacketPt2Ssrc2), false)); } -TEST(SsrcMuxFilterTest, RtcpPacketTest) { - cricket::SsrcMuxFilter ssrc_filter; - EXPECT_TRUE(ssrc_filter.AddStream(StreamParams::CreateLegacy(kSsrc1))); - EXPECT_TRUE(ssrc_filter.DemuxPacket( +TEST(BundleFilterTest, RtcpPacketTest) { + cricket::BundleFilter bundle_filter; + EXPECT_TRUE(bundle_filter.AddStream(StreamParams::CreateLegacy(kSsrc1))); + EXPECT_TRUE(bundle_filter.DemuxPacket( reinterpret_cast<const char*>(kRtcpPacketCompoundSrSdesSsrc1), sizeof(kRtcpPacketCompoundSrSdesSsrc1), true)); - EXPECT_TRUE(ssrc_filter.AddStream(StreamParams::CreateLegacy(kSsrc2))); - EXPECT_TRUE(ssrc_filter.DemuxPacket( + EXPECT_TRUE(bundle_filter.AddStream(StreamParams::CreateLegacy(kSsrc2))); + EXPECT_TRUE(bundle_filter.DemuxPacket( reinterpret_cast<const char*>(kRtcpPacketSrSsrc2), sizeof(kRtcpPacketSrSsrc2), true)); - EXPECT_TRUE(ssrc_filter.DemuxPacket( + EXPECT_TRUE(bundle_filter.DemuxPacket( reinterpret_cast<const char*>(kRtcpPacketSdesSsrc2), sizeof(kRtcpPacketSdesSsrc2), true)); - EXPECT_TRUE(ssrc_filter.RemoveStream(kSsrc2)); + EXPECT_TRUE(bundle_filter.RemoveStream(kSsrc2)); // RTCP Packets other than SR and RR are demuxed regardless of SSRC. - EXPECT_TRUE(ssrc_filter.DemuxPacket( + EXPECT_TRUE(bundle_filter.DemuxPacket( reinterpret_cast<const char*>(kRtcpPacketSdesSsrc2), sizeof(kRtcpPacketSdesSsrc2), true)); // RTCP Packets with 'special' SSRC 0x01 are demuxed also - EXPECT_TRUE(ssrc_filter.DemuxPacket( + EXPECT_TRUE(bundle_filter.DemuxPacket( reinterpret_cast<const char*>(kRtcpPacketSrSsrc01), sizeof(kRtcpPacketSrSsrc01), true)); - EXPECT_FALSE(ssrc_filter.DemuxPacket( + EXPECT_FALSE(bundle_filter.DemuxPacket( reinterpret_cast<const char*>(kRtcpPacketSrSsrc2), sizeof(kRtcpPacketSrSsrc2), true)); - EXPECT_FALSE(ssrc_filter.DemuxPacket( + EXPECT_FALSE(bundle_filter.DemuxPacket( reinterpret_cast<const char*>(kRtcpPacketFixedHeaderOnly), sizeof(kRtcpPacketFixedHeaderOnly), true)); - EXPECT_FALSE(ssrc_filter.DemuxPacket( + EXPECT_FALSE(bundle_filter.DemuxPacket( reinterpret_cast<const char*>(kRtcpPacketTooSmall), sizeof(kRtcpPacketTooSmall), true)); - EXPECT_TRUE(ssrc_filter.DemuxPacket( + EXPECT_TRUE(bundle_filter.DemuxPacket( reinterpret_cast<const char*>(kRtcpPacketNonCompoundRtcpPliFeedback), sizeof(kRtcpPacketNonCompoundRtcpPliFeedback), true)); + // If the streams_ is empty, rtcp packet passes through + EXPECT_TRUE(bundle_filter.RemoveStream(kSsrc1)); + EXPECT_FALSE(bundle_filter.HasStreams()); + EXPECT_TRUE(bundle_filter.DemuxPacket( + reinterpret_cast<const char*>(kRtcpPacketSrSsrc2), + sizeof(kRtcpPacketSrSsrc2), true)); } diff --git a/chromium/third_party/libjingle/source/talk/session/media/call.cc b/chromium/third_party/libjingle/source/talk/session/media/call.cc index 967846bd214..91fe146e95a 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/call.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/call.cc @@ -34,6 +34,7 @@ #include "talk/media/base/screencastid.h" #include "talk/p2p/base/parsing.h" #include "talk/session/media/call.h" +#include "talk/session/media/currentspeakermonitor.h" #include "talk/session/media/mediasessionclient.h" namespace cricket { @@ -74,6 +75,22 @@ bool ContentContainsCrypto(const cricket::ContentInfo* content) { } +AudioSourceProxy::AudioSourceProxy(Call* call) + : call_(call) { + call_->SignalAudioMonitor.connect(this, &AudioSourceProxy::OnAudioMonitor); + call_->SignalMediaStreamsUpdate.connect( + this, &AudioSourceProxy::OnMediaStreamsUpdate); +} + +void AudioSourceProxy::OnAudioMonitor(Call* call, const AudioInfo& info) { + SignalAudioMonitor(this, info); +} + +void AudioSourceProxy::OnMediaStreamsUpdate(Call* call, Session* session, + const MediaStreams& added, const MediaStreams& removed) { + SignalMediaStreamsUpdate(this, session, added, removed); +} + Call::Call(MediaSessionClient* session_client) : id_(talk_base::CreateRandomId()), session_client_(session_client), @@ -84,6 +101,7 @@ Call::Call(MediaSessionClient* session_client) video_muted_(false), send_to_voicemail_(true), playing_dtmf_(false) { + audio_source_proxy_.reset(new AudioSourceProxy(this)); } Call::~Call() { @@ -523,7 +541,7 @@ bool Call::StartScreencast(Session* session, VideoContentDescription* video = CreateVideoStreamUpdate(stream); // TODO(pthatcher): Wait until view request before sending video. - video_channel->SetLocalContent(video, CA_UPDATE); + video_channel->SetLocalContent(video, CA_UPDATE, NULL); SendVideoStreamUpdate(session, video); return true; } @@ -546,7 +564,7 @@ bool Call::StopScreencast(Session* session, // No ssrcs VideoContentDescription* video = CreateVideoStreamUpdate(stream); - video_channel->SetLocalContent(video, CA_UPDATE); + video_channel->SetLocalContent(video, CA_UPDATE, NULL); SendVideoStreamUpdate(session, video); return true; } @@ -718,7 +736,8 @@ void Call::StartSpeakerMonitor(Session* session) { StartAudioMonitor(session, kAudioMonitorPollPeriodMillis); } CurrentSpeakerMonitor* speaker_monitor = - new cricket::CurrentSpeakerMonitor(this, session); + new cricket::CurrentSpeakerMonitor( + audio_source_proxy_.get(), session); speaker_monitor->SignalUpdate.connect(this, &Call::OnSpeakerMonitor); speaker_monitor->Start(); speaker_monitor_map_[session->id()] = speaker_monitor; @@ -870,9 +889,11 @@ void Call::OnRemoteDescriptionUpdate(BaseSession* base_session, bool Call::UpdateVoiceChannelRemoteContent( Session* session, const AudioContentDescription* audio) { VoiceChannel* voice_channel = GetVoiceChannel(session); - if (!voice_channel->SetRemoteContent(audio, CA_UPDATE)) { - LOG(LS_ERROR) << "Failure in audio SetRemoteContent with CA_UPDATE"; - session->SetError(BaseSession::ERROR_CONTENT); + if (!voice_channel->SetRemoteContent(audio, CA_UPDATE, NULL)) { + const std::string error_desc = + "Failure in audio SetRemoteContent with CA_UPDATE"; + LOG(LS_ERROR) << error_desc; + session->SetError(BaseSession::ERROR_CONTENT, error_desc); return false; } return true; @@ -881,9 +902,11 @@ bool Call::UpdateVoiceChannelRemoteContent( bool Call::UpdateVideoChannelRemoteContent( Session* session, const VideoContentDescription* video) { VideoChannel* video_channel = GetVideoChannel(session); - if (!video_channel->SetRemoteContent(video, CA_UPDATE)) { - LOG(LS_ERROR) << "Failure in video SetRemoteContent with CA_UPDATE"; - session->SetError(BaseSession::ERROR_CONTENT); + if (!video_channel->SetRemoteContent(video, CA_UPDATE, NULL)) { + const std::string error_desc = + "Failure in video SetRemoteContent with CA_UPDATE"; + LOG(LS_ERROR) << error_desc; + session->SetError(BaseSession::ERROR_CONTENT, error_desc); return false; } return true; @@ -892,9 +915,11 @@ bool Call::UpdateVideoChannelRemoteContent( bool Call::UpdateDataChannelRemoteContent( Session* session, const DataContentDescription* data) { DataChannel* data_channel = GetDataChannel(session); - if (!data_channel->SetRemoteContent(data, CA_UPDATE)) { - LOG(LS_ERROR) << "Failure in data SetRemoteContent with CA_UPDATE"; - session->SetError(BaseSession::ERROR_CONTENT); + if (!data_channel->SetRemoteContent(data, CA_UPDATE, NULL)) { + const std::string error_desc = + "Failure in data SetRemoteContent with CA_UPDATE"; + LOG(LS_ERROR) << error_desc; + session->SetError(BaseSession::ERROR_CONTENT, error_desc); return false; } return true; @@ -1098,4 +1123,8 @@ Session* Call::InternalInitiateSession(const std::string& id, return session; } +AudioSourceProxy* Call::GetAudioSourceProxy() { + return audio_source_proxy_.get(); +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/session/media/call.h b/chromium/third_party/libjingle/source/talk/session/media/call.h index efb4ea396f4..063447a7f73 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/call.h +++ b/chromium/third_party/libjingle/source/talk/session/media/call.h @@ -48,6 +48,8 @@ namespace cricket { +struct AudioInfo; +class Call; class MediaSessionClient; class BaseChannel; class VoiceChannel; @@ -58,6 +60,26 @@ class DataChannel; struct CallOptions : public MediaSessionOptions { }; +// CurrentSpeakerMonitor used to have a dependency on Call. To remove this +// dependency, we create AudioSourceContext. CurrentSpeakerMonitor depends on +// AudioSourceContext. +// AudioSourceProxy acts as a proxy so that when SignalAudioMonitor +// in Call is triggered, SignalAudioMonitor in AudioSourceContext is triggered. +// Likewise, when OnMediaStreamsUpdate in Call is triggered, +// OnMediaStreamsUpdate in AudioSourceContext is triggered. +class AudioSourceProxy: public AudioSourceContext, public sigslot::has_slots<> { + public: + explicit AudioSourceProxy(Call* call); + + private: + void OnAudioMonitor(Call* call, const AudioInfo& info); + void OnMediaStreamsUpdate(Call* call, cricket::Session*, + const cricket::MediaStreams&, const cricket::MediaStreams&); + + AudioSourceContext* audio_source_context_; + Call* call_; +}; + class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { public: explicit Call(MediaSessionClient* session_client); @@ -167,6 +189,8 @@ class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { const ReceiveDataParams&, const talk_base::Buffer&> SignalDataReceived; + AudioSourceProxy* GetAudioSourceProxy(); + private: void OnMessage(talk_base::Message* message); void OnSessionState(BaseSession* base_session, BaseSession::State state); @@ -276,6 +300,8 @@ class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { VoiceMediaInfo last_voice_media_info_; + talk_base::scoped_ptr<AudioSourceProxy> audio_source_proxy_; + friend class MediaSessionClient; }; diff --git a/chromium/third_party/libjingle/source/talk/session/media/channel.cc b/chromium/third_party/libjingle/source/talk/session/media/channel.cc index 9a8559a5b05..575c7595339 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channel.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/channel.cc @@ -27,61 +27,34 @@ #include "talk/session/media/channel.h" +#include "talk/base/bind.h" #include "talk/base/buffer.h" #include "talk/base/byteorder.h" #include "talk/base/common.h" #include "talk/base/dscp.h" #include "talk/base/logging.h" +#include "talk/media/base/constants.h" #include "talk/media/base/rtputils.h" #include "talk/p2p/base/transportchannel.h" #include "talk/session/media/channelmanager.h" #include "talk/session/media/mediamessages.h" -#include "talk/session/media/rtcpmuxfilter.h" #include "talk/session/media/typingmonitor.h" namespace cricket { +using talk_base::Bind; + enum { - MSG_ENABLE = 1, - MSG_DISABLE, - MSG_MUTESTREAM, - MSG_ISSTREAMMUTED, - MSG_SETREMOTECONTENT, - MSG_SETLOCALCONTENT, - MSG_EARLYMEDIATIMEOUT, - MSG_CANINSERTDTMF, - MSG_INSERTDTMF, - MSG_GETSTATS, - MSG_SETRENDERER, - MSG_ADDRECVSTREAM, - MSG_REMOVERECVSTREAM, - MSG_ADDSENDSTREAM, - MSG_REMOVESENDSTREAM, - MSG_SETRINGBACKTONE, - MSG_PLAYRINGBACKTONE, - MSG_SETMAXSENDBANDWIDTH, - MSG_ADDSCREENCAST, - MSG_REMOVESCREENCAST, - MSG_SENDINTRAFRAME, - MSG_REQUESTINTRAFRAME, + MSG_EARLYMEDIATIMEOUT = 1, MSG_SCREENCASTWINDOWEVENT, MSG_RTPPACKET, MSG_RTCPPACKET, MSG_CHANNEL_ERROR, - MSG_SETCHANNELOPTIONS, - MSG_SCALEVOLUME, - MSG_HANDLEVIEWREQUEST, MSG_READYTOSENDDATA, - MSG_SENDDATA, MSG_DATARECEIVED, - MSG_SETCAPTURER, - MSG_ISSCREENCASTING, - MSG_GETSCREENCASTDETAILS, - MSG_SETSCREENCASTFACTORY, MSG_FIRSTPACKETRECEIVED, - MSG_SESSION_ERROR, - MSG_NEWSTREAMRECEIVED, + MSG_STREAMCLOSEDREMOTELY, }; // Value specified in RFC 5764. @@ -89,6 +62,17 @@ static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp"; static const int kAgcMinus10db = -10; +static void SetSessionError(BaseSession* session, BaseSession::Error error, + const std::string& error_desc) { + session->SetError(error, error_desc); +} + +static void SafeSetError(const std::string& message, std::string* error_desc) { + if (error_desc) { + *error_desc = message; + } +} + // TODO(hellner): use the device manager for creation of screen capturers when // the cl enabling it has landed. class NullScreenCapturerFactory : public VideoChannel::ScreenCapturerFactory { @@ -103,129 +87,11 @@ VideoChannel::ScreenCapturerFactory* CreateScreenCapturerFactory() { return new NullScreenCapturerFactory(); } -struct SetContentData : public talk_base::MessageData { - SetContentData(const MediaContentDescription* content, ContentAction action) - : content(content), - action(action), - result(false) { - } - const MediaContentDescription* content; - ContentAction action; - bool result; -}; - -struct SetBandwidthData : public talk_base::MessageData { - explicit SetBandwidthData(int value) : value(value), result(false) {} - int value; - bool result; -}; - -struct SetRingbackToneMessageData : public talk_base::MessageData { - SetRingbackToneMessageData(const void* b, int l) - : buf(b), - len(l), - result(false) { - } - const void* buf; - int len; - bool result; -}; - -struct PlayRingbackToneMessageData : public talk_base::MessageData { - PlayRingbackToneMessageData(uint32 s, bool p, bool l) - : ssrc(s), - play(p), - loop(l), - result(false) { - } - uint32 ssrc; - bool play; - bool loop; - bool result; -}; -typedef talk_base::TypedMessageData<bool> BoolMessageData; -struct DtmfMessageData : public talk_base::MessageData { - DtmfMessageData(uint32 ssrc, int event, int duration, int flags) - : ssrc(ssrc), - event(event), - duration(duration), - flags(flags), - result(false) { - } - uint32 ssrc; - int event; - int duration; - int flags; - bool result; -}; -struct ScaleVolumeMessageData : public talk_base::MessageData { - ScaleVolumeMessageData(uint32 s, double l, double r) - : ssrc(s), - left(l), - right(r), - result(false) { - } - uint32 ssrc; - double left; - double right; - bool result; -}; - -struct VoiceStatsMessageData : public talk_base::MessageData { - explicit VoiceStatsMessageData(VoiceMediaInfo* stats) - : result(false), - stats(stats) { - } - bool result; - VoiceMediaInfo* stats; -}; - -struct VideoStatsMessageData : public talk_base::MessageData { - explicit VideoStatsMessageData(VideoMediaInfo* stats) - : result(false), - stats(stats) { - } - bool result; - VideoMediaInfo* stats; -}; - struct PacketMessageData : public talk_base::MessageData { talk_base::Buffer packet; talk_base::DiffServCodePoint dscp; }; -struct AudioRenderMessageData: public talk_base::MessageData { - AudioRenderMessageData(uint32 s, AudioRenderer* r, bool l) - : ssrc(s), renderer(r), is_local(l), result(false) {} - uint32 ssrc; - AudioRenderer* renderer; - bool is_local; - bool result; -}; - -struct VideoRenderMessageData : public talk_base::MessageData { - VideoRenderMessageData(uint32 s, VideoRenderer* r) : ssrc(s), renderer(r) {} - uint32 ssrc; - VideoRenderer* renderer; -}; - -struct AddScreencastMessageData : public talk_base::MessageData { - AddScreencastMessageData(uint32 s, const ScreencastId& id) - : ssrc(s), - window_id(id), - result(NULL) { - } - uint32 ssrc; - ScreencastId window_id; - VideoCapturer* result; -}; - -struct RemoveScreencastMessageData : public talk_base::MessageData { - explicit RemoveScreencastMessageData(uint32 s) : ssrc(s), result(false) {} - uint32 ssrc; - bool result; -}; - struct ScreencastEventMessageData : public talk_base::MessageData { ScreencastEventMessageData(uint32 s, talk_base::WindowEvent we) : ssrc(s), @@ -235,15 +101,6 @@ struct ScreencastEventMessageData : public talk_base::MessageData { talk_base::WindowEvent event; }; -struct ViewRequestMessageData : public talk_base::MessageData { - explicit ViewRequestMessageData(const ViewRequest& r) - : request(r), - result(false) { - } - ViewRequest request; - bool result; -}; - struct VoiceChannelErrorMessageData : public talk_base::MessageData { VoiceChannelErrorMessageData(uint32 in_ssrc, VoiceMediaChannel::Error in_error) @@ -273,75 +130,9 @@ struct DataChannelErrorMessageData : public talk_base::MessageData { DataMediaChannel::Error error; }; -struct SessionErrorMessageData : public talk_base::MessageData { - explicit SessionErrorMessageData(cricket::BaseSession::Error error) - : error_(error) {} - - BaseSession::Error error_; -}; - -struct SsrcMessageData : public talk_base::MessageData { - explicit SsrcMessageData(uint32 ssrc) : ssrc(ssrc), result(false) {} - uint32 ssrc; - bool result; -}; - -struct StreamMessageData : public talk_base::MessageData { - explicit StreamMessageData(const StreamParams& in_sp) - : sp(in_sp), - result(false) { - } - StreamParams sp; - bool result; -}; - -struct MuteStreamData : public talk_base::MessageData { - MuteStreamData(uint32 ssrc, bool mute) - : ssrc(ssrc), mute(mute), result(false) {} - uint32 ssrc; - bool mute; - bool result; -}; - -struct AudioOptionsMessageData : public talk_base::MessageData { - explicit AudioOptionsMessageData(const AudioOptions& options) - : options(options), - result(false) { - } - AudioOptions options; - bool result; -}; -struct VideoOptionsMessageData : public talk_base::MessageData { - explicit VideoOptionsMessageData(const VideoOptions& options) - : options(options), - result(false) { - } - VideoOptions options; - bool result; -}; - -struct SetCapturerMessageData : public talk_base::MessageData { - SetCapturerMessageData(uint32 s, VideoCapturer* c) - : ssrc(s), - capturer(c), - result(false) { - } - uint32 ssrc; - VideoCapturer* capturer; - bool result; -}; - -struct IsScreencastingMessageData : public talk_base::MessageData { - IsScreencastingMessageData() - : result(false) { - } - bool result; -}; - -struct VideoChannel::ScreencastDetailsMessageData : - public talk_base::MessageData { - explicit ScreencastDetailsMessageData(uint32 s) +struct VideoChannel::ScreencastDetailsData { + explicit ScreencastDetailsData(uint32 s) : ssrc(s), fps(0), screencast_max_pixels(0) { } uint32 ssrc; @@ -349,14 +140,6 @@ struct VideoChannel::ScreencastDetailsMessageData : int screencast_max_pixels; }; -struct SetScreenCaptureFactoryMessageData : public talk_base::MessageData { - explicit SetScreenCaptureFactoryMessageData( - VideoChannel::ScreenCapturerFactory* f) - : screencapture_factory(f) { - } - VideoChannel::ScreenCapturerFactory* screencapture_factory; -}; - static const char* PacketType(bool rtcp) { return (!rtcp) ? "RTP" : "RTCP"; } @@ -404,7 +187,8 @@ BaseChannel::BaseChannel(talk_base::Thread* thread, remote_content_direction_(MD_INACTIVE), has_received_packet_(false), dtls_keyed_(false), - secure_required_(false) { + secure_required_(false), + rtp_abs_sendtime_extn_id_(-1) { ASSERT(worker_thread_ == talk_base::Thread::Current()); LOG(LS_INFO) << "Created channel for " << content_name; } @@ -414,7 +198,7 @@ BaseChannel::~BaseChannel() { Deinit(); StopConnectionMonitor(); FlushRtcpMessages(); // Send any outstanding RTCP packets. - Clear(); // eats any outstanding messages or packets + worker_thread_->Clear(this); // eats any outstanding messages or packets // We must destroy the media channel before the transport channel, otherwise // the media channel may try to send on the dead transport channel. NULLing // is not an effective strategy since the sends will come on another thread. @@ -462,67 +246,51 @@ void BaseChannel::Deinit() { media_channel_->SetInterface(NULL); } -// Can be called from thread other than worker thread bool BaseChannel::Enable(bool enable) { - Send(enable ? MSG_ENABLE : MSG_DISABLE); + worker_thread_->Invoke<void>(Bind( + enable ? &BaseChannel::EnableMedia_w : &BaseChannel::DisableMedia_w, + this)); return true; } -// Can be called from thread other than worker thread bool BaseChannel::MuteStream(uint32 ssrc, bool mute) { - MuteStreamData data(ssrc, mute); - Send(MSG_MUTESTREAM, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::MuteStream_w, this, ssrc, mute)); } bool BaseChannel::IsStreamMuted(uint32 ssrc) { - SsrcMessageData data(ssrc); - Send(MSG_ISSTREAMMUTED, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::IsStreamMuted_w, this, ssrc)); } bool BaseChannel::AddRecvStream(const StreamParams& sp) { - StreamMessageData data(sp); - Send(MSG_ADDRECVSTREAM, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::AddRecvStream_w, this, sp)); } bool BaseChannel::RemoveRecvStream(uint32 ssrc) { - SsrcMessageData data(ssrc); - Send(MSG_REMOVERECVSTREAM, &data); - return data.result; + return InvokeOnWorker(Bind(&BaseChannel::RemoveRecvStream_w, this, ssrc)); } bool BaseChannel::AddSendStream(const StreamParams& sp) { - StreamMessageData data(sp); - Send(MSG_ADDSENDSTREAM, &data); - return data.result; + return InvokeOnWorker( + Bind(&MediaChannel::AddSendStream, media_channel(), sp)); } bool BaseChannel::RemoveSendStream(uint32 ssrc) { - SsrcMessageData data(ssrc); - Send(MSG_REMOVESENDSTREAM, &data); - return data.result; + return InvokeOnWorker( + Bind(&MediaChannel::RemoveSendStream, media_channel(), ssrc)); } bool BaseChannel::SetLocalContent(const MediaContentDescription* content, - ContentAction action) { - SetContentData data(content, action); - Send(MSG_SETLOCALCONTENT, &data); - return data.result; + ContentAction action, + std::string* error_desc) { + return InvokeOnWorker(Bind(&BaseChannel::SetLocalContent_w, + this, content, action, error_desc)); } bool BaseChannel::SetRemoteContent(const MediaContentDescription* content, - ContentAction action) { - SetContentData data(content, action); - Send(MSG_SETREMOTECONTENT, &data); - return data.result; -} - -bool BaseChannel::SetMaxSendBandwidth(int max_bandwidth) { - SetBandwidthData data(max_bandwidth); - Send(MSG_SETMAXSENDBANDWIDTH, &data); - return data.result; + ContentAction action, + std::string* error_desc) { + return InvokeOnWorker(Bind(&BaseChannel::SetRemoteContent_w, + this, content, action, error_desc)); } void BaseChannel::StartConnectionMonitor(int cms) { @@ -696,14 +464,40 @@ bool BaseChannel::SendPacket(bool rtcp, talk_base::Buffer* packet, SignalSendPacketPreCrypto(packet->data(), packet->length(), rtcp); } + talk_base::PacketOptions options(dscp); // Protect if needed. if (srtp_filter_.IsActive()) { bool res; char* data = packet->data(); int len = static_cast<int>(packet->length()); if (!rtcp) { - res = srtp_filter_.ProtectRtp(data, len, - static_cast<int>(packet->capacity()), &len); + // If ENABLE_EXTERNAL_AUTH flag is on then packet authentication is not done + // inside libsrtp for a RTP packet. A external HMAC module will be writing + // a fake HMAC value. This is ONLY done for a RTP packet. + // Socket layer will update rtp sendtime extension header if present in + // packet with current time before updating the HMAC. +#if !defined(ENABLE_EXTERNAL_AUTH) + res = srtp_filter_.ProtectRtp( + data, len, static_cast<int>(packet->capacity()), &len); +#else + options.packet_time_params.rtp_sendtime_extension_id = + rtp_abs_sendtime_extn_id_; + res = srtp_filter_.ProtectRtp( + data, len, static_cast<int>(packet->capacity()), &len, + &options.packet_time_params.srtp_packet_index); + // If protection succeeds, let's get auth params from srtp. + if (res) { + uint8* auth_key = NULL; + int key_len; + res = srtp_filter_.GetRtpAuthParams( + &auth_key, &key_len, &options.packet_time_params.srtp_auth_tag_len); + if (res) { + options.packet_time_params.srtp_auth_key.resize(key_len); + options.packet_time_params.srtp_auth_key.assign(auth_key, + auth_key + key_len); + } + } +#endif if (!res) { int seq_num = -1; uint32 ssrc = 0; @@ -745,7 +539,7 @@ bool BaseChannel::SendPacket(bool rtcp, talk_base::Buffer* packet, } // Bon voyage. - int ret = channel->SendPacket(packet->data(), packet->length(), dscp, + int ret = channel->SendPacket(packet->data(), packet->length(), options, (secure() && secure_dtls()) ? PF_SRTP_BYPASS : 0); if (ret != static_cast<int>(packet->length())) { if (channel->GetError() == EWOULDBLOCK) { @@ -765,15 +559,9 @@ bool BaseChannel::WantsPacket(bool rtcp, talk_base::Buffer* packet) { << packet->length(); return false; } - // If this channel is suppose to handle RTP data, that is determined by - // checking against ssrc filter. This is necessary to do it here to avoid - // double decryption. - if (ssrc_filter_.IsActive() && - !ssrc_filter_.DemuxPacket(packet->data(), packet->length(), rtcp)) { - return false; - } - return true; + // Bundle filter handles both rtp and rtcp packets. + return bundle_filter_.DemuxPacket(packet->data(), packet->length(), rtcp); } void BaseChannel::HandlePacket(bool rtcp, talk_base::Buffer* packet, @@ -858,10 +646,11 @@ void BaseChannel::OnNewLocalDescription( GetFirstContent(session->local_description()); const MediaContentDescription* content_desc = GetContentDescription(content_info); + std::string error_desc; if (content_desc && content_info && !content_info->rejected && - !SetLocalContent(content_desc, action)) { + !SetLocalContent(content_desc, action, &error_desc)) { + SetSessionError(session_, BaseSession::ERROR_CONTENT, error_desc); LOG(LS_ERROR) << "Failure in SetLocalContent with action " << action; - session->SetError(BaseSession::ERROR_CONTENT); } } @@ -871,10 +660,11 @@ void BaseChannel::OnNewRemoteDescription( GetFirstContent(session->remote_description()); const MediaContentDescription* content_desc = GetContentDescription(content_info); + std::string error_desc; if (content_desc && content_info && !content_info->rejected && - !SetRemoteContent(content_desc, action)) { - LOG(LS_ERROR) << "Failure in SetRemoteContent with action " << action; - session->SetError(BaseSession::ERROR_CONTENT); + !SetRemoteContent(content_desc, action, &error_desc)) { + SetSessionError(session_, BaseSession::ERROR_CONTENT, error_desc); + LOG(LS_ERROR) << "Failure in SetRemoteContent with action " << action; } } @@ -939,19 +729,27 @@ void BaseChannel::ChannelWritable_w() { // If we're doing DTLS-SRTP, now is the time. if (!was_ever_writable_ && ShouldSetupDtlsSrtp()) { if (!SetupDtlsSrtp(false)) { - LOG(LS_ERROR) << "Couldn't finish DTLS-SRTP on RTP channel"; - SessionErrorMessageData data(BaseSession::ERROR_TRANSPORT); + const std::string error_desc = + "Couldn't set up DTLS-SRTP on RTP channel."; // Sent synchronously. - signaling_thread()->Send(this, MSG_SESSION_ERROR, &data); + signaling_thread()->Invoke<void>(Bind( + &SetSessionError, + session_, + BaseSession::ERROR_TRANSPORT, + error_desc)); return; } if (rtcp_transport_channel_) { if (!SetupDtlsSrtp(true)) { - LOG(LS_ERROR) << "Couldn't finish DTLS-SRTP on RTCP channel"; - SessionErrorMessageData data(BaseSession::ERROR_TRANSPORT); + const std::string error_desc = + "Couldn't set up DTLS-SRTP on RTCP channel"; // Sent synchronously. - signaling_thread()->Send(this, MSG_SESSION_ERROR, &data); + signaling_thread()->Invoke<void>(Bind( + &SetSessionError, + session_, + BaseSession::ERROR_TRANSPORT, + error_desc)); return; } } @@ -1086,62 +884,69 @@ void BaseChannel::ChannelNotWritable_w() { ChangeState(); } -// Sets the maximum video bandwidth for automatic bandwidth adjustment. -bool BaseChannel::SetMaxSendBandwidth_w(int max_bandwidth) { - return media_channel()->SetSendBandwidth(true, max_bandwidth); -} - // |dtls| will be set to true if DTLS is active for transport channel and // crypto is empty. bool BaseChannel::CheckSrtpConfig(const std::vector<CryptoParams>& cryptos, - bool* dtls) { + bool* dtls, + std::string* error_desc) { *dtls = transport_channel_->IsDtlsActive(); if (*dtls && !cryptos.empty()) { - LOG(LS_WARNING) << "Cryptos must be empty when DTLS is active."; + SafeSetError("Cryptos must be empty when DTLS is active.", + error_desc); return false; } return true; } bool BaseChannel::SetSrtp_w(const std::vector<CryptoParams>& cryptos, - ContentAction action, ContentSource src) { + ContentAction action, + ContentSource src, + std::string* error_desc) { + if (action == CA_UPDATE) { + // no crypto params. + return true; + } bool ret = false; bool dtls = false; - ret = CheckSrtpConfig(cryptos, &dtls); + ret = CheckSrtpConfig(cryptos, &dtls, error_desc); + if (!ret) { + return false; + } switch (action) { case CA_OFFER: // If DTLS is already active on the channel, we could be renegotiating // here. We don't update the srtp filter. - if (ret && !dtls) { + if (!dtls) { ret = srtp_filter_.SetOffer(cryptos, src); } break; case CA_PRANSWER: // If we're doing DTLS-SRTP, we don't want to update the filter // with an answer, because we already have SRTP parameters. - if (ret && !dtls) { + if (!dtls) { ret = srtp_filter_.SetProvisionalAnswer(cryptos, src); } break; case CA_ANSWER: // If we're doing DTLS-SRTP, we don't want to update the filter // with an answer, because we already have SRTP parameters. - if (ret && !dtls) { + if (!dtls) { ret = srtp_filter_.SetAnswer(cryptos, src); } break; - case CA_UPDATE: - // no crypto params. - ret = true; - break; default: break; } - return ret; + if (!ret) { + SafeSetError("Failed to setup SRTP filter.", error_desc); + return false; + } + return true; } bool BaseChannel::SetRtcpMux_w(bool enable, ContentAction action, - ContentSource src) { + ContentSource src, + std::string* error_desc) { bool ret = false; switch (action) { case CA_OFFER: @@ -1163,17 +968,21 @@ bool BaseChannel::SetRtcpMux_w(bool enable, ContentAction action, default: break; } + if (!ret) { + SafeSetError("Failed to setup RTCP mux filter.", error_desc); + return false; + } // |rtcp_mux_filter_| can be active if |action| is CA_PRANSWER or // CA_ANSWER, but we only want to tear down the RTCP transport channel if we // received a final answer. - if (ret && rtcp_mux_filter_.IsActive()) { + if (rtcp_mux_filter_.IsActive()) { // If the RTP transport is already writable, then so are we. if (transport_channel_->writable()) { ChannelWritable_w(); } } - return ret; + return true; } bool BaseChannel::AddRecvStream_w(const StreamParams& sp) { @@ -1181,27 +990,18 @@ bool BaseChannel::AddRecvStream_w(const StreamParams& sp) { if (!media_channel()->AddRecvStream(sp)) return false; - return ssrc_filter_.AddStream(sp); + return bundle_filter_.AddStream(sp); } bool BaseChannel::RemoveRecvStream_w(uint32 ssrc) { ASSERT(worker_thread() == talk_base::Thread::Current()); - ssrc_filter_.RemoveStream(ssrc); + bundle_filter_.RemoveStream(ssrc); return media_channel()->RemoveRecvStream(ssrc); } -bool BaseChannel::AddSendStream_w(const StreamParams& sp) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - return media_channel()->AddSendStream(sp); -} - -bool BaseChannel::RemoveSendStream_w(uint32 ssrc) { - ASSERT(worker_thread() == talk_base::Thread::Current()); - return media_channel()->RemoveSendStream(ssrc); -} - bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams, - ContentAction action) { + ContentAction action, + std::string* error_desc) { if (!VERIFY(action == CA_OFFER || action == CA_ANSWER || action == CA_PRANSWER || action == CA_UPDATE)) return false; @@ -1218,15 +1018,18 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams, local_streams_.push_back(*it); LOG(LS_INFO) << "Add send stream ssrc: " << it->first_ssrc(); } else { - LOG(LS_INFO) << "Failed to add send stream ssrc: " - << it->first_ssrc(); + std::ostringstream desc; + desc << "Failed to add send stream ssrc: " << it->first_ssrc(); + SafeSetError(desc.str(), error_desc); return false; } } else if (stream_exist && !it->has_ssrcs()) { if (!media_channel()->RemoveSendStream(existing_stream.first_ssrc())) { - LOG(LS_ERROR) << "Failed to remove send stream with ssrc " - << it->first_ssrc() << "."; - return false; + std::ostringstream desc; + desc << "Failed to remove send stream with ssrc " + << it->first_ssrc() << "."; + SafeSetError(desc.str(), error_desc); + return false; } RemoveStreamBySsrc(&local_streams_, existing_stream.first_ssrc()); } else { @@ -1243,8 +1046,10 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams, it != local_streams_.end(); ++it) { if (!GetStreamBySsrc(streams, it->first_ssrc(), NULL)) { if (!media_channel()->RemoveSendStream(it->first_ssrc())) { - LOG(LS_ERROR) << "Failed to remove send stream with ssrc " - << it->first_ssrc() << "."; + std::ostringstream desc; + desc << "Failed to remove send stream with ssrc " + << it->first_ssrc() << "."; + SafeSetError(desc.str(), error_desc); ret = false; } } @@ -1256,7 +1061,9 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams, if (media_channel()->AddSendStream(*it)) { LOG(LS_INFO) << "Add send ssrc: " << it->ssrcs[0]; } else { - LOG(LS_INFO) << "Failed to add send stream ssrc: " << it->first_ssrc(); + std::ostringstream desc; + desc << "Failed to add send stream ssrc: " << it->first_ssrc(); + SafeSetError(desc.str(), error_desc); ret = false; } } @@ -1267,7 +1074,8 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams, bool BaseChannel::UpdateRemoteStreams_w( const std::vector<StreamParams>& streams, - ContentAction action) { + ContentAction action, + std::string* error_desc) { if (!VERIFY(action == CA_OFFER || action == CA_ANSWER || action == CA_PRANSWER || action == CA_UPDATE)) return false; @@ -1284,15 +1092,18 @@ bool BaseChannel::UpdateRemoteStreams_w( remote_streams_.push_back(*it); LOG(LS_INFO) << "Add remote stream ssrc: " << it->first_ssrc(); } else { - LOG(LS_INFO) << "Failed to add remote stream ssrc: " - << it->first_ssrc(); + std::ostringstream desc; + desc << "Failed to add remote stream ssrc: " << it->first_ssrc(); + SafeSetError(desc.str(), error_desc); return false; } } else if (stream_exists && !it->has_ssrcs()) { if (!RemoveRecvStream_w(existing_stream.first_ssrc())) { - LOG(LS_ERROR) << "Failed to remove remote stream with ssrc " - << it->first_ssrc() << "."; - return false; + std::ostringstream desc; + desc << "Failed to remove remote stream with ssrc " + << it->first_ssrc() << "."; + SafeSetError(desc.str(), error_desc); + return false; } RemoveStreamBySsrc(&remote_streams_, existing_stream.first_ssrc()); } else { @@ -1312,8 +1123,10 @@ bool BaseChannel::UpdateRemoteStreams_w( it != remote_streams_.end(); ++it) { if (!GetStreamBySsrc(streams, it->first_ssrc(), NULL)) { if (!RemoveRecvStream_w(it->first_ssrc())) { - LOG(LS_ERROR) << "Failed to remove remote stream with ssrc " - << it->first_ssrc() << "."; + std::ostringstream desc; + desc << "Failed to remove remote stream with ssrc " + << it->first_ssrc() << "."; + SafeSetError(desc.str(), error_desc); ret = false; } } @@ -1325,8 +1138,9 @@ bool BaseChannel::UpdateRemoteStreams_w( if (AddRecvStream_w(*it)) { LOG(LS_INFO) << "Add remote ssrc: " << it->ssrcs[0]; } else { - LOG(LS_INFO) << "Failed to add remote stream ssrc: " - << it->first_ssrc(); + std::ostringstream desc; + desc << "Failed to add remote stream ssrc: " << it->first_ssrc(); + SafeSetError(desc.str(), error_desc); ret = false; } } @@ -1336,96 +1150,73 @@ bool BaseChannel::UpdateRemoteStreams_w( } bool BaseChannel::SetBaseLocalContent_w(const MediaContentDescription* content, - ContentAction action) { + ContentAction action, + std::string* error_desc) { // Cache secure_required_ for belt and suspenders check on SendPacket - secure_required_ = content->crypto_required(); - bool ret = UpdateLocalStreams_w(content->streams(), action); + secure_required_ = content->crypto_required() != CT_NONE; + bool ret = UpdateLocalStreams_w(content->streams(), action, error_desc); // Set local SRTP parameters (what we will encrypt with). - ret &= SetSrtp_w(content->cryptos(), action, CS_LOCAL); + ret &= SetSrtp_w(content->cryptos(), action, CS_LOCAL, error_desc); // Set local RTCP mux parameters. - ret &= SetRtcpMux_w(content->rtcp_mux(), action, CS_LOCAL); + ret &= SetRtcpMux_w(content->rtcp_mux(), action, CS_LOCAL, error_desc); // Set local RTP header extensions. if (content->rtp_header_extensions_set()) { - ret &= media_channel()->SetRecvRtpHeaderExtensions( - content->rtp_header_extensions()); + if (!media_channel()->SetRecvRtpHeaderExtensions( + content->rtp_header_extensions())) { + std::ostringstream desc; + desc << "Failed to set receive rtp header extensions for " + << MediaTypeToString(content->type()) << " content."; + SafeSetError(desc.str(), error_desc); + ret = false; + } } set_local_content_direction(content->direction()); return ret; } bool BaseChannel::SetBaseRemoteContent_w(const MediaContentDescription* content, - ContentAction action) { - bool ret = UpdateRemoteStreams_w(content->streams(), action); + ContentAction action, + std::string* error_desc) { + bool ret = UpdateRemoteStreams_w(content->streams(), action, error_desc); // Set remote SRTP parameters (what the other side will encrypt with). - ret &= SetSrtp_w(content->cryptos(), action, CS_REMOTE); + ret &= SetSrtp_w(content->cryptos(), action, CS_REMOTE, error_desc); // Set remote RTCP mux parameters. - ret &= SetRtcpMux_w(content->rtcp_mux(), action, CS_REMOTE); + ret &= SetRtcpMux_w(content->rtcp_mux(), action, CS_REMOTE, error_desc); // Set remote RTP header extensions. if (content->rtp_header_extensions_set()) { - ret &= media_channel()->SetSendRtpHeaderExtensions( - content->rtp_header_extensions()); + if (!media_channel()->SetSendRtpHeaderExtensions( + content->rtp_header_extensions())) { + std::ostringstream desc; + desc << "Failed to set send rtp header extensions for " + << MediaTypeToString(content->type()) << " content."; + SafeSetError(desc.str(), error_desc); + ret = false; + } else { + MaybeCacheRtpAbsSendTimeHeaderExtension(content->rtp_header_extensions()); + } } - if (content->bandwidth() != kAutoBandwidth) { - ret &= media_channel()->SetSendBandwidth(false, content->bandwidth()); + + if (!media_channel()->SetMaxSendBandwidth(content->bandwidth())) { + std::ostringstream desc; + desc << "Failed to set max send bandwidth for " + << MediaTypeToString(content->type()) << " content."; + SafeSetError(desc.str(), error_desc); + ret = false; } set_remote_content_direction(content->direction()); return ret; } +void BaseChannel::MaybeCacheRtpAbsSendTimeHeaderExtension( + const std::vector<RtpHeaderExtension>& extensions) { + const RtpHeaderExtension* send_time_extension = + FindHeaderExtension(extensions, kRtpAbsoluteSenderTimeHeaderExtension); + rtp_abs_sendtime_extn_id_ = + send_time_extension ? send_time_extension->id : -1; +} + void BaseChannel::OnMessage(talk_base::Message *pmsg) { switch (pmsg->message_id) { - case MSG_ENABLE: - EnableMedia_w(); - break; - case MSG_DISABLE: - DisableMedia_w(); - break; - case MSG_MUTESTREAM: { - MuteStreamData* data = static_cast<MuteStreamData*>(pmsg->pdata); - data->result = MuteStream_w(data->ssrc, data->mute); - break; - } - case MSG_ISSTREAMMUTED: { - SsrcMessageData* data = static_cast<SsrcMessageData*>(pmsg->pdata); - data->result = IsStreamMuted_w(data->ssrc); - break; - } - case MSG_SETLOCALCONTENT: { - SetContentData* data = static_cast<SetContentData*>(pmsg->pdata); - data->result = SetLocalContent_w(data->content, data->action); - break; - } - case MSG_SETREMOTECONTENT: { - SetContentData* data = static_cast<SetContentData*>(pmsg->pdata); - data->result = SetRemoteContent_w(data->content, data->action); - break; - } - case MSG_ADDRECVSTREAM: { - StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata); - data->result = AddRecvStream_w(data->sp); - break; - } - case MSG_REMOVERECVSTREAM: { - SsrcMessageData* data = static_cast<SsrcMessageData*>(pmsg->pdata); - data->result = RemoveRecvStream_w(data->ssrc); - break; - } - case MSG_ADDSENDSTREAM: { - StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata); - data->result = AddSendStream_w(data->sp); - break; - } - case MSG_REMOVESENDSTREAM: { - SsrcMessageData* data = static_cast<SsrcMessageData*>(pmsg->pdata); - data->result = RemoveSendStream_w(data->ssrc); - break; - } - case MSG_SETMAXSENDBANDWIDTH: { - SetBandwidthData* data = static_cast<SetBandwidthData*>(pmsg->pdata); - data->result = SetMaxSendBandwidth_w(data->value); - break; - } - case MSG_RTPPACKET: case MSG_RTCPPACKET: { PacketMessageData* data = static_cast<PacketMessageData*>(pmsg->pdata); @@ -1437,41 +1228,18 @@ void BaseChannel::OnMessage(talk_base::Message *pmsg) { SignalFirstPacketReceived(this); break; } - case MSG_SESSION_ERROR: { - SessionErrorMessageData* data = static_cast<SessionErrorMessageData*> - (pmsg->pdata); - session_->SetError(data->error_); - break; - } } } -void BaseChannel::Send(uint32 id, talk_base::MessageData *pdata) { - worker_thread_->Send(this, id, pdata); -} - -void BaseChannel::Post(uint32 id, talk_base::MessageData *pdata) { - worker_thread_->Post(this, id, pdata); -} - -void BaseChannel::PostDelayed(int cmsDelay, uint32 id, - talk_base::MessageData *pdata) { - worker_thread_->PostDelayed(cmsDelay, this, id, pdata); -} - -void BaseChannel::Clear(uint32 id, talk_base::MessageList* removed) { - worker_thread_->Clear(this, id, removed); -} - void BaseChannel::FlushRtcpMessages() { // Flush all remaining RTCP messages. This should only be called in // destructor. ASSERT(talk_base::Thread::Current() == worker_thread_); talk_base::MessageList rtcp_messages; - Clear(MSG_RTCPPACKET, &rtcp_messages); + worker_thread_->Clear(this, MSG_RTCPPACKET, &rtcp_messages); for (talk_base::MessageList::iterator it = rtcp_messages.begin(); it != rtcp_messages.end(); ++it) { - Send(MSG_RTCPPACKET, it->pdata); + worker_thread_->Send(this, MSG_RTCPPACKET, it->pdata); } } @@ -1510,21 +1278,17 @@ bool VoiceChannel::Init() { } bool VoiceChannel::SetRemoteRenderer(uint32 ssrc, AudioRenderer* renderer) { - AudioRenderMessageData data(ssrc, renderer, false); - Send(MSG_SETRENDERER, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceMediaChannel::SetRemoteRenderer, + media_channel(), ssrc, renderer)); } bool VoiceChannel::SetLocalRenderer(uint32 ssrc, AudioRenderer* renderer) { - AudioRenderMessageData data(ssrc, renderer, true); - Send(MSG_SETRENDERER, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceMediaChannel::SetLocalRenderer, + media_channel(), ssrc, renderer)); } bool VoiceChannel::SetRingbackTone(const void* buf, int len) { - SetRingbackToneMessageData data(buf, len); - Send(MSG_SETRINGBACKTONE, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceChannel::SetRingbackTone_w, this, buf, len)); } // TODO(juberti): Handle early media the right way. We should get an explicit @@ -1535,17 +1299,17 @@ bool VoiceChannel::SetRingbackTone(const void* buf, int len) { void VoiceChannel::SetEarlyMedia(bool enable) { if (enable) { // Start the early media timeout - PostDelayed(kEarlyMediaTimeout, MSG_EARLYMEDIATIMEOUT); + worker_thread()->PostDelayed(kEarlyMediaTimeout, this, + MSG_EARLYMEDIATIMEOUT); } else { // Stop the timeout if currently going. - Clear(MSG_EARLYMEDIATIMEOUT); + worker_thread()->Clear(this, MSG_EARLYMEDIATIMEOUT); } } bool VoiceChannel::PlayRingbackTone(uint32 ssrc, bool play, bool loop) { - PlayRingbackToneMessageData data(ssrc, play, loop); - Send(MSG_PLAYRINGBACKTONE, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceChannel::PlayRingbackTone_w, + this, ssrc, play, loop)); } bool VoiceChannel::PressDTMF(int digit, bool playout) { @@ -1558,27 +1322,24 @@ bool VoiceChannel::PressDTMF(int digit, bool playout) { } bool VoiceChannel::CanInsertDtmf() { - BoolMessageData data(false); - Send(MSG_CANINSERTDTMF, &data); - return data.data(); + return InvokeOnWorker(Bind(&VoiceMediaChannel::CanInsertDtmf, + media_channel())); } bool VoiceChannel::InsertDtmf(uint32 ssrc, int event_code, int duration, int flags) { - DtmfMessageData data(ssrc, event_code, duration, flags); - Send(MSG_INSERTDTMF, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceChannel::InsertDtmf_w, this, + ssrc, event_code, duration, flags)); } bool VoiceChannel::SetOutputScaling(uint32 ssrc, double left, double right) { - ScaleVolumeMessageData data(ssrc, left, right); - Send(MSG_SCALEVOLUME, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceMediaChannel::SetOutputScaling, + media_channel(), ssrc, left, right)); } + bool VoiceChannel::GetStats(VoiceMediaInfo* stats) { - VoiceStatsMessageData data(stats); - Send(MSG_GETSTATS, &data); - return data.result; + return InvokeOnWorker(Bind(&VoiceMediaChannel::GetStats, + media_channel(), stats)); } void VoiceChannel::StartMediaMonitor(int cms) { @@ -1686,25 +1447,36 @@ const ContentInfo* VoiceChannel::GetFirstContent( } bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, - ContentAction action) { + ContentAction action, + std::string* error_desc) { ASSERT(worker_thread() == talk_base::Thread::Current()); LOG(LS_INFO) << "Setting local voice description"; const AudioContentDescription* audio = static_cast<const AudioContentDescription*>(content); ASSERT(audio != NULL); - if (!audio) return false; + if (!audio) { + SafeSetError("Can't find audio content in local description.", error_desc); + return false; + } - bool ret = SetBaseLocalContent_w(content, action); + bool ret = SetBaseLocalContent_w(content, action, error_desc); // Set local audio codecs (what we want to receive). // TODO(whyuan): Change action != CA_UPDATE to !audio->partial() when partial // is set properly. if (action != CA_UPDATE || audio->has_codecs()) { - ret &= media_channel()->SetRecvCodecs(audio->codecs()); + if (!media_channel()->SetRecvCodecs(audio->codecs())) { + SafeSetError("Failed to set audio receive codecs.", error_desc); + ret = false; + } } // If everything worked, see if we can start receiving. if (ret) { + std::vector<AudioCodec>::const_iterator it = audio->codecs().begin(); + for (; it != audio->codecs().end(); ++it) { + bundle_filter()->AddPayloadType(it->id); + } ChangeState(); } else { LOG(LS_WARNING) << "Failed to set local voice description"; @@ -1713,22 +1485,29 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, } bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action) { + ContentAction action, + std::string* error_desc) { ASSERT(worker_thread() == talk_base::Thread::Current()); LOG(LS_INFO) << "Setting remote voice description"; const AudioContentDescription* audio = static_cast<const AudioContentDescription*>(content); ASSERT(audio != NULL); - if (!audio) return false; + if (!audio) { + SafeSetError("Can't find audio content in remote description.", error_desc); + return false; + } bool ret = true; // Set remote video codecs (what the other side wants to receive). if (action != CA_UPDATE || audio->has_codecs()) { - ret &= media_channel()->SetSendCodecs(audio->codecs()); + if (!media_channel()->SetSendCodecs(audio->codecs())) { + SafeSetError("Failed to set audio send codecs.", error_desc); + ret = false; + } } - ret &= SetBaseRemoteContent_w(content, action); + ret &= SetBaseRemoteContent_w(content, action, error_desc); if (action != CA_UPDATE) { // Tweak our audio processing settings, if needed. @@ -1781,10 +1560,6 @@ void VoiceChannel::HandleEarlyMediaTimeout() { } } -bool VoiceChannel::CanInsertDtmf_w() { - return media_channel()->CanInsertDtmf(); -} - bool VoiceChannel::InsertDtmf_w(uint32 ssrc, int event, int duration, int flags) { if (!enabled()) { @@ -1794,74 +1569,16 @@ bool VoiceChannel::InsertDtmf_w(uint32 ssrc, int event, int duration, return media_channel()->InsertDtmf(ssrc, event, duration, flags); } -bool VoiceChannel::SetOutputScaling_w(uint32 ssrc, double left, double right) { - return media_channel()->SetOutputScaling(ssrc, left, right); -} - -bool VoiceChannel::GetStats_w(VoiceMediaInfo* stats) { - return media_channel()->GetStats(stats); -} - bool VoiceChannel::SetChannelOptions(const AudioOptions& options) { - AudioOptionsMessageData data(options); - Send(MSG_SETCHANNELOPTIONS, &data); - return data.result; -} - -bool VoiceChannel::SetChannelOptions_w(const AudioOptions& options) { - return media_channel()->SetOptions(options); -} - -bool VoiceChannel::SetRenderer_w(uint32 ssrc, AudioRenderer* renderer, - bool is_local) { - if (is_local) - return media_channel()->SetLocalRenderer(ssrc, renderer); - - return media_channel()->SetRemoteRenderer(ssrc, renderer); + return InvokeOnWorker(Bind(&VoiceMediaChannel::SetOptions, + media_channel(), options)); } void VoiceChannel::OnMessage(talk_base::Message *pmsg) { switch (pmsg->message_id) { - case MSG_SETRINGBACKTONE: { - SetRingbackToneMessageData* data = - static_cast<SetRingbackToneMessageData*>(pmsg->pdata); - data->result = SetRingbackTone_w(data->buf, data->len); - break; - } - case MSG_PLAYRINGBACKTONE: { - PlayRingbackToneMessageData* data = - static_cast<PlayRingbackToneMessageData*>(pmsg->pdata); - data->result = PlayRingbackTone_w(data->ssrc, data->play, data->loop); - break; - } case MSG_EARLYMEDIATIMEOUT: HandleEarlyMediaTimeout(); break; - case MSG_CANINSERTDTMF: { - BoolMessageData* data = - static_cast<BoolMessageData*>(pmsg->pdata); - data->data() = CanInsertDtmf_w(); - break; - } - case MSG_INSERTDTMF: { - DtmfMessageData* data = - static_cast<DtmfMessageData*>(pmsg->pdata); - data->result = InsertDtmf_w(data->ssrc, data->event, data->duration, - data->flags); - break; - } - case MSG_SCALEVOLUME: { - ScaleVolumeMessageData* data = - static_cast<ScaleVolumeMessageData*>(pmsg->pdata); - data->result = SetOutputScaling_w(data->ssrc, data->left, data->right); - break; - } - case MSG_GETSTATS: { - VoiceStatsMessageData* data = - static_cast<VoiceStatsMessageData*>(pmsg->pdata); - data->result = GetStats_w(data->stats); - break; - } case MSG_CHANNEL_ERROR: { VoiceChannelErrorMessageData* data = static_cast<VoiceChannelErrorMessageData*>(pmsg->pdata); @@ -1869,18 +1586,6 @@ void VoiceChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } - case MSG_SETCHANNELOPTIONS: { - AudioOptionsMessageData* data = - static_cast<AudioOptionsMessageData*>(pmsg->pdata); - data->result = SetChannelOptions_w(data->options); - break; - } - case MSG_SETRENDERER: { - AudioRenderMessageData* data = - static_cast<AudioRenderMessageData*>(pmsg->pdata); - data->result = SetRenderer_w(data->ssrc, data->renderer, data->is_local); - break; - } default: BaseChannel::OnMessage(pmsg); break; @@ -1994,68 +1699,65 @@ VideoChannel::~VideoChannel() { } bool VideoChannel::SetRenderer(uint32 ssrc, VideoRenderer* renderer) { - VideoRenderMessageData data(ssrc, renderer); - Send(MSG_SETRENDERER, &data); + worker_thread()->Invoke<void>(Bind( + &VideoMediaChannel::SetRenderer, media_channel(), ssrc, renderer)); return true; } bool VideoChannel::ApplyViewRequest(const ViewRequest& request) { - ViewRequestMessageData data(request); - Send(MSG_HANDLEVIEWREQUEST, &data); - return data.result; + return InvokeOnWorker(Bind(&VideoChannel::ApplyViewRequest_w, this, request)); } VideoCapturer* VideoChannel::AddScreencast( uint32 ssrc, const ScreencastId& id) { - AddScreencastMessageData data(ssrc, id); - Send(MSG_ADDSCREENCAST, &data); - return data.result; + return worker_thread()->Invoke<VideoCapturer*>(Bind( + &VideoChannel::AddScreencast_w, this, ssrc, id)); } bool VideoChannel::SetCapturer(uint32 ssrc, VideoCapturer* capturer) { - SetCapturerMessageData data(ssrc, capturer); - Send(MSG_SETCAPTURER, &data); - return data.result; + return InvokeOnWorker(Bind(&VideoMediaChannel::SetCapturer, + media_channel(), ssrc, capturer)); } bool VideoChannel::RemoveScreencast(uint32 ssrc) { - RemoveScreencastMessageData data(ssrc); - Send(MSG_REMOVESCREENCAST, &data); - return data.result; + return InvokeOnWorker(Bind(&VideoChannel::RemoveScreencast_w, this, ssrc)); } bool VideoChannel::IsScreencasting() { - IsScreencastingMessageData data; - Send(MSG_ISSCREENCASTING, &data); - return data.result; + return InvokeOnWorker(Bind(&VideoChannel::IsScreencasting_w, this)); } int VideoChannel::GetScreencastFps(uint32 ssrc) { - ScreencastDetailsMessageData data(ssrc); - Send(MSG_GETSCREENCASTDETAILS, &data); + ScreencastDetailsData data(ssrc); + worker_thread()->Invoke<void>(Bind( + &VideoChannel::GetScreencastDetails_w, this, &data)); return data.fps; } int VideoChannel::GetScreencastMaxPixels(uint32 ssrc) { - ScreencastDetailsMessageData data(ssrc); - Send(MSG_GETSCREENCASTDETAILS, &data); + ScreencastDetailsData data(ssrc); + worker_thread()->Invoke<void>(Bind( + &VideoChannel::GetScreencastDetails_w, this, &data)); return data.screencast_max_pixels; } bool VideoChannel::SendIntraFrame() { - Send(MSG_SENDINTRAFRAME); + worker_thread()->Invoke<void>(Bind( + &VideoMediaChannel::SendIntraFrame, media_channel())); return true; } bool VideoChannel::RequestIntraFrame() { - Send(MSG_REQUESTINTRAFRAME); + worker_thread()->Invoke<void>(Bind( + &VideoMediaChannel::RequestIntraFrame, media_channel())); return true; } void VideoChannel::SetScreenCaptureFactory( ScreenCapturerFactory* screencapture_factory) { - SetScreenCaptureFactoryMessageData data(screencapture_factory); - Send(MSG_SETSCREENCASTFACTORY, &data); + worker_thread()->Invoke<void>(Bind( + &VideoChannel::SetScreenCaptureFactory_w, + this, screencapture_factory)); } void VideoChannel::ChangeState() { @@ -2078,10 +1780,10 @@ void VideoChannel::ChangeState() { LOG(LS_INFO) << "Changing video state, recv=" << recv << " send=" << send; } -bool VideoChannel::GetStats(VideoMediaInfo* stats) { - VideoStatsMessageData data(stats); - Send(MSG_GETSTATS, &data); - return data.result; +bool VideoChannel::GetStats( + const StatsOptions& options, VideoMediaInfo* stats) { + return InvokeOnWorker(Bind(&VideoMediaChannel::GetStats, + media_channel(), options, stats)); } void VideoChannel::StartMediaMonitor(int cms) { @@ -2105,19 +1807,26 @@ const ContentInfo* VideoChannel::GetFirstContent( } bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, - ContentAction action) { + ContentAction action, + std::string* error_desc) { ASSERT(worker_thread() == talk_base::Thread::Current()); LOG(LS_INFO) << "Setting local video description"; const VideoContentDescription* video = static_cast<const VideoContentDescription*>(content); ASSERT(video != NULL); - if (!video) return false; + if (!video) { + SafeSetError("Can't find video content in local description.", error_desc); + return false; + } - bool ret = SetBaseLocalContent_w(content, action); + bool ret = SetBaseLocalContent_w(content, action, error_desc); // Set local video codecs (what we want to receive). if (action != CA_UPDATE || video->has_codecs()) { - ret &= media_channel()->SetRecvCodecs(video->codecs()); + if (!media_channel()->SetRecvCodecs(video->codecs())) { + SafeSetError("Failed to set video receive codecs.", error_desc); + ret = false; + } } if (action != CA_UPDATE) { @@ -2133,6 +1842,10 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, // If everything worked, see if we can start receiving. if (ret) { + std::vector<VideoCodec>::const_iterator it = video->codecs().begin(); + for (; it != video->codecs().end(); ++it) { + bundle_filter()->AddPayloadType(it->id); + } ChangeState(); } else { LOG(LS_WARNING) << "Failed to set local video description"; @@ -2141,22 +1854,29 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, } bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action) { + ContentAction action, + std::string* error_desc) { ASSERT(worker_thread() == talk_base::Thread::Current()); LOG(LS_INFO) << "Setting remote video description"; const VideoContentDescription* video = static_cast<const VideoContentDescription*>(content); ASSERT(video != NULL); - if (!video) return false; + if (!video) { + SafeSetError("Can't find video content in remote description.", error_desc); + return false; + } bool ret = true; // Set remote video codecs (what the other side wants to receive). if (action != CA_UPDATE || video->has_codecs()) { - ret &= media_channel()->SetSendCodecs(video->codecs()); + if (!media_channel()->SetSendCodecs(video->codecs())) { + SafeSetError("Failed to set video send codecs.", error_desc); + ret = false; + } } - ret &= SetBaseRemoteContent_w(content, action); + ret &= SetBaseRemoteContent_w(content, action, error_desc); if (action != CA_UPDATE) { // Tweak our video processing settings, if needed. @@ -2219,10 +1939,6 @@ bool VideoChannel::ApplyViewRequest_w(const ViewRequest& request) { return ret; } -void VideoChannel::SetRenderer_w(uint32 ssrc, VideoRenderer* renderer) { - media_channel()->SetRenderer(ssrc, renderer); -} - VideoCapturer* VideoChannel::AddScreencast_w( uint32 ssrc, const ScreencastId& id) { if (screencast_capturers_.find(ssrc) != screencast_capturers_.end()) { @@ -2239,10 +1955,6 @@ VideoCapturer* VideoChannel::AddScreencast_w( return screen_capturer; } -bool VideoChannel::SetCapturer_w(uint32 ssrc, VideoCapturer* capturer) { - return media_channel()->SetCapturer(ssrc, capturer); -} - bool VideoChannel::RemoveScreencast_w(uint32 ssrc) { ScreencastMap::iterator iter = screencast_capturers_.find(ssrc); if (iter == screencast_capturers_.end()) { @@ -2258,8 +1970,8 @@ bool VideoChannel::IsScreencasting_w() const { return !screencast_capturers_.empty(); } -void VideoChannel::ScreencastDetails_w( - ScreencastDetailsMessageData* data) const { +void VideoChannel::GetScreencastDetails_w( + ScreencastDetailsData* data) const { ScreencastMap::const_iterator iter = screencast_capturers_.find(data->ssrc); if (iter == screencast_capturers_.end()) { return; @@ -2279,10 +1991,6 @@ void VideoChannel::SetScreenCaptureFactory_w( } } -bool VideoChannel::GetStats_w(VideoMediaInfo* stats) { - return media_channel()->GetStats(stats); -} - void VideoChannel::OnScreencastWindowEvent_s(uint32 ssrc, talk_base::WindowEvent we) { ASSERT(signaling_thread() == talk_base::Thread::Current()); @@ -2290,41 +1998,12 @@ void VideoChannel::OnScreencastWindowEvent_s(uint32 ssrc, } bool VideoChannel::SetChannelOptions(const VideoOptions &options) { - VideoOptionsMessageData data(options); - Send(MSG_SETCHANNELOPTIONS, &data); - return data.result; -} - -bool VideoChannel::SetChannelOptions_w(const VideoOptions &options) { - return media_channel()->SetOptions(options); + return InvokeOnWorker(Bind(&VideoMediaChannel::SetOptions, + media_channel(), options)); } void VideoChannel::OnMessage(talk_base::Message *pmsg) { switch (pmsg->message_id) { - case MSG_SETRENDERER: { - const VideoRenderMessageData* data = - static_cast<VideoRenderMessageData*>(pmsg->pdata); - SetRenderer_w(data->ssrc, data->renderer); - break; - } - case MSG_ADDSCREENCAST: { - AddScreencastMessageData* data = - static_cast<AddScreencastMessageData*>(pmsg->pdata); - data->result = AddScreencast_w(data->ssrc, data->window_id); - break; - } - case MSG_SETCAPTURER: { - SetCapturerMessageData* data = - static_cast<SetCapturerMessageData*>(pmsg->pdata); - data->result = SetCapturer_w(data->ssrc, data->capturer); - break; - } - case MSG_REMOVESCREENCAST: { - RemoveScreencastMessageData* data = - static_cast<RemoveScreencastMessageData*>(pmsg->pdata); - data->result = RemoveScreencast_w(data->ssrc); - break; - } case MSG_SCREENCASTWINDOWEVENT: { const ScreencastEventMessageData* data = static_cast<ScreencastEventMessageData*>(pmsg->pdata); @@ -2332,32 +2011,6 @@ void VideoChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } - case MSG_ISSCREENCASTING: { - IsScreencastingMessageData* data = - static_cast<IsScreencastingMessageData*>(pmsg->pdata); - data->result = IsScreencasting_w(); - break; - } - case MSG_GETSCREENCASTDETAILS: { - ScreencastDetailsMessageData* data = - static_cast<ScreencastDetailsMessageData*>(pmsg->pdata); - ScreencastDetails_w(data); - break; - } - case MSG_SENDINTRAFRAME: { - SendIntraFrame_w(); - break; - } - case MSG_REQUESTINTRAFRAME: { - RequestIntraFrame_w(); - break; - } - case MSG_SETCHANNELOPTIONS: { - VideoOptionsMessageData* data = - static_cast<VideoOptionsMessageData*>(pmsg->pdata); - data->result = SetChannelOptions_w(data->options); - break; - } case MSG_CHANNEL_ERROR: { const VideoChannelErrorMessageData* data = static_cast<VideoChannelErrorMessageData*>(pmsg->pdata); @@ -2365,24 +2018,6 @@ void VideoChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } - case MSG_HANDLEVIEWREQUEST: { - ViewRequestMessageData* data = - static_cast<ViewRequestMessageData*>(pmsg->pdata); - data->result = ApplyViewRequest_w(data->request); - break; - } - case MSG_SETSCREENCASTFACTORY: { - SetScreenCaptureFactoryMessageData* data = - static_cast<SetScreenCaptureFactoryMessageData*>(pmsg->pdata); - SetScreenCaptureFactory_w(data->screencapture_factory); - break; - } - case MSG_GETSTATS: { - VideoStatsMessageData* data = - static_cast<VideoStatsMessageData*>(pmsg->pdata); - data->result = GetStats_w(data->stats); - break; - } default: BaseChannel::OnMessage(pmsg); break; @@ -2428,9 +2063,8 @@ void VideoChannel::OnStateChange(VideoCapturer* capturer, CaptureState ev) { if (!GetLocalSsrc(capturer, &ssrc)) { return; } - ScreencastEventMessageData* pdata = - new ScreencastEventMessageData(ssrc, we); - signaling_thread()->Post(this, MSG_SCREENCASTWINDOWEVENT, pdata); + + OnScreencastWindowEvent(ssrc, we); } bool VideoChannel::GetLocalSsrc(const VideoCapturer* capturer, uint32* ssrc) { @@ -2515,8 +2149,8 @@ bool DataChannel::Init() { this, &DataChannel::OnDataChannelError); media_channel()->SignalReadyToSend.connect( this, &DataChannel::OnDataChannelReadyToSend); - media_channel()->SignalNewStreamReceived.connect( - this, &DataChannel::OnDataChannelNewStreamReceived); + media_channel()->SignalStreamClosedRemotely.connect( + this, &DataChannel::OnStreamClosedRemotely); srtp_filter()->SignalSrtpError.connect( this, &DataChannel::OnSrtpError); return true; @@ -2525,9 +2159,8 @@ bool DataChannel::Init() { bool DataChannel::SendData(const SendDataParams& params, const talk_base::Buffer& payload, SendDataResult* result) { - SendDataMessageData message_data(params, &payload, result); - Send(MSG_SENDDATA, &message_data); - return message_data.succeeded; + return InvokeOnWorker(Bind(&DataMediaChannel::SendData, + media_channel(), params, payload, result)); } const ContentInfo* DataChannel::GetFirstContent( @@ -2556,13 +2189,8 @@ bool DataChannel::WantsPacket(bool rtcp, talk_base::Buffer* packet) { return false; } -// Sets the maximum bandwidth. Anything over this will be dropped. -bool DataChannel::SetMaxSendBandwidth_w(int max_bps) { - LOG(LS_INFO) << "DataChannel: Setting max bandwidth to " << max_bps; - return media_channel()->SetSendBandwidth(false, max_bps); -} - -bool DataChannel::SetDataChannelType(DataChannelType new_data_channel_type) { +bool DataChannel::SetDataChannelType(DataChannelType new_data_channel_type, + std::string* error_desc) { // It hasn't been set before, so set it now. if (data_channel_type_ == DCT_NONE) { data_channel_type_ = new_data_channel_type; @@ -2571,9 +2199,11 @@ bool DataChannel::SetDataChannelType(DataChannelType new_data_channel_type) { // It's been set before, but doesn't match. That's bad. if (data_channel_type_ != new_data_channel_type) { - LOG(LS_WARNING) << "Data channel type mismatch." - << " Expected " << data_channel_type_ - << " Got " << new_data_channel_type; + std::ostringstream desc; + desc << "Data channel type mismatch." + << " Expected " << data_channel_type_ + << " Got " << new_data_channel_type; + SafeSetError(desc.str(), error_desc); return false; } @@ -2582,47 +2212,61 @@ bool DataChannel::SetDataChannelType(DataChannelType new_data_channel_type) { } bool DataChannel::SetDataChannelTypeFromContent( - const DataContentDescription* content) { + const DataContentDescription* content, + std::string* error_desc) { bool is_sctp = ((content->protocol() == kMediaProtocolSctp) || (content->protocol() == kMediaProtocolDtlsSctp)); DataChannelType data_channel_type = is_sctp ? DCT_SCTP : DCT_RTP; - return SetDataChannelType(data_channel_type); + return SetDataChannelType(data_channel_type, error_desc); } bool DataChannel::SetLocalContent_w(const MediaContentDescription* content, - ContentAction action) { + ContentAction action, + std::string* error_desc) { ASSERT(worker_thread() == talk_base::Thread::Current()); LOG(LS_INFO) << "Setting local data description"; const DataContentDescription* data = static_cast<const DataContentDescription*>(content); ASSERT(data != NULL); - if (!data) return false; + if (!data) { + SafeSetError("Can't find data content in local description.", error_desc); + return false; + } bool ret = false; - if (!SetDataChannelTypeFromContent(data)) { + if (!SetDataChannelTypeFromContent(data, error_desc)) { return false; } if (data_channel_type_ == DCT_SCTP) { // SCTP data channels don't need the rest of the stuff. - ret = UpdateLocalStreams_w(data->streams(), action); + ret = UpdateLocalStreams_w(data->streams(), action, error_desc); if (ret) { set_local_content_direction(content->direction()); // As in SetRemoteContent_w, make sure we set the local SCTP port // number as specified in our DataContentDescription. - ret = media_channel()->SetRecvCodecs(data->codecs()); + if (!media_channel()->SetRecvCodecs(data->codecs())) { + SafeSetError("Failed to set data receive codecs.", error_desc); + ret = false; + } } } else { - ret = SetBaseLocalContent_w(content, action); - + ret = SetBaseLocalContent_w(content, action, error_desc); if (action != CA_UPDATE || data->has_codecs()) { - ret &= media_channel()->SetRecvCodecs(data->codecs()); + if (!media_channel()->SetRecvCodecs(data->codecs())) { + SafeSetError("Failed to set data receive codecs.", error_desc); + ret = false; + } } } // If everything worked, see if we can start receiving. if (ret) { + std::vector<DataCodec>::const_iterator it = data->codecs().begin(); + for (; it != data->codecs().end(); ++it) { + bundle_filter()->AddPayloadType(it->id); + } ChangeState(); } else { LOG(LS_WARNING) << "Failed to set local data description"; @@ -2631,28 +2275,35 @@ bool DataChannel::SetLocalContent_w(const MediaContentDescription* content, } bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action) { + ContentAction action, + std::string* error_desc) { ASSERT(worker_thread() == talk_base::Thread::Current()); const DataContentDescription* data = static_cast<const DataContentDescription*>(content); ASSERT(data != NULL); - if (!data) return false; + if (!data) { + SafeSetError("Can't find data content in remote description.", error_desc); + return false; + } bool ret = true; - if (!SetDataChannelTypeFromContent(data)) { + if (!SetDataChannelTypeFromContent(data, error_desc)) { return false; } if (data_channel_type_ == DCT_SCTP) { LOG(LS_INFO) << "Setting SCTP remote data description"; // SCTP data channels don't need the rest of the stuff. - ret = UpdateRemoteStreams_w(content->streams(), action); + ret = UpdateRemoteStreams_w(content->streams(), action, error_desc); if (ret) { set_remote_content_direction(content->direction()); // We send the SCTP port number (not to be confused with the underlying // UDP port number) as a codec parameter. Make sure it gets there. - ret = media_channel()->SetSendCodecs(data->codecs()); + if (!media_channel()->SetSendCodecs(data->codecs())) { + SafeSetError("Failed to set data send codecs.", error_desc); + ret = false; + } } } else { // If the remote data doesn't have codecs and isn't an update, it @@ -2664,17 +2315,24 @@ bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content, // Set remote video codecs (what the other side wants to receive). if (action != CA_UPDATE || data->has_codecs()) { - ret &= media_channel()->SetSendCodecs(data->codecs()); + if (!media_channel()->SetSendCodecs(data->codecs())) { + SafeSetError("Failed to set data send codecs.", error_desc); + ret = false; + } } if (ret) { - ret &= SetBaseRemoteContent_w(content, action); + ret &= SetBaseRemoteContent_w(content, action, error_desc); } if (action != CA_UPDATE) { int bandwidth_bps = data->bandwidth(); - bool auto_bandwidth = (bandwidth_bps == kAutoBandwidth); - ret &= media_channel()->SetSendBandwidth(auto_bandwidth, bandwidth_bps); + if (!media_channel()->SetMaxSendBandwidth(bandwidth_bps)) { + std::ostringstream desc; + desc << "Failed to set max send bandwidth for data content."; + SafeSetError(desc.str(), error_desc); + ret = false; + } } } @@ -2702,9 +2360,8 @@ void DataChannel::ChangeState() { LOG(LS_ERROR) << "Failed to SetSend on data channel"; } - // Post to trigger SignalReadyToSendData. - signaling_thread()->Post(this, MSG_READYTOSENDDATA, - new DataChannelReadyToSendMessageData(send)); + // Trigger SignalReadyToSendData asynchronously. + OnDataChannelReadyToSend(send); LOG(LS_INFO) << "Changing data state, recv=" << recv << " send=" << send; } @@ -2719,13 +2376,6 @@ void DataChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } - case MSG_SENDDATA: { - SendDataMessageData* msg = - static_cast<SendDataMessageData*>(pmsg->pdata); - msg->succeeded = media_channel()->SendData( - msg->params, *(msg->payload), msg->result); - break; - } case MSG_DATARECEIVED: { DataReceivedMessageData* data = static_cast<DataReceivedMessageData*>(pmsg->pdata); @@ -2740,10 +2390,10 @@ void DataChannel::OnMessage(talk_base::Message *pmsg) { delete data; break; } - case MSG_NEWSTREAMRECEIVED: { - DataChannelNewStreamReceivedMessageData* data = - static_cast<DataChannelNewStreamReceivedMessageData*>(pmsg->pdata); - SignalNewStreamReceived(data->label, data->init); + case MSG_STREAMCLOSEDREMOTELY: { + talk_base::TypedMessageData<uint32>* data = + static_cast<talk_base::TypedMessageData<uint32>*>(pmsg->pdata); + SignalStreamClosedRemotely(data->data()); delete data; break; } @@ -2802,14 +2452,6 @@ void DataChannel::OnDataChannelReadyToSend(bool writable) { new DataChannelReadyToSendMessageData(writable)); } -void DataChannel::OnDataChannelNewStreamReceived( - const std::string& label, const webrtc::DataChannelInit& init) { - signaling_thread()->Post( - this, - MSG_NEWSTREAMRECEIVED, - new DataChannelNewStreamReceivedMessageData(label, init)); -} - void DataChannel::OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, SrtpFilter::Error error) { switch (error) { @@ -2841,4 +2483,10 @@ bool DataChannel::ShouldSetupDtlsSrtp() const { return (data_channel_type_ == DCT_RTP); } +void DataChannel::OnStreamClosedRemotely(uint32 sid) { + talk_base::TypedMessageData<uint32>* message = + new talk_base::TypedMessageData<uint32>(sid); + signaling_thread()->Post(this, MSG_STREAMCLOSEDREMOTELY, message); +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/session/media/channel.h b/chromium/third_party/libjingle/source/talk/session/media/channel.h index d297ee4b8fd..d48a64e10db 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channel.h +++ b/chromium/third_party/libjingle/source/talk/session/media/channel.h @@ -31,7 +31,6 @@ #include <string> #include <vector> -#include "talk/app/webrtc/datachannelinterface.h" #include "talk/base/asyncudpsocket.h" #include "talk/base/criticalsection.h" #include "talk/base/network.h" @@ -45,11 +44,11 @@ #include "talk/p2p/base/session.h" #include "talk/p2p/client/socketmonitor.h" #include "talk/session/media/audiomonitor.h" +#include "talk/session/media/bundlefilter.h" #include "talk/session/media/mediamonitor.h" #include "talk/session/media/mediasession.h" #include "talk/session/media/rtcpmuxfilter.h" #include "talk/session/media/srtpfilter.h" -#include "talk/session/media/ssrcmuxfilter.h" namespace cricket { @@ -112,10 +111,11 @@ class BaseChannel // Channel control bool SetLocalContent(const MediaContentDescription* content, - ContentAction action); + ContentAction action, + std::string* error_desc); bool SetRemoteContent(const MediaContentDescription* content, - ContentAction action); - bool SetMaxSendBandwidth(int max_bandwidth); + ContentAction action, + std::string* error_desc); bool Enable(bool enable); // Mute sending media on the stream with SSRC |ssrc| @@ -213,7 +213,7 @@ class BaseChannel } } - SsrcMuxFilter* ssrc_filter() { return &ssrc_filter_; } + BundleFilter* bundle_filter() { return &bundle_filter_; } const std::vector<StreamParams>& local_streams() const { return local_streams_; @@ -248,12 +248,6 @@ class BaseChannel SrtpFilter* srtp_filter() { return &srtp_filter_; } bool rtcp() const { return rtcp_; } - void Send(uint32 id, talk_base::MessageData* pdata = NULL); - void Post(uint32 id, talk_base::MessageData* pdata = NULL); - void PostDelayed(int cmsDelay, uint32 id = 0, - talk_base::MessageData* pdata = NULL); - void Clear(uint32 id = talk_base::MQID_ANY, - talk_base::MessageList* removed = NULL); void FlushRtcpMessages(); // NetworkInterface implementation, called by MediaEngine @@ -307,24 +301,40 @@ class BaseChannel virtual const ContentInfo* GetFirstContent( const SessionDescription* sdesc) = 0; bool UpdateLocalStreams_w(const std::vector<StreamParams>& streams, - ContentAction action); + ContentAction action, + std::string* error_desc); bool UpdateRemoteStreams_w(const std::vector<StreamParams>& streams, - ContentAction action); + ContentAction action, + std::string* error_desc); bool SetBaseLocalContent_w(const MediaContentDescription* content, - ContentAction action); + ContentAction action, + std::string* error_desc); virtual bool SetLocalContent_w(const MediaContentDescription* content, - ContentAction action) = 0; + ContentAction action, + std::string* error_desc) = 0; bool SetBaseRemoteContent_w(const MediaContentDescription* content, - ContentAction action); + ContentAction action, + std::string* error_desc); virtual bool SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action) = 0; - - bool CheckSrtpConfig(const std::vector<CryptoParams>& cryptos, bool* dtls); - bool SetSrtp_w(const std::vector<CryptoParams>& params, ContentAction action, - ContentSource src); - bool SetRtcpMux_w(bool enable, ContentAction action, ContentSource src); - - virtual bool SetMaxSendBandwidth_w(int max_bandwidth); + ContentAction action, + std::string* error_desc) = 0; + + // Helper method to get RTP Absoulute SendTime extension header id if + // present in remote supported extensions list. + void MaybeCacheRtpAbsSendTimeHeaderExtension( + const std::vector<RtpHeaderExtension>& extensions); + + bool CheckSrtpConfig(const std::vector<CryptoParams>& cryptos, + bool* dtls, + std::string* error_desc); + bool SetSrtp_w(const std::vector<CryptoParams>& params, + ContentAction action, + ContentSource src, + std::string* error_desc); + bool SetRtcpMux_w(bool enable, + ContentAction action, + ContentSource src, + std::string* error_desc); // From MessageHandler virtual void OnMessage(talk_base::Message* pmsg); @@ -335,6 +345,12 @@ class BaseChannel virtual void OnConnectionMonitorUpdate(SocketMonitor* monitor, const std::vector<ConnectionInfo>& infos) = 0; + // Helper function for invoking bool-returning methods on the worker thread. + template <class FunctorT> + bool InvokeOnWorker(const FunctorT& functor) { + return worker_thread_->Invoke<bool>(functor); + } + private: sigslot::signal3<const void*, size_t, bool> SignalSendPacketPreCrypto; sigslot::signal3<const void*, size_t, bool> SignalSendPacketPostCrypto; @@ -356,7 +372,7 @@ class BaseChannel TransportChannel* rtcp_transport_channel_; SrtpFilter srtp_filter_; RtcpMuxFilter rtcp_mux_filter_; - SsrcMuxFilter ssrc_filter_; + BundleFilter bundle_filter_; talk_base::scoped_ptr<SocketMonitor> socket_monitor_; bool enabled_; bool writable_; @@ -369,6 +385,7 @@ class BaseChannel bool has_received_packet_; bool dtls_keyed_; bool secure_required_; + int rtp_abs_sendtime_extn_id_; }; // VoiceChannel is a specialization that adds support for early media, DTMF, @@ -451,13 +468,14 @@ class VoiceChannel : public BaseChannel { virtual void ChangeState(); virtual const ContentInfo* GetFirstContent(const SessionDescription* sdesc); virtual bool SetLocalContent_w(const MediaContentDescription* content, - ContentAction action); + ContentAction action, + std::string* error_desc); virtual bool SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action); + ContentAction action, + std::string* error_desc); bool SetRingbackTone_w(const void* buf, int len); bool PlayRingbackTone_w(uint32 ssrc, bool play, bool loop); void HandleEarlyMediaTimeout(); - bool CanInsertDtmf_w(); bool InsertDtmf_w(uint32 ssrc, int event, int duration, int flags); bool SetOutputScaling_w(uint32 ssrc, double left, double right); bool GetStats_w(VoiceMediaInfo* stats); @@ -472,9 +490,6 @@ class VoiceChannel : public BaseChannel { void OnVoiceChannelError(uint32 ssrc, VoiceMediaChannel::Error error); void SendLastMediaError(); void OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, SrtpFilter::Error error); - // Configuration and setting. - bool SetChannelOptions_w(const AudioOptions& options); - bool SetRenderer_w(uint32 ssrc, AudioRenderer* renderer, bool is_local); static const int kEarlyMediaTimeout = 1000; bool received_media_; @@ -515,7 +530,7 @@ class VideoChannel : public BaseChannel { int GetScreencastFps(uint32 ssrc); int GetScreencastMaxPixels(uint32 ssrc); // Get statistics about the current media session. - bool GetStats(VideoMediaInfo* stats); + bool GetStats(const StatsOptions& options, VideoMediaInfo* stats); sigslot::signal2<VideoChannel*, const std::vector<ConnectionInfo>&> SignalConnectionMonitor; @@ -544,31 +559,24 @@ class VideoChannel : public BaseChannel { private: typedef std::map<uint32, VideoCapturer*> ScreencastMap; - struct ScreencastDetailsMessageData; + struct ScreencastDetailsData; // overrides from BaseChannel virtual void ChangeState(); virtual const ContentInfo* GetFirstContent(const SessionDescription* sdesc); virtual bool SetLocalContent_w(const MediaContentDescription* content, - ContentAction action); + ContentAction action, + std::string* error_desc); virtual bool SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action); - void SendIntraFrame_w() { - media_channel()->SendIntraFrame(); - } - void RequestIntraFrame_w() { - media_channel()->RequestIntraFrame(); - } - + ContentAction action, + std::string* error_desc); bool ApplyViewRequest_w(const ViewRequest& request); - void SetRenderer_w(uint32 ssrc, VideoRenderer* renderer); VideoCapturer* AddScreencast_w(uint32 ssrc, const ScreencastId& id); - bool SetCapturer_w(uint32 ssrc, VideoCapturer* capturer); bool RemoveScreencast_w(uint32 ssrc); void OnScreencastWindowEvent_s(uint32 ssrc, talk_base::WindowEvent we); bool IsScreencasting_w() const; - void ScreencastDetails_w(ScreencastDetailsMessageData* d) const; + void GetScreencastDetails_w(ScreencastDetailsData* d) const; void SetScreenCaptureFactory_w( ScreenCapturerFactory* screencapture_factory); bool GetStats_w(VideoMediaInfo* stats); @@ -586,8 +594,6 @@ class VideoChannel : public BaseChannel { void OnVideoChannelError(uint32 ssrc, VideoMediaChannel::Error error); void OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, SrtpFilter::Error error); - // Configuration and setting. - bool SetChannelOptions_w(const VideoOptions& options); VoiceChannel* voice_channel_; VideoRenderer* renderer_; @@ -634,11 +640,8 @@ class DataChannel : public BaseChannel { // That occurs when the channel is enabled, the transport is writable, // both local and remote descriptions are set, and the channel is unblocked. sigslot::signal1<bool> SignalReadyToSendData; - // Signal for notifying when a new stream is added from the remote side. Used - // for the in-band negotioation through the OPEN message for SCTP data - // channel. - sigslot::signal2<const std::string&, const webrtc::DataChannelInit&> - SignalNewStreamReceived; + // Signal for notifying that the remote side has closed the DataChannel. + sigslot::signal1<uint32> SignalStreamClosedRemotely; protected: // downcasts a MediaChannel. @@ -678,31 +681,23 @@ class DataChannel : public BaseChannel { typedef talk_base::TypedMessageData<bool> DataChannelReadyToSendMessageData; - struct DataChannelNewStreamReceivedMessageData - : public talk_base::MessageData { - DataChannelNewStreamReceivedMessageData( - const std::string& label, const webrtc::DataChannelInit& init) - : label(label), - init(init) { - } - const std::string label; - const webrtc::DataChannelInit init; - }; - // overrides from BaseChannel virtual const ContentInfo* GetFirstContent(const SessionDescription* sdesc); // If data_channel_type_ is DCT_NONE, set it. Otherwise, check that // it's the same as what was set previously. Returns false if it's // set to one type one type and changed to another type later. - bool SetDataChannelType(DataChannelType new_data_channel_type); + bool SetDataChannelType(DataChannelType new_data_channel_type, + std::string* error_desc); // Same as SetDataChannelType, but extracts the type from the // DataContentDescription. - bool SetDataChannelTypeFromContent(const DataContentDescription* content); - virtual bool SetMaxSendBandwidth_w(int max_bandwidth); + bool SetDataChannelTypeFromContent(const DataContentDescription* content, + std::string* error_desc); virtual bool SetLocalContent_w(const MediaContentDescription* content, - ContentAction action); + ContentAction action, + std::string* error_desc); virtual bool SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action); + ContentAction action, + std::string* error_desc); virtual void ChangeState(); virtual bool WantsPacket(bool rtcp, talk_base::Buffer* packet); @@ -717,9 +712,8 @@ class DataChannel : public BaseChannel { const ReceiveDataParams& params, const char* data, size_t len); void OnDataChannelError(uint32 ssrc, DataMediaChannel::Error error); void OnDataChannelReadyToSend(bool writable); - void OnDataChannelNewStreamReceived(const std::string& label, - const webrtc::DataChannelInit& init); void OnSrtpError(uint32 ssrc, SrtpFilter::Mode mode, SrtpFilter::Error error); + void OnStreamClosedRemotely(uint32 sid); talk_base::scoped_ptr<DataMediaMonitor> media_monitor_; // TODO(pthatcher): Make a separate SctpDataChannel and diff --git a/chromium/third_party/libjingle/source/talk/session/media/channel_unittest.cc b/chromium/third_party/libjingle/source/talk/session/media/channel_unittest.cc index 48a9bdef563..c22361d6570 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channel_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/channel_unittest.cc @@ -71,7 +71,8 @@ static const cricket::DataCodec kGoogleDataCodec(101, "google-data", 0); static const uint32 kSsrc1 = 0x1111; static const uint32 kSsrc2 = 0x2222; static const uint32 kSsrc3 = 0x3333; -static const char kCName[] = "a@b.com"; +static const int kAudioPts[] = {0, 8}; +static const int kVideoPts[] = {97, 99}; template<class ChannelT, class MediaChannelT, @@ -318,14 +319,17 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { } bool SendInitiate() { - bool result = channel1_->SetLocalContent(&local_media_content1_, CA_OFFER); + bool result = channel1_->SetLocalContent(&local_media_content1_, + CA_OFFER, NULL); if (result) { channel1_->Enable(true); - result = channel2_->SetRemoteContent(&remote_media_content1_, CA_OFFER); + result = channel2_->SetRemoteContent(&remote_media_content1_, + CA_OFFER, NULL); if (result) { session1_.Connect(&session2_); - result = channel2_->SetLocalContent(&local_media_content2_, CA_ANSWER); + result = channel2_->SetLocalContent(&local_media_content2_, + CA_ANSWER, NULL); } } return result; @@ -333,34 +337,39 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { bool SendAccept() { channel2_->Enable(true); - return channel1_->SetRemoteContent(&remote_media_content2_, CA_ANSWER); + return channel1_->SetRemoteContent(&remote_media_content2_, + CA_ANSWER, NULL); } bool SendOffer() { - bool result = channel1_->SetLocalContent(&local_media_content1_, CA_OFFER); + bool result = channel1_->SetLocalContent(&local_media_content1_, + CA_OFFER, NULL); if (result) { channel1_->Enable(true); - result = channel2_->SetRemoteContent(&remote_media_content1_, CA_OFFER); + result = channel2_->SetRemoteContent(&remote_media_content1_, + CA_OFFER, NULL); } return result; } bool SendProvisionalAnswer() { bool result = channel2_->SetLocalContent(&local_media_content2_, - CA_PRANSWER); + CA_PRANSWER, NULL); if (result) { channel2_->Enable(true); result = channel1_->SetRemoteContent(&remote_media_content2_, - CA_PRANSWER); + CA_PRANSWER, NULL); session1_.Connect(&session2_); } return result; } bool SendFinalAnswer() { - bool result = channel2_->SetLocalContent(&local_media_content2_, CA_ANSWER); + bool result = channel2_->SetLocalContent(&local_media_content2_, + CA_ANSWER, NULL); if (result) - result = channel1_->SetRemoteContent(&remote_media_content2_, CA_ANSWER); + result = channel1_->SetRemoteContent(&remote_media_content2_, + CA_ANSWER, NULL); return result; } @@ -401,13 +410,13 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { static_cast<int>(rtcp_packet_.size())); } // Methods to send custom data. - bool SendCustomRtp1(uint32 ssrc, int sequence_number) { - std::string data(CreateRtpData(ssrc, sequence_number)); + bool SendCustomRtp1(uint32 ssrc, int sequence_number, int pl_type = -1) { + std::string data(CreateRtpData(ssrc, sequence_number, pl_type)); return media_channel1_->SendRtp(data.c_str(), static_cast<int>(data.size())); } - bool SendCustomRtp2(uint32 ssrc, int sequence_number) { - std::string data(CreateRtpData(ssrc, sequence_number)); + bool SendCustomRtp2(uint32 ssrc, int sequence_number, int pl_type = -1) { + std::string data(CreateRtpData(ssrc, sequence_number, pl_type)); return media_channel2_->SendRtp(data.c_str(), static_cast<int>(data.size())); } @@ -438,13 +447,13 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { static_cast<int>(rtcp_packet_.size())); } // Methods to check custom data. - bool CheckCustomRtp1(uint32 ssrc, int sequence_number) { - std::string data(CreateRtpData(ssrc, sequence_number)); + bool CheckCustomRtp1(uint32 ssrc, int sequence_number, int pl_type = -1 ) { + std::string data(CreateRtpData(ssrc, sequence_number, pl_type)); return media_channel1_->CheckRtp(data.c_str(), static_cast<int>(data.size())); } - bool CheckCustomRtp2(uint32 ssrc, int sequence_number) { - std::string data(CreateRtpData(ssrc, sequence_number)); + bool CheckCustomRtp2(uint32 ssrc, int sequence_number, int pl_type = -1) { + std::string data(CreateRtpData(ssrc, sequence_number, pl_type)); return media_channel2_->CheckRtp(data.c_str(), static_cast<int>(data.size())); } @@ -458,11 +467,14 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { return media_channel2_->CheckRtcp(data.c_str(), static_cast<int>(data.size())); } - std::string CreateRtpData(uint32 ssrc, int sequence_number) { + std::string CreateRtpData(uint32 ssrc, int sequence_number, int pl_type) { std::string data(rtp_packet_); // Set SSRC in the rtp packet copy. talk_base::SetBE32(const_cast<char*>(data.c_str()) + 8, ssrc); talk_base::SetBE16(const_cast<char*>(data.c_str()) + 2, sequence_number); + if (pl_type >= 0) { + talk_base::Set8(const_cast<char*>(data.c_str()), 1, pl_type); + } return data; } std::string CreateRtcpData(uint32 ssrc) { @@ -591,9 +603,9 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateChannels(0, 0); typename T::Content content; CreateContent(0, kPcmuCodec, kH264Codec, &content); - EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER, NULL)); EXPECT_EQ(0U, media_channel1_->codecs().size()); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER, NULL)); ASSERT_EQ(1U, media_channel1_->codecs().size()); EXPECT_TRUE(CodecMatches(content.codecs()[0], media_channel1_->codecs()[0])); @@ -604,10 +616,10 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { void TestSetContentsNullOffer() { CreateChannels(0, 0); typename T::Content content; - EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER, NULL)); CreateContent(0, kPcmuCodec, kH264Codec, &content); EXPECT_EQ(0U, media_channel1_->codecs().size()); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER, NULL)); ASSERT_EQ(1U, media_channel1_->codecs().size()); EXPECT_TRUE(CodecMatches(content.codecs()[0], media_channel1_->codecs()[0])); @@ -623,13 +635,13 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateContent(0, kPcmuCodec, kH264Codec, &content); // Both sides agree on mux. Should no longer be a separate RTCP channel. content.set_rtcp_mux(true); - EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER, NULL)); EXPECT_TRUE(channel1_->rtcp_transport_channel() == NULL); // Only initiator supports mux. Should still have a separate RTCP channel. - EXPECT_TRUE(channel2_->SetLocalContent(&content, CA_OFFER)); + EXPECT_TRUE(channel2_->SetLocalContent(&content, CA_OFFER, NULL)); content.set_rtcp_mux(false); - EXPECT_TRUE(channel2_->SetRemoteContent(&content, CA_ANSWER)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content, CA_ANSWER, NULL)); EXPECT_TRUE(channel2_->rtcp_transport_channel() != NULL); } @@ -642,17 +654,17 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { typename T::Content content; CreateContent(0, kPcmuCodec, kH264Codec, &content); content.set_rtcp_mux(true); - EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_PRANSWER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_PRANSWER, NULL)); EXPECT_TRUE(channel1_->rtcp_transport_channel() != NULL); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER, NULL)); // Both sides agree on mux. Should no longer be a separate RTCP channel. EXPECT_TRUE(channel1_->rtcp_transport_channel() == NULL); // Only initiator supports mux. Should still have a separate RTCP channel. - EXPECT_TRUE(channel2_->SetLocalContent(&content, CA_OFFER)); + EXPECT_TRUE(channel2_->SetLocalContent(&content, CA_OFFER, NULL)); content.set_rtcp_mux(false); - EXPECT_TRUE(channel2_->SetRemoteContent(&content, CA_PRANSWER)); - EXPECT_TRUE(channel2_->SetRemoteContent(&content, CA_ANSWER)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content, CA_PRANSWER, NULL)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content, CA_ANSWER, NULL)); EXPECT_TRUE(channel2_->rtcp_transport_channel() != NULL); } @@ -663,7 +675,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { typename T::Content content; CreateContent(0, kPcmuCodec, kH264Codec, &content); content.set_buffered_mode_latency(101); - EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER, NULL)); EXPECT_EQ(0U, media_channel1_->codecs().size()); cricket::VideoOptions options; ASSERT_TRUE(media_channel1_->GetOptions(&options)); @@ -671,7 +683,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(options.buffered_mode_latency.Get(&latency)); EXPECT_EQ(101, latency); content.set_buffered_mode_latency(102); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER, NULL)); ASSERT_EQ(1U, media_channel1_->codecs().size()); EXPECT_TRUE(CodecMatches(content.codecs()[0], media_channel1_->codecs()[0])); @@ -688,8 +700,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { kPcmuCodec, kH264Codec, &content); EXPECT_EQ(0U, media_channel1_->codecs().size()); - EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER, NULL)); ASSERT_EQ(1U, media_channel1_->codecs().size()); EXPECT_TRUE(CodecMatches(content.codecs()[0], media_channel1_->codecs()[0])); @@ -698,14 +710,14 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { update_content.set_partial(true); CreateContent(0, kIsacCodec, kH264SvcCodec, &update_content); - EXPECT_TRUE(channel1_->SetRemoteContent(&update_content, CA_UPDATE)); + EXPECT_TRUE(channel1_->SetRemoteContent(&update_content, CA_UPDATE, NULL)); ASSERT_EQ(1U, media_channel1_->codecs().size()); EXPECT_TRUE(CodecMatches(update_content.codecs()[0], media_channel1_->codecs()[0])); // Now update without any codecs. This is ignored. typename T::Content empty_content; empty_content.set_partial(true); - EXPECT_TRUE(channel1_->SetRemoteContent(&empty_content, CA_UPDATE)); + EXPECT_TRUE(channel1_->SetRemoteContent(&empty_content, CA_UPDATE, NULL)); ASSERT_EQ(1U, media_channel1_->codecs().size()); EXPECT_TRUE(CodecMatches(update_content.codecs()[0], media_channel1_->codecs()[0])); @@ -751,7 +763,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateContent(0, kPcmuCodec, kH264Codec, &content1); content1.AddStream(stream1); EXPECT_EQ(0u, media_channel1_->send_streams().size()); - EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER, NULL)); ASSERT_EQ(1u, media_channel1_->send_streams().size()); EXPECT_EQ(stream1, media_channel1_->send_streams()[0]); @@ -762,7 +774,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { content2.AddStream(stream2); content2.AddStream(stream3); content2.set_partial(true); - EXPECT_TRUE(channel1_->SetLocalContent(&content2, CA_UPDATE)); + EXPECT_TRUE(channel1_->SetLocalContent(&content2, CA_UPDATE, NULL)); ASSERT_EQ(3u, media_channel1_->send_streams().size()); EXPECT_EQ(stream1, media_channel1_->send_streams()[0]); EXPECT_EQ(stream2, media_channel1_->send_streams()[1]); @@ -774,7 +786,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { stream1.ssrcs.clear(); content3.AddStream(stream1); content3.set_partial(true); - EXPECT_TRUE(channel1_->SetLocalContent(&content3, CA_UPDATE)); + EXPECT_TRUE(channel1_->SetLocalContent(&content3, CA_UPDATE, NULL)); ASSERT_EQ(2u, media_channel1_->send_streams().size()); EXPECT_EQ(stream2, media_channel1_->send_streams()[0]); EXPECT_EQ(stream3, media_channel1_->send_streams()[1]); @@ -784,7 +796,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { typename T::Content content4; content4.AddStream(stream2); content4.set_partial(true); - EXPECT_TRUE(channel1_->SetLocalContent(&content4, CA_UPDATE)); + EXPECT_TRUE(channel1_->SetLocalContent(&content4, CA_UPDATE, NULL)); ASSERT_EQ(2u, media_channel1_->send_streams().size()); EXPECT_EQ(stream2, media_channel1_->send_streams()[0]); EXPECT_EQ(stream3, media_channel1_->send_streams()[1]); @@ -818,7 +830,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateContent(0, kPcmuCodec, kH264Codec, &content1); content1.AddStream(stream1); EXPECT_EQ(0u, media_channel1_->recv_streams().size()); - EXPECT_TRUE(channel1_->SetRemoteContent(&content1, CA_OFFER)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content1, CA_OFFER, NULL)); ASSERT_EQ(1u, media_channel1_->codecs().size()); ASSERT_EQ(1u, media_channel1_->recv_streams().size()); @@ -830,7 +842,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { content2.AddStream(stream2); content2.AddStream(stream3); content2.set_partial(true); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_UPDATE)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_UPDATE, NULL)); ASSERT_EQ(3u, media_channel1_->recv_streams().size()); EXPECT_EQ(stream1, media_channel1_->recv_streams()[0]); EXPECT_EQ(stream2, media_channel1_->recv_streams()[1]); @@ -842,7 +854,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { stream1.ssrcs.clear(); content3.AddStream(stream1); content3.set_partial(true); - EXPECT_TRUE(channel1_->SetRemoteContent(&content3, CA_UPDATE)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content3, CA_UPDATE, NULL)); ASSERT_EQ(2u, media_channel1_->recv_streams().size()); EXPECT_EQ(stream2, media_channel1_->recv_streams()[0]); EXPECT_EQ(stream3, media_channel1_->recv_streams()[1]); @@ -852,7 +864,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { typename T::Content content4; content4.AddStream(stream2); content4.set_partial(true); - EXPECT_TRUE(channel1_->SetRemoteContent(&content4, CA_UPDATE)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content4, CA_UPDATE, NULL)); ASSERT_EQ(2u, media_channel1_->recv_streams().size()); EXPECT_EQ(stream2, media_channel1_->recv_streams()[0]); EXPECT_EQ(stream3, media_channel1_->recv_streams()[1]); @@ -879,20 +891,20 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { typename T::Content content1; CreateContent(0, kPcmuCodec, kH264Codec, &content1); content1.AddStream(stream1); - EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER, NULL)); EXPECT_TRUE(channel1_->Enable(true)); EXPECT_EQ(1u, media_channel1_->send_streams().size()); - EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL)); EXPECT_EQ(1u, media_channel2_->recv_streams().size()); session1_.Connect(&session2_); // Channel 2 do not send anything. typename T::Content content2; CreateContent(0, kPcmuCodec, kH264Codec, &content2); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_ANSWER, NULL)); EXPECT_EQ(0u, media_channel1_->recv_streams().size()); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_ANSWER)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_ANSWER, NULL)); EXPECT_TRUE(channel2_->Enable(true)); EXPECT_EQ(0u, media_channel2_->send_streams().size()); @@ -903,21 +915,21 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { typename T::Content content3; CreateContent(SECURE, kPcmuCodec, kH264Codec, &content3); content3.AddStream(stream2); - EXPECT_TRUE(channel2_->SetLocalContent(&content3, CA_OFFER)); + EXPECT_TRUE(channel2_->SetLocalContent(&content3, CA_OFFER, NULL)); ASSERT_EQ(1u, media_channel2_->send_streams().size()); EXPECT_EQ(stream2, media_channel2_->send_streams()[0]); - EXPECT_TRUE(channel1_->SetRemoteContent(&content3, CA_OFFER)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content3, CA_OFFER, NULL)); ASSERT_EQ(1u, media_channel1_->recv_streams().size()); EXPECT_EQ(stream2, media_channel1_->recv_streams()[0]); // Channel 1 replies but stop sending stream1. typename T::Content content4; CreateContent(SECURE, kPcmuCodec, kH264Codec, &content4); - EXPECT_TRUE(channel1_->SetLocalContent(&content4, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content4, CA_ANSWER, NULL)); EXPECT_EQ(0u, media_channel1_->send_streams().size()); - EXPECT_TRUE(channel2_->SetRemoteContent(&content4, CA_ANSWER)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content4, CA_ANSWER, NULL)); EXPECT_EQ(0u, media_channel2_->recv_streams().size()); EXPECT_TRUE(channel1_->secure()); @@ -936,13 +948,16 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(channel1_->Enable(true)); EXPECT_FALSE(media_channel1_->playout()); EXPECT_FALSE(media_channel1_->sending()); - EXPECT_TRUE(channel1_->SetLocalContent(&local_media_content1_, CA_OFFER)); + EXPECT_TRUE(channel1_->SetLocalContent(&local_media_content1_, + CA_OFFER, NULL)); EXPECT_TRUE(media_channel1_->playout()); EXPECT_FALSE(media_channel1_->sending()); - EXPECT_TRUE(channel2_->SetRemoteContent(&local_media_content1_, CA_OFFER)); + EXPECT_TRUE(channel2_->SetRemoteContent(&local_media_content1_, + CA_OFFER, NULL)); EXPECT_FALSE(media_channel2_->playout()); EXPECT_FALSE(media_channel2_->sending()); - EXPECT_TRUE(channel2_->SetLocalContent(&local_media_content2_, CA_ANSWER)); + EXPECT_TRUE(channel2_->SetLocalContent(&local_media_content2_, + CA_ANSWER, NULL)); EXPECT_FALSE(media_channel2_->playout()); EXPECT_FALSE(media_channel2_->sending()); session1_.Connect(&session2_); @@ -953,7 +968,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(channel2_->Enable(true)); EXPECT_TRUE(media_channel2_->playout()); EXPECT_TRUE(media_channel2_->sending()); - EXPECT_TRUE(channel1_->SetRemoteContent(&local_media_content2_, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetRemoteContent(&local_media_content2_, + CA_ANSWER, NULL)); EXPECT_TRUE(media_channel1_->playout()); EXPECT_TRUE(media_channel1_->sending()); } @@ -998,10 +1014,10 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_FALSE(media_channel2_->playout()); EXPECT_FALSE(media_channel2_->sending()); - EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER)); - EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER)); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_PRANSWER)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_PRANSWER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER, NULL)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_PRANSWER, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_PRANSWER, NULL)); session1_.Connect(&session2_); EXPECT_TRUE(media_channel1_->playout()); @@ -1011,8 +1027,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { // Update |content2| to be RecvOnly. content2.set_direction(cricket::MD_RECVONLY); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_PRANSWER)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_PRANSWER)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_PRANSWER, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_PRANSWER, NULL)); EXPECT_TRUE(media_channel1_->playout()); EXPECT_TRUE(media_channel1_->sending()); @@ -1021,8 +1037,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { // Update |content2| to be SendRecv. content2.set_direction(cricket::MD_SENDRECV); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_ANSWER)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_ANSWER)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_ANSWER, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_ANSWER, NULL)); EXPECT_TRUE(media_channel1_->playout()); EXPECT_TRUE(media_channel1_->sending()); @@ -1427,60 +1443,63 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(CheckNoRtp2()); } - void SendSsrcMuxToSsrcMuxWithRtcpMux() { + void SendBundleToBundle( + const int* pl_types, int len, bool rtcp_mux, bool secure) { + ASSERT_EQ(2, len); int sequence_number1_1 = 0, sequence_number2_2 = 0; - CreateChannels(SSRC_MUX | RTCP | RTCP_MUX, SSRC_MUX | RTCP | RTCP_MUX); + // Only pl_type1 was added to the bundle filter for both |channel1_| + // and |channel2_|. + int pl_type1 = pl_types[0]; + int pl_type2 = pl_types[1]; + int flags = SSRC_MUX | RTCP; + if (secure) flags |= SECURE; + uint32 expected_channels = 2U; + if (rtcp_mux) { + flags |= RTCP_MUX; + expected_channels = 1U; + } + CreateChannels(flags, flags); EXPECT_TRUE(SendInitiate()); EXPECT_EQ(2U, GetTransport1()->channels().size()); - EXPECT_EQ(1U, GetTransport2()->channels().size()); + EXPECT_EQ(expected_channels, GetTransport2()->channels().size()); EXPECT_TRUE(SendAccept()); - EXPECT_EQ(1U, GetTransport1()->channels().size()); - EXPECT_EQ(1U, GetTransport2()->channels().size()); - EXPECT_TRUE(channel1_->ssrc_filter()->IsActive()); - // channel1 - should have media_content2 as remote. i.e. kSsrc2 - EXPECT_TRUE(channel1_->ssrc_filter()->FindStream(kSsrc2)); - EXPECT_TRUE(channel2_->ssrc_filter()->IsActive()); - // channel2 - should have media_content1 as remote. i.e. kSsrc1 - EXPECT_TRUE(channel2_->ssrc_filter()->FindStream(kSsrc1)); - EXPECT_TRUE(SendCustomRtp1(kSsrc1, ++sequence_number1_1)); - EXPECT_TRUE(SendCustomRtp2(kSsrc2, ++sequence_number2_2)); - EXPECT_TRUE(SendCustomRtcp1(kSsrc1)); - EXPECT_TRUE(SendCustomRtcp2(kSsrc2)); - EXPECT_TRUE(CheckCustomRtp1(kSsrc2, sequence_number2_2)); + EXPECT_EQ(expected_channels, GetTransport1()->channels().size()); + EXPECT_EQ(expected_channels, GetTransport2()->channels().size()); + EXPECT_TRUE(channel1_->bundle_filter()->FindPayloadType(pl_type1)); + EXPECT_TRUE(channel2_->bundle_filter()->FindPayloadType(pl_type1)); + EXPECT_FALSE(channel1_->bundle_filter()->FindPayloadType(pl_type2)); + EXPECT_FALSE(channel2_->bundle_filter()->FindPayloadType(pl_type2)); + // channel1 - should only have media_content2 as remote. i.e. kSsrc2 + EXPECT_TRUE(channel1_->bundle_filter()->FindStream(kSsrc2)); + EXPECT_FALSE(channel1_->bundle_filter()->FindStream(kSsrc1)); + // channel2 - should only have media_content1 as remote. i.e. kSsrc1 + EXPECT_TRUE(channel2_->bundle_filter()->FindStream(kSsrc1)); + EXPECT_FALSE(channel2_->bundle_filter()->FindStream(kSsrc2)); + + // Both channels can receive pl_type1 only. + EXPECT_TRUE(SendCustomRtp1(kSsrc1, ++sequence_number1_1, pl_type1)); + EXPECT_TRUE(CheckCustomRtp2(kSsrc1, sequence_number1_1, pl_type1)); + EXPECT_TRUE(SendCustomRtp2(kSsrc2, ++sequence_number2_2, pl_type1)); + EXPECT_TRUE(CheckCustomRtp1(kSsrc2, sequence_number2_2, pl_type1)); EXPECT_TRUE(CheckNoRtp1()); - EXPECT_TRUE(CheckCustomRtp2(kSsrc1, sequence_number1_1)); EXPECT_TRUE(CheckNoRtp2()); + + // RTCP test + EXPECT_TRUE(SendCustomRtp1(kSsrc1, ++sequence_number1_1, pl_type2)); + EXPECT_FALSE(CheckCustomRtp2(kSsrc1, sequence_number1_1, pl_type2)); + EXPECT_TRUE(SendCustomRtp2(kSsrc2, ++sequence_number2_2, pl_type2)); + EXPECT_FALSE(CheckCustomRtp1(kSsrc2, sequence_number2_2, pl_type2)); + + EXPECT_TRUE(SendCustomRtcp1(kSsrc1)); + EXPECT_TRUE(SendCustomRtcp2(kSsrc2)); EXPECT_TRUE(CheckCustomRtcp1(kSsrc2)); EXPECT_TRUE(CheckNoRtcp1()); EXPECT_TRUE(CheckCustomRtcp2(kSsrc1)); EXPECT_TRUE(CheckNoRtcp2()); - } - void SendSsrcMuxToSsrcMux() { - int sequence_number1_1 = 0, sequence_number2_2 = 0; - CreateChannels(SSRC_MUX | RTCP, SSRC_MUX | RTCP); - EXPECT_TRUE(SendInitiate()); - EXPECT_EQ(2U, GetTransport1()->channels().size()); - EXPECT_EQ(2U, GetTransport2()->channels().size()); - EXPECT_TRUE(SendAccept()); - EXPECT_EQ(2U, GetTransport1()->channels().size()); - EXPECT_EQ(2U, GetTransport2()->channels().size()); - EXPECT_TRUE(channel1_->ssrc_filter()->IsActive()); - // channel1 - should have media_content2 as remote. i.e. kSsrc2 - EXPECT_TRUE(channel1_->ssrc_filter()->FindStream(kSsrc2)); - EXPECT_TRUE(channel2_->ssrc_filter()->IsActive()); - // channel2 - should have media_content1 as remote. i.e. kSsrc1 - EXPECT_TRUE(SendCustomRtp1(kSsrc1, ++sequence_number1_1)); - EXPECT_TRUE(SendCustomRtp2(kSsrc2, ++sequence_number2_2)); - EXPECT_TRUE(SendCustomRtcp1(kSsrc1)); - EXPECT_TRUE(SendCustomRtcp2(kSsrc2)); - EXPECT_TRUE(CheckCustomRtp1(kSsrc2, sequence_number2_2)); - EXPECT_FALSE(CheckCustomRtp1(kSsrc1, sequence_number2_2)); - EXPECT_TRUE(CheckCustomRtp2(kSsrc1, sequence_number1_1)); - EXPECT_FALSE(CheckCustomRtp2(kSsrc2, sequence_number1_1)); - EXPECT_TRUE(CheckCustomRtcp1(kSsrc2)); + EXPECT_TRUE(SendCustomRtcp1(kSsrc2)); + EXPECT_TRUE(SendCustomRtcp2(kSsrc1)); EXPECT_FALSE(CheckCustomRtcp1(kSsrc1)); - EXPECT_TRUE(CheckCustomRtcp2(kSsrc1)); EXPECT_FALSE(CheckCustomRtcp2(kSsrc2)); } @@ -1587,21 +1606,21 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { // Test failures in SetLocalContent. media_channel1_->set_fail_set_recv_codecs(true); - session1_.SetError(cricket::BaseSession::ERROR_NONE); + session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); session1_.SetState(cricket::Session::STATE_SENTINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_CONTENT, session1_.error()); media_channel1_->set_fail_set_recv_codecs(true); - session1_.SetError(cricket::BaseSession::ERROR_NONE); + session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); session1_.SetState(cricket::Session::STATE_SENTACCEPT); EXPECT_EQ(cricket::BaseSession::ERROR_CONTENT, session1_.error()); // Test failures in SetRemoteContent. media_channel1_->set_fail_set_send_codecs(true); - session1_.SetError(cricket::BaseSession::ERROR_NONE); + session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); session1_.SetState(cricket::Session::STATE_RECEIVEDINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_CONTENT, session1_.error()); media_channel1_->set_fail_set_send_codecs(true); - session1_.SetError(cricket::BaseSession::ERROR_NONE); + session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); session1_.SetState(cricket::Session::STATE_RECEIVEDACCEPT); EXPECT_EQ(cricket::BaseSession::ERROR_CONTENT, session1_.error()); } @@ -1613,7 +1632,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { cricket::SessionDescription* sdesc = CreateSessionDescriptionWithStream(1); EXPECT_TRUE(session1_.set_local_description(sdesc)); - session1_.SetError(cricket::BaseSession::ERROR_NONE); + session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); session1_.SetState(cricket::Session::STATE_SENTINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasSendStream(1)); @@ -1635,7 +1654,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { cricket::SessionDescription* sdesc = CreateSessionDescriptionWithStream(1); EXPECT_TRUE(session1_.set_remote_description(sdesc)); - session1_.SetError(cricket::BaseSession::ERROR_NONE); + session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); session1_.SetState(cricket::Session::STATE_RECEIVEDINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasRecvStream(1)); @@ -1655,7 +1674,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { cricket::SessionDescription* sdesc = CreateSessionDescriptionWithStream(1); EXPECT_TRUE(session1_.set_remote_description(sdesc)); - session1_.SetError(cricket::BaseSession::ERROR_NONE); + session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); session1_.SetState(cricket::Session::STATE_RECEIVEDINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasRecvStream(1)); @@ -1687,7 +1706,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { cricket::SessionDescription* sdesc = CreateSessionDescriptionWithStream(1); EXPECT_TRUE(session1_.set_local_description(sdesc)); - session1_.SetError(cricket::BaseSession::ERROR_NONE); + session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); session1_.SetState(cricket::Session::STATE_SENTINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasSendStream(1)); @@ -1742,10 +1761,24 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { error_); } - void TestSrtpError() { - static const unsigned char kBadPacket[] = { - 0x84, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - }; + void TestSrtpError(int pl_type) { + // For Audio, only pl_type 0 is added to the bundle filter. + // For Video, only pl_type 97 is added to the bundle filter. + // So we need to pass in pl_type so that the packet can pass through + // the bundle filter before it can be processed by the srtp filter. + // The packet is not a valid srtp packet because it is too short. + unsigned const char kBadPacket[] = {0x84, + static_cast<unsigned char>(pl_type), + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01}; CreateChannels(RTCP | SECURE, RTCP | SECURE); EXPECT_FALSE(channel1_->secure()); EXPECT_FALSE(channel2_->secure()); @@ -1810,8 +1843,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { CreateContent(0, kPcmuCodec, kH264Codec, &content); // Both sides agree on mux. Should no longer be a separate RTCP channel. content.set_rtcp_mux(true); - EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, CA_OFFER, NULL)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, CA_ANSWER, NULL)); EXPECT_TRUE(channel1_->rtcp_transport_channel() == NULL); TransportChannel* rtp = channel1_->transport_channel(); EXPECT_FALSE(media_channel1_->ready_to_send()); @@ -2266,7 +2299,7 @@ TEST_F(VoiceChannelTest, TestChangeStateError) { } TEST_F(VoiceChannelTest, TestSrtpError) { - Base::TestSrtpError(); + Base::TestSrtpError(kAudioPts[0]); } TEST_F(VoiceChannelTest, TestOnReadyToSend) { @@ -2378,12 +2411,22 @@ TEST_F(VoiceChannelTest, TestScaleVolumeMultiwayCall) { EXPECT_DOUBLE_EQ(0.0, right); } -TEST_F(VoiceChannelTest, SendSsrcMuxToSsrcMux) { - Base::SendSsrcMuxToSsrcMux(); +TEST_F(VoiceChannelTest, SendBundleToBundle) { + Base::SendBundleToBundle(kAudioPts, ARRAY_SIZE(kAudioPts), false, false); } -TEST_F(VoiceChannelTest, SendSsrcMuxToSsrcMuxWithRtcpMux) { - Base::SendSsrcMuxToSsrcMuxWithRtcpMux(); +TEST_F(VoiceChannelTest, SendBundleToBundleSecure) { + Base::SendBundleToBundle(kAudioPts, ARRAY_SIZE(kAudioPts), false, true); +} + +TEST_F(VoiceChannelTest, SendBundleToBundleWithRtcpMux) { + Base::SendBundleToBundle( + kAudioPts, ARRAY_SIZE(kAudioPts), true, false); +} + +TEST_F(VoiceChannelTest, SendBundleToBundleWithRtcpMuxSecure) { + Base::SendBundleToBundle( + kAudioPts, ARRAY_SIZE(kAudioPts), true, true); } TEST_F(VoiceChannelTest, TestSetChannelOptions) { @@ -2593,18 +2636,28 @@ TEST_F(VideoChannelTest, TestFlushRtcp) { Base::TestFlushRtcp(); } -TEST_F(VideoChannelTest, SendSsrcMuxToSsrcMux) { - Base::SendSsrcMuxToSsrcMux(); +TEST_F(VideoChannelTest, SendBundleToBundle) { + Base::SendBundleToBundle(kVideoPts, ARRAY_SIZE(kVideoPts), false, false); +} + +TEST_F(VideoChannelTest, SendBundleToBundleSecure) { + Base::SendBundleToBundle(kVideoPts, ARRAY_SIZE(kVideoPts), false, true); +} + +TEST_F(VideoChannelTest, SendBundleToBundleWithRtcpMux) { + Base::SendBundleToBundle( + kVideoPts, ARRAY_SIZE(kVideoPts), true, false); } -TEST_F(VideoChannelTest, SendSsrcMuxToSsrcMuxWithRtcpMux) { - Base::SendSsrcMuxToSsrcMuxWithRtcpMux(); +TEST_F(VideoChannelTest, SendBundleToBundleWithRtcpMuxSecure) { + Base::SendBundleToBundle( + kVideoPts, ARRAY_SIZE(kVideoPts), true, true); } // TODO(gangji): Add VideoChannelTest.TestChangeStateError. TEST_F(VideoChannelTest, TestSrtpError) { - Base::TestSrtpError(); + Base::TestSrtpError(kVideoPts[0]); } TEST_F(VideoChannelTest, TestOnReadyToSend) { diff --git a/chromium/third_party/libjingle/source/talk/session/media/channelmanager.cc b/chromium/third_party/libjingle/source/talk/session/media/channelmanager.cc index d4fcc79dfa7..88316b56321 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channelmanager.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/channelmanager.cc @@ -217,7 +217,11 @@ bool ChannelManager::Init() { } ASSERT(worker_thread_ != NULL); - if (worker_thread_ && worker_thread_->started()) { + ASSERT(worker_thread_->RunningForChannelManager()); + // TODO(fischman): remove the if below (and + // Thread::RunningForChannelManager()) once the ASSERT above has stuck for a + // month (2014/06/22). + if (worker_thread_ && worker_thread_->RunningForChannelManager()) { if (media_engine_->Init(worker_thread_)) { initialized_ = true; @@ -574,6 +578,29 @@ bool ChannelManager::SetAudioOptions_w( return ret; } +// Sets Engine-specific audio options according to enabled experiments. +bool ChannelManager::SetEngineAudioOptions(const AudioOptions& options) { + // If we're initialized, pass the settings to the media engine. + bool ret = false; + if (initialized_) { + ret = worker_thread_->Invoke<bool>( + Bind(&ChannelManager::SetEngineAudioOptions_w, this, options)); + } + + // If all worked well, save the audio options. + if (ret) { + audio_options_ = options; + } + return ret; +} + +bool ChannelManager::SetEngineAudioOptions_w(const AudioOptions& options) { + ASSERT(worker_thread_ == talk_base::Thread::Current()); + ASSERT(initialized_); + + return media_engine_->SetAudioOptions(options); +} + bool ChannelManager::GetOutputVolume(int* level) { if (!initialized_) { return false; @@ -939,12 +966,9 @@ VideoFormat ChannelManager::GetStartCaptureFormat() { Bind(&MediaEngineInterface::GetStartCaptureFormat, media_engine_.get())); } -bool ChannelManager::SetAudioOptions(const AudioOptions& options) { - if (!media_engine_->SetAudioOptions(options)) { - return false; - } - audio_options_ = options; - return true; +bool ChannelManager::StartAecDump(talk_base::PlatformFile file) { + return worker_thread_->Invoke<bool>( + Bind(&MediaEngineInterface::StartAecDump, media_engine_.get(), file)); } } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/session/media/channelmanager.h b/chromium/third_party/libjingle/source/talk/session/media/channelmanager.h index fdb8f733624..e8d6c0e5e4f 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channelmanager.h +++ b/chromium/third_party/libjingle/source/talk/session/media/channelmanager.h @@ -32,6 +32,7 @@ #include <vector> #include "talk/base/criticalsection.h" +#include "talk/base/fileutils.h" #include "talk/base/sigslotrepeater.h" #include "talk/base/thread.h" #include "talk/media/base/capturemanager.h" @@ -142,6 +143,8 @@ class ChannelManager : public talk_base::MessageHandler, bool SetAudioOptions(const std::string& wave_in_device, const std::string& wave_out_device, const AudioOptions& options); + // Sets Engine-specific audio options according to enabled experiments. + bool SetEngineAudioOptions(const AudioOptions& options); bool GetOutputVolume(int* level); bool SetOutputVolume(int level); bool IsSameCapturer(const std::string& capturer_name, @@ -214,6 +217,9 @@ class ChannelManager : public talk_base::MessageHandler, void SetVideoCaptureDeviceMaxFormat(const std::string& usb_id, const VideoFormat& max_format); + // Starts AEC dump using existing file. + bool StartAecDump(talk_base::PlatformFile file); + sigslot::repeater0<> SignalDevicesChange; sigslot::signal2<VideoCapturer*, CaptureState> SignalVideoCaptureStateChange; @@ -226,10 +232,6 @@ class ChannelManager : public talk_base::MessageHandler, // removed. VideoFormat GetStartCaptureFormat(); - // TODO(turajs): Remove this function when ACM2 is in use. Used mainly to - // choose between ACM1 and ACM2. - bool SetAudioOptions(const AudioOptions& options); - protected: // Adds non-transient parameters which can only be changed through the // options store. @@ -266,6 +268,7 @@ class ChannelManager : public talk_base::MessageHandler, void DestroySoundclip_w(Soundclip* soundclip); bool SetAudioOptions_w(const AudioOptions& options, int delay_offset, const Device* in_dev, const Device* out_dev); + bool SetEngineAudioOptions_w(const AudioOptions& options); bool SetCaptureDevice_w(const Device* cam_device); void OnVideoCaptureStateChange(VideoCapturer* capturer, CaptureState result); diff --git a/chromium/third_party/libjingle/source/talk/session/media/channelmanager_unittest.cc b/chromium/third_party/libjingle/source/talk/session/media/channelmanager_unittest.cc index d0d380d3eb8..cbf19f88858 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channelmanager_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/channelmanager_unittest.cc @@ -122,7 +122,9 @@ TEST_F(ChannelManagerTest, StartupShutdownOnThread) { } // Test that we fail to startup if we're given an unstarted thread. -TEST_F(ChannelManagerTest, StartupShutdownOnUnstartedThread) { +// TODO(fischman): delete once Thread::RunningForChannelManager() is gone +// (webrtc:3388). +TEST_F(ChannelManagerTest, DISABLED_StartupShutdownOnUnstartedThread) { EXPECT_TRUE(cm_->set_worker_thread(&worker_)); EXPECT_FALSE(cm_->Init()); EXPECT_FALSE(cm_->initialized()); @@ -319,6 +321,25 @@ TEST_F(ChannelManagerTest, SetAudioOptions) { EXPECT_FALSE(cm_->SetAudioOptions("audio-in9", "audio-out2", options)); } +TEST_F(ChannelManagerTest, SetEngineAudioOptions) { + EXPECT_TRUE(cm_->Init()); + // Test setting specific values. + AudioOptions options; + options.experimental_ns.Set(true); + EXPECT_TRUE(cm_->SetEngineAudioOptions(options)); + bool experimental_ns = false; + EXPECT_TRUE(fme_->audio_options().experimental_ns.Get(&experimental_ns)); + EXPECT_TRUE(experimental_ns); +} + +TEST_F(ChannelManagerTest, SetEngineAudioOptionsBeforeInitFails) { + // Test that values that we set before Init are not applied. + AudioOptions options; + options.experimental_ns.Set(true); + EXPECT_FALSE(cm_->SetEngineAudioOptions(options)); + EXPECT_FALSE(fme_->audio_options().experimental_ns.IsSet()); +} + TEST_F(ChannelManagerTest, SetCaptureDeviceBeforeInit) { // Test that values that we set before Init are applied. EXPECT_TRUE(cm_->SetCaptureDevice("video-in2")); diff --git a/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor.cc b/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor.cc index 1f3e0938fb9..8965cde9582 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor.cc @@ -28,7 +28,9 @@ #include "talk/session/media/currentspeakermonitor.h" #include "talk/base/logging.h" -#include "talk/session/media/call.h" +#include "talk/media/base/streamparams.h" +#include "talk/session/media/audiomonitor.h" +#include "talk/session/media/mediamessages.h" namespace cricket { @@ -39,9 +41,10 @@ const int kMaxAudioLevel = 9; const int kDefaultMinTimeBetweenSwitches = 1000; } -CurrentSpeakerMonitor::CurrentSpeakerMonitor(Call* call, BaseSession* session) +CurrentSpeakerMonitor::CurrentSpeakerMonitor( + AudioSourceContext* audio_source_context, BaseSession* session) : started_(false), - call_(call), + audio_source_context_(audio_source_context), session_(session), current_speaker_ssrc_(0), earliest_permitted_switch_time_(0), @@ -54,10 +57,12 @@ CurrentSpeakerMonitor::~CurrentSpeakerMonitor() { void CurrentSpeakerMonitor::Start() { if (!started_) { - call_->SignalAudioMonitor.connect( + audio_source_context_->SignalAudioMonitor.connect( this, &CurrentSpeakerMonitor::OnAudioMonitor); - call_->SignalMediaStreamsUpdate.connect( + audio_source_context_->SignalMediaStreamsUpdate.connect( this, &CurrentSpeakerMonitor::OnMediaStreamsUpdate); + audio_source_context_->SignalMediaStreamsReset.connect( + this, &CurrentSpeakerMonitor::OnMediaStreamsReset); started_ = true; } @@ -65,8 +70,8 @@ void CurrentSpeakerMonitor::Start() { void CurrentSpeakerMonitor::Stop() { if (started_) { - call_->SignalAudioMonitor.disconnect(this); - call_->SignalMediaStreamsUpdate.disconnect(this); + audio_source_context_->SignalAudioMonitor.disconnect(this); + audio_source_context_->SignalMediaStreamsUpdate.disconnect(this); started_ = false; ssrc_to_speaking_state_map_.clear(); @@ -80,7 +85,8 @@ void CurrentSpeakerMonitor::set_min_time_between_switches( min_time_between_switches_ = min_time_between_switches; } -void CurrentSpeakerMonitor::OnAudioMonitor(Call* call, const AudioInfo& info) { +void CurrentSpeakerMonitor::OnAudioMonitor( + AudioSourceContext* audio_source_context, const AudioInfo& info) { std::map<uint32, int> active_ssrc_to_level_map; cricket::AudioInfo::StreamList::const_iterator stream_list_it; for (stream_list_it = info.active_streams.begin(); @@ -187,22 +193,29 @@ void CurrentSpeakerMonitor::OnAudioMonitor(Call* call, const AudioInfo& info) { } } -void CurrentSpeakerMonitor::OnMediaStreamsUpdate(Call* call, - Session* session, - const MediaStreams& added, - const MediaStreams& removed) { - if (call == call_ && session == session_) { +void CurrentSpeakerMonitor::OnMediaStreamsUpdate( + AudioSourceContext* audio_source_context, BaseSession* session, + const MediaStreams& added, const MediaStreams& removed) { + + if (audio_source_context == audio_source_context_ && session == session_) { // Update the speaking state map based on added and removed streams. for (std::vector<cricket::StreamParams>::const_iterator - it = removed.video().begin(); it != removed.video().end(); ++it) { + it = removed.audio().begin(); it != removed.audio().end(); ++it) { ssrc_to_speaking_state_map_.erase(it->first_ssrc()); } for (std::vector<cricket::StreamParams>::const_iterator - it = added.video().begin(); it != added.video().end(); ++it) { + it = added.audio().begin(); it != added.audio().end(); ++it) { ssrc_to_speaking_state_map_[it->first_ssrc()] = SS_NOT_SPEAKING; } } } +void CurrentSpeakerMonitor::OnMediaStreamsReset( + AudioSourceContext* audio_source_context, BaseSession* session) { + if (audio_source_context == audio_source_context_ && session == session_) { + ssrc_to_speaking_state_map_.clear(); + } +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor.h b/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor.h index 1781db58c49..8e05c8e6772 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor.h +++ b/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor.h @@ -39,16 +39,34 @@ namespace cricket { class BaseSession; -class Call; class Session; struct AudioInfo; struct MediaStreams; -// Note that the call's audio monitor must be started before this is started. +class AudioSourceContext { + public: + sigslot::signal2<AudioSourceContext*, const cricket::AudioInfo&> + SignalAudioMonitor; + sigslot::signal2<AudioSourceContext*, cricket::BaseSession*> + SignalMediaStreamsReset; + sigslot::signal4<AudioSourceContext*, cricket::BaseSession*, + const cricket::MediaStreams&, const cricket::MediaStreams&> + SignalMediaStreamsUpdate; +}; + +// CurrentSpeakerMonitor can be used to monitor the audio-levels from +// many audio-sources and report on changes in the loudest audio-source. +// Its a generic type and relies on an AudioSourceContext which is aware of +// the audio-sources. AudioSourceContext needs to provide two signals namely +// SignalAudioInfoMonitor - provides audio info of the all current speakers. +// SignalMediaSourcesUpdated - provides updates when a speaker leaves or joins. +// Note that the AudioSourceContext's audio monitor must be started +// before this is started. // It's recommended that the audio monitor be started with a 100 ms period. class CurrentSpeakerMonitor : public sigslot::has_slots<> { public: - CurrentSpeakerMonitor(Call* call, BaseSession* session); + CurrentSpeakerMonitor(AudioSourceContext* audio_source_context, + BaseSession* session); ~CurrentSpeakerMonitor(); BaseSession* session() const { return session_; } @@ -62,16 +80,19 @@ class CurrentSpeakerMonitor : public sigslot::has_slots<> { void set_min_time_between_switches(uint32 min_time_between_switches); // This is fired when the current speaker changes, and provides his audio - // SSRC. This only fires after the audio monitor on the underlying Call has - // been started. + // SSRC. This only fires after the audio monitor on the underlying + // AudioSourceContext has been started. sigslot::signal2<CurrentSpeakerMonitor*, uint32> SignalUpdate; private: - void OnAudioMonitor(Call* call, const AudioInfo& info); - void OnMediaStreamsUpdate(Call* call, - Session* session, + void OnAudioMonitor(AudioSourceContext* audio_source_context, + const AudioInfo& info); + void OnMediaStreamsUpdate(AudioSourceContext* audio_source_context, + BaseSession* session, const MediaStreams& added, const MediaStreams& removed); + void OnMediaStreamsReset(AudioSourceContext* audio_source_context, + BaseSession* session); // These are states that a participant will pass through so that we gradually // recognize that they have started and stopped speaking. This avoids @@ -85,7 +106,7 @@ class CurrentSpeakerMonitor : public sigslot::has_slots<> { }; bool started_; - Call* call_; + AudioSourceContext* audio_source_context_; BaseSession* session_; std::map<uint32, SpeakingState> ssrc_to_speaking_state_map_; uint32 current_speaker_ssrc_; diff --git a/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor_unittest.cc b/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor_unittest.cc index 84c76185545..b65611f6d01 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/currentspeakermonitor_unittest.cc @@ -47,7 +47,7 @@ class MockCall : public Call { MockCall() : Call(NULL) {} void EmitAudioMonitor(const AudioInfo& info) { - SignalAudioMonitor(this, info); + GetAudioSourceProxy()->SignalAudioMonitor(GetAudioSourceProxy(), info); } }; @@ -56,7 +56,7 @@ class CurrentSpeakerMonitorTest : public testing::Test, public: CurrentSpeakerMonitorTest() { call_ = new MockCall(); - monitor_ = new CurrentSpeakerMonitor(call_, NULL); + monitor_ = new CurrentSpeakerMonitor(call_->GetAudioSourceProxy(), NULL); // Shrink the minimum time betweeen switches to 10 ms so we don't have to // slow down our tests. monitor_->set_min_time_between_switches(kMinTimeBetweenSwitches); diff --git a/chromium/third_party/libjingle/source/talk/session/media/externalhmac.cc b/chromium/third_party/libjingle/source/talk/session/media/externalhmac.cc new file mode 100644 index 00000000000..470668d7656 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/session/media/externalhmac.cc @@ -0,0 +1,173 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(HAVE_SRTP) && defined(ENABLE_EXTERNAL_AUTH) + +#include "talk/session/media/externalhmac.h" + +#include <stdlib.h> // For malloc/free. + +#ifdef SRTP_RELATIVE_PATH +#include "srtp.h" // NOLINT +#else +#include "third_party/libsrtp/include/srtp.h" +#endif // SRTP_RELATIVE_PATH + +#include "talk/base/logging.h" + +// Begin test case 0 */ +static const uint8_t kExternalHmacTestCase0Key[20] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b +}; + +static const uint8_t kExternalHmacTestCase0Data[8] = { + 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 // "Hi There" +}; + +static const uint8_t kExternalHmacFakeTag[10] = { + 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd +}; + +static const auth_test_case_t kExternalHmacTestCase0 = { + 20, // Octets in key + const_cast<uint8_t*>(kExternalHmacTestCase0Key), // Key + 8, // Octets in data + const_cast<uint8_t*>(kExternalHmacTestCase0Data), // Data + 10, // Octets in tag + const_cast<uint8_t*>(kExternalHmacFakeTag), // Tag + NULL // Pointer to next + // testcase +}; + +static const char kExternalHmacDescription[] = + "external hmac sha-1 authentication"; + +// auth_type_t external_hmac is the hmac metaobject + +static const auth_type_t external_hmac = { + external_hmac_alloc, + external_hmac_dealloc, + (auth_init_func) external_hmac_init, + (auth_compute_func) external_hmac_compute, + (auth_update_func) external_hmac_update, + (auth_start_func) external_hmac_start, + const_cast<char*>(kExternalHmacDescription), + 0, // Instance count. + const_cast<auth_test_case_t*>(&kExternalHmacTestCase0), + NULL, // No debugging module. + EXTERNAL_HMAC_SHA1 +}; + + +err_status_t external_hmac_alloc(auth_t** a, int key_len, int out_len) { + uint8_t* pointer; + + // Check key length - note that we don't support keys larger + // than 20 bytes yet + if (key_len > 20) + return err_status_bad_param; + + // Check output length - should be less than 20 bytes/ + if (out_len > 20) + return err_status_bad_param; + + // Allocate memory for auth and hmac_ctx_t structures. + pointer = new uint8_t[(sizeof(external_hmac_ctx_t) + sizeof(auth_t))]; + if (pointer == NULL) + return err_status_alloc_fail; + + // Set pointers + *a = (auth_t *)pointer; + // |external_hmac| is const and libsrtp expects |type| to be non-const. + // const conversion is required. |external_hmac| is constant because we don't + // want to increase global count in Chrome. + (*a)->type = const_cast<auth_type_t*>(&external_hmac); + (*a)->state = pointer + sizeof(auth_t); + (*a)->out_len = out_len; + (*a)->key_len = key_len; + (*a)->prefix_len = 0; + + return err_status_ok; +} + +err_status_t external_hmac_dealloc(auth_t* a) { + // Zeroize entire state + memset((uint8_t *)a, 0, sizeof(external_hmac_ctx_t) + sizeof(auth_t)); + + // Free memory + delete[] a; + + return err_status_ok; +} + +err_status_t external_hmac_init(external_hmac_ctx_t* state, + const uint8_t* key, + int key_len) { + if (key_len > HMAC_KEY_LENGTH) + return err_status_bad_param; + + memset(state->key, 0, key_len); + memcpy(state->key, key, key_len); + state->key_length = key_len; + return err_status_ok; +} + +err_status_t external_hmac_start(external_hmac_ctx_t* state) { + return err_status_ok; +} + +err_status_t external_hmac_update(external_hmac_ctx_t* state, + const uint8_t* message, + int msg_octets) { + return err_status_ok; +} + +err_status_t external_hmac_compute(external_hmac_ctx_t* state, + const void* message, + int msg_octets, + int tag_len, + uint8_t* result) { + memcpy(result, kExternalHmacFakeTag, tag_len); + return err_status_ok; +} + +err_status_t external_crypto_init() { + // |external_hmac| is const. const_cast is required as libsrtp expects + // non-const. + err_status_t status = crypto_kernel_replace_auth_type( + const_cast<auth_type_t*>(&external_hmac), EXTERNAL_HMAC_SHA1); + if (status) { + LOG(LS_ERROR) << "Error in replacing default auth module, error: " + << status; + return err_status_fail; + } + return err_status_ok; +} + +#endif // defined(HAVE_SRTP) && defined(ENABLE_EXTERNAL_AUTH) diff --git a/chromium/third_party/libjingle/source/talk/session/media/externalhmac.h b/chromium/third_party/libjingle/source/talk/session/media/externalhmac.h new file mode 100644 index 00000000000..287d9680a7a --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/session/media/externalhmac.h @@ -0,0 +1,90 @@ +/* + * libjingle + * Copyright 2014 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_SESSION_MEDIA_EXTERNAL_HMAC_H_ +#define TALK_SESSION_MEDIA_EXTERNAL_HMAC_H_ + +// External libsrtp HMAC auth module which implements methods defined in +// auth_type_t. +// The default auth module will be replaced only when the ENABLE_EXTERNAL_AUTH +// flag is enabled. This allows us to access to authentication keys, +// as the default auth implementation doesn't provide access and avoids +// hashing each packet twice. + +// How will libsrtp select this module? +// Libsrtp defines authentication function types identified by an unsigned +// integer, e.g. HMAC_SHA1 is 3. Using authentication ids, the application +// can plug any desired authentication modules into libsrtp. +// libsrtp also provides a mechanism to select different auth functions for +// individual streams. This can be done by setting the right value in +// the auth_type of srtp_policy_t. The application must first register auth +// functions and the corresponding authentication id using +// crypto_kernel_replace_auth_type function. +#if defined(HAVE_SRTP) && defined(ENABLE_EXTERNAL_AUTH) + +#include "talk/base/basictypes.h" +#ifdef SRTP_RELATIVE_PATH +#include "auth.h" // NOLINT +#else +#include "third_party/libsrtp/crypto/include/auth.h" +#endif // SRTP_RELATIVE_PATH + +#define EXTERNAL_HMAC_SHA1 HMAC_SHA1 + 1 +#define HMAC_KEY_LENGTH 20 + +// The HMAC context structure used to store authentication keys. +// The pointer to the key will be allocated in the external_hmac_init function. +// This pointer is owned by srtp_t in a template context. +typedef struct { + uint8 key[HMAC_KEY_LENGTH]; + int key_length; +} external_hmac_ctx_t; + +err_status_t external_hmac_alloc(auth_t** a, int key_len, int out_len); + +err_status_t external_hmac_dealloc(auth_t* a); + +err_status_t external_hmac_init(external_hmac_ctx_t* state, + const uint8_t* key, + int key_len); + +err_status_t external_hmac_start(external_hmac_ctx_t* state); + +err_status_t external_hmac_update(external_hmac_ctx_t* state, + const uint8_t* message, + int msg_octets); + +err_status_t external_hmac_compute(external_hmac_ctx_t* state, + const void* message, + int msg_octets, + int tag_len, + uint8_t* result); + +err_status_t external_crypto_init(); + +#endif // defined(HAVE_SRTP) && defined(ENABLE_EXTERNAL_AUTH) +#endif // TALK_SESSION_MEDIA_EXTERNAL_HMAC_H_ diff --git a/chromium/third_party/libjingle/source/talk/session/media/mediamessages.cc b/chromium/third_party/libjingle/source/talk/session/media/mediamessages.cc index 6b5d03cf95d..45c6c7965f1 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/mediamessages.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/mediamessages.cc @@ -52,13 +52,6 @@ bool ParseSsrc(const std::string& string, uint32* ssrc) { return talk_base::FromString(string, ssrc); } -bool ParseSsrc(const buzz::XmlElement* element, uint32* ssrc) { - if (element == NULL) { - return false; - } - return ParseSsrc(element->BodyText(), ssrc); -} - // Builds a <view> element according to the following spec: // goto/jinglemuc buzz::XmlElement* CreateViewElem(const std::string& name, diff --git a/chromium/third_party/libjingle/source/talk/session/media/mediasession.cc b/chromium/third_party/libjingle/source/talk/session/media/mediasession.cc index ba510b94150..006975f9319 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/mediasession.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/mediasession.cc @@ -62,6 +62,10 @@ using talk_base::scoped_ptr; // RFC4585 const char kMediaProtocolAvpf[] = "RTP/AVPF"; // RFC5124 +const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF"; + +// This should be replaced by "UDP/TLS/RTP/SAVPF", but we need to support it for +// now to be compatible with previous Chrome versions. const char kMediaProtocolSavpf[] = "RTP/SAVPF"; const char kMediaProtocolRtpPrefix[] = "RTP/"; @@ -308,6 +312,20 @@ static void GetCurrentStreamParams(const SessionDescription* sdesc, } } +// Filters the data codecs for the data channel type. +void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) { + // Filter RTP codec for SCTP and vice versa. + int codec_id = sctp ? kGoogleRtpDataCodecId : kGoogleSctpDataCodecId; + for (std::vector<DataCodec>::iterator iter = codecs->begin(); + iter != codecs->end();) { + if (iter->id == codec_id) { + iter = codecs->erase(iter); + } else { + ++iter; + } + } +} + template <typename IdStruct> class UsedIds { public: @@ -398,9 +416,9 @@ class UsedRtpHeaderExtensionIds : public UsedIds<RtpHeaderExtension> { } private: - // Min and Max local identifier as specified by RFC5285. + // Min and Max local identifier for one-byte header extensions, per RFC5285. static const int kLocalIdMin = 1; - static const int kLocalIdMax = 255; + static const int kLocalIdMax = 14; }; static bool IsSctp(const MediaContentDescription* desc) { @@ -691,7 +709,7 @@ template <class C> static bool CreateMediaContentOffer( const MediaSessionOptions& options, const std::vector<C>& codecs, - const SecureMediaPolicy& secure_policy, + const SecurePolicy& secure_policy, const CryptoParamsVec* current_cryptos, const std::vector<std::string>& crypto_suites, const RtpHeaderExtensions& rtp_extensions, @@ -701,7 +719,9 @@ static bool CreateMediaContentOffer( offer->AddCodecs(codecs); offer->SortCodecs(); - offer->set_crypto_required(secure_policy == SEC_REQUIRED); + if (secure_policy == SEC_REQUIRED) { + offer->set_crypto_required(CT_SDES); + } offer->set_rtcp_mux(options.rtcp_mux_enabled); offer->set_multistream(options.is_muc); offer->set_rtp_header_extensions(rtp_extensions); @@ -725,7 +745,7 @@ static bool CreateMediaContentOffer( } #endif - if (offer->crypto_required() && offer->cryptos().empty()) { + if (offer->crypto_required() == CT_SDES && offer->cryptos().empty()) { return false; } return true; @@ -841,7 +861,7 @@ static bool FindByUri(const RtpHeaderExtensions& extensions, // We assume that all URIs are given in a canonical format. if (it->uri == ext_to_match.uri) { if (found_extension != NULL) { - *found_extension= *it; + *found_extension = *it; } return true; } @@ -852,12 +872,16 @@ static bool FindByUri(const RtpHeaderExtensions& extensions, static void FindAndSetRtpHdrExtUsed( const RtpHeaderExtensions& reference_extensions, RtpHeaderExtensions* offered_extensions, + const RtpHeaderExtensions& other_extensions, UsedRtpHeaderExtensionIds* used_extensions) { for (RtpHeaderExtensions::const_iterator it = reference_extensions.begin(); it != reference_extensions.end(); ++it) { if (!FindByUri(*offered_extensions, *it, NULL)) { - RtpHeaderExtension ext = *it; - used_extensions->FindAndSetIdUsed(&ext); + RtpHeaderExtension ext; + if (!FindByUri(other_extensions, *it, &ext)) { + ext = *it; + used_extensions->FindAndSetIdUsed(&ext); + } offered_extensions->push_back(ext); } } @@ -903,7 +927,7 @@ static bool CreateMediaContentAnswer( const MediaContentDescriptionImpl<C>* offer, const MediaSessionOptions& options, const std::vector<C>& local_codecs, - const SecureMediaPolicy& sdes_policy, + const SecurePolicy& sdes_policy, const CryptoParamsVec* current_cryptos, const RtpHeaderExtensions& local_rtp_extenstions, StreamParamsVec* current_streams, @@ -934,7 +958,7 @@ static bool CreateMediaContentAnswer( } if (answer->cryptos().empty() && - (offer->crypto_required() || sdes_policy == SEC_REQUIRED)) { + (offer->crypto_required() == CT_SDES || sdes_policy == SEC_REQUIRED)) { return false; } @@ -967,17 +991,20 @@ static bool CreateMediaContentAnswer( } static bool IsMediaProtocolSupported(MediaType type, - const std::string& protocol) { + const std::string& protocol, + bool secure_transport) { // Data channels can have a protocol of SCTP or SCTP/DTLS. if (type == MEDIA_TYPE_DATA && - (protocol == kMediaProtocolSctp || - protocol == kMediaProtocolDtlsSctp)) { + ((protocol == kMediaProtocolSctp && !secure_transport)|| + (protocol == kMediaProtocolDtlsSctp && secure_transport))) { return true; } + // Since not all applications serialize and deserialize the media protocol, // we will have to accept |protocol| to be empty. - return protocol == kMediaProtocolAvpf || protocol == kMediaProtocolSavpf || - protocol.empty(); + return protocol == kMediaProtocolAvpf || protocol.empty() || + protocol == kMediaProtocolSavpf || + (protocol == kMediaProtocolDtlsSavpf && secure_transport); } static void SetMediaProtocol(bool secure_transport, @@ -1024,6 +1051,25 @@ static bool IsDtlsActive( return current_tdesc->secure(); } +std::string MediaTypeToString(MediaType type) { + std::string type_str; + switch (type) { + case MEDIA_TYPE_AUDIO: + type_str = "audio"; + break; + case MEDIA_TYPE_VIDEO: + type_str = "video"; + break; + case MEDIA_TYPE_DATA: + type_str = "data"; + break; + default: + ASSERT(false); + break; + } + return type_str; +} + void MediaSessionOptions::AddStream(MediaType type, const std::string& id, const std::string& sync_label) { @@ -1179,6 +1225,8 @@ SessionDescription* MediaSessionDescriptionFactory::CreateOffer( scoped_ptr<DataContentDescription> data(new DataContentDescription()); bool is_sctp = (options.data_channel_type == DCT_SCTP); + FilterDataCodecs(&data_codecs, is_sctp); + cricket::SecurePolicy sdes_policy = IsDtlsActive(CN_DATA, current_description) ? cricket::SEC_DISABLED : secure(); @@ -1296,8 +1344,9 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( } bool rejected = !options.has_audio || audio_content->rejected || - !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO, - audio_answer->protocol()); + !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO, + audio_answer->protocol(), + audio_transport->secure()); if (!rejected) { AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer.get()); @@ -1344,7 +1393,9 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( return NULL; } bool rejected = !options.has_video || video_content->rejected || - !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO, video_answer->protocol()); + !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO, + video_answer->protocol(), + video_transport->secure()); if (!rejected) { if (!AddTransportAnswer(video_content->name, *(video_transport.get()), answer.get())) { @@ -1372,6 +1423,10 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( if (!data_transport) { return NULL; } + bool is_sctp = (options.data_channel_type == DCT_SCTP); + std::vector<DataCodec> data_codecs(data_codecs_); + FilterDataCodecs(&data_codecs, is_sctp); + scoped_ptr<DataContentDescription> data_answer( new DataContentDescription()); // Do not require or create SDES cryptos if DTLS is used. @@ -1393,7 +1448,9 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( } bool rejected = !options.has_data() || data_content->rejected || - !IsMediaProtocolSupported(MEDIA_TYPE_DATA, data_answer->protocol()); + !IsMediaProtocolSupported(MEDIA_TYPE_DATA, + data_answer->protocol(), + data_transport->secure()); if (!rejected) { data_answer->set_bandwidth(options.data_bandwidth); if (!AddTransportAnswer(data_content->name, *(data_transport.get()), @@ -1488,6 +1545,8 @@ void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer( const SessionDescription* current_description, RtpHeaderExtensions* audio_extensions, RtpHeaderExtensions* video_extensions) const { + // All header extensions allocated from the same range to avoid potential + // issues when using BUNDLE. UsedRtpHeaderExtensionIds used_ids; audio_extensions->clear(); video_extensions->clear(); @@ -1514,9 +1573,9 @@ void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer( // Add our default RTP header extensions that are not in // |current_description|. FindAndSetRtpHdrExtUsed(audio_rtp_header_extensions(), audio_extensions, - &used_ids); + *video_extensions, &used_ids); FindAndSetRtpHdrExtUsed(video_rtp_header_extensions(), video_extensions, - &used_ids); + *audio_extensions, &used_ids); } bool MediaSessionDescriptionFactory::AddTransportOffer( diff --git a/chromium/third_party/libjingle/source/talk/session/media/mediasession.h b/chromium/third_party/libjingle/source/talk/session/media/mediasession.h index ff25f5a040e..5041de0a970 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/mediasession.h +++ b/chromium/third_party/libjingle/source/talk/session/media/mediasession.h @@ -54,15 +54,14 @@ typedef std::vector<DataCodec> DataCodecs; typedef std::vector<CryptoParams> CryptoParamsVec; typedef std::vector<RtpHeaderExtension> RtpHeaderExtensions; -// TODO(juberti): Replace SecureMediaPolicy with SecurePolicy everywhere. -typedef SecurePolicy SecureMediaPolicy; - enum MediaType { MEDIA_TYPE_AUDIO, MEDIA_TYPE_VIDEO, MEDIA_TYPE_DATA }; +std::string MediaTypeToString(MediaType type); + enum MediaContentDirection { MD_INACTIVE, MD_SENDONLY, @@ -70,11 +69,19 @@ enum MediaContentDirection { MD_SENDRECV }; +enum CryptoType { + CT_NONE, + CT_SDES, + CT_DTLS +}; + // RTC4585 RTP/AVPF extern const char kMediaProtocolAvpf[]; // RFC5124 RTP/SAVPF extern const char kMediaProtocolSavpf[]; +extern const char kMediaProtocolDtlsSavpf[]; + extern const char kMediaProtocolRtpPrefix[]; extern const char kMediaProtocolSctp[]; @@ -153,7 +160,7 @@ class MediaContentDescription : public ContentDescription { MediaContentDescription() : rtcp_mux_(false), bandwidth_(kAutoBandwidth), - crypto_required_(false), + crypto_required_(CT_NONE), rtp_header_extensions_set_(false), multistream_(false), conference_mode_(false), @@ -188,9 +195,10 @@ class MediaContentDescription : public ContentDescription { void set_cryptos(const std::vector<CryptoParams>& cryptos) { cryptos_ = cryptos; } - bool crypto_required() const { return crypto_required_; } - void set_crypto_required(bool crypto) { - crypto_required_ = crypto; + + CryptoType crypto_required() const { return crypto_required_; } + void set_crypto_required(CryptoType type) { + crypto_required_ = type; } const RtpHeaderExtensions& rtp_header_extensions() const { @@ -277,7 +285,7 @@ class MediaContentDescription : public ContentDescription { int bandwidth_; std::string protocol_; std::vector<CryptoParams> cryptos_; - bool crypto_required_; + CryptoType crypto_required_; std::vector<RtpHeaderExtension> rtp_header_extensions_; bool rtp_header_extensions_set_; bool multistream_; @@ -312,6 +320,16 @@ class MediaContentDescriptionImpl : public MediaContentDescription { void AddCodec(const C& codec) { codecs_.push_back(codec); } + void AddOrReplaceCodec(const C& codec) { + for (typename std::vector<C>::iterator iter = codecs_.begin(); + iter != codecs_.end(); ++iter) { + if (iter->id == codec.id) { + *iter = codec; + return; + } + } + AddCodec(codec); + } void AddCodecs(const std::vector<C>& codecs) { typename std::vector<C>::const_iterator codec; for (codec = codecs.begin(); codec != codecs.end(); ++codec) { diff --git a/chromium/third_party/libjingle/source/talk/session/media/mediasession_unittest.cc b/chromium/third_party/libjingle/source/talk/session/media/mediasession_unittest.cc index 0e645667162..cf492a65ee1 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/mediasession_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/mediasession_unittest.cc @@ -41,12 +41,12 @@ #ifdef HAVE_SRTP #define ASSERT_CRYPTO(cd, s, cs) \ - ASSERT_FALSE(cd->crypto_required()); \ + ASSERT_EQ(cricket::CT_NONE, cd->crypto_required()); \ ASSERT_EQ(s, cd->cryptos().size()); \ ASSERT_EQ(std::string(cs), cd->cryptos()[0].cipher_suite) #else #define ASSERT_CRYPTO(cd, s, cs) \ - ASSERT_FALSE(cd->crypto_required()); \ + ASSERT_EQ(cricket::CT_NONE, cd->crypto_required()); \ ASSERT_EQ(0U, cd->cryptos().size()); #endif @@ -143,6 +143,7 @@ static const RtpHeaderExtension kAudioRtpExtension1[] = { static const RtpHeaderExtension kAudioRtpExtension2[] = { RtpHeaderExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 2), RtpHeaderExtension("http://google.com/testing/audio_something_else", 8), + RtpHeaderExtension("http://google.com/testing/both_audio_and_video", 7), }; static const RtpHeaderExtension kAudioRtpExtensionAnswer[] = { @@ -151,18 +152,21 @@ static const RtpHeaderExtension kAudioRtpExtensionAnswer[] = { static const RtpHeaderExtension kVideoRtpExtension1[] = { RtpHeaderExtension("urn:ietf:params:rtp-hdrext:toffset", 14), - RtpHeaderExtension("http://google.com/testing/video_something", 15), + RtpHeaderExtension("http://google.com/testing/video_something", 13), }; static const RtpHeaderExtension kVideoRtpExtension2[] = { RtpHeaderExtension("urn:ietf:params:rtp-hdrext:toffset", 2), RtpHeaderExtension("http://google.com/testing/video_something_else", 14), + RtpHeaderExtension("http://google.com/testing/both_audio_and_video", 7), }; static const RtpHeaderExtension kVideoRtpExtensionAnswer[] = { RtpHeaderExtension("urn:ietf:params:rtp-hdrext:toffset", 14), }; +static const uint32 kSimulcastParamsSsrc[] = {10, 11, 20, 21, 30, 31}; +static const uint32 kSimSsrc[] = {10, 20, 30}; static const uint32 kFec1Ssrc[] = {10, 11}; static const uint32 kFec2Ssrc[] = {20, 21}; static const uint32 kFec3Ssrc[] = {30, 31}; @@ -192,6 +196,32 @@ class MediaSessionDescriptionFactoryTest : public testing::Test { tdf2_.set_identity(&id2_); } + // Create a video StreamParamsVec object with: + // - one video stream with 3 simulcast streams and FEC, + StreamParamsVec CreateComplexVideoStreamParamsVec() { + SsrcGroup sim_group("SIM", MAKE_VECTOR(kSimSsrc)); + SsrcGroup fec_group1("FEC", MAKE_VECTOR(kFec1Ssrc)); + SsrcGroup fec_group2("FEC", MAKE_VECTOR(kFec2Ssrc)); + SsrcGroup fec_group3("FEC", MAKE_VECTOR(kFec3Ssrc)); + + std::vector<SsrcGroup> ssrc_groups; + ssrc_groups.push_back(sim_group); + ssrc_groups.push_back(fec_group1); + ssrc_groups.push_back(fec_group2); + ssrc_groups.push_back(fec_group3); + + StreamParams simulcast_params; + simulcast_params.id = kVideoTrack1; + simulcast_params.ssrcs = MAKE_VECTOR(kSimulcastParamsSsrc); + simulcast_params.ssrc_groups = ssrc_groups; + simulcast_params.cname = "Video_SIM_FEC"; + simulcast_params.sync_label = kMediaStream1; + + StreamParamsVec video_streams; + video_streams.push_back(simulcast_params); + + return video_streams; + } bool CompareCryptoParams(const CryptoParamsVec& c1, const CryptoParamsVec& c2) { @@ -1588,17 +1618,19 @@ TEST_F(MediaSessionDescriptionFactoryTest, // extensions from the first offer/answer exchange plus the extensions only // |f2_| offer. // Since the default local extension id |f2_| uses has already been used by - // |f1_| for another extensions, it is changed to 255. + // |f1_| for another extensions, it is changed to 13. const RtpHeaderExtension kUpdatedAudioRtpExtensions[] = { kAudioRtpExtensionAnswer[0], - RtpHeaderExtension(kAudioRtpExtension2[1].uri, 255), + RtpHeaderExtension(kAudioRtpExtension2[1].uri, 13), + kAudioRtpExtension2[2], }; // Since the default local extension id |f2_| uses has already been used by - // |f1_| for another extensions, is is changed to 254. + // |f1_| for another extensions, is is changed to 12. const RtpHeaderExtension kUpdatedVideoRtpExtensions[] = { kVideoRtpExtensionAnswer[0], - RtpHeaderExtension(kVideoRtpExtension2[1].uri, 254), + RtpHeaderExtension(kVideoRtpExtension2[1].uri, 12), + kVideoRtpExtension2[2], }; const AudioContentDescription* updated_acd = @@ -1756,6 +1788,64 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoWithAnswerBundle) { TestCryptoWithBundle(false); } +// Verifies that creating answer fails if the offer has UDP/TLS/RTP/SAVPF but +// DTLS is not enabled locally. +TEST_F(MediaSessionDescriptionFactoryTest, + TestOfferDtlsSavpfWithoutDtlsFailed) { + f1_.set_secure(SEC_ENABLED); + f2_.set_secure(SEC_ENABLED); + tdf1_.set_secure(SEC_DISABLED); + tdf2_.set_secure(SEC_DISABLED); + + talk_base::scoped_ptr<SessionDescription> offer( + f1_.CreateOffer(MediaSessionOptions(), NULL)); + ASSERT_TRUE(offer.get() != NULL); + ContentInfo* offer_content = offer->GetContentByName("audio"); + ASSERT_TRUE(offer_content != NULL); + AudioContentDescription* offer_audio_desc = + static_cast<AudioContentDescription*>(offer_content->description); + offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf); + + talk_base::scoped_ptr<SessionDescription> answer( + f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL)); + ASSERT_TRUE(answer != NULL); + ContentInfo* answer_content = answer->GetContentByName("audio"); + ASSERT_TRUE(answer_content != NULL); + + ASSERT_TRUE(answer_content->rejected); +} + +// Offers UDP/TLS/RTP/SAVPF and verifies the answer can be created and contains +// UDP/TLS/RTP/SAVPF. +TEST_F(MediaSessionDescriptionFactoryTest, TestOfferDtlsSavpfCreateAnswer) { + f1_.set_secure(SEC_ENABLED); + f2_.set_secure(SEC_ENABLED); + tdf1_.set_secure(SEC_ENABLED); + tdf2_.set_secure(SEC_ENABLED); + + talk_base::scoped_ptr<SessionDescription> offer( + f1_.CreateOffer(MediaSessionOptions(), NULL)); + ASSERT_TRUE(offer.get() != NULL); + ContentInfo* offer_content = offer->GetContentByName("audio"); + ASSERT_TRUE(offer_content != NULL); + AudioContentDescription* offer_audio_desc = + static_cast<AudioContentDescription*>(offer_content->description); + offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf); + + talk_base::scoped_ptr<SessionDescription> answer( + f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL)); + ASSERT_TRUE(answer != NULL); + + const ContentInfo* answer_content = answer->GetContentByName("audio"); + ASSERT_TRUE(answer_content != NULL); + ASSERT_FALSE(answer_content->rejected); + + const AudioContentDescription* answer_audio_desc = + static_cast<const AudioContentDescription*>(answer_content->description); + EXPECT_EQ(std::string(cricket::kMediaProtocolDtlsSavpf), + answer_audio_desc->protocol()); +} + // Test that we include both SDES and DTLS in the offer, but only include SDES // in the answer if DTLS isn't negotiated. TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoDtls) { diff --git a/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient.cc b/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient.cc index 246592c617f..a5a652cc78d 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient.cc @@ -339,8 +339,9 @@ bool ParseGingleEncryption(const buzz::XmlElement* desc, encryption != NULL; encryption = encryption->NextNamed(QN_ENCRYPTION)) { if (encryption->FirstNamed(usage) != NULL) { - media->set_crypto_required( - GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)); + if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) { + media->set_crypto_required(CT_SDES); + } for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO); crypto != NULL; crypto = crypto->NextNamed(QN_CRYPTO)) { @@ -479,8 +480,9 @@ bool ParseJingleEncryption(const buzz::XmlElement* content_elem, return true; } - media->set_crypto_required( - GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)); + if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) { + media->set_crypto_required(CT_SDES); + } for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO); crypto != NULL; diff --git a/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient_unittest.cc b/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient_unittest.cc index 1ad93722bda..7cb1ec97e39 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient_unittest.cc @@ -891,7 +891,6 @@ const std::string kJingleInitiateDynamicWithoutNames( const uint32 kAudioSsrc = 4294967295U; const uint32 kVideoSsrc = 87654321; const uint32 kDataSsrc = 1010101; -const uint32 kDataSid = 0; // Note that this message does not specify a session ID. It must be populated // before use. const std::string kGingleAcceptWithSsrcs( @@ -2514,6 +2513,7 @@ class MediaSessionClientTest : public sigslot::has_slots<> { CheckDataSsrcForIncomingAccept(call_->sessions()[0]); } // TODO(pthatcher): Check kDataSid if DCT_SCTP. + // const uint32 kDataSid = 0; } size_t ClearStanzas() { @@ -2751,11 +2751,11 @@ class MediaSessionClientTest : public sigslot::has_slots<> { ClearStanzas(); } - void MakeSignalingSecure(cricket::SecureMediaPolicy secure) { + void MakeSignalingSecure(cricket::SecurePolicy secure) { client_->set_secure(secure); } - void ExpectCrypto(cricket::SecureMediaPolicy secure) { + void ExpectCrypto(cricket::SecurePolicy secure) { MakeSignalingSecure(secure); expect_incoming_crypto_ = true; #ifdef HAVE_SRTP diff --git a/chromium/third_party/libjingle/source/talk/session/media/planarfunctions_unittest.cc b/chromium/third_party/libjingle/source/talk/session/media/planarfunctions_unittest.cc new file mode 100644 index 00000000000..32cacf99575 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/session/media/planarfunctions_unittest.cc @@ -0,0 +1,1010 @@ +// libjingle +// Copyright 2014 Google Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// 3. The name of the author may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <string> + +#include "libyuv/convert.h" +#include "libyuv/convert_from.h" +#include "libyuv/convert_from_argb.h" +#include "libyuv/format_conversion.h" +#include "libyuv/mjpeg_decoder.h" +#include "libyuv/planar_functions.h" +#include "talk/base/flags.h" +#include "talk/base/gunit.h" +#include "talk/base/scoped_ptr.h" +#include "talk/media/base/testutils.h" +#include "talk/media/base/videocommon.h" + +// Undefine macros for the windows build. +#undef max +#undef min + +using cricket::DumpPlanarYuvTestImage; + +DEFINE_bool(planarfunctions_dump, false, + "whether to write out scaled images for inspection"); +DEFINE_int(planarfunctions_repeat, 1, + "how many times to perform each scaling operation (for perf testing)"); + +namespace cricket { + +// Number of testing colors in each color channel. +static const int kTestingColorChannelResolution = 6; + +// The total number of testing colors +// kTestingColorNum = kTestingColorChannelResolution^3; +static const int kTestingColorNum = kTestingColorChannelResolution * + kTestingColorChannelResolution * kTestingColorChannelResolution; + +static const int kWidth = 1280; +static const int kHeight = 720; +static const int kAlignment = 16; + +class PlanarFunctionsTest : public testing::TestWithParam<int> { + protected: + PlanarFunctionsTest() : dump_(false), repeat_(1) { + InitializeColorBand(); + } + + virtual void SetUp() { + dump_ = FLAG_planarfunctions_dump; + repeat_ = FLAG_planarfunctions_repeat; + } + + // Initialize the color band for testing. + void InitializeColorBand() { + testing_color_y_.reset(new uint8[kTestingColorNum]); + testing_color_u_.reset(new uint8[kTestingColorNum]); + testing_color_v_.reset(new uint8[kTestingColorNum]); + testing_color_r_.reset(new uint8[kTestingColorNum]); + testing_color_g_.reset(new uint8[kTestingColorNum]); + testing_color_b_.reset(new uint8[kTestingColorNum]); + int color_counter = 0; + for (int i = 0; i < kTestingColorChannelResolution; ++i) { + uint8 color_r = static_cast<uint8>( + i * 255 / (kTestingColorChannelResolution - 1)); + for (int j = 0; j < kTestingColorChannelResolution; ++j) { + uint8 color_g = static_cast<uint8>( + j * 255 / (kTestingColorChannelResolution - 1)); + for (int k = 0; k < kTestingColorChannelResolution; ++k) { + uint8 color_b = static_cast<uint8>( + k * 255 / (kTestingColorChannelResolution - 1)); + testing_color_r_[color_counter] = color_r; + testing_color_g_[color_counter] = color_g; + testing_color_b_[color_counter] = color_b; + // Converting the testing RGB colors to YUV colors. + ConvertRgbPixel(color_r, color_g, color_b, + &(testing_color_y_[color_counter]), + &(testing_color_u_[color_counter]), + &(testing_color_v_[color_counter])); + ++color_counter; + } + } + } + } + // Simple and slow RGB->YUV conversion. From NTSC standard, c/o Wikipedia. + // (from lmivideoframe_unittest.cc) + void ConvertRgbPixel(uint8 r, uint8 g, uint8 b, + uint8* y, uint8* u, uint8* v) { + *y = ClampUint8(.257 * r + .504 * g + .098 * b + 16); + *u = ClampUint8(-.148 * r - .291 * g + .439 * b + 128); + *v = ClampUint8(.439 * r - .368 * g - .071 * b + 128); + } + + uint8 ClampUint8(double value) { + value = std::max(0., std::min(255., value)); + uint8 uint8_value = static_cast<uint8>(value); + return uint8_value; + } + + // Generate a Red-Green-Blue inter-weaving chessboard-like + // YUV testing image (I420/I422/I444). + // The pattern looks like c0 c1 c2 c3 ... + // c1 c2 c3 c4 ... + // c2 c3 c4 c5 ... + // ............... + // The size of each chrome block is (block_size) x (block_size). + uint8* CreateFakeYuvTestingImage(int height, int width, int block_size, + libyuv::JpegSubsamplingType subsample_type, + uint8* &y_pointer, + uint8* &u_pointer, + uint8* &v_pointer) { + if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; } + int y_size = height * width; + int u_size, v_size; + int vertical_sample_ratio = 1, horizontal_sample_ratio = 1; + switch (subsample_type) { + case libyuv::kJpegYuv420: + u_size = ((height + 1) >> 1) * ((width + 1) >> 1); + v_size = u_size; + vertical_sample_ratio = 2, horizontal_sample_ratio = 2; + break; + case libyuv::kJpegYuv422: + u_size = height * ((width + 1) >> 1); + v_size = u_size; + vertical_sample_ratio = 1, horizontal_sample_ratio = 2; + break; + case libyuv::kJpegYuv444: + v_size = u_size = y_size; + vertical_sample_ratio = 1, horizontal_sample_ratio = 1; + break; + case libyuv::kJpegUnknown: + default: + return NULL; + break; + } + uint8* image_pointer = new uint8[y_size + u_size + v_size + kAlignment]; + y_pointer = ALIGNP(image_pointer, kAlignment); + u_pointer = ALIGNP(&image_pointer[y_size], kAlignment); + v_pointer = ALIGNP(&image_pointer[y_size + u_size], kAlignment); + uint8* current_y_pointer = y_pointer; + uint8* current_u_pointer = u_pointer; + uint8* current_v_pointer = v_pointer; + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + int color = ((i / block_size) + (j / block_size)) % kTestingColorNum; + *(current_y_pointer++) = testing_color_y_[color]; + if (i % horizontal_sample_ratio == 0 && + j % vertical_sample_ratio == 0) { + *(current_u_pointer++) = testing_color_u_[color]; + *(current_v_pointer++) = testing_color_v_[color]; + } + } + } + return image_pointer; + } + + // Generate a Red-Green-Blue inter-weaving chessboard-like + // YUY2/UYVY testing image. + // The pattern looks like c0 c1 c2 c3 ... + // c1 c2 c3 c4 ... + // c2 c3 c4 c5 ... + // ............... + // The size of each chrome block is (block_size) x (block_size). + uint8* CreateFakeInterleaveYuvTestingImage( + int height, int width, int block_size, + uint8* &yuv_pointer, FourCC fourcc_type) { + if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; } + if (fourcc_type != FOURCC_YUY2 && fourcc_type != FOURCC_UYVY) { + LOG(LS_ERROR) << "Format " << static_cast<int>(fourcc_type) + << " is not supported."; + return NULL; + } + // Regularize the width of the output to be even. + int awidth = (width + 1) & ~1; + + uint8* image_pointer = new uint8[2 * height * awidth + kAlignment]; + yuv_pointer = ALIGNP(image_pointer, kAlignment); + uint8* current_yuv_pointer = yuv_pointer; + switch (fourcc_type) { + case FOURCC_YUY2: { + for (int j = 0; j < height; ++j) { + for (int i = 0; i < awidth; i += 2, current_yuv_pointer += 4) { + int color1 = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + int color2 = (((i + 1) / block_size) + (j / block_size)) % + kTestingColorNum; + current_yuv_pointer[0] = testing_color_y_[color1]; + if (i < width) { + current_yuv_pointer[1] = static_cast<uint8>( + (static_cast<uint32>(testing_color_u_[color1]) + + static_cast<uint32>(testing_color_u_[color2])) / 2); + current_yuv_pointer[2] = testing_color_y_[color2]; + current_yuv_pointer[3] = static_cast<uint8>( + (static_cast<uint32>(testing_color_v_[color1]) + + static_cast<uint32>(testing_color_v_[color2])) / 2); + } else { + current_yuv_pointer[1] = testing_color_u_[color1]; + current_yuv_pointer[2] = 0; + current_yuv_pointer[3] = testing_color_v_[color1]; + } + } + } + break; + } + case FOURCC_UYVY: { + for (int j = 0; j < height; ++j) { + for (int i = 0; i < awidth; i += 2, current_yuv_pointer += 4) { + int color1 = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + int color2 = (((i + 1) / block_size) + (j / block_size)) % + kTestingColorNum; + if (i < width) { + current_yuv_pointer[0] = static_cast<uint8>( + (static_cast<uint32>(testing_color_u_[color1]) + + static_cast<uint32>(testing_color_u_[color2])) / 2); + current_yuv_pointer[1] = testing_color_y_[color1]; + current_yuv_pointer[2] = static_cast<uint8>( + (static_cast<uint32>(testing_color_v_[color1]) + + static_cast<uint32>(testing_color_v_[color2])) / 2); + current_yuv_pointer[3] = testing_color_y_[color2]; + } else { + current_yuv_pointer[0] = testing_color_u_[color1]; + current_yuv_pointer[1] = testing_color_y_[color1]; + current_yuv_pointer[2] = testing_color_v_[color1]; + current_yuv_pointer[3] = 0; + } + } + } + break; + } + } + return image_pointer; + } + // Generate a Red-Green-Blue inter-weaving chessboard-like + // Q420 testing image. + // The pattern looks like c0 c1 c2 c3 ... + // c1 c2 c3 c4 ... + // c2 c3 c4 c5 ... + // ............... + // The size of each chrome block is (block_size) x (block_size). + uint8* CreateFakeQ420TestingImage(int height, int width, int block_size, + uint8* &y_pointer, uint8* &yuy2_pointer) { + if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; } + // Regularize the width of the output to be even. + int awidth = (width + 1) & ~1; + + uint8* image_pointer = new uint8[(height / 2) * awidth * 2 + + ((height + 1) / 2) * width + kAlignment]; + y_pointer = ALIGNP(image_pointer, kAlignment); + yuy2_pointer = y_pointer + ((height + 1) / 2) * width; + uint8* current_yuy2_pointer = yuy2_pointer; + uint8* current_y_pointer = y_pointer; + for (int j = 0; j < height; ++j) { + if (j % 2 == 0) { + for (int i = 0; i < width; ++i) { + int color = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + *(current_y_pointer++) = testing_color_y_[color]; + } + } else { + for (int i = 0; i < awidth; i += 2, current_yuy2_pointer += 4) { + int color1 = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + int color2 = (((i + 1) / block_size) + (j / block_size)) % + kTestingColorNum; + current_yuy2_pointer[0] = testing_color_y_[color1]; + if (i < width) { + current_yuy2_pointer[1] = static_cast<uint8>( + (static_cast<uint32>(testing_color_u_[color1]) + + static_cast<uint32>(testing_color_u_[color2])) / 2); + current_yuy2_pointer[2] = testing_color_y_[color2]; + current_yuy2_pointer[3] = static_cast<uint8>( + (static_cast<uint32>(testing_color_v_[color1]) + + static_cast<uint32>(testing_color_v_[color2])) / 2); + } else { + current_yuy2_pointer[1] = testing_color_u_[color1]; + current_yuy2_pointer[2] = 0; + current_yuy2_pointer[3] = testing_color_v_[color1]; + } + } + } + } + return image_pointer; + } + + // Generate a Red-Green-Blue inter-weaving chessboard-like + // NV12 testing image. + // (Note: No interpolation is used.) + // The pattern looks like c0 c1 c2 c3 ... + // c1 c2 c3 c4 ... + // c2 c3 c4 c5 ... + // ............... + // The size of each chrome block is (block_size) x (block_size). + uint8* CreateFakeNV12TestingImage(int height, int width, int block_size, + uint8* &y_pointer, uint8* &uv_pointer) { + if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; } + + uint8* image_pointer = new uint8[height * width + + ((height + 1) / 2) * ((width + 1) / 2) * 2 + kAlignment]; + y_pointer = ALIGNP(image_pointer, kAlignment); + uv_pointer = y_pointer + height * width; + uint8* current_uv_pointer = uv_pointer; + uint8* current_y_pointer = y_pointer; + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + int color = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + *(current_y_pointer++) = testing_color_y_[color]; + } + if (j % 2 == 0) { + for (int i = 0; i < width; i += 2, current_uv_pointer += 2) { + int color = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + current_uv_pointer[0] = testing_color_u_[color]; + current_uv_pointer[1] = testing_color_v_[color]; + } + } + } + return image_pointer; + } + + // Generate a Red-Green-Blue inter-weaving chessboard-like + // M420 testing image. + // (Note: No interpolation is used.) + // The pattern looks like c0 c1 c2 c3 ... + // c1 c2 c3 c4 ... + // c2 c3 c4 c5 ... + // ............... + // The size of each chrome block is (block_size) x (block_size). + uint8* CreateFakeM420TestingImage( + int height, int width, int block_size, uint8* &m420_pointer) { + if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; } + + uint8* image_pointer = new uint8[height * width + + ((height + 1) / 2) * ((width + 1) / 2) * 2 + kAlignment]; + m420_pointer = ALIGNP(image_pointer, kAlignment); + uint8* current_m420_pointer = m420_pointer; + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + int color = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + *(current_m420_pointer++) = testing_color_y_[color]; + } + if (j % 2 == 1) { + for (int i = 0; i < width; i += 2, current_m420_pointer += 2) { + int color = ((i / block_size) + ((j - 1) / block_size)) % + kTestingColorNum; + current_m420_pointer[0] = testing_color_u_[color]; + current_m420_pointer[1] = testing_color_v_[color]; + } + } + } + return image_pointer; + } + + // Generate a Red-Green-Blue inter-weaving chessboard-like + // ARGB/ABGR/RAW/BG24 testing image. + // The pattern looks like c0 c1 c2 c3 ... + // c1 c2 c3 c4 ... + // c2 c3 c4 c5 ... + // ............... + // The size of each chrome block is (block_size) x (block_size). + uint8* CreateFakeArgbTestingImage(int height, int width, int block_size, + uint8* &argb_pointer, FourCC fourcc_type) { + if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; } + uint8* image_pointer = NULL; + if (fourcc_type == FOURCC_ABGR || fourcc_type == FOURCC_BGRA || + fourcc_type == FOURCC_ARGB) { + image_pointer = new uint8[height * width * 4 + kAlignment]; + } else if (fourcc_type == FOURCC_RAW || fourcc_type == FOURCC_24BG) { + image_pointer = new uint8[height * width * 3 + kAlignment]; + } else { + LOG(LS_ERROR) << "Format " << static_cast<int>(fourcc_type) + << " is not supported."; + return NULL; + } + argb_pointer = ALIGNP(image_pointer, kAlignment); + uint8* current_pointer = argb_pointer; + switch (fourcc_type) { + case FOURCC_ARGB: { + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + int color = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + *(current_pointer++) = testing_color_b_[color]; + *(current_pointer++) = testing_color_g_[color]; + *(current_pointer++) = testing_color_r_[color]; + *(current_pointer++) = 255; + } + } + break; + } + case FOURCC_ABGR: { + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + int color = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + *(current_pointer++) = testing_color_r_[color]; + *(current_pointer++) = testing_color_g_[color]; + *(current_pointer++) = testing_color_b_[color]; + *(current_pointer++) = 255; + } + } + break; + } + case FOURCC_BGRA: { + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + int color = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + *(current_pointer++) = 255; + *(current_pointer++) = testing_color_r_[color]; + *(current_pointer++) = testing_color_g_[color]; + *(current_pointer++) = testing_color_b_[color]; + } + } + break; + } + case FOURCC_24BG: { + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + int color = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + *(current_pointer++) = testing_color_b_[color]; + *(current_pointer++) = testing_color_g_[color]; + *(current_pointer++) = testing_color_r_[color]; + } + } + break; + } + case FOURCC_RAW: { + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + int color = ((i / block_size) + (j / block_size)) % + kTestingColorNum; + *(current_pointer++) = testing_color_r_[color]; + *(current_pointer++) = testing_color_g_[color]; + *(current_pointer++) = testing_color_b_[color]; + } + } + break; + } + default: { + LOG(LS_ERROR) << "Format " << static_cast<int>(fourcc_type) + << " is not supported."; + } + } + return image_pointer; + } + + // Check if two memory chunks are equal. + // (tolerate MSE errors within a threshold). + static bool IsMemoryEqual(const uint8* ibuf, const uint8* obuf, + int osize, double average_error) { + double sse = cricket::ComputeSumSquareError(ibuf, obuf, osize); + double error = sse / osize; // Mean Squared Error. + double PSNR = cricket::ComputePSNR(sse, osize); + LOG(LS_INFO) << "Image MSE: " << error << " Image PSNR: " << PSNR + << " First Diff Byte: " << FindDiff(ibuf, obuf, osize); + return (error < average_error); + } + + // Returns the index of the first differing byte. Easier to debug than memcmp. + static int FindDiff(const uint8* buf1, const uint8* buf2, int len) { + int i = 0; + while (i < len && buf1[i] == buf2[i]) { + i++; + } + return (i < len) ? i : -1; + } + + // Dump the result image (ARGB format). + void DumpArgbImage(const uint8* obuf, int width, int height) { + DumpPlanarArgbTestImage(GetTestName(), obuf, width, height); + } + + // Dump the result image (YUV420 format). + void DumpYuvImage(const uint8* obuf, int width, int height) { + DumpPlanarYuvTestImage(GetTestName(), obuf, width, height); + } + + std::string GetTestName() { + const testing::TestInfo* const test_info = + testing::UnitTest::GetInstance()->current_test_info(); + std::string test_name(test_info->name()); + return test_name; + } + + bool dump_; + int repeat_; + + // Y, U, V and R, G, B channels of testing colors. + talk_base::scoped_ptr<uint8[]> testing_color_y_; + talk_base::scoped_ptr<uint8[]> testing_color_u_; + talk_base::scoped_ptr<uint8[]> testing_color_v_; + talk_base::scoped_ptr<uint8[]> testing_color_r_; + talk_base::scoped_ptr<uint8[]> testing_color_g_; + talk_base::scoped_ptr<uint8[]> testing_color_b_; +}; + +TEST_F(PlanarFunctionsTest, I420Copy) { + uint8 *y_pointer = NULL, *u_pointer = NULL, *v_pointer = NULL; + int y_pitch = kWidth; + int u_pitch = (kWidth + 1) >> 1; + int v_pitch = (kWidth + 1) >> 1; + int y_size = kHeight * kWidth; + int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1); + int block_size = 3; + // Generate a fake input image. + talk_base::scoped_ptr<uint8[]> yuv_input( + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, + libyuv::kJpegYuv420, + y_pointer, u_pointer, v_pointer)); + // Allocate space for the output image. + talk_base::scoped_ptr<uint8[]> yuv_output( + new uint8[I420_SIZE(kHeight, kWidth) + kAlignment]); + uint8 *y_output_pointer = ALIGNP(yuv_output.get(), kAlignment); + uint8 *u_output_pointer = y_output_pointer + y_size; + uint8 *v_output_pointer = u_output_pointer + uv_size; + + for (int i = 0; i < repeat_; ++i) { + libyuv::I420Copy(y_pointer, y_pitch, + u_pointer, u_pitch, + v_pointer, v_pitch, + y_output_pointer, y_pitch, + u_output_pointer, u_pitch, + v_output_pointer, v_pitch, + kWidth, kHeight); + } + + // Expect the copied frame to be exactly the same. + EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_pointer, + I420_SIZE(kHeight, kWidth), 1.e-6)); + + if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); } +} + +TEST_F(PlanarFunctionsTest, I422ToI420) { + uint8 *y_pointer = NULL, *u_pointer = NULL, *v_pointer = NULL; + int y_pitch = kWidth; + int u_pitch = (kWidth + 1) >> 1; + int v_pitch = (kWidth + 1) >> 1; + int y_size = kHeight * kWidth; + int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1); + int block_size = 2; + // Generate a fake input image. + talk_base::scoped_ptr<uint8[]> yuv_input( + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, + libyuv::kJpegYuv422, + y_pointer, u_pointer, v_pointer)); + // Allocate space for the output image. + talk_base::scoped_ptr<uint8[]> yuv_output( + new uint8[I420_SIZE(kHeight, kWidth) + kAlignment]); + uint8 *y_output_pointer = ALIGNP(yuv_output.get(), kAlignment); + uint8 *u_output_pointer = y_output_pointer + y_size; + uint8 *v_output_pointer = u_output_pointer + uv_size; + // Generate the expected output. + uint8 *y_expected_pointer = NULL, *u_expected_pointer = NULL, + *v_expected_pointer = NULL; + talk_base::scoped_ptr<uint8[]> yuv_output_expected( + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, + libyuv::kJpegYuv420, + y_expected_pointer, u_expected_pointer, v_expected_pointer)); + + for (int i = 0; i < repeat_; ++i) { + libyuv::I422ToI420(y_pointer, y_pitch, + u_pointer, u_pitch, + v_pointer, v_pitch, + y_output_pointer, y_pitch, + u_output_pointer, u_pitch, + v_output_pointer, v_pitch, + kWidth, kHeight); + } + + // Compare the output frame with what is expected; expect exactly the same. + // Note: MSE should be set to a larger threshold if an odd block width + // is used, since the conversion will be lossy. + EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer, + I420_SIZE(kHeight, kWidth), 1.e-6)); + + if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); } +} + +TEST_P(PlanarFunctionsTest, Q420ToI420) { + // Get the unalignment offset + int unalignment = GetParam(); + uint8 *y_pointer = NULL, *yuy2_pointer = NULL; + int y_pitch = kWidth; + int yuy2_pitch = 2 * ((kWidth + 1) & ~1); + int u_pitch = (kWidth + 1) >> 1; + int v_pitch = (kWidth + 1) >> 1; + int y_size = kHeight * kWidth; + int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1); + int block_size = 2; + // Generate a fake input image. + talk_base::scoped_ptr<uint8[]> yuv_input( + CreateFakeQ420TestingImage(kHeight, kWidth, block_size, + y_pointer, yuy2_pointer)); + // Allocate space for the output image. + talk_base::scoped_ptr<uint8[]> yuv_output( + new uint8[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]); + uint8 *y_output_pointer = ALIGNP(yuv_output.get(), kAlignment) + + unalignment; + uint8 *u_output_pointer = y_output_pointer + y_size; + uint8 *v_output_pointer = u_output_pointer + uv_size; + // Generate the expected output. + uint8 *y_expected_pointer = NULL, *u_expected_pointer = NULL, + *v_expected_pointer = NULL; + talk_base::scoped_ptr<uint8[]> yuv_output_expected( + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, + libyuv::kJpegYuv420, + y_expected_pointer, u_expected_pointer, v_expected_pointer)); + + for (int i = 0; i < repeat_; ++i) { + libyuv::Q420ToI420(y_pointer, y_pitch, + yuy2_pointer, yuy2_pitch, + y_output_pointer, y_pitch, + u_output_pointer, u_pitch, + v_output_pointer, v_pitch, + kWidth, kHeight); + } + // Compare the output frame with what is expected; expect exactly the same. + // Note: MSE should be set to a larger threshold if an odd block width + // is used, since the conversion will be lossy. + EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer, + I420_SIZE(kHeight, kWidth), 1.e-6)); + + if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); } +} + +TEST_P(PlanarFunctionsTest, M420ToI420) { + // Get the unalignment offset + int unalignment = GetParam(); + uint8 *m420_pointer = NULL; + int y_pitch = kWidth; + int m420_pitch = kWidth; + int u_pitch = (kWidth + 1) >> 1; + int v_pitch = (kWidth + 1) >> 1; + int y_size = kHeight * kWidth; + int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1); + int block_size = 2; + // Generate a fake input image. + talk_base::scoped_ptr<uint8[]> yuv_input( + CreateFakeM420TestingImage(kHeight, kWidth, block_size, m420_pointer)); + // Allocate space for the output image. + talk_base::scoped_ptr<uint8[]> yuv_output( + new uint8[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]); + uint8 *y_output_pointer = ALIGNP(yuv_output.get(), kAlignment) + unalignment; + uint8 *u_output_pointer = y_output_pointer + y_size; + uint8 *v_output_pointer = u_output_pointer + uv_size; + // Generate the expected output. + uint8 *y_expected_pointer = NULL, *u_expected_pointer = NULL, + *v_expected_pointer = NULL; + talk_base::scoped_ptr<uint8[]> yuv_output_expected( + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, + libyuv::kJpegYuv420, + y_expected_pointer, u_expected_pointer, v_expected_pointer)); + + for (int i = 0; i < repeat_; ++i) { + libyuv::M420ToI420(m420_pointer, m420_pitch, + y_output_pointer, y_pitch, + u_output_pointer, u_pitch, + v_output_pointer, v_pitch, + kWidth, kHeight); + } + // Compare the output frame with what is expected; expect exactly the same. + // Note: MSE should be set to a larger threshold if an odd block width + // is used, since the conversion will be lossy. + EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer, + I420_SIZE(kHeight, kWidth), 1.e-6)); + + if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); } +} + +TEST_P(PlanarFunctionsTest, NV12ToI420) { + // Get the unalignment offset + int unalignment = GetParam(); + uint8 *y_pointer = NULL, *uv_pointer = NULL; + int y_pitch = kWidth; + int uv_pitch = 2 * ((kWidth + 1) >> 1); + int u_pitch = (kWidth + 1) >> 1; + int v_pitch = (kWidth + 1) >> 1; + int y_size = kHeight * kWidth; + int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1); + int block_size = 2; + // Generate a fake input image. + talk_base::scoped_ptr<uint8[]> yuv_input( + CreateFakeNV12TestingImage(kHeight, kWidth, block_size, + y_pointer, uv_pointer)); + // Allocate space for the output image. + talk_base::scoped_ptr<uint8[]> yuv_output( + new uint8[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]); + uint8 *y_output_pointer = ALIGNP(yuv_output.get(), kAlignment) + unalignment; + uint8 *u_output_pointer = y_output_pointer + y_size; + uint8 *v_output_pointer = u_output_pointer + uv_size; + // Generate the expected output. + uint8 *y_expected_pointer = NULL, *u_expected_pointer = NULL, + *v_expected_pointer = NULL; + talk_base::scoped_ptr<uint8[]> yuv_output_expected( + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, + libyuv::kJpegYuv420, + y_expected_pointer, u_expected_pointer, v_expected_pointer)); + + for (int i = 0; i < repeat_; ++i) { + libyuv::NV12ToI420(y_pointer, y_pitch, + uv_pointer, uv_pitch, + y_output_pointer, y_pitch, + u_output_pointer, u_pitch, + v_output_pointer, v_pitch, + kWidth, kHeight); + } + // Compare the output frame with what is expected; expect exactly the same. + // Note: MSE should be set to a larger threshold if an odd block width + // is used, since the conversion will be lossy. + EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer, + I420_SIZE(kHeight, kWidth), 1.e-6)); + + if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); } +} + +// A common macro for testing converting YUY2/UYVY to I420. +#define TEST_YUVTOI420(SRC_NAME, MSE, BLOCK_SIZE) \ +TEST_P(PlanarFunctionsTest, SRC_NAME##ToI420) { \ + /* Get the unalignment offset.*/ \ + int unalignment = GetParam(); \ + uint8 *yuv_pointer = NULL; \ + int yuv_pitch = 2 * ((kWidth + 1) & ~1); \ + int y_pitch = kWidth; \ + int u_pitch = (kWidth + 1) >> 1; \ + int v_pitch = (kWidth + 1) >> 1; \ + int y_size = kHeight * kWidth; \ + int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1); \ + int block_size = 2; \ + /* Generate a fake input image.*/ \ + talk_base::scoped_ptr<uint8[]> yuv_input( \ + CreateFakeInterleaveYuvTestingImage(kHeight, kWidth, BLOCK_SIZE, \ + yuv_pointer, FOURCC_##SRC_NAME)); \ + /* Allocate space for the output image.*/ \ + talk_base::scoped_ptr<uint8[]> yuv_output( \ + new uint8[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]); \ + uint8 *y_output_pointer = ALIGNP(yuv_output.get(), kAlignment) + \ + unalignment; \ + uint8 *u_output_pointer = y_output_pointer + y_size; \ + uint8 *v_output_pointer = u_output_pointer + uv_size; \ + /* Generate the expected output.*/ \ + uint8 *y_expected_pointer = NULL, *u_expected_pointer = NULL, \ + *v_expected_pointer = NULL; \ + talk_base::scoped_ptr<uint8[]> yuv_output_expected( \ + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, \ + libyuv::kJpegYuv420, \ + y_expected_pointer, u_expected_pointer, v_expected_pointer)); \ + for (int i = 0; i < repeat_; ++i) { \ + libyuv::SRC_NAME##ToI420(yuv_pointer, yuv_pitch, \ + y_output_pointer, y_pitch, \ + u_output_pointer, u_pitch, \ + v_output_pointer, v_pitch, \ + kWidth, kHeight); \ + } \ + /* Compare the output frame with what is expected.*/ \ + /* Note: MSE should be set to a larger threshold if an odd block width*/ \ + /* is used, since the conversion will be lossy.*/ \ + EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer, \ + I420_SIZE(kHeight, kWidth), MSE)); \ + if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); } \ +} \ + +// TEST_P(PlanarFunctionsTest, YUV2ToI420) +TEST_YUVTOI420(YUY2, 1.e-6, 2); +// TEST_P(PlanarFunctionsTest, UYVYToI420) +TEST_YUVTOI420(UYVY, 1.e-6, 2); + +// A common macro for testing converting I420 to ARGB, BGRA and ABGR. +#define TEST_YUVTORGB(SRC_NAME, DST_NAME, JPG_TYPE, MSE, BLOCK_SIZE) \ +TEST_F(PlanarFunctionsTest, SRC_NAME##To##DST_NAME) { \ + uint8 *y_pointer = NULL, *u_pointer = NULL, *v_pointer = NULL; \ + uint8 *argb_expected_pointer = NULL; \ + int y_pitch = kWidth; \ + int u_pitch = (kWidth + 1) >> 1; \ + int v_pitch = (kWidth + 1) >> 1; \ + /* Generate a fake input image.*/ \ + talk_base::scoped_ptr<uint8[]> yuv_input( \ + CreateFakeYuvTestingImage(kHeight, kWidth, BLOCK_SIZE, JPG_TYPE, \ + y_pointer, u_pointer, v_pointer)); \ + /* Generate the expected output.*/ \ + talk_base::scoped_ptr<uint8[]> argb_expected( \ + CreateFakeArgbTestingImage(kHeight, kWidth, BLOCK_SIZE, \ + argb_expected_pointer, FOURCC_##DST_NAME)); \ + /* Allocate space for the output.*/ \ + talk_base::scoped_ptr<uint8[]> argb_output( \ + new uint8[kHeight * kWidth * 4 + kAlignment]); \ + uint8 *argb_pointer = ALIGNP(argb_expected.get(), kAlignment); \ + for (int i = 0; i < repeat_; ++i) { \ + libyuv::SRC_NAME##To##DST_NAME(y_pointer, y_pitch, \ + u_pointer, u_pitch, \ + v_pointer, v_pitch, \ + argb_pointer, \ + kWidth * 4, \ + kWidth, kHeight); \ + } \ + EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer, \ + kHeight * kWidth * 4, MSE)); \ + if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); } \ +} + +// TEST_F(PlanarFunctionsTest, I420ToARGB) +TEST_YUVTORGB(I420, ARGB, libyuv::kJpegYuv420, 3., 2); +// TEST_F(PlanarFunctionsTest, I420ToABGR) +TEST_YUVTORGB(I420, ABGR, libyuv::kJpegYuv420, 3., 2); +// TEST_F(PlanarFunctionsTest, I420ToBGRA) +TEST_YUVTORGB(I420, BGRA, libyuv::kJpegYuv420, 3., 2); +// TEST_F(PlanarFunctionsTest, I422ToARGB) +TEST_YUVTORGB(I422, ARGB, libyuv::kJpegYuv422, 3., 2); +// TEST_F(PlanarFunctionsTest, I444ToARGB) +TEST_YUVTORGB(I444, ARGB, libyuv::kJpegYuv444, 3., 3); +// Note: an empirical MSE tolerance 3.0 is used here for the probable +// error from float-to-uint8 type conversion. + +TEST_F(PlanarFunctionsTest, I400ToARGB_Reference) { + uint8 *y_pointer = NULL, *u_pointer = NULL, *v_pointer = NULL; + int y_pitch = kWidth; + int u_pitch = (kWidth + 1) >> 1; + int v_pitch = (kWidth + 1) >> 1; + int block_size = 3; + // Generate a fake input image. + talk_base::scoped_ptr<uint8[]> yuv_input( + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, + libyuv::kJpegYuv420, + y_pointer, u_pointer, v_pointer)); + // As the comparison standard, we convert a grayscale image (by setting both + // U and V channels to be 128) using an I420 converter. + int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1); + + talk_base::scoped_ptr<uint8[]> uv(new uint8[uv_size + kAlignment]); + u_pointer = v_pointer = ALIGNP(uv.get(), kAlignment); + memset(u_pointer, 128, uv_size); + + // Allocate space for the output image and generate the expected output. + talk_base::scoped_ptr<uint8[]> argb_expected( + new uint8[kHeight * kWidth * 4 + kAlignment]); + talk_base::scoped_ptr<uint8[]> argb_output( + new uint8[kHeight * kWidth * 4 + kAlignment]); + uint8 *argb_expected_pointer = ALIGNP(argb_expected.get(), kAlignment); + uint8 *argb_pointer = ALIGNP(argb_output.get(), kAlignment); + + libyuv::I420ToARGB(y_pointer, y_pitch, + u_pointer, u_pitch, + v_pointer, v_pitch, + argb_expected_pointer, kWidth * 4, + kWidth, kHeight); + for (int i = 0; i < repeat_; ++i) { + libyuv::I400ToARGB_Reference(y_pointer, y_pitch, + argb_pointer, kWidth * 4, + kWidth, kHeight); + } + + // Note: I420ToARGB and I400ToARGB_Reference should produce identical results. + EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer, + kHeight * kWidth * 4, 2.)); + if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); } +} + +TEST_P(PlanarFunctionsTest, I400ToARGB) { + // Get the unalignment offset + int unalignment = GetParam(); + uint8 *y_pointer = NULL, *u_pointer = NULL, *v_pointer = NULL; + int y_pitch = kWidth; + int u_pitch = (kWidth + 1) >> 1; + int v_pitch = (kWidth + 1) >> 1; + int block_size = 3; + // Generate a fake input image. + talk_base::scoped_ptr<uint8[]> yuv_input( + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, + libyuv::kJpegYuv420, + y_pointer, u_pointer, v_pointer)); + // As the comparison standard, we convert a grayscale image (by setting both + // U and V channels to be 128) using an I420 converter. + int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1); + + // 1 byte extra if in the unaligned mode. + talk_base::scoped_ptr<uint8[]> uv(new uint8[uv_size * 2 + kAlignment]); + u_pointer = ALIGNP(uv.get(), kAlignment); + v_pointer = u_pointer + uv_size; + memset(u_pointer, 128, uv_size); + memset(v_pointer, 128, uv_size); + + // Allocate space for the output image and generate the expected output. + talk_base::scoped_ptr<uint8[]> argb_expected( + new uint8[kHeight * kWidth * 4 + kAlignment]); + // 1 byte extra if in the misalinged mode. + talk_base::scoped_ptr<uint8[]> argb_output( + new uint8[kHeight * kWidth * 4 + kAlignment + unalignment]); + uint8 *argb_expected_pointer = ALIGNP(argb_expected.get(), kAlignment); + uint8 *argb_pointer = ALIGNP(argb_output.get(), kAlignment) + unalignment; + + libyuv::I420ToARGB(y_pointer, y_pitch, + u_pointer, u_pitch, + v_pointer, v_pitch, + argb_expected_pointer, kWidth * 4, + kWidth, kHeight); + for (int i = 0; i < repeat_; ++i) { + libyuv::I400ToARGB(y_pointer, y_pitch, + argb_pointer, kWidth * 4, + kWidth, kHeight); + } + + // Note: current I400ToARGB uses an approximate method, + // so the error tolerance is larger here. + EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer, + kHeight * kWidth * 4, 64.0)); + if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); } +} + +TEST_P(PlanarFunctionsTest, ARGBToI400) { + // Get the unalignment offset + int unalignment = GetParam(); + // Create a fake ARGB input image. + uint8 *y_pointer = NULL, *u_pointer = NULL, *v_pointer = NULL; + uint8 *argb_pointer = NULL; + int block_size = 3; + // Generate a fake input image. + talk_base::scoped_ptr<uint8[]> argb_input( + CreateFakeArgbTestingImage(kHeight, kWidth, block_size, + argb_pointer, FOURCC_ARGB)); + // Generate the expected output. Only Y channel is used + talk_base::scoped_ptr<uint8[]> yuv_expected( + CreateFakeYuvTestingImage(kHeight, kWidth, block_size, + libyuv::kJpegYuv420, + y_pointer, u_pointer, v_pointer)); + // Allocate space for the Y output. + talk_base::scoped_ptr<uint8[]> y_output( + new uint8[kHeight * kWidth + kAlignment + unalignment]); + uint8 *y_output_pointer = ALIGNP(y_output.get(), kAlignment) + unalignment; + + for (int i = 0; i < repeat_; ++i) { + libyuv::ARGBToI400(argb_pointer, kWidth * 4, y_output_pointer, kWidth, + kWidth, kHeight); + } + // Check if the output matches the input Y channel. + // Note: an empirical MSE tolerance 2.0 is used here for the probable + // error from float-to-uint8 type conversion. + EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_pointer, + kHeight * kWidth, 2.)); + if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); } +} + +// A common macro for testing converting RAW, BG24, BGRA, and ABGR +// to ARGB. +#define TEST_ARGB(SRC_NAME, FC_ID, BPP, BLOCK_SIZE) \ +TEST_P(PlanarFunctionsTest, SRC_NAME##ToARGB) { \ + int unalignment = GetParam(); /* Get the unalignment offset.*/ \ + uint8 *argb_expected_pointer = NULL, *src_pointer = NULL; \ + /* Generate a fake input image.*/ \ + talk_base::scoped_ptr<uint8[]> src_input( \ + CreateFakeArgbTestingImage(kHeight, kWidth, BLOCK_SIZE, \ + src_pointer, FOURCC_##FC_ID)); \ + /* Generate the expected output.*/ \ + talk_base::scoped_ptr<uint8[]> argb_expected( \ + CreateFakeArgbTestingImage(kHeight, kWidth, BLOCK_SIZE, \ + argb_expected_pointer, FOURCC_ARGB)); \ + /* Allocate space for the output; 1 byte extra if in the unaligned mode.*/ \ + talk_base::scoped_ptr<uint8[]> argb_output( \ + new uint8[kHeight * kWidth * 4 + kAlignment + unalignment]); \ + uint8 *argb_pointer = ALIGNP(argb_output.get(), kAlignment) + unalignment; \ + for (int i = 0; i < repeat_; ++i) { \ + libyuv:: SRC_NAME##ToARGB(src_pointer, kWidth * (BPP), argb_pointer, \ + kWidth * 4, kWidth, kHeight); \ + } \ + /* Compare the result; expect identical.*/ \ + EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer, \ + kHeight * kWidth * 4, 1.e-6)); \ + if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); } \ +} + +TEST_ARGB(RAW, RAW, 3, 3); // TEST_P(PlanarFunctionsTest, RAWToARGB) +TEST_ARGB(BG24, 24BG, 3, 3); // TEST_P(PlanarFunctionsTest, BG24ToARGB) +TEST_ARGB(ABGR, ABGR, 4, 3); // TEST_P(PlanarFunctionsTest, ABGRToARGB) +TEST_ARGB(BGRA, BGRA, 4, 3); // TEST_P(PlanarFunctionsTest, BGRAToARGB) + +// Parameter Test: The parameter is the unalignment offset. +// Aligned data for testing assembly versions. +INSTANTIATE_TEST_CASE_P(PlanarFunctionsAligned, PlanarFunctionsTest, + ::testing::Values(0)); + +// Purposely unalign the output argb pointer to test slow path (C version). +INSTANTIATE_TEST_CASE_P(PlanarFunctionsMisaligned, PlanarFunctionsTest, + ::testing::Values(1)); + +} // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/session/media/srtpfilter.cc b/chromium/third_party/libjingle/source/talk/session/media/srtpfilter.cc index 8e1c2c1c4fd..10e9514e1cb 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/srtpfilter.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/srtpfilter.cc @@ -29,8 +29,9 @@ #include "talk/session/media/srtpfilter.h" +#include <string.h> + #include <algorithm> -#include <cstring> #include "talk/base/base64.h" #include "talk/base/logging.h" @@ -44,9 +45,16 @@ #ifdef HAVE_SRTP #ifdef SRTP_RELATIVE_PATH #include "srtp.h" // NOLINT +extern "C" srtp_stream_t srtp_get_stream(srtp_t srtp, uint32_t ssrc); +#include "srtp_priv.h" // NOLINT #else #include "third_party/libsrtp/include/srtp.h" +extern "C" srtp_stream_t srtp_get_stream(srtp_t srtp, uint32_t ssrc); +#include "third_party/libsrtp/include/srtp_priv.h" #endif // SRTP_RELATIVE_PATH +#ifdef ENABLE_EXTERNAL_AUTH +#include "talk/session/media/externalhmac.h" +#endif // ENABLE_EXTERNAL_AUTH #ifdef _DEBUG extern "C" debug_module_t mod_srtp; extern "C" debug_module_t mod_auth; @@ -158,7 +166,6 @@ bool SrtpFilter::SetRtpParams(const std::string& send_cs, LOG(LS_INFO) << "SRTP activated with negotiated parameters:" << " send cipher_suite " << send_cs << " recv cipher_suite " << recv_cs; - return true; } @@ -208,6 +215,16 @@ bool SrtpFilter::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { return send_session_->ProtectRtp(p, in_len, max_len, out_len); } +bool SrtpFilter::ProtectRtp(void* p, int in_len, int max_len, int* out_len, + int64* index) { + if (!IsActive()) { + LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active"; + return false; + } + + return send_session_->ProtectRtp(p, in_len, max_len, out_len, index); +} + bool SrtpFilter::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { if (!IsActive()) { LOG(LS_WARNING) << "Failed to ProtectRtcp: SRTP not active"; @@ -240,6 +257,15 @@ bool SrtpFilter::UnprotectRtcp(void* p, int in_len, int* out_len) { } } +bool SrtpFilter::GetRtpAuthParams(uint8** key, int* key_len, int* tag_len) { + if (!IsActive()) { + LOG(LS_WARNING) << "Failed to GetRtpAuthParams: SRTP not active"; + return false; + } + + return send_session_->GetRtpAuthParams(key, key_len, tag_len); +} + void SrtpFilter::set_signal_silent_time(uint32 signal_silent_time_in_ms) { signal_silent_time_in_ms_ = signal_silent_time_in_ms; if (state_ == ST_ACTIVE) { @@ -496,6 +522,14 @@ bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { return true; } +bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len, + int64* index) { + if (!ProtectRtp(p, in_len, max_len, out_len)) { + return false; + } + return (index) ? GetSendStreamPacketIndex(p, in_len, index) : true; +} + bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { if (!session_) { LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session"; @@ -554,6 +588,43 @@ bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) { return true; } +bool SrtpSession::GetRtpAuthParams(uint8** key, int* key_len, + int* tag_len) { +#if defined(ENABLE_EXTERNAL_AUTH) + external_hmac_ctx_t* external_hmac = NULL; + // stream_template will be the reference context for other streams. + // Let's use it for getting the keys. + srtp_stream_ctx_t* srtp_context = session_->stream_template; + if (srtp_context && srtp_context->rtp_auth) { + external_hmac = reinterpret_cast<external_hmac_ctx_t*>( + srtp_context->rtp_auth->state); + } + + if (!external_hmac) { + LOG(LS_ERROR) << "Failed to get auth keys from libsrtp!."; + return false; + } + + *key = external_hmac->key; + *key_len = external_hmac->key_length; + *tag_len = rtp_auth_tag_len_; + return true; +#else + return false; +#endif +} + +bool SrtpSession::GetSendStreamPacketIndex(void* p, int in_len, int64* index) { + srtp_hdr_t* hdr = reinterpret_cast<srtp_hdr_t*>(p); + srtp_stream_ctx_t* stream = srtp_get_stream(session_, hdr->ssrc); + if (stream == NULL) + return false; + + // Shift packet index, put into network byte order + *index = be64_to_cpu(rdbx_get_packet_index(&stream->rtp_rdbx) << 16); + return true; +} + void SrtpSession::set_signal_silent_time(uint32 signal_silent_time_in_ms) { srtp_stat_->set_signal_silent_time(signal_silent_time_in_ms); } @@ -596,6 +667,16 @@ bool SrtpSession::SetKey(int type, const std::string& cs, // TODO(astor) parse window size from WSH session-param policy.window_size = 1024; policy.allow_repeat_tx = 1; + // If external authentication option is enabled, supply custom auth module + // id EXTERNAL_HMAC_SHA1 in the policy structure. + // We want to set this option only for rtp packets. + // By default policy structure is initialized to HMAC_SHA1. +#if defined(ENABLE_EXTERNAL_AUTH) + // Enable external HMAC authentication only for outgoing streams. + if (type == ssrc_any_outbound) { + policy.rtp.auth_type = EXTERNAL_HMAC_SHA1; + } +#endif policy.next = NULL; int err = srtp_create(&session_, &policy); @@ -604,6 +685,7 @@ bool SrtpSession::SetKey(int type, const std::string& cs, return false; } + rtp_auth_tag_len_ = policy.rtp.auth_tag_len; rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len; return true; @@ -623,7 +705,13 @@ bool SrtpSession::Init() { LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err; return false; } - +#if defined(ENABLE_EXTERNAL_AUTH) + err = external_crypto_init(); + if (err != err_status_ok) { + LOG(LS_ERROR) << "Failed to initialize fake auth, err=" << err; + return false; + } +#endif inited_ = true; } diff --git a/chromium/third_party/libjingle/source/talk/session/media/srtpfilter.h b/chromium/third_party/libjingle/source/talk/session/media/srtpfilter.h index b6a269952a4..bc1735a40a4 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/srtpfilter.h +++ b/chromium/third_party/libjingle/source/talk/session/media/srtpfilter.h @@ -122,12 +122,18 @@ class SrtpFilter { // Encrypts/signs an individual RTP/RTCP packet, in-place. // If an HMAC is used, this will increase the packet size. bool ProtectRtp(void* data, int in_len, int max_len, int* out_len); + // Overloaded version, outputs packet index. + bool ProtectRtp(void* data, int in_len, int max_len, int* out_len, + int64* index); bool ProtectRtcp(void* data, int in_len, int max_len, int* out_len); // Decrypts/verifies an invidiual RTP/RTCP packet. // If an HMAC is used, this will decrease the packet size. bool UnprotectRtp(void* data, int in_len, int* out_len); bool UnprotectRtcp(void* data, int in_len, int* out_len); + // Returns rtp auth params from srtp context. + bool GetRtpAuthParams(uint8** key, int* key_len, int* tag_len); + // Update the silent threshold (in ms) for signaling errors. void set_signal_silent_time(uint32 signal_silent_time_in_ms); @@ -200,12 +206,18 @@ class SrtpSession { // Encrypts/signs an individual RTP/RTCP packet, in-place. // If an HMAC is used, this will increase the packet size. bool ProtectRtp(void* data, int in_len, int max_len, int* out_len); + // Overloaded version, outputs packet index. + bool ProtectRtp(void* data, int in_len, int max_len, int* out_len, + int64* index); bool ProtectRtcp(void* data, int in_len, int max_len, int* out_len); // Decrypts/verifies an invidiual RTP/RTCP packet. // If an HMAC is used, this will decrease the packet size. bool UnprotectRtp(void* data, int in_len, int* out_len); bool UnprotectRtcp(void* data, int in_len, int* out_len); + // Helper method to get authentication params. + bool GetRtpAuthParams(uint8** key, int* key_len, int* tag_len); + // Update the silent threshold (in ms) for signaling errors. void set_signal_silent_time(uint32 signal_silent_time_in_ms); @@ -217,9 +229,13 @@ class SrtpSession { private: bool SetKey(int type, const std::string& cs, const uint8* key, int len); + // Returns send stream current packet index from srtp db. + bool GetSendStreamPacketIndex(void* data, int in_len, int64* index); + static bool Init(); void HandleEvent(const srtp_event_data_t* ev); static void HandleEventThunk(srtp_event_data_t* ev); + static std::list<SrtpSession*>* sessions(); srtp_t session_; diff --git a/chromium/third_party/libjingle/source/talk/session/media/srtpfilter_unittest.cc b/chromium/third_party/libjingle/source/talk/session/media/srtpfilter_unittest.cc index 1b4aef2796e..4f0ebd49649 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/srtpfilter_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/srtpfilter_unittest.cc @@ -522,6 +522,25 @@ TEST_F(SrtpFilterTest, TestSetParamsKeyTooShort) { kTestKey1, kTestKeyLen - 1)); } +#if defined(ENABLE_EXTERNAL_AUTH) +TEST_F(SrtpFilterTest, TestGetSendAuthParams) { + EXPECT_TRUE(f1_.SetRtpParams(CS_AES_CM_128_HMAC_SHA1_32, + kTestKey1, kTestKeyLen, + CS_AES_CM_128_HMAC_SHA1_32, + kTestKey2, kTestKeyLen)); + EXPECT_TRUE(f1_.SetRtcpParams(CS_AES_CM_128_HMAC_SHA1_32, + kTestKey1, kTestKeyLen, + CS_AES_CM_128_HMAC_SHA1_32, + kTestKey2, kTestKeyLen)); + uint8* auth_key = NULL; + int auth_key_len = 0, auth_tag_len = 0; + EXPECT_TRUE(f1_.GetRtpAuthParams(&auth_key, &auth_key_len, &auth_tag_len)); + EXPECT_TRUE(auth_key != NULL); + EXPECT_EQ(20, auth_key_len); + EXPECT_EQ(4, auth_tag_len); +} +#endif + class SrtpSessionTest : public testing::Test { protected: virtual void SetUp() { @@ -606,6 +625,17 @@ TEST_F(SrtpSessionTest, TestProtect_AES_CM_128_HMAC_SHA1_32) { TestUnprotectRtcp(CS_AES_CM_128_HMAC_SHA1_32); } +TEST_F(SrtpSessionTest, TestGetSendStreamPacketIndex) { + EXPECT_TRUE(s1_.SetSend(CS_AES_CM_128_HMAC_SHA1_32, kTestKey1, kTestKeyLen)); + int64 index; + int out_len = 0; + EXPECT_TRUE(s1_.ProtectRtp(rtp_packet_, rtp_len_, + sizeof(rtp_packet_), &out_len, &index)); + // |index| will be shifted by 16. + int64 be64_index = be64_to_cpu(1 << 16); + EXPECT_EQ(be64_index, index); +} + // Test that we fail to unprotect if someone tampers with the RTP/RTCP paylaods. TEST_F(SrtpSessionTest, TestTamperReject) { int out_len; diff --git a/chromium/third_party/libjingle/source/talk/session/media/yuvscaler_unittest.cc b/chromium/third_party/libjingle/source/talk/session/media/yuvscaler_unittest.cc new file mode 100644 index 00000000000..93ac5343aa7 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/session/media/yuvscaler_unittest.cc @@ -0,0 +1,615 @@ +/* + * libjingle + * Copyright 2010 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sstream> + +#include "libyuv/cpu_id.h" +#include "libyuv/scale.h" +#include "talk/base/basictypes.h" +#include "talk/base/flags.h" +#include "talk/base/gunit.h" +#include "talk/base/scoped_ptr.h" +#include "talk/media/base/testutils.h" + +#if defined(_MSC_VER) +#define ALIGN16(var) __declspec(align(16)) var +#else +#define ALIGN16(var) var __attribute__((aligned(16))) +#endif + +using cricket::LoadPlanarYuvTestImage; +using cricket::DumpPlanarYuvTestImage; +using talk_base::scoped_ptr; + +DEFINE_bool(yuvscaler_dump, false, + "whether to write out scaled images for inspection"); +DEFINE_int(yuvscaler_repeat, 1, + "how many times to perform each scaling operation (for perf testing)"); + +static const int kAlignment = 16; + +// TEST_UNCACHED flushes cache to test real memory performance. +// TEST_RSTSC uses cpu cycles for more accurate benchmark of the scale function. +#ifndef __arm__ +// #define TEST_UNCACHED 1 +// #define TEST_RSTSC 1 +#endif + +#if defined(TEST_UNCACHED) || defined(TEST_RSTSC) +#ifdef _MSC_VER +#include <emmintrin.h> // NOLINT +#endif + +#if defined(__GNUC__) && defined(__i386__) +static inline uint64 __rdtsc(void) { + uint32_t a, d; + __asm__ volatile("rdtsc" : "=a" (a), "=d" (d)); + return (reinterpret_cast<uint64>(d) << 32) + a; +} + +static inline void _mm_clflush(volatile void *__p) { + asm volatile("clflush %0" : "+m" (*(volatile char *)__p)); +} +#endif + +static void FlushCache(uint8* dst, int count) { + while (count >= 32) { + _mm_clflush(dst); + dst += 32; + count -= 32; + } +} +#endif + +class YuvScalerTest : public testing::Test { + protected: + virtual void SetUp() { + dump_ = *FlagList::Lookup("yuvscaler_dump")->bool_variable(); + repeat_ = *FlagList::Lookup("yuvscaler_repeat")->int_variable(); + } + + // Scale an image and compare against a Lanczos-filtered test image. + // Lanczos is considered to be the "ideal" image resampling method, so we try + // to get as close to that as possible, while being as fast as possible. + bool TestScale(int iw, int ih, int ow, int oh, int offset, bool usefile, + bool optimize, int cpuflags, bool interpolate, + int memoffset, double* error) { + *error = 0.; + size_t isize = I420_SIZE(iw, ih); + size_t osize = I420_SIZE(ow, oh); + scoped_ptr<uint8[]> ibuffer(new uint8[isize + kAlignment + memoffset]()); + scoped_ptr<uint8[]> obuffer(new uint8[osize + kAlignment + memoffset]()); + scoped_ptr<uint8[]> xbuffer(new uint8[osize + kAlignment + memoffset]()); + + uint8 *ibuf = ALIGNP(ibuffer.get(), kAlignment) + memoffset; + uint8 *obuf = ALIGNP(obuffer.get(), kAlignment) + memoffset; + uint8 *xbuf = ALIGNP(xbuffer.get(), kAlignment) + memoffset; + + if (usefile) { + if (!LoadPlanarYuvTestImage("faces", iw, ih, ibuf) || + !LoadPlanarYuvTestImage("faces", ow, oh, xbuf)) { + LOG(LS_ERROR) << "Failed to load image"; + return false; + } + } else { + // These are used to test huge images. + memset(ibuf, 213, isize); // Input is constant color. + memset(obuf, 100, osize); // Output set to something wrong for now. + memset(xbuf, 213, osize); // Expected result. + } + +#ifdef TEST_UNCACHED + FlushCache(ibuf, isize); + FlushCache(obuf, osize); + FlushCache(xbuf, osize); +#endif + + // Scale down. + // If cpu true, disable cpu optimizations. Else allow auto detect + // TODO(fbarchard): set flags for libyuv + libyuv::MaskCpuFlags(cpuflags); +#ifdef TEST_RSTSC + uint64 t = 0; +#endif + for (int i = 0; i < repeat_; ++i) { +#ifdef TEST_UNCACHED + FlushCache(ibuf, isize); + FlushCache(obuf, osize); +#endif +#ifdef TEST_RSTSC + uint64 t1 = __rdtsc(); +#endif + EXPECT_EQ(0, libyuv::ScaleOffset(ibuf, iw, ih, obuf, ow, oh, + offset, interpolate)); +#ifdef TEST_RSTSC + uint64 t2 = __rdtsc(); + t += t2 - t1; +#endif + } + +#ifdef TEST_RSTSC + LOG(LS_INFO) << "Time: " << std::setw(9) << t; +#endif + + if (dump_) { + const testing::TestInfo* const test_info = + testing::UnitTest::GetInstance()->current_test_info(); + std::string test_name(test_info->name()); + DumpPlanarYuvTestImage(test_name, obuf, ow, oh); + } + + double sse = cricket::ComputeSumSquareError(obuf, xbuf, osize); + *error = sse / osize; // Mean Squared Error. + double PSNR = cricket::ComputePSNR(sse, osize); + LOG(LS_INFO) << "Image MSE: " << + std::setw(6) << std::setprecision(4) << *error << + " Image PSNR: " << PSNR; + return true; + } + + // Returns the index of the first differing byte. Easier to debug than memcmp. + static int FindDiff(const uint8* buf1, const uint8* buf2, int len) { + int i = 0; + while (i < len && buf1[i] == buf2[i]) { + i++; + } + return (i < len) ? i : -1; + } + + protected: + bool dump_; + int repeat_; +}; + +// Tests straight copy of data. +TEST_F(YuvScalerTest, TestCopy) { + const int iw = 640, ih = 360; + const int ow = 640, oh = 360; + ALIGN16(uint8 ibuf[I420_SIZE(iw, ih)]); + ALIGN16(uint8 obuf[I420_SIZE(ow, oh)]); + + // Load the frame, scale it, check it. + ASSERT_TRUE(LoadPlanarYuvTestImage("faces", iw, ih, ibuf)); + for (int i = 0; i < repeat_; ++i) { + libyuv::ScaleOffset(ibuf, iw, ih, obuf, ow, oh, 0, false); + } + if (dump_) DumpPlanarYuvTestImage("TestCopy", obuf, ow, oh); + EXPECT_EQ(-1, FindDiff(obuf, ibuf, sizeof(ibuf))); +} + +// Tests copy from 4:3 to 16:9. +TEST_F(YuvScalerTest, TestOffset16_10Copy) { + const int iw = 640, ih = 360; + const int ow = 640, oh = 480; + const int offset = (480 - 360) / 2; + scoped_ptr<uint8[]> ibuffer(new uint8[I420_SIZE(iw, ih) + kAlignment]); + scoped_ptr<uint8[]> obuffer(new uint8[I420_SIZE(ow, oh) + kAlignment]); + + uint8 *ibuf = ALIGNP(ibuffer.get(), kAlignment); + uint8 *obuf = ALIGNP(obuffer.get(), kAlignment); + + // Load the frame, scale it, check it. + ASSERT_TRUE(LoadPlanarYuvTestImage("faces", iw, ih, ibuf)); + + // Clear to black, which is Y = 0 and U and V = 128 + memset(obuf, 0, ow * oh); + memset(obuf + ow * oh, 128, ow * oh / 2); + for (int i = 0; i < repeat_; ++i) { + libyuv::ScaleOffset(ibuf, iw, ih, obuf, ow, oh, offset, false); + } + if (dump_) DumpPlanarYuvTestImage("TestOffsetCopy16_9", obuf, ow, oh); + EXPECT_EQ(-1, FindDiff(obuf + ow * offset, + ibuf, + iw * ih)); + EXPECT_EQ(-1, FindDiff(obuf + ow * oh + ow * offset / 4, + ibuf + iw * ih, + iw * ih / 4)); + EXPECT_EQ(-1, FindDiff(obuf + ow * oh * 5 / 4 + ow * offset / 4, + ibuf + iw * ih * 5 / 4, + iw * ih / 4)); +} + +// The following are 'cpu' flag values: +// Allow all SIMD optimizations +#define ALLFLAGS -1 +// Disable SSSE3 but allow other forms of SIMD (SSE2) +#define NOSSSE3 ~libyuv::kCpuHasSSSE3 +// Disable SSE2 and SSSE3 +#define NOSSE ~libyuv::kCpuHasSSE2 & ~libyuv::kCpuHasSSSE3 + +// TEST_M scale factor with variations of opt, align, int +#define TEST_M(name, iwidth, iheight, owidth, oheight, mse) \ +TEST_F(YuvScalerTest, name##Ref) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, false, ALLFLAGS, false, 0, &error)); \ + EXPECT_LE(error, mse); \ +} \ +TEST_F(YuvScalerTest, name##OptAligned) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, true, ALLFLAGS, false, 0, &error)); \ + EXPECT_LE(error, mse); \ +} \ +TEST_F(YuvScalerTest, name##OptUnaligned) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, true, ALLFLAGS, false, 1, &error)); \ + EXPECT_LE(error, mse); \ +} \ +TEST_F(YuvScalerTest, name##OptSSE2) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, true, NOSSSE3, false, 0, &error)); \ + EXPECT_LE(error, mse); \ +} \ +TEST_F(YuvScalerTest, name##OptC) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, true, NOSSE, false, 0, &error)); \ + EXPECT_LE(error, mse); \ +} \ +TEST_F(YuvScalerTest, name##IntRef) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, false, ALLFLAGS, true, 0, &error)); \ + EXPECT_LE(error, mse); \ +} \ +TEST_F(YuvScalerTest, name##IntOptAligned) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, true, ALLFLAGS, true, 0, &error)); \ + EXPECT_LE(error, mse); \ +} \ +TEST_F(YuvScalerTest, name##IntOptUnaligned) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, true, ALLFLAGS, true, 1, &error)); \ + EXPECT_LE(error, mse); \ +} \ +TEST_F(YuvScalerTest, name##IntOptSSE2) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, true, NOSSSE3, true, 0, &error)); \ + EXPECT_LE(error, mse); \ +} \ +TEST_F(YuvScalerTest, name##IntOptC) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, true, true, NOSSE, true, 0, &error)); \ + EXPECT_LE(error, mse); \ +} + +#define TEST_H(name, iwidth, iheight, owidth, oheight, opt, cpu, intr, mse) \ +TEST_F(YuvScalerTest, name) { \ + double error; \ + EXPECT_TRUE(TestScale(iwidth, iheight, owidth, oheight, \ + 0, false, opt, cpu, intr, 0, &error)); \ + EXPECT_LE(error, mse); \ +} + +// Test 4x3 aspect ratio scaling + +// Tests 1/1x scale down. +TEST_M(TestScale4by3Down11, 640, 480, 640, 480, 0) + +// Tests 3/4x scale down. +TEST_M(TestScale4by3Down34, 640, 480, 480, 360, 60) + +// Tests 1/2x scale down. +TEST_M(TestScale4by3Down12, 640, 480, 320, 240, 60) + +// Tests 3/8x scale down. +TEST_M(TestScale4by3Down38, 640, 480, 240, 180, 60) + +// Tests 1/4x scale down.. +TEST_M(TestScale4by3Down14, 640, 480, 160, 120, 60) + +// Tests 3/16x scale down. +TEST_M(TestScale4by3Down316, 640, 480, 120, 90, 120) + +// Tests 1/8x scale down. +TEST_M(TestScale4by3Down18, 640, 480, 80, 60, 150) + +// Tests 2/3x scale down. +TEST_M(TestScale4by3Down23, 480, 360, 320, 240, 60) + +// Tests 4/3x scale up. +TEST_M(TestScale4by3Up43, 480, 360, 640, 480, 60) + +// Tests 2/1x scale up. +TEST_M(TestScale4by3Up21, 320, 240, 640, 480, 60) + +// Tests 4/1x scale up. +TEST_M(TestScale4by3Up41, 160, 120, 640, 480, 80) + +// Test 16x10 aspect ratio scaling + +// Tests 1/1x scale down. +TEST_M(TestScale16by10Down11, 640, 400, 640, 400, 0) + +// Tests 3/4x scale down. +TEST_M(TestScale16by10Down34, 640, 400, 480, 300, 60) + +// Tests 1/2x scale down. +TEST_M(TestScale16by10Down12, 640, 400, 320, 200, 60) + +// Tests 3/8x scale down. +TEST_M(TestScale16by10Down38, 640, 400, 240, 150, 60) + +// Tests 1/4x scale down.. +TEST_M(TestScale16by10Down14, 640, 400, 160, 100, 60) + +// Tests 3/16x scale down. +TEST_M(TestScale16by10Down316, 640, 400, 120, 75, 120) + +// Tests 1/8x scale down. +TEST_M(TestScale16by10Down18, 640, 400, 80, 50, 150) + +// Tests 2/3x scale down. +TEST_M(TestScale16by10Down23, 480, 300, 320, 200, 60) + +// Tests 4/3x scale up. +TEST_M(TestScale16by10Up43, 480, 300, 640, 400, 60) + +// Tests 2/1x scale up. +TEST_M(TestScale16by10Up21, 320, 200, 640, 400, 60) + +// Tests 4/1x scale up. +TEST_M(TestScale16by10Up41, 160, 100, 640, 400, 80) + +// Test 16x9 aspect ratio scaling + +// Tests 1/1x scale down. +TEST_M(TestScaleDown11, 640, 360, 640, 360, 0) + +// Tests 3/4x scale down. +TEST_M(TestScaleDown34, 640, 360, 480, 270, 60) + +// Tests 1/2x scale down. +TEST_M(TestScaleDown12, 640, 360, 320, 180, 60) + +// Tests 3/8x scale down. +TEST_M(TestScaleDown38, 640, 360, 240, 135, 60) + +// Tests 1/4x scale down.. +TEST_M(TestScaleDown14, 640, 360, 160, 90, 60) + +// Tests 3/16x scale down. +TEST_M(TestScaleDown316, 640, 360, 120, 68, 120) + +// Tests 1/8x scale down. +TEST_M(TestScaleDown18, 640, 360, 80, 45, 150) + +// Tests 2/3x scale down. +TEST_M(TestScaleDown23, 480, 270, 320, 180, 60) + +// Tests 4/3x scale up. +TEST_M(TestScaleUp43, 480, 270, 640, 360, 60) + +// Tests 2/1x scale up. +TEST_M(TestScaleUp21, 320, 180, 640, 360, 60) + +// Tests 4/1x scale up. +TEST_M(TestScaleUp41, 160, 90, 640, 360, 80) + +// Test HD 4x3 aspect ratio scaling + +// Tests 1/1x scale down. +TEST_M(TestScaleHD4x3Down11, 1280, 960, 1280, 960, 0) + +// Tests 3/4x scale down. +TEST_M(TestScaleHD4x3Down34, 1280, 960, 960, 720, 60) + +// Tests 1/2x scale down. +TEST_M(TestScaleHD4x3Down12, 1280, 960, 640, 480, 60) + +// Tests 3/8x scale down. +TEST_M(TestScaleHD4x3Down38, 1280, 960, 480, 360, 60) + +// Tests 1/4x scale down.. +TEST_M(TestScaleHD4x3Down14, 1280, 960, 320, 240, 60) + +// Tests 3/16x scale down. +TEST_M(TestScaleHD4x3Down316, 1280, 960, 240, 180, 120) + +// Tests 1/8x scale down. +TEST_M(TestScaleHD4x3Down18, 1280, 960, 160, 120, 150) + +// Tests 2/3x scale down. +TEST_M(TestScaleHD4x3Down23, 960, 720, 640, 480, 60) + +// Tests 4/3x scale up. +TEST_M(TestScaleHD4x3Up43, 960, 720, 1280, 960, 60) + +// Tests 2/1x scale up. +TEST_M(TestScaleHD4x3Up21, 640, 480, 1280, 960, 60) + +// Tests 4/1x scale up. +TEST_M(TestScaleHD4x3Up41, 320, 240, 1280, 960, 80) + +// Test HD 16x10 aspect ratio scaling + +// Tests 1/1x scale down. +TEST_M(TestScaleHD16x10Down11, 1280, 800, 1280, 800, 0) + +// Tests 3/4x scale down. +TEST_M(TestScaleHD16x10Down34, 1280, 800, 960, 600, 60) + +// Tests 1/2x scale down. +TEST_M(TestScaleHD16x10Down12, 1280, 800, 640, 400, 60) + +// Tests 3/8x scale down. +TEST_M(TestScaleHD16x10Down38, 1280, 800, 480, 300, 60) + +// Tests 1/4x scale down.. +TEST_M(TestScaleHD16x10Down14, 1280, 800, 320, 200, 60) + +// Tests 3/16x scale down. +TEST_M(TestScaleHD16x10Down316, 1280, 800, 240, 150, 120) + +// Tests 1/8x scale down. +TEST_M(TestScaleHD16x10Down18, 1280, 800, 160, 100, 150) + +// Tests 2/3x scale down. +TEST_M(TestScaleHD16x10Down23, 960, 600, 640, 400, 60) + +// Tests 4/3x scale up. +TEST_M(TestScaleHD16x10Up43, 960, 600, 1280, 800, 60) + +// Tests 2/1x scale up. +TEST_M(TestScaleHD16x10Up21, 640, 400, 1280, 800, 60) + +// Tests 4/1x scale up. +TEST_M(TestScaleHD16x10Up41, 320, 200, 1280, 800, 80) + +// Test HD 16x9 aspect ratio scaling + +// Tests 1/1x scale down. +TEST_M(TestScaleHDDown11, 1280, 720, 1280, 720, 0) + +// Tests 3/4x scale down. +TEST_M(TestScaleHDDown34, 1280, 720, 960, 540, 60) + +// Tests 1/2x scale down. +TEST_M(TestScaleHDDown12, 1280, 720, 640, 360, 60) + +// Tests 3/8x scale down. +TEST_M(TestScaleHDDown38, 1280, 720, 480, 270, 60) + +// Tests 1/4x scale down.. +TEST_M(TestScaleHDDown14, 1280, 720, 320, 180, 60) + +// Tests 3/16x scale down. +TEST_M(TestScaleHDDown316, 1280, 720, 240, 135, 120) + +// Tests 1/8x scale down. +TEST_M(TestScaleHDDown18, 1280, 720, 160, 90, 150) + +// Tests 2/3x scale down. +TEST_M(TestScaleHDDown23, 960, 540, 640, 360, 60) + +// Tests 4/3x scale up. +TEST_M(TestScaleHDUp43, 960, 540, 1280, 720, 60) + +// Tests 2/1x scale up. +TEST_M(TestScaleHDUp21, 640, 360, 1280, 720, 60) + +// Tests 4/1x scale up. +TEST_M(TestScaleHDUp41, 320, 180, 1280, 720, 80) + +// Tests 1366x768 resolution for comparison to chromium scaler_bench +TEST_M(TestScaleHDUp1366, 1280, 720, 1366, 768, 10) + +// Tests odd source/dest sizes. 3 less to make chroma odd as well. +TEST_M(TestScaleHDUp1363, 1277, 717, 1363, 765, 10) + +// Tests 1/2x scale down, using optimized algorithm. +TEST_M(TestScaleOddDown12, 180, 100, 90, 50, 50) + +// Tests bilinear scale down +TEST_M(TestScaleOddDownBilin, 160, 100, 90, 50, 120) + +// Test huge buffer scales that are expected to use a different code path +// that avoids stack overflow but still work using point sampling. +// Max output size is 640 wide. + +// Tests interpolated 1/8x scale down, using optimized algorithm. +TEST_H(TestScaleDown18HDOptInt, 6144, 48, 768, 6, true, ALLFLAGS, true, 1) + +// Tests interpolated 1/8x scale down, using c_only optimized algorithm. +TEST_H(TestScaleDown18HDCOnlyOptInt, 6144, 48, 768, 6, true, NOSSE, true, 1) + +// Tests interpolated 3/8x scale down, using optimized algorithm. +TEST_H(TestScaleDown38HDOptInt, 2048, 16, 768, 6, true, ALLFLAGS, true, 1) + +// Tests interpolated 3/8x scale down, using no SSSE3 optimized algorithm. +TEST_H(TestScaleDown38HDNoSSSE3OptInt, 2048, 16, 768, 6, true, NOSSSE3, true, 1) + +// Tests interpolated 3/8x scale down, using c_only optimized algorithm. +TEST_H(TestScaleDown38HDCOnlyOptInt, 2048, 16, 768, 6, true, NOSSE, true, 1) + +// Tests interpolated 3/16x scale down, using optimized algorithm. +TEST_H(TestScaleDown316HDOptInt, 4096, 32, 768, 6, true, ALLFLAGS, true, 1) + +// Tests interpolated 3/16x scale down, using no SSSE3 optimized algorithm. +TEST_H(TestScaleDown316HDNoSSSE3OptInt, 4096, 32, 768, 6, true, NOSSSE3, true, + 1) + +// Tests interpolated 3/16x scale down, using c_only optimized algorithm. +TEST_H(TestScaleDown316HDCOnlyOptInt, 4096, 32, 768, 6, true, NOSSE, true, 1) + +// Test special sizes dont crash +// Tests scaling down to 1 pixel width +TEST_H(TestScaleDown1x6OptInt, 3, 24, 1, 6, true, ALLFLAGS, true, 4) + +// Tests scaling down to 1 pixel height +TEST_H(TestScaleDown6x1OptInt, 24, 3, 6, 1, true, ALLFLAGS, true, 4) + +// Tests scaling up from 1 pixel width +TEST_H(TestScaleUp1x6OptInt, 1, 6, 3, 24, true, ALLFLAGS, true, 4) + +// Tests scaling up from 1 pixel height +TEST_H(TestScaleUp6x1OptInt, 6, 1, 24, 3, true, ALLFLAGS, true, 4) + +// Test performance of a range of box filter scale sizes + +// Tests interpolated 1/2x scale down, using optimized algorithm. +TEST_H(TestScaleDown2xHDOptInt, 1280, 720, 1280 / 2, 720 / 2, true, ALLFLAGS, + true, 1) + +// Tests interpolated 1/3x scale down, using optimized algorithm. +TEST_H(TestScaleDown3xHDOptInt, 1280, 720, 1280 / 3, 720 / 3, true, ALLFLAGS, + true, 1) + +// Tests interpolated 1/4x scale down, using optimized algorithm. +TEST_H(TestScaleDown4xHDOptInt, 1280, 720, 1280 / 4, 720 / 4, true, ALLFLAGS, + true, 1) + +// Tests interpolated 1/5x scale down, using optimized algorithm. +TEST_H(TestScaleDown5xHDOptInt, 1280, 720, 1280 / 5, 720 / 5, true, ALLFLAGS, + true, 1) + +// Tests interpolated 1/6x scale down, using optimized algorithm. +TEST_H(TestScaleDown6xHDOptInt, 1280, 720, 1280 / 6, 720 / 6, true, ALLFLAGS, + true, 1) + +// Tests interpolated 1/7x scale down, using optimized algorithm. +TEST_H(TestScaleDown7xHDOptInt, 1280, 720, 1280 / 7, 720 / 7, true, ALLFLAGS, + true, 1) + +// Tests interpolated 1/8x scale down, using optimized algorithm. +TEST_H(TestScaleDown8xHDOptInt, 1280, 720, 1280 / 8, 720 / 8, true, ALLFLAGS, + true, 1) + +// Tests interpolated 1/8x scale down, using optimized algorithm. +TEST_H(TestScaleDown9xHDOptInt, 1280, 720, 1280 / 9, 720 / 9, true, ALLFLAGS, + true, 1) + +// Tests interpolated 1/8x scale down, using optimized algorithm. +TEST_H(TestScaleDown10xHDOptInt, 1280, 720, 1280 / 10, 720 / 10, true, ALLFLAGS, + true, 1) diff --git a/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc b/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc index ee88797c1a1..d95dc857d56 100644 --- a/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc +++ b/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc @@ -504,7 +504,8 @@ IPseudoTcpNotify::WriteResult PseudoTcpChannel::TcpWritePacket( ASSERT(cs_.CurrentThreadIsOwner()); ASSERT(tcp == tcp_); ASSERT(NULL != channel_); - int sent = channel_->SendPacket(buffer, len, talk_base::DSCP_NO_CHANGE); + talk_base::PacketOptions packet_options; + int sent = channel_->SendPacket(buffer, len, packet_options); if (sent > 0) { //LOG_F(LS_VERBOSE) << "(" << sent << ") Sent"; return IPseudoTcpNotify::WR_SUCCESS; diff --git a/chromium/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.cc b/chromium/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.cc index 9287d22ab5c..55f408387ba 100644 --- a/chromium/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.cc +++ b/chromium/third_party/libjingle/source/talk/session/tunnel/securetunnelsessionclient.cc @@ -360,8 +360,8 @@ void SecureTunnelSession::OnAccept() { const std::string& cert_pem = role_ == INITIATOR ? remote_tunnel->server_pem_certificate : remote_tunnel->client_pem_certificate; - talk_base::SSLCertificate* peer_cert = - ParseCertificate(cert_pem); + talk_base::scoped_ptr<talk_base::SSLCertificate> peer_cert( + ParseCertificate(cert_pem)); if (peer_cert == NULL) { ASSERT(role_ == INITIATOR); // when RESPONDER we validated it earlier LOG(LS_ERROR) @@ -373,7 +373,17 @@ void SecureTunnelSession::OnAccept() { talk_base::SSLStreamAdapter* ssl_stream = static_cast<talk_base::SSLStreamAdapter*>( ssl_stream_reference_->GetStream()); - ssl_stream->SetPeerCertificate(peer_cert); // pass ownership of certificate. + + std::string algorithm; + if (!peer_cert->GetSignatureDigestAlgorithm(&algorithm)) { + LOG(LS_ERROR) << "Failed to get the algorithm for the peer cert signature"; + return; + } + unsigned char digest[talk_base::MessageDigest::kMaxSize]; + size_t digest_len; + peer_cert->ComputeDigest(algorithm, digest, ARRAY_SIZE(digest), &digest_len); + ssl_stream->SetPeerCertificateDigest(algorithm, digest, digest_len); + // We no longer need our handle to the ssl stream. ssl_stream_reference_.reset(); LOG(LS_INFO) << "Connecting tunnel"; diff --git a/chromium/third_party/libjingle/source/talk/site_scons/site_tools/talk_linux.py b/chromium/third_party/libjingle/source/talk/site_scons/site_tools/talk_linux.py deleted file mode 100644 index acba92120f0..00000000000 --- a/chromium/third_party/libjingle/source/talk/site_scons/site_tools/talk_linux.py +++ /dev/null @@ -1,313 +0,0 @@ -# Copyright 2010 Google Inc. -# All Rights Reserved. -# Author: tschmelcher@google.com (Tristan Schmelcher) - -"""Tool for helpers used in linux building process.""" - -import os -import SCons.Defaults -import subprocess - - -def _OutputFromShellCommand(command): - process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) - return process.communicate()[0].strip() - - -# This is a pure SCons helper function. -def _InternalBuildDebianPackage(env, debian_files, package_files, - output_dir=None, force_version=None): - """Creates build rules to build a Debian package from the specified sources. - - Args: - env: SCons Environment. - debian_files: Array of the Debian control file sources that should be - copied into the package source tree, e.g., changelog, control, rules, - etc. - package_files: An array of 2-tuples listing the files that should be - copied into the package source tree. - The first element is the path where the file should be placed for the - .install control file to find it, relative to the generated debian - package source directory. - The second element is the file source. - output_dir: An optional directory to place the files in. If omitted, the - current output directory is used. - force_version: Optional. Forces the version of the package to start with - this version string if specified. If the last entry in the changelog - is not for a version that starts with this then a dummy entry is - generated with this version and a ~prerelease suffix (so that the - final version will compare as greater). - - Return: - A list of the targets (if any). - """ - if 0 != subprocess.call(['which', 'dpkg-buildpackage']): - print ('dpkg-buildpackage not installed on this system; ' - 'skipping DEB build stage') - return [] - # Read the control file and changelog file to determine the package name, - # version, and arch that the Debian build tools will use to name the - # generated files. - control_file = None - changelog_file = None - for file_name in debian_files: - if os.path.basename(file_name) == 'control': - control_file = env.File(file_name).srcnode().abspath - elif os.path.basename(file_name) == 'changelog': - changelog_file = env.File(file_name).srcnode().abspath - if not control_file: - raise Exception('Need to have a control file') - if not changelog_file: - raise Exception('Need to have a changelog file') - source = _OutputFromShellCommand( - "awk '/^Source:/ { print $2; }' " + control_file) - packages = _OutputFromShellCommand( - "awk '/^Package:/ { print $2; }' " + control_file).split('\n') - version = _OutputFromShellCommand( - "sed -nr '1 { s/.*\\((.*)\\).*/\\1/; p }' " + changelog_file) - arch = _OutputFromShellCommand('dpkg --print-architecture') - add_dummy_changelog_entry = False - if force_version and not version.startswith(force_version): - print ('Warning: no entry in ' + changelog_file + ' for version ' + - force_version + ' (last is ' + version +'). A dummy entry will be ' + - 'generated. Remember to add the real changelog entry before ' + - 'releasing.') - version = force_version + '~prerelease' - add_dummy_changelog_entry = True - source_dir_name = source + '_' + version + '_' + arch - target_file_names = [ source_dir_name + '.changes' ] - for package in packages: - package_file_name = package + '_' + version + '_' + arch + '.deb' - target_file_names.append(package_file_name) - # The targets - if output_dir: - targets = [os.path.join(output_dir, s) for s in target_file_names] - else: - targets = target_file_names - # Path to where we will construct the debian build tree. - deb_build_tree = os.path.join(source_dir_name, 'deb_build_tree') - # First copy the files. - for file_name in package_files: - env.Command(os.path.join(deb_build_tree, file_name[0]), file_name[1], - SCons.Defaults.Copy('$TARGET', '$SOURCE')) - env.Depends(targets, os.path.join(deb_build_tree, file_name[0])) - # Now copy the Debian metadata sources. We have to do this all at once so - # that we can remove the target directory before copying, because there - # can't be any other stale files there or else dpkg-buildpackage may use - # them and give incorrect build output. - copied_debian_files_paths = [] - for file_name in debian_files: - copied_debian_files_paths.append(os.path.join(deb_build_tree, 'debian', - os.path.basename(file_name))) - copy_commands = [ - """dir=$$(dirname $TARGET) && \ - rm -Rf $$dir && \ - mkdir -p $$dir && \ - cp $SOURCES $$dir && \ - chmod -R u+w $$dir""" - ] - if add_dummy_changelog_entry: - copy_commands += [ - """debchange -c $$(dirname $TARGET)/changelog --newversion %s \ - --distribution UNRELEASED \ - 'Developer preview build. (This entry was auto-generated.)'""" % - version - ] - env.Command(copied_debian_files_paths, debian_files, copy_commands) - env.Depends(targets, copied_debian_files_paths) - # Must explicitly specify -a because otherwise cross-builds won't work. - # Must explicitly specify -D because -a disables it. - # Must explicitly specify fakeroot because old dpkg tools don't assume that. - env.Command(targets, None, - """dir=%(dir)s && \ - cd $$dir && \ - dpkg-buildpackage -b -uc -a%(arch)s -D -rfakeroot && \ - cd $$OLDPWD && \ - for file in %(targets)s; do \ - mv $$dir/../$$file $$(dirname $TARGET) || exit 1; \ - done""" % - {'dir':env.Dir(deb_build_tree).path, - 'arch':arch, - 'targets':' '.join(target_file_names)}) - return targets - - -def BuildDebianPackage(env, debian_files, package_files, force_version=None): - """Creates build rules to build a Debian package from the specified sources. - - This is a Hammer-ified version of _InternalBuildDebianPackage that knows to - put the packages in the Hammer staging dir. - - Args: - env: SCons Environment. - debian_files: Array of the Debian control file sources that should be - copied into the package source tree, e.g., changelog, control, rules, - etc. - package_files: An array of 2-tuples listing the files that should be - copied into the package source tree. - The first element is the path where the file should be placed for the - .install control file to find it, relative to the generated debian - package source directory. - The second element is the file source. - force_version: Optional. Forces the version of the package to start with - this version string if specified. If the last entry in the changelog - is not for a version that starts with this then a dummy entry is - generated with this version and a ~prerelease suffix (so that the - final version will compare as greater). - - Return: - A list of the targets (if any). - """ - if not env.Bit('host_linux'): - return [] - return _InternalBuildDebianPackage(env, debian_files, package_files, - output_dir='$STAGING_DIR', force_version=force_version) - - -def _GetPkgConfigCommand(): - """Return the pkg-config command line to use. - - Returns: - A string specifying the pkg-config command line to use. - """ - return os.environ.get('PKG_CONFIG') or 'pkg-config' - - -def _EscapePosixShellArgument(arg): - """Escapes a shell command line argument so that it is interpreted literally. - - Args: - arg: The shell argument to escape. - - Returns: - The escaped string. - """ - return "'%s'" % arg.replace("'", "'\\''") - - -def _HavePackage(package): - """Whether the given pkg-config package name is present on the build system. - - Args: - package: The name of the package. - - Returns: - True if the package is present, else False - """ - return subprocess.call('%s --exists %s' % ( - _GetPkgConfigCommand(), - _EscapePosixShellArgument(package)), shell=True) == 0 - - -def _GetPackageFlags(flag_type, packages): - """Get the flags needed to compile/link against the given package(s). - - Returns the flags that are needed to compile/link against the given pkg-config - package(s). - - Args: - flag_type: The option to pkg-config specifying the type of flags to get. - packages: The list of package names as strings. - - Returns: - The flags of the requested type. - - Raises: - subprocess.CalledProcessError: The pkg-config command failed. - """ - pkg_config = _GetPkgConfigCommand() - command = ' '.join([pkg_config] + - [_EscapePosixShellArgument(arg) for arg in - [flag_type] + packages]) - process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) - output = process.communicate()[0] - if process.returncode != 0: - raise subprocess.CalledProcessError(process.returncode, pkg_config) - return output.strip().split(' ') - - -def GetPackageParams(env, packages): - """Get the params needed to compile/link against the given package(s). - - Returns the params that are needed to compile/link against the given - pkg-config package(s). - - Args: - env: The current SCons environment. - packages: The name of the package, or a list of names. - - Returns: - A dictionary containing the params. - - Raises: - Exception: One or more of the packages is not installed. - """ - if not env.Bit('host_linux'): - return {} - if not SCons.Util.is_List(packages): - packages = [packages] - for package in packages: - if not _HavePackage(package): - raise Exception(('Required package \"%s\" was not found. Please install ' - 'the package that provides the \"%s.pc\" file.') % - (package, package)) - package_ccflags = _GetPackageFlags('--cflags', packages) - package_libs = _GetPackageFlags('--libs', packages) - # Split package_libs into libs, libdirs, and misc. linker flags. (In a perfect - # world we could just leave libdirs in link_flags, but some linkers are - # somehow confused by the different argument order.) - libs = [flag[2:] for flag in package_libs if flag[0:2] == '-l'] - libdirs = [flag[2:] for flag in package_libs if flag[0:2] == '-L'] - link_flags = [flag for flag in package_libs if flag[0:2] not in ['-l', '-L']] - return { - 'ccflags': package_ccflags, - 'libs': libs, - 'libdirs': libdirs, - 'link_flags': link_flags, - 'dependent_target_settings' : { - 'libs': libs[:], - 'libdirs': libdirs[:], - 'link_flags': link_flags[:], - }, - } - - -def EnableFeatureWherePackagePresent(env, bit, cpp_flag, package): - """Enable a feature if a required pkg-config package is present. - - Args: - env: The current SCons environment. - bit: The name of the Bit to enable when the package is present. - cpp_flag: The CPP flag to enable when the package is present. - package: The name of the package. - """ - if not env.Bit('host_linux'): - return - if _HavePackage(package): - env.SetBits(bit) - env.Append(CPPDEFINES=[cpp_flag]) - else: - print ('Warning: Package \"%s\" not found. Feature \"%s\" will not be ' - 'built. To build with this feature, install the package that ' - 'provides the \"%s.pc\" file.') % (package, bit, package) - -def GetGccVersion(env): - if env.Bit('cross_compile'): - gcc_command = env['CXX'] - else: - gcc_command = 'gcc' - version_string = _OutputFromShellCommand( - '%s --version | head -n 1 |' - r'sed "s/.*\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/g"' % gcc_command) - return tuple([int(x or '0') for x in version_string.split('.')]) - -def generate(env): - if env.Bit('linux'): - env.AddMethod(EnableFeatureWherePackagePresent) - env.AddMethod(GetPackageParams) - env.AddMethod(BuildDebianPackage) - env.AddMethod(GetGccVersion) - - -def exists(env): - return 1 # Required by scons diff --git a/chromium/third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.py b/chromium/third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.py deleted file mode 100644 index b184ddc542d..00000000000 --- a/chromium/third_party/libjingle/source/talk/site_scons/site_tools/talk_noops.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2010 Google Inc. -# All Rights Reserved. -# Author: thaloun@google.com (Tim Haloun) - -"""Noop tool that defines builder functions for non-default platforms to - avoid errors when scanning sconsscripts.""" - -import SCons.Builder - - -def generate(env): - """SCons method.""" - if not env.Bit('windows'): - builder = SCons.Builder.Builder( - action='' - ) - env.Append(BUILDERS={'RES': builder, 'Grit': builder}) - -def exists(dummy): - return 1 diff --git a/chromium/third_party/libjingle/source/talk/site_scons/talk.py b/chromium/third_party/libjingle/source/talk/site_scons/talk.py deleted file mode 100644 index 745f1d813f8..00000000000 --- a/chromium/third_party/libjingle/source/talk/site_scons/talk.py +++ /dev/null @@ -1,635 +0,0 @@ -# Copyright 2010 Google Inc. -# All Rights Reserved. -# -# Author: Tim Haloun (thaloun@google.com) -# Daniel Petersson (dape@google.com) -# -import os -import SCons.Util - -class LibraryInfo: - """Records information on the libraries defined in a build configuration. - - Attributes: - lib_targets: Dictionary of library target params for lookups in - ExtendComponent(). - prebuilt_libraries: Set of all prebuilt static libraries. - system_libraries: Set of libraries not found in the above (used to detect - out-of-order build rules). - """ - - # Dictionary of LibraryInfo objects keyed by BUILD_TYPE value. - __library_info = {} - - @staticmethod - def get(env): - """Gets the LibraryInfo object for the current build type. - - Args: - env: The environment object. - - Returns: - The LibraryInfo object. - """ - return LibraryInfo.__library_info.setdefault(env['BUILD_TYPE'], - LibraryInfo()) - - def __init__(self): - self.lib_targets = {} - self.prebuilt_libraries = set() - self.system_libraries = set() - - -def _GetLibParams(env, lib): - """Gets the params for the given library if it is a library target. - - Returns the params that were specified when the given lib target name was - created, or None if no such lib target has been defined. In the None case, it - additionally records the negative result so as to detect out-of-order - dependencies for future targets. - - Args: - env: The environment object. - lib: The library's name as a string. - - Returns: - Its dictionary of params, or None. - """ - info = LibraryInfo.get(env) - if lib in info.lib_targets: - return info.lib_targets[lib] - else: - if lib not in info.prebuilt_libraries and lib not in info.system_libraries: - info.system_libraries.add(lib) - return None - - -def _RecordLibParams(env, lib, params): - """Record the params used for a library target. - - Record the params used for a library target while checking for several error - conditions. - - Args: - env: The environment object. - lib: The library target's name as a string. - params: Its dictionary of params. - - Raises: - Exception: The lib target has already been recorded, or the lib was - previously declared to be prebuilt, or the lib target is being defined - after a reverse library dependency. - """ - info = LibraryInfo.get(env) - if lib in info.lib_targets: - raise Exception('Multiple definitions of ' + lib) - if lib in info.prebuilt_libraries: - raise Exception(lib + ' already declared as a prebuilt library') - if lib in info.system_libraries: - raise Exception(lib + ' cannot be defined after its reverse library ' - 'dependencies') - info.lib_targets[lib] = params - - -def _IsPrebuiltLibrary(env, lib): - """Checks whether or not the given library is a prebuilt static library. - - Returns whether or not the given library name has been declared to be a - prebuilt static library. In the False case, it additionally records the - negative result so as to detect out-of-order dependencies for future targets. - - Args: - env: The environment object. - lib: The library's name as a string. - - Returns: - True or False - """ - info = LibraryInfo.get(env) - if lib in info.prebuilt_libraries: - return True - else: - if lib not in info.lib_targets and lib not in info.system_libraries: - info.system_libraries.add(lib) - return False - - -def _RecordPrebuiltLibrary(env, lib): - """Record that a library is a prebuilt static library. - - Record that the given library name refers to a prebuilt static library while - checking for several error conditions. - - Args: - env: The environment object. - lib: The library's name as a string. - - Raises: - Exception: The lib has already been recorded to be prebuilt, or the lib was - previously declared as a target, or the lib is being declared as - prebuilt after a reverse library dependency. - """ - info = LibraryInfo.get(env) - if lib in info.prebuilt_libraries: - raise Exception('Multiple prebuilt declarations of ' + lib) - if lib in info.lib_targets: - raise Exception(lib + ' already defined as a target') - if lib in info.system_libraries: - raise Exception(lib + ' cannot be declared as prebuilt after its reverse ' - 'library dependencies') - info.prebuilt_libraries.add(lib) - - -def _GenericLibrary(env, static, **kwargs): - """Extends ComponentLibrary to support multiplatform builds - of dynamic or static libraries. - - Args: - env: The environment object. - kwargs: The keyword arguments. - - Returns: - See swtoolkit ComponentLibrary - """ - params = CombineDicts(kwargs, {'COMPONENT_STATIC': static}) - return ExtendComponent(env, 'ComponentLibrary', **params) - - -def DeclarePrebuiltLibraries(env, libraries): - """Informs the build engine about external static libraries. - - Informs the build engine that the given external library name(s) are prebuilt - static libraries, as opposed to shared libraries. - - Args: - env: The environment object. - libraries: The library or libraries that are being declared as prebuilt - static libraries. - """ - if not SCons.Util.is_List(libraries): - libraries = [libraries] - for library in libraries: - _RecordPrebuiltLibrary(env, library) - - -def Library(env, **kwargs): - """Extends ComponentLibrary to support multiplatform builds of static - libraries. - - Args: - env: The current environment. - kwargs: The keyword arguments. - - Returns: - See swtoolkit ComponentLibrary - """ - return _GenericLibrary(env, True, **kwargs) - - -def DynamicLibrary(env, **kwargs): - """Extends ComponentLibrary to support multiplatform builds - of dynmic libraries. - - Args: - env: The environment object. - kwargs: The keyword arguments. - - Returns: - See swtoolkit ComponentLibrary - """ - return _GenericLibrary(env, False, **kwargs) - - -def Object(env, **kwargs): - return ExtendComponent(env, 'ComponentObject', **kwargs) - - -def Unittest(env, **kwargs): - """Extends ComponentTestProgram to support unittest built - for multiple platforms. - - Args: - env: The current environment. - kwargs: The keyword arguments. - - Returns: - See swtoolkit ComponentProgram. - """ - kwargs['name'] = kwargs['name'] + '_unittest' - - common_test_params = { - 'posix_cppdefines': ['GUNIT_NO_GOOGLE3', 'GTEST_HAS_RTTI=0'], - 'libs': ['unittest_main', 'gunit'] - } - if 'explicit_libs' not in kwargs: - common_test_params['win_libs'] = [ - 'advapi32', - 'crypt32', - 'iphlpapi', - 'secur32', - 'shell32', - 'shlwapi', - 'user32', - 'wininet', - 'ws2_32' - ] - common_test_params['lin_libs'] = [ - 'crypto', - 'pthread', - 'ssl', - ] - - params = CombineDicts(kwargs, common_test_params) - return ExtendComponent(env, 'ComponentTestProgram', **params) - - -def App(env, **kwargs): - """Extends ComponentProgram to support executables with platform specific - options. - - Args: - env: The current environment. - kwargs: The keyword arguments. - - Returns: - See swtoolkit ComponentProgram. - """ - if 'explicit_libs' not in kwargs: - common_app_params = { - 'win_libs': [ - 'advapi32', - 'crypt32', - 'iphlpapi', - 'secur32', - 'shell32', - 'shlwapi', - 'user32', - 'wininet', - 'ws2_32' - ]} - params = CombineDicts(kwargs, common_app_params) - else: - params = kwargs - return ExtendComponent(env, 'ComponentProgram', **params) - -def WiX(env, **kwargs): - """ Extends the WiX builder - Args: - env: The current environment. - kwargs: The keyword arguments. - - Returns: - The node produced by the environment's wix builder - """ - return ExtendComponent(env, 'WiX', **kwargs) - -def Repository(env, at, path): - """Maps a directory external to $MAIN_DIR to the given path so that sources - compiled from it end up in the correct place under $OBJ_DIR. NOT required - when only referring to header files. - - Args: - env: The current environment object. - at: The 'mount point' within the current directory. - path: Path to the actual directory. - """ - env.Dir(at).addRepository(env.Dir(path)) - - -def Components(*paths): - """Completes the directory paths with the correct file - names such that the directory/directory.scons name - convention can be used. - - Args: - paths: The paths to complete. If it refers to an existing - file then it is ignored. - - Returns: - The completed lif scons files that are needed to build talk. - """ - files = [] - for path in paths: - if os.path.isfile(path): - files.append(path) - else: - files.append(ExpandSconsPath(path)) - return files - - -def ExpandSconsPath(path): - """Expands a directory path into the path to the - scons file that our build uses. - Ex: magiflute/plugin/common => magicflute/plugin/common/common.scons - - Args: - path: The directory path to expand. - - Returns: - The expanded path. - """ - return '%s/%s.scons' % (path, os.path.basename(path)) - - -def ReadVersion(filename): - """Executes the supplied file and pulls out a version definition from it. """ - defs = {} - execfile(str(filename), defs) - if 'version' not in defs: - return '0.0.0.0' - version = defs['version'] - parts = version.split(',') - build = os.environ.get('GOOGLE_VERSION_BUILDNUMBER') - if build: - parts[-1] = str(build) - return '.'.join(parts) - - -#------------------------------------------------------------------------------- -# Helper methods for translating talk.Foo() declarations in to manipulations of -# environmuent construction variables, including parameter parsing and merging, -# -def PopEntry(dictionary, key): - """Get the value from a dictionary by key. If the key - isn't in the dictionary then None is returned. If it is in - the dictionary the value is fetched and then is it removed - from the dictionary. - - Args: - dictionary: The dictionary. - key: The key to get the value for. - Returns: - The value or None if the key is missing. - """ - value = None - if key in dictionary: - value = dictionary[key] - dictionary.pop(key) - return value - - -def MergeAndFilterByPlatform(env, params): - """Take a dictionary of arguments to lists of values, and, depending on - which platform we are targetting, merge the lists of associated keys. - Merge by combining value lists like so: - {win_foo = [a,b], lin_foo = [c,d], foo = [e], mac_bar = [f], bar = [g] } - becomes {foo = [a,b,e], bar = [g]} on windows, and - {foo = [e], bar = [f,g]} on mac - - Args: - env: The hammer environment which knows which platforms are active - params: The keyword argument dictionary. - Returns: - A new dictionary with the filtered and combined entries of params - """ - platforms = { - 'linux': 'lin_', - 'mac': 'mac_', - 'posix': 'posix_', - 'windows': 'win_', - } - active_prefixes = [ - platforms[x] for x in iter(platforms) if env.Bit(x) - ] - inactive_prefixes = [ - platforms[x] for x in iter(platforms) if not env.Bit(x) - ] - - merged = {} - for arg, values in params.iteritems(): - inactive_platform = False - - key = arg - - for prefix in active_prefixes: - if arg.startswith(prefix): - key = arg[len(prefix):] - - for prefix in inactive_prefixes: - if arg.startswith(prefix): - inactive_platform = True - - if inactive_platform: - continue - - AddToDict(merged, key, values) - - return merged - - -def MergeSettingsFromLibraryDependencies(env, params): - if 'libs' in params: - for lib in params['libs']: - libparams = _GetLibParams(env, lib) - if libparams: - if 'dependent_target_settings' in libparams: - params = CombineDicts( - params, - MergeAndFilterByPlatform( - env, - libparams['dependent_target_settings'])) - return params - - -def ExtendComponent(env, component, **kwargs): - """A wrapper around a scons builder function that preprocesses and post- - processes its inputs and outputs. For example, it merges and filters - certain keyword arguments before appending them to the environments - construction variables. It can build signed targets and 64bit copies - of targets as well. - - Args: - env: The hammer environment with which to build the target - component: The environment's builder function, e.g. ComponentProgram - kwargs: keyword arguments that are either merged, translated, and passed on - to the call to component, or which control execution. - TODO(): Document the fields, such as cppdefines->CPPDEFINES, - prepend_includedirs, include_talk_media_libs, etc. - Returns: - The output node returned by the call to component, or a subsequent signed - dependant node. - """ - env = env.Clone() - - # prune parameters intended for other platforms, then merge - params = MergeAndFilterByPlatform(env, kwargs) - - # get the 'target' field - name = PopEntry(params, 'name') - - # get the 'packages' field and process it if present (only used for Linux). - packages = PopEntry(params, 'packages') - if packages and len(packages): - params = CombineDicts(params, env.GetPackageParams(packages)) - - # save pristine params of lib targets for future reference - if 'ComponentLibrary' == component: - _RecordLibParams(env, name, dict(params)) - - # add any dependent target settings from library dependencies - params = MergeSettingsFromLibraryDependencies(env, params) - - # if this is a signed binary we need to make an unsigned version first - signed = env.Bit('windows') and PopEntry(params, 'signed') - if signed: - name = 'unsigned_' + name - - # potentially exit now - srcs = PopEntry(params, 'srcs') - if not srcs or not hasattr(env, component): - return None - - # apply any explicit dependencies - dependencies = PopEntry(params, 'depends') - if dependencies is not None: - env.Depends(name, dependencies) - - # put the contents of params into the environment - # some entries are renamed then appended, others renamed then prepended - appends = { - 'cppdefines' : 'CPPDEFINES', - 'libdirs' : 'LIBPATH', - 'link_flags' : 'LINKFLAGS', - 'libs' : 'LIBS', - 'FRAMEWORKS' : 'FRAMEWORKS', - } - prepends = {} - if env.Bit('windows'): - # MSVC compile flags have precedence at the beginning ... - prepends['ccflags'] = 'CCFLAGS' - else: - # ... while GCC compile flags have precedence at the end - appends['ccflags'] = 'CCFLAGS' - if PopEntry(params, 'prepend_includedirs'): - prepends['includedirs'] = 'CPPPATH' - else: - appends['includedirs'] = 'CPPPATH' - - for field, var in appends.items(): - values = PopEntry(params, field) - if values is not None: - env.Append(**{var : values}) - for field, var in prepends.items(): - values = PopEntry(params, field) - if values is not None: - env.Prepend(**{var : values}) - - # any other parameters are replaced without renaming - for field, value in params.items(): - env.Replace(**{field : value}) - - if env.Bit('linux') and 'LIBS' in env: - libs = env['LIBS'] - # When using --as-needed + --start/end-group, shared libraries need to come - # after --end-group on the command-line because the pruning decision only - # considers the preceding modules and --start/end-group may cause the - # effective position of early static libraries on the command-line to be - # deferred to the point of --end-group. To effect this, we move shared libs - # into _LIBFLAGS, which has the --end-group as its first entry. SCons does - # not track dependencies on system shared libraries anyway so we lose - # nothing by removing them from LIBS. - static_libs = [lib for lib in libs if - _GetLibParams(env, lib) or _IsPrebuiltLibrary(env, lib)] - shared_libs = ['-l' + lib for lib in libs if not - (_GetLibParams(env, lib) or _IsPrebuiltLibrary(env, lib))] - env.Replace(LIBS=static_libs) - env.Append(_LIBFLAGS=shared_libs) - - # invoke the builder function - builder = getattr(env, component) - - node = builder(name, srcs) - - if env.Bit('mac') and 'ComponentProgram' == component: - # Build .dSYM debug packages. This is useful even for non-stripped - # binaries, as the dsym utility will fetch symbols from all - # statically-linked libraries (the linker doesn't include them in to the - # final binary). - build_dsym = env.Command( - env.Dir('$STAGING_DIR/%s.dSYM' % node[0]), - node, - 'mkdir -p `dirname $TARGET` && dsymutil -o $TARGET $SOURCE') - env.Alias('all_dsym', env.Alias('%s.dSYM' % node[0], build_dsym)) - - if signed: - # Get the name of the built binary, then get the name of the final signed - # version from it. We need the output path since we don't know the file - # extension beforehand. - target = node[0].path.split('_', 1)[1] - # postsignprefix: If defined, postsignprefix is a string that should be - # prepended to the target executable. This is to provide a work around - # for EXEs and DLLs with the same name, which thus have PDBs with the - # same name. Setting postsignprefix allows the EXE and its PDB - # to be renamed and copied in a previous step; then the desired - # name of the EXE (but not PDB) is reconstructed after signing. - postsignprefix = PopEntry(params, 'postsignprefix') - if postsignprefix is not None: - target = postsignprefix + target - signed_node = env.SignedBinary( - source = node, - target = '$STAGING_DIR/' + target, - ) - env.Alias('signed_binaries', signed_node) - return signed_node - - return node - - -def AddToDict(dictionary, key, values, append=True): - """Merge the given key value(s) pair into a dictionary. If it contains an - entry with that key already, then combine by appending or prepending the - values as directed. Otherwise, assign a new keyvalue pair. - """ - if values is None: - return - - if key not in dictionary: - dictionary[key] = values - return - - cur = dictionary[key] - # TODO(dape): Make sure that there are no duplicates - # in the list. I can't use python set for this since - # the nodes that are returned by the SCONS builders - # are not hashable. - # dictionary[key] = list(set(cur).union(set(values))) - if append: - dictionary[key] = cur + values - else: - dictionary[key] = values + cur - - -def CombineDicts(a, b): - """Unions two dictionaries of arrays/dictionaries. - - Unions two dictionaries of arrays/dictionaries by combining the values of keys - shared between them. The original dictionaries should not be used again after - this call. - - Args: - a: First dict. - b: Second dict. - - Returns: - The union of a and b. - """ - c = {} - for key in a: - if key in b: - aval = a[key] - bval = b.pop(key) - if isinstance(aval, dict) and isinstance(bval, dict): - c[key] = CombineDicts(aval, bval) - else: - c[key] = aval + bval - else: - c[key] = a[key] - - for key in b: - c[key] = b[key] - - return c - - -def RenameKey(d, old, new, append=True): - AddToDict(d, new, PopEntry(d, old), append) diff --git a/chromium/third_party/libjingle/source/talk/xmllite/xmlparser.cc b/chromium/third_party/libjingle/source/talk/xmllite/xmlparser.cc index 3e4d73348ee..8802231872e 100644 --- a/chromium/third_party/libjingle/source/talk/xmllite/xmlparser.cc +++ b/chromium/third_party/libjingle/source/talk/xmllite/xmlparser.cc @@ -60,7 +60,7 @@ XmlDeclCallback(void * userData, const char * ver, const char * enc, int st) { } XmlParser::XmlParser(XmlParseHandler *pxph) : - context_(this), pxph_(pxph), sentError_(false) { + pxph_(pxph), sentError_(false) { expat_ = XML_ParserCreate(NULL); XML_SetUserData(expat_, this); XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback); @@ -196,8 +196,7 @@ XmlParser::ParseXml(XmlParseHandler *pxph, std::string text) { parser.Parse(text.c_str(), text.length(), true); } -XmlParser::ParseContext::ParseContext(XmlParser *parser) : - parser_(parser), +XmlParser::ParseContext::ParseContext() : xmlnsstack_(), raised_(XML_ERROR_NONE), line_number_(0), diff --git a/chromium/third_party/libjingle/source/talk/xmllite/xmlparser.h b/chromium/third_party/libjingle/source/talk/xmllite/xmlparser.h index 69cde75f770..4a79858e42f 100644 --- a/chromium/third_party/libjingle/source/talk/xmllite/xmlparser.h +++ b/chromium/third_party/libjingle/source/talk/xmllite/xmlparser.h @@ -87,7 +87,7 @@ private: class ParseContext : public XmlParseContext { public: - ParseContext(XmlParser * parser); + ParseContext(); virtual ~ParseContext(); virtual QName ResolveQName(const char * qname, bool isAttr); virtual void RaiseError(XML_Error err) { if (!raised_) raised_ = err; } @@ -102,7 +102,6 @@ private: void SetPosition(int line, int column, long byte_index); private: - const XmlParser * parser_; XmlnsStack xmlnsstack_; XML_Error raised_; XML_Size line_number_; diff --git a/chromium/third_party/libjingle/source/talk/xmpp/chatroommoduleimpl.cc b/chromium/third_party/libjingle/source/talk/xmpp/chatroommoduleimpl.cc index eb046d72112..a12ff5e029d 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/chatroommoduleimpl.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/chatroommoduleimpl.cc @@ -320,17 +320,13 @@ XmppChatroomModuleImpl::RequestExitChatroom() { if (!engine()) return XMPP_RETURN_BADSTATE; - // currently, can't leave a room unless you've entered - // no way to cancel a pending enter call - is that bad? - if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) - return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code? - // exiting a chatroom is a presence request to the server XmlElement element(QN_PRESENCE); element.AddAttr(QN_TO, member_jid().Str()); element.AddAttr(QN_TYPE, "unavailable"); XmppReturnStatus status = engine()->SendStanza(&element); - if (status == XMPP_RETURN_OK) { + if (status == XMPP_RETURN_OK && + chatroom_state_ == XMPP_CHATROOM_STATE_IN_ROOM) { return ClientChangeMyPresence(XMPP_CHATROOM_STATE_REQUESTED_EXIT); } return status; @@ -513,6 +509,7 @@ XmppChatroomModuleImpl::ServerChangedOtherPresence(const XmlElement& FireMemberChanged(member); } else if (presence->available() == XMPP_PRESENCE_UNAVAILABLE) { + member->SetPresence(presence.get()); chatroom_jid_members_.erase(pos); chatroom_jid_members_version_++; FireMemberExited(member); diff --git a/chromium/third_party/libjingle/source/talk/xmpp/constants.cc b/chromium/third_party/libjingle/source/talk/xmpp/constants.cc index c56796bc9e7..f69f84e469c 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/constants.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/constants.cc @@ -118,6 +118,10 @@ const char STR_MUC_ROOM_FEATURE_HANGOUT[] = "muc_es"; const char STR_MUC_ROOM_FEATURE_HANGOUT_LITE[] = "muc_lite"; const char STR_MUC_ROOM_FEATURE_BROADCAST[] = "broadcast"; const char STR_MUC_ROOM_FEATURE_MULTI_USER_VC[] = "muc_muvc"; +const char STR_MUC_ROOM_FEATURE_RECORDABLE[] = "recordable"; +const char STR_MUC_ROOM_FEATURE_CUSTOM_RECORDING[] = "custom_recording"; +const char STR_MUC_ROOM_OWNER_PROFILE_ID[] = "muc#roominfo_owner_profile_id"; +const char STR_MUC_ROOM_FEATURE_ABUSE_RECORDABLE[] = "abuse_recordable"; const char STR_ID_TYPE_CONVERSATION[] = "conversation"; const char NS_GOOGLE_MUC_HANGOUT[] = "google:muc#hangout"; @@ -213,7 +217,6 @@ const char NS_GOOGLE_AUTH_PROTOCOL[] = "http://www.google.com/talk/protocol/auth"; const StaticQName QN_GOOGLE_AUTH_CLIENT_USES_FULL_BIND_RESULT = { NS_GOOGLE_AUTH_PROTOCOL, "client-uses-full-bind-result" }; -const char NS_GOOGLE_AUTH_OLD[] = "google:auth"; const StaticQName QN_GOOGLE_ALLOW_NON_GOOGLE_ID_XMPP_LOGIN = { NS_GOOGLE_AUTH_PROTOCOL, "allow-non-google-login" }; const StaticQName QN_GOOGLE_AUTH_SERVICE = @@ -342,8 +345,6 @@ const StaticQName QN_NICK = { STR_EMPTY, "nick" }; const StaticQName QN_SUBSCRIPTION = { STR_EMPTY, "subscription" }; const StaticQName QN_TITLE1 = { STR_EMPTY, "title1" }; const StaticQName QN_TITLE2 = { STR_EMPTY, "title2" }; -const StaticQName QN_SOURCE = { STR_EMPTY, "source" }; -const StaticQName QN_TIME = { STR_EMPTY, "time" }; const StaticQName QN_XMLNS_CLIENT = { NS_XMLNS, STR_CLIENT }; const StaticQName QN_XMLNS_SERVER = { NS_XMLNS, STR_SERVER }; diff --git a/chromium/third_party/libjingle/source/talk/xmpp/constants.h b/chromium/third_party/libjingle/source/talk/xmpp/constants.h index c53abb5b86f..6d940957da6 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/constants.h +++ b/chromium/third_party/libjingle/source/talk/xmpp/constants.h @@ -111,6 +111,10 @@ extern const char STR_MUC_ROOM_FEATURE_HANGOUT[]; extern const char STR_MUC_ROOM_FEATURE_HANGOUT_LITE[]; extern const char STR_MUC_ROOM_FEATURE_BROADCAST[]; extern const char STR_MUC_ROOM_FEATURE_MULTI_USER_VC[]; +extern const char STR_MUC_ROOM_FEATURE_RECORDABLE[]; +extern const char STR_MUC_ROOM_FEATURE_CUSTOM_RECORDING[]; +extern const char STR_MUC_ROOM_OWNER_PROFILE_ID[]; +extern const char STR_MUC_ROOM_FEATURE_ABUSE_RECORDABLE[]; extern const char STR_ID_TYPE_CONVERSATION[]; extern const char NS_GOOGLE_MUC_HANGOUT[]; @@ -119,6 +123,7 @@ extern const StaticQName QN_GOOGLE_MUC_HANGOUT_INVITE_TYPE; extern const StaticQName QN_ATTR_CREATE_ACTIVITY; extern const StaticQName QN_GOOGLE_MUC_HANGOUT_PUBLIC; extern const StaticQName QN_GOOGLE_MUC_HANGOUT_INVITEE; +extern const StaticQName QN_GOOGLE_MUC_HANGOUT_NOTIFICATION_STATUS; extern const StaticQName QN_GOOGLE_MUC_HANGOUT_NOTIFICATION_TYPE; extern const StaticQName QN_GOOGLE_MUC_HANGOUT_HANGOUT_START_CONTEXT; extern const StaticQName QN_GOOGLE_MUC_HANGOUT_CONVERSATION_ID; diff --git a/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient.cc b/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient.cc index b6669a10865..aede56318cc 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient.cc @@ -42,56 +42,9 @@ namespace buzz { namespace { const char kPresenting[] = "s"; const char kNotPresenting[] = "o"; -const char kEmpty[] = ""; - -const std::string GetPublisherNickFromPubSubItem(const XmlElement* item_elem) { - if (item_elem == NULL) { - return ""; - } - - return Jid(item_elem->Attr(QN_ATTR_PUBLISHER)).resource(); -} } // namespace - -// Knows how to handle specific states and XML. -template <typename C> -class PubSubStateSerializer { - public: - virtual ~PubSubStateSerializer() {} - virtual XmlElement* Write(const QName& state_name, const C& state) = 0; - virtual C Parse(const XmlElement* state_elem) = 0; -}; - -// Knows how to create "keys" for states, which determines their -// uniqueness. Most states are per-nick, but block is -// per-blocker-and-blockee. This is independent of itemid, especially -// in the case of presenter state. -class PubSubStateKeySerializer { - public: - virtual ~PubSubStateKeySerializer() {} - virtual std::string GetKey(const std::string& publisher_nick, - const std::string& published_nick) = 0; -}; - -class PublishedNickKeySerializer : public PubSubStateKeySerializer { - public: - virtual std::string GetKey(const std::string& publisher_nick, - const std::string& published_nick) { - return published_nick; - } -}; - -class PublisherAndPublishedNicksKeySerializer - : public PubSubStateKeySerializer { - public: - virtual std::string GetKey(const std::string& publisher_nick, - const std::string& published_nick) { - return publisher_nick + ":" + published_nick; - } -}; - // A simple serialiazer where presence of item => true, lack of item // => false. class BoolStateSerializer : public PubSubStateSerializer<bool> { @@ -103,195 +56,11 @@ class BoolStateSerializer : public PubSubStateSerializer<bool> { return new XmlElement(state_name, true); } - virtual bool Parse(const XmlElement* state_elem) { - return state_elem != NULL; + virtual void Parse(const XmlElement* state_elem, bool *state_out) { + *state_out = state_elem != NULL; } }; -// Adapts PubSubClient to be specifically suited for pub sub call -// states. Signals state changes and keeps track of keys, which are -// normally nicks. -// TODO: Expose this as a generally useful class, not just -// private to hangouts. -template <typename C> -class PubSubStateClient : public sigslot::has_slots<> { - public: - // Gets ownership of the serializers, but not the client. - PubSubStateClient(const std::string& publisher_nick, - PubSubClient* client, - const QName& state_name, - C default_state, - PubSubStateKeySerializer* key_serializer, - PubSubStateSerializer<C>* state_serializer) - : publisher_nick_(publisher_nick), - client_(client), - state_name_(state_name), - default_state_(default_state) { - key_serializer_.reset(key_serializer); - state_serializer_.reset(state_serializer); - client_->SignalItems.connect( - this, &PubSubStateClient<C>::OnItems); - client_->SignalPublishResult.connect( - this, &PubSubStateClient<C>::OnPublishResult); - client_->SignalPublishError.connect( - this, &PubSubStateClient<C>::OnPublishError); - client_->SignalRetractResult.connect( - this, &PubSubStateClient<C>::OnRetractResult); - client_->SignalRetractError.connect( - this, &PubSubStateClient<C>::OnRetractError); - } - - virtual ~PubSubStateClient() {} - - virtual void Publish(const std::string& published_nick, - const C& state, - std::string* task_id_out) { - std::string key = key_serializer_->GetKey(publisher_nick_, published_nick); - std::string itemid = state_name_.LocalPart() + ":" + key; - if (StatesEqual(state, default_state_)) { - client_->RetractItem(itemid, task_id_out); - } else { - XmlElement* state_elem = state_serializer_->Write(state_name_, state); - state_elem->AddAttr(QN_NICK, published_nick); - client_->PublishItem(itemid, state_elem, task_id_out); - } - }; - - sigslot::signal1<const PubSubStateChange<C>&> SignalStateChange; - // Signal (task_id, item). item is NULL for retract. - sigslot::signal2<const std::string&, - const XmlElement*> SignalPublishResult; - // Signal (task_id, item, error stanza). item is NULL for retract. - sigslot::signal3<const std::string&, - const XmlElement*, - const XmlElement*> SignalPublishError; - - protected: - // return false if retracted item (no info or state given) - virtual bool ParseStateItem(const PubSubItem& item, - StateItemInfo* info_out, - bool* state_out) { - const XmlElement* state_elem = item.elem->FirstNamed(state_name_); - if (state_elem == NULL) { - return false; - } - - info_out->publisher_nick = GetPublisherNickFromPubSubItem(item.elem); - info_out->published_nick = state_elem->Attr(QN_NICK); - *state_out = state_serializer_->Parse(state_elem); - return true; - }; - - virtual bool StatesEqual(C state1, C state2) { - return state1 == state2; - } - - PubSubClient* client() { return client_; } - - private: - void OnItems(PubSubClient* pub_sub_client, - const std::vector<PubSubItem>& items) { - for (std::vector<PubSubItem>::const_iterator item = items.begin(); - item != items.end(); ++item) { - OnItem(*item); - } - } - - void OnItem(const PubSubItem& item) { - const std::string& itemid = item.itemid; - StateItemInfo info; - C new_state; - - bool retracted = !ParseStateItem(item, &info, &new_state); - if (retracted) { - bool known_itemid = - (info_by_itemid_.find(itemid) != info_by_itemid_.end()); - if (!known_itemid) { - // Nothing to retract, and nothing to publish. - // Probably a different state type. - return; - } else { - info = info_by_itemid_[itemid]; - info_by_itemid_.erase(itemid); - new_state = default_state_; - } - } else { - // TODO: Assert new key matches the known key. It - // shouldn't change! - info_by_itemid_[itemid] = info; - } - - std::string key = key_serializer_->GetKey( - info.publisher_nick, info.published_nick); - bool has_old_state = (state_by_key_.find(key) != state_by_key_.end()); - C old_state = has_old_state ? state_by_key_[key] : default_state_; - if ((retracted && !has_old_state) || StatesEqual(new_state, old_state)) { - // Nothing change, so don't bother signalling. - return; - } - - if (retracted || StatesEqual(new_state, default_state_)) { - // We treat a default state similar to a retract. - state_by_key_.erase(key); - } else { - state_by_key_[key] = new_state; - } - - PubSubStateChange<C> change; - if (!retracted) { - // Retracts do not have publisher information. - change.publisher_nick = info.publisher_nick; - } - change.published_nick = info.published_nick; - change.old_state = old_state; - change.new_state = new_state; - SignalStateChange(change); - } - - void OnPublishResult(PubSubClient* pub_sub_client, - const std::string& task_id, - const XmlElement* item) { - SignalPublishResult(task_id, item); - } - - void OnPublishError(PubSubClient* pub_sub_client, - const std::string& task_id, - const buzz::XmlElement* item, - const buzz::XmlElement* stanza) { - SignalPublishError(task_id, item, stanza); - } - - void OnRetractResult(PubSubClient* pub_sub_client, - const std::string& task_id) { - // There's no point in differentiating between publish and retract - // errors, so we simplify by making them both signal a publish - // result. - const XmlElement* item = NULL; - SignalPublishResult(task_id, item); - } - - void OnRetractError(PubSubClient* pub_sub_client, - const std::string& task_id, - const buzz::XmlElement* stanza) { - // There's no point in differentiating between publish and retract - // errors, so we simplify by making them both signal a publish - // error. - const XmlElement* item = NULL; - SignalPublishError(task_id, item, stanza); - } - - std::string publisher_nick_; - PubSubClient* client_; - const QName state_name_; - C default_state_; - talk_base::scoped_ptr<PubSubStateKeySerializer> key_serializer_; - talk_base::scoped_ptr<PubSubStateSerializer<C> > state_serializer_; - // key => state - std::map<std::string, C> state_by_key_; - // itemid => StateItemInfo - std::map<std::string, StateItemInfo> info_by_itemid_; -}; - class PresenterStateClient : public PubSubStateClient<bool> { public: PresenterStateClient(const std::string& publisher_nick, @@ -336,15 +105,17 @@ class PresenterStateClient : public PubSubStateClient<bool> { return false; } - info_out->publisher_nick = GetPublisherNickFromPubSubItem(item.elem); + info_out->publisher_nick = + client()->GetPublisherNickFromPubSubItem(item.elem); info_out->published_nick = presenter_elem->Attr(QN_NICK); *state_out = (presentation_item_elem->Attr( QN_PRESENTER_PRESENTATION_TYPE) != kNotPresenting); return true; } - virtual bool StatesEqual(bool state1, bool state2) { - return false; // Make every item trigger an event, even if state doesn't change. + virtual bool StatesEqual(const bool& state1, const bool& state2) { + // Make every item trigger an event, even if state doesn't change. + return false; } }; @@ -506,6 +277,7 @@ void HangoutPubSubClient::OnAudioMuteStateChange( bool is_muted = change.new_state; bool remote_action = (!change.publisher_nick.empty() && (change.publisher_nick != change.published_nick)); + if (remote_action) { const std::string& mutee_nick = change.published_nick; const std::string& muter_nick = change.publisher_nick; @@ -516,9 +288,8 @@ void HangoutPubSubClient::OnAudioMuteStateChange( } bool should_mute_locally = (mutee_nick == nick_); SignalRemoteMute(mutee_nick, muter_nick, should_mute_locally); - } else { - SignalAudioMuteStateChange(change.published_nick, was_muted, is_muted); } + SignalAudioMuteStateChange(change.published_nick, was_muted, is_muted); } const std::string GetAudioMuteNickFromItem(const XmlElement* item) { diff --git a/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient.h b/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient.h index a9986db1598..2fcd6913290 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient.h +++ b/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient.h @@ -37,6 +37,7 @@ #include "talk/base/sigslotrepeater.h" #include "talk/xmpp/jid.h" #include "talk/xmpp/pubsubclient.h" +#include "talk/xmpp/pubsubstateclient.h" // Gives a high-level API for MUC call PubSub needs such as // presenter state, recording state, mute state, and remote mute. @@ -47,30 +48,6 @@ class Jid; class XmlElement; class XmppTaskParentInterface; -// To handle retracts correctly, we need to remember certain details -// about an item. We could just cache the entire XML element, but -// that would take more memory and require re-parsing. -struct StateItemInfo { - std::string published_nick; - std::string publisher_nick; -}; - -// Represents a PubSub state change. Usually, the key is the nick, -// but not always. It's a per-state-type thing. Currently documented -// at https://docs.google.com/a/google.com/document/d/ -// 1QyHu_ufyVdf0VICdfc_DtJbrOdrdIUm4eM73RZqnivI/edit?hl=en_US -template <typename C> -struct PubSubStateChange { - // The nick of the user changing the state. - std::string publisher_nick; - // The nick of the user whose state is changing. - std::string published_nick; - C old_state; - C new_state; -}; - -template <typename C> class PubSubStateClient; - // A client tied to a specific MUC jid and local nick. Provides ways // to get updates and publish state and events. Must call // RequestAll() to start getting updates. diff --git a/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient_unittest.cc b/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient_unittest.cc index 0ffb248f3c3..1d1c14b13ee 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/hangoutpubsubclient_unittest.cc @@ -449,11 +449,14 @@ TEST_F(HangoutPubSubClientTest, TestRequest) { " </event>" "</message>"; + listener->last_is_audio_muted = false; xmpp_client->HandleStanza( buzz::XmlElement::ForStr(incoming_remote_mute_message)); EXPECT_EQ("mutee", listener->last_mutee_nick); EXPECT_EQ("muter", listener->last_muter_nick); EXPECT_FALSE(listener->last_should_mute); + EXPECT_EQ("mutee", listener->last_audio_muted_nick); + EXPECT_TRUE(listener->last_is_audio_muted); std::string incoming_remote_mute_me_message = "<message xmlns='jabber:client' from='room@domain.com'>" @@ -466,11 +469,14 @@ TEST_F(HangoutPubSubClientTest, TestRequest) { " </event>" "</message>"; + listener->last_is_audio_muted = false; xmpp_client->HandleStanza( buzz::XmlElement::ForStr(incoming_remote_mute_me_message)); EXPECT_EQ("me", listener->last_mutee_nick); EXPECT_EQ("muter", listener->last_muter_nick); EXPECT_TRUE(listener->last_should_mute); + EXPECT_EQ("me", listener->last_audio_muted_nick); + EXPECT_TRUE(listener->last_is_audio_muted); std::string incoming_media_block_message = "<message xmlns='jabber:client' from='room@domain.com'>" diff --git a/chromium/third_party/libjingle/source/talk/xmpp/pubsubclient.cc b/chromium/third_party/libjingle/source/talk/xmpp/pubsubclient.cc index 8d6d4c414a2..b62758710c7 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/pubsubclient.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/pubsubclient.cc @@ -134,4 +134,13 @@ void PubSubClient::OnRetractError(IqTask* task, SignalRetractError(this, retract_task->task_id(), stanza); } + +const std::string PubSubClient::GetPublisherNickFromPubSubItem( + const XmlElement* item_elem) { + if (item_elem == NULL) { + return ""; + } + + return Jid(item_elem->Attr(QN_ATTR_PUBLISHER)).resource(); +} } // namespace buzz diff --git a/chromium/third_party/libjingle/source/talk/xmpp/pubsubclient.h b/chromium/third_party/libjingle/source/talk/xmpp/pubsubclient.h index 099765a2d35..f0cd7a98f4a 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/pubsubclient.h +++ b/chromium/third_party/libjingle/source/talk/xmpp/pubsubclient.h @@ -101,6 +101,9 @@ class PubSubClient : public sigslot::has_slots<> { void RetractItem(const std::string& itemid, std::string* task_id_out); + // Get the publisher nick if it exists from the pubsub item. + const std::string GetPublisherNickFromPubSubItem(const XmlElement* item_elem); + private: void OnRequestError(IqTask* task, const XmlElement* stanza); diff --git a/chromium/third_party/libjingle/source/talk/xmpp/pubsubstateclient.cc b/chromium/third_party/libjingle/source/talk/xmpp/pubsubstateclient.cc new file mode 100644 index 00000000000..5cd7b1a703b --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/xmpp/pubsubstateclient.cc @@ -0,0 +1,42 @@ +/* + * libjingle + * Copyright 2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/xmpp/pubsubstateclient.h" + +namespace buzz { + +std::string PublishedNickKeySerializer::GetKey( + const std::string& publisher_nick, const std::string& published_nick) { + return published_nick; +} + +std::string PublisherAndPublishedNicksKeySerializer::GetKey( + const std::string& publisher_nick, const std::string& published_nick) { + return publisher_nick + ":" + published_nick; +} + +} // namespace buzz diff --git a/chromium/third_party/libjingle/source/talk/xmpp/pubsubstateclient.h b/chromium/third_party/libjingle/source/talk/xmpp/pubsubstateclient.h new file mode 100644 index 00000000000..f38658defd7 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/xmpp/pubsubstateclient.h @@ -0,0 +1,287 @@ +/* + * libjingle + * Copyright 2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_XMPP_PUBSUBSTATECLIENT_H_ +#define TALK_XMPP_PUBSUBSTATECLIENT_H_ + +#include <map> +#include <string> +#include <vector> + +#include "talk/base/scoped_ptr.h" +#include "talk/base/sigslot.h" +#include "talk/base/sigslotrepeater.h" +#include "talk/xmpp/constants.h" +#include "talk/xmpp/jid.h" +#include "talk/xmpp/pubsubclient.h" +#include "talk/xmllite/qname.h" +#include "talk/xmllite/xmlelement.h" + +namespace buzz { + +// To handle retracts correctly, we need to remember certain details +// about an item. We could just cache the entire XML element, but +// that would take more memory and require re-parsing. +struct StateItemInfo { + std::string published_nick; + std::string publisher_nick; +}; + +// Represents a PubSub state change. Usually, the key is the nick, +// but not always. It's a per-state-type thing. Look below on how keys are +// computed. +template <typename C> +struct PubSubStateChange { + // The nick of the user changing the state. + std::string publisher_nick; + // The nick of the user whose state is changing. + std::string published_nick; + C old_state; + C new_state; +}; + +// Knows how to handle specific states and XML. +template <typename C> +class PubSubStateSerializer { + public: + virtual ~PubSubStateSerializer() {} + virtual XmlElement* Write(const QName& state_name, const C& state) = 0; + virtual void Parse(const XmlElement* state_elem, C* state_out) = 0; +}; + +// Knows how to create "keys" for states, which determines their +// uniqueness. Most states are per-nick, but block is +// per-blocker-and-blockee. This is independent of itemid, especially +// in the case of presenter state. +class PubSubStateKeySerializer { + public: + virtual ~PubSubStateKeySerializer() {} + virtual std::string GetKey(const std::string& publisher_nick, + const std::string& published_nick) = 0; +}; + +class PublishedNickKeySerializer : public PubSubStateKeySerializer { + public: + virtual std::string GetKey(const std::string& publisher_nick, + const std::string& published_nick); +}; + +class PublisherAndPublishedNicksKeySerializer + : public PubSubStateKeySerializer { + public: + virtual std::string GetKey(const std::string& publisher_nick, + const std::string& published_nick); +}; + +// Adapts PubSubClient to be specifically suited for pub sub call +// states. Signals state changes and keeps track of keys, which are +// normally nicks. +template <typename C> +class PubSubStateClient : public sigslot::has_slots<> { + public: + // Gets ownership of the serializers, but not the client. + PubSubStateClient(const std::string& publisher_nick, + PubSubClient* client, + const QName& state_name, + C default_state, + PubSubStateKeySerializer* key_serializer, + PubSubStateSerializer<C>* state_serializer) + : publisher_nick_(publisher_nick), + client_(client), + state_name_(state_name), + default_state_(default_state) { + key_serializer_.reset(key_serializer); + state_serializer_.reset(state_serializer); + client_->SignalItems.connect( + this, &PubSubStateClient<C>::OnItems); + client_->SignalPublishResult.connect( + this, &PubSubStateClient<C>::OnPublishResult); + client_->SignalPublishError.connect( + this, &PubSubStateClient<C>::OnPublishError); + client_->SignalRetractResult.connect( + this, &PubSubStateClient<C>::OnRetractResult); + client_->SignalRetractError.connect( + this, &PubSubStateClient<C>::OnRetractError); + } + + virtual ~PubSubStateClient() {} + + virtual void Publish(const std::string& published_nick, + const C& state, + std::string* task_id_out) { + std::string key = key_serializer_->GetKey(publisher_nick_, published_nick); + std::string itemid = state_name_.LocalPart() + ":" + key; + if (StatesEqual(state, default_state_)) { + client_->RetractItem(itemid, task_id_out); + } else { + XmlElement* state_elem = state_serializer_->Write(state_name_, state); + state_elem->AddAttr(QN_NICK, published_nick); + client_->PublishItem(itemid, state_elem, task_id_out); + } + } + + sigslot::signal1<const PubSubStateChange<C>&> SignalStateChange; + // Signal (task_id, item). item is NULL for retract. + sigslot::signal2<const std::string&, + const XmlElement*> SignalPublishResult; + // Signal (task_id, item, error stanza). item is NULL for retract. + sigslot::signal3<const std::string&, + const XmlElement*, + const XmlElement*> SignalPublishError; + + protected: + // return false if retracted item (no info or state given) + virtual bool ParseStateItem(const PubSubItem& item, + StateItemInfo* info_out, + C* state_out) { + const XmlElement* state_elem = item.elem->FirstNamed(state_name_); + if (state_elem == NULL) { + return false; + } + + info_out->publisher_nick = + client_->GetPublisherNickFromPubSubItem(item.elem); + info_out->published_nick = state_elem->Attr(QN_NICK); + state_serializer_->Parse(state_elem, state_out); + return true; + } + + virtual bool StatesEqual(const C& state1, const C& state2) { + return state1 == state2; + } + + PubSubClient* client() { return client_; } + const QName& state_name() { return state_name_; } + + private: + void OnItems(PubSubClient* pub_sub_client, + const std::vector<PubSubItem>& items) { + for (std::vector<PubSubItem>::const_iterator item = items.begin(); + item != items.end(); ++item) { + OnItem(*item); + } + } + + void OnItem(const PubSubItem& item) { + const std::string& itemid = item.itemid; + StateItemInfo info; + C new_state; + + bool retracted = !ParseStateItem(item, &info, &new_state); + if (retracted) { + bool known_itemid = + (info_by_itemid_.find(itemid) != info_by_itemid_.end()); + if (!known_itemid) { + // Nothing to retract, and nothing to publish. + // Probably a different state type. + return; + } else { + info = info_by_itemid_[itemid]; + info_by_itemid_.erase(itemid); + new_state = default_state_; + } + } else { + // TODO: Assert new key matches the known key. It + // shouldn't change! + info_by_itemid_[itemid] = info; + } + + std::string key = key_serializer_->GetKey( + info.publisher_nick, info.published_nick); + bool has_old_state = (state_by_key_.find(key) != state_by_key_.end()); + C old_state = has_old_state ? state_by_key_[key] : default_state_; + if ((retracted && !has_old_state) || StatesEqual(new_state, old_state)) { + // Nothing change, so don't bother signalling. + return; + } + + if (retracted || StatesEqual(new_state, default_state_)) { + // We treat a default state similar to a retract. + state_by_key_.erase(key); + } else { + state_by_key_[key] = new_state; + } + + PubSubStateChange<C> change; + if (!retracted) { + // Retracts do not have publisher information. + change.publisher_nick = info.publisher_nick; + } + change.published_nick = info.published_nick; + change.old_state = old_state; + change.new_state = new_state; + SignalStateChange(change); + } + + void OnPublishResult(PubSubClient* pub_sub_client, + const std::string& task_id, + const XmlElement* item) { + SignalPublishResult(task_id, item); + } + + void OnPublishError(PubSubClient* pub_sub_client, + const std::string& task_id, + const buzz::XmlElement* item, + const buzz::XmlElement* stanza) { + SignalPublishError(task_id, item, stanza); + } + + void OnRetractResult(PubSubClient* pub_sub_client, + const std::string& task_id) { + // There's no point in differentiating between publish and retract + // errors, so we simplify by making them both signal a publish + // result. + const XmlElement* item = NULL; + SignalPublishResult(task_id, item); + } + + void OnRetractError(PubSubClient* pub_sub_client, + const std::string& task_id, + const buzz::XmlElement* stanza) { + // There's no point in differentiating between publish and retract + // errors, so we simplify by making them both signal a publish + // error. + const XmlElement* item = NULL; + SignalPublishError(task_id, item, stanza); + } + + std::string publisher_nick_; + PubSubClient* client_; + const QName state_name_; + C default_state_; + talk_base::scoped_ptr<PubSubStateKeySerializer> key_serializer_; + talk_base::scoped_ptr<PubSubStateSerializer<C> > state_serializer_; + // key => state + std::map<std::string, C> state_by_key_; + // itemid => StateItemInfo + std::map<std::string, StateItemInfo> info_by_itemid_; + + DISALLOW_COPY_AND_ASSIGN(PubSubStateClient); +}; +} // namespace buzz + +#endif // TALK_XMPP_PUBSUBSTATECLIENT_H_ diff --git a/chromium/third_party/libjingle/source/talk/xmpp/pubsubtasks.cc b/chromium/third_party/libjingle/source/talk/xmpp/pubsubtasks.cc index bbefbe55cf6..015708eb5e9 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/pubsubtasks.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/pubsubtasks.cc @@ -173,9 +173,16 @@ void PubSubRequestTask::HandleResult(const XmlElement* stanza) { SignalResult(this, items); } +int PubSubReceiveTask::ProcessStart() { + if (SignalUpdate.is_empty()) { + return STATE_DONE; + } + return ReceiveTask::ProcessStart(); +} + bool PubSubReceiveTask::WantsStanza(const XmlElement* stanza) { return MatchStanzaFrom(stanza, pubsubjid_) && - IsPubSubEventItemsElem(stanza, node_); + IsPubSubEventItemsElem(stanza, node_) && !SignalUpdate.is_empty(); } void PubSubReceiveTask::ReceiveStanza(const XmlElement* stanza) { diff --git a/chromium/third_party/libjingle/source/talk/xmpp/pubsubtasks.h b/chromium/third_party/libjingle/source/talk/xmpp/pubsubtasks.h index f0a1581783c..2ba618b341c 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/pubsubtasks.h +++ b/chromium/third_party/libjingle/source/talk/xmpp/pubsubtasks.h @@ -71,6 +71,7 @@ class PubSubReceiveTask : public ReceiveTask { node_(node) { } + virtual int ProcessStart(); sigslot::signal2<PubSubReceiveTask*, const std::vector<PubSubItem>&> SignalUpdate; diff --git a/chromium/third_party/libjingle/source/talk/xmpp/rostermoduleimpl.cc b/chromium/third_party/libjingle/source/talk/xmpp/rostermoduleimpl.cc index 31b3abdf0fb..993cfa905f8 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/rostermoduleimpl.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/rostermoduleimpl.cc @@ -351,10 +351,6 @@ XmppPresenceImpl::set_raw_xml(const XmlElement * xml) { xml->Name() != QN_PRESENCE) return XMPP_RETURN_BADARGUMENT; - const std::string& type = xml->Attr(QN_TYPE); - if (type != STR_EMPTY && type != "unavailable") - return XMPP_RETURN_BADARGUMENT; - raw_xml_.reset(new XmlElement(*xml)); return XMPP_RETURN_OK; } diff --git a/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc b/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc index d4c9c7d5f67..cf07ab70ab5 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc @@ -418,8 +418,7 @@ void XmppEngineImpl::StartTls(const std::string& domain) { XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine) : engine_(engine), - state_(engine->state_), - error_(engine->error_code_) { + state_(engine->state_) { engine->engine_entered_ += 1; } diff --git a/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.h b/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.h index e292e75d1ce..82786813651 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.h +++ b/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.h @@ -234,8 +234,6 @@ class XmppEngineImpl : public XmppEngine { private: XmppEngineImpl* engine_; State state_; - Error error_; - }; friend class StanzaParseHandler; |