diff options
author | Andras Becsi <andras.becsi@digia.com> | 2014-03-18 13:16:26 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-03-20 15:55:39 +0100 |
commit | 3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch) | |
tree | 92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/third_party/libjingle | |
parent | e90d7c4b152c56919d963987e2503f9909a666d2 (diff) | |
download | qtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz |
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies
needed on Windows.
Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42
Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu>
Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/third_party/libjingle')
299 files changed, 9205 insertions, 2767 deletions
diff --git a/chromium/third_party/libjingle/README.chromium b/chromium/third_party/libjingle/README.chromium index afa121db25f..f6b4983d65a 100644 --- a/chromium/third_party/libjingle/README.chromium +++ b/chromium/third_party/libjingle/README.chromium @@ -1,7 +1,7 @@ Name: libjingle URL: http://code.google.com/p/webrtc/ Version: unknown -Revision: 4819 +Revision: 5301 License: BSD License File: source/talk/COPYING Security Critical: yes diff --git a/chromium/third_party/libjingle/libjingle.gyp b/chromium/third_party/libjingle/libjingle.gyp index c56638a82ad..11bb9bb3c93 100644 --- a/chromium/third_party/libjingle/libjingle.gyp +++ b/chromium/third_party/libjingle/libjingle.gyp @@ -274,6 +274,7 @@ '<(libjingle_source)/talk/base/checks.h', '<(libjingle_source)/talk/base/common.cc', '<(libjingle_source)/talk/base/common.h', + '<(libjingle_source)/talk/base/compile_assert.h', '<(libjingle_source)/talk/base/cpumonitor.cc', '<(libjingle_source)/talk/base/cpumonitor.h', '<(libjingle_source)/talk/base/crc32.cc', @@ -316,6 +317,7 @@ '<(libjingle_source)/talk/base/messagehandler.h', '<(libjingle_source)/talk/base/messagequeue.cc', '<(libjingle_source)/talk/base/messagequeue.h', + '<(libjingle_source)/talk/base/move.h', '<(libjingle_source)/talk/base/nethelpers.cc', '<(libjingle_source)/talk/base/nethelpers.h', '<(libjingle_source)/talk/base/network.cc', @@ -383,6 +385,7 @@ '<(libjingle_source)/talk/base/taskparent.h', '<(libjingle_source)/talk/base/taskrunner.cc', '<(libjingle_source)/talk/base/taskrunner.h', + '<(libjingle_source)/talk/base/template_util.h', '<(libjingle_source)/talk/base/thread.cc', '<(libjingle_source)/talk/base/thread.h', '<(libjingle_source)/talk/base/timeutils.cc', @@ -730,6 +733,8 @@ '<(libjingle_source)/talk/media/devices/dummydevicemanager.h', '<(libjingle_source)/talk/media/devices/filevideocapturer.cc', '<(libjingle_source)/talk/media/devices/filevideocapturer.h', + '<(libjingle_source)/talk/media/sctp/sctputils.cc', + '<(libjingle_source)/talk/media/sctp/sctputils.h', '<(libjingle_source)/talk/media/webrtc/webrtccommon.h', '<(libjingle_source)/talk/media/webrtc/webrtcpassthroughrender.cc', '<(libjingle_source)/talk/media/webrtc/webrtcpassthroughrender.h', @@ -784,8 +789,8 @@ 'overrides/allocator_shim/allocator_stub.h', ], }], - # TODO(mallinath) - Enable SCTP for Android and iOS platforms. - ['OS!="android" and OS!="ios"', { + # TODO(mallinath) - Enable SCTP for iOS. + ['OS!="ios"', { 'defines': [ 'HAVE_SCTP', ], diff --git a/chromium/third_party/libjingle/overrides/init_webrtc.cc b/chromium/third_party/libjingle/overrides/init_webrtc.cc index 746f8831cd1..e0e05321f97 100644 --- a/chromium/third_party/libjingle/overrides/init_webrtc.cc +++ b/chromium/third_party/libjingle/overrides/init_webrtc.cc @@ -10,6 +10,7 @@ #include "base/native_library.h" #include "base/path_service.h" #include "talk/base/basictypes.h" +#include "third_party/libjingle/overrides/talk/base/logging.h" const unsigned char* GetCategoryGroupEnabled(const char* category_group) { return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); @@ -95,13 +96,19 @@ bool InitializeWebRtcModule() { // the alloc/dealloc functions. // PS: This function is actually implemented in allocator_proxy.cc with the // new/delete overrides. - return initialize_module(*CommandLine::ForCurrentProcess(), + InitDiagnosticLoggingDelegateFunctionFunction init_diagnostic_logging = NULL; + bool init_ok = initialize_module(*CommandLine::ForCurrentProcess(), #if !defined(OS_MACOSX) && !defined(OS_ANDROID) &Allocate, &Dellocate, #endif logging::GetLogMessageHandler(), &GetCategoryGroupEnabled, &AddTraceEvent, - &g_create_webrtc_media_engine, &g_destroy_webrtc_media_engine); + &g_create_webrtc_media_engine, &g_destroy_webrtc_media_engine, + &init_diagnostic_logging); + + if (init_ok) + talk_base::SetExtraLoggingInit(init_diagnostic_logging); + return init_ok; } cricket::MediaEngineInterface* CreateWebRtcMediaEngine( diff --git a/chromium/third_party/libjingle/overrides/init_webrtc.h b/chromium/third_party/libjingle/overrides/init_webrtc.h index 5bba57a12d3..acafe50e508 100644 --- a/chromium/third_party/libjingle/overrides/init_webrtc.h +++ b/chromium/third_party/libjingle/overrides/init_webrtc.h @@ -30,6 +30,9 @@ typedef cricket::MediaEngineInterface* (*CreateWebRtcMediaEngineFunction)( typedef void (*DestroyWebRtcMediaEngineFunction)( cricket::MediaEngineInterface* media_engine); +typedef void (*InitDiagnosticLoggingDelegateFunctionFunction)( + void (*DelegateFunction)(const std::string&)); + // A typedef for the main initialize function in libpeerconnection. // This will initialize logging in the module with the proper arguments // as well as provide pointers back to a couple webrtc factory functions. @@ -45,7 +48,8 @@ typedef bool (*InitializeModuleFunction)( webrtc::GetCategoryEnabledPtr trace_get_category_enabled, webrtc::AddTraceEventPtr trace_add_trace_event, CreateWebRtcMediaEngineFunction* create_media_engine, - DestroyWebRtcMediaEngineFunction* destroy_media_engine); + DestroyWebRtcMediaEngineFunction* destroy_media_engine, + InitDiagnosticLoggingDelegateFunctionFunction* init_diagnostic_logging); #if !defined(LIBPEERCONNECTION_IMPLEMENTATION) // Load and initialize the shared WebRTC module (libpeerconnection). diff --git a/chromium/third_party/libjingle/overrides/initialize_module.cc b/chromium/third_party/libjingle/overrides/initialize_module.cc index a2528b8c84a..fd1af1196bb 100644 --- a/chromium/third_party/libjingle/overrides/initialize_module.cc +++ b/chromium/third_party/libjingle/overrides/initialize_module.cc @@ -9,6 +9,7 @@ #include "init_webrtc.h" #include "talk/base/basictypes.h" #include "talk/media/webrtc/webrtcmediaengine.h" +#include "third_party/libjingle/overrides/talk/base/logging.h" #if !defined(LIBPEERCONNECTION_IMPLEMENTATION) || defined(LIBPEERCONNECTION_LIB) #error "Only compile the allocator proxy with the shared_library implementation" @@ -52,7 +53,9 @@ bool InitializeModule(const CommandLine& command_line, webrtc::GetCategoryEnabledPtr trace_get_category_enabled, webrtc::AddTraceEventPtr trace_add_trace_event, CreateWebRtcMediaEngineFunction* create_media_engine, - DestroyWebRtcMediaEngineFunction* destroy_media_engine) { + DestroyWebRtcMediaEngineFunction* destroy_media_engine, + InitDiagnosticLoggingDelegateFunctionFunction* + init_diagnostic_logging) { #if !defined(OS_MACOSX) && !defined(OS_ANDROID) g_alloc = alloc; g_dealloc = dealloc; @@ -60,6 +63,7 @@ bool InitializeModule(const CommandLine& command_line, *create_media_engine = &CreateWebRtcMediaEngine; *destroy_media_engine = &DestroyWebRtcMediaEngine; + *init_diagnostic_logging = &talk_base::InitDiagnosticLoggingDelegateFunction; if (CommandLine::Init(0, NULL)) { #if !defined(OS_WIN) diff --git a/chromium/third_party/libjingle/overrides/talk/base/logging.cc b/chromium/third_party/libjingle/overrides/talk/base/logging.cc index e3acfc39e74..3e471817d73 100644 --- a/chromium/third_party/libjingle/overrides/talk/base/logging.cc +++ b/chromium/third_party/libjingle/overrides/talk/base/logging.cc @@ -31,6 +31,8 @@ namespace talk_base { void (*g_logging_delegate_function)(const std::string&) = NULL; +void (*g_extra_logging_init_function)( + void (*logging_delegate_function)(const std::string&)) = NULL; #ifndef NDEBUG COMPILE_ASSERT(sizeof(base::subtle::Atomic32) == sizeof(base::PlatformThreadId), atomic32_not_same_size_as_platformthreadid); @@ -308,6 +310,16 @@ void InitDiagnosticLoggingDelegateFunction( IPAddress::set_strip_sensitive(true); #endif g_logging_delegate_function = delegate; + + if (g_extra_logging_init_function) + g_extra_logging_init_function(delegate); +} + +void SetExtraLoggingInit( + void (*function)(void (*delegate)(const std::string&))) { + CHECK(function); + CHECK(!g_extra_logging_init_function); + g_extra_logging_init_function = function; } } // namespace talk_base diff --git a/chromium/third_party/libjingle/overrides/talk/base/logging.h b/chromium/third_party/libjingle/overrides/talk/base/logging.h index cbc9317b7ae..a474985ce71 100644 --- a/chromium/third_party/libjingle/overrides/talk/base/logging.h +++ b/chromium/third_party/libjingle/overrides/talk/base/logging.h @@ -151,9 +151,14 @@ void LogMultiline(LoggingSeverity level, const char* label, bool input, const void* data, size_t len, bool hex_mode, LogMultilineState* state); +// TODO(grunell): Change name to InitDiagnosticLoggingDelegate or +// InitDiagnosticLogging. Change also in init_webrtc.h/cc. +// TODO(grunell): typedef the delegate function. void InitDiagnosticLoggingDelegateFunction( void (*delegate)(const std::string&)); +void SetExtraLoggingInit( + void (*function)(void (*delegate)(const std::string&))); } // namespace talk_base ////////////////////////////////////////////////////////////////////// diff --git a/chromium/third_party/libjingle/source/talk/OWNERS b/chromium/third_party/libjingle/source/talk/OWNERS index b55b3a38416..ddb55cf3daa 100644 --- a/chromium/third_party/libjingle/source/talk/OWNERS +++ b/chromium/third_party/libjingle/source/talk/OWNERS @@ -7,4 +7,6 @@ mallinath@webrtc.org perkj@webrtc.org sergeyu@chromium.org tommi@webrtc.org -wu@webrtc.org
\ No newline at end of file +wu@webrtc.org +per-file *.isolate=kjellander@webrtc.org + 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 9409fd7a210..6c9e0bc43ff 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.cc @@ -28,34 +28,44 @@ #include <string> -#include "talk/app/webrtc/webrtcsession.h" +#include "talk/app/webrtc/mediastreamprovider.h" #include "talk/base/logging.h" #include "talk/base/refcount.h" +#include "talk/media/sctp/sctputils.h" namespace webrtc { static size_t kMaxQueuedReceivedDataPackets = 100; static size_t kMaxQueuedSendDataPackets = 100; +enum { + MSG_CHANNELREADY, +}; + talk_base::scoped_refptr<DataChannel> DataChannel::Create( - WebRtcSession* session, + DataChannelProviderInterface* provider, + cricket::DataChannelType dct, const std::string& label, const DataChannelInit* config) { talk_base::scoped_refptr<DataChannel> channel( - new talk_base::RefCountedObject<DataChannel>(session, label)); + new talk_base::RefCountedObject<DataChannel>(provider, dct, label)); if (!channel->Init(config)) { return NULL; } return channel; } -DataChannel::DataChannel(WebRtcSession* session, const std::string& label) +DataChannel::DataChannel( + DataChannelProviderInterface* provider, + cricket::DataChannelType dct, + const std::string& label) : label_(label), observer_(NULL), state_(kConnecting), was_ever_writable_(false), - session_(session), - data_session_(NULL), + connected_to_provider_(false), + data_channel_type_(dct), + provider_(provider), send_ssrc_set_(false), send_ssrc_(0), receive_ssrc_set_(false), @@ -63,36 +73,44 @@ DataChannel::DataChannel(WebRtcSession* session, const std::string& label) } bool DataChannel::Init(const DataChannelInit* config) { - if (config) { - if (session_->data_channel_type() == cricket::DCT_RTP && - (config->reliable || - config->id != -1 || - config->maxRetransmits != -1 || - config->maxRetransmitTime != -1)) { - LOG(LS_ERROR) << "Failed to initialize the RTP data channel due to " + if (data_channel_type_ == cricket::DCT_RTP && + (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) { + LOG(LS_ERROR) << "Failed to initialize the SCTP data channel due to " << "invalid DataChannelInit."; return false; - } else if (session_->data_channel_type() == cricket::DCT_SCTP) { - 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) { - LOG(LS_ERROR) << - "maxRetransmits and maxRetransmitTime should not be both set."; - return false; - } + } + if (config->maxRetransmits != -1 && config->maxRetransmitTime != -1) { + LOG(LS_ERROR) << + "maxRetransmits and maxRetransmitTime should not be both set."; + return false; } config_ = *config; + + // Try to connect to the transport in case the transport channel already + // exists. + OnTransportChannelCreated(); + + // Checks if the transport is ready to send because the initial channel + // ready signal may have been sent before the DataChannel creation. + // This has to be done async because the upper layer objects (e.g. + // Chrome glue and WebKit) are not wired up properly until after this + // function returns. + if (provider_->ReadyToSendData()) { + talk_base::Thread::Current()->Post(this, MSG_CHANNELREADY, NULL); + } } - return true; -} -bool DataChannel::HasNegotiationCompleted() { - return send_ssrc_set_ == receive_ssrc_set_; + return true; } DataChannel::~DataChannel() { @@ -111,7 +129,7 @@ void DataChannel::UnregisterObserver() { } bool DataChannel::reliable() const { - if (session_->data_channel_type() == cricket::DCT_RTP) { + if (data_channel_type_ == cricket::DCT_RTP) { return false; } else { return config_.maxRetransmits == -1 && @@ -164,15 +182,13 @@ void DataChannel::QueueControl(const talk_base::Buffer* buffer) { queued_control_data_.push(buffer); } -bool DataChannel::SendControl(const talk_base::Buffer* buffer) { - if (state_ != kOpen) { - QueueControl(buffer); - return true; - } - if (session_->data_channel_type() == cricket::DCT_RTP) { - delete buffer; - return false; - } +bool DataChannel::SendOpenMessage(const talk_base::Buffer* raw_buffer) { + ASSERT(data_channel_type_ == cricket::DCT_SCTP && + was_ever_writable_ && + config_.id >= 0 && + !config_.negotiated); + + talk_base::scoped_ptr<const talk_base::Buffer> buffer(raw_buffer); cricket::SendDataParams send_params; send_params.ssrc = config_.id; @@ -180,22 +196,18 @@ bool DataChannel::SendControl(const talk_base::Buffer* buffer) { send_params.type = cricket::DMT_CONTROL; cricket::SendDataResult send_result; - bool retval = session_->data_channel()->SendData( - send_params, *buffer, &send_result); + bool retval = provider_->SendData(send_params, *buffer, &send_result); if (!retval && send_result == cricket::SDR_BLOCK) { // Link is congested. Queue for later. - QueueControl(buffer); - } else { - delete buffer; + QueueControl(buffer.release()); } return retval; } void DataChannel::SetReceiveSsrc(uint32 receive_ssrc) { + ASSERT(data_channel_type_ == cricket::DCT_RTP); + if (receive_ssrc_set_) { - ASSERT(session_->data_channel_type() == cricket::DCT_RTP || - !send_ssrc_set_ || - receive_ssrc_ == send_ssrc_); return; } receive_ssrc_ = receive_ssrc; @@ -209,10 +221,8 @@ void DataChannel::RemotePeerRequestClose() { } void DataChannel::SetSendSsrc(uint32 send_ssrc) { + ASSERT(data_channel_type_ == cricket::DCT_RTP); if (send_ssrc_set_) { - ASSERT(session_->data_channel_type() == cricket::DCT_RTP || - !receive_ssrc_set_ || - receive_ssrc_ == send_ssrc_); return; } send_ssrc_ = send_ssrc; @@ -220,8 +230,16 @@ void DataChannel::SetSendSsrc(uint32 send_ssrc) { UpdateState(); } +void DataChannel::OnMessage(talk_base::Message* msg) { + switch (msg->message_id) { + case MSG_CHANNELREADY: + OnChannelReady(true); + break; + } +} + // The underlaying data engine is closing. -// This function make sure the DataChannel is disconneced and change state to +// This function makes sure the DataChannel is disconnected and changes state to // kClosed. void DataChannel::OnDataEngineClose() { DoClose(); @@ -230,7 +248,9 @@ void DataChannel::OnDataEngineClose() { void DataChannel::OnDataReceived(cricket::DataChannel* channel, const cricket::ReceiveDataParams& params, const talk_base::Buffer& payload) { - if (params.ssrc != receive_ssrc_) { + uint32 expected_ssrc = + (data_channel_type_ == cricket::DCT_RTP) ? receive_ssrc_ : config_.id; + if (params.ssrc != expected_ssrc) { return; } @@ -258,8 +278,18 @@ void DataChannel::OnChannelReady(bool writable) { // for sending and now unblocked, so send the queued data now. 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; + } + SendOpenMessage(payload); + } + UpdateState(); - DeliverQueuedControlData(); ASSERT(queued_send_data_.empty()); } else if (state_ == kOpen) { DeliverQueuedSendData(); @@ -276,11 +306,14 @@ void DataChannel::DoClose() { void DataChannel::UpdateState() { switch (state_) { case kConnecting: { - if (HasNegotiationCompleted()) { - if (!IsConnectedToDataSession()) { - ConnectToDataSession(); + if (send_ssrc_set_ == receive_ssrc_set_) { + if (data_channel_type_ == cricket::DCT_RTP && !connected_to_provider_) { + connected_to_provider_ = provider_->ConnectDataChannel(this); } if (was_ever_writable_) { + // TODO(jiayl): Do not transition to kOpen if we failed to send the + // OPEN message. + DeliverQueuedControlData(); SetState(kOpen); // If we have received buffers before the channel got writable. // Deliver them now. @@ -293,10 +326,9 @@ void DataChannel::UpdateState() { break; } case kClosing: { - if (IsConnectedToDataSession()) { - DisconnectFromDataSession(); - } - if (HasNegotiationCompleted()) { + DisconnectFromTransport(); + + if (!send_ssrc_set_ && !receive_ssrc_set_) { SetState(kClosed); } break; @@ -313,29 +345,16 @@ void DataChannel::SetState(DataState state) { } } -void DataChannel::ConnectToDataSession() { - if (!session_->data_channel()) { - LOG(LS_ERROR) << "The DataEngine does not exist."; - ASSERT(session_->data_channel() != NULL); +void DataChannel::DisconnectFromTransport() { + if (!connected_to_provider_) return; - } - data_session_ = session_->data_channel(); - data_session_->SignalReadyToSendData.connect(this, - &DataChannel::OnChannelReady); - data_session_->SignalDataReceived.connect(this, &DataChannel::OnDataReceived); - cricket::StreamParams params = - cricket::StreamParams::CreateLegacy(id()); - data_session_->AddRecvStream(params); - data_session_->AddSendStream(params); -} + provider_->DisconnectDataChannel(this); + connected_to_provider_ = false; -void DataChannel::DisconnectFromDataSession() { - data_session_->RemoveSendStream(id()); - data_session_->RemoveRecvStream(id()); - data_session_->SignalReadyToSendData.disconnect(this); - data_session_->SignalDataReceived.disconnect(this); - data_session_ = NULL; + if (data_channel_type_ == cricket::DCT_SCTP) { + provider_->RemoveSctpDataStream(config_.id); + } } void DataChannel::DeliverQueuedReceivedData() { @@ -360,10 +379,12 @@ void DataChannel::ClearQueuedReceivedData() { } void DataChannel::DeliverQueuedSendData() { + ASSERT(was_ever_writable_ && state_ == kOpen); + + // TODO(jiayl): Sending OPEN message here contradicts with the pre-condition + // that the readyState is open. According to the standard, the channel should + // not become open before the OPEN message is sent. DeliverQueuedControlData(); - if (!was_ever_writable_) { - return; - } while (!queued_send_data_.empty()) { DataBuffer* buffer = queued_send_data_.front(); @@ -387,12 +408,11 @@ void DataChannel::ClearQueuedControlData() { } void DataChannel::DeliverQueuedControlData() { - if (was_ever_writable_) { - while (!queued_control_data_.empty()) { - const talk_base::Buffer *buf = queued_control_data_.front(); - queued_control_data_.pop(); - SendControl(buf); - } + ASSERT(was_ever_writable_); + while (!queued_control_data_.empty()) { + const talk_base::Buffer* buf = queued_control_data_.front(); + queued_control_data_.pop(); + SendOpenMessage(buf); } } @@ -408,16 +428,17 @@ bool DataChannel::InternalSendWithoutQueueing( const DataBuffer& buffer, cricket::SendDataResult* send_result) { cricket::SendDataParams send_params; - send_params.ssrc = send_ssrc_; - if (session_->data_channel_type() == cricket::DCT_SCTP) { + if (data_channel_type_ == cricket::DCT_SCTP) { send_params.ordered = config_.ordered; send_params.max_rtx_count = config_.maxRetransmits; send_params.max_rtx_ms = config_.maxRetransmitTime; + send_params.ssrc = config_.id; + } else { + send_params.ssrc = send_ssrc_; } send_params.type = buffer.binary ? cricket::DMT_BINARY : cricket::DMT_TEXT; - return session_->data_channel()->SendData(send_params, buffer.data, - send_result); + return provider_->SendData(send_params, buffer.data, send_result); } bool DataChannel::QueueSendData(const DataBuffer& buffer) { @@ -429,4 +450,22 @@ bool DataChannel::QueueSendData(const DataBuffer& buffer) { return true; } +void DataChannel::SetSctpSid(int sid) { + ASSERT(config_.id < 0 && sid >= 0 && data_channel_type_ == cricket::DCT_SCTP); + config_.id = sid; + provider_->AddSctpDataStream(sid); +} + +void DataChannel::OnTransportChannelCreated() { + ASSERT(data_channel_type_ == cricket::DCT_SCTP); + if (!connected_to_provider_) { + connected_to_provider_ = provider_->ConnectDataChannel(this); + } + // The sid may have been unassigned when provider_->ConnectDataChannel was + // done. So always add the streams even if connected_to_provider_ is true. + if (config_.id >= 0) { + provider_->AddSctpDataStream(config_.id); + } +} + } // namespace webrtc 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 3ce3c1b5b4e..bf31aed5d53 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/datachannel.h @@ -33,22 +33,45 @@ #include "talk/app/webrtc/datachannelinterface.h" #include "talk/app/webrtc/proxy.h" +#include "talk/base/messagehandler.h" #include "talk/base/scoped_ref_ptr.h" #include "talk/base/sigslot.h" +#include "talk/media/base/mediachannel.h" #include "talk/session/media/channel.h" namespace webrtc { -class WebRtcSession; +class DataChannel; + +class DataChannelProviderInterface { + public: + // Sends the data to the transport. + virtual bool SendData(const cricket::SendDataParams& params, + const talk_base::Buffer& payload, + cricket::SendDataResult* result) = 0; + // Connects to the transport signals. + virtual bool ConnectDataChannel(DataChannel* data_channel) = 0; + // Disconnects from the transport signals. + virtual void DisconnectDataChannel(DataChannel* data_channel) = 0; + // Adds the data channel SID to the transport for SCTP. + virtual void AddSctpDataStream(uint32 sid) = 0; + // Removes the data channel SID from the transport for SCTP. + virtual void RemoveSctpDataStream(uint32 sid) = 0; + // Returns true if the transport channel is ready to send data. + virtual bool ReadyToSendData() const = 0; + + protected: + virtual ~DataChannelProviderInterface() {} +}; // DataChannel is a an implementation of the DataChannelInterface based on -// libjingle's data engine. It provides an implementation of unreliable data -// channels. Currently this class is specifically designed to use RtpDataEngine, -// and will changed to use SCTP in the future. +// libjingle's data engine. It provides an implementation of unreliable or +// reliabledata channels. Currently this class is specifically designed to use +// both RtpDataEngine and SctpDataEngine. // DataChannel states: -// kConnecting: The channel has been created but SSRC for sending and receiving -// has not yet been set and the transport might not yet be ready. +// kConnecting: The channel has been created the transport might not yet be +// ready. // kOpen: The channel have a local SSRC set by a call to UpdateSendSsrc // and a remote SSRC set by call to UpdateReceiveSsrc and the transport // has been writable once. @@ -57,10 +80,12 @@ class WebRtcSession; // kClosed: Both UpdateReceiveSsrc and UpdateSendSsrc has been called with // SSRC==0. class DataChannel : public DataChannelInterface, - public sigslot::has_slots<> { + public sigslot::has_slots<>, + public talk_base::MessageHandler { public: static talk_base::scoped_refptr<DataChannel> Create( - WebRtcSession* session, + DataChannelProviderInterface* provider, + cricket::DataChannelType dct, const std::string& label, const DataChannelInit* config); @@ -83,20 +108,9 @@ class DataChannel : public DataChannelInterface, virtual void Close(); virtual DataState state() const { return state_; } virtual bool Send(const DataBuffer& buffer); - // Send a control message right now, or queue for later. - virtual bool SendControl(const talk_base::Buffer* buffer); - void ConnectToDataSession(); - - // Set the SSRC this channel should use to receive data from the - // underlying data engine. - void SetReceiveSsrc(uint32 receive_ssrc); - // The remote peer request that this channel should be closed. - void RemotePeerRequestClose(); - // Set the SSRC this channel should use to send data on the - // underlying data engine. |send_ssrc| == 0 means that the channel is no - // longer part of the session negotiation. - void SetSendSsrc(uint32 send_ssrc); + // talk_base::MessageHandler override. + virtual void OnMessage(talk_base::Message* msg); // Called if the underlying data engine is closing. void OnDataEngineClose(); @@ -105,24 +119,48 @@ class DataChannel : public DataChannelInterface, // underlying DataMediaChannel becomes ready, or when this channel is a new // stream on an existing DataMediaChannel, and we've finished negotiation. void OnChannelReady(bool writable); - protected: - DataChannel(WebRtcSession* session, const std::string& label); - virtual ~DataChannel(); - - bool Init(const DataChannelInit* config); - bool HasNegotiationCompleted(); // Sigslots from cricket::DataChannel void OnDataReceived(cricket::DataChannel* channel, const cricket::ReceiveDataParams& params, const talk_base::Buffer& payload); + // The remote peer request that this channel should be closed. + void RemotePeerRequestClose(); + + // The following methods are for SCTP only. + + // Sets the SCTP sid and adds to transport layer if not set yet. + void SetSctpSid(int sid); + // Called when the transport channel is created. + void OnTransportChannelCreated(); + + // The following methods are for RTP only. + + // Set the SSRC this channel should use to send data on the + // underlying data engine. |send_ssrc| == 0 means that the channel is no + // longer part of the session negotiation. + void SetSendSsrc(uint32 send_ssrc); + // Set the SSRC this channel should use to receive data from the + // underlying data engine. + void SetReceiveSsrc(uint32 receive_ssrc); + + cricket::DataChannelType data_channel_type() const { + return data_channel_type_; + } + + protected: + DataChannel(DataChannelProviderInterface* client, + cricket::DataChannelType dct, + const std::string& label); + virtual ~DataChannel(); + private: + bool Init(const DataChannelInit* config); void DoClose(); void UpdateState(); void SetState(DataState state); - void DisconnectFromDataSession(); - bool IsConnectedToDataSession() { return data_session_ != NULL; } + void DisconnectFromTransport(); void DeliverQueuedControlData(); void QueueControl(const talk_base::Buffer* buffer); void ClearQueuedControlData(); @@ -133,14 +171,17 @@ class DataChannel : public DataChannelInterface, bool InternalSendWithoutQueueing(const DataBuffer& buffer, cricket::SendDataResult* send_result); bool QueueSendData(const DataBuffer& buffer); + bool SendOpenMessage(const talk_base::Buffer* buffer); + std::string label_; DataChannelInit config_; DataChannelObserver* observer_; DataState state_; bool was_ever_writable_; - WebRtcSession* session_; - cricket::DataChannel* data_session_; + bool connected_to_provider_; + cricket::DataChannelType data_channel_type_; + DataChannelProviderInterface* provider_; bool send_ssrc_set_; uint32 send_ssrc_; bool receive_ssrc_set_; 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 2b0a9fe5a65..fdcd2f2cd11 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,118 +26,96 @@ */ #include "talk/app/webrtc/datachannel.h" -#include "talk/app/webrtc/jsep.h" -#include "talk/app/webrtc/mediastreamsignaling.h" -#include "talk/app/webrtc/test/fakeconstraints.h" -#include "talk/app/webrtc/test/fakedtlsidentityservice.h" -#include "talk/app/webrtc/webrtcsession.h" +#include "talk/app/webrtc/test/fakedatachannelprovider.h" #include "talk/base/gunit.h" -#include "talk/media/base/fakemediaengine.h" -#include "talk/media/devices/fakedevicemanager.h" -#include "talk/session/media/channelmanager.h" +#include "testing/base/public/gmock.h" -using webrtc::CreateSessionDescriptionObserver; -using webrtc::MediaConstraintsInterface; -using webrtc::SessionDescriptionInterface; +using webrtc::DataChannel; -const uint32 kFakeSsrc = 1; - -class CreateSessionDescriptionObserverForTest - : public talk_base::RefCountedObject<CreateSessionDescriptionObserver> { +class FakeDataChannelObserver : public webrtc::DataChannelObserver { public: - virtual void OnSuccess(SessionDescriptionInterface* desc) { - description_.reset(desc); - } - virtual void OnFailure(const std::string& error) {} - - SessionDescriptionInterface* description() { return description_.get(); } - - SessionDescriptionInterface* ReleaseDescription() { - return description_.release(); - } - - protected: - ~CreateSessionDescriptionObserverForTest() {} - - private: - talk_base::scoped_ptr<SessionDescriptionInterface> description_; + MOCK_METHOD0(OnStateChange, void()); + MOCK_METHOD1(OnMessage, void(const webrtc::DataBuffer& buffer)); }; class SctpDataChannelTest : public testing::Test { protected: SctpDataChannelTest() - : media_engine_(new cricket::FakeMediaEngine), - data_engine_(new cricket::FakeDataEngine), - channel_manager_( - new cricket::ChannelManager(media_engine_, - data_engine_, - new cricket::FakeDeviceManager(), - new cricket::CaptureManager(), - talk_base::Thread::Current())), - media_stream_signaling_( - new webrtc::MediaStreamSignaling(talk_base::Thread::Current(), - NULL, channel_manager_.get())), - session_(channel_manager_.get(), - talk_base::Thread::Current(), - talk_base::Thread::Current(), - NULL, - media_stream_signaling_.get()), - webrtc_data_channel_(NULL) {} - - virtual void SetUp() { - if (!talk_base::SSLStreamAdapter::HaveDtlsSrtp()) { - return; - } - channel_manager_->Init(); - webrtc::FakeConstraints constraints; - constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true); - constraints.AddMandatory(MediaConstraintsInterface::kEnableSctpDataChannels, - true); - ASSERT_TRUE(session_.Initialize(&constraints, - new FakeIdentityService())); - webrtc_data_channel_ = webrtc::DataChannel::Create(&session_, "test", NULL); - ASSERT_TRUE(media_stream_signaling_->AddDataChannel(webrtc_data_channel_)); - - talk_base::scoped_refptr<CreateSessionDescriptionObserverForTest> observer - = new CreateSessionDescriptionObserverForTest(); - session_.CreateOffer(observer.get(), NULL); - EXPECT_TRUE_WAIT(observer->description() != NULL, 2000); - ASSERT_TRUE(observer->description() != NULL); - ASSERT_TRUE(session_.SetLocalDescription(observer->ReleaseDescription(), - NULL)); - // Connect to the media channel. - webrtc_data_channel_->SetSendSsrc(kFakeSsrc); - webrtc_data_channel_->SetReceiveSsrc(kFakeSsrc); - session_.data_channel()->SignalReadyToSendData(true); + : webrtc_data_channel_( + DataChannel::Create(&provider_, cricket::DCT_SCTP, "test", &init_)) { } - void SetSendBlocked(bool blocked) { - bool was_blocked = data_engine_->GetChannel(0)->is_send_blocked(); - data_engine_->GetChannel(0)->set_send_blocked(blocked); - if (!blocked && was_blocked) { - session_.data_channel()->SignalReadyToSendData(true); + void SetChannelReady() { + provider_.set_transport_available(true); + webrtc_data_channel_->OnTransportChannelCreated(); + if (webrtc_data_channel_->id() < 0) { + webrtc_data_channel_->SetSctpSid(0); } + provider_.set_ready_to_send(true); + } + + void AddObserver() { + observer_.reset(new FakeDataChannelObserver()); + webrtc_data_channel_->RegisterObserver(observer_.get()); } - cricket::FakeMediaEngine* media_engine_; - cricket::FakeDataEngine* data_engine_; - talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_; - talk_base::scoped_ptr<webrtc::MediaStreamSignaling> media_stream_signaling_; - webrtc::WebRtcSession session_; - talk_base::scoped_refptr<webrtc::DataChannel> webrtc_data_channel_; + + webrtc::DataChannelInit init_; + FakeDataChannelProvider provider_; + talk_base::scoped_ptr<FakeDataChannelObserver> observer_; + talk_base::scoped_refptr<DataChannel> webrtc_data_channel_; }; +// Verifies that the data channel is connected to the transport after creation. +TEST_F(SctpDataChannelTest, ConnectedToTransportOnCreated) { + provider_.set_transport_available(true); + talk_base::scoped_refptr<DataChannel> dc = DataChannel::Create( + &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. + EXPECT_FALSE(provider_.IsSendStreamAdded(dc->id())); + EXPECT_FALSE(provider_.IsRecvStreamAdded(dc->id())); + + dc->SetSctpSid(0); + EXPECT_TRUE(provider_.IsSendStreamAdded(dc->id())); + EXPECT_TRUE(provider_.IsRecvStreamAdded(dc->id())); +} + +// Verifies that the data channel is connected to the transport if the transport +// is not available initially and becomes available later. +TEST_F(SctpDataChannelTest, ConnectedAfterTransportBecomesAvailable) { + EXPECT_FALSE(provider_.IsConnected(webrtc_data_channel_.get())); + + provider_.set_transport_available(true); + webrtc_data_channel_->OnTransportChannelCreated(); + EXPECT_TRUE(provider_.IsConnected(webrtc_data_channel_.get())); +} + +// Tests the state of the data channel. +TEST_F(SctpDataChannelTest, StateTransition) { + EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, + webrtc_data_channel_->state()); + SetChannelReady(); + + EXPECT_EQ(webrtc::DataChannelInterface::kOpen, webrtc_data_channel_->state()); + webrtc_data_channel_->Close(); + EXPECT_EQ(webrtc::DataChannelInterface::kClosed, + webrtc_data_channel_->state()); + // Verifies that it's disconnected from the transport. + EXPECT_FALSE(provider_.IsConnected(webrtc_data_channel_.get())); +} + // Tests that DataChannel::buffered_amount() is correct after the channel is // blocked. TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) { - if (!talk_base::SSLStreamAdapter::HaveDtlsSrtp()) { - return; - } + SetChannelReady(); webrtc::DataBuffer buffer("abcd"); EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount()); - SetSendBlocked(true); + provider_.set_send_blocked(true); + const int number_of_packets = 3; for (int i = 0; i < number_of_packets; ++i) { EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); @@ -149,13 +127,75 @@ TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) { // Tests that the queued data are sent when the channel transitions from blocked // to unblocked. TEST_F(SctpDataChannelTest, QueuedDataSentWhenUnblocked) { - if (!talk_base::SSLStreamAdapter::HaveDtlsSrtp()) { - return; - } + SetChannelReady(); webrtc::DataBuffer buffer("abcd"); - SetSendBlocked(true); + provider_.set_send_blocked(true); EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); - SetSendBlocked(false); + provider_.set_send_blocked(false); + SetChannelReady(); EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount()); } + +// Tests that the queued control message is sent when channel is ready. +TEST_F(SctpDataChannelTest, OpenMessageSent) { + // Initially the id is unassigned. + EXPECT_EQ(-1, webrtc_data_channel_->id()); + + SetChannelReady(); + EXPECT_GE(webrtc_data_channel_->id(), 0); + EXPECT_EQ(cricket::DMT_CONTROL, provider_.last_send_data_params().type); + EXPECT_EQ(provider_.last_send_data_params().ssrc, + static_cast<uint32>(webrtc_data_channel_->id())); +} + +// Tests that the DataChannel created after transport gets ready can enter OPEN +// state. +TEST_F(SctpDataChannelTest, LateCreatedChannelTransitionToOpen) { + SetChannelReady(); + webrtc::DataChannelInit init; + init.id = 1; + 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 messages are sent with the right ssrc. +TEST_F(SctpDataChannelTest, SendDataSsrc) { + webrtc_data_channel_->SetSctpSid(1); + SetChannelReady(); + webrtc::DataBuffer buffer("data"); + EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); + EXPECT_EQ(1U, provider_.last_send_data_params().ssrc); +} + +// Tests that the incoming messages with wrong ssrcs are rejected. +TEST_F(SctpDataChannelTest, ReceiveDataWithInvalidSsrc) { + webrtc_data_channel_->SetSctpSid(1); + 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); +} + +// Tests that the incoming messages with right ssrcs are acceted. +TEST_F(SctpDataChannelTest, ReceiveDataWithValidSsrc) { + webrtc_data_channel_->SetSctpSid(1); + 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); +} 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 3663aace528..2cd472a5a01 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource.cc @@ -53,7 +53,9 @@ const char MediaConstraintsInterface::kHighpassFilter[] = "googHighpassFilter"; const char MediaConstraintsInterface::kTypingNoiseDetection[] = "googTypingNoiseDetection"; -const char MediaConstraintsInterface::kInternalAecDump[] = "internalAecDump"; +const char MediaConstraintsInterface::kAudioMirroring[] = "googAudioMirroring"; +// TODO(perkj): Remove kInternalAecDump once its not used by Chrome. +const char MediaConstraintsInterface::kInternalAecDump[] = "deprecatedAecDump"; namespace { @@ -90,10 +92,10 @@ bool FromConstraints(const MediaConstraintsInterface::Constraints& constraints, options->noise_suppression.Set(value); else if (iter->key == MediaConstraintsInterface::kHighpassFilter) options->highpass_filter.Set(value); - else if (iter->key == MediaConstraintsInterface::kInternalAecDump) - options->aec_dump.Set(value); else if (iter->key == MediaConstraintsInterface::kTypingNoiseDetection) options->typing_detection.Set(value); + else if (iter->key == MediaConstraintsInterface::kAudioMirroring) + options->stereo_swapping.Set(value); else success = false; } @@ -103,14 +105,16 @@ bool FromConstraints(const MediaConstraintsInterface::Constraints& constraints, } // namespace talk_base::scoped_refptr<LocalAudioSource> LocalAudioSource::Create( + const PeerConnectionFactoryInterface::Options& options, const MediaConstraintsInterface* constraints) { talk_base::scoped_refptr<LocalAudioSource> source( new talk_base::RefCountedObject<LocalAudioSource>()); - source->Initialize(constraints); + source->Initialize(options, constraints); return source; } void LocalAudioSource::Initialize( + const PeerConnectionFactoryInterface::Options& options, const MediaConstraintsInterface* constraints) { if (!constraints) return; @@ -119,12 +123,14 @@ void LocalAudioSource::Initialize( // constraints. FromConstraints(constraints->GetOptional(), &options_); - cricket::AudioOptions options; - if (!FromConstraints(constraints->GetMandatory(), &options)) { + cricket::AudioOptions audio_options; + if (!FromConstraints(constraints->GetMandatory(), &audio_options)) { source_state_ = kEnded; return; } - options_.SetAll(options); + 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/localaudiosource.h b/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource.h index e0fda03d7f1..fb769ed621d 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource.h @@ -30,6 +30,7 @@ #include "talk/app/webrtc/mediastreaminterface.h" #include "talk/app/webrtc/notifier.h" +#include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/base/scoped_ptr.h" #include "talk/media/base/mediachannel.h" @@ -44,6 +45,7 @@ class LocalAudioSource : public Notifier<AudioSourceInterface> { public: // Creates an instance of LocalAudioSource. static talk_base::scoped_refptr<LocalAudioSource> Create( + const PeerConnectionFactoryInterface::Options& options, const MediaConstraintsInterface* constraints); virtual SourceState state() const { return source_state_; } @@ -58,7 +60,8 @@ class LocalAudioSource : public Notifier<AudioSourceInterface> { } private: - void Initialize(const MediaConstraintsInterface* constraints); + void Initialize(const PeerConnectionFactoryInterface::Options& options, + const MediaConstraintsInterface* constraints); cricket::AudioOptions options_; SourceState source_state_; diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource_unittest.cc b/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource_unittest.cc index ae077878d41..f8880e0f691 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/localaudiosource_unittest.cc @@ -39,6 +39,7 @@ using webrtc::LocalAudioSource; using webrtc::MediaConstraintsInterface; using webrtc::MediaSourceInterface; +using webrtc::PeerConnectionFactoryInterface; TEST(LocalAudioSourceTest, SetValidOptions) { webrtc::FakeConstraints constraints; @@ -52,7 +53,8 @@ TEST(LocalAudioSourceTest, SetValidOptions) { constraints.AddOptional(MediaConstraintsInterface::kHighpassFilter, true); talk_base::scoped_refptr<LocalAudioSource> source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); bool value; EXPECT_TRUE(source->options().echo_cancellation.Get(&value)); @@ -72,7 +74,8 @@ TEST(LocalAudioSourceTest, SetValidOptions) { TEST(LocalAudioSourceTest, OptionNotSet) { webrtc::FakeConstraints constraints; talk_base::scoped_refptr<LocalAudioSource> source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); bool value; EXPECT_FALSE(source->options().highpass_filter.Get(&value)); } @@ -83,7 +86,8 @@ TEST(LocalAudioSourceTest, MandatoryOverridesOptional) { constraints.AddOptional(MediaConstraintsInterface::kEchoCancellation, true); talk_base::scoped_refptr<LocalAudioSource> source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); bool value; EXPECT_TRUE(source->options().echo_cancellation.Get(&value)); @@ -96,7 +100,8 @@ TEST(LocalAudioSourceTest, InvalidOptional) { constraints.AddOptional("invalidKey", false); talk_base::scoped_refptr<LocalAudioSource> source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); EXPECT_EQ(MediaSourceInterface::kLive, source->state()); bool value; @@ -110,7 +115,8 @@ TEST(LocalAudioSourceTest, InvalidMandatory) { constraints.AddMandatory("invalidKey", false); talk_base::scoped_refptr<LocalAudioSource> source = - LocalAudioSource::Create(&constraints); + LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(), + &constraints); EXPECT_EQ(MediaSourceInterface::kEnded, source->state()); bool value; 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 bc3872ce472..ba6b09be910 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediaconstraintsinterface.h @@ -81,6 +81,7 @@ class MediaConstraintsInterface { static const char kNoiseSuppression[]; // googNoiseSuppression static const char kHighpassFilter[]; // googHighpassFilter static const char kTypingNoiseDetection[]; // googTypingNoiseDetection + static const char kAudioMirroring[]; // googAudioMirroring // Google-specific constraint keys for a local video source static const char kNoiseReduction[]; // googNoiseReduction @@ -106,15 +107,21 @@ class MediaConstraintsInterface { 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 + // Temporary pseudo-constraint for enabling DSCP through JS. + static const char kEnableDscp[]; // The prefix of internal-only constraints whose JS set values should be // stripped by Chrome before passed down to Libjingle. static const char kInternalConstraintPrefix[]; - // This constraint is for internal use only, representing the Chrome command - // line flag. So it is prefixed with "internal" so JS values will be removed. + // 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: 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 a23799f7369..7586938ce60 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.cc @@ -37,6 +37,7 @@ #include "talk/app/webrtc/videosource.h" #include "talk/app/webrtc/videotrack.h" #include "talk/base/bytebuffer.h" +#include "talk/base/stringutils.h" #include "talk/media/sctp/sctpdataengine.h" static const char kDefaultStreamLabel[] = "default"; @@ -189,7 +190,8 @@ MediaStreamSignaling::MediaStreamSignaling( remote_streams_(StreamCollection::Create()), remote_stream_factory_(new RemoteMediaStreamFactory(signaling_thread, channel_manager)), - last_allocated_sctp_id_(0) { + last_allocated_sctp_even_sid_(-2), + last_allocated_sctp_odd_sid_(-1) { options_.has_video = false; options_.has_audio = false; } @@ -203,51 +205,58 @@ void MediaStreamSignaling::TearDown() { OnDataChannelClose(); } -bool MediaStreamSignaling::IsSctpIdAvailable(int id) const { - if (id < 0 || id > static_cast<int>(cricket::kMaxSctpSid)) +bool MediaStreamSignaling::IsSctpSidAvailable(int sid) const { + if (sid < 0 || sid > static_cast<int>(cricket::kMaxSctpSid)) return false; - for (DataChannels::const_iterator iter = data_channels_.begin(); - iter != data_channels_.end(); + for (SctpDataChannels::const_iterator iter = sctp_data_channels_.begin(); + iter != sctp_data_channels_.end(); ++iter) { - if (iter->second->id() == id) { + if ((*iter)->id() == sid) { return false; } } return true; } -// Gets the first id that has not been taken by existing data -// channels. Starting from 1. -// Returns false if no id can be allocated. -// TODO(jiayl): Update to some kind of even/odd random number selection when the -// rules are fully standardized. -bool MediaStreamSignaling::AllocateSctpId(int* id) { +// Gets the first unused odd/even id based on the DTLS role. If |role| is +// SSL_CLIENT, the allocated id starts from 0 and takes even numbers; otherwise, +// the id starts from 1 and takes odd numbers. Returns false if no id can be +// allocated. +bool MediaStreamSignaling::AllocateSctpSid(talk_base::SSLRole role, int* sid) { + int& last_id = (role == talk_base::SSL_CLIENT) ? + last_allocated_sctp_even_sid_ : last_allocated_sctp_odd_sid_; + do { - last_allocated_sctp_id_++; - } while (last_allocated_sctp_id_ <= static_cast<int>(cricket::kMaxSctpSid) && - !IsSctpIdAvailable(last_allocated_sctp_id_)); + last_id += 2; + } while (last_id <= static_cast<int>(cricket::kMaxSctpSid) && + !IsSctpSidAvailable(last_id)); - if (last_allocated_sctp_id_ > static_cast<int>(cricket::kMaxSctpSid)) { - last_allocated_sctp_id_ = cricket::kMaxSctpSid; + if (last_id > static_cast<int>(cricket::kMaxSctpSid)) { return false; } - *id = last_allocated_sctp_id_; + *sid = last_id; return true; } bool MediaStreamSignaling::HasDataChannels() const { - return !data_channels_.empty(); + return !rtp_data_channels_.empty() || !sctp_data_channels_.empty(); } bool MediaStreamSignaling::AddDataChannel(DataChannel* data_channel) { ASSERT(data_channel != NULL); - if (data_channels_.find(data_channel->label()) != data_channels_.end()) { - LOG(LS_ERROR) << "DataChannel with label " << data_channel->label() - << " already exists."; - return false; + if (data_channel->data_channel_type() == cricket::DCT_RTP) { + if (rtp_data_channels_.find(data_channel->label()) != + rtp_data_channels_.end()) { + LOG(LS_ERROR) << "DataChannel with label " << data_channel->label() + << " already exists."; + return false; + } + rtp_data_channels_[data_channel->label()] = data_channel; + } else { + ASSERT(data_channel->data_channel_type() == cricket::DCT_SCTP); + sctp_data_channels_.push_back(data_channel); } - data_channels_[data_channel->label()] = data_channel; return true; } @@ -259,18 +268,14 @@ bool MediaStreamSignaling::AddDataChannelFromOpenMessage( << "are not supported."; return false; } - - if (data_channels_.find(label) != data_channels_.end()) { - LOG(LS_ERROR) << "DataChannel with label " << label - << " already exists."; - return false; - } scoped_refptr<DataChannel> channel( data_channel_factory_->CreateDataChannel(label, &config)); - data_channels_[label] = channel; + if (!channel.get()) { + LOG(LS_ERROR) << "Failed to create DataChannel from the OPEN message."; + return false; + } + sctp_data_channels_.push_back(channel); stream_observer_->OnAddDataChannel(channel); - // It's immediately ready to use. - channel->OnChannelReady(true); return true; } @@ -388,9 +393,8 @@ void MediaStreamSignaling::OnRemoteDescriptionChanged( const cricket::DataContentDescription* data_desc = static_cast<const cricket::DataContentDescription*>( data_content->description); - if (data_desc->protocol() == cricket::kMediaProtocolDtlsSctp) { - UpdateRemoteSctpDataChannels(); - } else { + if (talk_base::starts_with( + data_desc->protocol().data(), cricket::kMediaProtocolRtpPrefix)) { UpdateRemoteRtpDataChannels(data_desc->streams()); } } @@ -444,9 +448,8 @@ void MediaStreamSignaling::OnLocalDescriptionChanged( const cricket::DataContentDescription* data_desc = static_cast<const cricket::DataContentDescription*>( data_content->description); - if (data_desc->protocol() == cricket::kMediaProtocolDtlsSctp) { - UpdateLocalSctpDataChannels(); - } else { + if (talk_base::starts_with( + data_desc->protocol().data(), cricket::kMediaProtocolRtpPrefix)) { UpdateLocalRtpDataChannels(data_desc->streams()); } } @@ -461,10 +464,13 @@ void MediaStreamSignaling::OnVideoChannelClose() { } void MediaStreamSignaling::OnDataChannelClose() { - DataChannels::iterator it = data_channels_.begin(); - for (; it != data_channels_.end(); ++it) { - DataChannel* data_channel = it->second; - data_channel->OnDataEngineClose(); + RtpDataChannels::iterator it1 = rtp_data_channels_.begin(); + for (; it1 != rtp_data_channels_.end(); ++it1) { + it1->second->OnDataEngineClose(); + } + SctpDataChannels::iterator it2 = sctp_data_channels_.begin(); + for (; it2 != sctp_data_channels_.end(); ++it2) { + (*it2)->OnDataEngineClose(); } } @@ -522,8 +528,8 @@ void MediaStreamSignaling::UpdateSessionOptions() { } // Check for data channels. - DataChannels::const_iterator data_channel_it = data_channels_.begin(); - for (; data_channel_it != data_channels_.end(); ++data_channel_it) { + RtpDataChannels::const_iterator data_channel_it = rtp_data_channels_.begin(); + for (; data_channel_it != rtp_data_channels_.end(); ++data_channel_it) { const DataChannel* channel = data_channel_it->second; if (channel->state() == DataChannel::kConnecting || channel->state() == DataChannel::kOpen) { @@ -840,8 +846,9 @@ void MediaStreamSignaling::UpdateLocalRtpDataChannels( // For MediaStreams, the sync_label is the MediaStream label and the // track label is the same as |streamid|. const std::string& channel_label = it->sync_label; - DataChannels::iterator data_channel_it = data_channels_.find(channel_label); - if (!VERIFY(data_channel_it != data_channels_.end())) { + RtpDataChannels::iterator data_channel_it = + rtp_data_channels_.find(channel_label); + if (!VERIFY(data_channel_it != rtp_data_channels_.end())) { continue; } // Set the SSRC the data channel should use for sending. @@ -863,9 +870,9 @@ void MediaStreamSignaling::UpdateRemoteRtpDataChannels( // does not exist. Ex a=ssrc:444330170 mslabel:test1. std::string label = it->sync_label.empty() ? talk_base::ToString(it->first_ssrc()) : it->sync_label; - DataChannels::iterator data_channel_it = - data_channels_.find(label); - if (data_channel_it == data_channels_.end()) { + RtpDataChannels::iterator data_channel_it = + rtp_data_channels_.find(label); + if (data_channel_it == rtp_data_channels_.end()) { // This is a new data channel. CreateRemoteDataChannel(label, it->first_ssrc()); } else { @@ -879,8 +886,8 @@ void MediaStreamSignaling::UpdateRemoteRtpDataChannels( void MediaStreamSignaling::UpdateClosingDataChannels( const std::vector<std::string>& active_channels, bool is_local_update) { - DataChannels::iterator it = data_channels_.begin(); - while (it != data_channels_.end()) { + RtpDataChannels::iterator it = rtp_data_channels_.begin(); + while (it != rtp_data_channels_.end()) { DataChannel* data_channel = it->second; if (std::find(active_channels.begin(), active_channels.end(), data_channel->label()) != active_channels.end()) { @@ -894,8 +901,8 @@ void MediaStreamSignaling::UpdateClosingDataChannels( data_channel->RemotePeerRequestClose(); if (data_channel->state() == DataChannel::kClosed) { - data_channels_.erase(it); - it = data_channels_.begin(); + rtp_data_channels_.erase(it); + it = rtp_data_channels_.begin(); } else { ++it; } @@ -911,163 +918,34 @@ void MediaStreamSignaling::CreateRemoteDataChannel(const std::string& label, } scoped_refptr<DataChannel> channel( data_channel_factory_->CreateDataChannel(label, NULL)); + if (!channel.get()) { + LOG(LS_WARNING) << "Remote peer requested a DataChannel but" + << "CreateDataChannel failed."; + return; + } channel->SetReceiveSsrc(remote_ssrc); stream_observer_->OnAddDataChannel(channel); } - -// Format defined at -// http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04 -const uint8 DATA_CHANNEL_OPEN_MESSAGE_TYPE = 0x03; - -enum DataChannelOpenMessageChannelType { - DCOMCT_ORDERED_RELIABLE = 0x00, - DCOMCT_ORDERED_PARTIAL_RTXS = 0x01, - DCOMCT_ORDERED_PARTIAL_TIME = 0x02, - DCOMCT_UNORDERED_RELIABLE = 0x80, - DCOMCT_UNORDERED_PARTIAL_RTXS = 0x81, - DCOMCT_UNORDERED_PARTIAL_TIME = 0x82, -}; - -bool MediaStreamSignaling::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 - - talk_base::ByteBuffer buffer(payload.data(), payload.length()); - - uint8 message_type; - if (!buffer.ReadUInt8(&message_type)) { - LOG(LS_WARNING) << "Could not read OPEN message type."; - return false; - } - if (message_type != DATA_CHANNEL_OPEN_MESSAGE_TYPE) { - LOG(LS_WARNING) << "Data Channel OPEN message of unexpected type: " - << message_type; - return false; - } - - uint8 channel_type; - if (!buffer.ReadUInt8(&channel_type)) { - LOG(LS_WARNING) << "Could not read OPEN message channel type."; - return false; - } - uint16 priority; - if (!buffer.ReadUInt16(&priority)) { - LOG(LS_WARNING) << "Could not read OPEN message reliabilility prioirty."; - return false; - } - uint32 reliability_param; - if (!buffer.ReadUInt32(&reliability_param)) { - LOG(LS_WARNING) << "Could not read OPEN message reliabilility param."; - return false; +void MediaStreamSignaling::OnDataTransportCreatedForSctp() { + SctpDataChannels::iterator it = sctp_data_channels_.begin(); + for (; it != sctp_data_channels_.end(); ++it) { + (*it)->OnTransportChannelCreated(); } - uint16 label_length; - if (!buffer.ReadUInt16(&label_length)) { - LOG(LS_WARNING) << "Could not read OPEN message label length."; - return false; - } - uint16 protocol_length; - if (!buffer.ReadUInt16(&protocol_length)) { - LOG(LS_WARNING) << "Could not read OPEN message protocol length."; - return false; - } - if (!buffer.ReadString(label, (size_t) label_length)) { - LOG(LS_WARNING) << "Could not read OPEN message label"; - return false; - } - if (!buffer.ReadString(&config->protocol, protocol_length)) { - LOG(LS_WARNING) << "Could not read OPEN message protocol."; - return false; - } - - config->ordered = true; - switch (channel_type) { - case DCOMCT_UNORDERED_RELIABLE: - case DCOMCT_UNORDERED_PARTIAL_RTXS: - case DCOMCT_UNORDERED_PARTIAL_TIME: - config->ordered = false; - } - - config->maxRetransmits = -1; - config->maxRetransmitTime = -1; - switch (channel_type) { - case DCOMCT_ORDERED_PARTIAL_RTXS: - case DCOMCT_UNORDERED_PARTIAL_RTXS: - config->maxRetransmits = reliability_param; - - case DCOMCT_ORDERED_PARTIAL_TIME: - case DCOMCT_UNORDERED_PARTIAL_TIME: - config->maxRetransmitTime = reliability_param; - } - - return true; } -bool MediaStreamSignaling::WriteDataChannelOpenMessage( - const std::string& label, - const DataChannelInit& config, - talk_base::Buffer* payload) { - // Format defined at - // http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04 - // TODO(pthatcher) - - uint8 channel_type = 0; - uint32 reliability_param = 0; - uint16 priority = 0; - if (config.ordered) { - if (config.maxRetransmits > -1) { - channel_type = DCOMCT_ORDERED_PARTIAL_RTXS; - reliability_param = config.maxRetransmits; - } else if (config.maxRetransmitTime > -1) { - channel_type = DCOMCT_ORDERED_PARTIAL_TIME; - reliability_param = config.maxRetransmitTime; - } else { - channel_type = DCOMCT_ORDERED_RELIABLE; - } - } else { - if (config.maxRetransmits > -1) { - channel_type = DCOMCT_UNORDERED_PARTIAL_RTXS; - reliability_param = config.maxRetransmits; - } else if (config.maxRetransmitTime > -1) { - channel_type = DCOMCT_UNORDERED_PARTIAL_TIME; - reliability_param = config.maxRetransmitTime; - } else { - channel_type = DCOMCT_UNORDERED_RELIABLE; +void MediaStreamSignaling::OnDtlsRoleReadyForSctp(talk_base::SSLRole role) { + SctpDataChannels::iterator it = sctp_data_channels_.begin(); + for (; it != sctp_data_channels_.end(); ++it) { + if ((*it)->id() < 0) { + int sid; + if (!AllocateSctpSid(role, &sid)) { + LOG(LS_ERROR) << "Failed to allocate SCTP sid."; + continue; + } + (*it)->SetSctpSid(sid); } } - - talk_base::ByteBuffer buffer( - NULL, 20 + label.length() + config.protocol.length(), - talk_base::ByteBuffer::ORDER_NETWORK); - buffer.WriteUInt8(DATA_CHANNEL_OPEN_MESSAGE_TYPE); - buffer.WriteUInt8(channel_type); - buffer.WriteUInt16(priority); - buffer.WriteUInt32(reliability_param); - buffer.WriteUInt16(static_cast<uint16>(label.length())); - buffer.WriteUInt16(static_cast<uint16>(config.protocol.length())); - buffer.WriteString(label); - buffer.WriteString(config.protocol); - payload->SetData(buffer.Data(), buffer.Length()); - return true; -} - -void MediaStreamSignaling::UpdateLocalSctpDataChannels() { - DataChannels::iterator it = data_channels_.begin(); - for (; it != data_channels_.end(); ++it) { - DataChannel* data_channel = it->second; - data_channel->SetSendSsrc(data_channel->id()); - } -} - -void MediaStreamSignaling::UpdateRemoteSctpDataChannels() { - DataChannels::iterator it = data_channels_.begin(); - for (; it != data_channels_.end(); ++it) { - DataChannel* data_channel = it->second; - data_channel->SetReceiveSsrc(data_channel->id()); - } } } // 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 f64bf978ffc..c600f066285 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/mediastreamsignaling.h @@ -174,11 +174,11 @@ class MediaStreamSignaling { } // Checks if |id| is available to be assigned to a new SCTP data channel. - bool IsSctpIdAvailable(int id) const; + bool IsSctpSidAvailable(int sid) const; // Gets the first available SCTP id that is not assigned to any existing // data channels. - bool AllocateSctpId(int* id); + bool AllocateSctpSid(talk_base::SSLRole role, int* sid); // Adds |local_stream| to the collection of known MediaStreams that will be // offered in a SessionDescription. @@ -196,12 +196,6 @@ class MediaStreamSignaling { // After we receive an OPEN message, create a data channel and add it. bool AddDataChannelFromOpenMessage( const std::string& label, const DataChannelInit& config); - bool ParseDataChannelOpenMessage( - const talk_base::Buffer& payload, std::string* label, - DataChannelInit* config); - bool WriteDataChannelOpenMessage( - const std::string& label, const DataChannelInit& config, - talk_base::Buffer* payload); // Returns a MediaSessionOptions struct with options decided by |constraints|, // the local MediaStreams and DataChannels. @@ -255,8 +249,8 @@ class MediaStreamSignaling { StreamCollectionInterface* remote_streams() const { return remote_streams_.get(); } - void UpdateLocalSctpDataChannels(); - void UpdateRemoteSctpDataChannels(); + void OnDataTransportCreatedForSctp(); + void OnDtlsRoleReadyForSctp(talk_base::SSLRole role); private: struct RemotePeerInfo { @@ -386,10 +380,14 @@ class MediaStreamSignaling { TrackInfos local_audio_tracks_; TrackInfos local_video_tracks_; - int last_allocated_sctp_id_; + int last_allocated_sctp_even_sid_; + int last_allocated_sctp_odd_sid_; + typedef std::map<std::string, talk_base::scoped_refptr<DataChannel> > - DataChannels; - DataChannels data_channels_; + RtpDataChannels; + typedef std::vector<talk_base::scoped_refptr<DataChannel> > SctpDataChannels; + RtpDataChannels rtp_data_channels_; + SctpDataChannels sctp_data_channels_; }; } // namespace webrtc 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 ea1336450da..5b88aa0c53f 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 @@ -32,6 +32,7 @@ #include "talk/app/webrtc/mediastreamsignaling.h" #include "talk/app/webrtc/streamcollection.h" #include "talk/app/webrtc/test/fakeconstraints.h" +#include "talk/app/webrtc/test/fakedatachannelprovider.h" #include "talk/app/webrtc/videotrack.h" #include "talk/base/gunit.h" #include "talk/base/scoped_ptr.h" @@ -127,7 +128,7 @@ static const char kSdpStringWithMsidWithoutStreams[] = "o=- 0 0 IN IP4 127.0.0.1\r\n" "s=-\r\n" "t=0 0\r\n" - "a:msid-semantic: WMS\r\n" + "a=msid-semantic: WMS\r\n" "m=audio 1 RTP/AVPF 103\r\n" "a=mid:audio\r\n" "a=rtpmap:103 ISAC/16000\r\n" @@ -237,6 +238,23 @@ static bool CompareStreamCollections(StreamCollectionInterface* s1, return true; } +class FakeDataChannelFactory : public webrtc::DataChannelFactory { + public: + FakeDataChannelFactory(FakeDataChannelProvider* provider, + cricket::DataChannelType dct) + : provider_(provider), type_(dct) {} + + virtual talk_base::scoped_refptr<webrtc::DataChannel> CreateDataChannel( + const std::string& label, + const webrtc::DataChannelInit* config) { + return webrtc::DataChannel::Create(provider_, type_, label, config); + } + + private: + FakeDataChannelProvider* provider_; + cricket::DataChannelType type_; +}; + class MockSignalingObserver : public webrtc::MediaStreamSignalingObserver { public: MockSignalingObserver() @@ -417,6 +435,7 @@ class MediaStreamSignalingTest: public testing::Test { talk_base::Thread::Current())); signaling_.reset(new MediaStreamSignalingForTest(observer_.get(), channel_manager_.get())); + data_channel_provider_.reset(new FakeDataChannelProvider()); } // Create a collection of streams. @@ -507,12 +526,25 @@ class MediaStreamSignalingTest: public testing::Test { ASSERT_TRUE(stream->AddTrack(video_track)); } + talk_base::scoped_refptr<webrtc::DataChannel> AddDataChannel( + cricket::DataChannelType type, const std::string& label, int id) { + webrtc::DataChannelInit config; + config.id = id; + talk_base::scoped_refptr<webrtc::DataChannel> data_channel( + webrtc::DataChannel::Create( + data_channel_provider_.get(), type, label, &config)); + EXPECT_TRUE(data_channel.get() != NULL); + EXPECT_TRUE(signaling_->AddDataChannel(data_channel.get())); + return data_channel; + } + // ChannelManager is used by VideoSource, so it should be released after all // the video tracks. Put it as the first private variable should ensure that. talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_; talk_base::scoped_refptr<StreamCollection> reference_collection_; talk_base::scoped_ptr<MockSignalingObserver> observer_; talk_base::scoped_ptr<MediaStreamSignalingForTest> signaling_; + talk_base::scoped_ptr<FakeDataChannelProvider> data_channel_provider_; }; // Test that a MediaSessionOptions is created for an offer if @@ -1012,4 +1044,62 @@ TEST_F(MediaStreamSignalingTest, ChangeSsrcOnTrackInLocalSessionDescription) { observer_->VerifyLocalVideoTrack(kStreams[0], kVideoTracks[0], 98); } +// Verifies that an even SCTP id is allocated for SSL_CLIENT and an odd id for +// SSL_SERVER. +TEST_F(MediaStreamSignalingTest, SctpIdAllocationBasedOnRole) { + int id; + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_SERVER, &id)); + EXPECT_EQ(1, id); + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_CLIENT, &id)); + EXPECT_EQ(0, id); + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_SERVER, &id)); + EXPECT_EQ(3, id); + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_CLIENT, &id)); + EXPECT_EQ(2, id); +} + +// Verifies that SCTP ids of existing DataChannels are not reused. +TEST_F(MediaStreamSignalingTest, SctpIdAllocationNoReuse) { + int old_id = 1; + AddDataChannel(cricket::DCT_SCTP, "a", old_id); + int new_id; + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_SERVER, &new_id)); + EXPECT_NE(old_id, new_id); + + // Creates a DataChannel with id 0. + old_id = 0; + AddDataChannel(cricket::DCT_SCTP, "a", old_id); + ASSERT_TRUE(signaling_->AllocateSctpSid(talk_base::SSL_CLIENT, &new_id)); + EXPECT_NE(old_id, new_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; + talk_base::scoped_refptr<webrtc::DataChannel> data_channel = + webrtc::DataChannel::Create( + data_channel_provider_.get(), cricket::DCT_RTP, "a", &config); + ASSERT_TRUE(data_channel.get() != NULL); + EXPECT_FALSE(signaling_->AddDataChannel(data_channel.get())); +} + +// Verifies that duplicated label is allowed for SCTP data channel. +TEST_F(MediaStreamSignalingTest, SctpDuplicatedLabelAllowed) { + AddDataChannel(cricket::DCT_SCTP, "a", -1); + AddDataChannel(cricket::DCT_SCTP, "a", -1); +} + +// Verifies that duplicated label from OPEN message is allowed. +TEST_F(MediaStreamSignalingTest, DuplicatedLabelFromOpenMessageAllowed) { + AddDataChannel(cricket::DCT_SCTP, "a", -1); + + FakeDataChannelFactory fake_factory(data_channel_provider_.get(), + cricket::DCT_SCTP); + signaling_->SetDataChannelFactory(&fake_factory); + webrtc::DataChannelInit config; + config.id = 0; + EXPECT_TRUE(signaling_->AddDataChannelFromOpenMessage("a", config)); +} 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 22789bb42c2..761338fc9c4 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/README +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/README @@ -52,16 +52,16 @@ Example of building & using the unittest & app: ./out_sim/Debug/iossim out_sim/Debug/AppRTCDemo.app - To build & sign the sample app for an iOS device: - wrios && gclient runhooks && ninja -C out_ios/Debug AppRTCDemo + wrios && gclient runhooks && ninja -C out_ios/Debug-iphoneos AppRTCDemo - To install the sample app on an iOS device: - ideviceinstaller -i out_ios/Debug/AppRTCDemo.app + ideviceinstaller -i out_ios/Debug-iphoneos/AppRTCDemo.app (if installing ideviceinstaller from brew, use --HEAD to get support for .app directories) - Alternatively, use iPhone Configuration Utility: - Open "iPhone Configuration Utility" (http://support.apple.com/kb/DL1465) - Click the "Add" icon (command-o) - - Open the app under out_ios/Debug/AppRTCDemo (should be added to the Applications tab) + - Open the app under out_ios/Debug-iphoneos/AppRTCDemo (should be added to the Applications tab) - Click the device's name in the left-hand panel and select the Applications tab - Click Install on the AppRTCDemo line. (If you have any problems deploying for the first time, check 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 a6d79354915..07a29ee4565 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 @@ -31,11 +31,11 @@ #import "RTCICECandidate+internal.h" -@implementation RTCICECandidate { - NSString *_sdpMid; - NSInteger _sdpMLineIndex; - NSString *_sdp; -} +@implementation RTCICECandidate + +@synthesize sdpMid = _sdpMid; +@synthesize sdpMLineIndex = _sdpMLineIndex; +@synthesize sdp = _sdp; - (id)initWithMid:(NSString *)sdpMid index:(NSInteger)sdpMLineIndex 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 cc5a84a41fb..f01ed32358e 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 @@ -33,6 +33,10 @@ @implementation RTCICEServer +@synthesize URI = _URI; +@synthesize username = _username; +@synthesize password = _password; + - (id)initWithURI:(NSURL *)URI username:(NSString *)username password:(NSString *)password { 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 ee2ba1b1eef..31ac53ac105 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 @@ -29,6 +29,9 @@ @implementation RTCPair +@synthesize key = _key; +@synthesize value = _value; + - (id)initWithKey:(NSString *)key value:(NSString *)value { if ((self = [super init])) { _key = [key copy]; 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 73dce36f0aa..ae9d1583dc7 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 @@ -117,9 +117,9 @@ class RTCSetSessionDescriptionObserver : public SetSessionDescriptionObserver { } - (BOOL)addICECandidate:(RTCICECandidate *)candidate { - const webrtc::IceCandidateInterface *iceCandidate = candidate.candidate; - return self.peerConnection->AddIceCandidate(iceCandidate); - delete iceCandidate; + talk_base::scoped_ptr<const webrtc::IceCandidateInterface> iceCandidate( + candidate.candidate); + return self.peerConnection->AddIceCandidate(iceCandidate.get()); } - (BOOL)addStream:(RTCMediaStream *)stream 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 1c6e7e7bc0e..325110fb36a 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 @@ -64,6 +64,8 @@ @implementation RTCPeerConnectionFactory +@synthesize nativeFactory = _nativeFactory; + + (void)initializeSSL { BOOL initialized = talk_base::InitializeSSL(); NSAssert(initialized, @"Failed to initialize SSL library"); 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 4bd9b1447d5..dd2bbdc1410 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 @@ -31,10 +31,10 @@ #import "RTCSessionDescription+internal.h" -@implementation RTCSessionDescription { - NSString *_description; - NSString *_type; -} +@implementation RTCSessionDescription + +@synthesize description = _description; +@synthesize type = _type; - (id)initWithType:(NSString *)type sdp:(NSString *)sdp { if (!type || !sdp) { 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 3d3b10e2bed..23136152109 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 @@ -40,6 +40,8 @@ @implementation RTCVideoRenderer +@synthesize delegate = _delegate; + + (RTCVideoRenderer *)videoRenderGUIWithFrame:(CGRect)frame { // TODO (hughv): Implement. return nil; 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 c04c1c39ce6..85a4482b89e 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 @@ -44,6 +44,12 @@ @implementation RTCSessionDescriptionSyncObserver +@synthesize error = _error; +@synthesize sessionDescription = _sessionDescription; +@synthesize success = _success; +@synthesize condition = _condition; +@synthesize signaled = _signaled; + - (id)init { if ((self = [super init])) { if (!(_condition = [[NSCondition alloc] init])) 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 9eaf915c395..e10e8fc953a 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnection.cc @@ -42,8 +42,6 @@ namespace { using webrtc::PeerConnectionInterface; -// The min number of tokens in the ice uri. -static const size_t kMinIceUriTokens = 2; // The min number of tokens must present in Turn host uri. // e.g. user@turn.example.org static const size_t kTurnHostTokensNum = 2; @@ -103,6 +101,73 @@ struct GetStatsMsg : public talk_base::MessageData { talk_base::scoped_refptr<webrtc::StatsObserver> observer; }; +// |in_str| should be of format +// stunURI = scheme ":" stun-host [ ":" stun-port ] +// scheme = "stun" / "stuns" +// stun-host = IP-literal / IPv4address / reg-name +// stun-port = *DIGIT + +// draft-petithuguenin-behave-turn-uris-01 +// turnURI = scheme ":" turn-host [ ":" turn-port ] +// turn-host = username@IP-literal / IPv4address / reg-name +bool GetServiceTypeAndHostnameFromUri(const std::string& in_str, + ServiceType* service_type, + std::string* hostname) { + std::string::size_type colonpos = in_str.find(':'); + if (colonpos == std::string::npos) { + return false; + } + std::string type = in_str.substr(0, colonpos); + for (size_t i = 0; i < ARRAY_SIZE(kValidIceServiceTypes); ++i) { + if (type.compare(kValidIceServiceTypes[i]) == 0) { + *service_type = static_cast<ServiceType>(i); + break; + } + } + if (*service_type == INVALID) { + return false; + } + *hostname = in_str.substr(colonpos + 1, std::string::npos); + return true; +} + +// This method parses IPv6 and IPv4 literal strings, along with hostnames in +// standard hostname:port format. +// Consider following formats as correct. +// |hostname:port|, |[IPV6 address]:port|, |IPv4 address|:port, +// |hostname|, |[IPv6 address]|, |IPv4 address| +bool ParseHostnameAndPortFromString(const std::string& in_str, + std::string* host, + int* port) { + if (in_str.at(0) == '[') { + std::string::size_type closebracket = in_str.rfind(']'); + if (closebracket != std::string::npos) { + *host = in_str.substr(1, closebracket - 1); + std::string::size_type colonpos = in_str.find(':', closebracket); + if (std::string::npos != colonpos) { + if (!talk_base::FromString( + in_str.substr(closebracket + 2, std::string::npos), port)) { + return false; + } + } + } else { + return false; + } + } else { + std::string::size_type colonpos = in_str.find(':'); + if (std::string::npos != colonpos) { + *host = in_str.substr(0, colonpos); + if (!talk_base::FromString( + in_str.substr(colonpos + 1, std::string::npos), port)) { + return false; + } + } else { + *host = in_str; + } + } + return true; +} + typedef webrtc::PortAllocatorFactoryInterface::StunConfiguration StunConfiguration; typedef webrtc::PortAllocatorFactoryInterface::TurnConfiguration @@ -125,8 +190,6 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration, // transport-ext = 1*unreserved // turn-host = IP-literal / IPv4address / reg-name // turn-port = *DIGIT - - // TODO(ronghuawu): Handle IPV6 address for (size_t i = 0; i < configuration.size(); ++i) { webrtc::PeerConnectionInterface::IceServer server = configuration[i]; if (server.uri.empty()) { @@ -152,39 +215,41 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration, } } - tokens.clear(); - talk_base::tokenize(uri_without_transport, ':', &tokens); - if (tokens.size() < kMinIceUriTokens) { - LOG(WARNING) << "Invalid uri: " << server.uri; - continue; - } + std::string hoststring; ServiceType service_type = INVALID; - const std::string& type = tokens[0]; - for (size_t i = 0; i < ARRAY_SIZE(kValidIceServiceTypes); ++i) { - if (type.compare(kValidIceServiceTypes[i]) == 0) { - service_type = static_cast<ServiceType>(i); - break; - } - } - if (service_type == INVALID) { - LOG(WARNING) << "Invalid service type: " << type; + if (!GetServiceTypeAndHostnameFromUri(uri_without_transport, + &service_type, + &hoststring)) { + LOG(LS_WARNING) << "Invalid transport parameter in ICE URI: " + << uri_without_transport; continue; } - std::string address = tokens[1]; + + // Let's break hostname. + tokens.clear(); + talk_base::tokenize(hoststring, '@', &tokens); + hoststring = tokens[0]; + if (tokens.size() == kTurnHostTokensNum) { + server.username = talk_base::s_url_decode(tokens[0]); + hoststring = tokens[1]; + } + int port = kDefaultStunPort; - if (service_type == TURNS) + if (service_type == TURNS) { port = kDefaultStunTlsPort; + turn_transport_type = kTcpTransportType; + } - if (tokens.size() > kMinIceUriTokens) { - if (!talk_base::FromString(tokens[2], &port)) { - LOG(LS_WARNING) << "Failed to parse port string: " << tokens[2]; - continue; - } + std::string address; + if (!ParseHostnameAndPortFromString(hoststring, &address, &port)) { + LOG(WARNING) << "Invalid Hostname format: " << uri_without_transport; + continue; + } - if (port <= 0 || port > 0xffff) { - LOG(WARNING) << "Invalid port: " << port; - continue; - } + + if (port <= 0 || port > 0xffff) { + LOG(WARNING) << "Invalid port: " << port; + continue; } switch (service_type) { @@ -315,7 +380,8 @@ bool PeerConnection::DoInitialize( stats_.set_session(session_.get()); // Initialize the WebRtcSession. It creates transport channels etc. - if (!session_->Initialize(constraints, dtls_identity_service)) + if (!session_->Initialize(factory_->options(), constraints, + dtls_identity_service)) return false; // Register PeerConnection as receiver of local ice candidates. @@ -424,14 +490,6 @@ PeerConnection::CreateDataChannel( if (!channel.get()) return NULL; - // If we've already passed the underlying channel's setup phase, have the - // MediaStreamSignaling update data channels manually. - if (session_->data_channel() != NULL && - session_->data_channel_type() == cricket::DCT_SCTP) { - mediastream_signaling_->UpdateLocalSctpDataChannels(); - mediastream_signaling_->UpdateRemoteSctpDataChannels(); - } - observer_->OnRenegotiationNeeded(); return DataChannelProxy::Create(signaling_thread(), channel.get()); @@ -543,6 +601,7 @@ void PeerConnection::OnSessionStateChange(cricket::BaseSession* /*session*/, switch (state) { case cricket::BaseSession::STATE_INIT: ChangeSignalingState(PeerConnectionInterface::kStable); + break; case cricket::BaseSession::STATE_SENTINITIATE: ChangeSignalingState(PeerConnectionInterface::kHaveLocalOffer); break; 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 522d528020c..76d9cd73cf0 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 @@ -157,8 +157,8 @@ class PeerConnectionTestClientBase // Disable highpass filter so that we can get all the test audio frames. constraints.AddMandatory( MediaConstraintsInterface::kHighpassFilter, false); - talk_base::scoped_refptr<webrtc::LocalAudioSource> source = - webrtc::LocalAudioSource::Create(&constraints); + talk_base::scoped_refptr<webrtc::AudioSourceInterface> source = + peer_connection_factory_->CreateAudioSource(&constraints); // TODO(perkj): Test audio source when it is implemented. Currently audio // always use the default input. talk_base::scoped_refptr<webrtc::AudioTrackInterface> audio_track( @@ -633,12 +633,24 @@ class JsepTestClient } void SetReceiveAudioVideo(bool audio, bool video) { - session_description_constraints_.SetMandatoryReceiveAudio(audio); - session_description_constraints_.SetMandatoryReceiveVideo(video); + SetReceiveAudio(audio); + SetReceiveVideo(video); ASSERT_EQ(audio, can_receive_audio()); ASSERT_EQ(video, can_receive_video()); } + void SetReceiveAudio(bool audio) { + if (audio && can_receive_audio()) + return; + session_description_constraints_.SetMandatoryReceiveAudio(audio); + } + + void SetReceiveVideo(bool video) { + if (video && can_receive_video()) + return; + session_description_constraints_.SetMandatoryReceiveVideo(video); + } + void RemoveMsidFromReceivedSdp(bool remove) { remove_msid_ = remove; } @@ -989,6 +1001,10 @@ class P2PTestConductor : public testing::Test { }; typedef P2PTestConductor<JsepTestClient> JsepPeerConnectionP2PTestClient; +// Disable for TSan v2, see +// https://code.google.com/p/webrtc/issues/detail?id=1205 for details. +#if !defined(THREAD_SANITIZER) + // This test sets up a Jsep call between two parties and test Dtmf. // TODO(holmer): Disabled due to sometimes crashing on buildbots. // See issue webrtc/2378. @@ -1047,6 +1063,20 @@ TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestDtls) { VerifyRenderedSize(640, 480); } +// This test sets up a audio call initially and then upgrades to audio/video, +// using DTLS. +TEST_F(JsepPeerConnectionP2PTestClient, LocalP2PTestDtlsRenegotiate) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + FakeConstraints setup_constraints; + setup_constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, + true); + ASSERT_TRUE(CreateTestClients(&setup_constraints, &setup_constraints)); + receiving_client()->SetReceiveAudioVideo(true, false); + LocalP2PTest(); + receiving_client()->SetReceiveAudioVideo(true, true); + 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. @@ -1380,3 +1410,6 @@ 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 new file mode 100644 index 00000000000..da3c03da4eb --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionendtoend_unittest.cc @@ -0,0 +1,224 @@ +/* + * 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. + */ + +#include "talk/app/webrtc/test/peerconnectiontestwrapper.h" +#include "talk/base/gunit.h" +#include "talk/base/logging.h" +#include "talk/base/ssladapter.h" +#include "talk/base/sslstreamadapter.h" +#include "talk/base/stringencode.h" +#include "talk/base/stringutils.h" + +using webrtc::FakeConstraints; +using webrtc::MediaConstraintsInterface; +using webrtc::MediaStreamInterface; +using webrtc::PeerConnectionInterface; + +namespace { + +const char kExternalGiceUfrag[] = "1234567890123456"; +const char kExternalGicePwd[] = "123456789012345678901234"; + +void RemoveLinesFromSdp(const std::string& line_start, + std::string* sdp) { + const char kSdpLineEnd[] = "\r\n"; + size_t ssrc_pos = 0; + while ((ssrc_pos = sdp->find(line_start, ssrc_pos)) != + std::string::npos) { + size_t end_ssrc = sdp->find(kSdpLineEnd, ssrc_pos); + sdp->erase(ssrc_pos, end_ssrc - ssrc_pos + strlen(kSdpLineEnd)); + } +} + +// Add |newlines| to the |message| after |line|. +void InjectAfter(const std::string& line, + const std::string& newlines, + std::string* message) { + const std::string tmp = line + newlines; + talk_base::replace_substrs(line.c_str(), line.length(), + tmp.c_str(), tmp.length(), message); +} + +void Replace(const std::string& line, + const std::string& newlines, + std::string* message) { + talk_base::replace_substrs(line.c_str(), line.length(), + newlines.c_str(), newlines.length(), message); +} + +void UseExternalSdes(std::string* sdp) { + // Remove current crypto specification. + RemoveLinesFromSdp("a=crypto", sdp); + RemoveLinesFromSdp("a=fingerprint", sdp); + // Add external crypto. + const char kAudioSdes[] = + "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " + "inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR\r\n"; + const char kVideoSdes[] = + "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " + "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj\r\n"; + const char kDataSdes[] = + "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " + "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj\r\n"; + InjectAfter("a=mid:audio\r\n", kAudioSdes, sdp); + InjectAfter("a=mid:video\r\n", kVideoSdes, sdp); + InjectAfter("a=mid:data\r\n", kDataSdes, sdp); +} + +void UseGice(std::string* sdp) { + InjectAfter("t=0 0\r\n", "a=ice-options:google-ice\r\n", sdp); + + std::string ufragline = "a=ice-ufrag:"; + std::string pwdline = "a=ice-pwd:"; + RemoveLinesFromSdp(ufragline, sdp); + RemoveLinesFromSdp(pwdline, sdp); + ufragline.append(kExternalGiceUfrag); + ufragline.append("\r\n"); + pwdline.append(kExternalGicePwd); + pwdline.append("\r\n"); + const std::string ufrag_pwd = ufragline + pwdline; + + InjectAfter("a=mid:audio\r\n", ufrag_pwd, sdp); + InjectAfter("a=mid:video\r\n", ufrag_pwd, sdp); + InjectAfter("a=mid:data\r\n", ufrag_pwd, sdp); +} + +void RemoveBundle(std::string* sdp) { + RemoveLinesFromSdp("a=group:BUNDLE", sdp); +} + +} // namespace + +class PeerConnectionEndToEndTest + : public sigslot::has_slots<>, + public testing::Test { + public: + PeerConnectionEndToEndTest() + : caller_(new talk_base::RefCountedObject<PeerConnectionTestWrapper>( + "caller")), + callee_(new talk_base::RefCountedObject<PeerConnectionTestWrapper>( + "callee")) { + talk_base::InitializeSSL(NULL); + } + + void CreatePcs() { + CreatePcs(NULL); + } + + void CreatePcs(const MediaConstraintsInterface* pc_constraints) { + EXPECT_TRUE(caller_->CreatePc(pc_constraints)); + EXPECT_TRUE(callee_->CreatePc(pc_constraints)); + PeerConnectionTestWrapper::Connect(caller_.get(), callee_.get()); + } + + void GetAndAddUserMedia() { + FakeConstraints audio_constraints; + FakeConstraints video_constraints; + GetAndAddUserMedia(true, audio_constraints, true, video_constraints); + } + + void GetAndAddUserMedia(bool audio, FakeConstraints audio_constraints, + bool video, FakeConstraints video_constraints) { + caller_->GetAndAddUserMedia(audio, audio_constraints, + video, video_constraints); + callee_->GetAndAddUserMedia(audio, audio_constraints, + video, video_constraints); + } + + void Negotiate() { + caller_->CreateOffer(NULL); + } + + void WaitForCallEstablished() { + caller_->WaitForCallEstablished(); + callee_->WaitForCallEstablished(); + } + + void SetupLegacySdpConverter() { + caller_->SignalOnSdpCreated.connect( + this, &PeerConnectionEndToEndTest::ConvertToLegacySdp); + callee_->SignalOnSdpCreated.connect( + this, &PeerConnectionEndToEndTest::ConvertToLegacySdp); + } + + void ConvertToLegacySdp(std::string* sdp) { + UseExternalSdes(sdp); + UseGice(sdp); + RemoveBundle(sdp); + LOG(LS_INFO) << "ConvertToLegacySdp: " << *sdp; + } + + void SetupGiceConverter() { + caller_->SignalOnIceCandidateCreated.connect( + this, &PeerConnectionEndToEndTest::AddGiceCredsToCandidate); + callee_->SignalOnIceCandidateCreated.connect( + this, &PeerConnectionEndToEndTest::AddGiceCredsToCandidate); + } + + void AddGiceCredsToCandidate(std::string* sdp) { + std::string gice_creds = " username "; + gice_creds.append(kExternalGiceUfrag); + gice_creds.append(" password "); + gice_creds.append(kExternalGicePwd); + gice_creds.append("\r\n"); + Replace("\r\n", gice_creds, sdp); + LOG(LS_INFO) << "AddGiceCredsToCandidate: " << *sdp; + } + + ~PeerConnectionEndToEndTest() { + talk_base::CleanupSSL(); + } + + protected: + talk_base::scoped_refptr<PeerConnectionTestWrapper> caller_; + talk_base::scoped_refptr<PeerConnectionTestWrapper> callee_; +}; + +// Disable for TSan v2, see +// https://code.google.com/p/webrtc/issues/detail?id=1205 for details. +#if !defined(THREAD_SANITIZER) + +TEST_F(PeerConnectionEndToEndTest, Call) { + CreatePcs(); + GetAndAddUserMedia(); + Negotiate(); + WaitForCallEstablished(); +} + +TEST_F(PeerConnectionEndToEndTest, CallWithLegacySdp) { + FakeConstraints pc_constraints; + pc_constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, + false); + CreatePcs(&pc_constraints); + SetupLegacySdpConverter(); + SetupGiceConverter(); + GetAndAddUserMedia(); + Negotiate(); + WaitForCallEstablished(); +} + +#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 7d30fab8ec1..e8b8f63169c 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.cc @@ -261,7 +261,7 @@ talk_base::scoped_refptr<AudioSourceInterface> PeerConnectionFactory::CreateAudioSource_s( const MediaConstraintsInterface* constraints) { talk_base::scoped_refptr<LocalAudioSource> source( - LocalAudioSource::Create(constraints)); + LocalAudioSource::Create(options_, constraints)); return source; } 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 7faf6094244..dff885dfe95 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectionfactory.h @@ -40,6 +40,10 @@ namespace webrtc { class PeerConnectionFactory : public PeerConnectionFactoryInterface, public talk_base::MessageHandler { public: + virtual void SetOptions(const Options& options) { + options_ = options; + } + virtual talk_base::scoped_refptr<PeerConnectionInterface> CreatePeerConnection( const PeerConnectionInterface::IceServers& configuration, @@ -77,6 +81,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface, virtual cricket::ChannelManager* channel_manager(); virtual talk_base::Thread* signaling_thread(); virtual talk_base::Thread* worker_thread(); + const Options& options() const { return options_; } protected: PeerConnectionFactory(); @@ -109,6 +114,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface, bool owns_ptrs_; talk_base::Thread* signaling_thread_; talk_base::Thread* worker_thread_; + Options options_; talk_base::scoped_refptr<PortAllocatorFactoryInterface> allocator_factory_; // External Audio device used for audio playback. talk_base::scoped_refptr<AudioDeviceModule> default_adm_; 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 182c01c46d5..4ab9e35c891 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 @@ -61,12 +61,25 @@ static const char kTurnIceServerWithTransport[] = "turn:test@hello.com?transport=tcp"; static const char kSecureTurnIceServer[] = "turns:test@hello.com?transport=tcp"; +static const char kSecureTurnIceServerWithoutTransportParam[] = + "turns:test_no_transport@hello.com:443"; +static const char kSecureTurnIceServerWithoutTransportAndPortParam[] = + "turns:test_no_transport@hello.com"; static const char kTurnIceServerWithNoUsernameInUri[] = "turn:test.com:1234"; static const char kTurnPassword[] = "turnpassword"; static const int kDefaultStunPort = 3478; static const int kDefaultStunTlsPort = 5349; static const char kTurnUsername[] = "test"; +static const char kStunIceServerWithIPv4Address[] = "stun:1.2.3.4:1234"; +static const char kStunIceServerWithIPv4AddressWithoutPort[] = "stun:1.2.3.4"; +static const char kStunIceServerWithIPv6Address[] = "stun:[2401:fa00:4::]:1234"; +static const char kStunIceServerWithIPv6AddressWithoutPort[] = + "stun:[2401:fa00:4::]"; +static const char kStunIceServerWithInvalidIPv6Address[] = + "stun:[2401:fa00:4:::3478"; +static const char kTurnIceServerWithIPv6Address[] = + "turn:test@[2401:fa00:4::]:1234"; class NullPeerConnectionObserver : public PeerConnectionObserver { public: @@ -242,6 +255,12 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) { ice_server.uri = kSecureTurnIceServer; ice_server.password = kTurnPassword; ice_servers.push_back(ice_server); + ice_server.uri = kSecureTurnIceServerWithoutTransportParam; + ice_server.password = kTurnPassword; + ice_servers.push_back(ice_server); + ice_server.uri = kSecureTurnIceServerWithoutTransportAndPortParam; + ice_server.password = kTurnPassword; + ice_servers.push_back(ice_server); talk_base::scoped_refptr<PeerConnectionInterface> pc( factory_->CreatePeerConnection(ice_servers, NULL, allocator_factory_.get(), @@ -249,9 +268,62 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) { &observer_)); EXPECT_TRUE(pc.get() != NULL); TurnConfigurations turn_configs; - webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn( + webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1( "hello.com", kDefaultStunTlsPort, "test", kTurnPassword, "tcp", true); - turn_configs.push_back(turn); + turn_configs.push_back(turn1); + // TURNS with transport param should be default to tcp. + webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2( + "hello.com", 443, "test_no_transport", kTurnPassword, "tcp", true); + turn_configs.push_back(turn2); + webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn3( + "hello.com", kDefaultStunTlsPort, "test_no_transport", + kTurnPassword, "tcp", true); + turn_configs.push_back(turn3); + VerifyTurnConfigurations(turn_configs); +} + +TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) { + webrtc::PeerConnectionInterface::IceServers ice_servers; + webrtc::PeerConnectionInterface::IceServer ice_server; + ice_server.uri = kStunIceServerWithIPv4Address; + ice_servers.push_back(ice_server); + ice_server.uri = kStunIceServerWithIPv4AddressWithoutPort; + ice_servers.push_back(ice_server); + ice_server.uri = kStunIceServerWithIPv6Address; + ice_servers.push_back(ice_server); + ice_server.uri = kStunIceServerWithIPv6AddressWithoutPort; + ice_servers.push_back(ice_server); + ice_server.uri = kStunIceServerWithInvalidIPv6Address; + ice_servers.push_back(ice_server); + ice_server.uri = kTurnIceServerWithIPv6Address; + ice_server.password = kTurnPassword; + ice_servers.push_back(ice_server); + talk_base::scoped_refptr<PeerConnectionInterface> pc( + factory_->CreatePeerConnection(ice_servers, NULL, + allocator_factory_.get(), + NULL, + &observer_)); + EXPECT_TRUE(pc.get() != NULL); + StunConfigurations stun_configs; + webrtc::PortAllocatorFactoryInterface::StunConfiguration stun1( + "1.2.3.4", 1234); + stun_configs.push_back(stun1); + webrtc::PortAllocatorFactoryInterface::StunConfiguration stun2( + "1.2.3.4", 3478); + stun_configs.push_back(stun2); // Default port + webrtc::PortAllocatorFactoryInterface::StunConfiguration stun3( + "2401:fa00:4::", 1234); + stun_configs.push_back(stun3); + 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); + turn_configs.push_back(turn1); VerifyTurnConfigurations(turn_configs); } 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 bd86f3ed2f7..a127dad3a9d 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h @@ -390,6 +390,19 @@ class DTLSIdentityServiceInterface { // argument. class PeerConnectionFactoryInterface : public talk_base::RefCountInterface { public: + 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, 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 7aa06ef4248..093b8426856 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 @@ -37,6 +37,7 @@ #include "talk/app/webrtc/videosource.h" #include "talk/base/gunit.h" #include "talk/base/scoped_ptr.h" +#include "talk/base/ssladapter.h" #include "talk/base/sslstreamadapter.h" #include "talk/base/stringutils.h" #include "talk/base/thread.h" @@ -227,12 +228,17 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { class PeerConnectionInterfaceTest : public testing::Test { protected: virtual void SetUp() { + talk_base::InitializeSSL(NULL); pc_factory_ = webrtc::CreatePeerConnectionFactory( talk_base::Thread::Current(), talk_base::Thread::Current(), NULL, NULL, NULL); ASSERT_TRUE(pc_factory_.get() != NULL); } + virtual void TearDown() { + talk_base::CleanupSSL(); + } + void CreatePeerConnection() { CreatePeerConnection("", "", NULL); } @@ -981,31 +987,6 @@ TEST_F(PeerConnectionInterfaceTest, EXPECT_TRUE(channel == NULL); } -// The test verifies that the first id not used by existing data channels is -// assigned to a new data channel if no id is specified. -TEST_F(PeerConnectionInterfaceTest, AssignSctpDataChannelId) { - FakeConstraints constraints; - constraints.SetAllowDtlsSctpDataChannels(); - CreatePeerConnection(&constraints); - - webrtc::DataChannelInit config; - - scoped_refptr<DataChannelInterface> channel = - pc_->CreateDataChannel("1", &config); - EXPECT_TRUE(channel != NULL); - EXPECT_EQ(1, channel->id()); - - config.id = 4; - channel = pc_->CreateDataChannel("4", &config); - EXPECT_TRUE(channel != NULL); - EXPECT_EQ(config.id, channel->id()); - - config.id = -1; - channel = pc_->CreateDataChannel("2", &config); - EXPECT_TRUE(channel != NULL); - EXPECT_EQ(2, channel->id()); -} - // The test verifies that creating a SCTP data channel with an id already in use // or out of range should fail. TEST_F(PeerConnectionInterfaceTest, @@ -1015,13 +996,13 @@ TEST_F(PeerConnectionInterfaceTest, CreatePeerConnection(&constraints); webrtc::DataChannelInit config; + scoped_refptr<DataChannelInterface> channel; - scoped_refptr<DataChannelInterface> channel = - pc_->CreateDataChannel("1", &config); + config.id = 1; + channel = pc_->CreateDataChannel("1", &config); EXPECT_TRUE(channel != NULL); EXPECT_EQ(1, channel->id()); - config.id = 1; channel = pc_->CreateDataChannel("x", &config); EXPECT_TRUE(channel == NULL); @@ -1119,11 +1100,12 @@ TEST_F(PeerConnectionInterfaceTest, ReceiveFireFoxOffer) { cricket::GetFirstVideoContent(pc_->local_description()->description()); ASSERT_TRUE(content != NULL); EXPECT_FALSE(content->rejected); - +#ifdef HAVE_SCTP content = cricket::GetFirstDataContent(pc_->local_description()->description()); ASSERT_TRUE(content != NULL); EXPECT_TRUE(content->rejected); +#endif } // Test that we can create an audio only offer and receive an answer with a 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 06c4b44b082..7e1e7eec662 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.cc @@ -30,6 +30,8 @@ #include <utility> #include <vector> +#include "talk/base/base64.h" +#include "talk/base/scoped_ptr.h" #include "talk/session/media/channel.h" namespace webrtc { @@ -45,13 +47,23 @@ const char StatsReport::kStatsValueNameAvailableReceiveBandwidth[] = "googAvailableReceiveBandwidth"; const char StatsReport::kStatsValueNameAvailableSendBandwidth[] = "googAvailableSendBandwidth"; +const char StatsReport::kStatsValueNameAvgEncodeMs[] = "googAvgEncodeMs"; const char StatsReport::kStatsValueNameBucketDelay[] = "googBucketDelay"; const char StatsReport::kStatsValueNameBytesReceived[] = "bytesReceived"; const char StatsReport::kStatsValueNameBytesSent[] = "bytesSent"; +const char StatsReport::kStatsValueNameBandwidthLimitedResolution[] = + "googBandwidthLimitedResolution"; +const char StatsReport::kStatsValueNameCaptureJitterMs[] = + "googCaptureJitterMs"; +const char StatsReport::kStatsValueNameCaptureQueueDelayMsPerS[] = + "googCaptureQueueDelayMsPerS"; const char StatsReport::kStatsValueNameChannelId[] = "googChannelId"; const char StatsReport::kStatsValueNameCodecName[] = "googCodecName"; const char StatsReport::kStatsValueNameComponent[] = "googComponent"; const char StatsReport::kStatsValueNameContentName[] = "googContentName"; +const char StatsReport::kStatsValueNameCpuLimitedResolution[] = + "googCpuLimitedResolution"; +const char StatsReport::kStatsValueNameDer[] = "googDerBase64"; // Echo metrics from the audio processing module. const char StatsReport::kStatsValueNameEchoCancellationQualityMin[] = "googEchoCancellationQualityMin"; @@ -64,6 +76,11 @@ const char StatsReport::kStatsValueNameEchoReturnLoss[] = const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] = "googEchoCancellationReturnLossEnhancement"; +const char StatsReport::kStatsValueNameEncodeUsagePercent[] = + "googEncodeUsagePercent"; +const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint"; +const char StatsReport::kStatsValueNameFingerprintAlgorithm[] = + "googFingerprintAlgorithm"; const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived"; const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent"; const char StatsReport::kStatsValueNameFrameHeightReceived[] = @@ -76,14 +93,28 @@ const char StatsReport::kStatsValueNameFrameRateDecoded[] = "googFrameRateDecoded"; const char StatsReport::kStatsValueNameFrameRateOutput[] = "googFrameRateOutput"; +const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs"; +const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs"; +const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs"; +const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs"; +const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs"; +const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] = + "googMinPlayoutDelayMs"; +const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs"; + const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput"; const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent"; const char StatsReport::kStatsValueNameFrameWidthReceived[] = "googFrameWidthReceived"; const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent"; const char StatsReport::kStatsValueNameInitiator[] = "googInitiator"; +const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId"; const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived"; const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress"; +const char StatsReport::kStatsValueNameLocalCandidateType[] = + "googLocalCandidateType"; +const char StatsReport::kStatsValueNameLocalCertificateId[] = + "googLocalCertificateId"; const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived"; const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent"; const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived"; @@ -91,6 +122,10 @@ const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent"; const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost"; const char StatsReport::kStatsValueNameReadable[] = "googReadable"; const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress"; +const char StatsReport::kStatsValueNameRemoteCandidateType[] = + "googRemoteCandidateType"; +const char StatsReport::kStatsValueNameRemoteCertificateId[] = + "googRemoteCertificateId"; const char StatsReport::kStatsValueNameRetransmitBitrate[] = "googRetransmitBitrate"; const char StatsReport::kStatsValueNameRtt[] = "googRtt"; @@ -104,16 +139,20 @@ const char StatsReport::kStatsValueNameTransportType[] = "googTransportType"; const char StatsReport::kStatsValueNameTrackId[] = "googTrackId"; const char StatsReport::kStatsValueNameTypingNoiseState[] = "googTypingNoiseState"; +const char StatsReport::kStatsValueNameViewLimitedResolution[] = + "googViewLimitedResolution"; const char StatsReport::kStatsValueNameWritable[] = "googWritable"; const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession"; const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe"; +const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc"; const char StatsReport::kStatsReportTypeSsrc[] = "ssrc"; const char StatsReport::kStatsReportTypeTrack[] = "googTrack"; const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate"; const char StatsReport::kStatsReportTypeTransport[] = "googTransport"; const char StatsReport::kStatsReportTypeComponent[] = "googComponent"; const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair"; +const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate"; const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo"; @@ -229,6 +268,21 @@ void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) { info.framerate_decoded); report->AddValue(StatsReport::kStatsValueNameFrameRateOutput, info.framerate_output); + + report->AddValue(StatsReport::kStatsValueNameDecodeMs, + info.decode_ms); + report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs, + info.max_decode_ms); + report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs, + info.current_delay_ms); + report->AddValue(StatsReport::kStatsValueNameTargetDelayMs, + info.target_delay_ms); + report->AddValue(StatsReport::kStatsValueNameJitterBufferMs, + info.jitter_buffer_ms); + report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs, + info.min_playout_delay_ms); + report->AddValue(StatsReport::kStatsValueNameRenderDelayMs, + info.render_delay_ms); } void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { @@ -251,6 +305,19 @@ void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { info.framerate_sent); report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms); report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name); + report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution, + (info.adapt_reason & 0x1) > 0); + report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution, + (info.adapt_reason & 0x2) > 0); + report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution, + (info.adapt_reason & 0x4) > 0); + report->AddValue(StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms); + report->AddValue(StatsReport::kStatsValueNameCaptureJitterMs, + info.capture_jitter_ms); + report->AddValue(StatsReport::kStatsValueNameCaptureQueueDelayMsPerS, + info.capture_queue_delay_ms_per_s); + report->AddValue(StatsReport::kStatsValueNameEncodeUsagePercent, + info.encode_usage_percent); } void ExtractStats(const cricket::BandwidthEstimationInfo& info, @@ -281,24 +348,22 @@ void ExtractStats(const cricket::BandwidthEstimationInfo& info, info.bucket_delay); } -uint32 ExtractSsrc(const cricket::VoiceReceiverInfo& info) { - return info.ssrc; -} - -uint32 ExtractSsrc(const cricket::VoiceSenderInfo& info) { - return info.ssrc; +void ExtractRemoteStats(const cricket::MediaSenderInfo& info, + StatsReport* report) { + report->timestamp = info.remote_stats[0].timestamp; + // TODO(hta): Extract some stats here. } -uint32 ExtractSsrc(const cricket::VideoReceiverInfo& info) { - return info.ssrcs[0]; -} - -uint32 ExtractSsrc(const cricket::VideoSenderInfo& info) { - return info.ssrcs[0]; +void ExtractRemoteStats(const cricket::MediaReceiverInfo& info, + StatsReport* report) { + report->timestamp = info.remote_stats[0].timestamp; + // TODO(hta): Extract some stats here. } // Template to extract stats from a data vector. -// ExtractSsrc and ExtractStats must be defined and overloaded for each type. +// In order to use the template, the functions that are called from it, +// ExtractStats and ExtractRemoteStats, must be defined and overloaded +// for each type. template<typename T> void ExtractStatsFromList(const std::vector<T>& data, const std::string& transport_id, @@ -306,12 +371,21 @@ void ExtractStatsFromList(const std::vector<T>& data, typename std::vector<T>::const_iterator it = data.begin(); for (; it != data.end(); ++it) { std::string id; - uint32 ssrc = ExtractSsrc(*it); - StatsReport* report = collector->PrepareReport(ssrc, transport_id); + uint32 ssrc = it->ssrc(); + // Each object can result in 2 objects, a local and a remote object. + // TODO(hta): Handle the case of multiple SSRCs per object. + StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id); if (!report) { continue; } ExtractStats(*it, report); + if (it->remote_stats.size() > 0) { + report = collector->PrepareRemoteReport(ssrc, transport_id); + if (!report) { + continue; + } + ExtractRemoteStats(*it, report); + } } }; @@ -394,8 +468,9 @@ void StatsCollector::UpdateStats() { } } -StatsReport* StatsCollector::PrepareReport(uint32 ssrc, - const std::string& transport_id) { +StatsReport* StatsCollector::PrepareLocalReport( + uint32 ssrc, + const std::string& transport_id) { std::string ssrc_id = talk_base::ToString<uint32>(ssrc); StatsMap::iterator it = reports_.find(StatsId( StatsReport::kStatsReportTypeSsrc, ssrc_id)); @@ -415,10 +490,8 @@ StatsReport* StatsCollector::PrepareReport(uint32 ssrc, &track_id); } - StatsReport* report = &reports_[ - StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id)]; - report->id = StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id); - report->type = StatsReport::kStatsReportTypeSsrc; + StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc, + ssrc_id); // Clear out stats from previous GatherStats calls if any. if (report->timestamp != stats_gathering_started_) { @@ -434,6 +507,103 @@ StatsReport* StatsCollector::PrepareReport(uint32 ssrc, return report; } +StatsReport* StatsCollector::PrepareRemoteReport( + uint32 ssrc, + const std::string& transport_id) { + std::string ssrc_id = talk_base::ToString<uint32>(ssrc); + StatsMap::iterator it = reports_.find(StatsId( + StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id)); + + 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"; + return NULL; + } + } else { + // Keeps the old track id since we want to report the stats for inactive + // tracks. + ExtractValueFromReport(it->second, + StatsReport::kStatsValueNameTrackId, + &track_id); + } + + StatsReport* report = GetOrCreateReport( + StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id); + + // Clear out stats from previous GatherStats calls if any. + // The timestamp will be added later. Zero it for debugging. + report->values.clear(); + report->timestamp = 0; + + report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id); + report->AddValue(StatsReport::kStatsValueNameTrackId, track_id); + // Add the mapping of SSRC to transport. + report->AddValue(StatsReport::kStatsValueNameTransportId, + transport_id); + return report; +} + +std::string StatsCollector::AddOneCertificateReport( + const talk_base::SSLCertificate* cert, const std::string& issuer_id) { + // TODO(bemasc): Move this computation to a helper class that caches these + // values to reduce CPU use in GetStats. This will require adding a fast + // SSLCertificate::Equals() method to detect certificate changes. + + std::string digest_algorithm; + if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm)) + return std::string(); + + talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint( + talk_base::SSLFingerprint::Create(digest_algorithm, cert)); + std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint(); + + talk_base::Buffer der_buffer; + cert->ToDER(&der_buffer); + std::string der_base64; + talk_base::Base64::EncodeFromArray( + der_buffer.data(), der_buffer.length(), &der_base64); + + StatsReport report; + report.type = StatsReport::kStatsReportTypeCertificate; + report.id = StatsId(report.type, fingerprint); + report.timestamp = stats_gathering_started_; + report.AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint); + report.AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm, + digest_algorithm); + report.AddValue(StatsReport::kStatsValueNameDer, der_base64); + if (!issuer_id.empty()) + report.AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id); + reports_[report.id] = report; + return report.id; +} + +std::string StatsCollector::AddCertificateReports( + const talk_base::SSLCertificate* cert) { + // Produces a chain of StatsReports representing this certificate and the rest + // of its chain, and adds those reports to |reports_|. The return value is + // the id of the leaf report. The provided cert must be non-null, so at least + // one report will always be provided and the returned string will never be + // empty. + ASSERT(cert != NULL); + + std::string issuer_id; + talk_base::scoped_ptr<talk_base::SSLCertChain> chain; + if (cert->GetChain(chain.accept())) { + // This loop runs in reverse, i.e. from root to leaf, so that each + // certificate's issuer's report ID is known before the child certificate's + // report is generated. The root certificate does not have an issuer ID + // value. + for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) { + const talk_base::SSLCertificate& cert_i = chain->Get(i); + issuer_id = AddOneCertificateReport(&cert_i, issuer_id); + } + } + // Add the leaf certificate. + return AddOneCertificateReport(cert, issuer_id); +} + void StatsCollector::ExtractSessionInfo() { // Extract information from the base session. StatsReport report; @@ -454,6 +624,22 @@ void StatsCollector::ExtractSessionInfo() { for (cricket::TransportStatsMap::iterator transport_iter = stats.transport_stats.begin(); transport_iter != stats.transport_stats.end(); ++transport_iter) { + // Attempt to get a copy of the certificates from the transport and + // expose them in stats reports. All channels in a transport share the + // same local and remote certificates. + std::string local_cert_report_id, remote_cert_report_id; + cricket::Transport* transport = + session_->GetTransport(transport_iter->second.content_name); + if (transport) { + talk_base::scoped_ptr<talk_base::SSLIdentity> identity; + if (transport->GetIdentity(identity.accept())) + local_cert_report_id = AddCertificateReports( + &(identity->certificate())); + + talk_base::scoped_ptr<talk_base::SSLCertificate> cert; + if (transport->GetRemoteCertificate(cert.accept())) + remote_cert_report_id = AddCertificateReports(cert.get()); + } for (cricket::TransportChannelStatsList::iterator channel_iter = transport_iter->second.channel_stats.begin(); channel_iter != transport_iter->second.channel_stats.end(); @@ -467,6 +653,14 @@ void StatsCollector::ExtractSessionInfo() { channel_report.timestamp = stats_gathering_started_; channel_report.AddValue(StatsReport::kStatsValueNameComponent, channel_iter->component); + if (!local_cert_report_id.empty()) + channel_report.AddValue( + StatsReport::kStatsValueNameLocalCertificateId, + local_cert_report_id); + if (!remote_cert_report_id.empty()) + channel_report.AddValue( + StatsReport::kStatsValueNameRemoteCertificateId, + remote_cert_report_id); reports_[channel_report.id] = channel_report; for (size_t i = 0; i < channel_iter->connection_infos.size(); @@ -497,6 +691,13 @@ void StatsCollector::ExtractSessionInfo() { info.local_candidate.address().ToString()); report.AddValue(StatsReport::kStatsValueNameRemoteAddress, info.remote_candidate.address().ToString()); + report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt); + report.AddValue(StatsReport::kStatsValueNameTransportType, + info.local_candidate.protocol()); + report.AddValue(StatsReport::kStatsValueNameLocalCandidateType, + info.local_candidate.type()); + report.AddValue(StatsReport::kStatsValueNameRemoteCandidateType, + info.remote_candidate.type()); reports_[report.id] = report; } } @@ -573,4 +774,19 @@ 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* report = NULL; + std::map<std::string, StatsReport>::iterator it = reports_.find(statsid); + if (it == reports_.end()) { + report = &reports_[statsid]; // Create new element. + report->id = statsid; + report->type = type; + } else { + report = &(it->second); + } + return report; +} + } // 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 03a32c49348..01da059b510 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/statscollector.h @@ -65,9 +65,11 @@ class StatsCollector { // |reports|. bool GetStats(MediaStreamTrackInterface* track, StatsReports* reports); - WebRtcSession* session() { return session_; } - // Prepare an SSRC report for the given ssrc. Used internally. - StatsReport* PrepareReport(uint32 ssrc, const std::string& transport); + // Prepare an SSRC report for the given ssrc. Used internally + // in the ExtractStatsFromList template. + StatsReport* PrepareLocalReport(uint32 ssrc, const std::string& transport); + // Prepare an SSRC report for the given remote ssrc. Used internally. + StatsReport* PrepareRemoteReport(uint32 ssrc, const std::string& transport); // Extracts the ID of a Transport belonging to an SSRC. Used internally. bool GetTransportIdFromProxy(const std::string& proxy, std::string* transport_id); @@ -75,14 +77,25 @@ class StatsCollector { private: bool CopySelectedReports(const std::string& selector, StatsReports* reports); + // Helper method for AddCertificateReports. + std::string AddOneCertificateReport( + const talk_base::SSLCertificate* cert, const std::string& issuer_id); + + // Adds a report for this certificate and every certificate in its chain, and + // returns the leaf certificate's report's ID. + std::string AddCertificateReports(const talk_base::SSLCertificate* cert); + void ExtractSessionInfo(); void ExtractVoiceInfo(); void ExtractVideoInfo(); double GetTimeNow(); void BuildSsrcToTransportId(); + WebRtcSession* session() { return session_; } + webrtc::StatsReport* GetOrCreateReport(const std::string& type, + const std::string& id); // A map from the report id to the report. - std::map<std::string, webrtc::StatsReport> reports_; + std::map<std::string, StatsReport> reports_; // Raw pointer to the session the statistics are gathered from. WebRtcSession* session_; double stats_gathering_started_; 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 cce1645bcab..1adcb0e20aa 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 @@ -30,6 +30,8 @@ #include "talk/app/webrtc/mediastream.h" #include "talk/app/webrtc/videotrack.h" +#include "talk/base/base64.h" +#include "talk/base/fakesslidentity.h" #include "talk/base/gunit.h" #include "talk/media/base/fakemediaengine.h" #include "talk/media/devices/fakedevicemanager.h" @@ -56,15 +58,20 @@ namespace { const char kNotFound[] = "NOT FOUND"; const char kNoReports[] = "NO REPORTS"; +// Constant names for track identification. +const char kTrackId[] = "somename"; +const uint32 kSsrcOfTrack = 1234; + class MockWebRtcSession : public webrtc::WebRtcSession { public: explicit MockWebRtcSession(cricket::ChannelManager* channel_manager) : WebRtcSession(channel_manager, talk_base::Thread::Current(), - NULL, NULL, NULL) { + talk_base::Thread::Current(), NULL, NULL) { } MOCK_METHOD0(video_channel, cricket::VideoChannel*()); MOCK_METHOD2(GetTrackIdBySsrc, bool(uint32, std::string*)); MOCK_METHOD1(GetStats, bool(cricket::SessionStats*)); + MOCK_METHOD1(GetTransport, cricket::Transport*(const std::string&)); }; class MockVideoMediaChannel : public cricket::FakeVideoMediaChannel { @@ -76,8 +83,21 @@ class MockVideoMediaChannel : public cricket::FakeVideoMediaChannel { MOCK_METHOD1(GetStats, bool(cricket::VideoMediaInfo*)); }; +bool GetValue(const webrtc::StatsReport* report, + const std::string& name, + std::string* value) { + webrtc::StatsReport::Values::const_iterator it = report->values.begin(); + for (; it != report->values.end(); ++it) { + if (it->name == name) { + *value = it->value; + return true; + } + } + return false; +} + std::string ExtractStatsValue(const std::string& type, - webrtc::StatsReports reports, + const webrtc::StatsReports& reports, const std::string name) { if (reports.empty()) { return kNoReports; @@ -85,12 +105,9 @@ std::string ExtractStatsValue(const std::string& type, for (size_t i = 0; i < reports.size(); ++i) { if (reports[i].type != type) continue; - webrtc::StatsReport::Values::const_iterator it = - reports[i].values.begin(); - for (; it != reports[i].values.end(); ++it) { - if (it->name == name) { - return it->value; - } + std::string ret; + if (GetValue(&reports[i], name, &ret)) { + return ret; } } @@ -99,9 +116,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(webrtc::StatsReports reports, - const std::string& type, - int n) { +const webrtc::StatsReport* FindNthReportByType( + const webrtc::StatsReports& reports, const std::string& type, int n) { for (size_t i = 0; i < reports.size(); ++i) { if (reports[i].type == type) { n--; @@ -112,7 +128,7 @@ const webrtc::StatsReport* FindNthReportByType(webrtc::StatsReports reports, return NULL; } -const webrtc::StatsReport* FindReportById(webrtc::StatsReports reports, +const webrtc::StatsReport* FindReportById(const webrtc::StatsReports& reports, const std::string& id) { for (size_t i = 0; i < reports.size(); ++i) { if (reports[i].id == id) { @@ -134,6 +150,59 @@ std::string ExtractBweStatsValue(webrtc::StatsReports reports, webrtc::StatsReport::kStatsReportTypeBwe, reports, name); } +std::string DerToPem(const std::string& der) { + return talk_base::SSLIdentity::DerToPem( + talk_base::kPemTypeCertificate, + reinterpret_cast<const unsigned char*>(der.c_str()), + der.length()); +} + +std::vector<std::string> DersToPems( + const std::vector<std::string>& ders) { + std::vector<std::string> pems(ders.size()); + std::transform(ders.begin(), ders.end(), pems.begin(), DerToPem); + return pems; +} + +void CheckCertChainReports(const webrtc::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); + ASSERT_TRUE(report != NULL); + + std::string der_base64; + EXPECT_TRUE(GetValue( + report, webrtc::StatsReport::kStatsValueNameDer, &der_base64)); + std::string der = talk_base::Base64::Decode(der_base64, + talk_base::Base64::DO_STRICT); + EXPECT_EQ(ders[i], der); + + std::string fingerprint_algorithm; + EXPECT_TRUE(GetValue( + report, + webrtc::StatsReport::kStatsValueNameFingerprintAlgorithm, + &fingerprint_algorithm)); + // The digest algorithm for a FakeSSLCertificate is always SHA-1. + std::string sha_1_str = talk_base::DIGEST_SHA_1; + EXPECT_EQ(sha_1_str, fingerprint_algorithm); + + std::string dummy_fingerprint; // Value is not checked. + EXPECT_TRUE(GetValue( + report, + webrtc::StatsReport::kStatsValueNameFingerprint, + &dummy_fingerprint)); + + ++i; + if (!GetValue( + report, webrtc::StatsReport::kStatsValueNameIssuerId, &certificate_id)) + break; + } + EXPECT_EQ(ders.size(), i); +} + class StatsCollectorTest : public testing::Test { protected: StatsCollectorTest() @@ -142,14 +211,116 @@ class StatsCollectorTest : public testing::Test { new cricket::ChannelManager(media_engine_, new cricket::FakeDeviceManager(), talk_base::Thread::Current())), - session_(channel_manager_.get()) { + session_(channel_manager_.get()), + track_id_(kTrackId) { // By default, we ignore session GetStats calls. EXPECT_CALL(session_, GetStats(_)).WillRepeatedly(Return(false)); } + // This creates a standard setup with a transport called "trspname" + // having one transport channel + // and the specified virtual connection name. + void InitSessionStats(const std::string vc_name) { + const std::string kTransportName("trspname"); + cricket::TransportStats transport_stats; + cricket::TransportChannelStats channel_stats; + channel_stats.component = 1; + transport_stats.content_name = kTransportName; + transport_stats.channel_stats.push_back(channel_stats); + + session_stats_.transport_stats[kTransportName] = transport_stats; + session_stats_.proxy_to_transport[vc_name] = kTransportName; + } + + // Adds a track with a given SSRC into the stats. + void AddVideoTrackStats() { + stream_ = webrtc::MediaStream::Create("streamlabel"); + track_= webrtc::VideoTrack::Create(kTrackId, NULL); + stream_->AddTrack(track_); + EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(track_id_), + Return(true))); + } + + void TestCertificateReports(const talk_base::FakeSSLCertificate& local_cert, + const std::vector<std::string>& local_ders, + const talk_base::FakeSSLCertificate& remote_cert, + const std::vector<std::string>& remote_ders) { + webrtc::StatsCollector stats; // Implementation under test. + webrtc::StatsReports reports; // returned values. + stats.set_session(&session_); + + // Fake stats to process. + cricket::TransportChannelStats channel_stats; + channel_stats.component = 1; + + cricket::TransportStats transport_stats; + transport_stats.content_name = "audio"; + transport_stats.channel_stats.push_back(channel_stats); + + cricket::SessionStats session_stats; + session_stats.transport_stats[transport_stats.content_name] = + transport_stats; + + // Fake certificates to report. + talk_base::FakeSSLIdentity local_identity(local_cert); + talk_base::scoped_ptr<talk_base::FakeSSLCertificate> remote_cert_copy( + remote_cert.GetReference()); + + // Fake transport object. + talk_base::scoped_ptr<cricket::FakeTransport> transport( + new cricket::FakeTransport( + session_.signaling_thread(), + session_.worker_thread(), + transport_stats.content_name)); + transport->SetIdentity(&local_identity); + cricket::FakeTransportChannel* channel = + static_cast<cricket::FakeTransportChannel*>( + transport->CreateChannel(channel_stats.component)); + EXPECT_FALSE(channel == NULL); + channel->SetRemoteCertificate(remote_cert_copy.get()); + + // Configure MockWebRtcSession + EXPECT_CALL(session_, GetTransport(transport_stats.content_name)) + .WillOnce(Return(transport.get())); + EXPECT_CALL(session_, GetStats(_)) + .WillOnce(DoAll(SetArgPointee<0>(session_stats), + Return(true))); + EXPECT_CALL(session_, video_channel()) + .WillRepeatedly(ReturnNull()); + + stats.UpdateStats(); + + stats.GetStats(NULL, &reports); + + const webrtc::StatsReport* channel_report = FindNthReportByType( + reports, webrtc::StatsReport::kStatsReportTypeComponent, 1); + EXPECT_TRUE(channel_report != NULL); + + // Check local certificate chain. + std::string local_certificate_id = ExtractStatsValue( + webrtc::StatsReport::kStatsReportTypeComponent, + reports, + webrtc::StatsReport::kStatsValueNameLocalCertificateId); + EXPECT_NE(kNotFound, local_certificate_id); + CheckCertChainReports(reports, local_ders, local_certificate_id); + + // Check remote certificate chain. + std::string remote_certificate_id = ExtractStatsValue( + webrtc::StatsReport::kStatsReportTypeComponent, + reports, + webrtc::StatsReport::kStatsValueNameRemoteCertificateId); + EXPECT_NE(kNotFound, remote_certificate_id); + CheckCertChainReports(reports, remote_ders, remote_certificate_id); + } + cricket::FakeMediaEngine* media_engine_; talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_; MockWebRtcSession session_; + cricket::SessionStats session_stats_; + talk_base::scoped_refptr<webrtc::MediaStream> stream_; + talk_base::scoped_refptr<webrtc::VideoTrack> track_; + std::string track_id_; }; // This test verifies that 64-bit counters are passed successfully. @@ -161,20 +332,16 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { webrtc::StatsReports reports; // returned values. cricket::VideoSenderInfo video_sender_info; cricket::VideoMediaInfo stats_read; - const uint32 kSsrcOfTrack = 1234; - const std::string kNameOfTrack("somename"); // The number of bytes must be larger than 0xFFFFFFFF for this test. const int64 kBytesSent = 12345678901234LL; const std::string kBytesSentString("12345678901234"); stats.set_session(&session_); - talk_base::scoped_refptr<webrtc::MediaStream> stream( - webrtc::MediaStream::Create("streamlabel")); - stream->AddTrack(webrtc::VideoTrack::Create(kNameOfTrack, NULL)); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); // Construct a stats value to read. - video_sender_info.ssrcs.push_back(1234); + video_sender_info.add_ssrc(1234); video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); @@ -183,9 +350,6 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) { EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); - EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) - .WillOnce(DoAll(SetArgPointee<1>(kNameOfTrack), - Return(true))); stats.UpdateStats(); stats.GetStats(NULL, &reports); std::string result = ExtractSsrcStatsValue(reports, "bytesSent"); @@ -203,19 +367,15 @@ TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { cricket::VideoMediaInfo stats_read; // Set up an SSRC just to test that we get both kinds of stats back: SSRC and // BWE. - const uint32 kSsrcOfTrack = 1234; - const std::string kNameOfTrack("somename"); const int64 kBytesSent = 12345678901234LL; const std::string kBytesSentString("12345678901234"); stats.set_session(&session_); - talk_base::scoped_refptr<webrtc::MediaStream> stream( - webrtc::MediaStream::Create("streamlabel")); - stream->AddTrack(webrtc::VideoTrack::Create(kNameOfTrack, NULL)); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); // Construct a stats value to read. - video_sender_info.ssrcs.push_back(1234); + video_sender_info.add_ssrc(1234); video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); cricket::BandwidthEstimationInfo bwe; @@ -229,9 +389,7 @@ TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) { EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); - EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) - .WillOnce(DoAll(SetArgPointee<1>(kNameOfTrack), - Return(true))); + stats.UpdateStats(); stats.GetStats(NULL, &reports); std::string result = ExtractSsrcStatsValue(reports, "bytesSent"); @@ -281,13 +439,8 @@ TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) { MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, "", false, NULL); - const std::string kTrackId("somename"); - talk_base::scoped_refptr<webrtc::MediaStream> stream( - webrtc::MediaStream::Create("streamlabel")); - talk_base::scoped_refptr<webrtc::VideoTrack> track = - webrtc::VideoTrack::Create(kTrackId, NULL); - stream->AddTrack(track); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); stats.set_session(&session_); @@ -313,13 +466,8 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { MockVideoMediaChannel* media_channel = new MockVideoMediaChannel; cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, "", false, NULL); - const std::string kTrackId("somename"); - talk_base::scoped_refptr<webrtc::MediaStream> stream( - webrtc::MediaStream::Create("streamlabel")); - talk_base::scoped_refptr<webrtc::VideoTrack> track = - webrtc::VideoTrack::Create(kTrackId, NULL); - stream->AddTrack(track); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); stats.set_session(&session_); @@ -328,11 +476,10 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { // Constructs an ssrc stats update. cricket::VideoSenderInfo video_sender_info; cricket::VideoMediaInfo stats_read; - const uint32 kSsrcOfTrack = 1234; const int64 kBytesSent = 12345678901234LL; // Construct a stats value to read. - video_sender_info.ssrcs.push_back(1234); + video_sender_info.add_ssrc(1234); video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); @@ -341,23 +488,20 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { EXPECT_CALL(*media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(stats_read), Return(true))); - EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) - .WillOnce(DoAll(SetArgPointee<1>(kTrackId), - Return(true))); stats.UpdateStats(); stats.GetStats(NULL, &reports); - // |reports| should contain one session report, one track report, and one ssrc - // report. - EXPECT_EQ((size_t)3, reports.size()); + // |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); - stats.GetStats(track, &reports); - // |reports| should contain one session report, one track report, and one ssrc - // report. - EXPECT_EQ((size_t)3, reports.size()); + 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); @@ -380,13 +524,8 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { const std::string kVcName("vcname"); cricket::VideoChannel video_channel(talk_base::Thread::Current(), media_engine_, media_channel, &session_, kVcName, false, NULL); - const std::string kTrackId("somename"); - talk_base::scoped_refptr<webrtc::MediaStream> stream( - webrtc::MediaStream::Create("streamlabel")); - talk_base::scoped_refptr<webrtc::VideoTrack> track = - webrtc::VideoTrack::Create(kTrackId, NULL); - stream->AddTrack(track); - stats.AddStream(stream); + AddVideoTrackStats(); + stats.AddStream(stream_); stats.set_session(&session_); @@ -395,11 +534,10 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { // Constructs an ssrc stats update. cricket::VideoSenderInfo video_sender_info; cricket::VideoMediaInfo stats_read; - const uint32 kSsrcOfTrack = 1234; const int64 kBytesSent = 12345678901234LL; // Construct a stats value to read. - video_sender_info.ssrcs.push_back(1234); + video_sender_info.add_ssrc(1234); video_sender_info.bytes_sent = kBytesSent; stats_read.senders.push_back(video_sender_info); @@ -408,23 +546,10 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { EXPECT_CALL(*media_channel, GetStats(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read), Return(true))); - EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _)) - .WillOnce(DoAll(SetArgPointee<1>(kTrackId), - Return(true))); - // Instruct the session to return stats containing the transport channel. - const std::string kTransportName("trspname"); - cricket::SessionStats session_stats; - cricket::TransportStats transport_stats; - cricket::TransportChannelStats channel_stats; - channel_stats.component = 1; - transport_stats.content_name = kTransportName; - transport_stats.channel_stats.push_back(channel_stats); - - session_stats.transport_stats[kTransportName] = transport_stats; - session_stats.proxy_to_transport[kVcName] = kTransportName; + InitSessionStats(kVcName); EXPECT_CALL(session_, GetStats(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats), + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), Return(true))); stats.UpdateStats(); @@ -439,4 +564,214 @@ TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) { ASSERT_FALSE(transport_report == NULL); } +// This test verifies that a remote stats object will not be created for +// an outgoing SSRC where remote stats are not returned. +TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) { + webrtc::StatsCollector stats; // Implementation under test. + 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(); + stats.AddStream(stream_); + + stats.set_session(&session_); + + EXPECT_CALL(session_, video_channel()) + .WillRepeatedly(ReturnNull()); + + stats.UpdateStats(); + webrtc::StatsReports reports; + stats.GetStats(NULL, &reports); + const webrtc::StatsReport* remote_report = FindNthReportByType(reports, + webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1); + EXPECT_TRUE(remote_report == NULL); +} + +// This test verifies that a remote stats object will be created for +// an outgoing SSRC where stats are returned. +TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) { + webrtc::StatsCollector stats; // Implementation under test. + 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(); + stats.AddStream(stream_); + + stats.set_session(&session_); + + webrtc::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))); + + // Constructs an ssrc stats update. + cricket::VideoMediaInfo stats_read; + + cricket::SsrcReceiverInfo remote_ssrc_stats; + remote_ssrc_stats.timestamp = 12345.678; + remote_ssrc_stats.ssrc = kSsrcOfTrack; + cricket::VideoSenderInfo video_sender_info; + video_sender_info.add_ssrc(kSsrcOfTrack); + 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), + Return(true))); + + stats.UpdateStats(); + stats.GetStats(NULL, &reports); + const webrtc::StatsReport* remote_report = FindNthReportByType(reports, + webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1); + EXPECT_FALSE(remote_report == NULL); + EXPECT_NE(0, remote_report->timestamp); +} + +// This test verifies that all chained certificates are correctly +// reported +TEST_F(StatsCollectorTest, ChainedCertificateReportsCreated) { + // Build local certificate chain. + std::vector<std::string> local_ders(5); + local_ders[0] = "These"; + local_ders[1] = "are"; + local_ders[2] = "some"; + local_ders[3] = "der"; + local_ders[4] = "values"; + talk_base::FakeSSLCertificate local_cert(DersToPems(local_ders)); + + // Build remote certificate chain + std::vector<std::string> remote_ders(4); + remote_ders[0] = "A"; + remote_ders[1] = "non-"; + remote_ders[2] = "intersecting"; + remote_ders[3] = "set"; + talk_base::FakeSSLCertificate remote_cert(DersToPems(remote_ders)); + + TestCertificateReports(local_cert, local_ders, remote_cert, remote_ders); +} + +// This test verifies that all certificates without chains are correctly +// reported. +TEST_F(StatsCollectorTest, ChainlessCertificateReportsCreated) { + // Build local certificate. + std::string local_der = "This is the local der."; + talk_base::FakeSSLCertificate local_cert(DerToPem(local_der)); + + // Build remote certificate. + std::string remote_der = "This is somebody else's der."; + talk_base::FakeSSLCertificate remote_cert(DerToPem(remote_der)); + + TestCertificateReports(local_cert, std::vector<std::string>(1, local_der), + remote_cert, std::vector<std::string>(1, remote_der)); +} + +// This test verifies that the stats are generated correctly when no +// transport is present. +TEST_F(StatsCollectorTest, NoTransport) { + webrtc::StatsCollector stats; // Implementation under test. + webrtc::StatsReports reports; // returned values. + stats.set_session(&session_); + + // Fake stats to process. + cricket::TransportChannelStats channel_stats; + channel_stats.component = 1; + + cricket::TransportStats transport_stats; + transport_stats.content_name = "audio"; + transport_stats.channel_stats.push_back(channel_stats); + + cricket::SessionStats session_stats; + session_stats.transport_stats[transport_stats.content_name] = + transport_stats; + + // Configure MockWebRtcSession + EXPECT_CALL(session_, GetTransport(transport_stats.content_name)) + .WillOnce(ReturnNull()); + EXPECT_CALL(session_, GetStats(_)) + .WillOnce(DoAll(SetArgPointee<0>(session_stats), + Return(true))); + + EXPECT_CALL(session_, video_channel()) + .WillRepeatedly(ReturnNull()); + + stats.UpdateStats(); + stats.GetStats(NULL, &reports); + + // Check that the local certificate is absent. + std::string local_certificate_id = ExtractStatsValue( + webrtc::StatsReport::kStatsReportTypeComponent, + reports, + webrtc::StatsReport::kStatsValueNameLocalCertificateId); + ASSERT_EQ(kNotFound, local_certificate_id); + + // Check that the remote certificate is absent. + std::string remote_certificate_id = ExtractStatsValue( + webrtc::StatsReport::kStatsReportTypeComponent, + reports, + webrtc::StatsReport::kStatsValueNameRemoteCertificateId); + ASSERT_EQ(kNotFound, remote_certificate_id); +} + +// This test verifies that the stats are generated correctly when the transport +// does not have any certificates. +TEST_F(StatsCollectorTest, NoCertificates) { + webrtc::StatsCollector stats; // Implementation under test. + webrtc::StatsReports reports; // returned values. + stats.set_session(&session_); + + // Fake stats to process. + cricket::TransportChannelStats channel_stats; + channel_stats.component = 1; + + cricket::TransportStats transport_stats; + transport_stats.content_name = "audio"; + transport_stats.channel_stats.push_back(channel_stats); + + cricket::SessionStats session_stats; + session_stats.transport_stats[transport_stats.content_name] = + transport_stats; + + // Fake transport object. + talk_base::scoped_ptr<cricket::FakeTransport> transport( + new cricket::FakeTransport( + session_.signaling_thread(), + session_.worker_thread(), + transport_stats.content_name)); + + // Configure MockWebRtcSession + EXPECT_CALL(session_, GetTransport(transport_stats.content_name)) + .WillOnce(Return(transport.get())); + EXPECT_CALL(session_, GetStats(_)) + .WillOnce(DoAll(SetArgPointee<0>(session_stats), + Return(true))); + EXPECT_CALL(session_, video_channel()) + .WillRepeatedly(ReturnNull()); + + stats.UpdateStats(); + stats.GetStats(NULL, &reports); + + // Check that the local certificate is absent. + std::string local_certificate_id = ExtractStatsValue( + webrtc::StatsReport::kStatsReportTypeComponent, + reports, + webrtc::StatsReport::kStatsValueNameLocalCertificateId); + ASSERT_EQ(kNotFound, local_certificate_id); + + // Check that the remote certificate is absent. + std::string remote_certificate_id = ExtractStatsValue( + webrtc::StatsReport::kStatsReportTypeComponent, + reports, + webrtc::StatsReport::kStatsValueNameRemoteCertificateId); + ASSERT_EQ(kNotFound, remote_certificate_id); +} + } // 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 30a8b84165c..6890f9e017a 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/statstypes.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/statstypes.h @@ -88,6 +88,10 @@ class StatsReport { // The |id| field is the SSRC in decimal form of the rtp stream. static const char kStatsReportTypeSsrc[]; + // StatsReport of |type| = "remoteSsrc" is statistics for a specific + // rtp stream, generated by the remote end of the connection. + static const char kStatsReportTypeRemoteSsrc[]; + // StatsReport of |type| = "googTrack" is statistics for a specific media // track. The |id| field is the track id. static const char kStatsReportTypeTrack[]; @@ -99,6 +103,14 @@ class StatsReport { // The id of StatsReport of type VideoBWE. static const char kStatsReportVideoBweId[]; + // A StatsReport of |type| = "googCertificate" contains an SSL certificate + // transmitted by one of the endpoints of this connection. The |id| is + // controlled by the fingerprint, and is used to identify the certificate in + // the Channel stats (as "googLocalCertificateId" or + // "googRemoteCertificateId") and in any child certificates (as + // "googIssuerId"). + static const char kStatsReportTypeCertificate[]; + // StatsValue names static const char kStatsValueNameAudioOutputLevel[]; static const char kStatsValueNameAudioInputLevel[]; @@ -116,7 +128,14 @@ class StatsReport { // Internal StatsValue names + static const char kStatsValueNameAvgEncodeMs[]; + static const char kStatsValueNameEncodeUsagePercent[]; + static const char kStatsValueNameCaptureJitterMs[]; + static const char kStatsValueNameCaptureQueueDelayMsPerS[]; static const char kStatsValueNameCodecName[]; + static const char kStatsValueNameBandwidthLimitedResolution[]; + static const char kStatsValueNameCpuLimitedResolution[]; + static const char kStatsValueNameViewLimitedResolution[]; static const char kStatsValueNameEchoCancellationQualityMin[]; static const char kStatsValueNameEchoDelayMedian[]; static const char kStatsValueNameEchoDelayStdDev[]; @@ -129,6 +148,13 @@ class StatsReport { static const char kStatsValueNameFrameRateReceived[]; static const char kStatsValueNameFrameRateDecoded[]; static const char kStatsValueNameFrameRateOutput[]; + static const char kStatsValueNameDecodeMs[]; + static const char kStatsValueNameMaxDecodeMs[]; + static const char kStatsValueNameCurrentDelayMs[]; + static const char kStatsValueNameTargetDelayMs[]; + static const char kStatsValueNameJitterBufferMs[]; + static const char kStatsValueNameMinPlayoutDelayMs[]; + static const char kStatsValueNameRenderDelayMs[]; static const char kStatsValueNameFrameRateInput[]; static const char kStatsValueNameFrameRateSent[]; static const char kStatsValueNameFrameWidthReceived[]; @@ -152,6 +178,14 @@ class StatsReport { static const char kStatsValueNameTrackId[]; static const char kStatsValueNameSsrc[]; static const char kStatsValueNameTypingNoiseState[]; + static const char kStatsValueNameDer[]; + static const char kStatsValueNameFingerprint[]; + static const char kStatsValueNameFingerprintAlgorithm[]; + static const char kStatsValueNameIssuerId[]; + static const char kStatsValueNameLocalCertificateId[]; + static const char kStatsValueNameRemoteCertificateId[]; + static const char kStatsValueNameLocalCandidateType[]; + static const char kStatsValueNameRemoteCandidateType[]; }; typedef std::vector<StatsReport> StatsReports; 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 60c427d16f1..79f94fe3b4e 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsdp.cc @@ -145,6 +145,7 @@ static const char kAttributeFingerprint[] = "fingerprint"; static const char kAttributeSetup[] = "setup"; static const char kAttributeFmtp[] = "fmtp"; static const char kAttributeRtpmap[] = "rtpmap"; +static const char kAttributeSctpmap[] = "sctpmap"; static const char kAttributeRtcp[] = "rtcp"; static const char kAttributeIceUfrag[] = "ice-ufrag"; static const char kAttributeIcePwd[] = "ice-pwd"; @@ -210,7 +211,7 @@ 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 kDefaultSctpFmtProtocol[] = "webrtc-datachannel"; +static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel"; struct SsrcInfo { SsrcInfo() @@ -340,13 +341,15 @@ static bool ParseFailed(const std::string& message, const std::string& description, SdpParseError* error) { // Get the first line of |message| from |line_start|. - std::string first_line = message; + std::string first_line; size_t line_end = message.find(kNewLine, line_start); if (line_end != std::string::npos) { if (line_end > 0 && (message.at(line_end - 1) == kReturn)) { --line_end; } first_line = message.substr(line_start, (line_end - line_start)); + } else { + first_line = message.substr(line_start); } if (error) { @@ -1240,12 +1243,9 @@ void BuildMediaDescription(const ContentInfo* content_info, // Making sure we are not using "passive" mode. cricket::ConnectionRole role = transport_info->description.connection_role; - ASSERT(role == cricket::CONNECTIONROLE_ACTIVE || - role == cricket::CONNECTIONROLE_ACTPASS); + std::string dtls_role_str; + VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str)); InitAttrLine(kAttributeSetup, &os); - std::string dtls_role_str = role == cricket::CONNECTIONROLE_ACTPASS ? - cricket::CONNECTIONROLE_ACTPASS_STR : - cricket::CONNECTIONROLE_ACTIVE_STR; os << kSdpDelimiterColon << dtls_role_str; AddLine(os.str(), message); } @@ -1268,10 +1268,14 @@ void BuildMediaDescription(const ContentInfo* content_info, } void BuildSctpContentAttributes(std::string* message) { - cricket::DataCodec sctp_codec(kDefaultSctpFmt, kDefaultSctpFmtProtocol, 0); - sctp_codec.SetParam(kCodecParamSctpProtocol, kDefaultSctpFmtProtocol); - sctp_codec.SetParam(kCodecParamSctpStreams, cricket::kMaxSctpSid + 1); - AddFmtpLine(sctp_codec, message); + // draft-ietf-mmusic-sctp-sdp-04 + // a=sctpmap:sctpmap-number protocol [streams] + std::ostringstream os; + InitAttrLine(kAttributeSctpmap, &os); + os << kSdpDelimiterColon << kDefaultSctpFmt << kSdpDelimiterSpace + << kDefaultSctpmapProtocol << kSdpDelimiterSpace + << (cricket::kMaxSctpSid + 1); + AddLine(os.str(), message); } void BuildRtpContentAttributes( @@ -2111,10 +2115,25 @@ bool ParseMediaDescription(const std::string& message, codec_preference, static_cast<AudioContentDescription*>(content.get())); } else if (HasAttribute(line, kMediaTypeData)) { - content.reset(ParseContentDescription<DataContentDescription>( + DataContentDescription* desc = + ParseContentDescription<DataContentDescription>( message, cricket::MEDIA_TYPE_DATA, mline_index, protocol, codec_preference, pos, &content_name, - &transport, candidates, error)); + &transport, candidates, error); + + if (protocol == cricket::kMediaProtocolDtlsSctp) { + // Add the SCTP Port number as a pseudo-codec "port" parameter + cricket::DataCodec codec_port( + cricket::kGoogleSctpDataCodecId, cricket::kGoogleSctpDataCodecName, + 0); + codec_port.SetParam(cricket::kCodecParamPort, fields[3]); + LOG(INFO) << "ParseMediaDescription: Got SCTP Port Number " + << fields[3]; + desc->AddCodec(codec_port); + } + + content.reset(desc); + // We should always use the default bandwidth for RTP-based data // channels. Don't allow SDP to set the bandwidth, because that // would give JS the opportunity to "break the Internet". @@ -2171,6 +2190,12 @@ bool ParseMediaDescription(const std::string& message, return ParseFailed("", description.str(), error); } } + + size_t end_of_message = message.size(); + if (mline_index == -1 && *pos != end_of_message) { + ParseFailed(message, *pos, "Expects m line.", error); + return false; + } return true; } @@ -2364,7 +2389,7 @@ bool ParseContent(const std::string& message, if (*pos >= message.size()) { break; // Done parsing } else { - return ParseFailed(message, *pos, "Can't find valid SDP line.", error); + return ParseFailed(message, *pos, "Invalid SDP line.", error); } } 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 b1505aae7df..541868314c7 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 @@ -76,6 +76,7 @@ using webrtc::SessionDescriptionInterface; typedef std::vector<AudioCodec> AudioCodecs; typedef std::vector<Candidate> Candidates; +static const uint32 kDefaultSctpPort = 5000; static const char kSessionTime[] = "t=0 0\r\n"; static const uint32 kCandidatePriority = 2130706432U; // pref = 1.0 static const char kCandidateUfragVoice[] = "ufrag_voice"; @@ -281,7 +282,7 @@ static const char kSdpSctpDataChannelString[] = "a=ice-ufrag:ufrag_data\r\n" "a=ice-pwd:pwd_data\r\n" "a=mid:data_content_name\r\n" - "a=fmtp:5000 protocol=webrtc-datachannel; streams=65536\r\n"; + "a=sctpmap:5000 webrtc-datachannel 1024\r\n"; static const char kSdpSctpDataChannelWithCandidatesString[] = "m=application 2345 DTLS/SCTP 5000\r\n" @@ -296,7 +297,7 @@ static const char kSdpSctpDataChannelWithCandidatesString[] = "a=ice-ufrag:ufrag_data\r\n" "a=ice-pwd:pwd_data\r\n" "a=mid:data_content_name\r\n" - "a=fmtp:5000 protocol=webrtc-datachannel; streams=65536\r\n"; + "a=sctpmap:5000 webrtc-datachannel 1024\r\n"; // One candidate reference string as per W3c spec. @@ -974,6 +975,10 @@ class WebRtcSdpTest : public testing::Test { new DataContentDescription()); data_desc_ = data.get(); data_desc_->set_protocol(cricket::kMediaProtocolDtlsSctp); + DataCodec codec(cricket::kGoogleSctpDataCodecId, + cricket::kGoogleSctpDataCodecName, 0); + codec.SetParam(cricket::kCodecParamPort, kDefaultSctpPort); + data_desc_->AddCodec(codec); desc_.AddContent(kDataContentName, NS_JINGLE_DRAFT_SCTP, data.release()); EXPECT_TRUE(desc_.AddTransportInfo( TransportInfo(kDataContentName, @@ -1479,6 +1484,19 @@ TEST_F(WebRtcSdpTest, DeserializeSessionDescription) { EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc)); } +TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMline) { + JsepSessionDescription jdesc(kDummyString); + const char kSdpWithoutMline[] = + "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 local_stream_1 local_stream_2\r\n"; + // Deserialize + EXPECT_TRUE(SdpDeserialize(kSdpWithoutMline, &jdesc)); + EXPECT_EQ(0u, jdesc.description()->contents().size()); +} + TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) { JsepSessionDescription jdesc(kDummyString); std::string sdp_without_carriage_return = kSdpFullString; @@ -1761,6 +1779,41 @@ TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) { EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output)); } +TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndNewPort) { + AddSctpDataChannel(); + const uint16 kUnusualSctpPort = 9556; + char default_portstr[16]; + char unusual_portstr[16]; + talk_base::sprintfn(default_portstr, sizeof(default_portstr), "%d", + kDefaultSctpPort); + talk_base::sprintfn(unusual_portstr, sizeof(unusual_portstr), "%d", + kUnusualSctpPort); + + JsepSessionDescription jdesc(kDummyString); + // take our pre-built session description and change the SCTP port. + cricket::SessionDescription* mutant = desc_.Copy(); + DataContentDescription* dcdesc = static_cast<DataContentDescription*>( + mutant->GetContentDescriptionByName(kDataContentName)); + std::vector<cricket::DataCodec> codecs(dcdesc->codecs()); + EXPECT_EQ(codecs.size(), 1UL); + EXPECT_EQ(codecs[0].id, cricket::kGoogleSctpDataCodecId); + codecs[0].SetParam(cricket::kCodecParamPort, kUnusualSctpPort); + + // note: mutant's owned by jdesc now. + ASSERT_TRUE(jdesc.Initialize(mutant, kSessionId, kSessionVersion)); + mutant = NULL; + + std::string sdp_with_data = kSdpString; + sdp_with_data.append(kSdpSctpDataChannelString); + talk_base::replace_substrs(default_portstr, strlen(default_portstr), + unusual_portstr, strlen(unusual_portstr), + &sdp_with_data); + JsepSessionDescription jdesc_output(kDummyString); + + EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); + EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output)); +} + TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) { AddRtpDataChannel(); JsepSessionDescription jdesc(kDummyString); @@ -1799,6 +1852,18 @@ TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInvalidExtmap) { TestDeserializeExtmap(true, true); } +TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) { + JsepSessionDescription jdesc(kDummyString); + std::string sdp = kSdpFullString; + sdp = sdp.substr(0, sdp.size() - 2); // Remove \r\n at the end. + // Deserialize + SdpParseError error; + EXPECT_FALSE(webrtc::SdpDeserialize(sdp, &jdesc, &error)); + const std::string lastline = "a=ssrc:6 label:video_track_id_3"; + EXPECT_EQ(lastline, error.line); + EXPECT_EQ("Invalid SDP line.", error.description); +} + TEST_F(WebRtcSdpTest, DeserializeCandidateWithDifferentTransport) { JsepIceCandidate jcandidate(kDummyMid, kDummyIndex); std::string new_sdp = kSdpOneCandidate; @@ -1846,6 +1911,7 @@ TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) { ReplaceAndTryToParse("t=", kSdpDestroyer); // Broken media description + ReplaceAndTryToParse("m=audio", "c=IN IP4 74.125.224.39"); ReplaceAndTryToParse("m=video", kSdpDestroyer); // Invalid lines 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 f1fb40d51a0..7e153b3ffd1 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.cc @@ -54,9 +54,11 @@ using cricket::TransportInfo; namespace webrtc { -const char kInternalConstraintPrefix[] = "internal"; +const char MediaConstraintsInterface::kInternalConstraintPrefix[] = "internal"; // Supported MediaConstraints. +// DSCP constraints. +const char MediaConstraintsInterface::kEnableDscp[] = "googDscp"; // DTLS-SRTP pseudo-constraints. const char MediaConstraintsInterface::kEnableDtlsSrtp[] = "DtlsSrtpKeyAgreement"; @@ -67,7 +69,7 @@ const char MediaConstraintsInterface::kEnableRtpDataChannels[] = // line flag. So it is prefixed with kInternalConstraintPrefix so JS values // will be removed. const char MediaConstraintsInterface::kEnableSctpDataChannels[] = - "internalSctpDataChannels"; + "deprecatedSctpDataChannels"; // Error messages const char kSetLocalSdpFailed[] = "SetLocalDescription failed: "; @@ -81,6 +83,10 @@ 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."; +const char kSdpWithoutIceUfragPwd[] = + "Called with an SDP without ice-ufrag and ice-pwd."; const char kSessionError[] = "Session error code: "; const char kUpdateStateFailed[] = "Failed to update session state: "; const char kPushDownOfferTDFailed[] = @@ -110,10 +116,9 @@ static bool VerifyMediaDescriptions( // fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES // keys, will be caught in Transport negotiation, and backstopped by Channel's // |secure_required| check. -static bool VerifyCrypto(const SessionDescription* desc) { - if (!desc) { - return false; - } +static bool VerifyCrypto(const SessionDescription* desc, + bool dtls_enabled, + std::string* error) { const ContentInfos& contents = desc->contents(); for (size_t index = 0; index < contents.size(); ++index) { const ContentInfo* cinfo = &contents[index]; @@ -128,19 +133,53 @@ static bool VerifyCrypto(const SessionDescription* desc) { if (!media || !tinfo) { // Something is not right. LOG(LS_ERROR) << kInvalidSdp; + *error = kInvalidSdp; return false; } - if (media->cryptos().empty() && - !tinfo->description.identity_fingerprint) { - // Crypto must be supplied. - LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP."; - return false; + if (media->cryptos().empty()) { + if (!tinfo->description.identity_fingerprint) { + // Crypto must be supplied. + LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP."; + *error = kSdpWithoutCrypto; + return false; + } + if (!dtls_enabled) { + LOG(LS_WARNING) << + "Session description must have SDES when DTLS disabled."; + *error = kSdpWithoutSdesAndDtlsDisabled; + return false; + } } } return true; } +// Checks that each non-rejected content has ice-ufrag and ice-pwd set. +static bool VerifyIceUfragPwdPresent(const SessionDescription* desc) { + const ContentInfos& contents = desc->contents(); + for (size_t index = 0; index < contents.size(); ++index) { + const ContentInfo* cinfo = &contents[index]; + if (cinfo->rejected) { + continue; + } + + // If the content isn't rejected, ice-ufrag and ice-pwd must be present. + const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name); + if (!tinfo) { + // Something is not right. + LOG(LS_ERROR) << kInvalidSdp; + return false; + } + if (tinfo->description.ice_ufrag.empty() || + tinfo->description.ice_pwd.empty()) { + LOG(LS_ERROR) << "Session description must have ice ufrag and pwd."; + return false; + } + } + return true; +} + // Forces |sdesc->crypto_required| to the appropriate state based on the // current security policy, to ensure a failure occurs if there is an error // in crypto negotiation. @@ -392,6 +431,8 @@ WebRtcSession::WebRtcSession( ice_observer_(NULL), 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) { } @@ -416,6 +457,7 @@ WebRtcSession::~WebRtcSession() { } bool WebRtcSession::Initialize( + const PeerConnectionFactoryInterface::Options& options, const MediaConstraintsInterface* constraints, DTLSIdentityServiceInterface* dtls_identity_service) { // TODO(perkj): Take |constraints| into consideration. Return false if not all @@ -424,29 +466,26 @@ bool WebRtcSession::Initialize( bool value; // Enable DTLS by default if |dtls_identity_service| is valid. - bool dtls_enabled = (dtls_identity_service != NULL); - // |constraints| can override the default |dtls_enabled| value. + dtls_enabled_ = (dtls_identity_service != NULL); + // |constraints| can override the default |dtls_enabled_| value. if (FindConstraint( constraints, MediaConstraintsInterface::kEnableDtlsSrtp, &value, NULL)) { - dtls_enabled = value; + dtls_enabled_ = value; } // Enable creation of RTP data channels if the kEnableRtpDataChannels is set. - // It takes precendence over the kEnableSctpDataChannels constraint. + // It takes precendence over the disable_sctp_data_channels + // PeerConnectionFactoryInterface::Options. if (FindConstraint( constraints, MediaConstraintsInterface::kEnableRtpDataChannels, &value, NULL) && value) { LOG(LS_INFO) << "Allowing RTP data engine."; data_channel_type_ = cricket::DCT_RTP; } else { - bool sctp_enabled = FindConstraint( - constraints, - MediaConstraintsInterface::kEnableSctpDataChannels, - &value, NULL) && value; // DTLS has to be enabled to use SCTP. - if (sctp_enabled && dtls_enabled) { + if (!options.disable_sctp_data_channels && dtls_enabled_) { LOG(LS_INFO) << "Allowing SCTP data engine."; data_channel_type_ = cricket::DCT_SCTP; } @@ -455,6 +494,14 @@ bool WebRtcSession::Initialize( mediastream_signaling_->SetDataChannelFactory(this); } + // Find DSCP constraint. + if (FindConstraint( + constraints, + MediaConstraintsInterface::kEnableDscp, + &value, NULL)) { + dscp_enabled_ = value; + } + const cricket::VideoCodec default_codec( JsepSessionDescription::kDefaultVideoCodecId, JsepSessionDescription::kDefaultVideoCodecName, @@ -473,10 +520,15 @@ bool WebRtcSession::Initialize( this, id(), data_channel_type_, - dtls_enabled)); + dtls_enabled_)); webrtc_session_desc_factory_->SignalIdentityReady.connect( this, &WebRtcSession::OnIdentityReady); + + if (options.disable_encryption) { + webrtc_session_desc_factory_->SetSecure(cricket::SEC_DISABLED); + } + return true; } @@ -502,13 +554,13 @@ bool WebRtcSession::StartCandidatesAllocation() { return true; } -void WebRtcSession::set_secure_policy( +void WebRtcSession::SetSecurePolicy( cricket::SecureMediaPolicy secure_policy) { - webrtc_session_desc_factory_->set_secure(secure_policy); + webrtc_session_desc_factory_->SetSecure(secure_policy); } -cricket::SecureMediaPolicy WebRtcSession::secure_policy() const { - return webrtc_session_desc_factory_->secure(); +cricket::SecureMediaPolicy WebRtcSession::SecurePolicy() const { + return webrtc_session_desc_factory_->Secure(); } bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) { @@ -558,7 +610,7 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, } cricket::SecureMediaPolicy secure_policy = - webrtc_session_desc_factory_->secure(); + webrtc_session_desc_factory_->Secure(); // Update the MediaContentDescription crypto settings as per the policy set. UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description()); @@ -587,6 +639,10 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc, // local session description. mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get()); + talk_base::SSLRole role; + if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) { + mediastream_signaling_->OnDtlsRoleReadyForSctp(role); + } if (error() != cricket::BaseSession::ERROR_NONE) { return BadLocalSdp(SessionErrorMsg(error()), err_desc); } @@ -639,6 +695,12 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc, ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(), desc); remote_desc_.reset(desc_temp.release()); + + talk_base::SSLRole role; + if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) { + mediastream_signaling_->OnDtlsRoleReadyForSctp(role); + } + if (error() != cricket::BaseSession::ERROR_NONE) { return BadRemoteSdp(SessionErrorMsg(error()), err_desc); } @@ -904,9 +966,63 @@ sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() { return &SignalVoiceChannelDestroyed; } +bool WebRtcSession::SendData(const cricket::SendDataParams& params, + const talk_base::Buffer& payload, + cricket::SendDataResult* result) { + if (!data_channel_.get()) { + LOG(LS_ERROR) << "SendData called when data_channel_ is NULL."; + return false; + } + return data_channel_->SendData(params, payload, result); +} + +bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) { + if (!data_channel_.get()) { + LOG(LS_ERROR) << "ConnectDataChannel called when data_channel_ is NULL."; + return false; + } + data_channel_->SignalReadyToSendData.connect(webrtc_data_channel, + &DataChannel::OnChannelReady); + data_channel_->SignalDataReceived.connect(webrtc_data_channel, + &DataChannel::OnDataReceived); + return true; +} + +void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) { + if (!data_channel_.get()) { + LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL."; + return; + } + data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel); + data_channel_->SignalDataReceived.disconnect(webrtc_data_channel); +} + +void WebRtcSession::AddSctpDataStream(uint32 sid) { + if (!data_channel_.get()) { + LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL."; + return; + } + data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid)); + data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid)); +} + +void WebRtcSession::RemoveSctpDataStream(uint32 sid) { + if (!data_channel_.get()) { + LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is " + << "NULL."; + return; + } + data_channel_->RemoveRecvStream(sid); + data_channel_->RemoveSendStream(sid); +} + +bool WebRtcSession::ReadyToSendData() const { + return data_channel_.get() && data_channel_->ready_to_send_data(); +} + talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel( - const std::string& label, - const DataChannelInit* config) { + const std::string& label, + const DataChannelInit* config) { if (state() == STATE_RECEIVEDTERMINATE) { return NULL; } @@ -918,11 +1034,13 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel( if (data_channel_type_ == cricket::DCT_SCTP) { if (new_config.id < 0) { - if (!mediastream_signaling_->AllocateSctpId(&new_config.id)) { + talk_base::SSLRole role; + if (GetSslRole(&role) && + !mediastream_signaling_->AllocateSctpSid(role, &new_config.id)) { LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel."; return NULL; } - } else if (!mediastream_signaling_->IsSctpIdAvailable(new_config.id)) { + } else if (!mediastream_signaling_->IsSctpSidAvailable(new_config.id)) { LOG(LS_ERROR) << "Failed to create a SCTP data channel " << "because the id is already in use or out of range."; return NULL; @@ -930,32 +1048,10 @@ talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel( } talk_base::scoped_refptr<DataChannel> channel( - DataChannel::Create(this, label, &new_config)); - if (channel == NULL) - return NULL; - if (!mediastream_signaling_->AddDataChannel(channel)) + DataChannel::Create(this, data_channel_type_, label, &new_config)); + if (channel && !mediastream_signaling_->AddDataChannel(channel)) return NULL; - if (data_channel_type_ == cricket::DCT_SCTP) { - if (config == NULL) { - LOG(LS_WARNING) << "Could not send data channel OPEN message" - << " because of NULL config."; - return NULL; - } - if (data_channel_.get()) { - channel->SetReceiveSsrc(new_config.id); - channel->SetSendSsrc(new_config.id); - } - if (!config->negotiated) { - talk_base::Buffer *payload = new talk_base::Buffer; - if (!mediastream_signaling_->WriteDataChannelOpenMessage( - label, *config, payload)) { - LOG(LS_WARNING) << "Could not write data channel OPEN message"; - } - // SendControl may queue the message until the data channel's set up, - // or congestion clears. - channel->SendControl(payload); - } - } + return channel; } @@ -1268,24 +1364,43 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) { voice_channel_.reset(channel_manager_->CreateVoiceChannel( this, content->name, true)); - return (voice_channel_ != NULL); + if (!voice_channel_.get()) + return false; + + if (dscp_enabled_) { + cricket::AudioOptions options; + options.dscp.Set(true); + voice_channel_->SetChannelOptions(options); + } + return true; } bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) { video_channel_.reset(channel_manager_->CreateVideoChannel( this, content->name, true, voice_channel_.get())); - return (video_channel_ != NULL); + if (!video_channel_.get()) + return false; + + if (dscp_enabled_) { + cricket::VideoOptions options; + options.dscp.Set(true); + video_channel_->SetChannelOptions(options); + } + return true; } bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) { - bool rtcp = (data_channel_type_ == cricket::DCT_RTP); + bool sctp = (data_channel_type_ == cricket::DCT_SCTP); data_channel_.reset(channel_manager_->CreateDataChannel( - this, content->name, rtcp, data_channel_type_)); + this, content->name, !sctp, data_channel_type_)); if (!data_channel_.get()) { return false; } - data_channel_->SignalDataReceived.connect( - this, &WebRtcSession::OnDataReceived); + if (sctp) { + mediastream_signaling_->OnDataTransportCreatedForSctp(); + data_channel_->SignalNewStreamReceived.connect( + this, &WebRtcSession::OnNewDataChannelReceived); + } return true; } @@ -1302,27 +1417,11 @@ void WebRtcSession::CopySavedCandidates( saved_candidates_.clear(); } -// Look for OPEN messages and set up data channels in response. -void WebRtcSession::OnDataReceived( - cricket::DataChannel* channel, - const cricket::ReceiveDataParams& params, - const talk_base::Buffer& payload) { - if (params.type != cricket::DMT_CONTROL) { - return; - } - - std::string label; - DataChannelInit config; - if (!mediastream_signaling_->ParseDataChannelOpenMessage( - payload, &label, &config)) { - LOG(LS_WARNING) << "Failed to parse data channel OPEN message."; - return; - } - - config.negotiated = true; // This is the negotiation. - +void WebRtcSession::OnNewDataChannelReceived( + const std::string& label, const DataChannelInit& init) { + ASSERT(data_channel_type_ == cricket::DCT_SCTP); if (!mediastream_signaling_->AddDataChannelFromOpenMessage( - label, config)) { + label, init)) { LOG(LS_WARNING) << "Failed to create data channel from OPEN message."; return; } @@ -1383,9 +1482,15 @@ bool WebRtcSession::ValidateSessionDescription( } // Verify crypto settings. - if (webrtc_session_desc_factory_->secure() == cricket::SEC_REQUIRED && - !VerifyCrypto(sdesc->description())) { - return BadSdp(source, kSdpWithoutCrypto, error_desc); + std::string crypto_error; + if (webrtc_session_desc_factory_->Secure() == cricket::SEC_REQUIRED && + !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) { + return BadSdp(source, crypto_error, error_desc); + } + + // Verify ice-ufrag and ice-pwd. + if (!VerifyIceUfragPwdPresent(sdesc->description())) { + return BadSdp(source, kSdpWithoutIceUfragPwd, error_desc); } if (!ValidateBundleSettings(sdesc->description())) { 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 0cb049f4983..4c83906ae98 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsession.h @@ -42,20 +42,17 @@ #include "talk/session/media/mediasession.h" namespace cricket { - +class BaseChannel; class ChannelManager; class DataChannel; class StatsReport; class Transport; class VideoCapturer; -class BaseChannel; class VideoChannel; class VoiceChannel; - } // namespace cricket namespace webrtc { - class IceRestartAnswerLatch; class MediaStreamSignaling; class WebRtcSessionDescriptionFactory; @@ -68,6 +65,8 @@ extern const char kInvalidCandidates[]; extern const char kInvalidSdp[]; extern const char kMlineMismatch[]; extern const char kSdpWithoutCrypto[]; +extern const char kSdpWithoutSdesAndDtlsDisabled[]; +extern const char kSdpWithoutIceUfragPwd[]; extern const char kSessionError[]; extern const char kUpdateStateFailed[]; extern const char kPushDownOfferTDFailed[]; @@ -77,6 +76,7 @@ extern const char kPushDownAnswerTDFailed[]; // ICE state callback interface. class IceObserver { public: + IceObserver() {} // Called any time the IceConnectionState changes virtual void OnIceConnectionChange( PeerConnectionInterface::IceConnectionState new_state) {} @@ -92,13 +92,17 @@ class IceObserver { protected: ~IceObserver() {} + + private: + DISALLOW_COPY_AND_ASSIGN(IceObserver); }; class WebRtcSession : public cricket::BaseSession, public AudioProviderInterface, public DataChannelFactory, public VideoProviderInterface, - public DtmfProviderInterface { + public DtmfProviderInterface, + public DataChannelProviderInterface { public: WebRtcSession(cricket::ChannelManager* channel_manager, talk_base::Thread* signaling_thread, @@ -107,7 +111,8 @@ class WebRtcSession : public cricket::BaseSession, MediaStreamSignaling* mediastream_signaling); virtual ~WebRtcSession(); - bool Initialize(const MediaConstraintsInterface* constraints, + bool Initialize(const PeerConnectionFactoryInterface::Options& options, + const MediaConstraintsInterface* constraints, DTLSIdentityServiceInterface* dtls_identity_service); // Deletes the voice, video and data channel and changes the session state // to STATE_RECEIVEDTERMINATE. @@ -127,8 +132,8 @@ class WebRtcSession : public cricket::BaseSession, return data_channel_.get(); } - void set_secure_policy(cricket::SecureMediaPolicy secure_policy); - cricket::SecureMediaPolicy secure_policy() const; + void SetSecurePolicy(cricket::SecureMediaPolicy secure_policy); + cricket::SecureMediaPolicy SecurePolicy() const; // Get current ssl role from transport. bool GetSslRole(talk_base::SSLRole* role); @@ -180,6 +185,16 @@ class WebRtcSession : public cricket::BaseSession, int code, int duration); virtual sigslot::signal0<>* GetOnDestroyedSignal(); + // Implements DataChannelProviderInterface. + virtual bool SendData(const cricket::SendDataParams& params, + const talk_base::Buffer& payload, + cricket::SendDataResult* result) OVERRIDE; + virtual bool ConnectDataChannel(DataChannel* webrtc_data_channel) OVERRIDE; + virtual void DisconnectDataChannel(DataChannel* webrtc_data_channel) OVERRIDE; + virtual void AddSctpDataStream(uint32 sid) OVERRIDE; + virtual void RemoveSctpDataStream(uint32 sid) OVERRIDE; + virtual bool ReadyToSendData() const OVERRIDE; + talk_base::scoped_refptr<DataChannel> CreateDataChannel( const std::string& label, const DataChannelInit* config); @@ -260,10 +275,8 @@ class WebRtcSession : public cricket::BaseSession, // The |saved_candidates_| will be cleared after this function call. void CopySavedCandidates(SessionDescriptionInterface* dest_desc); - void OnDataReceived( - cricket::DataChannel* channel, - const cricket::ReceiveDataParams& params, - const talk_base::Buffer& payload); + void OnNewDataChannelReceived(const std::string& label, + const DataChannelInit& init); bool GetLocalTrackId(uint32 ssrc, std::string* track_id); bool GetRemoteTrackId(uint32 ssrc, std::string* track_id); @@ -299,6 +312,9 @@ class WebRtcSession : public cricket::BaseSession, std::vector<IceCandidateInterface*> saved_candidates_; // 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, @@ -315,8 +331,9 @@ class WebRtcSession : public cricket::BaseSession, sigslot::signal0<> SignalVoiceChannelDestroyed; sigslot::signal0<> SignalVideoChannelDestroyed; sigslot::signal0<> SignalDataChannelDestroyed; -}; + DISALLOW_COPY_AND_ASSIGN(WebRtcSession); +}; } // namespace webrtc #endif // TALK_APP_WEBRTC_WEBRTCSESSION_H_ 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 1531e0efc1a..5ec880a1db2 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 @@ -42,6 +42,7 @@ #include "talk/base/logging.h" #include "talk/base/network.h" #include "talk/base/physicalsocketserver.h" +#include "talk/base/ssladapter.h" #include "talk/base/sslstreamadapter.h" #include "talk/base/stringutils.h" #include "talk/base/thread.h" @@ -80,22 +81,26 @@ using webrtc::FakeConstraints; using webrtc::IceCandidateCollection; using webrtc::JsepIceCandidate; using webrtc::JsepSessionDescription; +using webrtc::PeerConnectionFactoryInterface; using webrtc::PeerConnectionInterface; using webrtc::SessionDescriptionInterface; using webrtc::StreamCollection; using webrtc::WebRtcSession; +using webrtc::kBundleWithoutRtcpMux; using webrtc::kMlineMismatch; +using webrtc::kPushDownAnswerTDFailed; +using webrtc::kPushDownPranswerTDFailed; using webrtc::kSdpWithoutCrypto; +using webrtc::kSdpWithoutIceUfragPwd; +using webrtc::kSdpWithoutSdesAndDtlsDisabled; using webrtc::kSessionError; using webrtc::kSetLocalSdpFailed; using webrtc::kSetRemoteSdpFailed; -using webrtc::kPushDownAnswerTDFailed; -using webrtc::kPushDownPranswerTDFailed; -using webrtc::kBundleWithoutRtcpMux; -static const SocketAddress kClientAddr1("11.11.11.11", 0); -static const SocketAddress kClientAddr2("22.22.22.22", 0); -static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT); +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 char kSessionVersion[] = "1"; @@ -109,10 +114,9 @@ static const char kMediaContentName1[] = "video"; static const int kIceCandidatesTimeout = 10000; -static const cricket::AudioCodec - kTelephoneEventCodec(106, "telephone-event", 8000, 0, 1, 0); -static const cricket::AudioCodec kCNCodec1(102, "CN", 8000, 0, 1, 0); -static const cricket::AudioCodec kCNCodec2(103, "CN", 16000, 0, 1, 0); +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"; // Add some extra |newlines| to the |message| after |line|. static void InjectAfter(const std::string& line, @@ -151,11 +155,17 @@ class MockIceObserver : public webrtc::IceObserver { // Found a new candidate. virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { - if (candidate->sdp_mline_index() == kMediaContentIndex0) { - mline_0_candidates_.push_back(candidate->candidate()); - } else if (candidate->sdp_mline_index() == kMediaContentIndex1) { - mline_1_candidates_.push_back(candidate->candidate()); + switch (candidate->sdp_mline_index()) { + case kMediaContentIndex0: + mline_0_candidates_.push_back(candidate->candidate()); + break; + case kMediaContentIndex1: + mline_1_candidates_.push_back(candidate->candidate()); + break; + default: + ASSERT(false); } + // The ICE gathering state should always be Gathering when a candidate is // received (or possibly Completed in the case of the final candidate). EXPECT_NE(PeerConnectionInterface::kIceGatheringNew, ice_gathering_state_); @@ -273,8 +283,10 @@ class WebRtcSessionTest : public testing::Test { vss_(new talk_base::VirtualSocketServer(pss_.get())), fss_(new talk_base::FirewallSocketServer(vss_.get())), ss_scope_(fss_.get()), - stun_server_(talk_base::Thread::Current(), kStunAddr), - allocator_(&network_manager_, kStunAddr, + 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()) { tdesc_factory_->set_protocol(cricket::ICEPROTO_HYBRID); @@ -285,6 +297,14 @@ class WebRtcSessionTest : public testing::Test { desc_factory_->set_add_legacy_streams(false); } + static void SetUpTestCase() { + talk_base::InitializeSSL(); + } + + static void TearDownTestCase() { + talk_base::CleanupSSL(); + } + void AddInterface(const SocketAddress& addr) { network_manager_.AddInterface(addr); } @@ -302,11 +322,14 @@ class WebRtcSessionTest : public testing::Test { EXPECT_EQ(PeerConnectionInterface::kIceGatheringNew, observer_.ice_gathering_state_); - EXPECT_TRUE(session_->Initialize(constraints_.get(), identity_service)); + EXPECT_TRUE(session_->Initialize(options_, constraints_.get(), + identity_service)); } void InitWithDtmfCodec() { // Add kTelephoneEventCodec for dtmf test. + const cricket::AudioCodec kTelephoneEventCodec( + 106, "telephone-event", 8000, 0, 1, 0); std::vector<cricket::AudioCodec> codecs; codecs.push_back(kTelephoneEventCodec); media_engine_->SetAudioCodecs(codecs); @@ -353,12 +376,12 @@ class WebRtcSessionTest : public testing::Test { return observer->ReleaseDescription(); } - bool ChannelsExist() { + bool ChannelsExist() const { return (session_->voice_channel() != NULL && session_->video_channel() != NULL); } - void CheckTransportChannels() { + void CheckTransportChannels() const { EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 1) != NULL); EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 2) != NULL); EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, 1) != NULL); @@ -431,7 +454,6 @@ class WebRtcSessionTest : public testing::Test { talk_base::ToString(talk_base::CreateRandomId()); identity_.reset(talk_base::SSLIdentity::Generate(identity_name)); tdesc_factory_->set_identity(identity_.get()); - tdesc_factory_->set_digest_algorithm(talk_base::DIGEST_SHA_256); tdesc_factory_->set_secure(cricket::SEC_REQUIRED); } @@ -440,17 +462,9 @@ class WebRtcSessionTest : public testing::Test { const TransportInfo* audio = sdp->GetTransportInfoByName("audio"); ASSERT_TRUE(audio != NULL); ASSERT_EQ(expected, audio->description.identity_fingerprint.get() != NULL); - if (expected) { - ASSERT_EQ(std::string(talk_base::DIGEST_SHA_256), audio->description. - identity_fingerprint->algorithm); - } const TransportInfo* video = sdp->GetTransportInfoByName("video"); ASSERT_TRUE(video != NULL); ASSERT_EQ(expected, video->description.identity_fingerprint.get() != NULL); - if (expected) { - ASSERT_EQ(std::string(talk_base::DIGEST_SHA_256), video->description. - identity_fingerprint->algorithm); - } } void VerifyAnswerFromNonCryptoOffer() { @@ -510,6 +524,31 @@ class WebRtcSessionTest : public testing::Test { } EXPECT_TRUE(expect_equal); } + + void RemoveIceUfragPwdLines(const SessionDescriptionInterface* current_desc, + 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 empty 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"; + talk_base::replace_substrs(ufrag_line.c_str(), ufrag_line.length(), + "", 0, + sdp); + talk_base::replace_substrs(pwd_line.c_str(), pwd_line.length(), + "", 0, + 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 @@ -604,6 +643,35 @@ class WebRtcSessionTest : public testing::Test { kSessionVersion, current_desc); } + JsepSessionDescription* CreateRemoteOfferWithSctpPort( + const char* sctp_stream_name, int new_port, + cricket::MediaSessionOptions options) { + options.data_channel_type = cricket::DCT_SCTP; + options.AddStream(cricket::MEDIA_TYPE_DATA, "datachannel", + sctp_stream_name); + return ChangeSDPSctpPort(new_port, CreateRemoteOffer(options)); + } + + // Takes ownership of offer_basis (and deletes it). + JsepSessionDescription* ChangeSDPSctpPort( + int new_port, webrtc::SessionDescriptionInterface *offer_basis) { + // Stringify the input SDP, swap the 5000 for 'new_port' and create a new + // SessionDescription from the mutated string. + const char* default_port_str = "5000"; + char new_port_str[16]; + talk_base::sprintfn(new_port_str, sizeof(new_port_str), "%d", new_port); + std::string offer_str; + offer_basis->ToString(&offer_str); + talk_base::replace_substrs(default_port_str, strlen(default_port_str), + new_port_str, strlen(new_port_str), + &offer_str); + JsepSessionDescription* offer = new JsepSessionDescription( + offer_basis->type()); + delete offer_basis; + offer->Initialize(offer_str, NULL); + return offer; + } + // Create a remote offer. Call mediastream_signaling_.UseOptionsWithStreamX() // before this function to decide which streams to create. JsepSessionDescription* CreateRemoteOffer() { @@ -648,7 +716,7 @@ class WebRtcSessionTest : public testing::Test { } void TestSessionCandidatesWithBundleRtcpMux(bool bundle, bool rtcp_mux) { - AddInterface(kClientAddr1); + AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); FakeConstraints constraints; @@ -718,7 +786,7 @@ class WebRtcSessionTest : public testing::Test { // New -> Checking -> Connected -> Disconnected -> Connected. // The Gathering state should go: New -> Gathering -> Completed. void TestLoopbackCall() { - AddInterface(kClientAddr1); + AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); SessionDescriptionInterface* offer = CreateOffer(NULL); @@ -753,7 +821,10 @@ class WebRtcSessionTest : public testing::Test { // Adding firewall rule to block ping requests, which should cause // transport channel failure. - fss_->AddRule(false, talk_base::FP_ANY, talk_base::FD_ANY, kClientAddr1); + fss_->AddRule(false, + talk_base::FP_ANY, + talk_base::FD_ANY, + talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionDisconnected, observer_.ice_connection_state_, kIceCandidatesTimeout); @@ -778,7 +849,10 @@ class WebRtcSessionTest : public testing::Test { // Adds CN codecs to FakeMediaEngine and MediaDescriptionFactory. void AddCNCodecs() { - // Add kTelephoneEventCodec for dtmf test. + const cricket::AudioCodec kCNCodec1(102, "CN", 8000, 0, 1, 0); + const cricket::AudioCodec kCNCodec2(103, "CN", 16000, 0, 1, 0); + + // Add kCNCodec for dtmf test. std::vector<cricket::AudioCodec> codecs = media_engine_->audio_codecs();; codecs.push_back(kCNCodec1); codecs.push_back(kCNCodec2); @@ -856,9 +930,11 @@ class WebRtcSessionTest : public testing::Test { talk_base::scoped_ptr<talk_base::VirtualSocketServer> vss_; talk_base::scoped_ptr<talk_base::FirewallSocketServer> fss_; talk_base::SocketServerScope ss_scope_; + talk_base::SocketAddress stun_socket_addr_; cricket::TestStunServer stun_server_; talk_base::FakeNetworkManager network_manager_; cricket::BasicPortAllocator allocator_; + PeerConnectionFactoryInterface::Options options_; talk_base::scoped_ptr<FakeConstraints> constraints_; FakeMediaStreamSignaling mediastream_signaling_; talk_base::scoped_ptr<WebRtcSessionForTest> session_; @@ -878,7 +954,7 @@ TEST_F(WebRtcSessionTest, TestInitializeWithDtls) { // Verifies that WebRtcSession uses SEC_REQUIRED by default. TEST_F(WebRtcSessionTest, TestDefaultSetSecurePolicy) { Init(NULL); - EXPECT_EQ(cricket::SEC_REQUIRED, session_->secure_policy()); + EXPECT_EQ(cricket::SEC_REQUIRED, session_->SecurePolicy()); } TEST_F(WebRtcSessionTest, TestSessionCandidates) { @@ -896,8 +972,8 @@ TEST_F(WebRtcSessionTest, TestSessionCandidatesWithBundleRtcpMux) { } TEST_F(WebRtcSessionTest, TestMultihomeCandidates) { - AddInterface(kClientAddr1); - AddInterface(kClientAddr2); + AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); + AddInterface(talk_base::SocketAddress(kClientAddrHost2, kClientAddrPort)); Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); InitiateCall(); @@ -907,13 +983,16 @@ TEST_F(WebRtcSessionTest, TestMultihomeCandidates) { } TEST_F(WebRtcSessionTest, TestStunError) { - AddInterface(kClientAddr1); - AddInterface(kClientAddr2); - fss_->AddRule(false, talk_base::FP_UDP, talk_base::FD_ANY, kClientAddr1); + AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); + AddInterface(talk_base::SocketAddress(kClientAddrHost2, kClientAddrPort)); + fss_->AddRule(false, + talk_base::FP_UDP, + talk_base::FD_ANY, + talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); InitiateCall(); - // Since kClientAddr1 is blocked, not expecting stun candidates for it. + // Since kClientAddrHost1 is blocked, not expecting stun candidates for it. EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout); EXPECT_EQ(6u, observer_.mline_0_candidates_.size()); EXPECT_EQ(6u, observer_.mline_1_candidates_.size()); @@ -1357,7 +1436,7 @@ TEST_F(WebRtcSessionTest, TestRemoteCandidatesAddedToSessionDescription) { // Test that local candidates are added to the local session description and // that they are retained if the local session description is changed. TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) { - AddInterface(kClientAddr1); + AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); CreateAndSetRemoteOfferAndLocalAnswer(); @@ -1421,7 +1500,7 @@ TEST_F(WebRtcSessionTest, TestSetRemoteSessionDescriptionWithCandidates) { // Test that offers and answers contains ice candidates when Ice candidates have // been gathered. TEST_F(WebRtcSessionTest, TestSetLocalAndRemoteDescriptionWithCandidates) { - AddInterface(kClientAddr1); + AddInterface(talk_base::SocketAddress(kClientAddrHost1, kClientAddrPort)); Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); // Ice is started but candidates are not provided until SetLocalDescription @@ -1872,8 +1951,8 @@ TEST_F(WebRtcSessionTest, VerifyCryptoParamsInSDP) { } TEST_F(WebRtcSessionTest, VerifyNoCryptoParamsInSDP) { + options_.disable_encryption = true; Init(NULL); - session_->set_secure_policy(cricket::SEC_DISABLED); mediastream_signaling_.SendAudioVideoStream1(); scoped_ptr<SessionDescriptionInterface> offer( CreateOffer(NULL)); @@ -1890,6 +1969,31 @@ TEST_F(WebRtcSessionTest, VerifyAnswerFromCryptoOffer) { VerifyAnswerFromCryptoOffer(); } +// This test verifies that setLocalDescription fails if +// no a=ice-ufrag and a=ice-pwd lines are present in the SDP. +TEST_F(WebRtcSessionTest, TestSetLocalDescriptionWithoutIce) { + Init(NULL); + mediastream_signaling_.SendAudioVideoStream1(); + talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL)); + std::string sdp; + RemoveIceUfragPwdLines(offer.get(), &sdp); + SessionDescriptionInterface* modified_offer = + CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL); + SetLocalDescriptionExpectError(kSdpWithoutIceUfragPwd, modified_offer); +} + +// This test verifies that setRemoteDescription fails if +// no a=ice-ufrag and a=ice-pwd lines are present in the SDP. +TEST_F(WebRtcSessionTest, TestSetRemoteDescriptionWithoutIce) { + Init(NULL); + talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateRemoteOffer()); + std::string sdp; + RemoveIceUfragPwdLines(offer.get(), &sdp); + SessionDescriptionInterface* modified_offer = + CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL); + SetRemoteDescriptionExpectError(kSdpWithoutIceUfragPwd, modified_offer); +} + TEST_F(WebRtcSessionTest, VerifyBundleFlagInPA) { // This test verifies BUNDLE flag in PortAllocator, if BUNDLE information in // local description is removed by the application, BUNDLE flag should be @@ -2337,9 +2441,9 @@ TEST_F(WebRtcSessionTest, TestCryptoAfterSetLocalDescription) { // This test verifies the crypto parameter when security is disabled. TEST_F(WebRtcSessionTest, TestCryptoAfterSetLocalDescriptionWithDisabled) { + options_.disable_encryption = true; Init(NULL); mediastream_signaling_.SendAudioVideoStream1(); - session_->set_secure_policy(cricket::SEC_DISABLED); talk_base::scoped_ptr<SessionDescriptionInterface> offer( CreateOffer(NULL)); @@ -2478,8 +2582,8 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { constraints_.reset(new FakeConstraints()); constraints_->AddOptional( webrtc::MediaConstraintsInterface::kEnableRtpDataChannels, true); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); + options_.disable_sctp_data_channels = false; + InitWithDtls(false); SetLocalDescriptionWithDataChannel(); @@ -2489,9 +2593,6 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { TEST_F(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); InitWithDtls(false); talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL)); @@ -2502,9 +2603,6 @@ TEST_F(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) { TEST_F(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); SetFactoryDtlsSrtp(); - constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); InitWithDtls(false); // Create remote offer with SCTP. @@ -2524,8 +2622,6 @@ TEST_F(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) { TEST_F(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) { constraints_.reset(new FakeConstraints()); constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); - constraints_->AddOptional( webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, false); InitWithDtls(false); @@ -2536,15 +2632,75 @@ TEST_F(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) { TEST_F(WebRtcSessionTest, TestSctpDataChannelWithDtls) { MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); - constraints_.reset(new FakeConstraints()); - constraints_->AddOptional( - webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true); InitWithDtls(false); SetLocalDescriptionWithDataChannel(); EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); } +TEST_F(WebRtcSessionTest, TestDisableSctpDataChannels) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + options_.disable_sctp_data_channels = true; + InitWithDtls(false); + + SetLocalDescriptionWithDataChannel(); + EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type()); +} + +TEST_F(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) { + MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp); + const int new_send_port = 9998; + const int new_recv_port = 7775; + + InitWithDtls(false); + SetFactoryDtlsSrtp(); + + // By default, don't actually add the codecs to desc_factory_; they don't + // actually get serialized for SCTP in BuildMediaDescription(). Instead, + // let the session description get parsed. That'll get the proper codecs + // into the stream. + cricket::MediaSessionOptions options; + JsepSessionDescription* offer = CreateRemoteOfferWithSctpPort( + "stream1", new_send_port, options); + + // SetRemoteDescription will take the ownership of the offer. + SetRemoteDescriptionWithoutError(offer); + + SessionDescriptionInterface* answer = ChangeSDPSctpPort( + new_recv_port, CreateAnswer(NULL)); + ASSERT_TRUE(answer != NULL); + + // Now set the local description, which'll take ownership of the answer. + SetLocalDescriptionWithoutError(answer); + + // TEST PLAN: Set the port number to something new, set it in the SDP, + // and pass it all the way down. + webrtc::DataChannelInit dci; + dci.reliable = true; + EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); + talk_base::scoped_refptr<webrtc::DataChannel> dc = + session_->CreateDataChannel("datachannel", &dci); + + cricket::FakeDataMediaChannel* ch = data_engine_->GetChannel(0); + int portnum = -1; + ASSERT_TRUE(ch != NULL); + ASSERT_EQ(1UL, ch->send_codecs().size()); + EXPECT_EQ(cricket::kGoogleSctpDataCodecId, ch->send_codecs()[0].id); + EXPECT_TRUE(!strcmp(cricket::kGoogleSctpDataCodecName, + ch->send_codecs()[0].name.c_str())); + EXPECT_TRUE(ch->send_codecs()[0].GetParam(cricket::kCodecParamPort, + &portnum)); + EXPECT_EQ(new_send_port, portnum); + + ASSERT_EQ(1UL, ch->recv_codecs().size()); + EXPECT_EQ(cricket::kGoogleSctpDataCodecId, ch->recv_codecs()[0].id); + EXPECT_TRUE(!strcmp(cricket::kGoogleSctpDataCodecName, + ch->recv_codecs()[0].name.c_str())); + EXPECT_TRUE(ch->recv_codecs()[0].GetParam(cricket::kCodecParamPort, + &portnum)); + EXPECT_EQ(new_recv_port, portnum); +} + // Verifies that CreateOffer succeeds when CreateOffer is called before async // identity generation is finished. TEST_F(WebRtcSessionTest, TestCreateOfferBeforeIdentityRequestReturnSuccess) { @@ -2629,6 +2785,54 @@ TEST_F(WebRtcSessionTest, VerifyMultipleAsyncCreateDescription( false, CreateSessionDescriptionRequest::kAnswer); } + +// Verifies that setRemoteDescription fails when DTLS is disabled and the remote +// offer has no SDES crypto but only DTLS fingerprint. +TEST_F(WebRtcSessionTest, TestSetRemoteOfferFailIfDtlsDisabledAndNoCrypto) { + // Init without DTLS. + Init(NULL); + // Create a remote offer with secured transport disabled. + cricket::MediaSessionOptions options; + JsepSessionDescription* offer(CreateRemoteOffer( + options, cricket::SEC_DISABLED)); + // Adds a DTLS fingerprint to the remote offer. + cricket::SessionDescription* sdp = offer->description(); + TransportInfo* audio = sdp->GetTransportInfoByName("audio"); + ASSERT_TRUE(audio != NULL); + ASSERT_TRUE(audio->description.identity_fingerprint.get() == NULL); + audio->description.identity_fingerprint.reset( + talk_base::SSLFingerprint::CreateFromRfc4572( + talk_base::DIGEST_SHA_256, kFakeDtlsFingerprint)); + SetRemoteDescriptionExpectError(kSdpWithoutSdesAndDtlsDisabled, + offer); +} + +// This test verifies DSCP is properly applied on the media channels. +TEST_F(WebRtcSessionTest, TestDscpConstraint) { + constraints_.reset(new FakeConstraints()); + constraints_->AddOptional( + webrtc::MediaConstraintsInterface::kEnableDscp, true); + Init(NULL); + mediastream_signaling_.SendAudioVideoStream1(); + SessionDescriptionInterface* offer = CreateOffer(NULL); + + SetLocalDescriptionWithoutError(offer); + + video_channel_ = media_engine_->GetVideoChannel(0); + voice_channel_ = media_engine_->GetVoiceChannel(0); + + ASSERT_TRUE(video_channel_ != NULL); + ASSERT_TRUE(voice_channel_ != NULL); + cricket::AudioOptions audio_options; + EXPECT_TRUE(voice_channel_->GetOptions(&audio_options)); + cricket::VideoOptions video_options; + EXPECT_TRUE(video_channel_->GetOptions(&video_options)); + EXPECT_TRUE(audio_options.dscp.IsSet()); + EXPECT_TRUE(audio_options.dscp.GetWithDefaultIfUnset(false)); + EXPECT_TRUE(video_options.dscp.IsSet()); + EXPECT_TRUE(video_options.dscp.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 30c49a718ad..b6f523ce0cf 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.cc +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.cc @@ -33,10 +33,10 @@ #include "talk/app/webrtc/mediastreamsignaling.h" #include "talk/app/webrtc/webrtcsession.h" -namespace webrtc { +using cricket::MediaSessionOptions; +namespace webrtc { namespace { - static const char kFailedDueToIdentityFailed[] = " failed because DTLS identity request failed"; @@ -46,25 +46,24 @@ static const char kWebRTCIdentityName[] = "WebRTC"; static const uint64 kInitSessionVersion = 2; -typedef cricket::MediaSessionOptions::Stream Stream; -typedef cricket::MediaSessionOptions::Streams Streams; - -static bool CompareStream(const Stream& stream1, const Stream& stream2) { - return (stream1.id < stream2.id); +static bool CompareStream(const MediaSessionOptions::Stream& stream1, + const MediaSessionOptions::Stream& stream2) { + return stream1.id < stream2.id; } -static bool SameId(const Stream& stream1, const Stream& stream2) { - return (stream1.id == stream2.id); +static bool SameId(const MediaSessionOptions::Stream& stream1, + const MediaSessionOptions::Stream& stream2) { + return stream1.id == stream2.id; } // Checks if each Stream within the |streams| has unique id. -static bool ValidStreams(const Streams& streams) { - Streams sorted_streams = streams; +static bool ValidStreams(const MediaSessionOptions::Streams& streams) { + MediaSessionOptions::Streams sorted_streams = streams; std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream); - Streams::iterator it = + MediaSessionOptions::Streams::iterator it = std::adjacent_find(sorted_streams.begin(), sorted_streams.end(), SameId); - return (it == sorted_streams.end()); + return it == sorted_streams.end(); } enum { @@ -83,7 +82,6 @@ struct CreateSessionDescriptionMsg : public talk_base::MessageData { std::string error; talk_base::scoped_ptr<webrtc::SessionDescriptionInterface> description; }; - } // namespace // static @@ -130,33 +128,35 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory( transport_desc_factory_.set_protocol(cricket::ICEPROTO_HYBRID); session_desc_factory_.set_add_legacy_streams(false); // By default SRTP-SDES is enabled in WebRtc. - set_secure(cricket::SEC_REQUIRED); - - if (dtls_enabled) { - if (identity_service_.get()) { - identity_request_observer_ = - new talk_base::RefCountedObject<WebRtcIdentityRequestObserver>(); - - identity_request_observer_->SignalRequestFailed.connect( - this, &WebRtcSessionDescriptionFactory::OnIdentityRequestFailed); - identity_request_observer_->SignalIdentityReady.connect( - this, &WebRtcSessionDescriptionFactory::OnIdentityReady); - - if (identity_service_->RequestIdentity(kWebRTCIdentityName, - kWebRTCIdentityName, - identity_request_observer_)) { - LOG(LS_VERBOSE) << "DTLS-SRTP enabled; sent DTLS identity request."; - identity_request_state_ = IDENTITY_WAITING; - } else { - LOG(LS_ERROR) << "Failed to send DTLS identity request."; - identity_request_state_ = IDENTITY_FAILED; - } - } else { + SetSecure(cricket::SEC_REQUIRED); + + if (!dtls_enabled) { + return; + } + + if (identity_service_.get()) { + identity_request_observer_ = + new talk_base::RefCountedObject<WebRtcIdentityRequestObserver>(); + + identity_request_observer_->SignalRequestFailed.connect( + this, &WebRtcSessionDescriptionFactory::OnIdentityRequestFailed); + identity_request_observer_->SignalIdentityReady.connect( + this, &WebRtcSessionDescriptionFactory::OnIdentityReady); + + if (identity_service_->RequestIdentity(kWebRTCIdentityName, + kWebRTCIdentityName, + identity_request_observer_)) { + LOG(LS_VERBOSE) << "DTLS-SRTP enabled; sent DTLS identity request."; identity_request_state_ = IDENTITY_WAITING; - // Do not generate the identity in the constructor since the caller has - // not got a chance to connect to SignalIdentityReady. - signaling_thread_->Post(this, MSG_GENERATE_IDENTITY, NULL); + } else { + LOG(LS_ERROR) << "Failed to send DTLS identity request."; + identity_request_state_ = IDENTITY_FAILED; } + } else { + identity_request_state_ = IDENTITY_WAITING; + // Do not generate the identity in the constructor since the caller has + // not got a chance to connect to SignalIdentityReady. + signaling_thread_->Post(this, MSG_GENERATE_IDENTITY, NULL); } } @@ -261,19 +261,15 @@ void WebRtcSessionDescriptionFactory::CreateAnswer( } } -void WebRtcSessionDescriptionFactory::set_secure( +void WebRtcSessionDescriptionFactory::SetSecure( cricket::SecureMediaPolicy secure_policy) { session_desc_factory_.set_secure(secure_policy); } -cricket::SecureMediaPolicy WebRtcSessionDescriptionFactory::secure() const { +cricket::SecureMediaPolicy WebRtcSessionDescriptionFactory::Secure() const { return session_desc_factory_.secure(); } -bool WebRtcSessionDescriptionFactory::waiting_for_identity() const { - return identity_request_state_ == IDENTITY_WAITING; -} - void WebRtcSessionDescriptionFactory::OnMessage(talk_base::Message* msg) { switch (msg->message_id) { case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: { @@ -438,7 +434,6 @@ void WebRtcSessionDescriptionFactory::SetIdentity( SignalIdentityReady(identity); transport_desc_factory_.set_identity(identity); - transport_desc_factory_.set_digest_algorithm(talk_base::DIGEST_SHA_256); transport_desc_factory_.set_secure(cricket::SEC_ENABLED); while (!create_session_description_requests_.empty()) { @@ -451,5 +446,4 @@ void WebRtcSessionDescriptionFactory::SetIdentity( create_session_description_requests_.pop(); } } - } // namespace webrtc 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 ba34e913749..ca614b4356d 100644 --- a/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.h +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/webrtcsessiondescriptionfactory.h @@ -34,21 +34,17 @@ #include "talk/session/media/mediasession.h" namespace cricket { - class ChannelManager; class TransportDescriptionFactory; - } // namespace cricket namespace webrtc { - class CreateSessionDescriptionObserver; class MediaConstraintsInterface; class MediaStreamSignaling; class SessionDescriptionInterface; class WebRtcSession; - // DTLS identity request callback class. class WebRtcIdentityRequestObserver : public DTLSIdentityRequestObserver, public sigslot::has_slots<> { @@ -116,13 +112,15 @@ class WebRtcSessionDescriptionFactory : public talk_base::MessageHandler, CreateSessionDescriptionObserver* observer, const MediaConstraintsInterface* constraints); - void set_secure(cricket::SecureMediaPolicy secure_policy); - cricket::SecureMediaPolicy secure() const; + void SetSecure(cricket::SecureMediaPolicy secure_policy); + cricket::SecureMediaPolicy Secure() const; sigslot::signal1<talk_base::SSLIdentity*> SignalIdentityReady; // For testing. - bool waiting_for_identity() const; + bool waiting_for_identity() const { + return identity_request_state_ == IDENTITY_WAITING; + } private: enum IdentityRequestState { @@ -166,7 +164,6 @@ class WebRtcSessionDescriptionFactory : public talk_base::MessageHandler, DISALLOW_COPY_AND_ASSIGN(WebRtcSessionDescriptionFactory); }; - } // namespace webrtc #endif // TALK_APP_WEBRTC_WEBRTCSESSIONDESCRIPTIONFACTORY_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/asyncpacketsocket.h b/chromium/third_party/libjingle/source/talk/base/asyncpacketsocket.h index 3b4748f510f..29ab55ffc47 100644 --- a/chromium/third_party/libjingle/source/talk/base/asyncpacketsocket.h +++ b/chromium/third_party/libjingle/source/talk/base/asyncpacketsocket.h @@ -31,9 +31,30 @@ #include "talk/base/dscp.h" #include "talk/base/sigslot.h" #include "talk/base/socket.h" +#include "talk/base/timeutils.h" namespace talk_base { +// This structure will have the information about when packet is actually +// received by socket. +struct PacketTime { + PacketTime() : timestamp(-1), not_before(-1) {} + PacketTime(int64 timestamp, int64 not_before) + : timestamp(timestamp), not_before(not_before) { + } + + int64 timestamp; // Receive time after socket delivers the data. + int64 not_before; // Earliest possible time the data could have arrived, + // indicating the potential error in the |timestamp| value, + // in case the system, is busy. For example, the time of + // the last select() call. + // If unknown, this value will be set to zero. +}; + +inline PacketTime CreatePacketTime(int64 not_before) { + return PacketTime(TimeMicros(), not_before); +} + // Provides the ability to receive packets asynchronously. Sends are not // buffered since it is acceptable to drop packets under high load. class AsyncPacketSocket : public sigslot::has_slots<> { @@ -78,8 +99,9 @@ class AsyncPacketSocket : public sigslot::has_slots<> { // Emitted each time a packet is read. Used only for UDP and // connected TCP sockets. - sigslot::signal4<AsyncPacketSocket*, const char*, size_t, - const SocketAddress&> SignalReadPacket; + sigslot::signal5<AsyncPacketSocket*, const char*, size_t, + const SocketAddress&, + const PacketTime&> SignalReadPacket; // Emitted when the socket is currently able to send. sigslot::signal1<AsyncPacketSocket*> SignalReadyToSend; diff --git a/chromium/third_party/libjingle/source/talk/base/asyncresolverinterface.h b/chromium/third_party/libjingle/source/talk/base/asyncresolverinterface.h new file mode 100644 index 00000000000..4d77c4f8881 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/asyncresolverinterface.h @@ -0,0 +1,64 @@ +/* + * 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_ASYNCRESOLVERINTERFACE_H_ +#define TALK_BASE_ASYNCRESOLVERINTERFACE_H_ + +#include "talk/base/sigslot.h" +#include "talk/base/socketaddress.h" + +namespace talk_base { + +// This interface defines the methods to resolve the address asynchronously. +class AsyncResolverInterface { + public: + AsyncResolverInterface() {} + virtual ~AsyncResolverInterface() {} + + // Start address resolve process. + virtual void Start(const SocketAddress& addr) = 0; + // Returns top most resolved address of |family| + virtual bool GetResolvedAddress(int family, SocketAddress* addr) const = 0; + // Returns error from resolver. + virtual int GetError() const = 0; + // Delete the resolver. + virtual void Destroy(bool wait) = 0; + // Returns top most resolved IPv4 address if address is resolved successfully. + // Otherwise returns address set in SetAddress. + SocketAddress address() const { + SocketAddress addr; + GetResolvedAddress(AF_INET, &addr); + return addr; + } + + // This signal is fired when address resolve process is completed. + sigslot::signal1<AsyncResolverInterface*> SignalDone; +}; + +} // namespace talk_base + +#endif diff --git a/chromium/third_party/libjingle/source/talk/base/asyncsocket.h b/chromium/third_party/libjingle/source/talk/base/asyncsocket.h index 3d12984b70d..97859a7527a 100644 --- a/chromium/third_party/libjingle/source/talk/base/asyncsocket.h +++ b/chromium/third_party/libjingle/source/talk/base/asyncsocket.h @@ -44,8 +44,16 @@ class AsyncSocket : public Socket { virtual AsyncSocket* Accept(SocketAddress* paddr) = 0; - sigslot::signal1<AsyncSocket*> SignalReadEvent; // ready to read - sigslot::signal1<AsyncSocket*> SignalWriteEvent; // ready to write + // SignalReadEvent and SignalWriteEvent use multi_threaded_local to allow + // access concurrently from different thread. + // For example SignalReadEvent::connect will be called in AsyncUDPSocket ctor + // but at the same time the SocketDispatcher maybe signaling the read event. + // ready to read + sigslot::signal1<AsyncSocket*, + sigslot::multi_threaded_local> SignalReadEvent; + // ready to write + sigslot::signal1<AsyncSocket*, + sigslot::multi_threaded_local> SignalWriteEvent; sigslot::signal1<AsyncSocket*> SignalConnectEvent; // connected sigslot::signal2<AsyncSocket*, int> SignalCloseEvent; // closed }; diff --git a/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.cc b/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.cc index 517e799c422..d2ae513fd57 100644 --- a/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.cc +++ b/chromium/third_party/libjingle/source/talk/base/asynctcpsocket.cc @@ -300,7 +300,8 @@ void AsyncTCPSocket::ProcessInput(char * data, size_t* len) { if (*len < kPacketLenSize + pkt_len) return; - SignalReadPacket(this, data + kPacketLenSize, pkt_len, remote_addr); + SignalReadPacket(this, data + kPacketLenSize, pkt_len, remote_addr, + CreatePacketTime(0)); *len -= kPacketLenSize + pkt_len; if (*len > 0) { diff --git a/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.cc b/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.cc index 97e5dff9836..50052630d99 100644 --- a/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.cc +++ b/chromium/third_party/libjingle/source/talk/base/asyncudpsocket.cc @@ -128,7 +128,8 @@ void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) { // TODO: Make sure that we got all of the packet. // If we did not, then we should resize our buffer to be large enough. - SignalReadPacket(this, buf_, (size_t)len, remote_addr); + SignalReadPacket(this, buf_, static_cast<size_t>(len), remote_addr, + CreatePacketTime(0)); } void AsyncUDPSocket::OnWriteEvent(AsyncSocket* socket) { diff --git a/chromium/third_party/libjingle/source/talk/base/atomicops.h b/chromium/third_party/libjingle/source/talk/base/atomicops.h index 94ade69730e..068cd5f3dc2 100644 --- a/chromium/third_party/libjingle/source/talk/base/atomicops.h +++ b/chromium/third_party/libjingle/source/talk/base/atomicops.h @@ -79,7 +79,7 @@ class FixedSizeLockFreeQueue { FixedSizeLockFreeQueue() : pushed_count_(0), popped_count_(0), capacity_(0), - data_(NULL) {} + data_() {} // Constructs an empty queue with the given capacity. FixedSizeLockFreeQueue(size_t capacity) : pushed_count_(0), popped_count_(0), @@ -157,7 +157,7 @@ class FixedSizeLockFreeQueue { volatile Atomic32 pushed_count_; volatile Atomic32 popped_count_; size_t capacity_; - talk_base::scoped_array<T> data_; + talk_base::scoped_ptr<T[]> data_; DISALLOW_COPY_AND_ASSIGN(FixedSizeLockFreeQueue); }; diff --git a/chromium/third_party/libjingle/source/talk/base/autodetectproxy.cc b/chromium/third_party/libjingle/source/talk/base/autodetectproxy.cc index 02cbaade517..a32043c1cad 100644 --- a/chromium/third_party/libjingle/source/talk/base/autodetectproxy.cc +++ b/chromium/third_party/libjingle/source/talk/base/autodetectproxy.cc @@ -82,7 +82,10 @@ void AutoDetectProxy::DoWork() { } void AutoDetectProxy::OnMessage(Message *msg) { - if (MSG_TIMEOUT == msg->message_id) { + if (MSG_UNRESOLVABLE == msg->message_id) { + // If we can't resolve the proxy, skip straight to failure. + Complete(PROXY_UNKNOWN); + } else if (MSG_TIMEOUT == msg->message_id) { OnCloseEvent(socket_, ETIMEDOUT); } else { // This must be the ST_MSG_WORKER_DONE message that deletes the @@ -136,22 +139,24 @@ void AutoDetectProxy::OnMessage(Message *msg) { } } -void AutoDetectProxy::OnResolveResult(SignalThread* thread) { - if (thread != resolver_) { +void AutoDetectProxy::OnResolveResult(AsyncResolverInterface* resolver) { + if (resolver != resolver_) { return; } - int error = resolver_->error(); + int error = resolver_->GetError(); if (error == 0) { LOG(LS_VERBOSE) << "Resolved " << proxy_.address << " to " << resolver_->address(); proxy_.address = resolver_->address(); - DoConnect(); + if (!DoConnect()) { + Thread::Current()->Post(this, MSG_TIMEOUT); + } } else { LOG(LS_INFO) << "Failed to resolve " << resolver_->address(); resolver_->Destroy(false); resolver_ = NULL; proxy_.address = SocketAddress(); - Thread::Current()->Post(this, MSG_TIMEOUT); + Thread::Current()->Post(this, MSG_UNRESOLVABLE); } } @@ -166,6 +171,7 @@ void AutoDetectProxy::Next() { if (socket_) { Thread::Current()->Clear(this, MSG_TIMEOUT); + Thread::Current()->Clear(this, MSG_UNRESOLVABLE); socket_->Close(); Thread::Current()->Dispose(socket_); socket_ = NULL; @@ -177,17 +183,18 @@ void AutoDetectProxy::Next() { if (!resolver_) { resolver_ = new AsyncResolver(); } - resolver_->set_address(proxy_.address); - resolver_->SignalWorkDone.connect(this, - &AutoDetectProxy::OnResolveResult); - resolver_->Start(); + resolver_->SignalDone.connect(this, &AutoDetectProxy::OnResolveResult); + resolver_->Start(proxy_.address); } else { - DoConnect(); + if (!DoConnect()) { + Thread::Current()->Post(this, MSG_TIMEOUT); + return; + } } Thread::Current()->PostDelayed(timeout, this, MSG_TIMEOUT); } -void AutoDetectProxy::DoConnect() { +bool AutoDetectProxy::DoConnect() { if (resolver_) { resolver_->Destroy(false); resolver_ = NULL; @@ -197,16 +204,18 @@ void AutoDetectProxy::DoConnect() { proxy_.address.family(), SOCK_STREAM); if (!socket_) { LOG(LS_VERBOSE) << "Unable to create socket for " << proxy_.address; - return; + return false; } socket_->SignalConnectEvent.connect(this, &AutoDetectProxy::OnConnectEvent); socket_->SignalReadEvent.connect(this, &AutoDetectProxy::OnReadEvent); socket_->SignalCloseEvent.connect(this, &AutoDetectProxy::OnCloseEvent); socket_->Connect(proxy_.address); + return true; } void AutoDetectProxy::Complete(ProxyType type) { Thread::Current()->Clear(this, MSG_TIMEOUT); + Thread::Current()->Clear(this, MSG_UNRESOLVABLE); if (socket_) { socket_->Close(); } diff --git a/chromium/third_party/libjingle/source/talk/base/autodetectproxy.h b/chromium/third_party/libjingle/source/talk/base/autodetectproxy.h index a6ad3d1134a..2cbeb82754e 100644 --- a/chromium/third_party/libjingle/source/talk/base/autodetectproxy.h +++ b/chromium/third_party/libjingle/source/talk/base/autodetectproxy.h @@ -42,7 +42,7 @@ namespace talk_base { // AutoDetectProxy /////////////////////////////////////////////////////////////////////////////// -class AsyncResolver; +class AsyncResolverInterface; class AsyncSocket; class AutoDetectProxy : public SignalThread { @@ -72,6 +72,7 @@ class AutoDetectProxy : public SignalThread { return GetProxySettingsForUrl(agent, url, proxy, true); } enum { MSG_TIMEOUT = SignalThread::ST_MSG_FIRST_AVAILABLE, + MSG_UNRESOLVABLE, ADP_MSG_FIRST_AVAILABLE}; protected: @@ -87,14 +88,14 @@ class AutoDetectProxy : public SignalThread { void OnConnectEvent(AsyncSocket * socket); void OnReadEvent(AsyncSocket * socket); void OnCloseEvent(AsyncSocket * socket, int error); - void OnResolveResult(SignalThread* thread); - void DoConnect(); + void OnResolveResult(AsyncResolverInterface* resolver); + bool DoConnect(); private: std::string agent_; std::string server_url_; ProxyInfo proxy_; - AsyncResolver* resolver_; + AsyncResolverInterface* resolver_; AsyncSocket* socket_; int next_; diff --git a/chromium/third_party/libjingle/source/talk/base/autodetectproxy_unittest.cc b/chromium/third_party/libjingle/source/talk/base/autodetectproxy_unittest.cc index 3fca4c6b49c..18241a3e815 100644 --- a/chromium/third_party/libjingle/source/talk/base/autodetectproxy_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/autodetectproxy_unittest.cc @@ -37,9 +37,16 @@ static const char kPath[] = "/"; static const char kHost[] = "relay.google.com"; static const uint16 kPort = 443; static const bool kSecure = true; -// Each of the two stages in AutoDetectProxy has a 2-second time-out, so 5 -// seconds total should be enough. -static const int kTimeoutMs = 5000; +// At most, AutoDetectProxy should take ~6 seconds. Each connect step is +// allotted 2 seconds, with the initial resolution + connect given an +// extra 2 seconds. The slowest case is: +// 1) Resolution + HTTPS takes full 4 seconds and fails (but resolution +// succeeds). +// 2) SOCKS5 takes the full 2 seconds. +// Socket creation time seems unbounded, and has been observed to take >1 second +// on a linux machine under load. As such, we allow for 10 seconds for timeout, +// though could still end up with some flakiness. +static const int kTimeoutMs = 10000; class AutoDetectProxyTest : public testing::Test, public sigslot::has_slots<> { public: diff --git a/chromium/third_party/libjingle/source/talk/base/base64.cc b/chromium/third_party/libjingle/source/talk/base/base64.cc index 7765f107e6d..79b045e80e2 100644 --- a/chromium/third_party/libjingle/source/talk/base/base64.cc +++ b/chromium/third_party/libjingle/source/talk/base/base64.cc @@ -20,7 +20,6 @@ #include "talk/base/common.h" -using std::string; using std::vector; namespace talk_base { @@ -96,7 +95,8 @@ bool Base64::IsBase64Encoded(const std::string& str) { return true; } -void Base64::EncodeFromArray(const void* data, size_t len, string* result) { +void Base64::EncodeFromArray(const void* data, size_t len, + std::string* result) { ASSERT(NULL != result); result->clear(); result->resize(((len + 2) / 3) * 4); @@ -190,8 +190,9 @@ size_t Base64::GetNextQuantum(DecodeFlags parse_flags, bool illegal_pads, } bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags, - string* result, size_t* data_used) { - return DecodeFromArrayTemplate<string>(data, len, flags, result, data_used); + std::string* result, size_t* data_used) { + return DecodeFromArrayTemplate<std::string>( + data, len, flags, result, data_used); } bool Base64::DecodeFromArray(const char* data, size_t len, DecodeFlags flags, diff --git a/chromium/third_party/libjingle/source/talk/base/buffer.h b/chromium/third_party/libjingle/source/talk/base/buffer.h index 311cfad726c..47096332c58 100644 --- a/chromium/third_party/libjingle/source/talk/base/buffer.h +++ b/chromium/third_party/libjingle/source/talk/base/buffer.h @@ -88,7 +88,7 @@ class Buffer { } void SetCapacity(size_t capacity) { if (capacity > capacity_) { - talk_base::scoped_array<char> data(new char[capacity]); + talk_base::scoped_ptr<char[]> data(new char[capacity]); memcpy(data.get(), data_.get(), length_); data_.swap(data); capacity_ = capacity; @@ -109,7 +109,7 @@ class Buffer { SetData(data, length); } - scoped_array<char> data_; + scoped_ptr<char[]> data_; size_t length_; size_t capacity_; }; diff --git a/chromium/third_party/libjingle/source/talk/base/common.h b/chromium/third_party/libjingle/source/talk/base/common.h index b9aeca4f781..a76748b3905 100644 --- a/chromium/third_party/libjingle/source/talk/base/common.h +++ b/chromium/third_party/libjingle/source/talk/base/common.h @@ -25,7 +25,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_BASE_COMMON_H_ +#ifndef TALK_BASE_COMMON_H_ // NOLINT #define TALK_BASE_COMMON_H_ #include "talk/base/basictypes.h" @@ -64,7 +64,7 @@ inline void Unused(const void*) {} #define strnicmp(x, y, n) strncasecmp(x, y, n) #define stricmp(x, y) strcasecmp(x, y) -// TODO: Remove this. std::max should be used everywhere in the code. +// TODO(fbarchard): Remove this. std::max should be used everywhere in the code. // NOMINMAX must be defined where we include <windows.h>. #define stdmax(x, y) std::max(x, y) #else @@ -181,9 +181,32 @@ inline bool ImplicitCastToBool(bool result) { return result; } #if defined(WIN32) #define OVERRIDE override #elif defined(__clang__) +// Clang defaults to C++03 and warns about using override. Squelch that. +// Intentionally no push/pop here so all users of OVERRIDE ignore the warning +// too. This is like passing -Wno-c++11-extensions, except that GCC won't die +// (because it won't see this pragma). +#pragma clang diagnostic ignored "-Wc++11-extensions" +#define OVERRIDE override +#elif defined(__GNUC__) && __cplusplus >= 201103 && \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40700 +// GCC 4.7 supports explicit virtual overrides when C++11 support is enabled. #define OVERRIDE override #else #define OVERRIDE #endif -#endif // TALK_BASE_COMMON_H_ +// Annotate a function indicating the caller must examine the return value. +// Use like: +// int foo() WARN_UNUSED_RESULT; +// To explicitly ignore a result, see |ignore_result()| in <base/basictypes.h>. +// TODO(ajm): Hack to avoid multiple definitions until the base/ of webrtc and +// libjingle are merged. +#if !defined(WARN_UNUSED_RESULT) +#if defined(__GNUC__) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED_RESULT +#endif +#endif // WARN_UNUSED_RESULT + +#endif // TALK_BASE_COMMON_H_ // NOLINT diff --git a/chromium/third_party/libjingle/source/talk/base/compile_assert.h b/chromium/third_party/libjingle/source/talk/base/compile_assert.h new file mode 100644 index 00000000000..7252d080049 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/compile_assert.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +// COMPILE_ASSERT macro, borrowed from google3/base/macros.h. +#ifndef TALK_BASE_COMPILE_ASSERT_H_ +#define TALK_BASE_COMPILE_ASSERT_H_ + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +// TODO(ajm): Hack to avoid multiple definitions until the base/ of webrtc and +// libjingle are merged. +#if !defined(COMPILE_ASSERT) +template <bool> +struct CompileAssert { +}; + +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] // NOLINT +#endif // COMPILE_ASSERT + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outer parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert<bool(expr)> +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +#endif // TALK_BASE_COMPILE_ASSERT_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/constructormagic.h b/chromium/third_party/libjingle/source/talk/base/constructormagic.h index 8b1f7ffacb2..3023044e95d 100644 --- a/chromium/third_party/libjingle/source/talk/base/constructormagic.h +++ b/chromium/third_party/libjingle/source/talk/base/constructormagic.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. */ @@ -32,7 +32,10 @@ void operator=(const TypeName&) // A macro to disallow the evil copy constructor and operator= functions -// This should be used in the private: declarations for a class +// This should be used in the private: declarations for a class. +// Undefine this, just in case. Some third-party includes have their own +// version. +#undef DISALLOW_COPY_AND_ASSIGN #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ DISALLOW_ASSIGN(TypeName) diff --git a/chromium/third_party/libjingle/source/talk/base/cpumonitor.cc b/chromium/third_party/libjingle/source/talk/base/cpumonitor.cc index 0a708874027..e9b481fdbd9 100644 --- a/chromium/third_party/libjingle/source/talk/base/cpumonitor.cc +++ b/chromium/third_party/libjingle/source/talk/base/cpumonitor.cc @@ -210,7 +210,7 @@ float CpuSampler::GetSystemLoad() { } else { if (nt_query_system_information) { ULONG returned_length = 0; - scoped_array<SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION> processor_info( + scoped_ptr<SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[]> processor_info( new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[cpus_]); nt_query_system_information( ::SystemProcessorPerformanceInformation, @@ -281,6 +281,13 @@ float CpuSampler::GetSystemLoad() { const uint64 cpu_times = nice + system + user; const uint64 total_times = cpu_times + idle; #endif // defined(LINUX) || defined(ANDROID) + +#if defined(__native_client__) + // TODO(ryanpetrie): Implement this via PPAPI when it's available. + const uint64 cpu_times = 0; + const uint64 total_times = 0; +#endif // defined(__native_client__) + system_.prev_load_time_ = timenow; system_.prev_load_ = UpdateCpuLoad(total_times, cpu_times * cpus_, @@ -359,6 +366,12 @@ float CpuSampler::GetProcessLoad() { (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) * kNumMicrosecsPerSec + usage.ru_utime.tv_usec + usage.ru_stime.tv_usec; #endif // defined(LINUX) || defined(ANDROID) + +#if defined(__native_client__) + // TODO(ryanpetrie): Implement this via PPAPI when it's available. + const uint64 cpu_times = 0; +#endif // defined(__native_client__) + process_.prev_load_time_ = timenow; process_.prev_load_ = UpdateCpuLoad(total_times, cpu_times, 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 952b89e73ab..b9f5ba33e77 100644 --- a/chromium/third_party/libjingle/source/talk/base/cpumonitor_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/cpumonitor_unittest.cc @@ -54,6 +54,9 @@ class BusyThread : public talk_base::Thread { BusyThread(double load, double duration, double interval) : load_(load), duration_(duration), interval_(interval) { } + virtual ~BusyThread() { + Stop(); + } void Run() { Timing time; double busy_time = interval_ * load_ / 100.0; diff --git a/chromium/third_party/libjingle/source/talk/base/dbus.cc b/chromium/third_party/libjingle/source/talk/base/dbus.cc index 8e071c7c7a8..78e717a646c 100644 --- a/chromium/third_party/libjingle/source/talk/base/dbus.cc +++ b/chromium/third_party/libjingle/source/talk/base/dbus.cc @@ -158,6 +158,10 @@ class DBusMonitor::DBusMonitoringThread : public talk_base::Thread { ASSERT(filter_list_); } + virtual ~DBusMonitoringThread() { + Stop(); + } + // Override virtual method of Thread. Context: worker-thread. virtual void Run() { ASSERT(NULL == connection_); diff --git a/chromium/third_party/libjingle/source/talk/base/fakesslidentity.h b/chromium/third_party/libjingle/source/talk/base/fakesslidentity.h index f3c44e42255..203bb83bf0d 100644 --- a/chromium/third_party/libjingle/source/talk/base/fakesslidentity.h +++ b/chromium/third_party/libjingle/source/talk/base/fakesslidentity.h @@ -28,6 +28,9 @@ #ifndef TALK_BASE_FAKESSLIDENTITY_H_ #define TALK_BASE_FAKESSLIDENTITY_H_ +#include <algorithm> +#include <vector> + #include "talk/base/messagedigest.h" #include "talk/base/sslidentity.h" @@ -36,12 +39,31 @@ namespace talk_base { class FakeSSLCertificate : public talk_base::SSLCertificate { public: explicit FakeSSLCertificate(const std::string& data) : data_(data) {} + explicit FakeSSLCertificate(const std::vector<std::string>& certs) + : data_(certs.front()) { + std::vector<std::string>::const_iterator it; + // Skip certs[0]. + for (it = certs.begin() + 1; it != certs.end(); ++it) { + certs_.push_back(FakeSSLCertificate(*it)); + } + } virtual FakeSSLCertificate* GetReference() const { return new FakeSSLCertificate(*this); } virtual std::string ToPEMString() const { return data_; } + virtual void ToDER(Buffer* der_buffer) const { + std::string der_string; + VERIFY(SSLIdentity::PemToDer(kPemTypeCertificate, data_, &der_string)); + der_buffer->SetData(der_string.c_str(), der_string.size()); + } + 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; + return true; + } virtual bool ComputeDigest(const std::string &algorithm, unsigned char *digest, std::size_t size, std::size_t *length) const { @@ -49,13 +71,27 @@ class FakeSSLCertificate : public talk_base::SSLCertificate { digest, size); return (*length != 0); } + virtual bool GetChain(SSLCertChain** chain) const { + if (certs_.empty()) + return false; + std::vector<SSLCertificate*> new_certs(certs_.size()); + std::transform(certs_.begin(), certs_.end(), new_certs.begin(), DupCert); + *chain = new SSLCertChain(new_certs); + return true; + } + private: + static FakeSSLCertificate* DupCert(FakeSSLCertificate cert) { + return cert.GetReference(); + } std::string data_; + std::vector<FakeSSLCertificate> certs_; }; class FakeSSLIdentity : public talk_base::SSLIdentity { public: explicit FakeSSLIdentity(const std::string& data) : cert_(data) {} + explicit FakeSSLIdentity(const FakeSSLCertificate& cert) : cert_(cert) {} virtual FakeSSLIdentity* GetReference() const { return new FakeSSLIdentity(*this); } diff --git a/chromium/third_party/libjingle/source/talk/base/helpers.cc b/chromium/third_party/libjingle/source/talk/base/helpers.cc index bda940b87e2..b10a3f742a7 100644 --- a/chromium/third_party/libjingle/source/talk/base/helpers.cc +++ b/chromium/third_party/libjingle/source/talk/base/helpers.cc @@ -239,7 +239,7 @@ bool CreateRandomString(size_t len, const char* table, int table_size, std::string* str) { str->clear(); - scoped_array<uint8> bytes(new uint8[len]); + scoped_ptr<uint8[]> bytes(new uint8[len]); if (!Rng().Generate(bytes.get(), len)) { LOG(LS_ERROR) << "Failed to generate random string!"; return false; diff --git a/chromium/third_party/libjingle/source/talk/base/httpclient.cc b/chromium/third_party/libjingle/source/talk/base/httpclient.cc index 5a16676a331..5bee9114b83 100644 --- a/chromium/third_party/libjingle/source/talk/base/httpclient.cc +++ b/chromium/third_party/libjingle/source/talk/base/httpclient.cc @@ -316,11 +316,11 @@ void HttpClient::reset() { base_.abort(HE_OPERATION_CANCELLED); } -void HttpClient::OnResolveResult(SignalThread* thread) { - if (thread != resolver_) { +void HttpClient::OnResolveResult(AsyncResolverInterface* resolver) { + if (resolver != resolver_) { return; } - int error = resolver_->error(); + int error = resolver_->GetError(); server_ = resolver_->address(); resolver_->Destroy(false); resolver_ = NULL; @@ -335,9 +335,8 @@ void HttpClient::OnResolveResult(SignalThread* thread) { void HttpClient::StartDNSLookup() { resolver_ = new AsyncResolver(); - resolver_->set_address(server_); - resolver_->SignalWorkDone.connect(this, &HttpClient::OnResolveResult); - resolver_->Start(); + resolver_->SignalDone.connect(this, &HttpClient::OnResolveResult); + resolver_->Start(server_); } void HttpClient::set_server(const SocketAddress& address) { diff --git a/chromium/third_party/libjingle/source/talk/base/httpclient.h b/chromium/third_party/libjingle/source/talk/base/httpclient.h index 2e77b0d181e..03deb228639 100644 --- a/chromium/third_party/libjingle/source/talk/base/httpclient.h +++ b/chromium/third_party/libjingle/source/talk/base/httpclient.h @@ -175,7 +175,7 @@ protected: HttpError OnHeaderAvailable(bool ignore_data, bool chunked, size_t data_size); void StartDNSLookup(); - void OnResolveResult(SignalThread* thread); + void OnResolveResult(AsyncResolverInterface* resolver); // IHttpNotify Interface virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size); @@ -199,7 +199,7 @@ private: scoped_ptr<HttpAuthContext> context_; DiskCache* cache_; CacheState cache_state_; - AsyncResolver* resolver_; + AsyncResolverInterface* resolver_; }; ////////////////////////////////////////////////////////////////////// diff --git a/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.cc b/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.cc index 222621904bd..433844ee481 100644 --- a/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.cc +++ b/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.cc @@ -109,7 +109,23 @@ bool LateBindingSymbolTable::LoadFromPath(const char *dll_path) { } #ifdef POSIX - handle_ = dlopen(dll_path, RTLD_NOW); + handle_ = dlopen(dll_path, + // RTLD_NOW front-loads symbol resolution so that errors are + // caught early instead of causing a process abort later. + // RTLD_LOCAL prevents other modules from automatically + // seeing symbol definitions in the newly-loaded tree. This + // is necessary for same-named symbols in different ABI + // versions of the same library to not explode. + RTLD_NOW|RTLD_LOCAL +#ifdef LINUX + // RTLD_DEEPBIND makes symbol dependencies in the + // newly-loaded tree prefer to resolve to definitions within + // that tree (the default on OS X). This is necessary for + // same-named symbols in different ABI versions of the same + // library to not explode. + |RTLD_DEEPBIND +#endif + ); // NOLINT #else #error Not implemented #endif diff --git a/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.h b/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.h index a53648bfe54..f4ad5a60989 100644 --- a/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.h +++ b/chromium/third_party/libjingle/source/talk/base/latebindingsymboltable.h @@ -67,6 +67,9 @@ class LateBindingSymbolTable { bool LoadFromPath(const char *dll_path); void Unload(); + // Gets the raw OS handle to the DLL. Be careful what you do with it. + DllHandle GetDllHandle() const { return handle_; } + private: void ClearSymbols(); diff --git a/chromium/third_party/libjingle/source/talk/base/libdbusglibsymboltable.cc b/chromium/third_party/libjingle/source/talk/base/libdbusglibsymboltable.cc index 9c4be7f3d31..6a3ebf3b143 100644 --- a/chromium/third_party/libjingle/source/talk/base/libdbusglibsymboltable.cc +++ b/chromium/third_party/libjingle/source/talk/base/libdbusglibsymboltable.cc @@ -33,7 +33,7 @@ namespace talk_base { #define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME LIBDBUS_GLIB_CLASS_NAME #define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST LIBDBUS_GLIB_SYMBOLS_LIST -#define LATE_BINDING_SYMBOL_TABLE_DLL_NAME "libdbus-glib-1.so" +#define LATE_BINDING_SYMBOL_TABLE_DLL_NAME "libdbus-glib-1.so.2" #include "talk/base/latebindingsymboltable.cc.def" } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/linuxwindowpicker_unittest.cc b/chromium/third_party/libjingle/source/talk/base/linuxwindowpicker_unittest.cc index 5ea9c93fde6..c248bbac2f6 100644 --- a/chromium/third_party/libjingle/source/talk/base/linuxwindowpicker_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/linuxwindowpicker_unittest.cc @@ -28,6 +28,7 @@ #include "talk/base/gunit.h" #include "talk/base/linuxwindowpicker.h" #include "talk/base/logging.h" +#include "talk/base/testutils.h" #include "talk/base/windowpicker.h" #ifndef LINUX @@ -37,6 +38,7 @@ namespace talk_base { TEST(LinuxWindowPickerTest, TestGetWindowList) { + MAYBE_SKIP_SCREENCAST_TEST(); LinuxWindowPicker window_picker; WindowDescriptionList descriptions; window_picker.Init(); @@ -44,6 +46,7 @@ TEST(LinuxWindowPickerTest, TestGetWindowList) { } TEST(LinuxWindowPickerTest, TestGetDesktopList) { + MAYBE_SKIP_SCREENCAST_TEST(); LinuxWindowPicker window_picker; DesktopDescriptionList descriptions; EXPECT_TRUE(window_picker.Init()); diff --git a/chromium/third_party/libjingle/source/talk/base/logging.cc b/chromium/third_party/libjingle/source/talk/base/logging.cc index 6653d345199..4c7eae172e7 100644 --- a/chromium/third_party/libjingle/source/talk/base/logging.cc +++ b/chromium/third_party/libjingle/source/talk/base/logging.cc @@ -123,8 +123,6 @@ LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev, LogErrorContext err_ctx, int err, const char* module) : severity_(sev), warn_slow_logs_delay_(WARN_SLOW_LOGS_DELAY) { - // Android's logging facility keeps track of timestamp and thread. -#ifndef ANDROID if (timestamp_) { uint32 time = TimeSince(LogStartTime()); // Also ensure WallClockStartTime is initialized, so that it matches @@ -141,7 +139,6 @@ LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev, print_stream_ << "[" << std::hex << id << std::dec << "] "; #endif // WIN32 } -#endif // !ANDROID if (severity_ >= ctx_sev_) { print_stream_ << Describe(sev) << "(" << DescribeFile(file) diff --git a/chromium/third_party/libjingle/source/talk/base/logging.h b/chromium/third_party/libjingle/source/talk/base/logging.h index 2f341fa784f..49e126bab0c 100644 --- a/chromium/third_party/libjingle/source/talk/base/logging.h +++ b/chromium/third_party/libjingle/source/talk/base/logging.h @@ -41,6 +41,8 @@ // LOG_V(sev) Like LOG(), but sev is a run-time variable of the LoggingSeverity // type (basically, it just doesn't prepend the namespace). // LOG_F(sev) Like LOG(), but includes the name of the current function. +// LOG_T(sev) Like LOG(), but includes the this pointer. +// LOG_T_F(sev) Like LOG_F(), but includes the this pointer. // LOG_GLE(M)(sev [, mod]) attempt to add a string description of the // HRESULT returned by GetLastError. The "M" variant allows searching of a // DLL's string table for the error description. @@ -310,8 +312,10 @@ class LogMessageVoidify { // The _F version prefixes the message with the current function name. #if (defined(__GNUC__) && defined(_DEBUG)) || defined(WANT_PRETTY_LOG_F) #define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": " +#define LOG_T_F(sev) LOG(sev) << this << ": " << __PRETTY_FUNCTION__ << ": " #else #define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": " +#define LOG_T_F(sev) LOG(sev) << this << ": " << __FUNCTION__ << ": " #endif #define LOG_CHECK_LEVEL(sev) \ @@ -328,6 +332,8 @@ inline bool LogCheckLevel(LoggingSeverity sev) { talk_base::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \ .stream() +#define LOG_T(sev) LOG(sev) << this << ": " + #else // !LOGGING // Hopefully, the compiler will optimize away some of this code. @@ -348,6 +354,8 @@ inline bool LogCheckLevel(LoggingSeverity sev) { talk_base::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \ .stream() +#define LOG_T(sev) LOG(sev) << this << ": " +#define LOG_T_F(sev) LOG(sev) << this << ": " << __FUNCTION__ << #endif // !LOGGING #define LOG_ERRNO_EX(sev, err) \ diff --git a/chromium/third_party/libjingle/source/talk/base/logging_unittest.cc b/chromium/third_party/libjingle/source/talk/base/logging_unittest.cc index b0c219fa3f9..53cab666cc9 100644 --- a/chromium/third_party/libjingle/source/talk/base/logging_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/logging_unittest.cc @@ -87,6 +87,12 @@ TEST(LogTest, MultipleStreams) { // Ensure we don't crash when adding/removing streams while threads are going. // We should restore the correct global state at the end. class LogThread : public Thread { + public: + virtual ~LogThread() { + Stop(); + } + + private: void Run() { // LS_SENSITIVE to avoid cluttering up any real logging going on LOG(LS_SENSITIVE) << "LOG"; diff --git a/chromium/third_party/libjingle/source/talk/base/macasyncsocket.cc b/chromium/third_party/libjingle/source/talk/base/macasyncsocket.cc index 54ad6046160..7841b4bf596 100644 --- a/chromium/third_party/libjingle/source/talk/base/macasyncsocket.cc +++ b/chromium/third_party/libjingle/source/talk/base/macasyncsocket.cc @@ -87,7 +87,7 @@ void MacAsyncSocket::OnResolveResult(SignalThread* thread) { if (thread != resolver_) { return; } - int error = resolver_->error(); + int error = resolver_->GetError(); if (error == 0) { error = DoConnect(resolver_->address()); } else { @@ -109,10 +109,9 @@ int MacAsyncSocket::Connect(const SocketAddress& addr) { if (addr.IsUnresolved()) { LOG(LS_VERBOSE) << "Resolving addr in MacAsyncSocket::Connect"; resolver_ = new AsyncResolver(); - resolver_->set_address(addr); resolver_->SignalWorkDone.connect(this, &MacAsyncSocket::OnResolveResult); - resolver_->Start(); + resolver_->Start(addr); state_ = CS_CONNECTING; return 0; } diff --git a/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver_unittest.mm b/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver_unittest.mm index d6f4b2c11c9..818c30d860c 100644 --- a/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver_unittest.mm +++ b/chromium/third_party/libjingle/source/talk/base/maccocoasocketserver_unittest.mm @@ -36,6 +36,9 @@ class WakeThread : public Thread { public: WakeThread(SocketServer* ss) : ss_(ss) { } + virtual ~WakeThread() { + Stop(); + } void Run() { ss_->WakeUp(); } diff --git a/chromium/third_party/libjingle/source/talk/base/macsocketserver_unittest.cc b/chromium/third_party/libjingle/source/talk/base/macsocketserver_unittest.cc index a4a71019dd6..f10aebcde34 100644 --- a/chromium/third_party/libjingle/source/talk/base/macsocketserver_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/macsocketserver_unittest.cc @@ -37,6 +37,9 @@ class WakeThread : public Thread { public: WakeThread(SocketServer* ss) : ss_(ss) { } + virtual ~WakeThread() { + Stop(); + } void Run() { ss_->WakeUp(); } diff --git a/chromium/third_party/libjingle/source/talk/base/macutils.cc b/chromium/third_party/libjingle/source/talk/base/macutils.cc index c73b0fa6f9d..a1dcc04d424 100644 --- a/chromium/third_party/libjingle/source/talk/base/macutils.cc +++ b/chromium/third_party/libjingle/source/talk/base/macutils.cc @@ -38,27 +38,29 @@ namespace talk_base { /////////////////////////////////////////////////////////////////////////////// bool ToUtf8(const CFStringRef str16, std::string* str8) { - if ((NULL == str16) || (NULL == str8)) + if ((NULL == str16) || (NULL == str8)) { return false; + } size_t maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str16), - kCFStringEncodingUTF8) - + 1; - scoped_array<char> buffer(new char[maxlen]); + kCFStringEncodingUTF8) + 1; + scoped_ptr<char[]> buffer(new char[maxlen]); if (!buffer || !CFStringGetCString(str16, buffer.get(), maxlen, - kCFStringEncodingUTF8)) + kCFStringEncodingUTF8)) { return false; + } str8->assign(buffer.get()); return true; } bool ToUtf16(const std::string& str8, CFStringRef* str16) { - if (NULL == str16) + if (NULL == str16) { return false; + } *str16 = CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(str8.data()), str8.length(), kCFStringEncodingUTF8, false); - return (NULL != *str16); + return NULL != *str16; } #ifdef OSX @@ -100,23 +102,25 @@ static bool GetGestalt(OSType ostype, int* value) { bool GetOSVersion(int* major, int* minor, int* bugfix) { ASSERT(major && minor && bugfix); - if (!GetGestalt(gestaltSystemVersion, major)) + if (!GetGestalt(gestaltSystemVersion, major)) { return false; + } if (*major < 0x1040) { *bugfix = *major & 0xF; *minor = (*major >> 4) & 0xF; *major = (*major >> 8); return true; } - return GetGestalt(gestaltSystemVersionMajor, major) - && GetGestalt(gestaltSystemVersionMinor, minor) - && GetGestalt(gestaltSystemVersionBugFix, bugfix); + return GetGestalt(gestaltSystemVersionMajor, major) && + GetGestalt(gestaltSystemVersionMinor, minor) && + GetGestalt(gestaltSystemVersionBugFix, bugfix); } MacOSVersionName GetOSVersionName() { int major = 0, minor = 0, bugfix = 0; - if (!GetOSVersion(&major, &minor, &bugfix)) + if (!GetOSVersion(&major, &minor, &bugfix)) { return kMacOSUnknown; + } if (major > 10) { return kMacOSNewer; } @@ -136,14 +140,17 @@ MacOSVersionName GetOSVersionName() { return kMacOSLion; case 8: return kMacOSMountainLion; + case 9: + return kMacOSMavericks; } return kMacOSNewer; } bool GetQuickTimeVersion(std::string* out) { int ver; - if (!GetGestalt(gestaltQuickTimeVersion, &ver)) + if (!GetGestalt(gestaltQuickTimeVersion, &ver)) { return false; + } std::stringstream ss; ss << std::hex << ver; diff --git a/chromium/third_party/libjingle/source/talk/base/macutils.h b/chromium/third_party/libjingle/source/talk/base/macutils.h index ad5e7ad2be7..17c09ed413c 100644 --- a/chromium/third_party/libjingle/source/talk/base/macutils.h +++ b/chromium/third_party/libjingle/source/talk/base/macutils.h @@ -56,7 +56,8 @@ enum MacOSVersionName { kMacOSSnowLeopard, // 10.6 kMacOSLion, // 10.7 kMacOSMountainLion, // 10.8 - kMacOSNewer, // 10.9+ + kMacOSMavericks, // 10.9 + kMacOSNewer, // 10.10+ }; bool GetOSVersion(int* major, int* minor, int* bugfix); diff --git a/chromium/third_party/libjingle/source/talk/base/macutils_unittest.cc b/chromium/third_party/libjingle/source/talk/base/macutils_unittest.cc index 25858a27775..dfc211a05b5 100644 --- a/chromium/third_party/libjingle/source/talk/base/macutils_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/macutils_unittest.cc @@ -30,12 +30,14 @@ TEST(MacUtilsTest, GetOsVersionName) { talk_base::MacOSVersionName ver = talk_base::GetOSVersionName(); + LOG(LS_INFO) << "GetOsVersionName " << ver; EXPECT_NE(talk_base::kMacOSUnknown, ver); } TEST(MacUtilsTest, GetQuickTimeVersion) { std::string version; EXPECT_TRUE(talk_base::GetQuickTimeVersion(&version)); + LOG(LS_INFO) << "GetQuickTimeVersion " << version; } TEST(MacUtilsTest, RunAppleScriptCompileError) { diff --git a/chromium/third_party/libjingle/source/talk/base/md5digest_unittest.cc b/chromium/third_party/libjingle/source/talk/base/md5digest_unittest.cc index 40b19e5a3f2..9232b40aefd 100644 --- a/chromium/third_party/libjingle/source/talk/base/md5digest_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/md5digest_unittest.cc @@ -38,7 +38,7 @@ std::string Md5(const std::string& input) { TEST(Md5DigestTest, TestSize) { Md5Digest md5; - EXPECT_EQ(16U, Md5Digest::kSize); + EXPECT_EQ(16, static_cast<int>(Md5Digest::kSize)); EXPECT_EQ(16U, md5.Size()); } diff --git a/chromium/third_party/libjingle/source/talk/base/messagedigest.cc b/chromium/third_party/libjingle/source/talk/base/messagedigest.cc index 6136ae28b3e..d91d0674b5f 100644 --- a/chromium/third_party/libjingle/source/talk/base/messagedigest.cc +++ b/chromium/third_party/libjingle/source/talk/base/messagedigest.cc @@ -85,7 +85,7 @@ size_t ComputeDigest(const std::string& alg, const void* input, size_t in_len, } std::string ComputeDigest(MessageDigest* digest, const std::string& input) { - scoped_array<char> output(new char[digest->Size()]); + scoped_ptr<char[]> output(new char[digest->Size()]); ComputeDigest(digest, input.data(), input.size(), output.get(), digest->Size()); return hex_encode(output.get(), digest->Size()); @@ -120,7 +120,7 @@ size_t ComputeHmac(MessageDigest* digest, } // Copy the key to a block-sized buffer to simplify padding. // If the key is longer than a block, hash it and use the result instead. - scoped_array<uint8> new_key(new uint8[block_len]); + scoped_ptr<uint8[]> new_key(new uint8[block_len]); if (key_len > block_len) { ComputeDigest(digest, key, key_len, new_key.get(), block_len); memset(new_key.get() + digest->Size(), 0, block_len - digest->Size()); @@ -129,13 +129,13 @@ size_t ComputeHmac(MessageDigest* digest, memset(new_key.get() + key_len, 0, block_len - key_len); } // Set up the padding from the key, salting appropriately for each padding. - scoped_array<uint8> o_pad(new uint8[block_len]), i_pad(new uint8[block_len]); + scoped_ptr<uint8[]> o_pad(new uint8[block_len]), i_pad(new uint8[block_len]); for (size_t i = 0; i < block_len; ++i) { o_pad[i] = 0x5c ^ new_key[i]; i_pad[i] = 0x36 ^ new_key[i]; } // Inner hash; hash the inner padding, and then the input buffer. - scoped_array<uint8> inner(new uint8[digest->Size()]); + scoped_ptr<uint8[]> inner(new uint8[digest->Size()]); digest->Update(i_pad.get(), block_len); digest->Update(input, in_len); digest->Finish(inner.get(), digest->Size()); @@ -158,7 +158,7 @@ size_t ComputeHmac(const std::string& alg, const void* key, size_t key_len, std::string ComputeHmac(MessageDigest* digest, const std::string& key, const std::string& input) { - scoped_array<char> output(new char[digest->Size()]); + scoped_ptr<char[]> output(new char[digest->Size()]); ComputeHmac(digest, key.data(), key.size(), input.data(), input.size(), output.get(), digest->Size()); return hex_encode(output.get(), digest->Size()); diff --git a/chromium/third_party/libjingle/source/talk/base/move.h b/chromium/third_party/libjingle/source/talk/base/move.h new file mode 100644 index 00000000000..a04b7c897a8 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/move.h @@ -0,0 +1,207 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_WEBRTC_FILES_TALK_BASE_MOVE_H_ +#define THIRD_PARTY_WEBRTC_FILES_TALK_BASE_MOVE_H_ + +// Macro with the boilerplate that makes a type move-only in C++03. +// +// USAGE +// +// This macro should be used instead of DISALLOW_COPY_AND_ASSIGN to create +// a "move-only" type. Unlike DISALLOW_COPY_AND_ASSIGN, this macro should be +// the first line in a class declaration. +// +// A class using this macro must call .Pass() (or somehow be an r-value already) +// before it can be: +// +// * Passed as a function argument +// * Used as the right-hand side of an assignment +// * Returned from a function +// +// Each class will still need to define their own "move constructor" and "move +// operator=" to make this useful. Here's an example of the macro, the move +// constructor, and the move operator= from the scoped_ptr class: +// +// template <typename T> +// class scoped_ptr { +// TALK_MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) +// public: +// scoped_ptr(RValue& other) : ptr_(other.release()) { } +// scoped_ptr& operator=(RValue& other) { +// swap(other); +// return *this; +// } +// }; +// +// Note that the constructor must NOT be marked explicit. +// +// For consistency, the second parameter to the macro should always be RValue +// unless you have a strong reason to do otherwise. It is only exposed as a +// macro parameter so that the move constructor and move operator= don't look +// like they're using a phantom type. +// +// +// HOW THIS WORKS +// +// For a thorough explanation of this technique, see: +// +// http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor +// +// The summary is that we take advantage of 2 properties: +// +// 1) non-const references will not bind to r-values. +// 2) C++ can apply one user-defined conversion when initializing a +// variable. +// +// The first lets us disable the copy constructor and assignment operator +// by declaring private version of them with a non-const reference parameter. +// +// For l-values, direct initialization still fails like in +// DISALLOW_COPY_AND_ASSIGN because the copy constructor and assignment +// operators are private. +// +// For r-values, the situation is different. The copy constructor and +// assignment operator are not viable due to (1), so we are trying to call +// a non-existent constructor and non-existing operator= rather than a private +// one. Since we have not committed an error quite yet, we can provide an +// alternate conversion sequence and a constructor. We add +// +// * a private struct named "RValue" +// * a user-defined conversion "operator RValue()" +// * a "move constructor" and "move operator=" that take the RValue& as +// their sole parameter. +// +// Only r-values will trigger this sequence and execute our "move constructor" +// or "move operator=." L-values will match the private copy constructor and +// operator= first giving a "private in this context" error. This combination +// gives us a move-only type. +// +// For signaling a destructive transfer of data from an l-value, we provide a +// method named Pass() which creates an r-value for the current instance +// triggering the move constructor or move operator=. +// +// Other ways to get r-values is to use the result of an expression like a +// function call. +// +// Here's an example with comments explaining what gets triggered where: +// +// class Foo { +// TALK_MOVE_ONLY_TYPE_FOR_CPP_03(Foo, RValue); +// +// public: +// ... API ... +// Foo(RValue other); // Move constructor. +// Foo& operator=(RValue rhs); // Move operator= +// }; +// +// Foo MakeFoo(); // Function that returns a Foo. +// +// Foo f; +// Foo f_copy(f); // ERROR: Foo(Foo&) is private in this context. +// Foo f_assign; +// f_assign = f; // ERROR: operator=(Foo&) is private in this context. +// +// +// Foo f(MakeFoo()); // R-value so alternate conversion executed. +// Foo f_copy(f.Pass()); // R-value so alternate conversion executed. +// f = f_copy.Pass(); // R-value so alternate conversion executed. +// +// +// IMPLEMENTATION SUBTLETIES WITH RValue +// +// The RValue struct is just a container for a pointer back to the original +// object. It should only ever be created as a temporary, and no external +// class should ever declare it or use it in a parameter. +// +// It is tempting to want to use the RValue type in function parameters, but +// excluding the limited usage here for the move constructor and move +// operator=, doing so would mean that the function could take both r-values +// and l-values equially which is unexpected. See COMPARED To Boost.Move for +// more details. +// +// An alternate, and incorrect, implementation of the RValue class used by +// Boost.Move makes RValue a fieldless child of the move-only type. RValue& +// is then used in place of RValue in the various operators. The RValue& is +// "created" by doing *reinterpret_cast<RValue*>(this). This has the appeal +// of never creating a temporary RValue struct even with optimizations +// disabled. Also, by virtue of inheritance you can treat the RValue +// reference as if it were the move-only type itself. Unfortunately, +// using the result of this reinterpret_cast<> is actually undefined behavior +// due to C++98 5.2.10.7. In certain compilers (e.g., NaCl) the optimizer +// will generate non-working code. +// +// In optimized builds, both implementations generate the same assembly so we +// choose the one that adheres to the standard. +// +// +// COMPARED TO C++11 +// +// In C++11, you would implement this functionality using an r-value reference +// and our .Pass() method would be replaced with a call to std::move(). +// +// This emulation also has a deficiency where it uses up the single +// user-defined conversion allowed by C++ during initialization. This can +// cause problems in some API edge cases. For instance, in scoped_ptr, it is +// impossible to make a function "void Foo(scoped_ptr<Parent> p)" accept a +// value of type scoped_ptr<Child> even if you add a constructor to +// scoped_ptr<> that would make it look like it should work. C++11 does not +// have this deficiency. +// +// +// COMPARED TO Boost.Move +// +// Our implementation similar to Boost.Move, but we keep the RValue struct +// private to the move-only type, and we don't use the reinterpret_cast<> hack. +// +// In Boost.Move, RValue is the boost::rv<> template. This type can be used +// when writing APIs like: +// +// void MyFunc(boost::rv<Foo>& f) +// +// that can take advantage of rv<> to avoid extra copies of a type. However you +// would still be able to call this version of MyFunc with an l-value: +// +// Foo f; +// MyFunc(f); // Uh oh, we probably just destroyed |f| w/o calling Pass(). +// +// unless someone is very careful to also declare a parallel override like: +// +// void MyFunc(const Foo& f) +// +// that would catch the l-values first. This was declared unsafe in C++11 and +// a C++11 compiler will explicitly fail MyFunc(f). Unfortunately, we cannot +// ensure this in C++03. +// +// Since we have no need for writing such APIs yet, our implementation keeps +// RValue private and uses a .Pass() method to do the conversion instead of +// trying to write a version of "std::move()." Writing an API like std::move() +// would require the RValue struct to be public. +// +// +// CAVEATS +// +// If you include a move-only type as a field inside a class that does not +// explicitly declare a copy constructor, the containing class's implicit +// copy constructor will change from Containing(const Containing&) to +// Containing(Containing&). This can cause some unexpected errors. +// +// http://llvm.org/bugs/show_bug.cgi?id=11528 +// +// The workaround is to explicitly declare your copy constructor. +// +#define TALK_MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \ + private: \ + struct rvalue_type { \ + explicit rvalue_type(type* object) : object(object) {} \ + type* object; \ + }; \ + type(type&); \ + void operator=(type&); \ + public: \ + operator rvalue_type() { return rvalue_type(this); } \ + type Pass() { return type(rvalue_type(this)); } \ + private: + +#endif // THIRD_PARTY_WEBRTC_FILES_TALK_BASE_MOVE_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/natserver.cc b/chromium/third_party/libjingle/source/talk/base/natserver.cc index 483542591e4..46980487175 100644 --- a/chromium/third_party/libjingle/source/talk/base/natserver.cc +++ b/chromium/third_party/libjingle/source/talk/base/natserver.cc @@ -107,7 +107,7 @@ NATServer::~NATServer() { void NATServer::OnInternalPacket( AsyncPacketSocket* socket, const char* buf, size_t size, - const SocketAddress& addr) { + const SocketAddress& addr, const PacketTime& packet_time) { // Read the intended destination from the wire. SocketAddress dest_addr; @@ -123,7 +123,7 @@ void NATServer::OnInternalPacket( ASSERT(iter != int_map_->end()); // Allow the destination to send packets back to the source. - iter->second->whitelist->insert(dest_addr); + iter->second->WhitelistInsert(dest_addr); // Send the packet to its intended destination. iter->second->socket->SendTo(buf + length, size - length, dest_addr, @@ -132,7 +132,7 @@ void NATServer::OnInternalPacket( void NATServer::OnExternalPacket( AsyncPacketSocket* socket, const char* buf, size_t size, - const SocketAddress& remote_addr) { + const SocketAddress& remote_addr, const PacketTime& packet_time) { SocketAddress local_addr = socket->GetLocalAddress(); @@ -141,7 +141,7 @@ void NATServer::OnExternalPacket( ASSERT(iter != ext_map_->end()); // Allow the NAT to reject this packet. - if (Filter(iter->second, remote_addr)) { + if (ShouldFilterOut(iter->second, remote_addr)) { LOG(LS_INFO) << "Packet from " << remote_addr.ToSensitiveString() << " was filtered out by the NAT."; return; @@ -149,7 +149,7 @@ void NATServer::OnExternalPacket( // Forward this packet to the internal address. // First prepend the address in a quasi-STUN format. - scoped_array<char> real_buf(new char[size + kNATEncodedIPv6AddressSize]); + scoped_ptr<char[]> real_buf(new char[size + kNATEncodedIPv6AddressSize]); size_t addrlength = PackAddressForNAT(real_buf.get(), size + kNATEncodedIPv6AddressSize, remote_addr); @@ -173,8 +173,9 @@ void NATServer::Translate(const SocketAddressPair& route) { socket->SignalReadPacket.connect(this, &NATServer::OnExternalPacket); } -bool NATServer::Filter(TransEntry* entry, const SocketAddress& ext_addr) { - return entry->whitelist->find(ext_addr) == entry->whitelist->end(); +bool NATServer::ShouldFilterOut(TransEntry* entry, + const SocketAddress& ext_addr) { + return entry->WhitelistContains(ext_addr); } NATServer::TransEntry::TransEntry( @@ -188,4 +189,14 @@ NATServer::TransEntry::~TransEntry() { delete socket; } +void NATServer::TransEntry::WhitelistInsert(const SocketAddress& addr) { + CritScope cs(&crit_); + whitelist->insert(addr); +} + +bool NATServer::TransEntry::WhitelistContains(const SocketAddress& ext_addr) { + CritScope cs(&crit_); + return whitelist->find(ext_addr) == whitelist->end(); +} + } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/natserver.h b/chromium/third_party/libjingle/source/talk/base/natserver.h index 0a6083cbbdc..05d3475c92d 100644 --- a/chromium/third_party/libjingle/source/talk/base/natserver.h +++ b/chromium/third_party/libjingle/source/talk/base/natserver.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. */ @@ -79,9 +79,11 @@ class NATServer : public sigslot::has_slots<> { // Packets received on one of the networks. void OnInternalPacket(AsyncPacketSocket* socket, const char* buf, - size_t size, const SocketAddress& addr); + size_t size, const SocketAddress& addr, + const PacketTime& packet_time); void OnExternalPacket(AsyncPacketSocket* socket, const char* buf, - size_t size, const SocketAddress& remote_addr); + size_t size, const SocketAddress& remote_addr, + const PacketTime& packet_time); private: typedef std::set<SocketAddress, AddrCmp> AddressSet; @@ -91,9 +93,13 @@ class NATServer : public sigslot::has_slots<> { TransEntry(const SocketAddressPair& r, AsyncUDPSocket* s, NAT* nat); ~TransEntry(); + void WhitelistInsert(const SocketAddress& addr); + bool WhitelistContains(const SocketAddress& ext_addr); + SocketAddressPair route; AsyncUDPSocket* socket; AddressSet* whitelist; + CriticalSection crit_; }; typedef std::map<SocketAddressPair, TransEntry*, RouteCmp> InternalMap; @@ -103,7 +109,7 @@ class NATServer : public sigslot::has_slots<> { void Translate(const SocketAddressPair& route); /* Determines whether the NAT would filter out a packet from this address. */ - bool Filter(TransEntry* entry, const SocketAddress& ext_addr); + bool ShouldFilterOut(TransEntry* entry, const SocketAddress& ext_addr); NAT* nat_; SocketFactory* internal_; diff --git a/chromium/third_party/libjingle/source/talk/base/natsocketfactory.cc b/chromium/third_party/libjingle/source/talk/base/natsocketfactory.cc index a7c4240b3e9..395069e6658 100644 --- a/chromium/third_party/libjingle/source/talk/base/natsocketfactory.cc +++ b/chromium/third_party/libjingle/source/talk/base/natsocketfactory.cc @@ -85,7 +85,7 @@ size_t UnpackAddressFromNAT(const char* buf, size_t buf_size, class NATSocket : public AsyncSocket, public sigslot::has_slots<> { public: explicit NATSocket(NATInternalSocketFactory* sf, int family, int type) - : sf_(sf), family_(family), type_(type), async_(true), connected_(false), + : sf_(sf), family_(family), type_(type), connected_(false), socket_(NULL), buf_(NULL), size_(0) { } @@ -154,7 +154,7 @@ class NATSocket : public AsyncSocket, public sigslot::has_slots<> { return socket_->SendTo(data, size, addr); } // This array will be too large for IPv4 packets, but only by 12 bytes. - scoped_array<char> buf(new char[size + kNATEncodedIPv6AddressSize]); + scoped_ptr<char[]> buf(new char[size + kNATEncodedIPv6AddressSize]); size_t addrlength = PackAddressForNAT(buf.get(), size + kNATEncodedIPv6AddressSize, addr); @@ -312,7 +312,6 @@ class NATSocket : public AsyncSocket, public sigslot::has_slots<> { NATInternalSocketFactory* sf_; int family_; int type_; - bool async_; bool connected_; SocketAddress remote_addr_; SocketAddress server_addr_; // address of the NAT server diff --git a/chromium/third_party/libjingle/source/talk/base/nethelpers.cc b/chromium/third_party/libjingle/source/talk/base/nethelpers.cc index eebc6cfa7ea..e6310ac45fe 100644 --- a/chromium/third_party/libjingle/source/talk/base/nethelpers.cc +++ b/chromium/third_party/libjingle/source/talk/base/nethelpers.cc @@ -67,7 +67,27 @@ int ResolveHostname(const std::string& hostname, int family, } // AsyncResolver -AsyncResolver::AsyncResolver() : error_(0) { +AsyncResolver::AsyncResolver() : error_(-1) { +} + +void AsyncResolver::Start(const SocketAddress& addr) { + addr_ = addr; + // SignalThred Start will kickoff the resolve process. + SignalThread::Start(); +} + +bool AsyncResolver::GetResolvedAddress(int family, SocketAddress* addr) const { + if (error_ != 0 || addresses_.empty()) + return false; + + *addr = addr_; + for (size_t i = 0; i < addresses_.size(); ++i) { + if (family == addresses_[i].family()) { + addr->SetIP(addresses_[i]); + return true; + } + } + return false; } void AsyncResolver::DoWork() { @@ -76,9 +96,7 @@ void AsyncResolver::DoWork() { } void AsyncResolver::OnWorkDone() { - if (addresses_.size() > 0) { - addr_.SetIP(addresses_[0]); - } + SignalDone(this); } const char* inet_ntop(int af, const void *src, char* dst, socklen_t size) { @@ -109,7 +127,7 @@ bool HasIPv6Enabled() { return false; } DWORD protbuff_size = 4096; - scoped_array<char> protocols; + scoped_ptr<char[]> protocols; LPWSAPROTOCOL_INFOW protocol_infos = NULL; int requested_protocols[2] = {AF_INET6, 0}; diff --git a/chromium/third_party/libjingle/source/talk/base/nethelpers.h b/chromium/third_party/libjingle/source/talk/base/nethelpers.h index 66f79108f31..a49f48ac7cd 100644 --- a/chromium/third_party/libjingle/source/talk/base/nethelpers.h +++ b/chromium/third_party/libjingle/source/talk/base/nethelpers.h @@ -37,25 +37,30 @@ #include <list> +#include "talk/base/asyncresolverinterface.h" #include "talk/base/signalthread.h" #include "talk/base/sigslot.h" #include "talk/base/socketaddress.h" namespace talk_base { +class AsyncResolverTest; + // AsyncResolver will perform async DNS resolution, signaling the result on -// the inherited SignalWorkDone when the operation completes. -class AsyncResolver : public SignalThread { +// the SignalDone from AsyncResolverInterface when the operation completes. +class AsyncResolver : public SignalThread, public AsyncResolverInterface { public: AsyncResolver(); + virtual ~AsyncResolver() {} + + virtual void Start(const SocketAddress& addr); + virtual bool GetResolvedAddress(int family, SocketAddress* addr) const; + virtual int GetError() const { return error_; } + virtual void Destroy(bool wait) { SignalThread::Destroy(wait); } - const SocketAddress& address() const { return addr_; } const std::vector<IPAddress>& addresses() const { return addresses_; } - void set_address(const SocketAddress& addr) { addr_ = addr; } - int error() const { return error_; } void set_error(int error) { error_ = error; } - protected: virtual void DoWork(); virtual void OnWorkDone(); diff --git a/chromium/third_party/libjingle/source/talk/base/network.cc b/chromium/third_party/libjingle/source/talk/base/network.cc index d6367c32b37..d4dda138135 100644 --- a/chromium/third_party/libjingle/source/talk/base/network.cc +++ b/chromium/third_party/libjingle/source/talk/base/network.cc @@ -32,10 +32,18 @@ #include "talk/base/network.h" #ifdef POSIX +// linux/if.h can't be included at the same time as the posix sys/if.h, and +// it's transitively required by linux/route.h, so include that version on +// linux instead of the standard posix one. +#if defined(ANDROID) || defined(LINUX) +#include <linux/if.h> +#include <linux/route.h> +#else +#include <net/if.h> +#endif #include <sys/socket.h> #include <sys/utsname.h> #include <sys/ioctl.h> -#include <net/if.h> #include <unistd.h> #include <errno.h> #ifdef ANDROID @@ -173,7 +181,8 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, } BasicNetworkManager::BasicNetworkManager() - : thread_(NULL), sent_first_update_(false), start_count_(0) { + : thread_(NULL), sent_first_update_(false), start_count_(0), + ignore_non_default_routes_(false) { } BasicNetworkManager::~BasicNetworkManager() { @@ -307,7 +316,7 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, NetworkMap current_networks; // MSDN recommends a 15KB buffer for the first try at GetAdaptersAddresses. size_t buffer_size = 16384; - scoped_array<char> adapter_info(new char[buffer_size]); + scoped_ptr<char[]> adapter_info(new char[buffer_size]); PIP_ADAPTER_ADDRESSES adapter_addrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_info.get()); int adapter_flags = (GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST | @@ -397,14 +406,52 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, } #endif // WIN32 -bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) { +#if defined(ANDROID) || defined(LINUX) +bool IsDefaultRoute(const std::string& network_name) { + FileStream fs; + if (!fs.Open("/proc/net/route", "r", NULL)) { + LOG(LS_WARNING) << "Couldn't read /proc/net/route, skipping default " + << "route check (assuming everything is a default route)."; + return true; + } else { + std::string line; + while (fs.ReadLine(&line) == SR_SUCCESS) { + char iface_name[256]; + unsigned int iface_ip, iface_gw, iface_mask, iface_flags; + if (sscanf(line.c_str(), + "%255s %8X %8X %4X %*d %*u %*d %8X", + iface_name, &iface_ip, &iface_gw, + &iface_flags, &iface_mask) == 5 && + network_name == iface_name && + iface_mask == 0 && + (iface_flags & (RTF_UP | RTF_HOST)) == RTF_UP) { + return true; + } + } + } + return false; +} +#endif + +bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) const { + // Ignore networks on the explicit ignore list. + for (size_t i = 0; i < network_ignore_list_.size(); ++i) { + if (network.name() == network_ignore_list_[i]) { + return true; + } + } #ifdef POSIX - // Ignore local networks (lo, lo0, etc) - // Also filter out VMware interfaces, typically named vmnet1 and vmnet8 + // Filter out VMware interfaces, typically named vmnet1 and vmnet8 if (strncmp(network.name().c_str(), "vmnet", 5) == 0 || strncmp(network.name().c_str(), "vnic", 4) == 0) { return true; } +#if defined(ANDROID) || defined(LINUX) + // Make sure this is a default route, if we're ignoring non-defaults. + if (ignore_non_default_routes_ && !IsDefaultRoute(network.name())) { + return true; + } +#endif #elif defined(WIN32) // Ignore any HOST side vmware adapters with a description like: // VMware Virtual Ethernet Adapter for VMnet1 diff --git a/chromium/third_party/libjingle/source/talk/base/network.h b/chromium/third_party/libjingle/source/talk/base/network.h index f87063da5db..63f3e732fd9 100644 --- a/chromium/third_party/libjingle/source/talk/base/network.h +++ b/chromium/third_party/libjingle/source/talk/base/network.h @@ -127,6 +127,18 @@ class BasicNetworkManager : public NetworkManagerBase, virtual void OnMessage(Message* msg); bool started() { return start_count_ > 0; } + // Sets the network ignore list, which is empty by default. Any network on + // the ignore list will be filtered from network enumeration results. + void set_network_ignore_list(const std::vector<std::string>& list) { + network_ignore_list_ = list; + } +#if defined(ANDROID) || defined(LINUX) + // Sets the flag for ignoring non-default routes. + void set_ignore_non_default_routes(bool value) { + ignore_non_default_routes_ = true; + } +#endif + protected: #if defined(POSIX) // Separated from CreateNetworks for tests. @@ -139,7 +151,7 @@ class BasicNetworkManager : public NetworkManagerBase, bool CreateNetworks(bool include_ignored, NetworkList* networks) const; // Determines if a network should be ignored. - static bool IsIgnoredNetwork(const Network& network); + bool IsIgnoredNetwork(const Network& network) const; private: friend class NetworkTest; @@ -149,6 +161,8 @@ class BasicNetworkManager : public NetworkManagerBase, Thread* thread_; bool sent_first_update_; int start_count_; + std::vector<std::string> network_ignore_list_; + bool ignore_non_default_routes_; }; // Represents a Unix-type network interface, with a name and single address. 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 ee0f0aa09ea..e11e78daa4a 100644 --- a/chromium/third_party/libjingle/source/talk/base/network_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/network_unittest.cc @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2011, Google Inc. + * Copyright 2004 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,6 +37,9 @@ #endif #endif #include "talk/base/gunit.h" +#ifdef WIN32 +#include "talk/base/logging.h" // For LOG_GLE +#endif namespace talk_base { @@ -54,8 +57,9 @@ class NetworkTest : public testing::Test, public sigslot::has_slots<> { network_manager.MergeNetworkList(list, changed); } - bool IsIgnoredNetwork(const Network& network) { - return BasicNetworkManager::IsIgnoredNetwork(network); + bool IsIgnoredNetwork(BasicNetworkManager& network_manager, + const Network& network) { + return network_manager.IsIgnoredNetwork(network); } NetworkManager::NetworkList GetNetworks( @@ -96,11 +100,28 @@ TEST_F(NetworkTest, TestNetworkIgnore) { IPAddress(0x12345600U), 24); Network ipv4_network2("test_eth1", "Test Network Adapter 2", IPAddress(0x00010000U), 16); - EXPECT_FALSE(IsIgnoredNetwork(ipv4_network1)); - EXPECT_TRUE(IsIgnoredNetwork(ipv4_network2)); + BasicNetworkManager network_manager; + EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network1)); + EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network2)); +} + +TEST_F(NetworkTest, TestIgnoreList) { + Network ignore_me("ignore_me", "Ignore me please!", + IPAddress(0x12345600U), 24); + Network include_me("include_me", "Include me please!", + IPAddress(0x12345600U), 24); + BasicNetworkManager network_manager; + EXPECT_FALSE(IsIgnoredNetwork(network_manager, ignore_me)); + EXPECT_FALSE(IsIgnoredNetwork(network_manager, include_me)); + std::vector<std::string> ignore_list; + ignore_list.push_back("ignore_me"); + network_manager.set_network_ignore_list(ignore_list); + EXPECT_TRUE(IsIgnoredNetwork(network_manager, ignore_me)); + EXPECT_FALSE(IsIgnoredNetwork(network_manager, include_me)); } -TEST_F(NetworkTest, TestCreateNetworks) { +// Test is failing on Windows opt: b/11288214 +TEST_F(NetworkTest, DISABLED_TestCreateNetworks) { BasicNetworkManager manager; NetworkManager::NetworkList result = GetNetworks(manager, true); // We should be able to bind to any addresses we find. @@ -113,7 +134,7 @@ TEST_F(NetworkTest, TestCreateNetworks) { IPAddress ip = (*it)->ip(); SocketAddress bindaddress(ip, 0); bindaddress.SetScopeID((*it)->scope_id()); - // TODO: Make this use talk_base::AsyncSocket once it supports IPv6. + // TODO(thaloun): Use talk_base::AsyncSocket once it supports IPv6. int fd = static_cast<int>(socket(ip.family(), SOCK_STREAM, IPPROTO_TCP)); if (fd > 0) { size_t ipsize = bindaddress.ToSockAddrStorage(&storage); @@ -121,6 +142,9 @@ TEST_F(NetworkTest, TestCreateNetworks) { int success = ::bind(fd, reinterpret_cast<sockaddr*>(&storage), static_cast<int>(ipsize)); +#ifdef WIN32 + if (success) LOG_GLE(LS_ERROR) << "Socket bind failed."; +#endif EXPECT_EQ(0, success); #ifdef WIN32 closesocket(fd); @@ -469,7 +493,7 @@ TEST_F(NetworkTest, TestIPv6Toggle) { NetworkManager::NetworkList list; #ifndef WIN32 // There should be at least one IPv6 network (fe80::/64 should be in there). - // TODO: Disabling this test on windows for the moment as the test + // TODO(thaloun): Disabling this test on windows for the moment as the test // machines don't seem to have IPv6 installed on them at all. manager.set_ipv6_enabled(true); list = GetNetworks(manager, true); @@ -517,5 +541,49 @@ TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) { } #endif // defined(POSIX) +#if defined(LINUX) +// If you want to test non-default routes, you can do the following on a linux +// machine: +// 1) Load the dummy network driver: +// sudo modprobe dummy +// sudo ifconfig dummy0 127.0.0.1 +// 2) Run this test and confirm the output says it found a dummy route (and +// passes). +// 3) When done: +// sudo rmmmod dummy +TEST_F(NetworkTest, TestIgnoreNonDefaultRoutes) { + BasicNetworkManager manager; + NetworkManager::NetworkList list; + list = GetNetworks(manager, false); + bool found_dummy = false; + LOG(LS_INFO) << "Looking for dummy network: "; + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + LOG(LS_INFO) << " Network name: " << (*it)->name(); + found_dummy |= (*it)->name().find("dummy0") != std::string::npos; + } + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + delete (*it); + } + if (!found_dummy) { + LOG(LS_INFO) << "No dummy found, quitting."; + return; + } + LOG(LS_INFO) << "Found dummy, running again while ignoring non-default " + << "routes."; + manager.set_ignore_non_default_routes(true); + list = GetNetworks(manager, false); + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + LOG(LS_INFO) << " Network name: " << (*it)->name(); + EXPECT_TRUE((*it)->name().find("dummy0") == std::string::npos); + } + for (NetworkManager::NetworkList::iterator it = list.begin(); + it != list.end(); ++it) { + delete (*it); + } +} +#endif } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/nssidentity.cc b/chromium/third_party/libjingle/source/talk/base/nssidentity.cc index c660aee0a51..053035e562e 100644 --- a/chromium/third_party/libjingle/source/talk/base/nssidentity.cc +++ b/chromium/third_party/libjingle/source/talk/base/nssidentity.cc @@ -26,6 +26,10 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <algorithm> +#include <string> +#include <vector> + #if HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H @@ -34,8 +38,6 @@ #include "talk/base/nssidentity.h" -#include <string> - #include "cert.h" #include "cryptohi.h" #include "keyhi.h" @@ -90,6 +92,43 @@ NSSKeyPair *NSSKeyPair::GetReference() { return new NSSKeyPair(privkey, pubkey); } +NSSCertificate::NSSCertificate(CERTCertificate* cert) + : certificate_(CERT_DupCertificate(cert)) { + ASSERT(certificate_ != NULL); +} + +static void DeleteCert(SSLCertificate* cert) { + delete cert; +} + +NSSCertificate::NSSCertificate(CERTCertList* cert_list) { + // Copy the first cert into certificate_. + CERTCertListNode* node = CERT_LIST_HEAD(cert_list); + certificate_ = CERT_DupCertificate(node->cert); + + // Put any remaining certificates into the chain. + node = CERT_LIST_NEXT(node); + std::vector<SSLCertificate*> certs; + for (; !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) { + certs.push_back(new NSSCertificate(node->cert)); + } + + if (!certs.empty()) + chain_.reset(new SSLCertChain(certs)); + + // The SSLCertChain constructor copies its input, so now we have to delete + // the originals. + std::for_each(certs.begin(), certs.end(), DeleteCert); +} + +NSSCertificate::NSSCertificate(CERTCertificate* cert, SSLCertChain* chain) + : certificate_(CERT_DupCertificate(cert)) { + ASSERT(certificate_ != NULL); + if (chain) + chain_.reset(chain->Copy()); +} + + NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) { std::string der; if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der)) @@ -105,15 +144,13 @@ NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) { if (!cert) return NULL; - return new NSSCertificate(cert); + NSSCertificate* ret = new NSSCertificate(cert); + CERT_DestroyCertificate(cert); + return ret; } NSSCertificate *NSSCertificate::GetReference() const { - CERTCertificate *certificate = CERT_DupCertificate(certificate_); - if (!certificate) - return NULL; - - return new NSSCertificate(certificate); + return new NSSCertificate(certificate_, chain_.get()); } std::string NSSCertificate::ToPEMString() const { @@ -122,6 +159,10 @@ std::string NSSCertificate::ToPEMString() const { certificate_->derCert.len); } +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) { const SECHashObject *ho; @@ -134,6 +175,54 @@ bool NSSCertificate::GetDigestLength(const std::string &algorithm, return true; } +bool NSSCertificate::GetSignatureDigestAlgorithm(std::string* algorithm) const { + // The function sec_DecodeSigAlg in NSS provides this mapping functionality. + // Unfortunately it is private, so the functionality must be duplicated here. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=925165 . + SECOidTag sig_alg = SECOID_GetAlgorithmTag(&certificate_->signature); + switch (sig_alg) { + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + *algorithm = DIGEST_MD5; + break; + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE: + case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE: + case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: + case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: + case SEC_OID_MISSI_DSS: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_DSS_OLD: + *algorithm = DIGEST_SHA_1; + break; + case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: + case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION: + case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST: + *algorithm = DIGEST_SHA_224; + break; + case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: + case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: + case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST: + *algorithm = DIGEST_SHA_256; + break; + case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: + case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: + *algorithm = DIGEST_SHA_384; + break; + case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: + case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: + *algorithm = DIGEST_SHA_512; + break; + default: + // Unknown algorithm. There are several unhandled options that are less + // common and more complex. + algorithm->clear(); + return false; + } + return true; +} + bool NSSCertificate::ComputeDigest(const std::string &algorithm, unsigned char *digest, std::size_t size, std::size_t *length) const { @@ -156,6 +245,14 @@ bool NSSCertificate::ComputeDigest(const std::string &algorithm, return true; } +bool NSSCertificate::GetChain(SSLCertChain** chain) const { + if (!chain_) + return false; + + *chain = chain_->Copy(); + return true; +} + bool NSSCertificate::Equals(const NSSCertificate *tocompare) const { if (!certificate_->derCert.len) return false; @@ -301,9 +398,9 @@ NSSIdentity *NSSIdentity::Generate(const std::string &common_name) { fail: delete keypair; - CERT_DestroyCertificate(certificate); done: + if (certificate) CERT_DestroyCertificate(certificate); if (subject_name) CERT_DestroyName(subject_name); if (spki) SECKEY_DestroySubjectPublicKeyInfo(spki); if (certreq) CERT_DestroyCertificateRequest(certreq); diff --git a/chromium/third_party/libjingle/source/talk/base/nssidentity.h b/chromium/third_party/libjingle/source/talk/base/nssidentity.h index 725c546277c..3f97ebbb619 100644 --- a/chromium/third_party/libjingle/source/talk/base/nssidentity.h +++ b/chromium/third_party/libjingle/source/talk/base/nssidentity.h @@ -66,7 +66,10 @@ class NSSKeyPair { class NSSCertificate : public SSLCertificate { public: static NSSCertificate* FromPEMString(const std::string& pem_string); - explicit NSSCertificate(CERTCertificate* cert) : certificate_(cert) {} + // The caller retains ownership of the argument to all the constructors, + // and the constructor makes a copy. + explicit NSSCertificate(CERTCertificate* cert); + explicit NSSCertificate(CERTCertList* cert_list); virtual ~NSSCertificate() { if (certificate_) CERT_DestroyCertificate(certificate_); @@ -76,24 +79,32 @@ class NSSCertificate : public SSLCertificate { virtual std::string ToPEMString() const; + virtual void ToDER(Buffer* der_buffer) const; + + 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; + virtual bool GetChain(SSLCertChain** chain) const; + CERTCertificate* certificate() { return certificate_; } // Helper function to get the length of a digest static bool GetDigestLength(const std::string& algorithm, std::size_t* length); - // Comparison + // Comparison. Only the certificate itself is considered, not the chain. bool Equals(const NSSCertificate* tocompare) const; private: + NSSCertificate(CERTCertificate* cert, SSLCertChain* chain); static bool GetDigestObject(const std::string& algorithm, const SECHashObject** hash_object); CERTCertificate* certificate_; + scoped_ptr<SSLCertChain> chain_; DISALLOW_EVIL_CONSTRUCTORS(NSSCertificate); }; diff --git a/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.cc b/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.cc index c9a540d521e..185c243f5e5 100644 --- a/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.cc +++ b/chromium/third_party/libjingle/source/talk/base/nssstreamadapter.cc @@ -821,6 +821,13 @@ SECStatus NSSStreamAdapter::AuthCertificateHook(void *arg, if (ok) { stream->cert_ok_ = true; + + // Record the peer's certificate chain. + CERTCertList* cert_list = SSL_PeerCertificateChain(fd); + ASSERT(cert_list != NULL); + + stream->peer_certificate_.reset(new NSSCertificate(cert_list)); + CERT_DestroyCertList(cert_list); return SECSuccess; } diff --git a/chromium/third_party/libjingle/source/talk/base/openssldigest.cc b/chromium/third_party/libjingle/source/talk/base/openssldigest.cc index bb0e027cfed..3d9276de844 100644 --- a/chromium/third_party/libjingle/source/talk/base/openssldigest.cc +++ b/chromium/third_party/libjingle/source/talk/base/openssldigest.cc @@ -98,6 +98,34 @@ bool OpenSSLDigest::GetDigestEVP(const std::string& algorithm, return true; } +bool OpenSSLDigest::GetDigestName(const EVP_MD* md, + std::string* algorithm) { + ASSERT(md != NULL); + ASSERT(algorithm != NULL); + + int md_type = EVP_MD_type(md); + if (md_type == NID_md5) { + *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) { + *algorithm = DIGEST_SHA_256; + } else if (md_type == NID_sha384) { + *algorithm = DIGEST_SHA_384; + } else if (md_type == NID_sha512) { + *algorithm = DIGEST_SHA_512; +#endif + } else { + algorithm->clear(); + return false; + } + + return true; +} + bool OpenSSLDigest::GetDigestSize(const std::string& algorithm, size_t* length) { const EVP_MD *md; diff --git a/chromium/third_party/libjingle/source/talk/base/openssldigest.h b/chromium/third_party/libjingle/source/talk/base/openssldigest.h index 0a12189d0a1..d5cf878786a 100644 --- a/chromium/third_party/libjingle/source/talk/base/openssldigest.h +++ b/chromium/third_party/libjingle/source/talk/base/openssldigest.h @@ -2,26 +2,26 @@ * libjingle * Copyright 2004--2012, 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. */ @@ -47,9 +47,12 @@ class OpenSSLDigest : public MessageDigest { // Outputs the digest value to |buf| with length |len|. virtual size_t Finish(void* buf, size_t len); - // Helper function to look up a digest. + // Helper function to look up a digest's EVP by name. static bool GetDigestEVP(const std::string &algorithm, const EVP_MD** md); + // Helper function to look up a digest's name by EVP. + static bool GetDigestName(const EVP_MD* md, + std::string* algorithm); // Helper function to get the length of a digest. static bool GetDigestSize(const std::string &algorithm, size_t* len); diff --git a/chromium/third_party/libjingle/source/talk/base/opensslidentity.cc b/chromium/third_party/libjingle/source/talk/base/opensslidentity.cc index a48c94fd753..4ff76016183 100644 --- a/chromium/third_party/libjingle/source/talk/base/opensslidentity.cc +++ b/chromium/third_party/libjingle/source/talk/base/opensslidentity.cc @@ -40,6 +40,7 @@ #include <openssl/rsa.h> #include <openssl/crypto.h> +#include "talk/base/checks.h" #include "talk/base/helpers.h" #include "talk/base/logging.h" #include "talk/base/openssldigest.h" @@ -211,7 +212,9 @@ OpenSSLCertificate* OpenSSLCertificate::Generate( #ifdef _DEBUG PrintCert(x509); #endif - return new OpenSSLCertificate(x509); + OpenSSLCertificate* ret = new OpenSSLCertificate(x509); + X509_free(x509); + return ret; } OpenSSLCertificate* OpenSSLCertificate::FromPEMString( @@ -224,10 +227,20 @@ OpenSSLCertificate* OpenSSLCertificate::FromPEMString( X509 *x509 = PEM_read_bio_X509(bio, NULL, NULL, const_cast<char*>("\0")); BIO_free(bio); - if (x509) - return new OpenSSLCertificate(x509); - else + if (!x509) return NULL; + + OpenSSLCertificate* ret = new OpenSSLCertificate(x509); + X509_free(x509); + return ret; +} + +// NOTE: This implementation only functions correctly after InitializeSSL +// and before CleanupSSL. +bool OpenSSLCertificate::GetSignatureDigestAlgorithm( + std::string* algorithm) const { + return OpenSSLDigest::GetDigestName( + EVP_get_digestbyobj(x509_->sig_alg->algorithm), algorithm); } bool OpenSSLCertificate::ComputeDigest(const std::string &algorithm, @@ -264,11 +277,14 @@ OpenSSLCertificate::~OpenSSLCertificate() { std::string OpenSSLCertificate::ToPEMString() const { BIO* bio = BIO_new(BIO_s_mem()); - if (!bio) - return NULL; + if (!bio) { + UNREACHABLE(); + return std::string(); + } if (!PEM_write_bio_X509(bio, x509_)) { BIO_free(bio); - return NULL; + UNREACHABLE(); + return std::string(); } BIO_write(bio, "\0", 1); char* buffer; @@ -278,7 +294,29 @@ std::string OpenSSLCertificate::ToPEMString() const { return ret; } +void OpenSSLCertificate::ToDER(Buffer* der_buffer) const { + // In case of failure, make sure to leave the buffer empty. + der_buffer->SetData(NULL, 0); + + // Calculates the DER representation of the certificate, from scratch. + BIO* bio = BIO_new(BIO_s_mem()); + if (!bio) { + UNREACHABLE(); + return; + } + if (!i2d_X509_bio(bio, x509_)) { + BIO_free(bio); + UNREACHABLE(); + return; + } + char* data; + size_t length = BIO_get_mem_data(bio, &data); + der_buffer->SetData(data, length); + BIO_free(bio); +} + void OpenSSLCertificate::AddReference() const { + ASSERT(x509_ != NULL); CRYPTO_add(&x509_->references, 1, CRYPTO_LOCK_X509); } diff --git a/chromium/third_party/libjingle/source/talk/base/opensslidentity.h b/chromium/third_party/libjingle/source/talk/base/opensslidentity.h index ca001b5cfa0..af18c5c4d09 100644 --- a/chromium/third_party/libjingle/source/talk/base/opensslidentity.h +++ b/chromium/third_party/libjingle/source/talk/base/opensslidentity.h @@ -25,8 +25,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_BASE_OPENSSLIDENTITY_H__ -#define TALK_BASE_OPENSSLIDENTITY_H__ +#ifndef TALK_BASE_OPENSSLIDENTITY_H_ +#define TALK_BASE_OPENSSLIDENTITY_H_ #include <openssl/evp.h> #include <openssl/x509.h> @@ -72,6 +72,11 @@ class OpenSSLKeyPair { // which is also reference counted inside the OpenSSL library. class OpenSSLCertificate : public SSLCertificate { public: + // Caller retains ownership of the X509 object. + explicit OpenSSLCertificate(X509* x509) : x509_(x509) { + AddReference(); + } + static OpenSSLCertificate* Generate(OpenSSLKeyPair* key_pair, const std::string& common_name); static OpenSSLCertificate* FromPEMString(const std::string& pem_string); @@ -79,7 +84,6 @@ class OpenSSLCertificate : public SSLCertificate { virtual ~OpenSSLCertificate(); virtual OpenSSLCertificate* GetReference() const { - AddReference(); return new OpenSSLCertificate(x509_); } @@ -87,6 +91,8 @@ class OpenSSLCertificate : public SSLCertificate { virtual std::string ToPEMString() const; + 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, @@ -99,10 +105,16 @@ class OpenSSLCertificate : public SSLCertificate { std::size_t size, std::size_t *length); - private: - explicit OpenSSLCertificate(X509* x509) : x509_(x509) { - ASSERT(x509_ != NULL); + virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const; + + virtual bool GetChain(SSLCertChain** chain) const { + // Chains are not yet supported when using OpenSSL. + // OpenSSLStreamAdapter::SSLVerifyCallback currently requires the remote + // certificate to be self-signed. + return false; } + + private: void AddReference() const; X509* x509_; @@ -148,4 +160,4 @@ class OpenSSLIdentity : public SSLIdentity { } // namespace talk_base -#endif // TALK_BASE_OPENSSLIDENTITY_H__ +#endif // TALK_BASE_OPENSSLIDENTITY_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.cc b/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.cc index 16021a96bd9..034dfcf9266 100644 --- a/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.cc +++ b/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.cc @@ -217,6 +217,14 @@ void OpenSSLStreamAdapter::SetPeerCertificate(SSLCertificate* cert) { peer_certificate_.reset(static_cast<OpenSSLCertificate*>(cert)); } +bool OpenSSLStreamAdapter::GetPeerCertificate(SSLCertificate** cert) const { + if (!peer_certificate_) + return false; + + *cert = peer_certificate_->GetReference(); + return true; +} + bool OpenSSLStreamAdapter::SetPeerCertificateDigest(const std::string &digest_alg, const unsigned char* @@ -857,6 +865,9 @@ int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) { LOG(LS_INFO) << "Accepted self-signed peer certificate authority"; ok = 1; + + // Record the peer's certificate. + stream->peer_certificate_.reset(new OpenSSLCertificate(cert)); } } } diff --git a/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.h b/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.h index 8e92a10a5fa..3c478187fcc 100644 --- a/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.h +++ b/chromium/third_party/libjingle/source/talk/base/opensslstreamadapter.h @@ -86,6 +86,8 @@ class OpenSSLStreamAdapter : public SSLStreamAdapter { const unsigned char* digest_val, size_t digest_len); + virtual bool GetPeerCertificate(SSLCertificate** cert) const; + virtual int StartSSLWithServer(const char* server_name); virtual int StartSSLWithPeer(); virtual void SetMode(SSLMode mode); @@ -190,8 +192,8 @@ class OpenSSLStreamAdapter : 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 certificate that the peer must present or did present. Initially + // null in traditional mode, until the connection is established. scoped_ptr<OpenSSLCertificate> peer_certificate_; // In peer-to-peer mode, the digest of the certificate that // the peer must present. diff --git a/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.cc b/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.cc index f14c3bd69c6..43be440e7bd 100644 --- a/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.cc +++ b/chromium/third_party/libjingle/source/talk/base/physicalsocketserver.cc @@ -200,9 +200,8 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { if (addr.IsUnresolved()) { LOG(LS_VERBOSE) << "Resolving addr in PhysicalSocket::Connect"; resolver_ = new AsyncResolver(); - resolver_->set_address(addr); - resolver_->SignalWorkDone.connect(this, &PhysicalSocket::OnResolveResult); - resolver_->Start(); + resolver_->SignalDone.connect(this, &PhysicalSocket::OnResolveResult); + resolver_->Start(addr); state_ = CS_CONNECTING; return 0; } @@ -222,7 +221,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { UpdateLastError(); if (err == 0) { state_ = CS_CONNECTED; - } else if (IsBlockingError(error_)) { + } else if (IsBlockingError(GetError())) { state_ = CS_CONNECTING; enabled_events_ |= DE_CONNECT; } else { @@ -234,10 +233,12 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { } int GetError() const { + CritScope cs(&crit_); return error_; } void SetError(int error) { + CritScope cs(&crit_); error_ = error; } @@ -290,7 +291,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { MaybeRemapSendError(); // We have seen minidumps where this may be false. ASSERT(sent <= static_cast<int>(cb)); - if ((sent < 0) && IsBlockingError(error_)) { + if ((sent < 0) && IsBlockingError(GetError())) { enabled_events_ |= DE_WRITE; } return sent; @@ -312,7 +313,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { MaybeRemapSendError(); // We have seen minidumps where this may be false. ASSERT(sent <= static_cast<int>(length)); - if ((sent < 0) && IsBlockingError(error_)) { + if ((sent < 0) && IsBlockingError(GetError())) { enabled_events_ |= DE_WRITE; } return sent; @@ -329,16 +330,17 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { // Must turn this back on so that the select() loop will notice the close // event. enabled_events_ |= DE_READ; - error_ = EWOULDBLOCK; + SetError(EWOULDBLOCK); return SOCKET_ERROR; } UpdateLastError(); - bool success = (received >= 0) || IsBlockingError(error_); + int error = GetError(); + bool success = (received >= 0) || IsBlockingError(error); if (udp_ || success) { enabled_events_ |= DE_READ; } if (!success) { - LOG_F(LS_VERBOSE) << "Error = " << error_; + LOG_F(LS_VERBOSE) << "Error = " << error; } return received; } @@ -352,12 +354,13 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { UpdateLastError(); if ((received >= 0) && (out_addr != NULL)) SocketAddressFromSockAddrStorage(addr_storage, out_addr); - bool success = (received >= 0) || IsBlockingError(error_); + int error = GetError(); + bool success = (received >= 0) || IsBlockingError(error); if (udp_ || success) { enabled_events_ |= DE_READ; } if (!success) { - LOG_F(LS_VERBOSE) << "Error = " << error_; + LOG_F(LS_VERBOSE) << "Error = " << error; } return received; } @@ -408,7 +411,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { int EstimateMTU(uint16* mtu) { SocketAddress addr = GetRemoteAddress(); if (addr.IsAny()) { - error_ = ENOTCONN; + SetError(ENOTCONN); return -1; } @@ -416,7 +419,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { // Gets the interface MTU (TTL=1) for the interface used to reach |addr|. WinPing ping; if (!ping.IsValid()) { - error_ = EINVAL; // can't think of a better error ID + SetError(EINVAL); // can't think of a better error ID return -1; } int header_size = ICMP_HEADER_SIZE; @@ -432,7 +435,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { ICMP_PING_TIMEOUT_MILLIS, 1, false); if (result == WinPing::PING_FAIL) { - error_ = EINVAL; // can't think of a better error ID + SetError(EINVAL); // can't think of a better error ID return -1; } else if (result != WinPing::PING_TOO_LARGE) { *mtu = PACKET_MAXIMUMS[level]; @@ -447,7 +450,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { // SIOCGIFMTU would work if we knew which interface would be used, but // figuring that out is pretty complicated. For now we'll return an error // and let the caller pick a default MTU. - error_ = EINVAL; + SetError(EINVAL); return -1; #elif defined(LINUX) || defined(ANDROID) // Gets the path MTU. @@ -462,18 +465,22 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { ASSERT((0 <= value) && (value <= 65536)); *mtu = value; return 0; +#elif defined(__native_client__) + // Most socket operations, including this, will fail in NaCl's sandbox. + error_ = EACCES; + return -1; #endif } SocketServer* socketserver() { return ss_; } protected: - void OnResolveResult(SignalThread* thread) { - if (thread != resolver_) { + void OnResolveResult(AsyncResolverInterface* resolver) { + if (resolver != resolver_) { return; } - int error = resolver_->error(); + int error = resolver_->GetError(); if (error == 0) { error = DoConnect(resolver_->address()); } else { @@ -481,13 +488,13 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { } if (error) { - error_ = error; - SignalCloseEvent(this, error_); + SetError(error); + SignalCloseEvent(this, error); } } void UpdateLastError() { - error_ = LAST_SYSTEM_ERROR; + SetError(LAST_SYSTEM_ERROR); } void MaybeRemapSendError() { @@ -497,8 +504,8 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { // ENOBUFS - The output queue for a network interface is full. // This generally indicates that the interface has stopped sending, // but may be caused by transient congestion. - if (error_ == ENOBUFS) { - error_ = EWOULDBLOCK; + if (GetError() == ENOBUFS) { + SetError(EWOULDBLOCK); } #endif } @@ -530,6 +537,9 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { *slevel = IPPROTO_TCP; *sopt = TCP_NODELAY; break; + case OPT_DSCP: + LOG(LS_WARNING) << "Socket::OPT_DSCP not supported."; + return -1; default: ASSERT(false); return -1; @@ -542,6 +552,8 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { uint8 enabled_events_; bool udp_; int error_; + // Protects |error_| that is accessed from different threads. + mutable CriticalSection crit_; ConnState state_; AsyncResolver* resolver_; diff --git a/chromium/third_party/libjingle/source/talk/base/profiler.cc b/chromium/third_party/libjingle/source/talk/base/profiler.cc index 9d0f32bb2d4..4c2aac4163b 100644 --- a/chromium/third_party/libjingle/source/talk/base/profiler.cc +++ b/chromium/third_party/libjingle/source/talk/base/profiler.cc @@ -71,8 +71,7 @@ void ProfilerEvent::Start() { ++start_count_; } -void ProfilerEvent::Stop() { - uint64 stop_time = TimeNanos(); +void ProfilerEvent::Stop(uint64 stop_time) { --start_count_; ASSERT(start_count_ >= 0); if (start_count_ == 0) { @@ -94,6 +93,10 @@ void ProfilerEvent::Stop() { } } +void ProfilerEvent::Stop() { + Stop(TimeNanos()); +} + double ProfilerEvent::standard_deviation() const { if (event_count_ <= 1) return 0.0; return sqrt(sum_of_squared_differences_ / (event_count_ - 1.0)); @@ -105,11 +108,29 @@ Profiler* Profiler::Instance() { } void Profiler::StartEvent(const std::string& event_name) { - events_[event_name].Start(); + lock_.LockShared(); + EventMap::iterator it = events_.find(event_name); + bool needs_insert = (it == events_.end()); + lock_.UnlockShared(); + + if (needs_insert) { + // Need an exclusive lock to modify the map. + ExclusiveScope scope(&lock_); + it = events_.insert( + EventMap::value_type(event_name, ProfilerEvent())).first; + } + + it->second.Start(); } void Profiler::StopEvent(const std::string& event_name) { - events_[event_name].Stop(); + // Get the time ASAP, then wait for the lock. + uint64 stop_time = TimeNanos(); + SharedScope scope(&lock_); + EventMap::iterator it = events_.find(event_name); + if (it != events_.end()) { + it->second.Stop(stop_time); + } } void Profiler::ReportToLog(const char* file, int line, @@ -118,6 +139,9 @@ void Profiler::ReportToLog(const char* file, int line, if (!LogMessage::Loggable(severity_to_use)) { return; } + + SharedScope scope(&lock_); + { // Output first line. LogMessage msg(file, line, severity_to_use); msg.stream() << "=== Profile report "; @@ -126,16 +150,11 @@ void Profiler::ReportToLog(const char* file, int line, } msg.stream() << "==="; } - typedef std::map<std::string, ProfilerEvent>::const_iterator iterator; - for (iterator it = events_.begin(); it != events_.end(); ++it) { + for (EventMap::const_iterator it = events_.begin(); + it != events_.end(); ++it) { if (event_prefix.empty() || it->first.find(event_prefix) == 0) { LogMessage(file, line, severity_to_use).stream() - << it->first << " count=" << it->second.event_count() - << " total=" << FormattedTime(it->second.total_time()) - << " mean=" << FormattedTime(it->second.mean()) - << " min=" << FormattedTime(it->second.minimum()) - << " max=" << FormattedTime(it->second.maximum()) - << " sd=" << it->second.standard_deviation(); + << it->first << " " << it->second; } } LogMessage(file, line, severity_to_use).stream() @@ -148,15 +167,17 @@ void Profiler::ReportAllToLog(const char* file, int line, } const ProfilerEvent* Profiler::GetEvent(const std::string& event_name) const { - std::map<std::string, ProfilerEvent>::const_iterator it = + SharedScope scope(&lock_); + EventMap::const_iterator it = events_.find(event_name); return (it == events_.end()) ? NULL : &it->second; } bool Profiler::Clear() { + ExclusiveScope scope(&lock_); bool result = true; // Clear all events that aren't started. - std::map<std::string, ProfilerEvent>::iterator it = events_.begin(); + EventMap::iterator it = events_.begin(); while (it != events_.end()) { if (it->second.is_started()) { ++it; // Can't clear started events. @@ -168,4 +189,15 @@ bool Profiler::Clear() { return result; } +std::ostream& operator<<(std::ostream& stream, + const ProfilerEvent& profiler_event) { + stream << "count=" << profiler_event.event_count() + << " total=" << FormattedTime(profiler_event.total_time()) + << " mean=" << FormattedTime(profiler_event.mean()) + << " min=" << FormattedTime(profiler_event.minimum()) + << " max=" << FormattedTime(profiler_event.maximum()) + << " sd=" << profiler_event.standard_deviation(); + return stream; +} + } // namespace talk_base diff --git a/chromium/third_party/libjingle/source/talk/base/profiler.h b/chromium/third_party/libjingle/source/talk/base/profiler.h index 1198b8e2630..90c5c722a30 100644 --- a/chromium/third_party/libjingle/source/talk/base/profiler.h +++ b/chromium/third_party/libjingle/source/talk/base/profiler.h @@ -37,7 +37,7 @@ // } // Another example: // void StartAsyncProcess() { -// PROFILE_START("My event"); +// PROFILE_START("My async event"); // DoSomethingAsyncAndThenCall(&Callback); // } // void Callback() { @@ -54,6 +54,7 @@ #include "talk/base/basictypes.h" #include "talk/base/common.h" #include "talk/base/logging.h" +#include "talk/base/sharedexclusivelock.h" // Profiling could be switched via a build flag, but for now, it's always on. #define ENABLE_PROFILING @@ -105,6 +106,7 @@ class ProfilerEvent { ProfilerEvent(); void Start(); void Stop(); + void Stop(uint64 stop_time); double standard_deviation() const; double total_time() const { return total_time_; } double mean() const { return mean_; } @@ -142,7 +144,9 @@ class Profiler { private: Profiler() {} - std::map<std::string, ProfilerEvent> events_; + typedef std::map<std::string, ProfilerEvent> EventMap; + EventMap events_; + mutable SharedExclusiveLock lock_; DISALLOW_COPY_AND_ASSIGN(Profiler); }; @@ -164,6 +168,9 @@ class ProfilerScope { DISALLOW_COPY_AND_ASSIGN(ProfilerScope); }; +std::ostream& operator<<(std::ostream& stream, + const ProfilerEvent& profiler_event); + } // namespace talk_base #endif // TALK_BASE_PROFILER_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 5a8364e10b0..90f743c6285 100644 --- a/chromium/third_party/libjingle/source/talk/base/scoped_ptr.h +++ b/chromium/third_party/libjingle/source/talk/base/scoped_ptr.h @@ -1,34 +1,102 @@ -// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. -// Copyright (c) 2001, 2002 Peter Dimov +// Borrowed from chromium. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Scopers help you manage ownership of a pointer, helping you easily manage the +// a pointer within a scope, and automatically destroying the pointer at the +// end of a scope. There are two main classes you will use, which correspond +// to the operators new/delete and new[]/delete[]. // -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. +// Example usage (scoped_ptr<T>): +// { +// scoped_ptr<Foo> foo(new Foo("wee")); +// } // foo goes out of scope, releasing the pointer with it. // -// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation. +// { +// scoped_ptr<Foo> foo; // No pointer managed. +// foo.reset(new Foo("wee")); // Now a pointer is managed. +// foo.reset(new Foo("wee2")); // Foo("wee") was destroyed. +// foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed. +// foo->Method(); // Foo::Method() called. +// foo.get()->Method(); // Foo::Method() called. +// SomeFunc(foo.release()); // SomeFunc takes ownership, foo no longer +// // manages a pointer. +// foo.reset(new Foo("wee4")); // foo manages a pointer again. +// foo.reset(); // Foo("wee4") destroyed, foo no longer +// // manages a pointer. +// } // foo wasn't managing a pointer, so nothing was destroyed. // +// Example usage (scoped_ptr<T[]>): +// { +// scoped_ptr<Foo[]> foo(new Foo[100]); +// foo.get()->Method(); // Foo::Method on the 0th element. +// foo[10].Method(); // Foo::Method on the 10th element. +// } +// +// These scopers also implement part of the functionality of C++11 unique_ptr +// in that they are "movable but not copyable." You can use the scopers in +// the parameter and return types of functions to signify ownership transfer +// in to and out of a function. When calling a function that has a scoper +// as the argument type, it must be called with the result of an analogous +// scoper's Pass() function or another function that generates a temporary; +// passing by copy will NOT work. Here is an example using scoped_ptr: +// +// void TakesOwnership(scoped_ptr<Foo> arg) { +// // Do something with arg +// } +// scoped_ptr<Foo> CreateFoo() { +// // No need for calling Pass() because we are constructing a temporary +// // for the return value. +// return scoped_ptr<Foo>(new Foo("new")); +// } +// scoped_ptr<Foo> PassThru(scoped_ptr<Foo> arg) { +// return arg.Pass(); +// } +// +// { +// scoped_ptr<Foo> ptr(new Foo("yay")); // ptr manages Foo("yay"). +// TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay"). +// scoped_ptr<Foo> ptr2 = CreateFoo(); // ptr2 owns the return Foo. +// scoped_ptr<Foo> ptr3 = // ptr3 now owns what was in ptr2. +// PassThru(ptr2.Pass()); // ptr2 is correspondingly NULL. +// } +// +// Notice that if you do not call Pass() when returning from PassThru(), or +// when invoking TakesOwnership(), the code will not compile because scopers +// are not copyable; they only implement move semantics which require calling +// the Pass() function to signify a destructive transfer of state. CreateFoo() +// is different though because we are constructing a temporary on the return +// line and thus can avoid needing to call Pass(). +// +// Pass() properly handles upcast in initialization, i.e. you can use a +// scoped_ptr<Child> to initialize a scoped_ptr<Parent>: +// +// scoped_ptr<Foo> foo(new Foo()); +// scoped_ptr<FooParent> parent(foo.Pass()); +// +// PassAs<>() should be used to upcast return value in return statement: +// +// scoped_ptr<Foo> CreateFoo() { +// scoped_ptr<FooChild> result(new FooChild()); +// return result.PassAs<Foo>(); +// } +// +// Note that PassAs<>() is implemented only for scoped_ptr<T>, but not for +// scoped_ptr<T[]>. This is because casting array pointers may not be safe. -// scoped_ptr mimics a built-in pointer except that it guarantees deletion -// of the object pointed to, either on destruction of the scoped_ptr or via -// an explicit reset(). scoped_ptr is a simple solution for simple needs; -// use shared_ptr or std::auto_ptr if your needs are more complex. - -// scoped_ptr_malloc added in by Google. When one of -// these goes out of scope, instead of doing a delete or delete[], it -// calls free(). scoped_ptr_malloc<char> is likely to see much more -// use than any other specializations. - -// release() added in by Google. Use this to conditionally -// transfer ownership of a heap-allocated object to the caller, usually on -// method success. #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 <algorithm> // For std::swap(). + #include "talk/base/common.h" // for ASSERT +#include "talk/base/compile_assert.h" // for COMPILE_ASSERT +#include "talk/base/move.h" // for TALK_MOVE_ONLY_TYPE_FOR_CPP_03 +#include "talk/base/template_util.h" // for is_convertible, is_array #ifdef _WIN32 namespace std { using ::ptrdiff_t; }; @@ -36,242 +104,487 @@ namespace std { using ::ptrdiff_t; }; namespace talk_base { -template <typename T> -class scoped_ptr { - private: +// Function object which deletes its parameter, which must be a pointer. +// If C is an array type, invokes 'delete[]' on the parameter; otherwise, +// invokes 'delete'. The default deleter for scoped_ptr<T>. +template <class T> +struct DefaultDeleter { + DefaultDeleter() {} + template <typename U> DefaultDeleter(const DefaultDeleter<U>& other) { + // IMPLEMENTATION NOTE: C++11 20.7.1.1.2p2 only provides this constructor + // if U* is implicitly convertible to T* and U is not an array type. + // + // Correct implementation should use SFINAE to disable this + // constructor. However, since there are no other 1-argument constructors, + // using a COMPILE_ASSERT() based on is_convertible<> and requiring + // complete types is simpler and will cause compile failures for equivalent + // misuses. + // + // Note, the is_convertible<U*, T*> check also ensures that U is not an + // array. T is guaranteed to be a non-array, so any U* where U is an array + // cannot convert to T*. + enum { T_must_be_complete = sizeof(T) }; + enum { U_must_be_complete = sizeof(U) }; + COMPILE_ASSERT((talk_base::is_convertible<U*, T*>::value), + U_ptr_must_implicitly_convert_to_T_ptr); + } + inline void operator()(T* ptr) const { + enum { type_must_be_complete = sizeof(T) }; + delete ptr; + } +}; - T* ptr; +// Specialization of DefaultDeleter for array types. +template <class T> +struct DefaultDeleter<T[]> { + inline void operator()(T* ptr) const { + enum { type_must_be_complete = sizeof(T) }; + delete[] ptr; + } - scoped_ptr(scoped_ptr const &); - scoped_ptr & operator=(scoped_ptr const &); + private: + // Disable this operator for any U != T because it is undefined to execute + // an array delete when the static type of the array mismatches the dynamic + // type. + // + // References: + // C++98 [expr.delete]p3 + // http://cplusplus.github.com/LWG/lwg-defects.html#938 + template <typename U> void operator()(U* array) const; +}; - public: +template <class T, int n> +struct DefaultDeleter<T[n]> { + // Never allow someone to declare something like scoped_ptr<int[10]>. + COMPILE_ASSERT(sizeof(T) == -1, do_not_use_array_with_size_as_type); +}; - typedef T element_type; +// Function object which invokes 'free' on its parameter, which must be +// a pointer. Can be used to store malloc-allocated pointers in scoped_ptr: +// +// scoped_ptr<int, talk_base::FreeDeleter> foo_ptr( +// static_cast<int*>(malloc(sizeof(int)))); +struct FreeDeleter { + inline void operator()(void* ptr) const { + free(ptr); + } +}; - explicit scoped_ptr(T* p = NULL): ptr(p) {} +namespace internal { - ~scoped_ptr() { - typedef char type_must_be_complete[sizeof(T)]; - delete ptr; +// Minimal implementation of the core logic of scoped_ptr, suitable for +// reuse in both scoped_ptr and its specializations. +template <class T, class D> +class scoped_ptr_impl { + public: + explicit scoped_ptr_impl(T* p) : data_(p) { } + + // Initializer for deleters that have data parameters. + scoped_ptr_impl(T* p, const D& d) : data_(p, d) {} + + // Templated constructor that destructively takes the value from another + // scoped_ptr_impl. + template <typename U, typename V> + scoped_ptr_impl(scoped_ptr_impl<U, V>* other) + : data_(other->release(), other->get_deleter()) { + // We do not support move-only deleters. We could modify our move + // emulation to have talk_base::subtle::move() and + // talk_base::subtle::forward() + // functions that are imperfect emulations of their C++11 equivalents, + // but until there's a requirement, just assume deleters are copyable. } - void reset(T* p = NULL) { - typedef char type_must_be_complete[sizeof(T)]; + template <typename U, typename V> + void TakeState(scoped_ptr_impl<U, V>* other) { + // See comment in templated constructor above regarding lack of support + // for move-only deleters. + reset(other->release()); + get_deleter() = other->get_deleter(); + } - if (ptr != p) { - T* obj = ptr; - ptr = p; - // Delete last, in case obj destructor indirectly results in ~scoped_ptr - delete obj; + ~scoped_ptr_impl() { + if (data_.ptr != NULL) { + // Not using get_deleter() saves one function call in non-optimized + // builds. + static_cast<D&>(data_)(data_.ptr); } } - T& operator*() const { - ASSERT(ptr != NULL); - return *ptr; + void reset(T* p) { + // This is a self-reset, which is no longer allowed: http://crbug.com/162971 + if (p != NULL && p == data_.ptr) + abort(); + + // Note that running data_.ptr = p can lead to undefined behavior if + // get_deleter()(get()) deletes this. In order to pevent this, reset() + // should update the stored pointer before deleting its old value. + // + // However, changing reset() to use that behavior may cause current code to + // break in unexpected ways. If the destruction of the owned object + // dereferences the scoped_ptr when it is destroyed by a call to reset(), + // then it will incorrectly dispatch calls to |p| rather than the original + // value of |data_.ptr|. + // + // During the transition period, set the stored pointer to NULL while + // deleting the object. Eventually, this safety check will be removed to + // prevent the scenario initially described from occuring and + // http://crbug.com/176091 can be closed. + T* old = data_.ptr; + data_.ptr = NULL; + if (old != NULL) + static_cast<D&>(data_)(old); + data_.ptr = p; } - T* operator->() const { - ASSERT(ptr != NULL); - return ptr; - } + T* get() const { return data_.ptr; } - T* get() const { - return ptr; - } + D& get_deleter() { return data_; } + const D& get_deleter() const { return data_; } - void swap(scoped_ptr & b) { - T* tmp = b.ptr; - b.ptr = ptr; - ptr = tmp; + void swap(scoped_ptr_impl& p2) { + // Standard swap idiom: 'using std::swap' ensures that std::swap is + // present in the overload set, but we call swap unqualified so that + // any more-specific overloads can be used, if available. + using std::swap; + swap(static_cast<D&>(data_), static_cast<D&>(p2.data_)); + swap(data_.ptr, p2.data_.ptr); } T* release() { - T* tmp = ptr; - ptr = NULL; - return tmp; + T* old_ptr = data_.ptr; + data_.ptr = NULL; + return old_ptr; } T** accept() { - if (ptr) { - delete ptr; - ptr = NULL; - } - return &ptr; + reset(NULL); + return &(data_.ptr); } T** use() { - return &ptr; + return &(data_.ptr); } - // Allow scoped_ptr<T> to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). - // Borrowed from chromium's scoped_ptr implementation. - typedef T* scoped_ptr::*Testable; - operator Testable() const { return ptr ? &scoped_ptr::ptr : NULL; } - + private: + // Needed to allow type-converting constructor. + template <typename U, typename V> friend class scoped_ptr_impl; + + // Use the empty base class optimization to allow us to have a D + // member, while avoiding any space overhead for it when D is an + // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good + // discussion of this technique. + struct Data : public D { + explicit Data(T* ptr_in) : ptr(ptr_in) {} + Data(T* ptr_in, const D& other) : D(other), ptr(ptr_in) {} + T* ptr; + }; + + Data data_; + + DISALLOW_COPY_AND_ASSIGN(scoped_ptr_impl); }; -template<typename T> inline -void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) { - a.swap(b); -} +} // namespace internal +// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T> +// automatically deletes the pointer it holds (if any). +// That is, scoped_ptr<T> owns the T object that it points to. +// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object. +// Also like T*, scoped_ptr<T> is thread-compatible, and once you +// dereference it, you get the thread safety guarantees of T. +// +// The size of scoped_ptr is small. On most compilers, when using the +// DefaultDeleter, sizeof(scoped_ptr<T>) == sizeof(T*). Custom deleters will +// increase the size proportional to whatever state they need to have. See +// comments inside scoped_ptr_impl<> for details. +// +// Current implementation targets having a strict subset of C++11's +// unique_ptr<> features. Known deficiencies include not supporting move-only +// deleteres, function pointers as deleters, and deleters with reference +// types. +template <class T, class D = talk_base::DefaultDeleter<T> > +class scoped_ptr { + TALK_MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) + public: + // The element and deleter types. + typedef T element_type; + typedef D deleter_type; + + // Constructor. Defaults to initializing with NULL. + scoped_ptr() : impl_(NULL) { } + + // Constructor. Takes ownership of p. + explicit scoped_ptr(element_type* p) : impl_(p) { } + + // Constructor. Allows initialization of a stateful deleter. + scoped_ptr(element_type* p, const D& d) : impl_(p, d) { } + + // Constructor. Allows construction from a scoped_ptr rvalue for a + // convertible type and deleter. + // + // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this constructor distinct + // from the normal move constructor. By C++11 20.7.1.2.1.21, this constructor + // has different post-conditions if D is a reference type. Since this + // implementation does not support deleters with reference type, + // we do not need a separate move constructor allowing us to avoid one + // use of SFINAE. You only need to care about this if you modify the + // implementation of scoped_ptr. + template <typename U, typename V> + scoped_ptr(scoped_ptr<U, V> other) : impl_(&other.impl_) { + COMPILE_ASSERT(!talk_base::is_array<U>::value, U_cannot_be_an_array); + } + // Constructor. Move constructor for C++03 move emulation of this type. + scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) { } + + // operator=. Allows assignment from a scoped_ptr rvalue for a convertible + // type and deleter. + // + // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this operator= distinct from + // the normal move assignment operator. By C++11 20.7.1.2.3.4, this templated + // form has different requirements on for move-only Deleters. Since this + // implementation does not support move-only Deleters, we do not need a + // separate move assignment operator allowing us to avoid one use of SFINAE. + // You only need to care about this if you modify the implementation of + // scoped_ptr. + template <typename U, typename V> + scoped_ptr& operator=(scoped_ptr<U, V> rhs) { + COMPILE_ASSERT(!talk_base::is_array<U>::value, U_cannot_be_an_array); + impl_.TakeState(&rhs.impl_); + return *this; + } -// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to -// is guaranteed, either on destruction of the scoped_array or via an explicit -// reset(). Use shared_array or std::vector if your needs are more complex. + // Reset. Deletes the currently owned object, if any. + // Then takes ownership of a new object, if given. + void reset(element_type* p = NULL) { impl_.reset(p); } -template<typename T> -class scoped_array { - private: + // Accessors to get the owned object. + // operator* and operator-> will assert() if there is no current object. + element_type& operator*() const { + ASSERT(impl_.get() != NULL); + return *impl_.get(); + } + element_type* operator->() const { + ASSERT(impl_.get() != NULL); + return impl_.get(); + } + element_type* get() const { return impl_.get(); } - T* ptr; + // Access to the deleter. + deleter_type& get_deleter() { return impl_.get_deleter(); } + const deleter_type& get_deleter() const { return impl_.get_deleter(); } - scoped_array(scoped_array const &); - scoped_array & operator=(scoped_array const &); + // Allow scoped_ptr<element_type> to be used in boolean expressions, but not + // implicitly convertible to a real bool (which is dangerous). + // + // Note that this trick is only safe when the == and != operators + // are declared explicitly, as otherwise "scoped_ptr1 == + // scoped_ptr2" will compile but do the wrong thing (i.e., convert + // to Testable and then do the comparison). + private: + typedef talk_base::internal::scoped_ptr_impl<element_type, deleter_type> + scoped_ptr::*Testable; public: + operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; } - typedef T element_type; - - explicit scoped_array(T* p = NULL) : ptr(p) {} - - ~scoped_array() { - typedef char type_must_be_complete[sizeof(T)]; - delete[] ptr; - } - - void reset(T* p = NULL) { - typedef char type_must_be_complete[sizeof(T)]; + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(const element_type* p) const { return impl_.get() == p; } + bool operator!=(const element_type* p) const { return impl_.get() != p; } - if (ptr != p) { - T* arr = ptr; - ptr = p; - // Delete last, in case arr destructor indirectly results in ~scoped_array - delete [] arr; - } - } - - T& operator[](std::ptrdiff_t i) const { - ASSERT(ptr != NULL); - ASSERT(i >= 0); - return ptr[i]; + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + impl_.swap(p2.impl_); } - T* get() const { - return ptr; + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + element_type* release() WARN_UNUSED_RESULT { + return impl_.release(); } - void swap(scoped_array & b) { - T* tmp = b.ptr; - b.ptr = ptr; - ptr = tmp; + // Delete the currently held pointer and return a pointer + // to allow overwriting of the current pointer address. + element_type** accept() WARN_UNUSED_RESULT { + return impl_.accept(); } - T* release() { - T* tmp = ptr; - ptr = NULL; - return tmp; + // Return a pointer to the current pointer address. + element_type** use() WARN_UNUSED_RESULT { + return impl_.use(); } - T** accept() { - if (ptr) { - delete [] ptr; - ptr = NULL; - } - return &ptr; + // C++98 doesn't support functions templates with default parameters which + // makes it hard to write a PassAs() that understands converting the deleter + // while preserving simple calling semantics. + // + // Until there is a use case for PassAs() with custom deleters, just ignore + // the custom deleter. + template <typename PassAsType> + scoped_ptr<PassAsType> PassAs() { + return scoped_ptr<PassAsType>(Pass()); } - // Allow scoped_array<T> to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). - // Borrowed from chromium's scoped_array implementation. - typedef T* scoped_array::*Testable; - operator Testable() const { return ptr ? &scoped_array::ptr : NULL; } -}; - -template<class T> inline -void swap(scoped_array<T>& a, scoped_array<T>& b) { - a.swap(b); -} - -// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a -// second template argument, the function used to free the object. - -template<typename T, void (*FF)(T*) = free> class scoped_ptr_malloc { private: + // Needed to reach into |impl_| in the constructor. + template <typename U, typename V> friend class scoped_ptr; + talk_base::internal::scoped_ptr_impl<element_type, deleter_type> impl_; + + // Forbidden for API compatibility with std::unique_ptr. + explicit scoped_ptr(int disallow_construction_from_null); + + // Forbid comparison of scoped_ptr types. If U != T, it totally + // doesn't make sense, and if U == T, it still doesn't make sense + // because you should never have the same object owned by two different + // scoped_ptrs. + template <class U> bool operator==(scoped_ptr<U> const& p2) const; + template <class U> bool operator!=(scoped_ptr<U> const& p2) const; +}; - T* ptr; - - scoped_ptr_malloc(scoped_ptr_malloc const &); - scoped_ptr_malloc & operator=(scoped_ptr_malloc const &); +template <class T, class D> +class scoped_ptr<T[], D> { + TALK_MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) public: - + // The element and deleter types. typedef T element_type; + typedef D deleter_type; + + // Constructor. Defaults to initializing with NULL. + scoped_ptr() : impl_(NULL) { } + + // Constructor. Stores the given array. Note that the argument's type + // must exactly match T*. In particular: + // - it cannot be a pointer to a type derived from T, because it is + // inherently unsafe in the general case to access an array through a + // pointer whose dynamic type does not match its static type (eg., if + // T and the derived types had different sizes access would be + // incorrectly calculated). Deletion is also always undefined + // (C++98 [expr.delete]p3). If you're doing this, fix your code. + // - it cannot be NULL, because NULL is an integral expression, not a + // pointer to T. Use the no-argument version instead of explicitly + // passing NULL. + // - it cannot be const-qualified differently from T per unique_ptr spec + // (http://cplusplus.github.com/LWG/lwg-active.html#2118). Users wanting + // to work around this may use implicit_cast<const T*>(). + // However, because of the first bullet in this comment, users MUST + // NOT use implicit_cast<Base*>() to upcast the static type of the array. + explicit scoped_ptr(element_type* array) : impl_(array) { } + + // Constructor. Move constructor for C++03 move emulation of this type. + scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) { } + + // operator=. Move operator= for C++03 move emulation of this type. + scoped_ptr& operator=(RValue rhs) { + impl_.TakeState(&rhs.object->impl_); + return *this; + } - explicit scoped_ptr_malloc(T* p = 0): ptr(p) {} + // Reset. Deletes the currently owned array, if any. + // Then takes ownership of a new object, if given. + void reset(element_type* array = NULL) { impl_.reset(array); } - ~scoped_ptr_malloc() { - FF(ptr); + // Accessors to get the owned array. + element_type& operator[](size_t i) const { + ASSERT(impl_.get() != NULL); + return impl_.get()[i]; } + element_type* get() const { return impl_.get(); } - void reset(T* p = 0) { - if (ptr != p) { - FF(ptr); - ptr = p; - } - } + // Access to the deleter. + deleter_type& get_deleter() { return impl_.get_deleter(); } + const deleter_type& get_deleter() const { return impl_.get_deleter(); } - T& operator*() const { - ASSERT(ptr != 0); - return *ptr; - } + // Allow scoped_ptr<element_type> to be used in boolean expressions, but not + // implicitly convertible to a real bool (which is dangerous). + private: + typedef talk_base::internal::scoped_ptr_impl<element_type, deleter_type> + scoped_ptr::*Testable; - T* operator->() const { - ASSERT(ptr != 0); - return ptr; - } + public: + operator Testable() const { return impl_.get() ? &scoped_ptr::impl_ : NULL; } + + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(element_type* array) const { return impl_.get() == array; } + bool operator!=(element_type* array) const { return impl_.get() != array; } - T* get() const { - return ptr; + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + impl_.swap(p2.impl_); } - void swap(scoped_ptr_malloc & b) { - T* tmp = b.ptr; - b.ptr = ptr; - ptr = tmp; + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + element_type* release() WARN_UNUSED_RESULT { + return impl_.release(); } - T* release() { - T* tmp = ptr; - ptr = 0; - return tmp; + // Delete the currently held pointer and return a pointer + // to allow overwriting of the current pointer address. + element_type** accept() WARN_UNUSED_RESULT { + return impl_.accept(); } - T** accept() { - if (ptr) { - FF(ptr); - ptr = 0; - } - return &ptr; + // Return a pointer to the current pointer address. + element_type** use() WARN_UNUSED_RESULT { + return impl_.use(); } - // Allow scoped_ptr_malloc<T> to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). - // Borrowed from chromium's scoped_ptr_malloc implementation. - typedef T* scoped_ptr_malloc::*Testable; - operator Testable() const { return ptr ? &scoped_ptr_malloc::ptr : NULL; } + private: + // Force element_type to be a complete type. + enum { type_must_be_complete = sizeof(element_type) }; + + // Actually hold the data. + talk_base::internal::scoped_ptr_impl<element_type, deleter_type> impl_; + + // Disable initialization from any type other than element_type*, by + // providing a constructor that matches such an initialization, but is + // private and has no definition. This is disabled because it is not safe to + // call delete[] on an array whose static type does not match its dynamic + // type. + template <typename U> explicit scoped_ptr(U* array); + explicit scoped_ptr(int disallow_construction_from_null); + + // Disable reset() from any type other than element_type*, for the same + // reasons as the constructor above. + template <typename U> void reset(U* array); + void reset(int disallow_reset_from_null); + + // Forbid comparison of scoped_ptr types. If U != T, it totally + // doesn't make sense, and if U == T, it still doesn't make sense + // because you should never have the same object owned by two different + // scoped_ptrs. + template <class U> bool operator==(scoped_ptr<U> const& p2) const; + template <class U> bool operator!=(scoped_ptr<U> const& p2) const; }; -template<typename T, void (*FF)(T*)> inline -void swap(scoped_ptr_malloc<T,FF>& a, scoped_ptr_malloc<T,FF>& b) { - a.swap(b); +} // namespace talk_base + +// Free functions +template <class T, class D> +void swap(talk_base::scoped_ptr<T, D>& p1, talk_base::scoped_ptr<T, D>& p2) { + p1.swap(p2); } -} // namespace talk_base +template <class T, class D> +bool operator==(T* p1, const talk_base::scoped_ptr<T, D>& p2) { + return p1 == p2.get(); +} + +template <class T, class D> +bool operator!=(T* p1, const talk_base::scoped_ptr<T, D>& p2) { + return p1 != p2.get(); +} #endif // #ifndef TALK_BASE_SCOPED_PTR_H__ diff --git a/chromium/third_party/libjingle/source/talk/base/sha1digest_unittest.cc b/chromium/third_party/libjingle/source/talk/base/sha1digest_unittest.cc index 5ab68192463..d6c14b6d25d 100644 --- a/chromium/third_party/libjingle/source/talk/base/sha1digest_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/sha1digest_unittest.cc @@ -38,7 +38,7 @@ std::string Sha1(const std::string& input) { TEST(Sha1DigestTest, TestSize) { Sha1Digest sha1; - EXPECT_EQ(20U, Sha1Digest::kSize); + EXPECT_EQ(20, static_cast<int>(Sha1Digest::kSize)); EXPECT_EQ(20U, sha1.Size()); } diff --git a/chromium/third_party/libjingle/source/talk/base/signalthread.h b/chromium/third_party/libjingle/source/talk/base/signalthread.h index 79c00be3121..46dd0a3bad5 100644 --- a/chromium/third_party/libjingle/source/talk/base/signalthread.h +++ b/chromium/third_party/libjingle/source/talk/base/signalthread.h @@ -123,6 +123,7 @@ class SignalThread class Worker : public Thread { public: explicit Worker(SignalThread* parent) : parent_(parent) {} + virtual ~Worker() { Stop(); } virtual void Run() { parent_->Run(); } private: 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 4458f52ef2b..e5734d4df11 100644 --- a/chromium/third_party/libjingle/source/talk/base/signalthread_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/signalthread_unittest.cc @@ -120,6 +120,10 @@ class OwnerThread : public Thread, public sigslot::has_slots<> { has_run_(false) { } + virtual ~OwnerThread() { + Stop(); + } + virtual void Run() { SignalThreadTest::SlowSignalThread* signal_thread = new SignalThreadTest::SlowSignalThread(harness_); 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 eafab81f408..a9c4dbb0de4 100644 --- a/chromium/third_party/libjingle/source/talk/base/socket_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/socket_unittest.cc @@ -693,8 +693,8 @@ void SocketTest::TcpInternal(const IPAddress& loopback) { // Create test data. const size_t kDataSize = 1024 * 1024; - scoped_array<char> send_buffer(new char[kDataSize]); - scoped_array<char> recv_buffer(new char[kDataSize]); + scoped_ptr<char[]> send_buffer(new char[kDataSize]); + scoped_ptr<char[]> recv_buffer(new char[kDataSize]); size_t send_pos = 0, recv_pos = 0; for (size_t i = 0; i < kDataSize; ++i) { send_buffer[i] = static_cast<char>(i % 256); @@ -934,7 +934,7 @@ void SocketTest::UdpReadyToSend(const IPAddress& loopback) { scoped_ptr<TestClient> client( new TestClient(AsyncUDPSocket::Create(ss_, empty))); int test_packet_size = 1200; - talk_base::scoped_array<char> test_packet(new char[test_packet_size]); + talk_base::scoped_ptr<char[]> test_packet(new char[test_packet_size]); // Init the test packet just to avoid memcheck warning. memset(test_packet.get(), 0, test_packet_size); // Set the send buffer size to the same size as the test packet to have a diff --git a/chromium/third_party/libjingle/source/talk/base/ssladapter.cc b/chromium/third_party/libjingle/source/talk/base/ssladapter.cc index b7d82943ac2..5bcb619c2e3 100644 --- a/chromium/third_party/libjingle/source/talk/base/ssladapter.cc +++ b/chromium/third_party/libjingle/source/talk/base/ssladapter.cc @@ -58,6 +58,7 @@ SSLAdapter::Create(AsyncSocket* socket) { #elif SSL_USE_OPENSSL // && !SSL_USE_SCHANNEL return new OpenSSLAdapter(socket); #else // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL + delete socket; return NULL; #endif // !SSL_USE_OPENSSL && !SSL_USE_SCHANNEL } diff --git a/chromium/third_party/libjingle/source/talk/base/ssladapter.h b/chromium/third_party/libjingle/source/talk/base/ssladapter.h index 1583dc2eb61..4d2dbcde129 100644 --- a/chromium/third_party/libjingle/source/talk/base/ssladapter.h +++ b/chromium/third_party/libjingle/source/talk/base/ssladapter.h @@ -47,7 +47,9 @@ class SSLAdapter : public AsyncSocketAdapter { // negotiation will begin as soon as the socket connects. virtual int StartSSL(const char* hostname, bool restartable) = 0; - // Create the default SSL adapter for this platform + // Create the default SSL adapter for this platform. On failure, returns NULL + // and deletes |socket|. Otherwise, the returned SSLAdapter takes ownership + // of |socket|. static SSLAdapter* Create(AsyncSocket* socket); private: diff --git a/chromium/third_party/libjingle/source/talk/base/sslfingerprint.h b/chromium/third_party/libjingle/source/talk/base/sslfingerprint.h index 4d41156f862..b85778947e6 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslfingerprint.h +++ b/chromium/third_party/libjingle/source/talk/base/sslfingerprint.h @@ -47,9 +47,14 @@ struct SSLFingerprint { return NULL; } + return Create(algorithm, &(identity->certificate())); + } + + static SSLFingerprint* Create(const std::string& algorithm, + const talk_base::SSLCertificate* cert) { uint8 digest_val[64]; size_t digest_len; - bool ret = identity->certificate().ComputeDigest( + bool ret = cert->ComputeDigest( algorithm, digest_val, sizeof(digest_val), &digest_len); if (!ret) { return NULL; diff --git a/chromium/third_party/libjingle/source/talk/base/sslidentity.h b/chromium/third_party/libjingle/source/talk/base/sslidentity.h index b9425f78fdf..89b1008983c 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslidentity.h +++ b/chromium/third_party/libjingle/source/talk/base/sslidentity.h @@ -30,11 +30,18 @@ #ifndef TALK_BASE_SSLIDENTITY_H_ #define TALK_BASE_SSLIDENTITY_H_ +#include <algorithm> #include <string> +#include <vector> + +#include "talk/base/buffer.h" #include "talk/base/messagedigest.h" namespace talk_base { +// Forward declaration due to circular dependency with SSLCertificate. +class SSLCertChain; + // Abstract interface overridden by SSL library specific // implementations. @@ -55,19 +62,76 @@ class SSLCertificate { virtual ~SSLCertificate() {} // Returns a new SSLCertificate object instance wrapping the same - // underlying certificate. + // underlying certificate, including its chain if present. // Caller is responsible for freeing the returned object. virtual SSLCertificate* GetReference() const = 0; + // Provides the cert chain, or returns false. The caller owns the chain. + // The chain includes a copy of each certificate, excluding the leaf. + virtual bool GetChain(SSLCertChain** chain) const = 0; + // Returns a PEM encoded string representation of the certificate. virtual std::string ToPEMString() const = 0; + // Provides a DER encoded binary representation of the certificate. + virtual void ToDER(Buffer* der_buffer) const = 0; + + // Gets the name of the digest algorithm that was used to compute this + // certificate's signature. + 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; }; +// SSLCertChain is a simple wrapper for a vector of SSLCertificates. It serves +// primarily to ensure proper memory management (especially deletion) of the +// SSLCertificate pointers. +class SSLCertChain { + public: + // These constructors copy the provided SSLCertificate(s), so the caller + // retains ownership. + explicit SSLCertChain(const std::vector<SSLCertificate*>& certs) { + ASSERT(!certs.empty()); + certs_.resize(certs.size()); + std::transform(certs.begin(), certs.end(), certs_.begin(), DupCert); + } + explicit SSLCertChain(const SSLCertificate* cert) { + certs_.push_back(cert->GetReference()); + } + + ~SSLCertChain() { + std::for_each(certs_.begin(), certs_.end(), DeleteCert); + } + + // Vector access methods. + size_t GetSize() const { return certs_.size(); } + + // Returns a temporary reference, only valid until the chain is destroyed. + const SSLCertificate& Get(size_t pos) const { return *(certs_[pos]); } + + // Returns a new SSLCertChain object instance wrapping the same underlying + // certificate chain. Caller is responsible for freeing the returned object. + SSLCertChain* Copy() const { + return new SSLCertChain(certs_); + } + + private: + // Helper function for duplicating a vector of certificates. + static SSLCertificate* DupCert(const SSLCertificate* cert) { + return cert->GetReference(); + } + + // Helper function for deleting a vector of certificates. + static void DeleteCert(SSLCertificate* cert) { delete cert; } + + std::vector<SSLCertificate*> certs_; + + DISALLOW_COPY_AND_ASSIGN(SSLCertChain); +}; + // Our identity in an SSL negotiation: a keypair and certificate (both // with the same public key). // This too is pretty much immutable once created. @@ -108,4 +172,4 @@ extern const char kPemTypeRsaPrivateKey[]; } // namespace talk_base -#endif // TALK_BASE_SSLIDENTITY_H__ +#endif // TALK_BASE_SSLIDENTITY_H_ 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 6970426781d..b63b8b9d439 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslidentity_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/sslidentity_unittest.cc @@ -57,7 +57,7 @@ const unsigned char kTestCertSha1[] = {0xA6, 0xC8, 0x59, 0xEA, class SSLIdentityTest : public testing::Test { public: SSLIdentityTest() : - identity1_(NULL), identity2_(NULL) { + identity1_(), identity2_() { } ~SSLIdentityTest() { @@ -83,6 +83,22 @@ class SSLIdentityTest : public testing::Test { ASSERT_TRUE(test_cert_); } + void TestGetSignatureDigestAlgorithm() { + std::string digest_algorithm; + // Both NSSIdentity::Generate and OpenSSLIdentity::Generate are + // hard-coded to generate RSA-SHA1 certificates. + ASSERT_TRUE(identity1_->certificate().GetSignatureDigestAlgorithm( + &digest_algorithm)); + ASSERT_EQ(talk_base::DIGEST_SHA_1, digest_algorithm); + ASSERT_TRUE(identity2_->certificate().GetSignatureDigestAlgorithm( + &digest_algorithm)); + ASSERT_EQ(talk_base::DIGEST_SHA_1, digest_algorithm); + + // The test certificate has an MD5-based signature. + ASSERT_TRUE(test_cert_->GetSignatureDigestAlgorithm(&digest_algorithm)); + ASSERT_EQ(talk_base::DIGEST_MD5, digest_algorithm); + } + void TestDigest(const std::string &algorithm, size_t expected_len, const unsigned char *expected_digest = NULL) { unsigned char digest1[64]; @@ -203,3 +219,7 @@ TEST_F(SSLIdentityTest, PemDerConversion) { "CERTIFICATE", reinterpret_cast<const unsigned char*>(der.data()), der.length())); } + +TEST_F(SSLIdentityTest, GetSignatureDigestAlgorithm) { + TestGetSignatureDigestAlgorithm(); +} diff --git a/chromium/third_party/libjingle/source/talk/base/sslstreamadapter.h b/chromium/third_party/libjingle/source/talk/base/sslstreamadapter.h index 2afe1daf180..3a7797370c3 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslstreamadapter.h +++ b/chromium/third_party/libjingle/source/talk/base/sslstreamadapter.h @@ -25,8 +25,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_BASE_SSLSTREAMADAPTER_H__ -#define TALK_BASE_SSLSTREAMADAPTER_H__ +#ifndef TALK_BASE_SSLSTREAMADAPTER_H_ +#define TALK_BASE_SSLSTREAMADAPTER_H_ #include <string> #include <vector> @@ -111,9 +111,9 @@ class SSLStreamAdapter : public StreamAdapterInterface { // mode. // Generally, SetIdentity() and possibly SetServerRole() should have // been called before this. - // SetPeerCertificate() must also be called. It may be called after - // StartSSLWithPeer() but must be called before the underlying - // stream opens. + // SetPeerCertificate() or SetPeerCertificateDigest() must also be called. + // It may be called after StartSSLWithPeer() but must be called before the + // underlying stream opens. virtual int StartSSLWithPeer() = 0; // Specify the certificate that our peer is expected to use in @@ -138,6 +138,13 @@ 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. + virtual bool GetPeerCertificate(SSLCertificate** cert) const = 0; + // Key Exporter interface from RFC 5705 // Arguments are: // label -- the exporter label. @@ -182,4 +189,4 @@ class SSLStreamAdapter : public StreamAdapterInterface { } // namespace talk_base -#endif // TALK_BASE_SSLSTREAMADAPTER_H__ +#endif // TALK_BASE_SSLSTREAMADAPTER_H_ 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 1fe1a663484..4b2fd6d84c0 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslstreamadapter_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/sslstreamadapter_unittest.cc @@ -33,6 +33,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/sslconfig.h" #include "talk/base/sslidentity.h" @@ -388,6 +389,13 @@ class SSLStreamAdapterTestBase : public testing::Test, return server_ssl_->GetDtlsSrtpCipher(retval); } + bool GetPeerCertificate(bool client, talk_base::SSLCertificate** cert) { + if (client) + return client_ssl_->GetPeerCertificate(cert); + else + return server_ssl_->GetPeerCertificate(cert); + } + bool ExportKeyingMaterial(const char *label, const unsigned char *context, size_t context_len, @@ -754,7 +762,7 @@ TEST_F(SSLStreamAdapterTestDTLS, }; // Test a handshake with small MTU -TEST_F(SSLStreamAdapterTestDTLS, TestDTLSConnectWithSmallMtu) { +TEST_F(SSLStreamAdapterTestDTLS, DISABLED_TestDTLSConnectWithSmallMtu) { MAYBE_SKIP_TEST(HaveDtls); SetMtu(700); SetHandshakeWait(20000); @@ -885,3 +893,42 @@ TEST_F(SSLStreamAdapterTestDTLSFromPEMStrings, TestTransfer) { TestHandshake(); TestTransfer(100); } + +// Test getting the remote certificate. +TEST_F(SSLStreamAdapterTestDTLSFromPEMStrings, TestDTLSGetPeerCertificate) { + MAYBE_SKIP_TEST(HaveDtls); + + // Peer certificates haven't been received yet. + talk_base::scoped_ptr<talk_base::SSLCertificate> client_peer_cert; + ASSERT_FALSE(GetPeerCertificate(true, client_peer_cert.accept())); + ASSERT_FALSE(client_peer_cert != NULL); + + talk_base::scoped_ptr<talk_base::SSLCertificate> server_peer_cert; + ASSERT_FALSE(GetPeerCertificate(false, server_peer_cert.accept())); + ASSERT_FALSE(server_peer_cert != NULL); + + TestHandshake(); + + // The client should have a peer certificate after the handshake. + ASSERT_TRUE(GetPeerCertificate(true, client_peer_cert.accept())); + ASSERT_TRUE(client_peer_cert != NULL); + + // It's not kCERT_PEM. + std::string client_peer_string = client_peer_cert->ToPEMString(); + ASSERT_NE(kCERT_PEM, client_peer_string); + + // It must not have a chain, because the test certs are self-signed. + talk_base::SSLCertChain* client_peer_chain; + ASSERT_FALSE(client_peer_cert->GetChain(&client_peer_chain)); + + // The server should have a peer certificate after the handshake. + ASSERT_TRUE(GetPeerCertificate(false, server_peer_cert.accept())); + ASSERT_TRUE(server_peer_cert != NULL); + + // It's kCERT_PEM + ASSERT_EQ(kCERT_PEM, server_peer_cert->ToPEMString()); + + // It must not have a chain, because the test certs are self-signed. + talk_base::SSLCertChain* server_peer_chain; + ASSERT_FALSE(server_peer_cert->GetChain(&server_peer_chain)); +} diff --git a/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.cc b/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.cc index 5a1a255505d..b42faa80c60 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.cc +++ b/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.cc @@ -87,6 +87,14 @@ void SSLStreamAdapterHelper::SetPeerCertificate(SSLCertificate* cert) { peer_certificate_.reset(cert); } +bool SSLStreamAdapterHelper::GetPeerCertificate(SSLCertificate** cert) const { + if (!peer_certificate_) + return false; + + *cert = peer_certificate_->GetReference(); + return true; +} + bool SSLStreamAdapterHelper::SetPeerCertificateDigest( const std::string &digest_alg, const unsigned char* digest_val, diff --git a/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.h b/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.h index e8cb3b08b1b..7c280566127 100644 --- a/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.h +++ b/chromium/third_party/libjingle/source/talk/base/sslstreamadapterhelper.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. */ @@ -63,6 +63,7 @@ class SSLStreamAdapterHelper : public SSLStreamAdapter { virtual bool SetPeerCertificateDigest(const std::string& digest_alg, const unsigned char* digest_val, size_t digest_len); + virtual bool GetPeerCertificate(SSLCertificate** cert) const; virtual StreamState GetState() const; virtual void Close(); diff --git a/chromium/third_party/libjingle/source/talk/base/stream.cc b/chromium/third_party/libjingle/source/talk/base/stream.cc index 20adfcfa903..c1cf90743a0 100644 --- a/chromium/third_party/libjingle/source/talk/base/stream.cc +++ b/chromium/third_party/libjingle/source/talk/base/stream.cc @@ -166,7 +166,7 @@ StreamAdapterInterface::~StreamAdapterInterface() { /////////////////////////////////////////////////////////////////////////////// StreamTap::StreamTap(StreamInterface* stream, StreamInterface* tap) - : StreamAdapterInterface(stream), tap_(NULL), tap_result_(SR_SUCCESS), + : StreamAdapterInterface(stream), tap_(), tap_result_(SR_SUCCESS), tap_error_(0) { AttachTap(tap); } @@ -523,6 +523,106 @@ void FileStream::DoClose() { fclose(file_); } +CircularFileStream::CircularFileStream(size_t max_size) + : max_write_size_(max_size), + position_(0), + marked_position_(max_size / 2), + last_write_position_(0), + read_segment_(READ_LATEST), + read_segment_available_(0) { +} + +bool CircularFileStream::Open( + const std::string& filename, const char* mode, int* error) { + if (!FileStream::Open(filename.c_str(), mode, error)) + return false; + + if (strchr(mode, "r") != NULL) { // Opened in read mode. + // Check if the buffer has been overwritten and determine how to read the + // log in time sequence. + size_t file_size; + GetSize(&file_size); + if (file_size == position_) { + // The buffer has not been overwritten yet. Read 0 .. file_size + read_segment_ = READ_LATEST; + read_segment_available_ = file_size; + } else { + // The buffer has been over written. There are three segments: The first + // one is 0 .. marked_position_, which is the marked earliest log. The + // second one is position_ .. file_size, which is the middle log. The + // last one is marked_position_ .. position_, which is the latest log. + read_segment_ = READ_MARKED; + read_segment_available_ = marked_position_; + last_write_position_ = position_; + } + + // Read from the beginning. + position_ = 0; + SetPosition(position_); + } + + return true; +} + +StreamResult CircularFileStream::Read(void* buffer, size_t buffer_len, + size_t* read, int* error) { + if (read_segment_available_ == 0) { + size_t file_size; + switch (read_segment_) { + case READ_MARKED: // Finished READ_MARKED and start READ_MIDDLE. + read_segment_ = READ_MIDDLE; + position_ = last_write_position_; + SetPosition(position_); + GetSize(&file_size); + read_segment_available_ = file_size - position_; + break; + + case READ_MIDDLE: // Finished READ_MIDDLE and start READ_LATEST. + read_segment_ = READ_LATEST; + position_ = marked_position_; + SetPosition(position_); + read_segment_available_ = last_write_position_ - position_; + break; + + default: // Finished READ_LATEST and return EOS. + return talk_base::SR_EOS; + } + } + + size_t local_read; + if (!read) read = &local_read; + + size_t to_read = talk_base::_min(buffer_len, read_segment_available_); + talk_base::StreamResult result + = talk_base::FileStream::Read(buffer, to_read, read, error); + if (result == talk_base::SR_SUCCESS) { + read_segment_available_ -= *read; + position_ += *read; + } + return result; +} + +StreamResult CircularFileStream::Write(const void* data, size_t data_len, + size_t* written, int* error) { + if (position_ >= max_write_size_) { + ASSERT(position_ == max_write_size_); + position_ = marked_position_; + SetPosition(position_); + } + + size_t local_written; + if (!written) written = &local_written; + + size_t to_eof = max_write_size_ - position_; + size_t to_write = talk_base::_min(data_len, to_eof); + talk_base::StreamResult result + = talk_base::FileStream::Write(data, to_write, written, error); + if (result == talk_base::SR_SUCCESS) { + position_ += *written; + } + return result; +} + AsyncWriteStream::~AsyncWriteStream() { write_thread_->Clear(this, 0, NULL); ClearBufferAndWrite(); diff --git a/chromium/third_party/libjingle/source/talk/base/stream.h b/chromium/third_party/libjingle/source/talk/base/stream.h index 6700c402df6..4571def9bb9 100644 --- a/chromium/third_party/libjingle/source/talk/base/stream.h +++ b/chromium/third_party/libjingle/source/talk/base/stream.h @@ -468,6 +468,35 @@ class FileStream : public StreamInterface { DISALLOW_EVIL_CONSTRUCTORS(FileStream); }; +// A stream that caps the output at a certain size, dropping content from the +// middle of the logical stream and maintaining equal parts of the start/end of +// the logical stream. +class CircularFileStream : public FileStream { + public: + explicit CircularFileStream(size_t max_size); + + virtual bool Open(const std::string& filename, const char* mode, int* error); + virtual StreamResult Read(void* buffer, size_t buffer_len, + size_t* read, int* error); + virtual StreamResult Write(const void* data, size_t data_len, + size_t* written, int* error); + + private: + enum ReadSegment { + READ_MARKED, // Read 0 .. marked_position_ + READ_MIDDLE, // Read position_ .. file_size + READ_LATEST, // Read marked_position_ .. position_ if the buffer was + // overwritten or 0 .. position_ otherwise. + }; + + size_t max_write_size_; + size_t position_; + size_t marked_position_; + size_t last_write_position_; + ReadSegment read_segment_; + size_t read_segment_available_; +}; + // A stream which pushes writes onto a separate thread and // returns from the write call immediately. @@ -662,7 +691,7 @@ class FifoBuffer : public StreamInterface { size_t offset, size_t* bytes_written); StreamState state_; // keeps the opened/closed state of the stream - scoped_array<char> buffer_; // the allocated buffer + scoped_ptr<char[]> buffer_; // the allocated buffer size_t buffer_length_; // size of the allocated buffer size_t data_length_; // amount of readable data in the buffer size_t read_position_; // offset to the readable data diff --git a/chromium/third_party/libjingle/source/talk/base/systeminfo.cc b/chromium/third_party/libjingle/source/talk/base/systeminfo.cc index ec1865889ec..ff331ac71a6 100644 --- a/chromium/third_party/libjingle/source/talk/base/systeminfo.cc +++ b/chromium/third_party/libjingle/source/talk/base/systeminfo.cc @@ -74,7 +74,7 @@ static void GetProcessorInformation(int* physical_cpus, int* cache_size) { // Determine buffer size, allocate and get processor information. // Size can change between calls (unlikely), so a loop is done. DWORD return_length = 0; - scoped_array<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> infos; + scoped_ptr<SYSTEM_LOGICAL_PROCESSOR_INFORMATION[]> infos; while (!glpi(infos.get(), &return_length)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { infos.reset(new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[ @@ -183,6 +183,8 @@ SystemInfo::SystemInfo() if (!sysctlbyname("machdep.cpu.stepping", &sysctl_value, &length, NULL, 0)) { cpu_stepping_ = static_cast<int>(sysctl_value); } +#elif defined(__native_client__) + // TODO(ryanpetrie): Implement this via PPAPI when it's available. #else // LINUX || ANDROID ProcCpuInfo proc_info; if (proc_info.LoadFromSystem()) { diff --git a/chromium/third_party/libjingle/source/talk/base/template_util.h b/chromium/third_party/libjingle/source/talk/base/template_util.h new file mode 100644 index 00000000000..5cc4ba430a2 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/base/template_util.h @@ -0,0 +1,106 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TALK_BASE_TEMPLATE_UTIL_H_ +#define TALK_BASE_TEMPLATE_UTIL_H_ + +#include <cstddef> // For size_t. + +namespace talk_base { + +// template definitions from tr1 + +template<class T, T v> +struct integral_constant { + static const T value = v; + typedef T value_type; + typedef integral_constant<T, v> type; +}; + +template <class T, T v> const T integral_constant<T, v>::value; + +typedef integral_constant<bool, true> true_type; +typedef integral_constant<bool, false> false_type; + +template <class T> struct is_pointer : false_type {}; +template <class T> struct is_pointer<T*> : true_type {}; + +template <class T, class U> struct is_same : public false_type {}; +template <class T> struct is_same<T,T> : true_type {}; + +template<class> struct is_array : public false_type {}; +template<class T, size_t n> struct is_array<T[n]> : public true_type {}; +template<class T> struct is_array<T[]> : public true_type {}; + +template <class T> struct is_non_const_reference : false_type {}; +template <class T> struct is_non_const_reference<T&> : true_type {}; +template <class T> struct is_non_const_reference<const T&> : false_type {}; + +template <class T> struct is_void : false_type {}; +template <> struct is_void<void> : true_type {}; + +namespace internal { + +// Types YesType and NoType are guaranteed such that sizeof(YesType) < +// sizeof(NoType). +typedef char YesType; + +struct NoType { + YesType dummy[2]; +}; + +// This class is an implementation detail for is_convertible, and you +// don't need to know how it works to use is_convertible. For those +// who care: we declare two different functions, one whose argument is +// of type To and one with a variadic argument list. We give them +// return types of different size, so we can use sizeof to trick the +// compiler into telling us which function it would have chosen if we +// had called it with an argument of type From. See Alexandrescu's +// _Modern C++ Design_ for more details on this sort of trick. + +struct ConvertHelper { + template <typename To> + static YesType Test(To); + + template <typename To> + static NoType Test(...); + + template <typename From> + static From& Create(); +}; + +// Used to determine if a type is a struct/union/class. Inspired by Boost's +// is_class type_trait implementation. +struct IsClassHelper { + template <typename C> + static YesType Test(void(C::*)(void)); + + template <typename C> + static NoType Test(...); +}; + +} // namespace internal + +// Inherits from true_type if From is convertible to To, false_type otherwise. +// +// Note that if the type is convertible, this will be a true_type REGARDLESS +// of whether or not the conversion would emit a warning. +template <typename From, typename To> +struct is_convertible + : integral_constant<bool, + sizeof(internal::ConvertHelper::Test<To>( + internal::ConvertHelper::Create<From>())) == + sizeof(internal::YesType)> { +}; + +template <typename T> +struct is_class + : integral_constant<bool, + sizeof(internal::IsClassHelper::Test<T>(0)) == + sizeof(internal::YesType)> { +}; + +} // namespace talk_base + +#endif // TALK_BASE_TEMPLATE_UTIL_H_ diff --git a/chromium/third_party/libjingle/source/talk/base/testclient.cc b/chromium/third_party/libjingle/source/talk/base/testclient.cc index 0ef85183117..04d60309963 100644 --- a/chromium/third_party/libjingle/source/talk/base/testclient.cc +++ b/chromium/third_party/libjingle/source/talk/base/testclient.cc @@ -80,13 +80,20 @@ TestClient::Packet* TestClient::NextPacket() { // the wrong thread to non-thread-safe objects. uint32 end = TimeAfter(kTimeout); - while (packets_->size() == 0 && TimeUntil(end) > 0) + while (TimeUntil(end) > 0) { + { + CritScope cs(&crit_); + if (packets_->size() != 0) { + break; + } + } Thread::Current()->ProcessMessages(1); + } // Return the first packet placed in the queue. Packet* packet = NULL; + CritScope cs(&crit_); if (packets_->size() > 0) { - CritScope cs(&crit_); packet = packets_->front(); packets_->erase(packets_->begin()); } @@ -128,7 +135,8 @@ bool TestClient::ready_to_send() const { } void TestClient::OnPacket(AsyncPacketSocket* socket, const char* buf, - size_t size, const SocketAddress& remote_addr) { + size_t size, const SocketAddress& remote_addr, + const PacketTime& packet_time) { CritScope cs(&crit_); packets_->push_back(new Packet(remote_addr, buf, size)); } diff --git a/chromium/third_party/libjingle/source/talk/base/testclient.h b/chromium/third_party/libjingle/source/talk/base/testclient.h index 1e1780a2988..87e32df1f46 100644 --- a/chromium/third_party/libjingle/source/talk/base/testclient.h +++ b/chromium/third_party/libjingle/source/talk/base/testclient.h @@ -94,7 +94,8 @@ class TestClient : public sigslot::has_slots<> { Socket::ConnState GetState(); // Slot for packets read on the socket. void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t len, - const SocketAddress& remote_addr); + const SocketAddress& remote_addr, + const PacketTime& packet_time); void OnReadyToSend(AsyncPacketSocket* socket); CriticalSection crit_; diff --git a/chromium/third_party/libjingle/source/talk/base/testechoserver.h b/chromium/third_party/libjingle/source/talk/base/testechoserver.h index 10466fa4359..5c1045423c3 100644 --- a/chromium/third_party/libjingle/source/talk/base/testechoserver.h +++ b/chromium/third_party/libjingle/source/talk/base/testechoserver.h @@ -67,7 +67,8 @@ class TestEchoServer : public sigslot::has_slots<> { } } void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t size, - const SocketAddress& remote_addr) { + const SocketAddress& remote_addr, + const PacketTime& packet_time) { socket->Send(buf, size, DSCP_NO_CHANGE); } void OnClose(AsyncPacketSocket* socket, int err) { diff --git a/chromium/third_party/libjingle/source/talk/base/testutils.h b/chromium/third_party/libjingle/source/talk/base/testutils.h index 769d95f6464..e8ad7200940 100644 --- a/chromium/third_party/libjingle/source/talk/base/testutils.h +++ b/chromium/third_party/libjingle/source/talk/base/testutils.h @@ -30,6 +30,13 @@ // Utilities for testing talk_base infrastructure in unittests +#ifdef LINUX +#include <X11/Xlib.h> +// X defines a few macros that stomp on types that gunit.h uses. +#undef None +#undef Bool +#endif + #include <map> #include <vector> #include "talk/base/asyncsocket.h" @@ -565,6 +572,38 @@ inline AssertionResult CmpHelperFileEq(const char* expected_expression, /////////////////////////////////////////////////////////////////////////////// +// Helpers for determining if X/screencasting is available (on linux). + +#define MAYBE_SKIP_SCREENCAST_TEST() \ + if (!testing::IsScreencastingAvailable()) { \ + LOG(LS_WARNING) << "Skipping test, since it doesn't have the requisite " \ + << "X environment for screen capture."; \ + return; \ + } \ + +#ifdef LINUX +struct XDisplay { + XDisplay() : display_(XOpenDisplay(NULL)) { } + ~XDisplay() { if (display_) XCloseDisplay(display_); } + bool IsValid() const { return display_ != NULL; } + operator Display*() { return display_; } + private: + Display* display_; +}; +#endif + +// Returns true if screencasting is available. When false, anything that uses +// screencasting features may fail. +inline bool IsScreencastingAvailable() { +#ifdef LINUX + XDisplay display; + if (!display.IsValid()) { + LOG(LS_WARNING) << "No X Display available."; + return false; + } +#endif + return true; +} } // namespace testing #endif // TALK_BASE_TESTUTILS_H__ diff --git a/chromium/third_party/libjingle/source/talk/base/thread.cc b/chromium/third_party/libjingle/source/talk/base/thread.cc index d21d5f19503..d07efb51568 100644 --- a/chromium/third_party/libjingle/source/talk/base/thread.cc +++ b/chromium/third_party/libjingle/source/talk/base/thread.cc @@ -146,7 +146,6 @@ Thread::Thread(SocketServer* ss) : MessageQueue(ss), priority_(PRIORITY_NORMAL), started_(false), - has_sends_(false), #if defined(WIN32) thread_(NULL), thread_id_(0), @@ -405,7 +404,6 @@ void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { smsg.msg = msg; smsg.ready = &ready; sendlist_.push_back(smsg); - has_sends_ = true; } // Wait for a reply @@ -413,11 +411,15 @@ void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { ss_->WakeUp(); bool waited = false; + crit_.Enter(); while (!ready) { + crit_.Leave(); current_thread->ReceiveSends(); current_thread->socketserver()->Wait(kForever, false); waited = true; + crit_.Enter(); } + crit_.Leave(); // Our Wait loop above may have consumed some WakeUp events for this // MessageQueue, that weren't relevant to this Send. Losing these WakeUps can @@ -436,11 +438,6 @@ void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) { } void Thread::ReceiveSends() { - // Before entering critical section, check boolean. - - if (!has_sends_) - return; - // Receive a sent message. Cleanup scenarios: // - thread sending exits: We don't allow this, since thread can exit // only via Join, so Send must complete. @@ -456,7 +453,6 @@ void Thread::ReceiveSends() { *smsg.ready = true; smsg.thread->socketserver()->WakeUp(); } - has_sends_ = false; crit_.Leave(); } @@ -561,6 +557,7 @@ AutoThread::AutoThread(SocketServer* ss) : Thread(ss) { } AutoThread::~AutoThread() { + Stop(); if (ThreadManager::Instance()->CurrentThread() == this) { ThreadManager::Instance()->SetCurrentThread(NULL); } diff --git a/chromium/third_party/libjingle/source/talk/base/thread.h b/chromium/third_party/libjingle/source/talk/base/thread.h index 55ec0daf722..4dc09f641d7 100644 --- a/chromium/third_party/libjingle/source/talk/base/thread.h +++ b/chromium/third_party/libjingle/source/talk/base/thread.h @@ -111,9 +111,15 @@ class Runnable { DISALLOW_COPY_AND_ASSIGN(Runnable); }; +// WARNING! SUBCLASSES MUST CALL Stop() IN THEIR DESTRUCTORS! See ~Thread(). + class Thread : public MessageQueue { public: explicit Thread(SocketServer* ss = NULL); + // NOTE: ALL SUBCLASSES OF Thread MUST CALL Stop() IN THEIR DESTRUCTORS (or + // guarantee Stop() is explicitly called before the subclass is destroyed). + // This is required to avoid a data race between the destructor modifying the + // vtable, and the Thread::PreRun calling the virtual method Run(). virtual ~Thread(); static Thread* Current(); @@ -255,7 +261,6 @@ class Thread : public MessageQueue { std::string name_; ThreadPriority priority_; bool started_; - bool has_sends_; #ifdef POSIX pthread_t thread_; @@ -292,6 +297,7 @@ class AutoThread : public Thread { class ComThread : public Thread { public: ComThread() {} + virtual ~ComThread() { Stop(); } protected: virtual void Run(); 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 246faa4a53e..896fbabc5fd 100644 --- a/chromium/third_party/libjingle/source/talk/base/thread_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/thread_unittest.cc @@ -81,7 +81,8 @@ class SocketClient : public TestGenerator, public sigslot::has_slots<> { SocketAddress address() const { return socket_->GetLocalAddress(); } void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t size, - const SocketAddress& remote_addr) { + const SocketAddress& remote_addr, + const PacketTime& packet_time) { EXPECT_EQ(size, sizeof(uint32)); uint32 prev = reinterpret_cast<const uint32*>(buf)[0]; uint32 result = Next(prev); @@ -122,7 +123,7 @@ class MessageClient : public MessageHandler, public TestGenerator { class CustomThread : public talk_base::Thread { public: CustomThread() {} - virtual ~CustomThread() {} + virtual ~CustomThread() { Stop(); } bool Start() { return false; } }; @@ -136,6 +137,7 @@ class SignalWhenDestroyedThread : public Thread { } virtual ~SignalWhenDestroyedThread() { + Stop(); event_->Set(); } @@ -159,8 +161,8 @@ class Functor2 { bool* flag_; }; - -TEST(ThreadTest, Main) { +// See: https://code.google.com/p/webrtc/issues/detail?id=2409 +TEST(ThreadTest, DISABLED_Main) { const SocketAddress addr("127.0.0.1", 0); // Create the messaging client on its own thread. diff --git a/chromium/third_party/libjingle/source/talk/base/timeutils.cc b/chromium/third_party/libjingle/source/talk/base/timeutils.cc index 66b9bf2c7c0..54db3418bbb 100644 --- a/chromium/third_party/libjingle/source/talk/base/timeutils.cc +++ b/chromium/third_party/libjingle/source/talk/base/timeutils.cc @@ -94,6 +94,10 @@ uint32 Time() { return static_cast<uint32>(TimeNanos() / kNumNanosecsPerMillisec); } +uint64 TimeMicros() { + return static_cast<uint64>(TimeNanos() / kNumNanosecsPerMicrosec); +} + #if defined(WIN32) static const uint64 kFileTimeToUnixTimeEpochOffset = 116444736000000000ULL; diff --git a/chromium/third_party/libjingle/source/talk/base/timeutils.h b/chromium/third_party/libjingle/source/talk/base/timeutils.h index 545e86a1243..f13c3f2ef2d 100644 --- a/chromium/third_party/libjingle/source/talk/base/timeutils.h +++ b/chromium/third_party/libjingle/source/talk/base/timeutils.h @@ -42,6 +42,8 @@ static const int64 kNumMicrosecsPerMillisec = kNumMicrosecsPerSec / kNumMillisecsPerSec; static const int64 kNumNanosecsPerMillisec = kNumNanosecsPerSec / kNumMillisecsPerSec; +static const int64 kNumNanosecsPerMicrosec = kNumNanosecsPerSec / + kNumMicrosecsPerSec; // January 1970, in NTP milliseconds. static const int64 kJan1970AsNtpMillisecs = INT64_C(2208988800000); @@ -50,6 +52,8 @@ typedef uint32 TimeStamp; // Returns the current time in milliseconds. uint32 Time(); +// Returns the current time in microseconds. +uint64 TimeMicros(); // Returns the current time in nanoseconds. uint64 TimeNanos(); 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 f3b13fc0f62..b31b8c8b074 100644 --- a/chromium/third_party/libjingle/source/talk/base/virtualsocket_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/virtualsocket_unittest.cc @@ -46,7 +46,7 @@ struct Sender : public MessageHandler { Sender(Thread* th, AsyncSocket* s, uint32 rt) : thread(th), socket(new AsyncUDPSocket(s)), done(false), rate(rt), count(0) { - last_send = Time(); + last_send = talk_base::Time(); thread->PostDelayed(NextDelay(), this, 1); } @@ -61,7 +61,7 @@ struct Sender : public MessageHandler { if (done) return; - uint32 cur_time = Time(); + uint32 cur_time = talk_base::Time(); uint32 delay = cur_time - last_send; uint32 size = rate * delay / 1000; size = std::min<uint32>(size, 4096); @@ -97,7 +97,8 @@ struct Receiver : public MessageHandler, public sigslot::has_slots<> { } void OnReadPacket(AsyncPacketSocket* s, const char* data, size_t size, - const SocketAddress& remote_addr) { + const SocketAddress& remote_addr, + const PacketTime& packet_time) { ASSERT_EQ(socket.get(), s); ASSERT_GE(size, 4U); @@ -105,7 +106,7 @@ struct Receiver : public MessageHandler, public sigslot::has_slots<> { sec_count += size; uint32 send_time = *reinterpret_cast<const uint32*>(data); - uint32 recv_time = Time(); + uint32 recv_time = talk_base::Time(); uint32 delay = recv_time - send_time; sum += delay; sum_sq += delay * delay; @@ -863,7 +864,8 @@ TEST_F(VirtualSocketServerTest, delay_v4) { DelayTest(ipv4_test_addr); } -TEST_F(VirtualSocketServerTest, delay_v6) { +// See: https://code.google.com/p/webrtc/issues/detail?id=2409 +TEST_F(VirtualSocketServerTest, DISABLED_delay_v6) { SocketAddress ipv6_test_addr(IPAddress(in6addr_any), 1000); DelayTest(ipv6_test_addr); } diff --git a/chromium/third_party/libjingle/source/talk/base/win32filesystem.cc b/chromium/third_party/libjingle/source/talk/base/win32filesystem.cc index 42c03885586..5caeac2a32d 100644 --- a/chromium/third_party/libjingle/source/talk/base/win32filesystem.cc +++ b/chromium/third_party/libjingle/source/talk/base/win32filesystem.cc @@ -111,7 +111,7 @@ bool Win32Filesystem::CreatePrivateFile(const Pathname &filename) { &token_user_size); // Get the TOKEN_USER structure. - scoped_array<char> token_user_bytes(new char[token_user_size]); + scoped_ptr<char[]> token_user_bytes(new char[token_user_size]); PTOKEN_USER token_user = reinterpret_cast<PTOKEN_USER>( token_user_bytes.get()); memset(token_user, 0, token_user_size); @@ -137,7 +137,7 @@ bool Win32Filesystem::CreatePrivateFile(const Pathname &filename) { GetLengthSid(token_user->User.Sid); // Allocate it. - scoped_array<char> acl_bytes(new char[acl_size]); + scoped_ptr<char[]> acl_bytes(new char[acl_size]); PACL acl = reinterpret_cast<PACL>(acl_bytes.get()); memset(acl, 0, acl_size); if (!::InitializeAcl(acl, acl_size, ACL_REVISION)) { @@ -440,7 +440,7 @@ bool Win32Filesystem::GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { Pathname Win32Filesystem::GetCurrentDirectory() { Pathname cwd; int path_len = 0; - scoped_array<wchar_t> path; + scoped_ptr<wchar_t[]> path; do { int needed = ::GetCurrentDirectory(path_len, path.get()); if (needed == 0) { diff --git a/chromium/third_party/libjingle/source/talk/base/win32regkey.cc b/chromium/third_party/libjingle/source/talk/base/win32regkey.cc index cb98cafadeb..403fdc014c0 100644 --- a/chromium/third_party/libjingle/source/talk/base/win32regkey.cc +++ b/chromium/third_party/libjingle/source/talk/base/win32regkey.cc @@ -160,7 +160,7 @@ HRESULT RegKey::GetValue(const wchar_t* full_key_name, ASSERT(full_key_name != NULL); DWORD byte_count = 0; - scoped_array<byte> buffer; + scoped_ptr<byte[]> buffer; HRESULT hr = GetValueStaticHelper(full_key_name, value_name, REG_BINARY, buffer.accept(), &byte_count); if (SUCCEEDED(hr)) { @@ -179,7 +179,7 @@ HRESULT RegKey::GetValue(const wchar_t* full_key_name, ASSERT(full_key_name != NULL); DWORD byte_count = 0; - scoped_array<byte> buffer; + scoped_ptr<byte[]> buffer; HRESULT hr = GetValueStaticHelper(full_key_name, value_name, REG_BINARY, buffer.accept(), &byte_count); if (SUCCEEDED(hr)) { @@ -206,7 +206,7 @@ HRESULT RegKey::GetValue(const wchar_t* full_key_name, ASSERT(full_key_name != NULL); ASSERT(value != NULL); - scoped_array<wchar_t> buffer; + scoped_ptr<wchar_t[]> buffer; HRESULT hr = RegKey::GetValue(full_key_name, value_name, buffer.accept()); if (SUCCEEDED(hr)) { value->assign(buffer.get()); diff --git a/chromium/third_party/libjingle/source/talk/base/win32socketserver.cc b/chromium/third_party/libjingle/source/talk/base/win32socketserver.cc index 55128e7a255..95666697337 100644 --- a/chromium/third_party/libjingle/source/talk/base/win32socketserver.cc +++ b/chromium/third_party/libjingle/source/talk/base/win32socketserver.cc @@ -620,6 +620,9 @@ int Win32Socket::TranslateOption(Option opt, int* slevel, int* sopt) { *slevel = IPPROTO_TCP; *sopt = TCP_NODELAY; break; + case OPT_DSCP: + LOG(LS_WARNING) << "Socket::OPT_DSCP not supported."; + return -1; default: ASSERT(false); return -1; diff --git a/chromium/third_party/libjingle/source/talk/base/win32socketserver.h b/chromium/third_party/libjingle/source/talk/base/win32socketserver.h index 1fa65235ea1..34598793db0 100644 --- a/chromium/third_party/libjingle/source/talk/base/win32socketserver.h +++ b/chromium/third_party/libjingle/source/talk/base/win32socketserver.h @@ -156,6 +156,7 @@ class Win32Thread : public Thread { set_socketserver(&ss_); } virtual ~Win32Thread() { + Stop(); set_socketserver(NULL); } virtual void Run() { diff --git a/chromium/third_party/libjingle/source/talk/base/windowpicker_unittest.cc b/chromium/third_party/libjingle/source/talk/base/windowpicker_unittest.cc index e1a815d4048..436854a3c48 100644 --- a/chromium/third_party/libjingle/source/talk/base/windowpicker_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/base/windowpicker_unittest.cc @@ -1,4 +1,5 @@ #include "talk/base/gunit.h" +#include "talk/base/testutils.h" #include "talk/base/window.h" #include "talk/base/windowpicker.h" #include "talk/base/windowpickerfactory.h" @@ -10,6 +11,7 @@ #endif TEST(WindowPickerTest, GetWindowList) { + MAYBE_SKIP_SCREENCAST_TEST(); if (!talk_base::WindowPickerFactory::IsSupported()) { LOG(LS_INFO) << "skipping test: window capturing is not supported with " << "current configuration."; @@ -24,6 +26,7 @@ TEST(WindowPickerTest, GetWindowList) { // TODO(hughv) Investigate why this fails on pulse but not locally after // upgrading to XCode 4.5. The failure is GetDesktopList returning FALSE. TEST(WindowPickerTest, DISABLE_ON_MAC(GetDesktopList)) { + MAYBE_SKIP_SCREENCAST_TEST(); if (!talk_base::WindowPickerFactory::IsSupported()) { LOG(LS_INFO) << "skipping test: window capturing is not supported with " << "current configuration."; diff --git a/chromium/third_party/libjingle/source/talk/build/isolate.gypi b/chromium/third_party/libjingle/source/talk/build/isolate.gypi new file mode 100644 index 00000000000..7b0ac1254d6 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/build/isolate.gypi @@ -0,0 +1,136 @@ +# +# 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. +# + +# Copied from Chromium's src/build/isolate.gypi +# +# It was necessary to copy this file to libjingle , because the path to +# 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 + +# This file is meant to be included into a target to provide a rule +# to "build" .isolate files into a .isolated file. +# +# To use this, create a gyp target with the following form: +# 'conditions': [ +# ['test_isolation_mode != "noop"', { +# 'targets': [ +# { +# 'target_name': 'foo_test_run', +# 'type': 'none', +# 'dependencies': [ +# 'foo_test', +# ], +# 'includes': [ +# '../build/isolate.gypi', +# 'foo_test.isolate', +# ], +# 'sources': [ +# 'foo_test.isolate', +# ], +# }, +# ], +# }], +# ], +# +# Note: foo_test.isolate is included and a source file. It is an inherent +# property of the .isolate format. This permits to define GYP variables but is +# a stricter format than GYP so isolate.py can read it. +# +# The generated .isolated file will be: +# <(PRODUCT_DIR)/foo_test.isolated + +{ + 'rules': [ + { + 'rule_name': 'isolate', + 'extension': 'isolate', + 'inputs': [ + # 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 + # dependency. This improves the runtime of the build driver since it + # doesn't have to stat() all these files. + # + # More importantly, it means that even if a isolate_dependency_tracked + # file is missing, for example if a file was deleted and the .isolate + # file was not updated, that won't break the build, especially in the + # case where foo_tests_run is not built! This should be reenabled once + # the switch-over to running tests on Swarm is completed. + #'<@(isolate_dependency_tracked)', + ], + 'outputs': [ + '<(PRODUCT_DIR)/<(RULE_INPUT_ROOT).isolated', + ], + '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)', + ], + }, { + '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)', + ], + }], + ['test_isolation_fail_on_missing == 0', { + '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 52e67a2d2fa..59974f7a834 100644 --- a/chromium/third_party/libjingle/source/talk/examples/android/AndroidManifest.xml +++ b/chromium/third_party/libjingle/source/talk/examples/android/AndroidManifest.xml @@ -13,7 +13,6 @@ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:label="@string/app_name" diff --git a/chromium/third_party/libjingle/source/talk/examples/android/project.properties b/chromium/third_party/libjingle/source/talk/examples/android/project.properties index a3ee5ab64f5..8459f9b8107 100644 --- a/chromium/third_party/libjingle/source/talk/examples/android/project.properties +++ b/chromium/third_party/libjingle/source/talk/examples/android/project.properties @@ -11,4 +11,6 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-19 + +java.compilerargs=-Xlint:all -Werror diff --git a/chromium/third_party/libjingle/source/talk/examples/call/callclient.cc b/chromium/third_party/libjingle/source/talk/examples/call/callclient.cc index 66c4b6fa69f..849455eaa54 100644 --- a/chromium/third_party/libjingle/source/talk/examples/call/callclient.cc +++ b/chromium/third_party/libjingle/source/talk/examples/call/callclient.cc @@ -396,7 +396,7 @@ CallClient::CallClient(buzz::XmppClient* xmpp_client, transport_protocol_(cricket::ICEPROTO_HYBRID), sdes_policy_(cricket::SEC_DISABLED), dtls_policy_(cricket::SEC_DISABLED), - ssl_identity_(NULL), + ssl_identity_(), show_roster_messages_(false) { xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange); my_status_.set_caps_node(caps_node); @@ -1601,7 +1601,7 @@ void CallClient::PrintStats() const { vmi.senders.begin(); it != vmi.senders.end(); ++it) { console_->PrintLine("Sender: ssrc=%u codec='%s' bytes=%d packets=%d " "rtt=%d jitter=%d", - it->ssrc, it->codec_name.c_str(), it->bytes_sent, + it->ssrc(), it->codec_name.c_str(), it->bytes_sent, it->packets_sent, it->rtt_ms, it->jitter_ms); } @@ -1609,7 +1609,7 @@ void CallClient::PrintStats() const { vmi.receivers.begin(); it != vmi.receivers.end(); ++it) { console_->PrintLine("Receiver: ssrc=%u bytes=%d packets=%d " "jitter=%d loss=%.2f", - it->ssrc, it->bytes_rcvd, it->packets_rcvd, + it->ssrc(), it->bytes_rcvd, it->packets_rcvd, it->jitter_ms, it->fraction_lost); } } diff --git a/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.cc b/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.cc index 1b59910b0b8..59b1c69fb9a 100644 --- a/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.cc +++ b/chromium/third_party/libjingle/source/talk/examples/chat/chatapp.cc @@ -38,10 +38,10 @@ ChatApp::ChatApp(buzz::XmppClient* xmpp_client, talk_base::Thread* main_thread) : xmpp_client_(xmpp_client), - presence_out_task_(NULL), - presence_receive_task_(NULL), - message_send_task_(NULL), - message_received_task_(NULL), + 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); 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 index 99f5166669b..d6c86d8422f 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m +++ b/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppClient.m @@ -49,6 +49,19 @@ @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); 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 index 34aa7520cbf..65cdd097944 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m +++ b/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m @@ -142,6 +142,14 @@ @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 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 index ab84c0932b4..bd346efcd3a 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCViewController.m +++ b/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/APPRTCViewController.m @@ -33,6 +33,10 @@ @implementation APPRTCViewController +@synthesize textField = _textField; +@synthesize textInstructions = _textInstructions; +@synthesize textOutput = _textOutput; + - (void)viewDidLoad { [super viewDidLoad]; self.textField.delegate = self; 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 index 9126f67c70a..e0d9a807692 100644 --- a/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/GAEChannelClient.m +++ b/chromium/third_party/libjingle/source/talk/examples/ios/AppRTCDemo/GAEChannelClient.m @@ -38,6 +38,9 @@ @implementation GAEChannelClient +@synthesize delegate = _delegate; +@synthesize webView = _webView; + - (id)initWithToken:(NSString *)token delegate:(id<GAEMessageHandler>)delegate { self = [super init]; if (self) { diff --git a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/linux/main_wnd.h b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/linux/main_wnd.h index c23c1164be6..5a44640e1f3 100644 --- a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/linux/main_wnd.h +++ b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/linux/main_wnd.h @@ -110,7 +110,7 @@ class GtkMainWnd : public MainWindow { } protected: - talk_base::scoped_array<uint8> image_; + talk_base::scoped_ptr<uint8[]> image_; int width_; int height_; GtkMainWnd* main_wnd_; diff --git a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/main_wnd.h b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/main_wnd.h index 0ed0f1422bf..6d2bf3eeee3 100644 --- a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/main_wnd.h +++ b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/main_wnd.h @@ -147,7 +147,7 @@ class MainWnd : public MainWindow { HWND wnd_; BITMAPINFO bmi_; - talk_base::scoped_array<uint8> image_; + talk_base::scoped_ptr<uint8[]> image_; CRITICAL_SECTION buffer_lock_; talk_base::scoped_refptr<webrtc::VideoTrackInterface> rendered_track_; }; diff --git a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/peer_connection_client.cc b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/peer_connection_client.cc index 403fabda182..9cdaedcbf05 100644 --- a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/peer_connection_client.cc +++ b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/peer_connection_client.cc @@ -134,17 +134,16 @@ void PeerConnectionClient::Connect(const std::string& server, int port, if (server_address_.IsUnresolved()) { state_ = RESOLVING; resolver_ = new talk_base::AsyncResolver(); - resolver_->SignalWorkDone.connect(this, - &PeerConnectionClient::OnResolveResult); - resolver_->set_address(server_address_); - resolver_->Start(); + resolver_->SignalDone.connect(this, &PeerConnectionClient::OnResolveResult); + resolver_->Start(server_address_); } else { DoConnect(); } } -void PeerConnectionClient::OnResolveResult(talk_base::SignalThread *t) { - if (resolver_->error() != 0) { +void PeerConnectionClient::OnResolveResult( + talk_base::AsyncResolverInterface* resolver) { + if (resolver_->GetError() != 0) { callback_->OnServerConnectionFailure(); resolver_->Destroy(false); resolver_ = NULL; diff --git a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/peer_connection_client.h b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/peer_connection_client.h index d31262b6767..43fee3456d3 100644 --- a/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/peer_connection_client.h +++ b/chromium/third_party/libjingle/source/talk/examples/peerconnection/client/peer_connection_client.h @@ -121,7 +121,7 @@ class PeerConnectionClient : public sigslot::has_slots<>, void OnClose(talk_base::AsyncSocket* socket, int err); - void OnResolveResult(talk_base::SignalThread *t); + void OnResolveResult(talk_base::AsyncResolverInterface* resolver); PeerConnectionClientObserver* callback_; talk_base::SocketAddress server_address_; diff --git a/chromium/third_party/libjingle/source/talk/libjingle.gyp b/chromium/third_party/libjingle/source/talk/libjingle.gyp index 5250eec1b8f..dced3d924be 100755 --- a/chromium/third_party/libjingle/source/talk/libjingle.gyp +++ b/chromium/third_party/libjingle/source/talk/libjingle.gyp @@ -105,14 +105,14 @@ # included here, or better yet, build a proper .jar in webrtc # and include it here. 'android_java_files': [ - '<(webrtc_modules_dir)/audio_device/android/java/src/org/webrtc/voiceengine/WebRTCAudioDevice.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/CaptureCapabilityAndroid.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', '<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViEAndroidGLES20.java', '<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViERenderer.java', '<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViESurfaceRenderer.java', + '<(webrtc_modules_dir)/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java', + '<(webrtc_modules_dir)/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java', ], }, 'action_name': 'create_jar', @@ -258,6 +258,7 @@ 'base/asynchttprequest.cc', 'base/asynchttprequest.h', 'base/asyncpacketsocket.h', + 'base/asyncresolverinterface.h', 'base/asyncsocket.cc', 'base/asyncsocket.h', 'base/asynctcpsocket.cc', @@ -842,6 +843,8 @@ # TODO(ronghuawu): Enable when SCTP is ready. # 'media/sctp/sctpdataengine.cc', # 'media/sctp/sctpdataengine.h', + 'media/sctp/sctputils.cc', + 'media/sctp/sctputils.h', 'media/webrtc/webrtccommon.h', 'media/webrtc/webrtcexport.h', 'media/webrtc/webrtcmediaengine.h', diff --git a/chromium/third_party/libjingle/source/talk/libjingle.scons b/chromium/third_party/libjingle/source/talk/libjingle.scons index cfc02491d1e..87b43f50ed0 100644 --- a/chromium/third_party/libjingle/source/talk/libjingle.scons +++ b/chromium/third_party/libjingle/source/talk/libjingle.scons @@ -290,6 +290,7 @@ talk.Library(env, name = "jingle", "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", @@ -676,6 +677,7 @@ talk.Unittest(env, name = "media", "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", diff --git a/chromium/third_party/libjingle/source/talk/libjingle_examples.gyp b/chromium/third_party/libjingle/source/talk/libjingle_examples.gyp index d4aa4a42712..1e2eeb0fb75 100755 --- a/chromium/third_party/libjingle/source/talk/libjingle_examples.gyp +++ b/chromium/third_party/libjingle/source/talk/libjingle_examples.gyp @@ -261,35 +261,6 @@ '-framework UIKit', ], }, - 'postbuilds': [ - { - # Ideally app signing would be a part of gyp. - # Delete if/when that comes to pass. - 'postbuild_name': 'Sign AppRTCDemo', - 'variables': { - 'variables': { - 'key_id%': '<!(security find-identity -p codesigning -v | grep "iPhone Developer" | awk \'{print $2}\')', - }, - 'key_id%': '<(key_id)', - # Total HACK to give a more informative message when multiple - # codesigning keys are present in the default keychain. Ideally - # we could pick more intelligently among the keys, but as a - # first cut just tell the developer to specify a key identity - # explicitly. - 'ensure_single_key': '<!(python -c "assert \'\\n\' not in \'\'\'<(key_id)\'\'\', \'key_id gyp variable needs to be set explicitly because there are multiple codesigning keys!\'")', - }, - 'conditions': [ - ['key_id==""', { - 'action': [ 'echo', 'Skipping signing' ], - }, { - 'action': [ - '/usr/bin/codesign', '-v', '--force', '--sign', '<(key_id)', - '${BUILT_PRODUCTS_DIR}/AppRTCDemo.app', - ], - }], - ], - }, - ], }, # target AppRTCDemo ], # targets }], # OS=="ios" @@ -325,6 +296,7 @@ '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/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', @@ -332,6 +304,9 @@ 'outputs': [ '<(PRODUCT_DIR)/AppRTCDemo-debug.apk', ], + 'variables': { + 'ant_log': '<(INTERMEDIATE_DIR)/ant.log', + }, 'action': [ 'bash', '-ec', 'rm -fr <(_outputs) examples/android/{bin,libs} && ' @@ -339,8 +314,10 @@ '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 && ' - 'ant debug && ' - 'cd - && ' + 'mkdir -p <(INTERMEDIATE_DIR) && ' + '{ ant -q -l <(ant_log) debug || ' + ' { 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 new file mode 100644 index 00000000000..36b50b5f3aa --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/libjingle_media_unittest.isolate @@ -0,0 +1,46 @@ +# +# 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. +# +{ + 'conditions': [ + ['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/', + ], + }, + }], + ], +} diff --git a/chromium/third_party/libjingle/source/talk/libjingle_p2p_unittest.isolate b/chromium/third_party/libjingle/source/talk/libjingle_p2p_unittest.isolate new file mode 100644 index 00000000000..b5ad4ff2d89 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/libjingle_p2p_unittest.isolate @@ -0,0 +1,45 @@ +# +# 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. +# +{ + 'conditions': [ + ['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/', + ], + }, + }], + ], +} diff --git a/chromium/third_party/libjingle/source/talk/libjingle_peerconnection_unittest.isolate b/chromium/third_party/libjingle/source/talk/libjingle_peerconnection_unittest.isolate new file mode 100644 index 00000000000..e7dd6878a64 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/libjingle_peerconnection_unittest.isolate @@ -0,0 +1,45 @@ +# +# 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. +# +{ + 'conditions': [ + ['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/', + ], + }, + }], + ], +} diff --git a/chromium/third_party/libjingle/source/talk/libjingle_sound_unittest.isolate b/chromium/third_party/libjingle/source/talk/libjingle_sound_unittest.isolate new file mode 100644 index 00000000000..7166337956c --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/libjingle_sound_unittest.isolate @@ -0,0 +1,45 @@ +# +# 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. +# +{ + 'conditions': [ + ['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/', + ], + }, + }], + ], +} diff --git a/chromium/third_party/libjingle/source/talk/libjingle_tests.gyp b/chromium/third_party/libjingle/source/talk/libjingle_tests.gyp index 546767d1fb5..7bfea590d7d 100755 --- a/chromium/third_party/libjingle/source/talk/libjingle_tests.gyp +++ b/chromium/third_party/libjingle/source/talk/libjingle_tests.gyp @@ -88,6 +88,7 @@ 'media/base/testutils.cc', 'media/base/testutils.h', 'media/devices/fakedevicemanager.h', + 'media/webrtc/dummyinstantiation.cc', 'media/webrtc/fakewebrtccommon.h', 'media/webrtc/fakewebrtcdeviceinfo.h', 'media/webrtc/fakewebrtcvcmfactory.h', @@ -375,7 +376,7 @@ ], # TODO(ronghuawu): Reenable below unit tests that require gmock. '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', @@ -383,6 +384,7 @@ # 'app/webrtc/mediastreamhandler_unittest.cc', 'app/webrtc/mediastreamsignaling_unittest.cc', 'app/webrtc/peerconnection_unittest.cc', + 'app/webrtc/peerconnectionendtoend_unittest.cc', 'app/webrtc/peerconnectionfactory_unittest.cc', 'app/webrtc/peerconnectioninterface_unittest.cc', # 'app/webrtc/peerconnectionproxy_unittest.cc', @@ -391,11 +393,14 @@ 'app/webrtc/test/fakeaudiocapturemodule.h', 'app/webrtc/test/fakeaudiocapturemodule_unittest.cc', 'app/webrtc/test/fakeconstraints.h', + 'app/webrtc/test/fakedatachannelprovider.h', 'app/webrtc/test/fakedtlsidentityservice.h', 'app/webrtc/test/fakemediastreamsignaling.h', 'app/webrtc/test/fakeperiodicvideocapturer.h', 'app/webrtc/test/fakevideotrackrenderer.h', 'app/webrtc/test/mockpeerconnectionobservers.h', + 'app/webrtc/test/peerconnectiontestwrapper.h', + 'app/webrtc/test/peerconnectiontestwrapper.cc', 'app/webrtc/test/testsdpstrings.h', 'app/webrtc/videosource_unittest.cc', 'app/webrtc/videotrack_unittest.cc', @@ -517,5 +522,79 @@ }, # target libjingle_peerconnection_objc_test ], }], + ['test_isolation_mode != "noop"', { + 'targets': [ + { + 'target_name': 'libjingle_media_unittest_run', + 'type': 'none', + 'dependencies': [ + 'libjingle_media_unittest', + ], + 'includes': [ + 'build/isolate.gypi', + 'libjingle_media_unittest.isolate', + ], + 'sources': [ + 'libjingle_media_unittest.isolate', + ], + }, + { + 'target_name': 'libjingle_p2p_unittest_run', + 'type': 'none', + 'dependencies': [ + 'libjingle_p2p_unittest', + ], + 'includes': [ + 'build/isolate.gypi', + 'libjingle_p2p_unittest.isolate', + ], + 'sources': [ + 'libjingle_p2p_unittest.isolate', + ], + }, + { + 'target_name': 'libjingle_peerconnection_unittest_run', + 'type': 'none', + 'dependencies': [ + 'libjingle_peerconnection_unittest', + ], + 'includes': [ + 'build/isolate.gypi', + 'libjingle_peerconnection_unittest.isolate', + ], + 'sources': [ + 'libjingle_peerconnection_unittest.isolate', + ], + }, + { + 'target_name': 'libjingle_sound_unittest_run', + 'type': 'none', + 'dependencies': [ + 'libjingle_sound_unittest', + ], + 'includes': [ + 'build/isolate.gypi', + 'libjingle_sound_unittest.isolate', + ], + 'sources': [ + 'libjingle_sound_unittest.isolate', + ], + }, + { + 'target_name': 'libjingle_unittest_run', + 'type': 'none', + 'dependencies': [ + 'libjingle_unittest', + ], + 'includes': [ + 'build/isolate.gypi', + 'libjingle_unittest.isolate', + ], + 'sources': [ + 'libjingle_unittest.isolate', + ], + }, + ], + }], ], } diff --git a/chromium/third_party/libjingle/source/talk/libjingle_unittest.isolate b/chromium/third_party/libjingle/source/talk/libjingle_unittest.isolate new file mode 100644 index 00000000000..e678af013e4 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/libjingle_unittest.isolate @@ -0,0 +1,45 @@ +# +# 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. +# +{ + 'conditions': [ + ['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/', + ], + }, + }], + ], +} diff --git a/chromium/third_party/libjingle/source/talk/media/base/capturemanager.h b/chromium/third_party/libjingle/source/talk/media/base/capturemanager.h index 9c443950aff..5226e7b470d 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/capturemanager.h +++ b/chromium/third_party/libjingle/source/talk/media/base/capturemanager.h @@ -67,19 +67,19 @@ class CaptureManager : public sigslot::has_slots<> { CaptureManager() {} virtual ~CaptureManager(); - bool StartVideoCapture(VideoCapturer* video_capturer, - const VideoFormat& desired_format); - bool StopVideoCapture(VideoCapturer* video_capturer, - const VideoFormat& format); + virtual bool StartVideoCapture(VideoCapturer* video_capturer, + const VideoFormat& desired_format); + virtual bool StopVideoCapture(VideoCapturer* video_capturer, + const VideoFormat& format); // Possibly restarts the capturer. If |options| is set to kRequestRestart, // the CaptureManager chooses whether this request can be handled with the // current state or if a restart is actually needed. If |options| is set to // kForceRestart, the capturer is restarted. - bool RestartVideoCapture(VideoCapturer* video_capturer, - const VideoFormat& previous_format, - const VideoFormat& desired_format, - RestartOptions options); + virtual bool RestartVideoCapture(VideoCapturer* video_capturer, + const VideoFormat& previous_format, + const VideoFormat& desired_format, + RestartOptions options); virtual bool AddVideoRenderer(VideoCapturer* video_capturer, VideoRenderer* video_renderer); 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 5529c2abcfa..9162ce4fd12 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/constants.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/constants.cc @@ -39,30 +39,30 @@ const float kHighSystemCpuThreshold = 0.85f; const float kLowSystemCpuThreshold = 0.65f; const float kProcessCpuThreshold = 0.10f; -const char* kRtxCodecName = "rtx"; +const char kRtxCodecName[] = "rtx"; // RTP payload type is in the 0-127 range. Use 128 to indicate "all" payload // types. const int kWildcardPayloadType = -1; -const char* kCodecParamAssociatedPayloadType = "apt"; +const char kCodecParamAssociatedPayloadType[] = "apt"; -const char* kOpusCodecName = "opus"; +const char kOpusCodecName[] = "opus"; // draft-spittka-payload-rtp-opus-03.txt -const char* kCodecParamPTime = "ptime"; -const char* kCodecParamMaxPTime = "maxptime"; -const char* kCodecParamMinPTime = "minptime"; -const char* kCodecParamSPropStereo = "sprop-stereo"; -const char* kCodecParamStereo = "stereo"; -const char* kCodecParamUseInbandFec = "useinbandfec"; -const char* kCodecParamMaxAverageBitrate = "maxaveragebitrate"; +const char kCodecParamPTime[] = "ptime"; +const char kCodecParamMaxPTime[] = "maxptime"; +const char kCodecParamMinPTime[] = "minptime"; +const char kCodecParamSPropStereo[] = "sprop-stereo"; +const char kCodecParamStereo[] = "stereo"; +const char kCodecParamUseInbandFec[] = "useinbandfec"; +const char kCodecParamMaxAverageBitrate[] = "maxaveragebitrate"; -const char* kCodecParamSctpProtocol = "protocol"; -const char* kCodecParamSctpStreams = "streams"; +const char kCodecParamSctpProtocol[] = "protocol"; +const char kCodecParamSctpStreams[] = "streams"; -const char* kParamValueTrue = "1"; -const char* kParamValueEmpty = ""; +const char kParamValueTrue[] = "1"; +const char kParamValueEmpty[] = ""; const int kOpusDefaultMaxPTime = 120; const int kOpusDefaultPTime = 20; @@ -77,14 +77,15 @@ const int kPreferredSPropStereo = 0; const int kPreferredStereo = 0; const int kPreferredUseInbandFec = 0; -const char* kRtcpFbParamNack = "nack"; -const char* kRtcpFbParamRemb = "goog-remb"; +const char kRtcpFbParamNack[] = "nack"; +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* kCodecParamMaxQuantization = "x-google-max-quantization"; +const char kRtcpFbParamCcm[] = "ccm"; +const char kRtcpFbCcmParamFir[] = "fir"; +const char kCodecParamMaxBitrate[] = "x-google-max-bitrate"; +const char kCodecParamMinBitrate[] = "x-google-min-bitrate"; +const char kCodecParamMaxQuantization[] = "x-google-max-quantization"; +const char kCodecParamPort[] = "x-google-port"; const int kGoogleRtpDataCodecId = 101; const char kGoogleRtpDataCodecName[] = "google-data"; 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 afcfb13ffc1..b80c0fc106c 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/constants.h +++ b/chromium/third_party/libjingle/source/talk/media/base/constants.h @@ -43,30 +43,30 @@ extern const float kHighSystemCpuThreshold; extern const float kLowSystemCpuThreshold; extern const float kProcessCpuThreshold; -extern const char* kRtxCodecName; +extern const char kRtxCodecName[]; // Codec parameters extern const int kWildcardPayloadType; -extern const char* kCodecParamAssociatedPayloadType; +extern const char kCodecParamAssociatedPayloadType[]; -extern const char* kOpusCodecName; +extern const char kOpusCodecName[]; // Attribute parameters -extern const char* kCodecParamPTime; -extern const char* kCodecParamMaxPTime; +extern const char kCodecParamPTime[]; +extern const char kCodecParamMaxPTime[]; // fmtp parameters -extern const char* kCodecParamMinPTime; -extern const char* kCodecParamSPropStereo; -extern const char* kCodecParamStereo; -extern const char* kCodecParamUseInbandFec; -extern const char* kCodecParamMaxAverageBitrate; -extern const char* kCodecParamSctpProtocol; -extern const char* kCodecParamSctpStreams; - -extern const char* kParamValueTrue; +extern const char kCodecParamMinPTime[]; +extern const char kCodecParamSPropStereo[]; +extern const char kCodecParamStereo[]; +extern const char kCodecParamUseInbandFec[]; +extern const char kCodecParamMaxAverageBitrate[]; +extern const char kCodecParamSctpProtocol[]; +extern const char kCodecParamSctpStreams[]; + +extern const char kParamValueTrue[]; // Parameters are stored as parameter/value pairs. For parameters who do not // have a value, |kParamValueEmpty| should be used as value. -extern const char* kParamValueEmpty; +extern const char kParamValueEmpty[]; // opus parameters. // Default value for maxptime according to @@ -88,17 +88,18 @@ extern const int kPreferredStereo; extern const int kPreferredUseInbandFec; // rtcp-fb messages according to RFC 4585 -extern const char* kRtcpFbParamNack; +extern const char kRtcpFbParamNack[]; // rtcp-fb messages according to // http://tools.ietf.org/html/draft-alvestrand-rmcat-remb-00 -extern const char* kRtcpFbParamRemb; +extern const char kRtcpFbParamRemb[]; // ccm submessages according to RFC 5104 -extern const char* kRtcpFbParamCcm; -extern const char* kRtcpFbCcmParamFir; +extern const char kRtcpFbParamCcm[]; +extern const char kRtcpFbCcmParamFir[]; // Google specific parameters -extern const char* kCodecParamMaxBitrate; -extern const char* kCodecParamMinBitrate; -extern const char* kCodecParamMaxQuantization; +extern const char kCodecParamMaxBitrate[]; +extern const char kCodecParamMinBitrate[]; +extern const char kCodecParamMaxQuantization[]; +extern const char kCodecParamPort[]; // We put the data codec names here so callers of // DataEngine::CreateChannel don't have to import rtpdataengine.h or diff --git a/chromium/third_party/libjingle/source/talk/media/base/cpuid.cc b/chromium/third_party/libjingle/source/talk/media/base/cpuid.cc index 9fd78658899..bd87d2e3cb0 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/cpuid.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/cpuid.cc @@ -51,8 +51,8 @@ void CpuInfo::MaskCpuFlagsForTest(int enable_flags) { bool IsCoreIOrBetter() { #if !defined(DISABLE_YUV) && (defined(__i386__) || defined(__x86_64__) || \ defined(_M_IX86) || defined(_M_X64)) - int cpu_info[4]; - libyuv::CpuId(cpu_info, 0); // Function 0: Vendor ID + uint32 cpu_info[4]; + libyuv::CpuId(0, 0, &cpu_info[0]); // Function 0: Vendor ID if (cpu_info[1] == 0x756e6547 && cpu_info[3] == 0x49656e69 && cpu_info[2] == 0x6c65746e) { // GenuineIntel // Detect CPU Family and Model @@ -62,7 +62,7 @@ bool IsCoreIOrBetter() { // 13:12 - Processor Type // 19:16 - Extended Model // 27:20 - Extended Family - libyuv::CpuId(cpu_info, 1); // Function 1: Family and Model + libyuv::CpuId(1, 0, &cpu_info[0]); // Function 1: Family and Model int family = ((cpu_info[0] >> 8) & 0x0f) | ((cpu_info[0] >> 16) & 0xff0); int model = ((cpu_info[0] >> 4) & 0x0f) | ((cpu_info[0] >> 12) & 0xf0); // CpuFamily | CpuModel | Name 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 7ef0c9b1ab3..1a4e8aba897 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/fakemediaengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/fakemediaengine.h @@ -191,10 +191,12 @@ template <class Base> class RtpHelper : public Base { return true; } void set_playout(bool playout) { playout_ = playout; } - virtual void OnPacketReceived(talk_base::Buffer* packet) { + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) { rtp_packets_.push_back(std::string(packet->data(), packet->length())); } - virtual void OnRtcpReceived(talk_base::Buffer* packet) { + virtual void OnRtcpReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) { rtcp_packets_.push_back(std::string(packet->data(), packet->length())); } virtual void OnReadyToSend(bool ready) { @@ -678,18 +680,11 @@ class FakeBaseEngine { public: FakeBaseEngine() : loglevel_(-1), - options_(0), options_changed_(false), fail_create_channel_(false) {} bool Init(talk_base::Thread* worker_thread) { return true; } void Terminate() {} - bool SetOptions(int options) { - options_ = options; - options_changed_ = true; - return true; - } - void SetLogging(int level, const char* filter) { loglevel_ = level; logfilter_ = filter; @@ -704,7 +699,6 @@ class FakeBaseEngine { protected: int loglevel_; std::string logfilter_; - int options_; // Flag used by optionsmessagehandler_unittest for checking whether any // relevant setting has been updated. // TODO(thaloun): Replace with explicit checks of before & after values. @@ -725,6 +719,17 @@ class FakeVoiceEngine : public FakeBaseEngine { codecs_.push_back(AudioCodec(101, "fake_audio_codec", 0, 0, 1, 0)); } int GetCapabilities() { return AUDIO_SEND | AUDIO_RECV; } + AudioOptions GetAudioOptions() const { + return options_; + } + AudioOptions GetOptions() const { + return options_; + } + bool SetOptions(const AudioOptions& options) { + options_ = options; + options_changed_ = true; + return true; + } VoiceMediaChannel* CreateChannel() { if (fail_create_channel_) { @@ -808,6 +813,7 @@ class FakeVoiceEngine : public FakeBaseEngine { std::string out_device_; VoiceProcessor* rx_processor_; VoiceProcessor* tx_processor_; + AudioOptions options_; friend class FakeMediaEngine; }; @@ -819,11 +825,23 @@ class FakeVideoEngine : public FakeBaseEngine { // sanity checks against that. codecs_.push_back(VideoCodec(0, "fake_video_codec", 0, 0, 0, 0)); } + bool GetOptions(VideoOptions* options) const { + *options = options_; + return true; + } + bool SetOptions(const VideoOptions& options) { + options_ = options; + options_changed_ = true; + return true; + } int GetCapabilities() { return VIDEO_SEND | VIDEO_RECV; } bool SetDefaultEncoderConfig(const VideoEncoderConfig& config) { default_encoder_config_ = config; return true; } + VideoEncoderConfig GetDefaultEncoderConfig() const { + return default_encoder_config_; + } const VideoEncoderConfig& default_encoder_config() const { return default_encoder_config_; } @@ -883,6 +901,7 @@ class FakeVideoEngine : public FakeBaseEngine { VideoRenderer* renderer_; bool capture_; VideoProcessor* processor_; + VideoOptions options_; friend class FakeMediaEngine; }; @@ -912,7 +931,7 @@ class FakeMediaEngine : return video_.GetChannel(index); } - int audio_options() const { return voice_.options_; } + AudioOptions audio_options() const { return voice_.options_; } int audio_delay_offset() const { return voice_.delay_offset_; } int output_volume() const { return voice_.output_volume_; } const VideoEncoderConfig& default_video_encoder_config() const { diff --git a/chromium/third_party/libjingle/source/talk/media/base/fakenetworkinterface.h b/chromium/third_party/libjingle/source/talk/media/base/fakenetworkinterface.h index d0f277e8f42..eb0175b7f82 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/fakenetworkinterface.h +++ b/chromium/third_party/libjingle/source/talk/media/base/fakenetworkinterface.h @@ -34,6 +34,7 @@ #include "talk/base/buffer.h" #include "talk/base/byteorder.h" #include "talk/base/criticalsection.h" +#include "talk/base/dscp.h" #include "talk/base/messagehandler.h" #include "talk/base/messagequeue.h" #include "talk/base/thread.h" @@ -51,7 +52,8 @@ class FakeNetworkInterface : public MediaChannel::NetworkInterface, dest_(NULL), conf_(false), sendbuf_size_(-1), - recvbuf_size_(-1) { + recvbuf_size_(-1), + dscp_(talk_base::DSCP_NO_CHANGE) { } void SetDestination(MediaChannel* dest) { dest_ = dest; } @@ -128,6 +130,7 @@ class FakeNetworkInterface : public MediaChannel::NetworkInterface, int sendbuf_size() const { return sendbuf_size_; } int recvbuf_size() const { return recvbuf_size_; } + talk_base::DiffServCodePoint dscp() const { return dscp_; } protected: virtual bool SendPacket(talk_base::Buffer* packet, @@ -182,6 +185,8 @@ class FakeNetworkInterface : public MediaChannel::NetworkInterface, sendbuf_size_ = option; } else if (opt == talk_base::Socket::OPT_RCVBUF) { recvbuf_size_ = option; + } else if (opt == talk_base::Socket::OPT_DSCP) { + dscp_ = static_cast<talk_base::DiffServCodePoint>(option); } return 0; } @@ -196,9 +201,11 @@ class FakeNetworkInterface : public MediaChannel::NetworkInterface, msg->pdata); if (dest_) { if (msg->message_id == ST_RTP) { - dest_->OnPacketReceived(&msg_data->data()); + dest_->OnPacketReceived(&msg_data->data(), + talk_base::CreatePacketTime(0)); } else { - dest_->OnRtcpReceived(&msg_data->data()); + dest_->OnRtcpReceived(&msg_data->data(), + talk_base::CreatePacketTime(0)); } } delete msg_data; @@ -244,6 +251,7 @@ class FakeNetworkInterface : public MediaChannel::NetworkInterface, std::vector<talk_base::Buffer> rtcp_packets_; int sendbuf_size_; int recvbuf_size_; + talk_base::DiffServCodePoint dscp_; }; } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/base/fakevideocapturer.h b/chromium/third_party/libjingle/source/talk/media/base/fakevideocapturer.h index 5a33265b3ac..8dc69c34546 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/fakevideocapturer.h +++ b/chromium/third_party/libjingle/source/talk/media/base/fakevideocapturer.h @@ -101,7 +101,7 @@ class FakeVideoCapturer : public cricket::VideoCapturer { frame.time_stamp = initial_unix_timestamp_ + next_timestamp_; next_timestamp_ += 33333333; // 30 fps - talk_base::scoped_array<char> data(new char[size]); + talk_base::scoped_ptr<char[]> data(new char[size]); frame.data = data.get(); // Copy something non-zero into the buffer so Validate wont complain that // the frame is all duplicate. 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 1a3547c0ce7..dfec607d286 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.cc @@ -83,7 +83,8 @@ VoiceMediaChannel* FileMediaEngine::CreateChannel() { } } - return new FileVoiceChannel(input_file_stream, output_file_stream); + return new FileVoiceChannel(input_file_stream, output_file_stream, + rtp_sender_thread_); } VideoMediaChannel* FileMediaEngine::CreateVideoChannel( @@ -113,18 +114,20 @@ VideoMediaChannel* FileMediaEngine::CreateVideoChannel( } } - return new FileVideoChannel(input_file_stream, output_file_stream); + return new FileVideoChannel(input_file_stream, output_file_stream, + rtp_sender_thread_); } /////////////////////////////////////////////////////////////////////////// // Definition of RtpSenderReceiver. /////////////////////////////////////////////////////////////////////////// -class RtpSenderReceiver - : public talk_base::Thread, public talk_base::MessageHandler { +class RtpSenderReceiver : public talk_base::MessageHandler { public: RtpSenderReceiver(MediaChannel* channel, talk_base::StreamInterface* input_file_stream, - talk_base::StreamInterface* output_file_stream); + talk_base::StreamInterface* output_file_stream, + talk_base::Thread* sender_thread); + virtual ~RtpSenderReceiver(); // Called by media channel. Context: media channel thread. bool SetSend(bool send); @@ -148,6 +151,8 @@ class RtpSenderReceiver talk_base::scoped_ptr<talk_base::StreamInterface> output_stream_; talk_base::scoped_ptr<RtpDumpLoopReader> rtp_dump_reader_; talk_base::scoped_ptr<RtpDumpWriter> rtp_dump_writer_; + talk_base::Thread* sender_thread_; + bool own_sender_thread_; // RTP dump packet read from the input stream. RtpDumpPacket rtp_dump_packet_; uint32 start_send_time_; @@ -164,30 +169,48 @@ class RtpSenderReceiver RtpSenderReceiver::RtpSenderReceiver( MediaChannel* channel, talk_base::StreamInterface* input_file_stream, - talk_base::StreamInterface* output_file_stream) + talk_base::StreamInterface* output_file_stream, + talk_base::Thread* sender_thread) : media_channel_(channel), + input_stream_(input_file_stream), + output_stream_(output_file_stream), sending_(false), first_packet_(true) { - input_stream_.reset(input_file_stream); + if (sender_thread == NULL) { + sender_thread_ = new talk_base::Thread(); + own_sender_thread_ = true; + } else { + sender_thread_ = sender_thread; + own_sender_thread_ = false; + } + if (input_stream_) { rtp_dump_reader_.reset(new RtpDumpLoopReader(input_stream_.get())); // Start the sender thread, which reads rtp dump records, waits based on // the record timestamps, and sends the RTP packets to the network. - Thread::Start(); + if (own_sender_thread_) { + sender_thread_->Start(); + } } // Create a rtp dump writer for the output RTP dump stream. - output_stream_.reset(output_file_stream); if (output_stream_) { rtp_dump_writer_.reset(new RtpDumpWriter(output_stream_.get())); } } +RtpSenderReceiver::~RtpSenderReceiver() { + if (own_sender_thread_) { + sender_thread_->Stop(); + delete sender_thread_; + } +} + bool RtpSenderReceiver::SetSend(bool send) { bool was_sending = sending_; sending_ = send; if (!was_sending && sending_) { - PostDelayed(0, this); // Wake up the send thread. + sender_thread_->PostDelayed(0, this); // Wake up the send thread. start_send_time_ = talk_base::Time(); } return true; @@ -211,7 +234,6 @@ void RtpSenderReceiver::OnMessage(talk_base::Message* pmsg) { // to sleep until SetSend(true) wakes it up. return; } - if (!first_packet_) { // Send the previously read packet. SendRtpPacket(&rtp_dump_packet_.data[0], rtp_dump_packet_.data.size()); @@ -221,9 +243,9 @@ void RtpSenderReceiver::OnMessage(talk_base::Message* pmsg) { int wait = talk_base::TimeUntil( start_send_time_ + rtp_dump_packet_.elapsed_time); wait = talk_base::_max(0, wait); - PostDelayed(wait, this); + sender_thread_->PostDelayed(wait, this); } else { - Quit(); + sender_thread_->Quit(); } } @@ -257,10 +279,12 @@ bool RtpSenderReceiver::SendRtpPacket(const void* data, size_t len) { /////////////////////////////////////////////////////////////////////////// FileVoiceChannel::FileVoiceChannel( talk_base::StreamInterface* input_file_stream, - talk_base::StreamInterface* output_file_stream) + talk_base::StreamInterface* output_file_stream, + talk_base::Thread* rtp_sender_thread) : send_ssrc_(0), rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream, - output_file_stream)) {} + output_file_stream, + rtp_sender_thread)) {} FileVoiceChannel::~FileVoiceChannel() {} @@ -291,7 +315,8 @@ bool FileVoiceChannel::RemoveSendStream(uint32 ssrc) { return true; } -void FileVoiceChannel::OnPacketReceived(talk_base::Buffer* packet) { +void FileVoiceChannel::OnPacketReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { rtp_sender_receiver_->OnPacketReceived(packet); } @@ -300,10 +325,12 @@ void FileVoiceChannel::OnPacketReceived(talk_base::Buffer* packet) { /////////////////////////////////////////////////////////////////////////// FileVideoChannel::FileVideoChannel( talk_base::StreamInterface* input_file_stream, - talk_base::StreamInterface* output_file_stream) + talk_base::StreamInterface* output_file_stream, + talk_base::Thread* rtp_sender_thread) : send_ssrc_(0), rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream, - output_file_stream)) {} + output_file_stream, + rtp_sender_thread)) {} FileVideoChannel::~FileVideoChannel() {} @@ -334,7 +361,8 @@ bool FileVideoChannel::RemoveSendStream(uint32 ssrc) { return true; } -void FileVideoChannel::OnPacketReceived(talk_base::Buffer* packet) { +void FileVideoChannel::OnPacketReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { rtp_sender_receiver_->OnPacketReceived(packet); } 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 2c8525d9630..dfdb037ab38 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/filemediaengine.h @@ -50,7 +50,7 @@ namespace cricket { // only the RTP dump packets. TODO(whyuan): Enable RTCP packets. class FileMediaEngine : public MediaEngineInterface { public: - FileMediaEngine() {} + FileMediaEngine() : rtp_sender_thread_(NULL) {} virtual ~FileMediaEngine() {} // Set the file name of the input or output RTP dump for voice or video. @@ -86,12 +86,16 @@ class FileMediaEngine : public MediaEngineInterface { virtual VoiceMediaChannel* CreateChannel(); virtual VideoMediaChannel* CreateVideoChannel(VoiceMediaChannel* voice_ch); virtual SoundclipMedia* CreateSoundclip() { return NULL; } - virtual bool SetAudioOptions(int options) { return true; } - virtual bool SetVideoOptions(int options) { return true; } + virtual AudioOptions GetAudioOptions() const { return AudioOptions(); } + virtual bool SetAudioOptions(const AudioOptions& options) { return true; } + virtual bool SetVideoOptions(const VideoOptions& options) { return true; } virtual bool SetAudioDelayOffset(int offset) { return true; } virtual bool SetDefaultVideoEncoderConfig(const VideoEncoderConfig& config) { return true; } + virtual VideoEncoderConfig GetDefaultVideoEncoderConfig() const { + return VideoEncoderConfig(); + } virtual bool SetSoundDevices(const Device* in_dev, const Device* out_dev) { return true; } @@ -155,6 +159,10 @@ class FileMediaEngine : public MediaEngineInterface { return signal_state_change_; } + void set_rtp_sender_thread(talk_base::Thread* thread) { + rtp_sender_thread_ = thread; + } + private: std::string voice_input_filename_; std::string voice_output_filename_; @@ -166,6 +174,7 @@ class FileMediaEngine : public MediaEngineInterface { std::vector<RtpHeaderExtension> video_rtp_header_extensions_; sigslot::repeater2<VideoCapturer*, CaptureState> signal_state_change_; + talk_base::Thread* rtp_sender_thread_; DISALLOW_COPY_AND_ASSIGN(FileMediaEngine); }; @@ -175,7 +184,8 @@ class RtpSenderReceiver; // Forward declaration. Defined in the .cc file. class FileVoiceChannel : public VoiceMediaChannel { public: FileVoiceChannel(talk_base::StreamInterface* input_file_stream, - talk_base::StreamInterface* output_file_stream); + talk_base::StreamInterface* output_file_stream, + talk_base::Thread* rtp_sender_thread); virtual ~FileVoiceChannel(); // Implement pure virtual methods of VoiceMediaChannel. @@ -222,8 +232,10 @@ class FileVoiceChannel : public VoiceMediaChannel { virtual bool GetStats(VoiceMediaInfo* info) { return true; } // Implement pure virtual methods of MediaChannel. - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet) {} + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); + virtual void OnRtcpReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) {} virtual void OnReadyToSend(bool ready) {} virtual bool AddSendStream(const StreamParams& sp); virtual bool RemoveSendStream(uint32 ssrc); @@ -251,7 +263,8 @@ class FileVoiceChannel : public VoiceMediaChannel { class FileVideoChannel : public VideoMediaChannel { public: FileVideoChannel(talk_base::StreamInterface* input_file_stream, - talk_base::StreamInterface* output_file_stream); + talk_base::StreamInterface* output_file_stream, + talk_base::Thread* rtp_sender_thread); virtual ~FileVideoChannel(); // Implement pure virtual methods of VideoMediaChannel. @@ -287,8 +300,10 @@ class FileVideoChannel : public VideoMediaChannel { virtual bool RequestIntraFrame() { return false; } // Implement pure virtual methods of MediaChannel. - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet) {} + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); + virtual void OnRtcpReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) {} virtual void OnReadyToSend(bool ready) {} virtual bool AddSendStream(const StreamParams& sp); virtual bool RemoveSendStream(uint32 ssrc); diff --git a/chromium/third_party/libjingle/source/talk/media/base/filemediaengine_unittest.cc b/chromium/third_party/libjingle/source/talk/media/base/filemediaengine_unittest.cc index e4d72bbe610..b1b021d090d 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/filemediaengine_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/filemediaengine_unittest.cc @@ -63,7 +63,7 @@ class FileNetworkInterface : public MediaChannel::NetworkInterface { if (!packet) return false; if (media_channel_) { - media_channel_->OnPacketReceived(packet); + media_channel_->OnPacketReceived(packet, talk_base::PacketTime()); } if (dump_writer_.get() && talk_base::SR_SUCCESS != dump_writer_->WriteRtpPacket( @@ -136,6 +136,7 @@ class FileMediaEngineTest : public testing::Test { engine_->set_voice_output_filename(voice_out); engine_->set_video_input_filename(video_in); engine_->set_video_output_filename(video_out); + engine_->set_rtp_sender_thread(talk_base::Thread::Current()); voice_channel_.reset(engine_->CreateChannel()); video_channel_.reset(engine_->CreateVideoChannel(NULL)); @@ -220,8 +221,10 @@ TEST_F(FileMediaEngineTest, TestDefaultImplementation) { EXPECT_TRUE(NULL == voice_channel_.get()); EXPECT_TRUE(NULL == video_channel_.get()); EXPECT_TRUE(NULL == engine_->CreateSoundclip()); - EXPECT_TRUE(engine_->SetAudioOptions(0)); - EXPECT_TRUE(engine_->SetVideoOptions(0)); + cricket::AudioOptions audio_options; + EXPECT_TRUE(engine_->SetAudioOptions(audio_options)); + cricket::VideoOptions video_options; + EXPECT_TRUE(engine_->SetVideoOptions(video_options)); VideoEncoderConfig video_encoder_config; EXPECT_TRUE(engine_->SetDefaultVideoEncoderConfig(video_encoder_config)); EXPECT_TRUE(engine_->SetSoundDevices(NULL, NULL)); 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 a405f8d28ee..6863311f2dd 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.cc @@ -276,19 +276,21 @@ bool HybridVideoMediaChannel::GetStats(VideoMediaInfo* info) { active_channel_->GetStats(info); } -void HybridVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { +void HybridVideoMediaChannel::OnPacketReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { // Eat packets until we have an active channel; if (active_channel_) { - active_channel_->OnPacketReceived(packet); + active_channel_->OnPacketReceived(packet, packet_time); } else { LOG(LS_INFO) << "HybridVideoChannel: Eating early RTP packet"; } } -void HybridVideoMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) { +void HybridVideoMediaChannel::OnRtcpReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { // Eat packets until we have an active channel; if (active_channel_) { - active_channel_->OnRtcpReceived(packet); + active_channel_->OnRtcpReceived(packet, packet_time); } else { LOG(LS_INFO) << "HybridVideoChannel: Eating early RTCP packet"; } 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 ab638c9e452..a49a1aa2c88 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/hybridvideoengine.h @@ -87,8 +87,10 @@ class HybridVideoMediaChannel : public VideoMediaChannel { virtual bool GetStats(VideoMediaInfo* info); - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet); + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); + virtual void OnRtcpReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); virtual void OnReadyToSend(bool ready); virtual void UpdateAspectRatio(int ratio_w, int ratio_h); @@ -183,8 +185,8 @@ class HybridVideoEngine : public HybridVideoEngineInterface { channel1.release(), channel2.release()); } - bool SetOptions(int o) { - return video1_.SetOptions(o) && video2_.SetOptions(o); + bool SetOptions(const VideoOptions& options) { + return video1_.SetOptions(options) && video2_.SetOptions(options); } bool SetDefaultEncoderConfig(const VideoEncoderConfig& config) { VideoEncoderConfig conf = config; @@ -204,6 +206,20 @@ class HybridVideoEngine : public HybridVideoEngineInterface { } return true; } + VideoEncoderConfig GetDefaultEncoderConfig() const { + // This looks pretty strange, but, in practice, it'll do sane things if + // GetDefaultEncoderConfig is only called after SetDefaultEncoderConfig, + // since both engines should be essentially equivalent at that point. If it + // hasn't been called, though, we'll use the first meaningful encoder + // config, or the config from the second video engine if neither are + // meaningful. + VideoEncoderConfig config = video1_.GetDefaultEncoderConfig(); + if (config.max_codec.width != 0) { + return config; + } else { + return video2_.GetDefaultEncoderConfig(); + } + } const std::vector<VideoCodec>& codecs() const { return codecs_; } 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 7431bc1001f..94ae03f8d80 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/mediachannel.h +++ b/chromium/third_party/libjingle/source/talk/media/base/mediachannel.h @@ -50,6 +50,10 @@ class RateLimiter; class Timing; } +namespace webrtc { +struct DataChannelInit; +} + namespace cricket { class AudioRenderer; @@ -158,29 +162,55 @@ struct AudioOptions { void SetAll(const AudioOptions& change) { echo_cancellation.SetFrom(change.echo_cancellation); auto_gain_control.SetFrom(change.auto_gain_control); + rx_auto_gain_control.SetFrom(change.rx_auto_gain_control); noise_suppression.SetFrom(change.noise_suppression); highpass_filter.SetFrom(change.highpass_filter); stereo_swapping.SetFrom(change.stereo_swapping); typing_detection.SetFrom(change.typing_detection); + aecm_generate_comfort_noise.SetFrom(change.aecm_generate_comfort_noise); conference_mode.SetFrom(change.conference_mode); adjust_agc_delta.SetFrom(change.adjust_agc_delta); experimental_agc.SetFrom(change.experimental_agc); experimental_aec.SetFrom(change.experimental_aec); 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); + tx_agc_limiter.SetFrom(change.tx_agc_limiter); + rx_agc_target_dbov.SetFrom(change.rx_agc_target_dbov); + rx_agc_digital_compression_gain.SetFrom( + change.rx_agc_digital_compression_gain); + rx_agc_limiter.SetFrom(change.rx_agc_limiter); + recording_sample_rate.SetFrom(change.recording_sample_rate); + playout_sample_rate.SetFrom(change.playout_sample_rate); + dscp.SetFrom(change.dscp); } bool operator==(const AudioOptions& o) const { return echo_cancellation == o.echo_cancellation && auto_gain_control == o.auto_gain_control && + rx_auto_gain_control == o.rx_auto_gain_control && noise_suppression == o.noise_suppression && highpass_filter == o.highpass_filter && stereo_swapping == o.stereo_swapping && typing_detection == o.typing_detection && + aecm_generate_comfort_noise == o.aecm_generate_comfort_noise && conference_mode == o.conference_mode && experimental_agc == o.experimental_agc && experimental_aec == o.experimental_aec && adjust_agc_delta == o.adjust_agc_delta && - aec_dump == o.aec_dump; + 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 && + rx_agc_target_dbov == o.rx_agc_target_dbov && + rx_agc_digital_compression_gain == o.rx_agc_digital_compression_gain && + rx_agc_limiter == o.rx_agc_limiter && + recording_sample_rate == o.recording_sample_rate && + playout_sample_rate == o.playout_sample_rate && + dscp == o.dscp; } std::string ToString() const { @@ -188,15 +218,29 @@ struct AudioOptions { ost << "AudioOptions {"; ost << ToStringIfSet("aec", echo_cancellation); ost << ToStringIfSet("agc", auto_gain_control); + ost << ToStringIfSet("rx_agc", rx_auto_gain_control); ost << ToStringIfSet("ns", noise_suppression); ost << ToStringIfSet("hf", highpass_filter); ost << ToStringIfSet("swap", stereo_swapping); ost << ToStringIfSet("typing", typing_detection); + ost << ToStringIfSet("comfort_noise", aecm_generate_comfort_noise); ost << ToStringIfSet("conference", conference_mode); ost << ToStringIfSet("agc_delta", adjust_agc_delta); ost << ToStringIfSet("experimental_agc", experimental_agc); ost << ToStringIfSet("experimental_aec", experimental_aec); 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); + ost << ToStringIfSet("tx_agc_limiter", tx_agc_limiter); + ost << ToStringIfSet("rx_agc_target_dbov", rx_agc_target_dbov); + ost << ToStringIfSet("rx_agc_digital_compression_gain", + rx_agc_digital_compression_gain); + ost << ToStringIfSet("rx_agc_limiter", rx_agc_limiter); + ost << ToStringIfSet("recording_sample_rate", recording_sample_rate); + ost << ToStringIfSet("playout_sample_rate", playout_sample_rate); + ost << ToStringIfSet("dscp", dscp); ost << "}"; return ost.str(); } @@ -206,6 +250,8 @@ struct AudioOptions { Settable<bool> echo_cancellation; // Audio processing to adjust the sensitivity of the local mic dynamically. Settable<bool> auto_gain_control; + // Audio processing to apply gain to the remote audio. + Settable<bool> rx_auto_gain_control; // Audio processing to filter out background noise. Settable<bool> noise_suppression; // Audio processing to remove background noise of lower frequencies. @@ -214,11 +260,24 @@ struct AudioOptions { Settable<bool> stereo_swapping; // Audio processing to detect typing. Settable<bool> typing_detection; + Settable<bool> aecm_generate_comfort_noise; Settable<bool> conference_mode; Settable<int> adjust_agc_delta; Settable<bool> experimental_agc; Settable<bool> experimental_aec; 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; + Settable<bool> tx_agc_limiter; + Settable<uint16> rx_agc_target_dbov; + Settable<uint16> rx_agc_digital_compression_gain; + Settable<bool> rx_agc_limiter; + Settable<uint32> recording_sample_rate; + Settable<uint32> playout_sample_rate; + // Set DSCP value for packet sent from audio channel. + Settable<bool> dscp; }; // Options that can be applied to a VideoMediaChannel or a VideoMediaEngine. @@ -239,13 +298,13 @@ struct VideoOptions { adapt_view_switch.SetFrom(change.adapt_view_switch); video_adapt_third.SetFrom(change.video_adapt_third); video_noise_reduction.SetFrom(change.video_noise_reduction); - video_three_layers.SetFrom(change.video_three_layers); - video_enable_camera_list.SetFrom(change.video_enable_camera_list); 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_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); cpu_overuse_detection.SetFrom(change.cpu_overuse_detection); conference_mode.SetFrom(change.conference_mode); @@ -255,6 +314,9 @@ struct VideoOptions { system_high_adaptation_threshhold.SetFrom( change.system_high_adaptation_threshhold); buffered_mode_latency.SetFrom(change.buffered_mode_latency); + lower_min_bitrate.SetFrom(change.lower_min_bitrate); + dscp.SetFrom(change.dscp); + suspend_below_min_bitrate.SetFrom(change.suspend_below_min_bitrate); } bool operator==(const VideoOptions& o) const { @@ -264,12 +326,11 @@ struct VideoOptions { adapt_view_switch == o.adapt_view_switch && video_adapt_third == o.video_adapt_third && video_noise_reduction == o.video_noise_reduction && - video_three_layers == o.video_three_layers && - video_enable_camera_list == o.video_enable_camera_list && video_one_layer_screencast == o.video_one_layer_screencast && video_high_bitrate == o.video_high_bitrate && video_watermark == o.video_watermark && 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 && cpu_overuse_detection == o.cpu_overuse_detection && conference_mode == o.conference_mode && @@ -278,7 +339,10 @@ struct VideoOptions { o.system_low_adaptation_threshhold && system_high_adaptation_threshhold == o.system_high_adaptation_threshhold && - buffered_mode_latency == o.buffered_mode_latency; + 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; } std::string ToString() const { @@ -290,13 +354,13 @@ struct VideoOptions { ost << ToStringIfSet("adapt view switch", adapt_view_switch); ost << ToStringIfSet("video adapt third", video_adapt_third); ost << ToStringIfSet("noise reduction", video_noise_reduction); - ost << ToStringIfSet("3 layers", video_three_layers); - ost << ToStringIfSet("camera list", video_enable_camera_list); ost << ToStringIfSet("1 layer screencast", video_one_layer_screencast); ost << ToStringIfSet("high bitrate", video_high_bitrate); ost << ToStringIfSet("watermark", video_watermark); 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("cpu overuse detection", cpu_overuse_detection); ost << ToStringIfSet("conference mode", conference_mode); @@ -304,6 +368,10 @@ struct VideoOptions { ost << ToStringIfSet("low", system_low_adaptation_threshhold); ost << ToStringIfSet("high", system_high_adaptation_threshhold); ost << ToStringIfSet("buffered mode latency", buffered_mode_latency); + ost << ToStringIfSet("lower min bitrate", lower_min_bitrate); + ost << ToStringIfSet("dscp", dscp); + ost << ToStringIfSet("suspend below min bitrate", + suspend_below_min_bitrate); ost << "}"; return ost.str(); } @@ -320,10 +388,6 @@ struct VideoOptions { Settable<bool> video_adapt_third; // Enable denoising? Settable<bool> video_noise_reduction; - // Experimental: Enable multi layer? - Settable<bool> video_three_layers; - // Experimental: Enable camera list? - Settable<bool> video_enable_camera_list; // Experimental: Enable one layer screencast? Settable<bool> video_one_layer_screencast; // Experimental: Enable WebRtc higher bitrate? @@ -332,6 +396,8 @@ struct VideoOptions { Settable<bool> video_watermark; // 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; // Enable WebRTC Cpu Overuse Detection, which is a new version of the CPU @@ -348,6 +414,13 @@ struct VideoOptions { SettablePercent system_high_adaptation_threshhold; // Specify buffered mode latency in milliseconds. Settable<int> buffered_mode_latency; + // Make minimum configured send bitrate even lower than usual, at 30kbit. + Settable<bool> lower_min_bitrate; + // Set DSCP value for packet sent from video channel. + Settable<bool> dscp; + // 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; }; // A class for playing out soundclips. @@ -436,9 +509,11 @@ class MediaChannel : public sigslot::has_slots<> { } // Called when a RTP packet is received. - virtual void OnPacketReceived(talk_base::Buffer* packet) = 0; + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) = 0; // Called when a RTCP packet is received. - virtual void OnRtcpReceived(talk_base::Buffer* packet) = 0; + virtual void OnRtcpReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) = 0; // Called when the socket's ability to send has changed. virtual void OnReadyToSend(bool ready) = 0; // Creates a new outgoing media stream with SSRCs and CNAME as described @@ -486,6 +561,21 @@ class MediaChannel : public sigslot::has_slots<> { return network_interface_->SetOption(type, opt, option); } + protected: + // This method sets DSCP |value| on both RTP and RTCP channels. + int SetDscp(talk_base::DiffServCodePoint value) { + int ret; + ret = SetOption(NetworkInterface::ST_RTP, + talk_base::Socket::OPT_DSCP, + value); + if (ret == 0) { + ret = SetOption(NetworkInterface::ST_RTCP, + talk_base::Socket::OPT_DSCP, + value); + } + return ret; + } + private: bool DoSendPacket(talk_base::Buffer* packet, bool rtcp) { talk_base::CritScope cs(&network_interface_crit_); @@ -509,15 +599,125 @@ enum SendFlags { SEND_MICROPHONE }; -struct VoiceSenderInfo { - VoiceSenderInfo() +// The stats information is structured as follows: +// Media are represented by either MediaSenderInfo or MediaReceiverInfo. +// Media contains a vector of SSRC infos that are exclusively used by this +// media. (SSRCs shared between media streams can't be represented.) + +// Information about an SSRC. +// This data may be locally recorded, or received in an RTCP SR or RR. +struct SsrcSenderInfo { + SsrcSenderInfo() : ssrc(0), - bytes_sent(0), + timestamp(0) { + } + uint32 ssrc; + double timestamp; // NTP timestamp, represented as seconds since epoch. +}; + +struct SsrcReceiverInfo { + SsrcReceiverInfo() + : ssrc(0), + timestamp(0) { + } + uint32 ssrc; + double timestamp; +}; + +struct MediaSenderInfo { + MediaSenderInfo() + : bytes_sent(0), packets_sent(0), packets_lost(0), fraction_lost(0.0), - ext_seqnum(0), - rtt_ms(0), + rtt_ms(0) { + } + void add_ssrc(const SsrcSenderInfo& stat) { + local_stats.push_back(stat); + } + // Temporary utility function for call sites that only provide SSRC. + // As more info is added into SsrcSenderInfo, this function should go away. + void add_ssrc(uint32 ssrc) { + SsrcSenderInfo stat; + stat.ssrc = ssrc; + add_ssrc(stat); + } + // Utility accessor for clients that are only interested in ssrc numbers. + std::vector<uint32> ssrcs() const { + std::vector<uint32> retval; + for (std::vector<SsrcSenderInfo>::const_iterator it = local_stats.begin(); + it != local_stats.end(); ++it) { + retval.push_back(it->ssrc); + } + return retval; + } + // Utility accessor for clients that make the assumption only one ssrc + // exists per media. + // This will eventually go away. + uint32 ssrc() const { + if (local_stats.size() > 0) { + return local_stats[0].ssrc; + } else { + return 0; + } + } + int64 bytes_sent; + int packets_sent; + int packets_lost; + float fraction_lost; + int rtt_ms; + std::string codec_name; + std::vector<SsrcSenderInfo> local_stats; + std::vector<SsrcReceiverInfo> remote_stats; +}; + +struct MediaReceiverInfo { + MediaReceiverInfo() + : bytes_rcvd(0), + packets_rcvd(0), + packets_lost(0), + fraction_lost(0.0) { + } + void add_ssrc(const SsrcReceiverInfo& stat) { + local_stats.push_back(stat); + } + // Temporary utility function for call sites that only provide SSRC. + // As more info is added into SsrcSenderInfo, this function should go away. + void add_ssrc(uint32 ssrc) { + SsrcReceiverInfo stat; + stat.ssrc = ssrc; + add_ssrc(stat); + } + std::vector<uint32> ssrcs() const { + std::vector<uint32> retval; + for (std::vector<SsrcReceiverInfo>::const_iterator it = local_stats.begin(); + it != local_stats.end(); ++it) { + retval.push_back(it->ssrc); + } + return retval; + } + // Utility accessor for clients that make the assumption only one ssrc + // exists per media. + // This will eventually go away. + uint32 ssrc() const { + if (local_stats.size() > 0) { + return local_stats[0].ssrc; + } else { + return 0; + } + } + + int64 bytes_rcvd; + int packets_rcvd; + int packets_lost; + float fraction_lost; + std::vector<SsrcReceiverInfo> local_stats; + std::vector<SsrcSenderInfo> remote_stats; +}; + +struct VoiceSenderInfo : public MediaSenderInfo { + VoiceSenderInfo() + : ext_seqnum(0), jitter_ms(0), audio_level(0), aec_quality_min(0.0), @@ -528,14 +728,7 @@ struct VoiceSenderInfo { typing_noise_detected(false) { } - uint32 ssrc; - std::string codec_name; - int64 bytes_sent; - int packets_sent; - int packets_lost; - float fraction_lost; int ext_seqnum; - int rtt_ms; int jitter_ms; int audio_level; float aec_quality_min; @@ -546,14 +739,9 @@ struct VoiceSenderInfo { bool typing_noise_detected; }; -struct VoiceReceiverInfo { +struct VoiceReceiverInfo : public MediaReceiverInfo { VoiceReceiverInfo() - : ssrc(0), - bytes_rcvd(0), - packets_rcvd(0), - packets_lost(0), - fraction_lost(0.0), - ext_seqnum(0), + : ext_seqnum(0), jitter_ms(0), jitter_buffer_ms(0), jitter_buffer_preferred_ms(0), @@ -562,11 +750,6 @@ struct VoiceReceiverInfo { expand_rate(0) { } - uint32 ssrc; - int64 bytes_rcvd; - int packets_rcvd; - int packets_lost; - float fraction_lost; int ext_seqnum; int jitter_ms; int jitter_buffer_ms; @@ -577,36 +760,28 @@ struct VoiceReceiverInfo { float expand_rate; }; -struct VideoSenderInfo { +struct VideoSenderInfo : public MediaSenderInfo { VideoSenderInfo() - : bytes_sent(0), - packets_sent(0), - packets_cached(0), - packets_lost(0), - fraction_lost(0.0), + : packets_cached(0), firs_rcvd(0), nacks_rcvd(0), - rtt_ms(0), frame_width(0), frame_height(0), framerate_input(0), framerate_sent(0), nominal_bitrate(0), preferred_bitrate(0), - adapt_reason(0) { + adapt_reason(0), + capture_jitter_ms(0), + avg_encode_ms(0), + encode_usage_percent(0), + capture_queue_delay_ms_per_s(0) { } - std::vector<uint32> ssrcs; std::vector<SsrcGroup> ssrc_groups; - std::string codec_name; - int64 bytes_sent; - int packets_sent; int packets_cached; - int packets_lost; - float fraction_lost; int firs_rcvd; int nacks_rcvd; - int rtt_ms; int frame_width; int frame_height; int framerate_input; @@ -614,15 +789,15 @@ struct VideoSenderInfo { int nominal_bitrate; int preferred_bitrate; int adapt_reason; + int capture_jitter_ms; + int avg_encode_ms; + int encode_usage_percent; + int capture_queue_delay_ms_per_s; }; -struct VideoReceiverInfo { +struct VideoReceiverInfo : public MediaReceiverInfo { VideoReceiverInfo() - : bytes_rcvd(0), - packets_rcvd(0), - packets_lost(0), - packets_concealed(0), - fraction_lost(0.0), + : packets_concealed(0), firs_sent(0), nacks_sent(0), frame_width(0), @@ -631,17 +806,18 @@ struct VideoReceiverInfo { framerate_decoded(0), framerate_output(0), framerate_render_input(0), - framerate_render_output(0) { + framerate_render_output(0), + decode_ms(0), + max_decode_ms(0), + jitter_buffer_ms(0), + min_playout_delay_ms(0), + render_delay_ms(0), + target_delay_ms(0), + current_delay_ms(0) { } - std::vector<uint32> ssrcs; std::vector<SsrcGroup> ssrc_groups; - int64 bytes_rcvd; - // vector<int> layer_bytes_rcvd; - int packets_rcvd; - int packets_lost; int packets_concealed; - float fraction_lost; int firs_sent; int nacks_sent; int frame_width; @@ -653,31 +829,42 @@ struct VideoReceiverInfo { int framerate_render_input; // Framerate that the renderer reports. int framerate_render_output; + + // All stats below are gathered per-VideoReceiver, but some will be correlated + // across MediaStreamTracks. NOTE(hta): when sinking stats into per-SSRC + // structures, reflect this in the new layout. + + // Current frame decode latency. + int decode_ms; + // Maximum observed frame decode latency. + int max_decode_ms; + // Jitter (network-related) latency. + int jitter_buffer_ms; + // Requested minimum playout latency. + int min_playout_delay_ms; + // Requested latency to account for rendering delay. + int render_delay_ms; + // Target overall delay: network+decode+render, accounting for + // min_playout_delay_ms. + int target_delay_ms; + // Current overall delay, possibly ramping towards target_delay_ms. + int current_delay_ms; }; -struct DataSenderInfo { +struct DataSenderInfo : public MediaSenderInfo { DataSenderInfo() - : ssrc(0), - bytes_sent(0), - packets_sent(0) { + : ssrc(0) { } uint32 ssrc; - std::string codec_name; - int64 bytes_sent; - int packets_sent; }; -struct DataReceiverInfo { +struct DataReceiverInfo : public MediaReceiverInfo { DataReceiverInfo() - : ssrc(0), - bytes_rcvd(0), - packets_rcvd(0) { + : ssrc(0) { } uint32 ssrc; - int64 bytes_rcvd; - int packets_rcvd; }; struct BandwidthEstimationInfo { @@ -946,25 +1133,15 @@ class DataMediaChannel : public MediaChannel { virtual ~DataMediaChannel() {} - virtual bool SetSendBandwidth(bool autobw, int bps) = 0; virtual bool SetSendCodecs(const std::vector<DataCodec>& codecs) = 0; virtual bool SetRecvCodecs(const std::vector<DataCodec>& codecs) = 0; - virtual bool SetRecvRtpHeaderExtensions( - const std::vector<RtpHeaderExtension>& extensions) = 0; - virtual bool SetSendRtpHeaderExtensions( - const std::vector<RtpHeaderExtension>& extensions) = 0; - virtual bool AddSendStream(const StreamParams& sp) = 0; - virtual bool RemoveSendStream(uint32 ssrc) = 0; - virtual bool AddRecvStream(const StreamParams& sp) = 0; - virtual bool RemoveRecvStream(uint32 ssrc) = 0; + virtual bool MuteStream(uint32 ssrc, bool on) { return false; } // TODO(pthatcher): Implement this. virtual bool GetStats(DataMediaInfo* info) { return true; } virtual bool SetSend(bool send) = 0; virtual bool SetReceive(bool receive) = 0; - virtual void OnPacketReceived(talk_base::Buffer* packet) = 0; - virtual void OnRtcpReceived(talk_base::Buffer* packet) = 0; virtual bool SendData( const SendDataParams& params, @@ -980,6 +1157,11 @@ 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; }; } // 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 8ebc13b1086..f9165728dde 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/mediaengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/mediaengine.h @@ -60,30 +60,6 @@ class VideoCapturer; // proper synchronization between both media types. class MediaEngineInterface { public: - // Bitmask flags for options that may be supported by the media engine - // implementation. This can be converted to and from an - // AudioOptions struct for backwards compatibility with calls that - // use flags until we transition to using structs everywhere. - enum AudioFlags { - // Audio processing that attempts to filter away the output signal from - // later inbound pickup. - ECHO_CANCELLATION = 1 << 0, - // Audio processing to adjust the sensitivity of the local mic dynamically. - AUTO_GAIN_CONTROL = 1 << 1, - // Audio processing to filter out background noise. - NOISE_SUPPRESSION = 1 << 2, - // Audio processing to remove background noise of lower frequencies. - HIGHPASS_FILTER = 1 << 3, - // A switch to swap which captured signal is left and right in stereo mode. - STEREO_FLIPPING = 1 << 4, - // Controls delegation echo cancellation to use the OS' facility. - SYSTEM_AEC_MODE = 1 << 5, - - ALL_AUDIO_OPTIONS = (1 << 6) - 1, - DEFAULT_AUDIO_OPTIONS = ECHO_CANCELLATION | AUTO_GAIN_CONTROL | - NOISE_SUPPRESSION | HIGHPASS_FILTER, - }; - // Default value to be used for SetAudioDelayOffset(). static const int kDefaultAudioDelayOffset; @@ -109,10 +85,12 @@ class MediaEngineInterface { virtual SoundclipMedia *CreateSoundclip() = 0; // Configuration + // Gets global audio options. + virtual AudioOptions GetAudioOptions() const = 0; // Sets global audio options. "options" are from AudioOptions, above. - virtual bool SetAudioOptions(int options) = 0; + virtual bool SetAudioOptions(const AudioOptions& options) = 0; // Sets global video options. "options" are from VideoOptions, above. - virtual bool SetVideoOptions(int options) = 0; + virtual bool SetVideoOptions(const VideoOptions& options) = 0; // Sets the value used by the echo canceller to offset delay values obtained // from the OS. virtual bool SetAudioDelayOffset(int offset) = 0; @@ -120,6 +98,10 @@ class MediaEngineInterface { // and encode video. virtual bool SetDefaultVideoEncoderConfig(const VideoEncoderConfig& config) = 0; + // Gets the default (maximum) codec/resolution and encoder option used to + // capture and encode video, as set by SetDefaultVideoEncoderConfig or the + // default from the video engine if not previously set. + virtual VideoEncoderConfig GetDefaultVideoEncoderConfig() const = 0; // Device selection // TODO(tschmelcher): Add method for selecting the soundclip device. @@ -210,11 +192,14 @@ class CompositeMediaEngine : public MediaEngineInterface { return voice_.CreateSoundclip(); } - virtual bool SetAudioOptions(int o) { - return voice_.SetOptions(o); + virtual AudioOptions GetAudioOptions() const { + return voice_.GetOptions(); + } + virtual bool SetAudioOptions(const AudioOptions& options) { + return voice_.SetOptions(options); } - virtual bool SetVideoOptions(int o) { - return video_.SetOptions(o); + virtual bool SetVideoOptions(const VideoOptions& options) { + return video_.SetOptions(options); } virtual bool SetAudioDelayOffset(int offset) { return voice_.SetDelayOffset(offset); @@ -222,6 +207,9 @@ class CompositeMediaEngine : public MediaEngineInterface { virtual bool SetDefaultVideoEncoderConfig(const VideoEncoderConfig& config) { return video_.SetDefaultEncoderConfig(config); } + virtual VideoEncoderConfig GetDefaultVideoEncoderConfig() const { + return video_.GetDefaultEncoderConfig(); + } virtual bool SetSoundDevices(const Device* in_device, const Device* out_device) { @@ -304,7 +292,8 @@ class NullVoiceEngine { return NULL; } bool SetDelayOffset(int offset) { return true; } - bool SetOptions(int opts) { return true; } + AudioOptions GetOptions() const { return AudioOptions(); } + bool SetOptions(const AudioOptions& options) { return true; } bool SetDevices(const Device* in_device, const Device* out_device) { return true; } @@ -344,7 +333,10 @@ class NullVideoEngine { VoiceMediaChannel* voice_media_channel) { return NULL; } - bool SetOptions(int opts) { return true; } + bool SetOptions(const VideoOptions& options) { return true; } + VideoEncoderConfig GetDefaultEncoderConfig() const { + return VideoEncoderConfig(); + } bool SetDefaultEncoderConfig(const VideoEncoderConfig& config) { 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 2a23c100597..0f84c836fc6 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.cc @@ -230,7 +230,8 @@ bool RtpDataMediaChannel::RemoveRecvStream(uint32 ssrc) { return true; } -void RtpDataMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { +void RtpDataMediaChannel::OnPacketReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { RtpHeader header; if (!GetRtpHeader(packet->data(), packet->length(), &header)) { // Don't want to log for every corrupt packet. @@ -259,10 +260,12 @@ void RtpDataMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { DataCodec codec; if (!FindCodecById(recv_codecs_, header.payload_type, &codec)) { - LOG(LS_WARNING) << "Not receiving packet " - << header.ssrc << ":" << header.seq_num - << " (" << data_len << ")" - << " because unknown payload id: " << header.payload_type; + // For bundling, this will be logged for every message. + // So disable this logging. + // LOG(LS_WARNING) << "Not receiving packet " + // << header.ssrc << ":" << header.seq_num + // << " (" << data_len << ")" + // << " because unknown payload id: " << header.payload_type; return; } @@ -342,10 +345,6 @@ bool RtpDataMediaChannel::SendData( << "; already sent " << send_limiter_->used_in_period() << "/" << send_limiter_->max_per_period(); return false; - } else { - LOG(LS_VERBOSE) << "Sending data packet of len=" << packet_len - << "; already sent " << send_limiter_->used_in_period() - << "/" << send_limiter_->max_per_period(); } RtpHeader header; @@ -363,12 +362,12 @@ bool RtpDataMediaChannel::SendData( packet.AppendData(&kReservedSpace, sizeof(kReservedSpace)); packet.AppendData(payload.data(), payload.length()); - // Uncomment this for easy debugging. - // LOG(LS_INFO) << "Sent packet: " - // << " stream=" << found_stream.id - // << ", seqnum=" << header.seq_num - // << ", timestamp=" << header.timestamp - // << ", len=" << data_len; + LOG(LS_VERBOSE) << "Sent RTP data packet: " + << " stream=" << found_stream.id + << " ssrc=" << header.ssrc + << ", seqnum=" << header.seq_num + << ", timestamp=" << header.timestamp + << ", len=" << payload.length(); MediaChannel::SendPacket(&packet); send_limiter_->Use(packet_len, now); 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 bc7b667eac6..59e6589532d 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.h +++ b/chromium/third_party/libjingle/source/talk/media/base/rtpdataengine.h @@ -115,8 +115,10 @@ class RtpDataMediaChannel : public DataMediaChannel { receiving_ = receive; return true; } - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet) {} + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); + virtual void OnRtcpReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) {} virtual void OnReadyToSend(bool ready) {} virtual bool SendData( const SendDataParams& params, 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 4ee072ee782..a86ab3b3125 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 @@ -294,7 +294,8 @@ TEST_F(RtpDataMediaChannelTest, SendData) { cricket::RtpHeader header1 = GetSentDataHeader(1); EXPECT_EQ(header1.ssrc, 42U); EXPECT_EQ(header1.payload_type, 103); - EXPECT_EQ(header0.seq_num + 1, header1.seq_num); + EXPECT_EQ(static_cast<uint16>(header0.seq_num + 1), + static_cast<uint16>(header1.seq_num)); EXPECT_EQ(header0.timestamp + 180000, header1.timestamp); } @@ -353,9 +354,11 @@ TEST_F(RtpDataMediaChannelTest, SendDataMultipleClocks) { cricket::RtpHeader header1b = GetSentDataHeader(2); cricket::RtpHeader header2b = GetSentDataHeader(3); - EXPECT_EQ(header1a.seq_num + 1, header1b.seq_num); + EXPECT_EQ(static_cast<uint16>(header1a.seq_num + 1), + static_cast<uint16>(header1b.seq_num)); EXPECT_EQ(header1a.timestamp + 90000, header1b.timestamp); - EXPECT_EQ(header2a.seq_num + 1, header2b.seq_num); + EXPECT_EQ(static_cast<uint16>(header2a.seq_num + 1), + static_cast<uint16>(header2b.seq_num)); EXPECT_EQ(header2a.timestamp + 180000, header2b.timestamp); } @@ -420,13 +423,13 @@ TEST_F(RtpDataMediaChannelTest, ReceiveData) { talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel()); // SetReceived not called. - dmc->OnPacketReceived(&packet); + dmc->OnPacketReceived(&packet, talk_base::PacketTime()); EXPECT_FALSE(HasReceivedData()); dmc->SetReceive(true); // Unknown payload id - dmc->OnPacketReceived(&packet); + dmc->OnPacketReceived(&packet, talk_base::PacketTime()); EXPECT_FALSE(HasReceivedData()); cricket::DataCodec codec; @@ -437,7 +440,7 @@ TEST_F(RtpDataMediaChannelTest, ReceiveData) { ASSERT_TRUE(dmc->SetRecvCodecs(codecs)); // Unknown stream - dmc->OnPacketReceived(&packet); + dmc->OnPacketReceived(&packet, talk_base::PacketTime()); EXPECT_FALSE(HasReceivedData()); cricket::StreamParams stream; @@ -445,7 +448,7 @@ TEST_F(RtpDataMediaChannelTest, ReceiveData) { ASSERT_TRUE(dmc->AddRecvStream(stream)); // Finally works! - dmc->OnPacketReceived(&packet); + dmc->OnPacketReceived(&packet, talk_base::PacketTime()); EXPECT_TRUE(HasReceivedData()); EXPECT_EQ("abcde", GetReceivedData()); EXPECT_EQ(5U, GetReceivedDataLen()); @@ -460,6 +463,6 @@ TEST_F(RtpDataMediaChannelTest, InvalidRtpPackets) { talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel()); // Too short - dmc->OnPacketReceived(&packet); + dmc->OnPacketReceived(&packet, talk_base::PacketTime()); EXPECT_FALSE(HasReceivedData()); } diff --git a/chromium/third_party/libjingle/source/talk/media/base/streamparams.cc b/chromium/third_party/libjingle/source/talk/media/base/streamparams.cc index 08eeea7a42a..19d8269044a 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/streamparams.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/streamparams.cc @@ -27,12 +27,14 @@ #include "talk/media/base/streamparams.h" +#include <list> #include <sstream> namespace cricket { const char kFecSsrcGroupSemantics[] = "FEC"; const char kFidSsrcGroupSemantics[] = "FID"; +const char kSimSsrcGroupSemantics[] = "SIM"; static std::string SsrcsToString(const std::vector<uint32>& ssrcs) { std::ostringstream ost; @@ -179,4 +181,49 @@ bool RemoveStreamByIds(StreamParamsVec* streams, return RemoveStream(streams, StreamSelector(groupid, id)); } +bool IsOneSsrcStream(const StreamParams& sp) { + if (sp.ssrcs.size() == 1 && sp.ssrc_groups.empty()) { + return true; + } + if (sp.ssrcs.size() == 2) { + const SsrcGroup* fid_group = sp.get_ssrc_group(kFidSsrcGroupSemantics); + if (fid_group != NULL) { + return (sp.ssrcs == fid_group->ssrcs); + } + } + return false; +} + +static void RemoveFirst(std::list<uint32>* ssrcs, uint32 value) { + std::list<uint32>::iterator it = + std::find(ssrcs->begin(), ssrcs->end(), value); + if (it != ssrcs->end()) { + ssrcs->erase(it); + } +} + +bool IsSimulcastStream(const StreamParams& sp) { + const SsrcGroup* const sg = sp.get_ssrc_group(kSimSsrcGroupSemantics); + if (sg == NULL || sg->ssrcs.size() < 2) { + return false; + } + // Start with all StreamParams SSRCs. Remove simulcast SSRCs (from sg) and + // RTX SSRCs. If we still have SSRCs left, we don't know what they're for. + // Also we remove first-found SSRCs only. So duplicates should lead to errors. + std::list<uint32> sp_ssrcs(sp.ssrcs.begin(), sp.ssrcs.end()); + for (size_t i = 0; i < sg->ssrcs.size(); ++i) { + RemoveFirst(&sp_ssrcs, sg->ssrcs[i]); + } + for (size_t i = 0; i < sp.ssrc_groups.size(); ++i) { + const SsrcGroup& group = sp.ssrc_groups[i]; + if (group.semantics.compare(kFidSsrcGroupSemantics) != 0 || + group.ssrcs.size() != 2) { + continue; + } + RemoveFirst(&sp_ssrcs, group.ssrcs[1]); + } + // If there's SSRCs left that we don't know how to handle, we bail out. + return sp_ssrcs.size() == 0; +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/base/streamparams.h b/chromium/third_party/libjingle/source/talk/media/base/streamparams.h index 1561d6f92ea..b57f1f7810f 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/streamparams.h +++ b/chromium/third_party/libjingle/source/talk/media/base/streamparams.h @@ -31,6 +31,14 @@ // described by one StreamParams object // SsrcGroup is used to describe the relationship between the SSRCs that // are used for this media source. +// E.x: Consider a source that is sent as 3 simulcast streams +// Let the simulcast elements have SSRC 10, 20, 30. +// Let each simulcast element use FEC and let the protection packets have +// SSRC 11,21,31. +// To describe this 4 SsrcGroups are needed, +// StreamParams would then contain ssrc = {10,11,20,21,30,31} and +// ssrc_groups = {{SIM,{10,20,30}, {FEC,{10,11}, {FEC, {20,21}, {FEC {30,31}}} +// Please see RFC 5576. #ifndef TALK_MEDIA_BASE_STREAMPARAMS_H_ #define TALK_MEDIA_BASE_STREAMPARAMS_H_ @@ -46,6 +54,7 @@ namespace cricket { extern const char kFecSsrcGroupSemantics[]; extern const char kFidSsrcGroupSemantics[]; +extern const char kSimSsrcGroupSemantics[]; struct SsrcGroup { SsrcGroup(const std::string& usage, const std::vector<uint32>& ssrcs) @@ -204,6 +213,16 @@ bool RemoveStreamByIds(StreamParamsVec* streams, const std::string& groupid, const std::string& id); +// Checks if |sp| defines parameters for a single primary stream. There may +// be an RTX stream associated with the primary stream. Leaving as non-static so +// we can test this function. +bool IsOneSsrcStream(const StreamParams& sp); + +// Checks if |sp| defines parameters for one Simulcast stream. There may be RTX +// streams associated with the simulcast streams. Leaving as non-static so we +// can test this function. +bool IsSimulcastStream(const StreamParams& sp); + } // namespace cricket #endif // TALK_MEDIA_BASE_STREAMPARAMS_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/base/streamparams_unittest.cc b/chromium/third_party/libjingle/source/talk/media/base/streamparams_unittest.cc index 99d1603e628..f3a03d63635 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/streamparams_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/streamparams_unittest.cc @@ -29,8 +29,10 @@ #include "talk/media/base/streamparams.h" #include "talk/media/base/testutils.h" -static const uint32 kSscrs1[] = {1}; -static const uint32 kSscrs2[] = {1, 2}; +static const uint32 kSsrcs1[] = {1}; +static const uint32 kSsrcs2[] = {1, 2}; +static const uint32 kSsrcs3[] = {1, 2, 3}; +static const uint32 kRtxSsrcs3[] = {4, 5, 6}; static cricket::StreamParams CreateStreamParamsWithSsrcGroup( const std::string& semantics, const uint32 ssrcs_in[], size_t len) { @@ -44,10 +46,10 @@ static cricket::StreamParams CreateStreamParamsWithSsrcGroup( TEST(SsrcGroup, EqualNotEqual) { cricket::SsrcGroup ssrc_groups[] = { - cricket::SsrcGroup("ABC", MAKE_VECTOR(kSscrs1)), - cricket::SsrcGroup("ABC", MAKE_VECTOR(kSscrs2)), - cricket::SsrcGroup("Abc", MAKE_VECTOR(kSscrs2)), - cricket::SsrcGroup("abc", MAKE_VECTOR(kSscrs2)), + cricket::SsrcGroup("ABC", MAKE_VECTOR(kSsrcs1)), + cricket::SsrcGroup("ABC", MAKE_VECTOR(kSsrcs2)), + cricket::SsrcGroup("Abc", MAKE_VECTOR(kSsrcs2)), + cricket::SsrcGroup("abc", MAKE_VECTOR(kSsrcs2)), }; for (size_t i = 0; i < ARRAY_SIZE(ssrc_groups); ++i) { @@ -59,18 +61,18 @@ TEST(SsrcGroup, EqualNotEqual) { } TEST(SsrcGroup, HasSemantics) { - cricket::SsrcGroup sg1("ABC", MAKE_VECTOR(kSscrs1)); + cricket::SsrcGroup sg1("ABC", MAKE_VECTOR(kSsrcs1)); EXPECT_TRUE(sg1.has_semantics("ABC")); - cricket::SsrcGroup sg2("Abc", MAKE_VECTOR(kSscrs1)); + cricket::SsrcGroup sg2("Abc", MAKE_VECTOR(kSsrcs1)); EXPECT_FALSE(sg2.has_semantics("ABC")); - cricket::SsrcGroup sg3("abc", MAKE_VECTOR(kSscrs1)); + cricket::SsrcGroup sg3("abc", MAKE_VECTOR(kSsrcs1)); EXPECT_FALSE(sg3.has_semantics("ABC")); } TEST(SsrcGroup, ToString) { - cricket::SsrcGroup sg1("ABC", MAKE_VECTOR(kSscrs1)); + cricket::SsrcGroup sg1("ABC", MAKE_VECTOR(kSsrcs1)); EXPECT_STREQ("{semantics:ABC;ssrcs:[1]}", sg1.ToString().c_str()); } @@ -88,22 +90,22 @@ TEST(StreamParams, CreateLegacy) { TEST(StreamParams, HasSsrcGroup) { cricket::StreamParams sp = - CreateStreamParamsWithSsrcGroup("XYZ", kSscrs2, ARRAY_SIZE(kSscrs2)); + CreateStreamParamsWithSsrcGroup("XYZ", kSsrcs2, ARRAY_SIZE(kSsrcs2)); EXPECT_EQ(2U, sp.ssrcs.size()); - EXPECT_EQ(kSscrs2[0], sp.first_ssrc()); + EXPECT_EQ(kSsrcs2[0], sp.first_ssrc()); EXPECT_TRUE(sp.has_ssrcs()); - EXPECT_TRUE(sp.has_ssrc(kSscrs2[0])); - EXPECT_TRUE(sp.has_ssrc(kSscrs2[1])); + EXPECT_TRUE(sp.has_ssrc(kSsrcs2[0])); + EXPECT_TRUE(sp.has_ssrc(kSsrcs2[1])); EXPECT_TRUE(sp.has_ssrc_group("XYZ")); EXPECT_EQ(1U, sp.ssrc_groups.size()); EXPECT_EQ(2U, sp.ssrc_groups[0].ssrcs.size()); - EXPECT_EQ(kSscrs2[0], sp.ssrc_groups[0].ssrcs[0]); - EXPECT_EQ(kSscrs2[1], sp.ssrc_groups[0].ssrcs[1]); + EXPECT_EQ(kSsrcs2[0], sp.ssrc_groups[0].ssrcs[0]); + EXPECT_EQ(kSsrcs2[1], sp.ssrc_groups[0].ssrcs[1]); } TEST(StreamParams, GetSsrcGroup) { cricket::StreamParams sp = - CreateStreamParamsWithSsrcGroup("XYZ", kSscrs2, ARRAY_SIZE(kSscrs2)); + CreateStreamParamsWithSsrcGroup("XYZ", kSsrcs2, ARRAY_SIZE(kSsrcs2)); EXPECT_EQ(NULL, sp.get_ssrc_group("xyz")); EXPECT_EQ(&sp.ssrc_groups[0], sp.get_ssrc_group("XYZ")); } @@ -112,13 +114,13 @@ TEST(StreamParams, EqualNotEqual) { cricket::StreamParams l1 = cricket::StreamParams::CreateLegacy(1); cricket::StreamParams l2 = cricket::StreamParams::CreateLegacy(2); cricket::StreamParams sg1 = - CreateStreamParamsWithSsrcGroup("ABC", kSscrs1, ARRAY_SIZE(kSscrs1)); + CreateStreamParamsWithSsrcGroup("ABC", kSsrcs1, ARRAY_SIZE(kSsrcs1)); cricket::StreamParams sg2 = - CreateStreamParamsWithSsrcGroup("ABC", kSscrs2, ARRAY_SIZE(kSscrs2)); + CreateStreamParamsWithSsrcGroup("ABC", kSsrcs2, ARRAY_SIZE(kSsrcs2)); cricket::StreamParams sg3 = - CreateStreamParamsWithSsrcGroup("Abc", kSscrs2, ARRAY_SIZE(kSscrs2)); + CreateStreamParamsWithSsrcGroup("Abc", kSsrcs2, ARRAY_SIZE(kSsrcs2)); cricket::StreamParams sg4 = - CreateStreamParamsWithSsrcGroup("abc", kSscrs2, ARRAY_SIZE(kSscrs2)); + CreateStreamParamsWithSsrcGroup("abc", kSsrcs2, ARRAY_SIZE(kSsrcs2)); cricket::StreamParams sps[] = {l1, l2, sg1, sg2, sg3, sg4}; for (size_t i = 0; i < ARRAY_SIZE(sps); ++i) { @@ -159,7 +161,90 @@ TEST(StreamParams, FidFunctions) { TEST(StreamParams, ToString) { cricket::StreamParams sp = - CreateStreamParamsWithSsrcGroup("XYZ", kSscrs2, ARRAY_SIZE(kSscrs2)); + CreateStreamParamsWithSsrcGroup("XYZ", kSsrcs2, ARRAY_SIZE(kSsrcs2)); EXPECT_STREQ("{ssrcs:[1,2];ssrc_groups:{semantics:XYZ;ssrcs:[1,2]};}", sp.ToString().c_str()); } + + +TEST(StreamParams, TestIsOneSsrcStream_LegacyStream) { + EXPECT_TRUE( + cricket::IsOneSsrcStream(cricket::StreamParams::CreateLegacy(13))); +} + +TEST(StreamParams, TestIsOneSsrcStream_SingleRtxStream) { + cricket::StreamParams stream; + stream.add_ssrc(13); + EXPECT_TRUE(stream.AddFidSsrc(13, 14)); + EXPECT_TRUE(cricket::IsOneSsrcStream(stream)); +} + +TEST(StreamParams, TestIsOneSsrcStream_SimulcastStream) { + EXPECT_FALSE(cricket::IsOneSsrcStream( + cricket::CreateSimStreamParams("cname", MAKE_VECTOR(kSsrcs2)))); + EXPECT_FALSE(cricket::IsOneSsrcStream( + cricket::CreateSimStreamParams("cname", MAKE_VECTOR(kSsrcs3)))); +} + +TEST(StreamParams, TestIsOneSsrcStream_SimRtxStream) { + cricket::StreamParams stream = + cricket::CreateSimWithRtxStreamParams("cname", + MAKE_VECTOR(kSsrcs3), + MAKE_VECTOR(kRtxSsrcs3)); + EXPECT_FALSE(cricket::IsOneSsrcStream(stream)); +} + +TEST(StreamParams, TestIsSimulcastStream_LegacyStream) { + EXPECT_FALSE( + cricket::IsSimulcastStream(cricket::StreamParams::CreateLegacy(13))); +} + +TEST(StreamParams, TestIsSimulcastStream_SingleRtxStream) { + cricket::StreamParams stream; + stream.add_ssrc(13); + EXPECT_TRUE(stream.AddFidSsrc(13, 14)); + EXPECT_FALSE(cricket::IsSimulcastStream(stream)); +} + +TEST(StreamParams, TestIsSimulcastStream_SimulcastStream) { + EXPECT_TRUE(cricket::IsSimulcastStream( + cricket::CreateSimStreamParams("cname", MAKE_VECTOR(kSsrcs2)))); + EXPECT_TRUE(cricket::IsSimulcastStream( + cricket::CreateSimStreamParams("cname", MAKE_VECTOR(kSsrcs3)))); +} + +TEST(StreamParams, TestIsSimulcastStream_SimRtxStream) { + cricket::StreamParams stream = + cricket::CreateSimWithRtxStreamParams("cname", + MAKE_VECTOR(kSsrcs3), + MAKE_VECTOR(kRtxSsrcs3)); + EXPECT_TRUE(cricket::IsSimulcastStream(stream)); +} + +TEST(StreamParams, TestIsSimulcastStream_InvalidStreams) { + // stream1 has extra non-sim, non-fid ssrc. + cricket::StreamParams stream1 = + cricket::CreateSimWithRtxStreamParams("cname", + MAKE_VECTOR(kSsrcs3), + MAKE_VECTOR(kRtxSsrcs3)); + stream1.add_ssrc(25); + EXPECT_FALSE(cricket::IsSimulcastStream(stream1)); + + // stream2 has invalid fid-group (no primary). + cricket::StreamParams stream2; + stream2.add_ssrc(13); + EXPECT_TRUE(stream2.AddFidSsrc(13, 14)); + std::remove(stream2.ssrcs.begin(), stream2.ssrcs.end(), 13); + EXPECT_FALSE(cricket::IsSimulcastStream(stream2)); + + // stream3 has two SIM groups. + cricket::StreamParams stream3 = + cricket::CreateSimStreamParams("cname", MAKE_VECTOR(kSsrcs2)); + std::vector<uint32> sim_ssrcs = MAKE_VECTOR(kRtxSsrcs3); + cricket::SsrcGroup sg(cricket::kSimSsrcGroupSemantics, sim_ssrcs); + for (size_t i = 0; i < sim_ssrcs.size(); i++) { + stream3.add_ssrc(sim_ssrcs[i]); + } + stream3.ssrc_groups.push_back(sg); + EXPECT_FALSE(cricket::IsSimulcastStream(stream3)); +} 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 3edb5c7cfbb..9b1b16d21fc 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/testutils.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/testutils.cc @@ -336,4 +336,30 @@ bool VideoFrameEqual(const VideoFrame* frame0, const VideoFrame* frame1) { return true; } +cricket::StreamParams CreateSimStreamParams( + const std::string& cname, const std::vector<uint32>& ssrcs) { + cricket::StreamParams sp; + cricket::SsrcGroup sg(cricket::kSimSsrcGroupSemantics, ssrcs); + sp.ssrcs = ssrcs; + sp.ssrc_groups.push_back(sg); + sp.cname = cname; + return sp; +} + +// There should be an rtx_ssrc per ssrc. +cricket::StreamParams CreateSimWithRtxStreamParams( + const std::string& cname, const std::vector<uint32>& ssrcs, + const std::vector<uint32>& rtx_ssrcs) { + cricket::StreamParams sp = CreateSimStreamParams(cname, ssrcs); + for (size_t i = 0; i < ssrcs.size(); ++i) { + sp.ssrcs.push_back(rtx_ssrcs[i]); + std::vector<uint32> fid_ssrcs; + fid_ssrcs.push_back(ssrcs[i]); + fid_ssrcs.push_back(rtx_ssrcs[i]); + cricket::SsrcGroup fid_group(cricket::kFidSsrcGroupSemantics, fid_ssrcs); + sp.ssrc_groups.push_back(fid_group); + } + return sp; +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/base/testutils.h b/chromium/third_party/libjingle/source/talk/media/base/testutils.h index 4d037b7ba38..dd13d5a0e59 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/testutils.h +++ b/chromium/third_party/libjingle/source/talk/media/base/testutils.h @@ -28,13 +28,6 @@ #ifndef TALK_MEDIA_BASE_TESTUTILS_H_ #define TALK_MEDIA_BASE_TESTUTILS_H_ -#ifdef LINUX -#include <X11/Xlib.h> -// X defines a few macros that stomp on types that gunit.h uses. -#undef None -#undef Bool -#endif - #include <string> #include <vector> @@ -245,36 +238,14 @@ bool ContainsMatchingCodec(const std::vector<C>& codecs, const C& codec) { return false; } -#define MAYBE_SKIP_SCREENCAST_TEST() \ - if (!cricket::IsScreencastingAvailable()) { \ - LOG(LS_WARNING) << "Skipping test, since it doesn't have the requisite " \ - << "X environment for screen capture."; \ - return; \ - } \ - -#ifdef LINUX -struct XDisplay { - XDisplay() : display_(XOpenDisplay(NULL)) { } - ~XDisplay() { if (display_) XCloseDisplay(display_); } - bool IsValid() const { return display_ != NULL; } - operator Display*() { return display_; } - private: - Display* display_; -}; -#endif - -// Returns true if screencasting is available. When false, anything that uses -// screencasting features may fail. -inline bool IsScreencastingAvailable() { -#ifdef LINUX - XDisplay display; - if (!display.IsValid()) { - LOG(LS_WARNING) << "No X Display available."; - return false; - } -#endif - return true; -} +// Create Simulcast StreamParams with given |ssrcs| and |cname|. +cricket::StreamParams CreateSimStreamParams( + const std::string& cname, const std::vector<uint32>& ssrcs); +// Create Simulcast stream with given |ssrcs| and |rtx_ssrcs|. +// The number of |rtx_ssrcs| must match number of |ssrcs|. +cricket::StreamParams CreateSimWithRtxStreamParams( + const std::string& cname, const std::vector<uint32>& ssrcs, + const std::vector<uint32>& rtx_ssrcs); } // namespace cricket 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 20114fbd383..22b1f7d8ddb 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videoadapter.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/videoadapter.cc @@ -165,8 +165,8 @@ VideoAdapter::VideoAdapter() frames_(0), adapted_frames_(0), adaption_changes_(0), - previous_width(0), - previous_height(0), + previous_width_(0), + previous_height_(0), black_output_(false), is_black_(false), interval_next_frame_(0) { @@ -240,11 +240,11 @@ int VideoAdapter::GetOutputNumPixels() const { // TODO(fbarchard): Add AdaptFrameRate function that only drops frames but // not resolution. bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, - const VideoFrame** out_frame) { - talk_base::CritScope cs(&critical_section_); + VideoFrame** out_frame) { if (!in_frame || !out_frame) { return false; } + talk_base::CritScope cs(&critical_section_); ++frames_; // Update input to actual frame dimensions. @@ -306,8 +306,8 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, // resolution changes as well. Consider dropping the statistics into their // own class which could be queried publically. bool changed = false; - if (previous_width && (previous_width != (*out_frame)->GetWidth() || - previous_height != (*out_frame)->GetHeight())) { + if (previous_width_ && (previous_width_ != (*out_frame)->GetWidth() || + previous_height_ != (*out_frame)->GetHeight())) { show = true; ++adaption_changes_; changed = true; @@ -325,8 +325,8 @@ bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, << "x" << (*out_frame)->GetHeight() << " Changed: " << (changed ? "true" : "false"); } - previous_width = (*out_frame)->GetWidth(); - previous_height = (*out_frame)->GetHeight(); + previous_width_ = (*out_frame)->GetWidth(); + previous_height_ = (*out_frame)->GetHeight(); return true; } @@ -376,7 +376,7 @@ bool VideoAdapter::StretchToOutputFrame(const VideoFrame* in_frame) { /////////////////////////////////////////////////////////////////////// // Implementation of CoordinatedVideoAdapter CoordinatedVideoAdapter::CoordinatedVideoAdapter() - : cpu_adaptation_(false), + : cpu_adaptation_(true), cpu_smoothing_(false), gd_adaptation_(true), view_adaptation_(true), 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 2bd31d5df5c..12be4fab1ab 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videoadapter.h +++ b/chromium/third_party/libjingle/source/talk/media/base/videoadapter.h @@ -62,7 +62,7 @@ class VideoAdapter { // 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, const VideoFrame** out_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 " @@ -90,8 +90,8 @@ class VideoAdapter { int frames_; // Number of input frames. int adapted_frames_; // 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. + size_t previous_width_; // Previous adapter output width. + size_t previous_height_; // Previous adapter output height. bool black_output_; // Flag to tell if we need to black output_frame_. bool is_black_; // Flag to tell if output_frame_ is currently black. int64 interval_next_frame_; @@ -109,7 +109,7 @@ class CoordinatedVideoAdapter : public VideoAdapter, public sigslot::has_slots<> { public: enum AdaptRequest { UPGRADE, KEEP, DOWNGRADE }; - enum { + enum AdaptReasonEnum { ADAPTREASON_CPU = 1, ADAPTREASON_BANDWIDTH = 2, ADAPTREASON_VIEW = 4 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 acab19d6616..26fcfa9fbb4 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videocapturer.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/videocapturer.cc @@ -373,7 +373,7 @@ void VideoCapturer::OnFrameCaptured(VideoCapturer*, // TODO(fbarchard): Avoid scale and convert if muted. // Temporary buffer is scoped here so it will persist until i420_frame.Init() // makes a copy of the frame, converting to I420. - talk_base::scoped_array<uint8> temp_buffer; + talk_base::scoped_ptr<uint8[]> temp_buffer; // YUY2 can be scaled vertically using an ARGB scaler. Aspect ratio is only // a problem on OSX. OSX always converts webcams to YUY2 or UYVY. bool can_scale = @@ -468,21 +468,32 @@ void VideoCapturer::OnFrameCaptured(VideoCapturer*, } VIDEO_FRAME_NAME i420_frame; - if (!i420_frame.Init(captured_frame, desired_width, desired_height)) { + if (!i420_frame.Alias(captured_frame, desired_width, desired_height)) { // TODO(fbarchard): LOG more information about captured frame attributes. LOG(LS_ERROR) << "Couldn't convert to I420! " << "From " << ToString(captured_frame) << " To " << desired_width << " x " << desired_height; return; } - if (!muted_ && !ApplyProcessors(&i420_frame)) { + + VideoFrame* adapted_frame = &i420_frame; + if (!SignalAdaptFrame.is_empty() && !IsScreencast()) { + VideoFrame* out_frame = NULL; + SignalAdaptFrame(this, adapted_frame, &out_frame); + if (!out_frame) { + return; // VideoAdapter dropped the frame. + } + adapted_frame = out_frame; + } + + if (!muted_ && !ApplyProcessors(adapted_frame)) { // Processor dropped the frame. return; } if (muted_) { - i420_frame.SetToBlack(); + adapted_frame->SetToBlack(); } - SignalVideoFrame(this, &i420_frame); + SignalVideoFrame(this, adapted_frame); #endif // VIDEO_FRAME_NAME } 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 933fc825000..2bd68bca162 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videocapturer.h +++ b/chromium/third_party/libjingle/source/talk/media/base/videocapturer.h @@ -255,6 +255,11 @@ 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. sigslot::signal2<VideoCapturer*, const VideoFrame*, sigslot::multi_threaded_local> SignalVideoFrame; 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 5dd45d73075..b051d526a3c 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videocommon.cc +++ b/chromium/third_party/libjingle/source/talk/media/base/videocommon.cc @@ -153,12 +153,15 @@ void ComputeScale(int frame_width, int frame_height, int fps, // Compute size to crop video frame to. // If cropped_format_* is 0, return the frame_* size as is. -void ComputeCrop(int cropped_format_width, - int cropped_format_height, +void ComputeCrop(int cropped_format_width, int cropped_format_height, int frame_width, int frame_height, int pixel_width, int pixel_height, int rotation, int* cropped_width, int* cropped_height) { + // Transform screen crop to camera space if rotated. + if (rotation == 90 || rotation == 270) { + std::swap(cropped_format_width, cropped_format_height); + } ASSERT(cropped_format_width >= 0); ASSERT(cropped_format_height >= 0); ASSERT(frame_width > 0); @@ -182,39 +185,26 @@ void ComputeCrop(int cropped_format_width, static_cast<float>(frame_height * pixel_height); float crop_aspect = static_cast<float>(cropped_format_width) / static_cast<float>(cropped_format_height); - int new_frame_width = frame_width; - int new_frame_height = frame_height; - if (rotation == 90 || rotation == 270) { - frame_aspect = 1.0f / frame_aspect; - new_frame_width = frame_height; - new_frame_height = frame_width; - } - // kAspectThresh is the maximum aspect ratio difference that we'll accept - // for cropping. The value 1.33 is based on 4:3 being cropped to 16:9. + // for cropping. The value 1.34 allows cropping from 4:3 to 16:9. // Set to zero to disable cropping entirely. // TODO(fbarchard): crop to multiple of 16 width for better performance. - const float kAspectThresh = 16.f / 9.f / (4.f / 3.f) + 0.01f; // 1.33 + const float kAspectThresh = 1.34f; // Wide aspect - crop horizontally if (frame_aspect > crop_aspect && frame_aspect < crop_aspect * kAspectThresh) { // Round width down to multiple of 4 to avoid odd chroma width. // Width a multiple of 4 allows a half size image to have chroma channel - // that avoids rounding errors. lmi and webrtc have odd width limitations. - new_frame_width = static_cast<int>((crop_aspect * frame_height * + // that avoids rounding errors. + frame_width = static_cast<int>((crop_aspect * frame_height * pixel_height) / pixel_width + 0.5f) & ~3; - } else if (crop_aspect > frame_aspect && - crop_aspect < frame_aspect * kAspectThresh) { - new_frame_height = static_cast<int>((frame_width * pixel_width) / + } else if (frame_aspect < crop_aspect && + frame_aspect > crop_aspect / kAspectThresh) { + frame_height = static_cast<int>((frame_width * pixel_width) / (crop_aspect * pixel_height) + 0.5f) & ~1; } - - *cropped_width = new_frame_width; - *cropped_height = new_frame_height; - if (rotation == 90 || rotation == 270) { - *cropped_width = new_frame_height; - *cropped_height = new_frame_width; - } + *cropped_width = frame_width; + *cropped_height = frame_height; } // Compute the frame size that makes pixels square pixel aspect ratio. 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 91228437351..455a47b79a0 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 @@ -276,6 +276,15 @@ TEST(VideoCommonTest, TestComputeCrop) { EXPECT_EQ(640, cropped_width); EXPECT_EQ(480, cropped_height); + // Request 9:16 from VGA rotated (portrait). Expect crop. + ComputeCrop(360, 640, // Crop size 9:16 + 640, 480, // Frame is 3:4 portrait + 1, 1, // Normal 1:1 pixels + 90, + &cropped_width, &cropped_height); + EXPECT_EQ(640, cropped_width); + EXPECT_EQ(360, cropped_height); + // Cropped size 0x0. Expect no cropping. // This is used when adding multiple capturers ComputeCrop(0, 0, // Crop size 0x0 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 d8b9bcb2955..d9266f2c4b4 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 @@ -54,6 +54,12 @@ EXPECT_EQ((h), (r).height()); \ EXPECT_EQ(0, (r).errors()); \ +#define EXPECT_GT_FRAME_ON_RENDERER_WAIT(r, c, w, h, t) \ + EXPECT_TRUE_WAIT((r).num_rendered_frames() >= (c) && \ + (w) == (r).width() && \ + (h) == (r).height(), (t)); \ + EXPECT_EQ(0, (r).errors()); \ + static const uint32 kTimeout = 5000U; static const uint32 kSsrc = 1234u; static const uint32 kRtxSsrc = 4321u; @@ -69,11 +75,13 @@ inline bool IsEqualCodec(const cricket::VideoCodec& a, IsEqualRes(a, b.width, b.height, b.framerate); } +namespace std { inline std::ostream& operator<<(std::ostream& s, const cricket::VideoCodec& c) { s << "{" << c.name << "(" << c.id << "), " << c.width << "x" << c.height << "x" << c.framerate << "}"; return s; } +} // namespace std inline int TimeBetweenSend(const cricket::VideoCodec& codec) { return static_cast<int>( @@ -788,9 +796,9 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_GT(info.senders[0].framerate_sent, 0); ASSERT_EQ(1U, info.receivers.size()); - EXPECT_EQ(1U, info.senders[0].ssrcs.size()); - EXPECT_EQ(1U, info.receivers[0].ssrcs.size()); - EXPECT_EQ(info.senders[0].ssrcs[0], info.receivers[0].ssrcs[0]); + EXPECT_EQ(1U, info.senders[0].ssrcs().size()); + EXPECT_EQ(1U, info.receivers[0].ssrcs().size()); + EXPECT_EQ(info.senders[0].ssrcs()[0], info.receivers[0].ssrcs()[0]); EXPECT_EQ(NumRtpBytes(), info.receivers[0].bytes_rcvd); EXPECT_EQ(NumRtpPackets(), info.receivers[0].packets_rcvd); EXPECT_EQ(0.0, info.receivers[0].fraction_lost); @@ -847,8 +855,8 @@ class VideoMediaChannelTest : public testing::Test, ASSERT_EQ(2U, info.receivers.size()); for (size_t i = 0; i < info.receivers.size(); ++i) { - EXPECT_EQ(1U, info.receivers[i].ssrcs.size()); - EXPECT_EQ(i + 1, info.receivers[i].ssrcs[0]); + EXPECT_EQ(1U, info.receivers[i].ssrcs().size()); + 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); @@ -903,12 +911,12 @@ class VideoMediaChannelTest : public testing::Test, 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(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(1U, info.senders[1].ssrcs.size()); - EXPECT_EQ(5678U, info.senders[1].ssrcs[0]); + 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); // The capturer must be unregistered here as it runs out of it's scope next. @@ -973,7 +981,7 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_TRUE(SetSend(true)); EXPECT_TRUE(channel_->SetRender(true)); EXPECT_EQ(0, renderer_.num_rendered_frames()); - channel_->OnPacketReceived(&packet1); + channel_->OnPacketReceived(&packet1, talk_base::PacketTime()); SetRendererAsDefault(); EXPECT_TRUE(SendFrame()); EXPECT_FRAME_WAIT(1, DefaultCodec().width, DefaultCodec().height, kTimeout); @@ -1000,7 +1008,8 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_GE(2, NumRtpPackets()); uint32 ssrc = 0; size_t last_packet = NumRtpPackets() - 1; - talk_base::scoped_ptr<const talk_base::Buffer> p(GetRtpPacket(last_packet)); + talk_base::scoped_ptr<const talk_base::Buffer> + p(GetRtpPacket(static_cast<int>(last_packet))); ParseRtpPacket(p.get(), NULL, NULL, NULL, NULL, &ssrc, NULL); EXPECT_EQ(kSsrc, ssrc); @@ -1017,7 +1026,7 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_TRUE_WAIT(NumRtpPackets() > rtp_packets, kTimeout); last_packet = NumRtpPackets() - 1; - p.reset(GetRtpPacket(last_packet)); + p.reset(GetRtpPacket(static_cast<int>(last_packet))); ParseRtpPacket(p.get(), NULL, NULL, NULL, NULL, &ssrc, NULL); EXPECT_EQ(789u, ssrc); } @@ -1165,7 +1174,11 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_TRUE(channel_->SetRenderer(kSsrc, &renderer1)); EXPECT_TRUE(SendFrame()); - EXPECT_FRAME_ON_RENDERER_WAIT( + // Because the default channel is used, RemoveRecvStream above is not going + // to delete the channel. As a result the engine will continue to receive + // and decode the 3 frames sent above. So it is possible we will receive + // 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); } @@ -1277,16 +1290,15 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_FRAME_WAIT(1, 640, 400, kTimeout); // Remove the capturer. EXPECT_TRUE(channel_->SetCapturer(kSsrc, NULL)); + // Wait for one black frame for removing the capturer. + EXPECT_FRAME_WAIT(2, 640, 400, kTimeout); + // No capturer was added, so this RemoveCapturer should // fail. EXPECT_FALSE(channel_->SetCapturer(kSsrc, NULL)); - // Wait for frames to stop flowing. talk_base::Thread::Current()->ProcessMessages(300); - int num_frames = renderer_.num_rendered_frames(); - // Wait to make sure no more frames are sent - WAIT(renderer_.num_rendered_frames() != num_frames, 300); // Verify no more frames were sent. - EXPECT_EQ(num_frames, renderer_.num_rendered_frames()); + EXPECT_EQ(2, renderer_.num_rendered_frames()); } // Tests that we can add and remove capturer as unique sources. @@ -1373,10 +1385,8 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_EQ(0, renderer.num_rendered_frames()); EXPECT_TRUE(SendFrame()); - EXPECT_TRUE_WAIT(renderer.num_rendered_frames() >= 1 && - codec.width == renderer.width() && - codec.height == renderer.height(), kTimeout); - EXPECT_EQ(0, renderer.errors()); + EXPECT_GT_FRAME_ON_RENDERER_WAIT( + renderer, 1, codec.width, codec.height, kTimeout); // Registering an external capturer is currently the same as screen casting // (update the test when this changes). @@ -1394,9 +1404,8 @@ class VideoMediaChannelTest : public testing::Test, EXPECT_TRUE(capturer->CaptureCustomFrame(kWidth, kHeight, cricket::FOURCC_ARGB)); EXPECT_TRUE(capturer->CaptureFrame()); - EXPECT_TRUE_WAIT(renderer.num_rendered_frames() >= 2 && - kScaledWidth == renderer.width() && - kScaledHeight == renderer.height(), kTimeout); + EXPECT_GT_FRAME_ON_RENDERER_WAIT( + renderer, 2, kScaledWidth, kScaledHeight, kTimeout); EXPECT_TRUE(channel_->SetCapturer(kSsrc, NULL)); } diff --git a/chromium/third_party/libjingle/source/talk/media/base/videoframe_unittest.h b/chromium/third_party/libjingle/source/talk/media/base/videoframe_unittest.h index f70e5675b01..d7be7e38875 100644 --- a/chromium/third_party/libjingle/source/talk/media/base/videoframe_unittest.h +++ b/chromium/third_party/libjingle/source/talk/media/base/videoframe_unittest.h @@ -157,7 +157,7 @@ class VideoFrameTest : public testing::Test { prefix.c_str(), frame.GetWidth(), frame.GetHeight()); size_t out_size = cricket::VideoFrame::SizeOf(frame.GetWidth(), frame.GetHeight()); - talk_base::scoped_array<uint8> out(new uint8[out_size]); + talk_base::scoped_ptr<uint8[]> out(new uint8[out_size]); frame.CopyToBuffer(out.get(), out_size); return DumpSample(filename, out.get(), out_size); } @@ -514,7 +514,7 @@ class VideoFrameTest : public testing::Test { T frame1, frame2; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); size_t buf_size = kWidth * kHeight * 2; - talk_base::scoped_array<uint8> buf(new uint8[buf_size + kAlignment]); + talk_base::scoped_ptr<uint8[]> buf(new uint8[buf_size + kAlignment]); uint8* y = ALIGNP(buf.get(), kAlignment); uint8* u = y + kWidth * kHeight; uint8* v = u + (kWidth / 2) * kHeight; @@ -527,7 +527,7 @@ class VideoFrameTest : public testing::Test { kWidth, kHeight)); EXPECT_TRUE(LoadFrame(y, buf_size, cricket::FOURCC_I422, kWidth, kHeight, &frame2)); - EXPECT_TRUE(IsEqual(frame1, frame2, 0)); + EXPECT_TRUE(IsEqual(frame1, frame2, 1)); } // Test constructing an image from a YUY2 buffer. @@ -535,7 +535,7 @@ class VideoFrameTest : public testing::Test { T frame1, frame2; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); size_t buf_size = kWidth * kHeight * 2; - talk_base::scoped_array<uint8> buf(new uint8[buf_size + kAlignment]); + talk_base::scoped_ptr<uint8[]> buf(new uint8[buf_size + kAlignment]); uint8* yuy2 = ALIGNP(buf.get(), kAlignment); EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.GetYPlane(), frame1.GetYPitch(), frame1.GetUPlane(), frame1.GetUPitch(), @@ -552,7 +552,7 @@ class VideoFrameTest : public testing::Test { T frame1, frame2; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); size_t buf_size = kWidth * kHeight * 2; - talk_base::scoped_array<uint8> buf(new uint8[buf_size + kAlignment + 1]); + talk_base::scoped_ptr<uint8[]> buf(new uint8[buf_size + kAlignment + 1]); uint8* yuy2 = ALIGNP(buf.get(), kAlignment) + 1; EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.GetYPlane(), frame1.GetYPitch(), frame1.GetUPlane(), frame1.GetUPitch(), @@ -718,7 +718,7 @@ class VideoFrameTest : public testing::Test { void ConstructRGB565() { T frame1, frame2; size_t out_size = kWidth * kHeight * 2; - talk_base::scoped_array<uint8> outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr<uint8[]> outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); T frame; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); @@ -734,7 +734,7 @@ class VideoFrameTest : public testing::Test { void ConstructARGB1555() { T frame1, frame2; size_t out_size = kWidth * kHeight * 2; - talk_base::scoped_array<uint8> outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr<uint8[]> outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); T frame; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); @@ -750,7 +750,7 @@ class VideoFrameTest : public testing::Test { void ConstructARGB4444() { T frame1, frame2; size_t out_size = kWidth * kHeight * 2; - talk_base::scoped_array<uint8> outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr<uint8[]> outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); T frame; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); @@ -769,7 +769,7 @@ class VideoFrameTest : public testing::Test { #define TEST_BYR(NAME, BAYER) \ void NAME() { \ size_t bayer_size = kWidth * kHeight; \ - talk_base::scoped_array<uint8> bayerbuf(new uint8[ \ + talk_base::scoped_ptr<uint8[]> bayerbuf(new uint8[ \ bayer_size + kAlignment]); \ uint8 *bayer = ALIGNP(bayerbuf.get(), kAlignment); \ T frame1, frame2; \ @@ -994,7 +994,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ } // Convert back to ARGB. size_t out_size = 4; - talk_base::scoped_array<uint8> outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr<uint8[]> outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); EXPECT_EQ(out_size, frame.ConvertToRgbBuffer(cricket::FOURCC_ARGB, @@ -1031,7 +1031,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ } // Convert back to ARGB size_t out_size = 10 * 4; - talk_base::scoped_array<uint8> outbuf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr<uint8[]> outbuf(new uint8[out_size + kAlignment]); uint8 *out = ALIGNP(outbuf.get(), kAlignment); EXPECT_EQ(out_size, frame.ConvertToRgbBuffer(cricket::FOURCC_ARGB, @@ -1154,6 +1154,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ int size_adjust, bool expected_result) { T frame; talk_base::scoped_ptr<talk_base::MemoryStream> ms(LoadSample(name)); + ASSERT_TRUE(ms.get() != NULL); const uint8* sample = reinterpret_cast<const uint8*>(ms.get()->GetBuffer()); size_t sample_size; ms->GetSize(&sample_size); @@ -1162,7 +1163,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ // Allocate a buffer with end page aligned. const int kPadToHeapSized = 16 * 1024 * 1024; - talk_base::scoped_array<uint8> page_buffer( + talk_base::scoped_ptr<uint8[]> page_buffer( new uint8[((data_size + kPadToHeapSized + 4095) & ~4095)]); uint8* data_ptr = page_buffer.get(); if (!data_ptr) { @@ -1393,6 +1394,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ T frame1, frame2; talk_base::scoped_ptr<talk_base::MemoryStream> ms( LoadSample(kImageFilename)); + ASSERT_TRUE(ms.get() != NULL); size_t data_size; ms->GetSize(&data_size); EXPECT_TRUE(frame1.InitToBlack(kWidth, kHeight, 1, 1, 0, 0)); @@ -1427,7 +1429,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ int astride = kWidth * bpp + rowpad; size_t out_size = astride * kHeight; - talk_base::scoped_array<uint8> outbuf(new uint8[out_size + kAlignment + 1]); + talk_base::scoped_ptr<uint8[]> outbuf(new uint8[out_size + kAlignment + 1]); memset(outbuf.get(), 0, out_size + kAlignment + 1); uint8 *outtop = ALIGNP(outbuf.get(), kAlignment); uint8 *out = outtop; @@ -1841,7 +1843,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ void ConvertToI422Buffer() { T frame1, frame2; size_t out_size = kWidth * kHeight * 2; - talk_base::scoped_array<uint8> buf(new uint8[out_size + kAlignment]); + talk_base::scoped_ptr<uint8[]> buf(new uint8[out_size + kAlignment]); uint8* y = ALIGNP(buf.get(), kAlignment); uint8* u = y + kWidth * kHeight; uint8* v = u + (kWidth / 2) * kHeight; @@ -1859,13 +1861,13 @@ void Construct##FOURCC##Rotate##ROTATE() { \ kWidth, kHeight, kWidth, kHeight, y, out_size, 1, 1, 0, 0, cricket::ROTATION_0)); - EXPECT_TRUE(IsEqual(frame1, frame2, 0)); + EXPECT_TRUE(IsEqual(frame1, frame2, 1)); } #define TEST_TOBYR(NAME, BAYER) \ void NAME() { \ size_t bayer_size = kWidth * kHeight; \ - talk_base::scoped_array<uint8> bayerbuf(new uint8[ \ + talk_base::scoped_ptr<uint8[]> bayerbuf(new uint8[ \ bayer_size + kAlignment]); \ uint8 *bayer = ALIGNP(bayerbuf.get(), kAlignment); \ T frame; \ @@ -1894,7 +1896,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ } \ void NAME##Unaligned() { \ size_t bayer_size = kWidth * kHeight; \ - talk_base::scoped_array<uint8> bayerbuf(new uint8[ \ + talk_base::scoped_ptr<uint8[]> bayerbuf(new uint8[ \ bayer_size + 1 + kAlignment]); \ uint8 *bayer = ALIGNP(bayerbuf.get(), kAlignment) + 1; \ T frame; \ @@ -1931,7 +1933,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ #define TEST_BYRTORGB(NAME, BAYER) \ void NAME() { \ size_t bayer_size = kWidth * kHeight; \ - talk_base::scoped_array<uint8> bayerbuf(new uint8[ \ + talk_base::scoped_ptr<uint8[]> bayerbuf(new uint8[ \ bayer_size + kAlignment]); \ uint8 *bayer1 = ALIGNP(bayerbuf.get(), kAlignment); \ for (int i = 0; i < kWidth * kHeight; ++i) { \ @@ -1947,7 +1949,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ kWidth * 4, \ kWidth, kHeight); \ } \ - talk_base::scoped_array<uint8> bayer2buf(new uint8[ \ + talk_base::scoped_ptr<uint8[]> bayer2buf(new uint8[ \ bayer_size + kAlignment]); \ uint8 *bayer2 = ALIGNP(bayer2buf.get(), kAlignment); \ libyuv::ARGBToBayer##BAYER(reinterpret_cast<uint8*>(ms->GetBuffer()), \ @@ -2007,10 +2009,11 @@ void Construct##FOURCC##Rotate##ROTATE() { \ T frame; talk_base::scoped_ptr<talk_base::MemoryStream> ms( LoadSample(kImageFilename)); + ASSERT_TRUE(ms.get() != NULL); ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight, &frame)); size_t out_size = kWidth * kHeight * 3 / 2; - talk_base::scoped_array<uint8> out(new uint8[out_size]); + talk_base::scoped_ptr<uint8[]> out(new uint8[out_size]); for (int i = 0; i < repeat_; ++i) { EXPECT_EQ(out_size, frame.CopyToBuffer(out.get(), out_size)); } @@ -2021,6 +2024,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ T source; talk_base::scoped_ptr<talk_base::MemoryStream> ms( LoadSample(kImageFilename)); + ASSERT_TRUE(ms.get() != NULL); ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight, &source)); @@ -2039,6 +2043,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ T frame; talk_base::scoped_ptr<talk_base::MemoryStream> ms( LoadSample(kImageFilename)); + ASSERT_TRUE(ms.get() != NULL); talk_base::MemoryStream ms2; size_t size; ASSERT_TRUE(ms->GetSize(&size)); @@ -2056,7 +2061,7 @@ void Construct##FOURCC##Rotate##ROTATE() { \ void CopyToBuffer1Pixel() { size_t out_size = 3; - talk_base::scoped_array<uint8> out(new uint8[out_size + 1]); + talk_base::scoped_ptr<uint8[]> out(new uint8[out_size + 1]); memset(out.get(), 0xfb, out_size + 1); // Fill buffer uint8 pixel[3] = { 1, 2, 3 }; T frame; diff --git a/chromium/third_party/libjingle/source/talk/media/devices/carbonvideorenderer.h b/chromium/third_party/libjingle/source/talk/media/devices/carbonvideorenderer.h index e09118613ba..6c52fcfc69c 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/carbonvideorenderer.h +++ b/chromium/third_party/libjingle/source/talk/media/devices/carbonvideorenderer.h @@ -57,7 +57,7 @@ class CarbonVideoRenderer : public VideoRenderer { static OSStatus DrawEventHandler(EventHandlerCallRef handler, EventRef event, void* data); - talk_base::scoped_array<uint8> image_; + talk_base::scoped_ptr<uint8[]> image_; talk_base::CriticalSection image_crit_; int image_width_; int image_height_; 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 6f4aa33ffca..150b55855d9 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/devicemanager.cc @@ -278,18 +278,14 @@ VideoCapturer* DeviceManager::CreateDesktopCapturer( bool DeviceManager::GetAudioDevices(bool input, std::vector<Device>* devs) { devs->clear(); -#if defined(IOS) || defined(ANDROID) - // Under Android, we don't access the device file directly. - // Arbitrary use 0 for the mic and 1 for the output. - // These ids are used in MediaEngine::SetSoundDevices(in, out); - // The strings are for human consumption. - if (input) { - devs->push_back(Device("audiorecord", 0)); - } else { - devs->push_back(Device("audiotrack", 1)); - } +#if defined(ANDROID) + // Under Android, 0 is always required for the playout device and 0 is the + // default for the recording device. + devs->push_back(Device("default-device", 0)); return true; #else + // Other platforms either have their own derived class implementation + // (desktop) or don't use device manager for audio devices (iOS). return false; #endif } 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 174781624b1..f5c078d3fa4 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer.cc @@ -28,6 +28,7 @@ #include "talk/media/devices/filevideocapturer.h" #include "talk/base/bytebuffer.h" +#include "talk/base/criticalsection.h" #include "talk/base/logging.h" #include "talk/base/thread.h" @@ -108,6 +109,10 @@ class FileVideoCapturer::FileReadThread finished_(false) { } + virtual ~FileReadThread() { + 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 @@ -117,6 +122,8 @@ class FileVideoCapturer::FileReadThread PostDelayed(waiting_time_ms, this); Thread::Run(); } + + talk_base::CritScope cs(&crit_); finished_ = true; } @@ -131,10 +138,14 @@ class FileVideoCapturer::FileReadThread } // Check if Run() is finished. - bool Finished() const { return finished_; } + bool Finished() const { + talk_base::CritScope cs(&crit_); + return finished_; + } private: FileVideoCapturer* capturer_; + mutable talk_base::CriticalSection crit_; bool finished_; DISALLOW_COPY_AND_ASSIGN(FileReadThread); @@ -224,9 +235,9 @@ CaptureState FileVideoCapturer::Start(const VideoFormat& capture_format) { SetCaptureFormat(&capture_format); // Create a thread to read the file. file_read_thread_ = new FileReadThread(this); - bool ret = file_read_thread_->Start(); start_time_ns_ = kNumNanoSecsPerMilliSec * static_cast<int64>(talk_base::Time()); + bool ret = file_read_thread_->Start(); if (ret) { LOG(LS_INFO) << "File video capturer '" << GetId() << "' started"; return CS_RUNNING; diff --git a/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer_unittest.cc b/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer_unittest.cc index 489f98d451a..610d4f120aa 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/filevideocapturer_unittest.cc @@ -174,7 +174,8 @@ TEST_F(FileVideoCapturerTest, TestRepeatForever) { EXPECT_EQ(listener.frame_height(), capture_format_.height); } -TEST_F(FileVideoCapturerTest, TestPartialFrameHeader) { +// See: https://code.google.com/p/webrtc/issues/detail?id=2409 +TEST_F(FileVideoCapturerTest, DISABLED_TestPartialFrameHeader) { EXPECT_TRUE(OpenFile("1.frame_plus_1.byte")); VideoCapturerListener listener; capturer_->SignalFrameCaptured.connect( diff --git a/chromium/third_party/libjingle/source/talk/media/devices/gdivideorenderer.cc b/chromium/third_party/libjingle/source/talk/media/devices/gdivideorenderer.cc index d5edfc6e43c..9633eb6d8e8 100755 --- a/chromium/third_party/libjingle/source/talk/media/devices/gdivideorenderer.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/gdivideorenderer.cc @@ -69,6 +69,10 @@ class GdiVideoRenderer::VideoWindow : public talk_base::Win32Window { public: explicit WindowThread(VideoWindow* window) : window_(window) {} + virtual ~WindowThread() { + Stop(); + } + // Override virtual method of talk_base::Thread. Context: worker Thread. virtual void Run() { // Initialize the window @@ -94,7 +98,7 @@ class GdiVideoRenderer::VideoWindow : public talk_base::Win32Window { void OnRenderFrame(const VideoFrame* frame); BITMAPINFO bmi_; - talk_base::scoped_array<uint8> image_; + talk_base::scoped_ptr<uint8[]> image_; talk_base::scoped_ptr<WindowThread> window_thread_; // The initial position of the window. int initial_x_; diff --git a/chromium/third_party/libjingle/source/talk/media/devices/gtkvideorenderer.h b/chromium/third_party/libjingle/source/talk/media/devices/gtkvideorenderer.h index 6276b51c806..744c19f4908 100755 --- a/chromium/third_party/libjingle/source/talk/media/devices/gtkvideorenderer.h +++ b/chromium/third_party/libjingle/source/talk/media/devices/gtkvideorenderer.h @@ -56,7 +56,7 @@ class GtkVideoRenderer : public VideoRenderer { // Check if the window has been closed. bool IsClosed() const; - talk_base::scoped_array<uint8> image_; + talk_base::scoped_ptr<uint8[]> image_; GtkWidget* window_; GtkWidget* draw_area_; // The initial position of the window. diff --git a/chromium/third_party/libjingle/source/talk/media/devices/libudevsymboltable.cc b/chromium/third_party/libjingle/source/talk/media/devices/libudevsymboltable.cc index b1d9d31e645..20154e1fcad 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/libudevsymboltable.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/libudevsymboltable.cc @@ -27,6 +27,10 @@ #include "talk/media/devices/libudevsymboltable.h" +#include <dlfcn.h> + +#include "talk/base/logging.h" + namespace cricket { #define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME LIBUDEV_SYMBOLS_CLASS_NAME @@ -37,4 +41,32 @@ namespace cricket { #undef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST #undef LATE_BINDING_SYMBOL_TABLE_DLL_NAME +bool IsWrongLibUDevAbiVersion(talk_base::DllHandle libudev_0) { + talk_base::DllHandle libudev_1 = dlopen("libudev.so.1", + RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD); + bool unsafe_symlink = (libudev_0 == libudev_1); + if (unsafe_symlink) { + // .0 and .1 are distinct ABIs, so if they point to the same thing then one + // of them must be wrong. Probably the old has been symlinked to the new in + // a misguided attempt at backwards compatibility. + LOG(LS_ERROR) << "libudev.so.0 and libudev.so.1 unsafely point to the" + " same thing; not using libudev"; + } else if (libudev_1) { + // If libudev.so.1 is resident but distinct from libudev.so.0, then some + // system library loaded the new ABI separately. This is not a problem for + // LateBindingSymbolTable because its symbol look-ups are restricted to its + // DllHandle, but having libudev.so.0 resident may cause problems for that + // system library because symbol names are not namespaced by DLL. (Although + // our use of RTLD_LOCAL should avoid most problems.) + LOG(LS_WARNING) + << "libudev.so.1 is resident but distinct from libudev.so.0"; + } + if (libudev_1) { + // Release the refcount that we acquired above. (Does not unload the DLL; + // whoever loaded it still needs it.) + dlclose(libudev_1); + } + return unsafe_symlink; +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/devices/libudevsymboltable.h b/chromium/third_party/libjingle/source/talk/media/devices/libudevsymboltable.h index 046a4b0e88b..aa8c590351e 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/libudevsymboltable.h +++ b/chromium/third_party/libjingle/source/talk/media/devices/libudevsymboltable.h @@ -66,6 +66,14 @@ namespace cricket { #undef LATE_BINDING_SYMBOL_TABLE_CLASS_NAME #undef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST +// libudev has changed ABIs to libudev.so.1 in recent distros and lots of users +// and/or software (including Google Chrome) are symlinking the old to the new. +// The entire point of ABI versions is that you can't safely do that, and +// it has caused crashes in the wild. This function checks if the DllHandle that +// we got back for libudev.so.0 is actually for libudev.so.1. If so, the library +// cannot safely be used. +bool IsWrongLibUDevAbiVersion(talk_base::DllHandle libudev_0); + } // namespace cricket #endif // TALK_MEDIA_DEVICES_LIBUDEVSYMBOLTABLE_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/devices/linuxdeviceinfo.cc b/chromium/third_party/libjingle/source/talk/media/devices/linuxdeviceinfo.cc index 7b5200112c6..b1bc9ddb1f1 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/linuxdeviceinfo.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/linuxdeviceinfo.cc @@ -52,7 +52,8 @@ class ScopedLibUdev { ScopedLibUdev() {} bool Init() { - return libudev_.Load(); + return libudev_.Load() && + !IsWrongLibUDevAbiVersion(libudev_.GetDllHandle()); } LibUDevSymbolTable libudev_; 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 2096eeb274a..e3e55ff7959 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/linuxdevicemanager.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/linuxdevicemanager.cc @@ -306,8 +306,9 @@ 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, // and we don't want to halt LinuxDeviceManager initialization in such a case. - if (!libudev_.Load()) { - LOG(LS_WARNING) << "libudev not present/usable; LinuxDeviceWatcher disabled"; + if (!libudev_.Load() || IsWrongLibUDevAbiVersion(libudev_.GetDllHandle())) { + LOG(LS_WARNING) + << "libudev not present/usable; LinuxDeviceWatcher disabled"; return true; } udev_ = libudev_.udev_new()(); 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 10d85a0f737..e92408e416c 100644 --- a/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanager.cc +++ b/chromium/third_party/libjingle/source/talk/media/devices/macdevicemanager.cc @@ -120,7 +120,7 @@ static bool GetAudioDeviceIDs(bool input, } size_t num_devices = propsize / sizeof(AudioDeviceID); - talk_base::scoped_array<AudioDeviceID> device_ids( + talk_base::scoped_ptr<AudioDeviceID[]> device_ids( new AudioDeviceID[num_devices]); err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, 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 3d450c6863b..653273bd2e3 100644 --- a/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.cc @@ -31,12 +31,14 @@ #include <stdio.h> #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/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 cricket { @@ -242,8 +244,8 @@ DataMediaChannel* SctpDataEngine::CreateChannel( SctpDataMediaChannel::SctpDataMediaChannel(talk_base::Thread* thread) : worker_thread_(thread), - local_port_(kSctpDefaultPort), - remote_port_(kSctpDefaultPort), + local_port_(-1), + remote_port_(-1), sock_(NULL), sending_(false), receiving_(false), @@ -344,6 +346,12 @@ 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_) { @@ -534,7 +542,8 @@ bool SctpDataMediaChannel::SendData( } // Called by network interface when a packet has been received. -void SctpDataMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { +void SctpDataMediaChannel::OnPacketReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): " << " length=" << packet->length() << ", sending: " << sending_; // Only give receiving packets to usrsctp after if connected. This enables two @@ -578,7 +587,23 @@ void SctpDataMediaChannel::OnDataFromSctpToChannel( StreamParams found_stream; if (!GetStreamBySsrc(streams_, params.ssrc, &found_stream)) { if (params.type == DMT_CONTROL) { - SignalDataReceived(params, buffer->data(), buffer->length()); + 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; @@ -677,6 +702,38 @@ void SctpDataMediaChannel::OnNotificationAssocChange( } } +// 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. +static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs, + int id, const std::string& name, + const std::string& param, int* dest) { + std::string value; + Codec match_pattern; + match_pattern.id = id; + match_pattern.name = name; + for (size_t i = 0; i < codecs.size(); ++i) { + if (codecs[i].Matches(match_pattern)) { + if (codecs[i].GetParam(param, &value)) { + *dest = talk_base::FromString<int>(value); + return true; + } + } + } + return false; +} + +bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) { + return GetCodecIntParameter( + codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort, + &remote_port_); +} + +bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) { + return GetCodecIntParameter( + codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort, + &local_port_); +} void SctpDataMediaChannel::OnPacketFromSctpToNetwork( talk_base::Buffer* buffer) { 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 429016e0e6a..4d05cf36e02 100644 --- a/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.h +++ b/chromium/third_party/libjingle/source/talk/media/sctp/sctpdataengine.h @@ -56,7 +56,7 @@ 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 = USHRT_MAX; +const uint32 kMaxSctpSid = 1023; // A DataEngine that interacts with usrsctp. // @@ -149,7 +149,8 @@ class SctpDataMediaChannel : public DataMediaChannel, const talk_base::Buffer& payload, SendDataResult* result = NULL); // A packet is received from the network interface. Posted to OnMessage. - virtual void OnPacketReceived(talk_base::Buffer* packet); + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); // Exposed to allow Post call from c-callbacks. talk_base::Thread* worker_thread() const { return worker_thread_; } @@ -168,13 +169,10 @@ class SctpDataMediaChannel : public DataMediaChannel, const std::vector<RtpHeaderExtension>& extensions) { return true; } virtual bool SetSendRtpHeaderExtensions( const std::vector<RtpHeaderExtension>& extensions) { return true; } - virtual bool SetSendCodecs(const std::vector<DataCodec>& codecs) { - return true; - } - virtual bool SetRecvCodecs(const std::vector<DataCodec>& codecs) { - return true; - } - virtual void OnRtcpReceived(talk_base::Buffer* packet) {} + virtual bool SetSendCodecs(const std::vector<DataCodec>& codecs); + virtual bool SetRecvCodecs(const std::vector<DataCodec>& codecs); + virtual void OnRtcpReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) {} virtual void OnReadyToSend(bool ready) {} // Helper for debugging. @@ -211,7 +209,8 @@ class SctpDataMediaChannel : public DataMediaChannel, talk_base::Thread* worker_thread_; // The local and remote SCTP port to use. These are 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. + // related to the ports at the IP level. If set to -1, we default to + // kSctpDefaultPort. int local_port_; int remote_port_; struct socket* sock_; // The socket created by usrsctp_socket(...). 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 363e7dfeb64..b4ad6ce33e2 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 @@ -30,6 +30,7 @@ #include <stdio.h> #include <string> +#include "talk/app/webrtc/datachannelinterface.h" #include "talk/base/buffer.h" #include "talk/base/criticalsection.h" #include "talk/base/gunit.h" @@ -41,6 +42,7 @@ #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, @@ -82,7 +84,7 @@ class SctpFakeNetworkInterface : public cricket::MediaChannel::NetworkInterface, static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>( msg->pdata)->data(); if (dest_) { - dest_->OnPacketReceived(buffer); + dest_->OnPacketReceived(buffer, talk_base::PacketTime()); } delete buffer; } @@ -161,7 +163,8 @@ class SignalReadyToSendObserver : public sigslot::has_slots<> { }; // SCTP Data Engine testing framework. -class SctpDataMediaChannelTest : public testing::Test { +class SctpDataMediaChannelTest : public testing::Test, + public sigslot::has_slots<> { protected: virtual void SetUp() { engine_.reset(new cricket::SctpDataEngine()); @@ -212,6 +215,8 @@ 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; } @@ -246,6 +251,14 @@ 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_; @@ -254,6 +267,8 @@ 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. @@ -316,3 +331,30 @@ TEST_F(SctpDataMediaChannelTest, SendData) { channel2()->SetSend(false); LOG(LS_VERBOSE) << "Cleaning up. -----------------------------"; } + +TEST_F(SctpDataMediaChannelTest, SendReceiveOpenMessage) { + SetupConnectedChannels(); + + std::string label("x"); + webrtc::DataChannelInit config; + config.id = 10; + + // 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; + 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); +} diff --git a/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.cc b/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.cc new file mode 100644 index 00000000000..4073905de7e --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.cc @@ -0,0 +1,176 @@ +/* + * 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. + */ + +#include "talk/media/sctp/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 { + +// Format defined at +// http://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-00#section-6.1 + +static const uint8 DATA_CHANNEL_OPEN_MESSAGE_TYPE = 0x03; + +enum DataChannelOpenMessageChannelType { + DCOMCT_ORDERED_RELIABLE = 0x00, + DCOMCT_ORDERED_PARTIAL_RTXS = 0x01, + DCOMCT_ORDERED_PARTIAL_TIME = 0x02, + DCOMCT_UNORDERED_RELIABLE = 0x80, + DCOMCT_UNORDERED_PARTIAL_RTXS = 0x81, + DCOMCT_UNORDERED_PARTIAL_TIME = 0x82, +}; + +bool ParseDataChannelOpenMessage( + const talk_base::Buffer& payload, + std::string* label, + webrtc::DataChannelInit* config) { + // Format defined at + // http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04 + + talk_base::ByteBuffer buffer(payload.data(), payload.length()); + + uint8 message_type; + if (!buffer.ReadUInt8(&message_type)) { + LOG(LS_WARNING) << "Could not read OPEN message type."; + return false; + } + if (message_type != DATA_CHANNEL_OPEN_MESSAGE_TYPE) { + LOG(LS_WARNING) << "Data Channel OPEN message of unexpected type: " + << message_type; + return false; + } + + uint8 channel_type; + if (!buffer.ReadUInt8(&channel_type)) { + LOG(LS_WARNING) << "Could not read OPEN message channel type."; + return false; + } + + uint16 priority; + if (!buffer.ReadUInt16(&priority)) { + LOG(LS_WARNING) << "Could not read OPEN message reliabilility prioirty."; + return false; + } + uint32 reliability_param; + if (!buffer.ReadUInt32(&reliability_param)) { + LOG(LS_WARNING) << "Could not read OPEN message reliabilility param."; + return false; + } + uint16 label_length; + if (!buffer.ReadUInt16(&label_length)) { + LOG(LS_WARNING) << "Could not read OPEN message label length."; + return false; + } + uint16 protocol_length; + if (!buffer.ReadUInt16(&protocol_length)) { + LOG(LS_WARNING) << "Could not read OPEN message protocol length."; + return false; + } + if (!buffer.ReadString(label, (size_t) label_length)) { + LOG(LS_WARNING) << "Could not read OPEN message label"; + return false; + } + if (!buffer.ReadString(&config->protocol, protocol_length)) { + LOG(LS_WARNING) << "Could not read OPEN message protocol."; + return false; + } + + config->ordered = true; + switch (channel_type) { + case DCOMCT_UNORDERED_RELIABLE: + case DCOMCT_UNORDERED_PARTIAL_RTXS: + case DCOMCT_UNORDERED_PARTIAL_TIME: + config->ordered = false; + } + + config->maxRetransmits = -1; + config->maxRetransmitTime = -1; + switch (channel_type) { + case DCOMCT_ORDERED_PARTIAL_RTXS: + case DCOMCT_UNORDERED_PARTIAL_RTXS: + config->maxRetransmits = reliability_param; + break; + case DCOMCT_ORDERED_PARTIAL_TIME: + case DCOMCT_UNORDERED_PARTIAL_TIME: + config->maxRetransmitTime = reliability_param; + break; + } + + return true; +} + +bool WriteDataChannelOpenMessage( + const std::string& label, + const webrtc::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; + uint32 reliability_param = 0; + uint16 priority = 0; + if (config.ordered) { + if (config.maxRetransmits > -1) { + channel_type = DCOMCT_ORDERED_PARTIAL_RTXS; + reliability_param = config.maxRetransmits; + } else if (config.maxRetransmitTime > -1) { + channel_type = DCOMCT_ORDERED_PARTIAL_TIME; + reliability_param = config.maxRetransmitTime; + } else { + channel_type = DCOMCT_ORDERED_RELIABLE; + } + } else { + if (config.maxRetransmits > -1) { + channel_type = DCOMCT_UNORDERED_PARTIAL_RTXS; + reliability_param = config.maxRetransmits; + } else if (config.maxRetransmitTime > -1) { + channel_type = DCOMCT_UNORDERED_PARTIAL_TIME; + reliability_param = config.maxRetransmitTime; + } else { + channel_type = DCOMCT_UNORDERED_RELIABLE; + } + } + + talk_base::ByteBuffer buffer( + NULL, 20 + label.length() + config.protocol.length(), + talk_base::ByteBuffer::ORDER_NETWORK); + buffer.WriteUInt8(DATA_CHANNEL_OPEN_MESSAGE_TYPE); + buffer.WriteUInt8(channel_type); + buffer.WriteUInt16(priority); + buffer.WriteUInt32(reliability_param); + buffer.WriteUInt16(static_cast<uint16>(label.length())); + buffer.WriteUInt16(static_cast<uint16>(config.protocol.length())); + buffer.WriteString(label); + buffer.WriteString(config.protocol); + payload->SetData(buffer.Data(), buffer.Length()); + return true; +} + +} // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.h b/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.h new file mode 100644 index 00000000000..d349274a9a6 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/sctp/sctputils.h @@ -0,0 +1,53 @@ +/* + * 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_MEDIA_BASE_SCTPUTILS_H_ +#define TALK_MEDIA_BASE_SCTPUTILS_H_ + +#include <string> + +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); + +bool WriteDataChannelOpenMessage(const std::string& label, + const webrtc::DataChannelInit& config, + talk_base::Buffer* payload); + +} // namespace cricket + +#endif // TALK_MEDIA_BASE_SCTPUTILS_H_ diff --git a/chromium/third_party/libjingle/source/talk/media/sctp/sctputils_unittest.cc b/chromium/third_party/libjingle/source/talk/media/sctp/sctputils_unittest.cc new file mode 100644 index 00000000000..70f67b8b581 --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/sctp/sctputils_unittest.cc @@ -0,0 +1,153 @@ +/* + * 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. + */ + +#include "talk/app/webrtc/datachannelinterface.h" +#include "talk/base/bytebuffer.h" +#include "talk/base/gunit.h" +#include "talk/media/sctp/sctputils.h" + +class SctpUtilsTest : public testing::Test { + public: + void VerifyOpenMessageFormat(const talk_base::Buffer& packet, + const std::string& label, + const webrtc::DataChannelInit& config) { + uint8 message_type; + uint8 channel_type; + uint32 reliability; + uint16 priority; + uint16 label_length; + uint16 protocol_length; + + talk_base::ByteBuffer buffer(packet.data(), packet.length()); + ASSERT_TRUE(buffer.ReadUInt8(&message_type)); + EXPECT_EQ(0x03, message_type); + + ASSERT_TRUE(buffer.ReadUInt8(&channel_type)); + if (config.ordered) { + EXPECT_EQ(config.maxRetransmits > -1 ? + 0x01 : (config.maxRetransmitTime > -1 ? 0x02 : 0), + channel_type); + } else { + EXPECT_EQ(config.maxRetransmits > -1 ? + 0x81 : (config.maxRetransmitTime > -1 ? 0x82 : 0x80), + channel_type); + } + + ASSERT_TRUE(buffer.ReadUInt16(&priority)); + + ASSERT_TRUE(buffer.ReadUInt32(&reliability)); + if (config.maxRetransmits > -1 || config.maxRetransmitTime > -1) { + EXPECT_EQ(config.maxRetransmits > -1 ? + config.maxRetransmits : config.maxRetransmitTime, + static_cast<int>(reliability)); + } + + ASSERT_TRUE(buffer.ReadUInt16(&label_length)); + ASSERT_TRUE(buffer.ReadUInt16(&protocol_length)); + EXPECT_EQ(label.size(), label_length); + EXPECT_EQ(config.protocol.size(), protocol_length); + + std::string label_output; + ASSERT_TRUE(buffer.ReadString(&label_output, label_length)); + EXPECT_EQ(label, label_output); + std::string protocol_output; + ASSERT_TRUE(buffer.ReadString(&protocol_output, protocol_length)); + EXPECT_EQ(config.protocol, protocol_output); + } +}; + +TEST_F(SctpUtilsTest, WriteParseMessageWithOrderedReliable) { + std::string input_label = "abc"; + webrtc::DataChannelInit config; + config.protocol = "y"; + + talk_base::Buffer packet; + ASSERT_TRUE( + cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); + + VerifyOpenMessageFormat(packet, input_label, config); + + std::string output_label; + webrtc::DataChannelInit output_config; + ASSERT_TRUE(cricket::ParseDataChannelOpenMessage( + packet, &output_label, &output_config)); + + EXPECT_EQ(input_label, output_label); + EXPECT_EQ(config.protocol, output_config.protocol); + EXPECT_EQ(config.ordered, output_config.ordered); + EXPECT_EQ(config.maxRetransmitTime, output_config.maxRetransmitTime); + EXPECT_EQ(config.maxRetransmits, output_config.maxRetransmits); +} + +TEST_F(SctpUtilsTest, WriteParseOpenMessageWithMaxRetransmitTime) { + std::string input_label = "abc"; + webrtc::DataChannelInit config; + config.ordered = false; + config.maxRetransmitTime = 10; + config.protocol = "y"; + + talk_base::Buffer packet; + ASSERT_TRUE( + cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); + + VerifyOpenMessageFormat(packet, input_label, config); + + std::string output_label; + webrtc::DataChannelInit output_config; + ASSERT_TRUE(cricket::ParseDataChannelOpenMessage( + packet, &output_label, &output_config)); + + EXPECT_EQ(input_label, output_label); + EXPECT_EQ(config.protocol, output_config.protocol); + EXPECT_EQ(config.ordered, output_config.ordered); + EXPECT_EQ(config.maxRetransmitTime, output_config.maxRetransmitTime); + EXPECT_EQ(-1, output_config.maxRetransmits); +} + +TEST_F(SctpUtilsTest, WriteParseOpenMessageWithMaxRetransmits) { + std::string input_label = "abc"; + webrtc::DataChannelInit config; + config.maxRetransmits = 10; + config.protocol = "y"; + + talk_base::Buffer packet; + ASSERT_TRUE( + cricket::WriteDataChannelOpenMessage(input_label, config, &packet)); + + VerifyOpenMessageFormat(packet, input_label, config); + + std::string output_label; + webrtc::DataChannelInit output_config; + ASSERT_TRUE(cricket::ParseDataChannelOpenMessage( + packet, &output_label, &output_config)); + + EXPECT_EQ(input_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); +} diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/dummyinstantiation.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/dummyinstantiation.cc new file mode 100644 index 00000000000..7d27967099b --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/dummyinstantiation.cc @@ -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. + */ + +#include "talk/media/webrtc/fakewebrtcvideoengine.h" +#include "talk/media/webrtc/fakewebrtcvoiceengine.h" + +static void EnsureAPIMatch() { + new cricket::FakeWebRtcVoiceEngine(NULL, 0); + new cricket::FakeWebRtcVideoDecoder(); + new cricket::FakeWebRtcVideoEncoder(); + new cricket::FakeWebRtcVideoEngine(NULL, 0); + new cricket::FakeWebRtcVideoEngine::Capturer(); +} 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 31de172a211..bb75c2a7feb 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideoengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvideoengine.h @@ -33,6 +33,7 @@ #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/webrtc/fakewebrtccommon.h" @@ -276,6 +277,8 @@ class FakeWebRtcVideoEngine send(false), receive_(false), can_transmit_(true), + remote_rtx_ssrc_(-1), + rtx_send_payload_type(-1), rtcp_status_(webrtc::kRtcpNone), key_frame_request_method_(webrtc::kViEKeyFrameRequestNone), tmmbr_(false), @@ -306,6 +309,9 @@ class FakeWebRtcVideoEngine bool receive_; bool can_transmit_; std::map<int, int> ssrcs_; + std::map<int, int> rtx_ssrcs_; + int remote_rtx_ssrc_; + int rtx_send_payload_type; std::string cname_; webrtc::ViERTCPMode rtcp_status_; webrtc::ViEKeyFrameRequestMethod key_frame_request_method_; @@ -333,12 +339,14 @@ class FakeWebRtcVideoEngine }; class Capturer : public webrtc::ViEExternalCapture { public: - Capturer() : channel_id_(-1), denoising_(false), last_capture_time_(0) { } + Capturer() : channel_id_(-1), denoising_(false), + last_capture_time_(0), incoming_frame_num_(0) { } int channel_id() const { return channel_id_; } void set_channel_id(int channel_id) { channel_id_ = channel_id; } bool denoising() const { return denoising_; } void set_denoising(bool denoising) { denoising_ = denoising; } - int64 last_capture_time() { return last_capture_time_; } + int64 last_capture_time() const { return last_capture_time_; } + int incoming_frame_num() const { return incoming_frame_num_; } // From ViEExternalCapture virtual int IncomingFrame(unsigned char* videoFrame, @@ -353,6 +361,7 @@ class FakeWebRtcVideoEngine const webrtc::ViEVideoFrameI420& video_frame, unsigned long long captureTime) { last_capture_time_ = captureTime; + ++incoming_frame_num_; return 0; } @@ -360,6 +369,7 @@ class FakeWebRtcVideoEngine int channel_id_; bool denoising_; int64 last_capture_time_; + int incoming_frame_num_; }; FakeWebRtcVideoEngine(const cricket::VideoCodec* const* codecs, @@ -402,6 +412,16 @@ class FakeWebRtcVideoEngine int GetLastCapturer() const { return last_capturer_; } int GetNumCapturers() const { return static_cast<int>(capturers_.size()); } + int GetIncomingFrameNum(int channel_id) const { + for (std::map<int, Capturer*>::const_iterator iter = capturers_.begin(); + iter != capturers_.end(); ++iter) { + Capturer* capturer = iter->second; + if (capturer->channel_id() == channel_id) { + return capturer->incoming_frame_num(); + } + } + return -1; + } void set_fail_alloc_capturer(bool fail_alloc_capturer) { fail_alloc_capturer_ = fail_alloc_capturer; } @@ -500,10 +520,23 @@ class FakeWebRtcVideoEngine return static_cast<int>( channels_.find(channel)->second->ssrcs_.size()); } + int GetNumRtxSsrcs(int channel) const { + WEBRTC_ASSERT_CHANNEL(channel); + return static_cast<int>( + channels_.find(channel)->second->rtx_ssrcs_.size()); + } bool GetIsTransmitting(int channel) const { WEBRTC_ASSERT_CHANNEL(channel); return channels_.find(channel)->second->can_transmit_; } + int GetRtxSsrc(int channel, int simulcast_idx) const { + WEBRTC_ASSERT_CHANNEL(channel); + if (channels_.find(channel)->second->rtx_ssrcs_.find(simulcast_idx) == + channels_.find(channel)->second->rtx_ssrcs_.end()) { + return -1; + } + return channels_.find(channel)->second->rtx_ssrcs_[simulcast_idx]; + } bool ReceiveCodecRegistered(int channel, const webrtc::VideoCodec& codec) const { WEBRTC_ASSERT_CHANNEL(channel); @@ -557,6 +590,14 @@ class FakeWebRtcVideoEngine WEBRTC_ASSERT_CHANNEL(channel); channels_[channel]->receive_bandwidth_ = receive_bandwidth; }; + int GetRtxSendPayloadType(int channel) { + WEBRTC_CHECK_CHANNEL(channel); + return channels_[channel]->rtx_send_payload_type; + } + int GetRemoteRtxSsrc(int channel) { + WEBRTC_CHECK_CHANNEL(channel); + return channels_.find(channel)->second->remote_rtx_ssrc_; + } WEBRTC_STUB(Release, ()); @@ -599,6 +640,9 @@ class FakeWebRtcVideoEngine } WEBRTC_STUB(RegisterCpuOveruseObserver, (int channel, webrtc::CpuOveruseObserver* observer)); +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_STUB(CpuOveruseMeasures, (int, int*, int*, int*, int*)); +#endif WEBRTC_STUB(ConnectAudioChannel, (const int, const int)); WEBRTC_STUB(DisconnectAudioChannel, (const int)); WEBRTC_FUNC(StartSend, (const int channel)) { @@ -716,6 +760,7 @@ 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::ViECapture WEBRTC_STUB(NumberOfCaptureDevices, ()); @@ -782,7 +827,12 @@ 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_STUB(ReceivedRTCPPacket, (const int, const void*, const int)); // Not using WEBRTC_STUB due to bool return value virtual bool IsIPv6Enabled(int channel) { return true; } @@ -851,11 +901,28 @@ class FakeWebRtcVideoEngine const webrtc::StreamType usage, const unsigned char idx)) { WEBRTC_CHECK_CHANNEL(channel); - channels_[channel]->ssrcs_[idx] = ssrc; + switch (usage) { + case webrtc::kViEStreamTypeNormal: + channels_[channel]->ssrcs_[idx] = ssrc; + break; + case webrtc::kViEStreamTypeRtx: + channels_[channel]->rtx_ssrcs_[idx] = ssrc; + break; + default: + return -1; + } return 0; } - WEBRTC_STUB_CONST(SetRemoteSSRCType, (const int, - const webrtc::StreamType, const unsigned int)); + + WEBRTC_FUNC_CONST(SetRemoteSSRCType, (const int channel, + const webrtc::StreamType usage, const unsigned int ssrc)) { + WEBRTC_CHECK_CHANNEL(channel); + if (usage == webrtc::kViEStreamTypeRtx) { + channels_.find(channel)->second->remote_rtx_ssrc_ = ssrc; + return 0; + } + return -1; + } WEBRTC_FUNC_CONST(GetLocalSSRC, (const int channel, unsigned int& ssrc)) { @@ -867,7 +934,12 @@ class FakeWebRtcVideoEngine WEBRTC_STUB_CONST(GetRemoteSSRC, (const int, unsigned int&)); WEBRTC_STUB_CONST(GetRemoteCSRCs, (const int, unsigned int*)); - WEBRTC_STUB(SetRtxSendPayloadType, (const int, const uint8)); + WEBRTC_FUNC(SetRtxSendPayloadType, (const int channel, + const uint8 payload_type)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->rtx_send_payload_type = payload_type; + return 0; + } WEBRTC_STUB(SetRtxReceivePayloadType, (const int, const uint8)); WEBRTC_STUB(SetStartSequenceNumber, (const int, unsigned short)); @@ -968,6 +1040,9 @@ class FakeWebRtcVideoEngine channels_[channel]->rtp_absolute_send_time_receive_id_ = (enable) ? id : 0; 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; @@ -979,6 +1054,12 @@ class FakeWebRtcVideoEngine unsigned int&, unsigned int&, unsigned int&, int&)); WEBRTC_STUB_CONST(GetRTPStatistics, (const int, unsigned int&, unsigned int&, unsigned int&, unsigned int&)); + WEBRTC_STUB_CONST(GetReceiveChannelRtcpStatistics, (const int, + webrtc::RtcpStatistics&, int&)); + WEBRTC_STUB_CONST(GetSendChannelRtcpStatistics, (const int, + webrtc::RtcpStatistics&, int&)); + WEBRTC_STUB_CONST(GetRtpStatistics, (const int, webrtc::StreamDataCounters&, + webrtc::StreamDataCounters&)); WEBRTC_FUNC_CONST(GetBandwidthUsage, (const int channel, unsigned int& total_bitrate, unsigned int& video_bitrate, unsigned int& fec_bitrate, unsigned int& nack_bitrate)) { @@ -1021,6 +1102,30 @@ class FakeWebRtcVideoEngine } return 0; } + WEBRTC_STUB(RegisterSendChannelRtcpStatisticsCallback, + (int, webrtc::RtcpStatisticsCallback*)); + WEBRTC_STUB(DeregisterSendChannelRtcpStatisticsCallback, + (int, webrtc::RtcpStatisticsCallback*)); + WEBRTC_STUB(RegisterReceiveChannelRtcpStatisticsCallback, + (int, webrtc::RtcpStatisticsCallback*)); + WEBRTC_STUB(DeregisterReceiveChannelRtcpStatisticsCallback, + (int, webrtc::RtcpStatisticsCallback*)); + WEBRTC_STUB(RegisterSendChannelRtpStatisticsCallback, + (int, webrtc::StreamDataCountersCallback*)); + WEBRTC_STUB(DeregisterSendChannelRtpStatisticsCallback, + (int, webrtc::StreamDataCountersCallback*)); + WEBRTC_STUB(RegisterReceiveChannelRtpStatisticsCallback, + (int, webrtc::StreamDataCountersCallback*)); + WEBRTC_STUB(DeregisterReceiveChannelRtpStatisticsCallback, + (int, webrtc::StreamDataCountersCallback*)); + WEBRTC_STUB(RegisterSendBitrateObserver, + (int, webrtc::BitrateStatisticsObserver*)); + WEBRTC_STUB(DeregisterSendBitrateObserver, + (int, webrtc::BitrateStatisticsObserver*)); + WEBRTC_STUB(RegisterSendFrameCountObserver, + (int, webrtc::FrameCountObserver*)); + WEBRTC_STUB(DeregisterSendFrameCountObserver, + (int, webrtc::FrameCountObserver*)); WEBRTC_STUB(StartRTPDump, (const int, const char*, webrtc::RTPDirections)); WEBRTC_STUB(StopRTPDump, (const int, webrtc::RTPDirections)); @@ -1046,7 +1151,12 @@ class FakeWebRtcVideoEngine return 0; } WEBRTC_STUB(EnableColorEnhancement, (const int, const bool)); - + WEBRTC_VOID_STUB(RegisterPreEncodeCallback, + (int, webrtc::I420FrameCallback*)); + WEBRTC_VOID_STUB(DeRegisterPreEncodeCallback, (int)); + WEBRTC_VOID_STUB(RegisterPreRenderCallback, + (int, webrtc::I420FrameCallback*)); + WEBRTC_VOID_STUB(DeRegisterPreRenderCallback, (int)); // webrtc::ViEExternalCodec WEBRTC_FUNC(RegisterExternalSendCodec, (const int channel, const unsigned char pl_type, webrtc::VideoEncoder*, 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 c3cd786009d..a68d65ef750 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvoiceengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/fakewebrtcvoiceengine.h @@ -34,11 +34,14 @@ #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/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 cricket { @@ -75,7 +78,7 @@ class FakeWebRtcVoiceEngine int dtmf_length_ms; }; struct Channel { - Channel() + explicit Channel(bool use_experimental_acm) : external_transport(false), send(false), playout(false), @@ -87,14 +90,18 @@ class FakeWebRtcVoiceEngine fec(false), nack(false), media_processor_registered(false), + rx_agc_enabled(false), + rx_agc_mode(webrtc::kAgcDefault), cn8_type(13), cn16_type(105), dtmf_type(106), fec_type(117), nack_max_packets(0), send_ssrc(0), - level_header_ext_(-1) { + level_header_ext_(-1), + using_experimental_acm(use_experimental_acm) { memset(&send_codec, 0, sizeof(send_codec)); + memset(&rx_agc_config, 0, sizeof(rx_agc_config)); } bool external_transport; bool send; @@ -107,6 +114,9 @@ class FakeWebRtcVoiceEngine bool fec; bool nack; bool media_processor_registered; + bool rx_agc_enabled; + webrtc::AgcModes rx_agc_mode; + webrtc::AgcConfig rx_agc_config; int cn8_type; int cn16_type; int dtmf_type; @@ -118,6 +128,7 @@ class FakeWebRtcVoiceEngine std::vector<webrtc::CodecInst> recv_codecs; webrtc::CodecInst send_codec; std::list<std::string> packets; + bool using_experimental_acm; }; FakeWebRtcVoiceEngine(const cricket::AudioCodec* const* codecs, @@ -144,6 +155,8 @@ class FakeWebRtcVoiceEngine send_fail_channel_(-1), fail_start_recording_microphone_(false), recording_microphone_(false), + recording_sample_rate_(-1), + playout_sample_rate_(-1), media_processor_(NULL) { memset(&agc_config_, 0, sizeof(agc_config_)); } @@ -169,7 +182,7 @@ class FakeWebRtcVoiceEngine } return -1; } - int GetNumChannels() const { return channels_.size(); } + int GetNumChannels() const { return static_cast<int>(channels_.size()); } bool GetPlayout(int channel) { return channels_[channel]->playout; } @@ -191,6 +204,10 @@ class FakeWebRtcVoiceEngine int GetNACKMaxPackets(int channel) { return channels_[channel]->nack_max_packets; } + bool IsUsingExperimentalAcm(int channel) { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_[channel]->using_experimental_acm; + } int GetSendCNPayloadType(int channel, bool wideband) { return (wideband) ? channels_[channel]->cn16_type : @@ -244,6 +261,19 @@ class FakeWebRtcVoiceEngine true); } } + int AddChannel(bool use_experimental_acm) { + if (fail_create_channel_) { + return -1; + } + Channel* ch = new Channel(use_experimental_acm); + for (int i = 0; i < NumOfCodecs(); ++i) { + webrtc::CodecInst codec; + GetCodec(i, codec); + ch->recv_codecs.push_back(codec); + } + channels_[++last_channel_] = ch; + return last_channel_; + } WEBRTC_STUB(Release, ()); @@ -267,17 +297,13 @@ class FakeWebRtcVoiceEngine return NULL; } WEBRTC_FUNC(CreateChannel, ()) { - if (fail_create_channel_) { - return -1; - } - Channel* ch = new Channel(); - for (int i = 0; i < NumOfCodecs(); ++i) { - webrtc::CodecInst codec; - GetCodec(i, codec); - ch->recv_codecs.push_back(codec); - } - channels_[++last_channel_] = ch; - return last_channel_; + return AddChannel(false); + } + 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(DeleteChannel, (int channel)) { WEBRTC_CHECK_CHANNEL(channel); @@ -584,15 +610,34 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(AudioDeviceControl, (unsigned int, unsigned int, unsigned int)); WEBRTC_STUB(SetLoudspeakerStatus, (bool enable)); WEBRTC_STUB(GetLoudspeakerStatus, (bool& enabled)); - WEBRTC_STUB(SetRecordingSampleRate, (unsigned int samples_per_sec)); - WEBRTC_STUB_CONST(RecordingSampleRate, (unsigned int* samples_per_sec)); - WEBRTC_STUB(SetPlayoutSampleRate, (unsigned int samples_per_sec)); - WEBRTC_STUB_CONST(PlayoutSampleRate, (unsigned int* samples_per_sec)); + WEBRTC_FUNC(SetRecordingSampleRate, (unsigned int samples_per_sec)) { + recording_sample_rate_ = samples_per_sec; + return 0; + } + WEBRTC_FUNC_CONST(RecordingSampleRate, (unsigned int* samples_per_sec)) { + *samples_per_sec = recording_sample_rate_; + return 0; + } + WEBRTC_FUNC(SetPlayoutSampleRate, (unsigned int samples_per_sec)) { + playout_sample_rate_ = samples_per_sec; + return 0; + } + WEBRTC_FUNC_CONST(PlayoutSampleRate, (unsigned int* samples_per_sec)) { + *samples_per_sec = playout_sample_rate_; + return 0; + } WEBRTC_STUB(EnableBuiltInAEC, (bool enable)); virtual bool BuiltInAECIsEnabled() const { return true; } // 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, @@ -841,12 +886,27 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(SetRxNsStatus, (int channel, bool enable, webrtc::NsModes mode)); WEBRTC_STUB(GetRxNsStatus, (int channel, bool& enabled, webrtc::NsModes& mode)); - WEBRTC_STUB(SetRxAgcStatus, (int channel, bool enable, - webrtc::AgcModes mode)); - WEBRTC_STUB(GetRxAgcStatus, (int channel, bool& enabled, - webrtc::AgcModes& mode)); - WEBRTC_STUB(SetRxAgcConfig, (int channel, webrtc::AgcConfig config)); - WEBRTC_STUB(GetRxAgcConfig, (int channel, webrtc::AgcConfig& config)); + WEBRTC_FUNC(SetRxAgcStatus, (int channel, bool enable, + webrtc::AgcModes mode)) { + channels_[channel]->rx_agc_enabled = enable; + channels_[channel]->rx_agc_mode = mode; + return 0; + } + WEBRTC_FUNC(GetRxAgcStatus, (int channel, bool& enabled, + webrtc::AgcModes& mode)) { + enabled = channels_[channel]->rx_agc_enabled; + mode = channels_[channel]->rx_agc_mode; + return 0; + } + + WEBRTC_FUNC(SetRxAgcConfig, (int channel, webrtc::AgcConfig config)) { + channels_[channel]->rx_agc_config = config; + return 0; + } + WEBRTC_FUNC(GetRxAgcConfig, (int channel, webrtc::AgcConfig& config)) { + config = channels_[channel]->rx_agc_config; + return 0; + } WEBRTC_STUB(RegisterRxVadObserver, (int, webrtc::VoERxVadCallback&)); WEBRTC_STUB(DeRegisterRxVadObserver, (int channel)); @@ -863,6 +923,9 @@ 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)) { @@ -996,6 +1059,8 @@ class FakeWebRtcVoiceEngine int send_fail_channel_; bool fail_start_recording_microphone_; bool recording_microphone_; + int recording_sample_rate_; + int playout_sample_rate_; DtmfInfo dtmf_info_; webrtc::VoEMediaProcess* media_processor_; }; 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 a2ee6587610..94e7a99d826 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcmediaengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcmediaengine.h @@ -87,10 +87,13 @@ class WebRtcMediaEngine : public cricket::MediaEngineInterface { virtual SoundclipMedia* CreateSoundclip() OVERRIDE { return delegate_->CreateSoundclip(); } - virtual bool SetAudioOptions(int options) OVERRIDE { + virtual AudioOptions GetAudioOptions() const OVERRIDE { + return delegate_->GetAudioOptions(); + } + virtual bool SetAudioOptions(const AudioOptions& options) OVERRIDE { return delegate_->SetAudioOptions(options); } - virtual bool SetVideoOptions(int options) OVERRIDE { + virtual bool SetVideoOptions(const VideoOptions& options) OVERRIDE { return delegate_->SetVideoOptions(options); } virtual bool SetAudioDelayOffset(int offset) OVERRIDE { @@ -100,6 +103,9 @@ class WebRtcMediaEngine : public cricket::MediaEngineInterface { const VideoEncoderConfig& config) OVERRIDE { return delegate_->SetDefaultVideoEncoderConfig(config); } + virtual VideoEncoderConfig GetDefaultVideoEncoderConfig() const OVERRIDE { + return delegate_->GetDefaultVideoEncoderConfig(); + } virtual bool SetSoundDevices( const Device* in_device, const Device* out_device) OVERRIDE { return delegate_->SetSoundDevices(in_device, out_device); 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 bcfda4e84e8..6e81b401613 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.cc @@ -328,14 +328,10 @@ void WebRtcVideoCapturer::OnIncomingCapturedFrame(const int32_t id, // to one block for it. int length = webrtc::CalcBufferSize(webrtc::kI420, sample.width(), sample.height()); - if (!captured_frame_.get() || - captured_frame_->length() != static_cast<size_t>(length)) { - captured_frame_.reset(new FrameBuffer(length)); - } - // TODO(ronghuawu): Refactor the WebRtcVideoFrame to avoid memory copy. - webrtc::ExtractBuffer(sample, length, - reinterpret_cast<uint8_t*>(captured_frame_->data())); - WebRtcCapturedFrame frame(sample, captured_frame_->data(), length); + capture_buffer_.resize(length); + // TODO(ronghuawu): Refactor the WebRtcCapturedFrame to avoid memory copy. + webrtc::ExtractBuffer(sample, length, &capture_buffer_[0]); + WebRtcCapturedFrame frame(sample, &capture_buffer_[0], length); SignalFrameCaptured(this, &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 eb999564409..c20a05919e0 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideocapturer.h @@ -88,7 +88,7 @@ class WebRtcVideoCapturer : public VideoCapturer, talk_base::scoped_ptr<WebRtcVcmFactoryInterface> factory_; webrtc::VideoCaptureModule* module_; int captured_frames_; - talk_base::scoped_ptr<FrameBuffer> captured_frame_; + std::vector<uint8_t> capture_buffer_; }; struct WebRtcCapturedFrame : public CapturedFrame { 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 fd7e5bfa3f6..88e09fc3842 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.cc @@ -93,6 +93,9 @@ 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; + static const int kVideoMtu = 1200; static const int kVideoRtpBufferSize = 65536; @@ -153,6 +156,11 @@ static const int kRtpTimeOffsetExtensionId = 2; 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 +static const talk_base::DiffServCodePoint kVideoDscpValue = + talk_base::DSCP_AF41; static bool IsNackEnabled(const VideoCodec& codec) { return codec.HasFeedbackParam(FeedbackParam(kRtcpFbParamNack, @@ -220,9 +228,8 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { } virtual int DeliverFrame(unsigned char* buffer, int buffer_size, - uint32_t time_stamp, int64_t render_time - , void* handle - ) { + uint32_t time_stamp, int64_t render_time, + void* handle) { talk_base::CritScope cs(&crit_); frame_rate_tracker_.Update(1); if (renderer_ == NULL) { @@ -250,8 +257,8 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { int DeliverBufferFrame(unsigned char* buffer, int buffer_size, int64 elapsed_time, int64 time_stamp) { WebRtcVideoFrame video_frame; - video_frame.Attach(buffer, buffer_size, width_, height_, - 1, 1, elapsed_time, time_stamp, 0); + video_frame.Alias(buffer, buffer_size, width_, height_, + 1, 1, elapsed_time, time_stamp, 0); // Sanity check on decoded frame size. @@ -261,9 +268,6 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { } int ret = renderer_->RenderFrame(&video_frame) ? 0 : -1; - uint8* buffer_temp; - size_t buffer_size_temp; - video_frame.Detach(&buffer_temp, &buffer_size_temp); return ret; } @@ -309,6 +313,13 @@ class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver { : video_channel_(video_channel), framerate_(0), bitrate_(0), + decode_ms_(0), + max_decode_ms_(0), + current_delay_ms_(0), + target_delay_ms_(0), + jitter_buffer_ms_(0), + min_playout_delay_ms_(0), + render_delay_ms_(0), firs_requested_(0) { } @@ -318,23 +329,61 @@ class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver { virtual void IncomingRate(const int videoChannel, const unsigned int framerate, const unsigned int bitrate) { + talk_base::CritScope cs(&crit_); ASSERT(video_channel_ == videoChannel); framerate_ = framerate; bitrate_ = bitrate; } + + virtual void DecoderTiming(int decode_ms, + int max_decode_ms, + int current_delay_ms, + int target_delay_ms, + int jitter_buffer_ms, + int min_playout_delay_ms, + int render_delay_ms) { + talk_base::CritScope cs(&crit_); + decode_ms_ = decode_ms; + max_decode_ms_ = max_decode_ms; + current_delay_ms_ = current_delay_ms; + target_delay_ms_ = target_delay_ms; + jitter_buffer_ms_ = jitter_buffer_ms; + min_playout_delay_ms_ = min_playout_delay_ms; + render_delay_ms_ = render_delay_ms; + } + virtual void RequestNewKeyFrame(const int videoChannel) { + talk_base::CritScope cs(&crit_); ASSERT(video_channel_ == videoChannel); ++firs_requested_; } - int framerate() const { return framerate_; } - int bitrate() const { return bitrate_; } - int firs_requested() const { return firs_requested_; } + // 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_; + rinfo->current_delay_ms = current_delay_ms_; + rinfo->target_delay_ms = target_delay_ms_; + rinfo->jitter_buffer_ms = jitter_buffer_ms_; + rinfo->min_playout_delay_ms = min_playout_delay_ms_; + rinfo->render_delay_ms = render_delay_ms_; + } private: + mutable talk_base::CriticalSection crit_; int video_channel_; int framerate_; int bitrate_; + int decode_ms_; + int max_decode_ms_; + int current_delay_ms_; + int target_delay_ms_; + int jitter_buffer_ms_; + int min_playout_delay_ms_; + int render_delay_ms_; int firs_requested_; }; @@ -343,25 +392,45 @@ class WebRtcEncoderObserver : public webrtc::ViEEncoderObserver { explicit WebRtcEncoderObserver(int video_channel) : video_channel_(video_channel), framerate_(0), - bitrate_(0) { + bitrate_(0), + suspended_(false) { } // virtual functions from VieEncoderObserver. virtual void OutgoingRate(const int videoChannel, const unsigned int framerate, const unsigned int bitrate) { + talk_base::CritScope cs(&crit_); ASSERT(video_channel_ == videoChannel); framerate_ = framerate; bitrate_ = bitrate; } - int framerate() const { return framerate_; } - int bitrate() const { return bitrate_; } + virtual void SuspendChange(int video_channel, bool is_suspended) { + talk_base::CritScope cs(&crit_); + ASSERT(video_channel_ == video_channel); + suspended_ = is_suspended; + } + + int framerate() const { + talk_base::CritScope cs(&crit_); + return framerate_; + } + int bitrate() const { + talk_base::CritScope cs(&crit_); + return bitrate_; + } + bool suspended() const { + talk_base::CritScope cs(&crit_); + return suspended_; + } private: + mutable talk_base::CriticalSection crit_; int video_channel_; int framerate_; int bitrate_; + bool suspended_; }; class WebRtcLocalStreamInfo { @@ -514,7 +583,8 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { external_capture_(external_capture), capturer_updated_(false), interval_(0), - video_adapter_(new CoordinatedVideoAdapter) { + video_adapter_(new CoordinatedVideoAdapter), + cpu_monitor_(cpu_monitor) { overuse_observer_.reset(new WebRtcOveruseObserver(video_adapter_.get())); SignalCpuAdaptationUnable.repeat(video_adapter_->SignalCpuAdaptationUnable); if (cpu_monitor) { @@ -562,10 +632,6 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { } } - bool AdaptFrame(const VideoFrame* in_frame, const VideoFrame** out_frame) { - *out_frame = NULL; - return video_adapter_->AdaptFrame(in_frame, out_frame); - } int CurrentAdaptReason() const { return video_adapter_->adapt_reason(); } @@ -606,9 +672,18 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { // 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); } } + void AdaptFrame(VideoCapturer* capturer, const VideoFrame* input, + VideoFrame** adapted) { + video_adapter_->AdaptFrame(input, adapted); + } + void ApplyCpuOptions(const VideoOptions& options) { bool cpu_adapt, cpu_smoothing, adapt_third; float low, med, high; @@ -633,6 +708,9 @@ class WebRtcVideoChannelSendInfo : public sigslot::has_slots<> { } void SetCpuOveruseDetection(bool enable) { + if (cpu_monitor_ && enable) { + cpu_monitor_->SignalUpdate.disconnect(video_adapter_.get()); + } overuse_observer_->Enable(enable); video_adapter_->set_cpu_adaptation(enable); } @@ -689,14 +767,16 @@ 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_; }; const WebRtcVideoEngine::VideoCodecPref WebRtcVideoEngine::kVideoCodecPrefs[] = { - {kVp8PayloadName, 100, 0}, - {kRedPayloadName, 116, 1}, - {kFecPayloadName, 117, 2}, + {kVp8PayloadName, 100, -1, 0}, + {kRedPayloadName, 116, -1, 1}, + {kFecPayloadName, 117, -1, 2}, + {kRtxCodecName, 96, 100, 3}, }; // The formats are sorted by the descending order of width. We use the order to @@ -900,7 +980,7 @@ int WebRtcVideoEngine::GetCapabilities() { return VIDEO_RECV | VIDEO_SEND; } -bool WebRtcVideoEngine::SetOptions(int options) { +bool WebRtcVideoEngine::SetOptions(const VideoOptions &options) { return true; } @@ -909,6 +989,17 @@ bool WebRtcVideoEngine::SetDefaultEncoderConfig( return SetDefaultCodec(config.max_codec); } +VideoEncoderConfig WebRtcVideoEngine::GetDefaultEncoderConfig() const { + ASSERT(!video_codecs_.empty()); + VideoCodec max_codec(kVideoCodecPrefs[0].payload_type, + kVideoCodecPrefs[0].name, + video_codecs_[0].width, + video_codecs_[0].height, + video_codecs_[0].framerate, + 0); + return VideoEncoderConfig(max_codec); +} + // SetDefaultCodec may be called while the capturer is running. For example, a // test call is started in a page with QVGA default codec, and then a real call // is started in another page with VGA default codec. This is the corner case @@ -919,6 +1010,7 @@ bool WebRtcVideoEngine::SetDefaultCodec(const VideoCodec& codec) { return false; } + ASSERT(!video_codecs_.empty()); default_codec_format_ = VideoFormat( video_codecs_[0].width, video_codecs_[0].height, @@ -1223,7 +1315,8 @@ static void AddDefaultFeedbackParams(VideoCodec* codec) { } // Rebuilds the codec list to be only those that are less intensive -// than the specified codec. Prefers internal codec over external. +// than the specified codec. Prefers internal codec over external with +// higher preference field. bool WebRtcVideoEngine::RebuildCodecList(const VideoCodec& in_codec) { if (!FindCodec(in_codec)) return false; @@ -1243,6 +1336,10 @@ bool WebRtcVideoEngine::RebuildCodecList(const VideoCodec& in_codec) { if (_stricmp(kVp8PayloadName, codec.name.c_str()) == 0) { AddDefaultFeedbackParams(&codec); } + if (pref.associated_payload_type != -1) { + codec.SetParam(kCodecParamAssociatedPayloadType, + pref.associated_payload_type); + } video_codecs_.push_back(codec); internal_codec_names.insert(codec.name); } @@ -1262,7 +1359,9 @@ bool WebRtcVideoEngine::RebuildCodecList(const VideoCodec& in_codec) { codecs[i].max_width, codecs[i].max_height, codecs[i].max_fps, - static_cast<int>(codecs.size() + ARRAY_SIZE(kVideoCodecPrefs) - i)); + // Use negative preference on external codec to ensure the internal + // codec is preferred. + static_cast<int>(0 - i)); AddDefaultFeedbackParams(&codec); video_codecs_.push_back(codec); } @@ -1410,6 +1509,7 @@ WebRtcVideoMediaChannel::WebRtcVideoMediaChannel( remb_enabled_(false), render_started_(false), first_receive_ssrc_(0), + send_rtx_type_(-1), send_red_type_(-1), send_fec_type_(-1), send_min_bitrate_(kMinVideoBitrate), @@ -1486,12 +1586,19 @@ bool WebRtcVideoMediaChannel::SetSendCodecs( if (sending_) { ConvertToCricketVideoCodec(*send_codec_, ¤t); } + std::map<int, int> primary_rtx_pt_mapping; for (std::vector<VideoCodec>::const_iterator iter = codecs.begin(); iter != codecs.end(); ++iter) { if (_stricmp(iter->name.c_str(), kRedPayloadName) == 0) { send_red_type_ = iter->id; } else if (_stricmp(iter->name.c_str(), kFecPayloadName) == 0) { send_fec_type_ = iter->id; + } else if (_stricmp(iter->name.c_str(), kRtxCodecName) == 0) { + int rtx_type = iter->id; + int rtx_primary_type = -1; + if (iter->GetParam(kCodecParamAssociatedPayloadType, &rtx_primary_type)) { + primary_rtx_pt_mapping[rtx_primary_type] = rtx_type; + } } else if (engine()->CanSendCodec(*iter, current, &checked_codec)) { webrtc::VideoCodec wcodec; if (engine()->ConvertFromCricketVideoCodec(checked_codec, &wcodec)) { @@ -1547,6 +1654,14 @@ bool WebRtcVideoMediaChannel::SetSendCodecs( // Select the first matched codec. webrtc::VideoCodec& codec(send_codecs[0]); + // Set RTX payload type if primary now active. This value will be used in + // SetSendCodec. + std::map<int, int>::const_iterator rtx_it = + primary_rtx_pt_mapping.find(static_cast<int>(codec.plType)); + if (rtx_it != primary_rtx_pt_mapping.end()) { + send_rtx_type_ = rtx_it->second; + } + if (!SetSendCodec( codec, codec.minBitrate, codec.startBitrate, codec.maxBitrate)) { return false; @@ -1648,9 +1763,9 @@ bool WebRtcVideoMediaChannel::SetSend(bool send) { bool WebRtcVideoMediaChannel::AddSendStream(const StreamParams& sp) { LOG(LS_INFO) << "AddSendStream " << sp.ToString(); - if (!IsOneSsrcStream(sp)) { - LOG(LS_ERROR) << "AddSendStream: bad local stream parameters"; - return false; + if (!IsOneSsrcStream(sp) && !IsSimulcastStream(sp)) { + LOG(LS_ERROR) << "AddSendStream: bad local stream parameters"; + return false; } uint32 ssrc_key; @@ -1680,6 +1795,11 @@ bool WebRtcVideoMediaChannel::AddSendStream(const StreamParams& sp) { return false; } + // Set the corresponding RTX SSRC. + if (!SetLocalRtxSsrc(channel_id, sp, sp.first_ssrc(), 0)) { + return false; + } + // Set RTCP CName. if (engine()->vie()->rtp()->SetRTCPCName(channel_id, sp.cname.c_str()) != 0) { @@ -1784,10 +1904,11 @@ bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) { return false; } - // TODO(perkj): Implement recv media from multiple SSRCs per stream. - if (sp.ssrcs.size() != 1) { - LOG(LS_ERROR) << "WebRtcVideoMediaChannel supports one receiving SSRC per" - << " stream"; + // 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; } @@ -1800,6 +1921,16 @@ bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) { return false; } + // 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); + return false; + } + // Get the default renderer. VideoRenderer* default_renderer = NULL; if (InConferenceMode()) { @@ -1877,17 +2008,15 @@ bool WebRtcVideoMediaChannel::RemoveRecvStream(uint32 ssrc) { LOG(LS_INFO) << "Removing video stream " << ssrc << " with VideoEngine channel #" << channel_id; + bool ret = true; if (engine()->vie()->base()->DeleteChannel(channel_id) == -1) { LOG_RTCERR1(DeleteChannel, channel_id); - // Leak the WebRtcVideoChannelRecvInfo owned by |it| but remove the channel - // from recv_channels_. - recv_channels_.erase(it); - return false; + ret = false; } // Delete the WebRtcVideoChannelRecvInfo pointed to by it->second. delete info; recv_channels_.erase(it); - return true; + return ret; } bool WebRtcVideoMediaChannel::StartSend() { @@ -1952,10 +2081,6 @@ bool WebRtcVideoMediaChannel::SendIntraFrame() { return success; } -bool WebRtcVideoMediaChannel::IsOneSsrcStream(const StreamParams& sp) { - return (sp.ssrcs.size() == 1 && sp.ssrc_groups.size() == 0); -} - bool WebRtcVideoMediaChannel::HasReadySendChannels() { return !send_channels_.empty() && ((send_channels_.size() > 1) || @@ -1994,18 +2119,6 @@ bool WebRtcVideoMediaChannel::GetSendChannelKey(uint32 local_ssrc, } WebRtcVideoChannelSendInfo* WebRtcVideoMediaChannel::GetSendChannel( - VideoCapturer* video_capturer) { - for (SendChannelMap::iterator iter = send_channels_.begin(); - iter != send_channels_.end(); ++iter) { - WebRtcVideoChannelSendInfo* send_channel = iter->second; - if (send_channel->video_capturer() == video_capturer) { - return send_channel; - } - } - return NULL; -} - -WebRtcVideoChannelSendInfo* WebRtcVideoMediaChannel::GetSendChannel( uint32 local_ssrc) { uint32 key; if (!GetSendChannelKey(local_ssrc, &key)) { @@ -2034,6 +2147,18 @@ bool WebRtcVideoMediaChannel::CreateSendChannelKey(uint32 local_ssrc, return true; } +int WebRtcVideoMediaChannel::GetSendChannelNum(VideoCapturer* capturer) { + int num = 0; + for (SendChannelMap::iterator iter = send_channels_.begin(); + iter != send_channels_.end(); ++iter) { + WebRtcVideoChannelSendInfo* send_channel = iter->second; + if (send_channel->video_capturer() == capturer) { + ++num; + } + } + return num; +} + uint32 WebRtcVideoMediaChannel::GetDefaultChannelSsrc() { WebRtcVideoChannelSendInfo* send_channel = send_channels_[0]; const StreamParams* sp = send_channel->stream_params(); @@ -2049,11 +2174,8 @@ bool WebRtcVideoMediaChannel::DeleteSendChannel(uint32 ssrc_key) { return false; } WebRtcVideoChannelSendInfo* send_channel = send_channels_[ssrc_key]; - VideoCapturer* capturer = send_channel->video_capturer(); - if (capturer != NULL) { - capturer->SignalVideoFrame.disconnect(this); - send_channel->set_video_capturer(NULL); - } + MaybeDisconnectCapturer(send_channel->video_capturer()); + send_channel->set_video_capturer(NULL); int channel_id = send_channel->channel_id(); int capture_id = send_channel->capture_id(); @@ -2092,7 +2214,7 @@ bool WebRtcVideoMediaChannel::RemoveCapturer(uint32 ssrc) { if (capturer == NULL) { return false; } - capturer->SignalVideoFrame.disconnect(this); + MaybeDisconnectCapturer(capturer); send_channel->set_video_capturer(NULL); const int64 timestamp = send_channel->local_stream_info()->time_stamp(); if (send_codec_) { @@ -2157,7 +2279,9 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { WebRtcLocalStreamInfo* channel_stream_info = send_channel->local_stream_info(); - sinfo.ssrcs = send_params->ssrcs; + for (size_t i = 0; i < send_params->ssrcs.size(); ++i) { + sinfo.add_ssrc(send_params->ssrcs[i]); + } sinfo.codec_name = send_codec_->plName; sinfo.bytes_sent = bytes_sent; sinfo.packets_sent = packets_sent; @@ -2174,26 +2298,45 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { sinfo.nominal_bitrate = send_channel->encoder_observer()->bitrate(); sinfo.preferred_bitrate = send_max_bitrate_; sinfo.adapt_reason = send_channel->CurrentAdaptReason(); + 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; + int capture_queue_delay_ms_per_s = 0; + if (engine()->vie()->base()->CpuOveruseMeasures( + channel_id, + &capture_jitter_ms, + &avg_encode_time_ms, + &encode_usage_percent, + &capture_queue_delay_ms_per_s) == 0) { + sinfo.capture_jitter_ms = capture_jitter_ms; + sinfo.avg_encode_ms = avg_encode_time_ms; + sinfo.encode_usage_percent = encode_usage_percent; + sinfo.capture_queue_delay_ms_per_s = capture_queue_delay_ms_per_s; + } +#endif - // Get received RTCP statistics for the sender, if available. + // 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 // yet. - uint16 r_fraction_lost; - unsigned int r_cumulative_lost; - unsigned int r_extended_max; - unsigned int r_jitter; - int r_rtt_ms; - - if (engine_->vie()->rtp()->GetSentRTCPStatistics( - channel_id, - r_fraction_lost, - r_cumulative_lost, - r_extended_max, - r_jitter, r_rtt_ms) == 0) { + webrtc::RtcpStatistics outgoing_stream_rtcp_stats; + int outgoing_stream_rtt_ms; + + if (engine_->vie()->rtp()->GetSendChannelRtcpStatistics( + channel_id, + outgoing_stream_rtcp_stats, + outgoing_stream_rtt_ms) == 0) { // Convert Q8 to float. - sinfo.packets_lost = r_cumulative_lost; - sinfo.fraction_lost = static_cast<float>(r_fraction_lost) / (1 << 8); - sinfo.rtt_ms = r_rtt_ms; + sinfo.packets_lost = outgoing_stream_rtcp_stats.cumulative_lost; + sinfo.fraction_lost = static_cast<float>( + outgoing_stream_rtcp_stats.fraction_lost) / (1 << 8); + sinfo.rtt_ms = outgoing_stream_rtt_ms; } info->senders.push_back(sinfo); @@ -2249,41 +2392,39 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) { ssrc == 0) continue; - unsigned int bytes_sent, packets_sent, bytes_recv, packets_recv; - if (engine_->vie()->rtp()->GetRTPStatistics( - channel->channel_id(), bytes_sent, packets_sent, bytes_recv, - packets_recv) != 0) { + webrtc::StreamDataCounters sent; + webrtc::StreamDataCounters received; + if (engine_->vie()->rtp()->GetRtpStatistics(channel->channel_id(), + sent, received) != 0) { LOG_RTCERR1(GetRTPStatistics, channel->channel_id()); return false; } VideoReceiverInfo rinfo; - rinfo.ssrcs.push_back(ssrc); - rinfo.bytes_rcvd = bytes_recv; - rinfo.packets_rcvd = packets_recv; + rinfo.add_ssrc(ssrc); + rinfo.bytes_rcvd = received.bytes; + rinfo.packets_rcvd = received.packets; rinfo.packets_lost = -1; rinfo.packets_concealed = -1; rinfo.fraction_lost = -1; // from SentRTCP - rinfo.firs_sent = channel->decoder_observer()->firs_requested(); rinfo.nacks_sent = -1; rinfo.frame_width = channel->render_adapter()->width(); rinfo.frame_height = channel->render_adapter()->height(); - rinfo.framerate_rcvd = channel->decoder_observer()->framerate(); int fps = channel->render_adapter()->framerate(); rinfo.framerate_decoded = fps; rinfo.framerate_output = fps; - - // Get sent RTCP statistics. - uint16 s_fraction_lost; - unsigned int s_cumulative_lost; - unsigned int s_extended_max; - unsigned int s_jitter; - int s_rtt_ms; - if (engine_->vie()->rtp()->GetReceivedRTCPStatistics(channel->channel_id(), - s_fraction_lost, s_cumulative_lost, s_extended_max, - s_jitter, s_rtt_ms) == 0) { + channel->decoder_observer()->ExportTo(&rinfo); + + // Get our locally created statistics of the received RTP stream. + webrtc::RtcpStatistics incoming_stream_rtcp_stats; + int incoming_stream_rtt_ms; + if (engine_->vie()->rtp()->GetReceiveChannelRtcpStatistics( + channel->channel_id(), + incoming_stream_rtcp_stats, + incoming_stream_rtt_ms) == 0) { // Convert Q8 to float. - rinfo.packets_lost = s_cumulative_lost; - rinfo.fraction_lost = static_cast<float>(s_fraction_lost) / (1 << 8); + rinfo.packets_lost = incoming_stream_rtcp_stats.cumulative_lost; + rinfo.fraction_lost = static_cast<float>( + incoming_stream_rtcp_stats.fraction_lost) / (1 << 8); } info->receivers.push_back(rinfo); @@ -2324,14 +2465,10 @@ bool WebRtcVideoMediaChannel::SetCapturer(uint32 ssrc, return false; } VideoCapturer* old_capturer = send_channel->video_capturer(); - if (old_capturer) { - old_capturer->SignalVideoFrame.disconnect(this); - } + MaybeDisconnectCapturer(old_capturer); send_channel->set_video_capturer(capturer); - capturer->SignalVideoFrame.connect( - this, - &WebRtcVideoMediaChannel::AdaptAndSendFrame); + MaybeConnectCapturer(capturer); if (!capturer->IsScreencast() && ratio_w_ != 0 && ratio_h_ != 0) { capturer->UpdateAspectRatio(ratio_w_, ratio_h_); } @@ -2348,7 +2485,8 @@ bool WebRtcVideoMediaChannel::RequestIntraFrame() { return false; } -void WebRtcVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { +void WebRtcVideoMediaChannel::OnPacketReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { // Pick which channel to send this packet to. If this packet doesn't match // any multiplexed streams, just send it to the default channel. Otherwise, // send it to the specific decoder instance for that stream. @@ -2363,10 +2501,16 @@ void WebRtcVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { engine()->vie()->network()->ReceivedRTPPacket( which_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(talk_base::Buffer* packet) { +void WebRtcVideoMediaChannel::OnRtcpReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { // Sending channels need all RTCP packets with feedback information. // Even sender reports can contain attached report blocks. // Receiving channels need sender reports in order to create @@ -2496,7 +2640,7 @@ bool WebRtcVideoMediaChannel::SetSendBandwidth(bool autobw, int bps) { int max_bitrate; if (autobw) { // Use the default values for min bitrate. - min_bitrate = kMinVideoBitrate; + 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. @@ -2537,6 +2681,12 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { 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 = + options.suspend_below_min_bitrate.IsSet() && + (options_.suspend_below_min_bitrate != options.suspend_below_min_bitrate); + bool conference_mode_turned_off = false; if (options_.conference_mode.IsSet() && options.conference_mode.IsSet() && options_.conference_mode.GetWithDefaultIfUnset(false) && @@ -2559,6 +2709,17 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { // Adjust send codec bitrate if needed. int conf_max_bitrate = kDefaultConferenceModeMaxVideoBitrate; + // 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); + + 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; + } + int expected_bitrate = send_max_bitrate_; if (InConferenceMode()) { expected_bitrate = conf_max_bitrate; @@ -2570,7 +2731,8 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { } if (send_codec_ && - (send_max_bitrate_ != expected_bitrate || denoiser_changed)) { + (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_, @@ -2623,6 +2785,25 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { send_channel->SetCpuOveruseDetection(cpu_overuse_detection); } } + if (dscp_option_changed) { + talk_base::DiffServCodePoint dscp = talk_base::DSCP_DEFAULT; + if (options.dscp.GetWithDefaultIfUnset(false)) + dscp = kVideoDscpValue; + 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)) { + for (SendChannelMap::iterator it = send_channels_.begin(); + it != send_channels_.end(); ++it) { + engine()->vie()->codec()->SuspendBelowMinBitrate( + it->second->channel_id()); + } + } else { + LOG(LS_WARNING) << "Cannot disable video suspension once it is enabled"; + } + } return true; } @@ -2677,39 +2858,23 @@ bool WebRtcVideoMediaChannel::GetRenderer(uint32 ssrc, return true; } -void WebRtcVideoMediaChannel::AdaptAndSendFrame(VideoCapturer* capturer, - const VideoFrame* frame) { - if (capturer->IsScreencast()) { - // Do not adapt frames that are screencast. - SendFrame(capturer, frame); - return; - } - // TODO(thorcarpenter): This is broken. One capturer registered on two ssrc - // will not send any video to the second ssrc send channel. We should remove - // GetSendChannel(capturer) and pass in an ssrc here. - WebRtcVideoChannelSendInfo* send_channel = GetSendChannel(capturer); - if (!send_channel) { - SendFrame(capturer, frame); - return; - } - const VideoFrame* output_frame = NULL; - send_channel->AdaptFrame(frame, &output_frame); - if (output_frame) { - SendFrame(send_channel, output_frame, capturer->IsScreencast()); - } -} - -// TODO(zhurunz): Add unittests to test this function. void WebRtcVideoMediaChannel::SendFrame(VideoCapturer* capturer, const VideoFrame* frame) { - // If there's send channel registers to the |capturer|, then only send the - // frame to that channel and return. Otherwise send the frame to the default - // channel, which currently taking frames from the engine. - WebRtcVideoChannelSendInfo* send_channel = GetSendChannel(capturer); - if (send_channel) { - SendFrame(send_channel, frame, capturer->IsScreencast()); + // If the |capturer| is registered to any send channel, then send the frame + // to those send channels. + bool capturer_is_channel_owned = false; + for (SendChannelMap::iterator iter = send_channels_.begin(); + iter != send_channels_.end(); ++iter) { + WebRtcVideoChannelSendInfo* send_channel = iter->second; + if (send_channel->video_capturer() == capturer) { + SendFrame(send_channel, frame, capturer->IsScreencast()); + capturer_is_channel_owned = true; + } + } + if (capturer_is_channel_owned) { return; } + // TODO(hellner): Remove below for loop once the captured frame no longer // come from the engine, i.e. the engine no longer owns a capturer. for (SendChannelMap::iterator iter = send_channels_.begin(); @@ -3120,7 +3285,8 @@ bool WebRtcVideoMediaChannel::SetNackFec(int channel_id, LOG_RTCERR1(SetNACKStatus, channel_id); return false; } - LOG(LS_INFO) << "NACK enabled for channel " << channel_id; + std::string enabled = nack_enabled ? "enabled" : "disabled"; + LOG(LS_INFO) << "NACK " << enabled << " for channel " << channel_id; } return true; } @@ -3217,6 +3383,15 @@ bool WebRtcVideoMediaChannel::SetSendCodec( return false; } + // NOTE: SetRtxSendPayloadType must be called after all simulcast SSRCs + // are configured. Otherwise ssrc's configured after this point will use + // the primary PT for RTX. + if (send_rtx_type_ != -1 && + engine()->vie()->rtp()->SetRtxSendPayloadType(channel_id, + send_rtx_type_) != 0) { + LOG_RTCERR2(SetRtxSendPayloadType, channel_id, send_rtx_type_); + return false; + } } send_channel->set_interval( cricket::VideoFormat::FpsToInterval(target_codec.maxFramerate)); @@ -3292,6 +3467,9 @@ void WebRtcVideoMediaChannel::LogSendCodecChange(const std::string& reason) { << vie_codec.codecSpecific.VP8.keyFrameInterval; } + if (send_rtx_type_ != -1) { + LOG(LS_INFO) << "RTX payload type: " << send_rtx_type_; + } } bool WebRtcVideoMediaChannel::SetReceiveCodecs( @@ -3556,6 +3734,35 @@ bool WebRtcVideoMediaChannel::SetHeaderExtension(ExtensionSetterFunction setter, header_extension_uri); return SetHeaderExtension(setter, channel_id, extension); } + +bool WebRtcVideoMediaChannel::SetLocalRtxSsrc(int channel_id, + const StreamParams& send_params, + uint32 primary_ssrc, + int stream_idx) { + uint32 rtx_ssrc = 0; + bool has_rtx = send_params.GetFidSsrc(primary_ssrc, &rtx_ssrc); + if (has_rtx && engine()->vie()->rtp()->SetLocalSSRC( + channel_id, rtx_ssrc, webrtc::kViEStreamTypeRtx, stream_idx) != 0) { + LOG_RTCERR4(SetLocalSSRC, channel_id, rtx_ssrc, + webrtc::kViEStreamTypeRtx, stream_idx); + return false; + } + return true; +} + +void WebRtcVideoMediaChannel::MaybeConnectCapturer(VideoCapturer* capturer) { + if (capturer != NULL && GetSendChannelNum(capturer) == 1) { + capturer->SignalVideoFrame.connect(this, + &WebRtcVideoMediaChannel::SendFrame); + } +} + +void WebRtcVideoMediaChannel::MaybeDisconnectCapturer(VideoCapturer* capturer) { + if (capturer != NULL && GetSendChannelNum(capturer) == 1) { + capturer->SignalVideoFrame.disconnect(this); + } +} + } // namespace cricket #endif // HAVE_WEBRTC_VIDEO 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 f0293bb5d41..289903a0722 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoengine.h @@ -104,8 +104,9 @@ class WebRtcVideoEngine : public sigslot::has_slots<>, void Terminate(); int GetCapabilities(); - bool SetOptions(int options); + bool SetOptions(const VideoOptions &options); bool SetDefaultEncoderConfig(const VideoEncoderConfig& config); + VideoEncoderConfig GetDefaultEncoderConfig() const; WebRtcVideoMediaChannel* CreateChannel(VoiceMediaChannel* voice_channel); @@ -179,6 +180,9 @@ class WebRtcVideoEngine : public sigslot::has_slots<>, struct VideoCodecPref { const char* name; int payload_type; + // For RTX, this field is the payload-type that RTX applies to. + // For other codecs, it should be set to -1. + int associated_payload_type; int pref; }; @@ -262,8 +266,10 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, virtual bool SendIntraFrame(); virtual bool RequestIntraFrame(); - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet); + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); + virtual void OnRtcpReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); virtual void OnReadyToSend(bool ready); virtual bool MuteStream(uint32 ssrc, bool on); virtual bool SetRecvRtpHeaderExtensions( @@ -286,8 +292,6 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, bool SendFrame(WebRtcVideoChannelSendInfo* channel_info, const VideoFrame* frame, bool is_screencast); - void AdaptAndSendFrame(VideoCapturer* capturer, const VideoFrame* frame); - // Thunk functions for use with HybridVideoEngine void OnLocalFrame(VideoCapturer* capturer, const VideoFrame* frame) { SendFrame(0u, frame, capturer->IsScreencast()); @@ -355,9 +359,6 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, bool StopSend(WebRtcVideoChannelSendInfo* send_channel); bool SendIntraFrame(int channel_id); - // Send with one local SSRC. Normal case. - bool IsOneSsrcStream(const StreamParams& sp); - bool HasReadySendChannels(); // Send channel key returns the key corresponding to the provided local SSRC @@ -365,11 +366,12 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, // If the local ssrc correspond to that of the default channel the key is 0. // For all other channels the returned key will be the same as the local ssrc. bool GetSendChannelKey(uint32 local_ssrc, uint32* key); - WebRtcVideoChannelSendInfo* GetSendChannel(VideoCapturer* video_capturer); WebRtcVideoChannelSendInfo* GetSendChannel(uint32 local_ssrc); // Creates a new unique key that can be used for inserting a new send channel // into |send_channels_| bool CreateSendChannelKey(uint32 local_ssrc, uint32* key); + // Get the number of the send channels |capturer| registered with. + int GetSendChannelNum(VideoCapturer* capturer); bool IsDefaultChannel(int channel_id) const { return channel_id == vie_channel_; @@ -399,6 +401,17 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, // Signal when cpu adaptation has no further scope to adapt. void OnCpuAdaptationUnable(); + // Set the local (send-side) RTX SSRC corresponding to primary_ssrc. + bool SetLocalRtxSsrc(int channel_id, const StreamParams& send_params, + uint32 primary_ssrc, int stream_idx); + + // Connect |capturer| to WebRtcVideoMediaChannel if it is only registered + // to one send channel, i.e. the first send channel. + void MaybeConnectCapturer(VideoCapturer* capturer); + // Disconnect |capturer| from WebRtcVideoMediaChannel if it is only registered + // to one send channel, i.e. the last send channel. + void MaybeDisconnectCapturer(VideoCapturer* capturer); + // Global state. WebRtcVideoEngine* engine_; VoiceMediaChannel* voice_channel_; @@ -422,6 +435,7 @@ class WebRtcVideoMediaChannel : public talk_base::MessageHandler, // Global send side state. SendChannelMap send_channels_; talk_base::scoped_ptr<webrtc::VideoCodec> send_codec_; + int send_rtx_type_; int send_red_type_; int send_fec_type_; int send_min_bitrate_; 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 04662caf7c1..d5886a13822 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 @@ -69,6 +69,12 @@ 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 kRtxSsrcs3[] = {4, 5, 6}; + class FakeViEWrapper : public cricket::ViEWrapper { public: @@ -117,11 +123,7 @@ class WebRtcVideoEngineTestFake : public testing::Test, return false; } cricket::WebRtcVideoFrame frame; - size_t size = width * height * 3 / 2; // I420 - talk_base::scoped_array<uint8> pixel(new uint8[size]); - if (!frame.Init(cricket::FOURCC_I420, - width, height, width, height, - pixel.get(), size, 1, 1, 0, 0, 0)) { + if (!frame.InitToBlack(width, height, 1, 1, 0, 0)) { return false; } cricket::FakeVideoCapturer capturer; @@ -137,11 +139,7 @@ class WebRtcVideoEngineTestFake : public testing::Test, return false; } cricket::WebRtcVideoFrame frame; - size_t size = width * height * 3 / 2; // I420 - talk_base::scoped_array<uint8> pixel(new uint8[size]); - if (!frame.Init(cricket::FOURCC_I420, - width, height, width, height, - pixel.get(), size, 1, 1, 0, timestamp, 0)) { + if (!frame.InitToBlack(width, height, 1, 1, 0, 0)) { return false; } cricket::FakeVideoCapturer capturer; @@ -380,6 +378,25 @@ TEST_F(WebRtcVideoEngineTestFake, SetOptionsWithMaxBitrate) { channel_num, kVP8Codec.width, kVP8Codec.height, 0, 20, 10, 20); } +TEST_F(WebRtcVideoEngineTestFake, SetOptionsWithLoweredBitrate) { + EXPECT_TRUE(SetupEngine()); + int channel_num = vie_.GetLastChannel(); + std::vector<cricket::VideoCodec> codecs(engine_.codecs()); + codecs[0].params[cricket::kCodecParamMinBitrate] = "50"; + codecs[0].params[cricket::kCodecParamMaxBitrate] = "100"; + EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + + VerifyVP8SendCodec( + channel_num, kVP8Codec.width, kVP8Codec.height, 0, 100, 50, 100); + + // Verify that min bitrate changes after SetOptions(). + cricket::VideoOptions options; + options.lower_min_bitrate.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + VerifyVP8SendCodec( + channel_num, kVP8Codec.width, kVP8Codec.height, 0, 100, 30, 100); +} + TEST_F(WebRtcVideoEngineTestFake, MaxBitrateResetWithConferenceMode) { EXPECT_TRUE(SetupEngine()); int channel_num = vie_.GetLastChannel(); @@ -637,6 +654,45 @@ TEST_F(WebRtcVideoEngineTestFake, RembEnabledOnReceiveChannels) { EXPECT_TRUE(vie_.GetRembStatusContribute(new_channel_num)); } +TEST_F(WebRtcVideoEngineTestFake, RecvStreamWithRtx) { + EXPECT_TRUE(SetupEngine()); + int default_channel = vie_.GetLastChannel(); + cricket::VideoOptions options; + options.conference_mode.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + EXPECT_TRUE(channel_->AddSendStream( + cricket::CreateSimWithRtxStreamParams("cname", + MAKE_VECTOR(kSsrcs3), + MAKE_VECTOR(kRtxSsrcs3)))); + EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); + EXPECT_TRUE(channel_->SetSend(true)); + EXPECT_TRUE(channel_->AddRecvStream( + cricket::CreateSimWithRtxStreamParams("cname", + MAKE_VECTOR(kSsrcs1), + MAKE_VECTOR(kRtxSsrc1)))); + int new_channel_num = vie_.GetLastChannel(); + EXPECT_NE(default_channel, new_channel_num); + EXPECT_EQ(4, vie_.GetRemoteRtxSsrc(new_channel_num)); +} + +TEST_F(WebRtcVideoEngineTestFake, RecvStreamNoRtx) { + EXPECT_TRUE(SetupEngine()); + int default_channel = vie_.GetLastChannel(); + cricket::VideoOptions options; + options.conference_mode.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + EXPECT_TRUE(channel_->AddSendStream( + cricket::CreateSimWithRtxStreamParams("cname", + MAKE_VECTOR(kSsrcs3), + MAKE_VECTOR(kRtxSsrcs3)))); + EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); + EXPECT_TRUE(channel_->SetSend(true)); + EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1))); + int new_channel_num = vie_.GetLastChannel(); + EXPECT_NE(default_channel, new_channel_num); + EXPECT_EQ(-1, vie_.GetRemoteRtxSsrc(new_channel_num)); +} + // Test support for RTP timestamp offset header extension. TEST_F(WebRtcVideoEngineTestFake, RtpTimestampOffsetHeaderExtensions) { EXPECT_TRUE(SetupEngine()); @@ -1160,8 +1216,56 @@ TEST_F(WebRtcVideoEngineTestFake, SetOptionsWithDenoising) { EXPECT_FALSE(vie_.GetCaptureDenoising(capture_id)); } +TEST_F(WebRtcVideoEngineTestFake, MultipleSendStreamsWithOneCapturer) { + EXPECT_TRUE(SetupEngine()); + + // Start the capturer + cricket::FakeVideoCapturer capturer; + cricket::VideoFormat capture_format_vga = cricket::VideoFormat(640, 480, + cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_I420); + EXPECT_EQ(cricket::CS_RUNNING, capturer.Start(capture_format_vga)); + + // Add send streams and connect the capturer + for (unsigned int i = 0; i < sizeof(kSsrcs2)/sizeof(kSsrcs2[0]); ++i) { + EXPECT_TRUE(channel_->AddSendStream( + cricket::StreamParams::CreateLegacy(kSsrcs2[i]))); + // Register the capturer to the ssrc. + EXPECT_TRUE(channel_->SetCapturer(kSsrcs2[i], &capturer)); + } + + const int channel0 = vie_.GetChannelFromLocalSsrc(kSsrcs2[0]); + ASSERT_NE(-1, channel0); + const int channel1 = vie_.GetChannelFromLocalSsrc(kSsrcs2[1]); + ASSERT_NE(-1, channel1); + ASSERT_NE(channel0, 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(capturer.CaptureFrame()); + EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0)); + EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel1)); + + EXPECT_TRUE(channel_->RemoveSendStream(kSsrcs2[0])); + EXPECT_TRUE(capturer.CaptureFrame()); + // channel0 is the default channel, so it won't be deleted. + // But it should be disconnected from the capturer. + EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0)); + EXPECT_EQ(2, vie_.GetIncomingFrameNum(channel1)); + + EXPECT_TRUE(channel_->RemoveSendStream(kSsrcs2[1])); + EXPECT_TRUE(capturer.CaptureFrame()); + EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0)); + // channel1 has already been deleted. + EXPECT_EQ(-1, vie_.GetIncomingFrameNum(channel1)); +} + -TEST_F(WebRtcVideoEngineTestFake, SendReceiveBitratesStats) { +// Disabled since its flaky: b/11288120 +TEST_F(WebRtcVideoEngineTestFake, DISABLED_SendReceiveBitratesStats) { EXPECT_TRUE(SetupEngine()); cricket::VideoOptions options; options.conference_mode.Set(true); @@ -1299,7 +1403,7 @@ TEST_F(WebRtcVideoEngineTestFake, TestSetInvalidCpuThreshold) { 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(3U, c.size()); + EXPECT_EQ(4U, c.size()); cricket::VideoCodec vp8(104, "VP8", 320, 200, 30, 0); EXPECT_TRUE(engine_.FindCodec(vp8)); @@ -1334,6 +1438,24 @@ TEST_F(WebRtcVideoEngineTest, FindCodec) { 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) { @@ -1488,6 +1610,21 @@ 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; @@ -1672,8 +1809,14 @@ TEST_F(WebRtcVideoEngineTestFake, RegisterEncoderIfFactoryIsGiven) { codecs.push_back(kVP8Codec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->AddSendStream( + cricket::StreamParams::CreateLegacy(kSsrc))); + EXPECT_TRUE(vie_.ExternalEncoderRegistered(channel_num, 100)); EXPECT_EQ(1, vie_.GetNumExternalEncoderRegistered(channel_num)); + + // Remove stream previously added to free the external encoder instance. + EXPECT_TRUE(channel_->RemoveSendStream(kSsrc)); } TEST_F(WebRtcVideoEngineTestFake, DontRegisterEncoderMultipleTimes) { @@ -1686,6 +1829,9 @@ TEST_F(WebRtcVideoEngineTestFake, DontRegisterEncoderMultipleTimes) { codecs.push_back(kVP8Codec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->AddSendStream( + cricket::StreamParams::CreateLegacy(kSsrc))); + EXPECT_TRUE(vie_.ExternalEncoderRegistered(channel_num, 100)); EXPECT_EQ(1, vie_.GetNumExternalEncoderRegistered(channel_num)); EXPECT_EQ(1, encoder_factory_.GetNumCreatedEncoders()); @@ -1693,6 +1839,9 @@ TEST_F(WebRtcVideoEngineTestFake, DontRegisterEncoderMultipleTimes) { 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)); } TEST_F(WebRtcVideoEngineTestFake, RegisterEncoderWithMultipleSendStreams) { @@ -1785,8 +1934,12 @@ TEST_F(WebRtcVideoEngineTestFake, ExternalCodecAddedToTheEnd) { encoder_factory_.NotifyCodecsAvailable(); codecs = engine_.codecs(); + cricket::VideoCodec internal_codec = codecs[0]; + cricket::VideoCodec external_codec = codecs[codecs.size() - 1]; // The external codec will appear at last. - EXPECT_EQ("GENERIC", codecs[codecs.size() - 1].name); + EXPECT_EQ("GENERIC", external_codec.name); + // The internal codec is preferred. + EXPECT_GE(internal_codec.preference, external_codec.preference); } // Test that external codec with be ignored if it has the same name as one of @@ -1820,9 +1973,15 @@ TEST_F(WebRtcVideoEngineTestFake, UpdateEncoderCodecsAfterSetFactory) { codecs.push_back(kVP8Codec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); + EXPECT_TRUE(channel_->AddSendStream( + cricket::StreamParams::CreateLegacy(kSsrc))); + 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)); } // Tests that OnReadyToSend will be propagated into ViE. 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 584aac0c3f3..16aa4cdcebc 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.cc @@ -42,49 +42,74 @@ static const int kWatermarkOffsetFromLeft = 8; static const int kWatermarkOffsetFromBottom = 8; static const unsigned char kWatermarkMaxYValue = 64; -FrameBuffer::FrameBuffer() : length_(0) {} - -FrameBuffer::FrameBuffer(size_t length) : length_(0) { - char* buffer = new char[length]; - SetData(buffer, length); +// 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. +// * Buffers passed using Alias() are not owned and will not be destroyed on +// FrameBuffer destruction, The buffer then must outlive the FrameBuffer. +class WebRtcVideoFrame::FrameBuffer { + public: + FrameBuffer(); + explicit FrameBuffer(size_t length); + ~FrameBuffer(); + + void Attach(uint8* data, size_t length); + void Alias(uint8* data, size_t length); + uint8* data(); + size_t length() const; + + webrtc::VideoFrame* frame(); + const webrtc::VideoFrame* frame() const; + + private: + talk_base::scoped_ptr<uint8[]> owned_data_; + webrtc::VideoFrame video_frame_; +}; + +WebRtcVideoFrame::FrameBuffer::FrameBuffer() {} + +WebRtcVideoFrame::FrameBuffer::FrameBuffer(size_t length) { + uint8* buffer = new uint8[length]; + Attach(buffer, length); } -FrameBuffer::~FrameBuffer() { - // Make sure that the video_frame_ doesn't delete the buffer as it may be - // shared between multiple WebRtcVideoFrame. +WebRtcVideoFrame::FrameBuffer::~FrameBuffer() { + // Make sure that |video_frame_| doesn't delete the buffer, as |owned_data_| + // will release the buffer if this FrameBuffer owns it. uint8_t* new_memory = NULL; uint32_t new_length = 0; uint32_t new_size = 0; video_frame_.Swap(new_memory, new_length, new_size); } -void FrameBuffer::SetData(char* data, size_t length) { - data_.reset(data); - length_ = length; +void WebRtcVideoFrame::FrameBuffer::Attach(uint8* data, size_t length) { + Alias(data, length); + owned_data_.reset(data); +} + +void WebRtcVideoFrame::FrameBuffer::Alias(uint8* data, size_t length) { + owned_data_.reset(); uint8_t* new_memory = reinterpret_cast<uint8_t*>(data); - uint32_t new_length = static_cast<int>(length); - uint32_t new_size = static_cast<int>(length); + uint32_t new_length = static_cast<uint32_t>(length); + uint32_t new_size = static_cast<uint32_t>(length); video_frame_.Swap(new_memory, new_length, new_size); } -void FrameBuffer::ReturnData(char** data, size_t* length) { - uint8_t* old_memory = NULL; - uint32_t old_length = 0; - uint32_t old_size = 0; - video_frame_.Swap(old_memory, old_length, old_size); - data_.release(); - length_ = 0; - *length = old_length; - *data = reinterpret_cast<char*>(old_memory); +uint8* WebRtcVideoFrame::FrameBuffer::data() { + return video_frame_.Buffer(); } -char* FrameBuffer::data() { return data_.get(); } - -size_t FrameBuffer::length() const { return length_; } +size_t WebRtcVideoFrame::FrameBuffer::length() const { + return video_frame_.Length(); +} -webrtc::VideoFrame* FrameBuffer::frame() { return &video_frame_; } +webrtc::VideoFrame* WebRtcVideoFrame::FrameBuffer::frame() { + return &video_frame_; +} -const webrtc::VideoFrame* FrameBuffer::frame() const { return &video_frame_; } +const webrtc::VideoFrame* WebRtcVideoFrame::FrameBuffer::frame() const { + return &video_frame_; +} WebRtcVideoFrame::WebRtcVideoFrame() : video_buffer_(new RefCountedBuffer()), is_black_(false) {} @@ -106,6 +131,25 @@ bool WebRtcVideoFrame::Init(const CapturedFrame* frame, int dw, int dh) { frame->time_stamp, frame->rotation); } +bool WebRtcVideoFrame::Alias(const CapturedFrame* frame, int dw, int dh) { + if (CanonicalFourCC(frame->fourcc) != FOURCC_I420 || frame->rotation != 0 || + frame->width != dw || frame->height != dh) { + // TODO(fbarchard): Enable aliasing of more formats. + return Init(frame, dw, dh); + } else { + Alias(static_cast<uint8*>(frame->data), + frame->data_size, + frame->width, + frame->height, + frame->pixel_width, + frame->pixel_height, + frame->elapsed_time, + frame->time_stamp, + frame->rotation); + return true; + } +} + bool WebRtcVideoFrame::InitToBlack(int w, int h, size_t pixel_width, size_t pixel_height, int64 elapsed_time, int64 time_stamp) { @@ -116,20 +160,16 @@ bool WebRtcVideoFrame::InitToBlack(int w, int h, size_t pixel_width, return true; } -void WebRtcVideoFrame::Attach( +void WebRtcVideoFrame::Alias( uint8* buffer, size_t buffer_size, int w, int h, size_t pixel_width, size_t pixel_height, int64 elapsed_time, int64 time_stamp, int rotation) { talk_base::scoped_refptr<RefCountedBuffer> video_buffer( new RefCountedBuffer()); - video_buffer->SetData(reinterpret_cast<char*>(buffer), buffer_size); + video_buffer->Alias(buffer, buffer_size); Attach(video_buffer.get(), buffer_size, w, h, pixel_width, pixel_height, elapsed_time, time_stamp, rotation); } -void WebRtcVideoFrame::Detach(uint8** data, size_t* length) { - video_buffer_->ReturnData(reinterpret_cast<char**>(data), length); -} - size_t WebRtcVideoFrame::GetWidth() const { return frame()->Width(); } size_t WebRtcVideoFrame::GetHeight() const { return frame()->Height(); } @@ -179,7 +219,7 @@ uint8* WebRtcVideoFrame::GetVPlane() { } VideoFrame* WebRtcVideoFrame::Copy() const { - const char* old_buffer = video_buffer_->data(); + uint8* old_buffer = video_buffer_->data(); if (!old_buffer) return NULL; size_t new_buffer_size = video_buffer_->length(); @@ -192,7 +232,7 @@ VideoFrame* WebRtcVideoFrame::Copy() const { } bool WebRtcVideoFrame::MakeExclusive() { - const int length = static_cast<int>(video_buffer_->length()); + const size_t length = video_buffer_->length(); RefCountedBuffer* exclusive_buffer = new RefCountedBuffer(length); memcpy(exclusive_buffer->data(), video_buffer_->data(), length); Attach(exclusive_buffer, length, frame()->Width(), frame()->Height(), @@ -279,6 +319,14 @@ bool WebRtcVideoFrame::AddWatermark() { return true; } +webrtc::VideoFrame* WebRtcVideoFrame::frame() { + return video_buffer_->frame(); +} + +const webrtc::VideoFrame* WebRtcVideoFrame::frame() const { + return video_buffer_->frame(); +} + bool WebRtcVideoFrame::Reset( uint32 format, int w, int h, int dw, int dh, uint8* sample, size_t sample_size, size_t pixel_width, size_t pixel_height, 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 18475a69775..8191a58d875 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.h @@ -39,31 +39,8 @@ namespace cricket { struct CapturedFrame; -// Class that takes ownership of the frame passed to it. -class FrameBuffer { - public: - FrameBuffer(); - explicit FrameBuffer(size_t length); - ~FrameBuffer(); - - void SetData(char* data, size_t length); - void ReturnData(char** data, size_t* length); - char* data(); - size_t length() const; - - webrtc::VideoFrame* frame(); - const webrtc::VideoFrame* frame() const; - - private: - talk_base::scoped_array<char> data_; - size_t length_; - webrtc::VideoFrame video_frame_; -}; - class WebRtcVideoFrame : public VideoFrame { public: - typedef talk_base::RefCountedObject<FrameBuffer> RefCountedBuffer; - WebRtcVideoFrame(); ~WebRtcVideoFrame(); @@ -77,17 +54,22 @@ class WebRtcVideoFrame : public VideoFrame { bool Init(const CapturedFrame* frame, int dw, int dh); + // Aliases this WebRtcVideoFrame to a CapturedFrame. |frame| must outlive + // this WebRtcVideoFrame. + bool Alias(const CapturedFrame* frame, int dw, int dh); + bool InitToBlack(int w, int h, size_t pixel_width, size_t pixel_height, int64 elapsed_time, int64 time_stamp); - void Attach(uint8* buffer, size_t buffer_size, int w, int h, - size_t pixel_width, size_t pixel_height, int64 elapsed_time, - int64 time_stamp, int rotation); + // Aliases this WebRtcVideoFrame to a memory buffer. |buffer| must outlive + // this WebRtcVideoFrame. + void Alias(uint8* buffer, size_t buffer_size, int w, int h, + size_t pixel_width, size_t pixel_height, int64 elapsed_time, + int64 time_stamp, int rotation); - void Detach(uint8** data, size_t* length); bool AddWatermark(); - webrtc::VideoFrame* frame() { return video_buffer_->frame(); } - webrtc::VideoFrame* frame() const { return video_buffer_->frame(); } + webrtc::VideoFrame* frame(); + const webrtc::VideoFrame* frame() const; // From base class VideoFrame. virtual bool Reset(uint32 format, int w, int h, int dw, int dh, uint8* sample, @@ -126,6 +108,9 @@ class WebRtcVideoFrame : public VideoFrame { size_t size, int stride_rgb) const; private: + class FrameBuffer; + typedef talk_base::RefCountedObject<FrameBuffer> RefCountedBuffer; + void Attach(RefCountedBuffer* video_buffer, size_t buffer_size, int w, int h, size_t pixel_width, size_t pixel_height, int64 elapsed_time, int64 time_stamp, int rotation); diff --git a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe_unittest.cc b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe_unittest.cc index 2f0decb2898..e63c5d5e561 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe_unittest.cc @@ -53,7 +53,7 @@ class WebRtcVideoFrameTest : public VideoFrameTest<cricket::WebRtcVideoFrame> { captured_frame.height = frame_height; captured_frame.data_size = (frame_width * frame_height) + ((frame_width + 1) / 2) * ((frame_height + 1) / 2) * 2; - talk_base::scoped_array<uint8> captured_frame_buffer( + talk_base::scoped_ptr<uint8[]> captured_frame_buffer( new uint8[captured_frame.data_size]); captured_frame.data = captured_frame_buffer.get(); @@ -264,35 +264,16 @@ TEST_WEBRTCVIDEOFRAME(CopyIsRef) TEST_WEBRTCVIDEOFRAME(MakeExclusive) // These functions test implementation-specific details. -TEST_F(WebRtcVideoFrameTest, AttachAndRelease) { +TEST_F(WebRtcVideoFrameTest, Alias) { cricket::WebRtcVideoFrame frame1, frame2; ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); - const int64 time_stamp = 0x7FFFFFFFFFFFFFF0LL; + const int64 time_stamp = INT64_C(0x7FFFFFFFFFFFFFF0); frame1.SetTimeStamp(time_stamp); EXPECT_EQ(time_stamp, frame1.GetTimeStamp()); - frame2.Attach(frame1.frame()->Buffer(), frame1.frame()->Size(), - kWidth, kHeight, 1, 1, - frame1.GetElapsedTime(), frame1.GetTimeStamp(), 0); + frame2.Alias(frame1.frame()->Buffer(), frame1.frame()->Size(), + kWidth, kHeight, 1, 1, + frame1.GetElapsedTime(), frame1.GetTimeStamp(), 0); EXPECT_TRUE(IsEqual(frame1, frame2, 0)); - uint8* buffer; - size_t size; - frame2.Detach(&buffer, &size); - EXPECT_EQ(frame1.frame()->Buffer(), buffer); - EXPECT_EQ(frame1.frame()->Size(), size); - EXPECT_TRUE(IsNull(frame2)); - EXPECT_TRUE(IsSize(frame1, kWidth, kHeight)); -} - -TEST_F(WebRtcVideoFrameTest, Transfer) { - cricket::WebRtcVideoFrame frame1, frame2; - ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); - uint8* buffer; - size_t size; - frame1.Detach(&buffer, &size); - frame2.Attach(buffer, size, kWidth, kHeight, 1, 1, - frame1.GetElapsedTime(), frame1.GetTimeStamp(), 0); - EXPECT_TRUE(IsNull(frame1)); - EXPECT_TRUE(IsSize(frame2, kWidth, kHeight)); } // Tests the Init function with different cropped size. 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 86860389314..3bdc4039c85 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.cc +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.cc @@ -50,6 +50,7 @@ #include "talk/media/base/streamparams.h" #include "talk/media/base/voiceprocessor.h" #include "talk/media/webrtc/webrtcvoe.h" +#include "webrtc/common.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" #ifdef WIN32 @@ -124,6 +125,10 @@ static const int kOpusStereoBitrate = 64000; // Opus bitrate should be in the range between 6000 and 510000. static const int kOpusMinBitrate = 6000; static const int kOpusMaxBitrate = 510000; +// Default audio dscp value. +// See http://tools.ietf.org/html/rfc2474 for details. +// See also http://tools.ietf.org/html/draft-jennings-rtcweb-qos-00 +static const talk_base::DiffServCodePoint kAudioDscpValue = talk_base::DSCP_EF; // Ensure we open the file in a writeable path on ChromeOS and Android. This // workaround can be removed when it's possible to specify a filename for audio @@ -203,11 +208,31 @@ static bool FindCodec(const std::vector<AudioCodec>& codecs, } return false; } + static bool IsNackEnabled(const AudioCodec& codec) { return codec.HasFeedbackParam(FeedbackParam(kRtcpFbParamNack, kParamValueEmpty)); } +// Gets the default set of options applied to the engine. Historically, these +// were supplied as a combination of flags from the channel manager (ec, agc, +// ns, and highpass) and the rest hardcoded in InitInternal. +static AudioOptions GetDefaultEngineOptions() { + AudioOptions options; + options.echo_cancellation.Set(true); + options.auto_gain_control.Set(true); + options.noise_suppression.Set(true); + options.highpass_filter.Set(true); + options.stereo_swapping.Set(false); + options.typing_detection.Set(true); + options.conference_mode.Set(false); + options.adjust_agc_delta.Set(0); + options.experimental_agc.Set(false); + options.experimental_aec.Set(false); + options.aec_dump.Set(false); + options.experimental_acm.Set(false); + return options; +} class WebRtcSoundclipMedia : public SoundclipMedia { public: @@ -233,7 +258,10 @@ class WebRtcSoundclipMedia : public SoundclipMedia { } bool Init() { - webrtc_channel_ = engine_->voe_sc()->base()->CreateChannel(); + if (!engine_->voe_sc()) { + return false; + } + webrtc_channel_ = engine_->CreateSoundclipVoiceChannel(); if (webrtc_channel_ == -1) { LOG_RTCERR0(CreateChannel); return false; @@ -299,12 +327,14 @@ class WebRtcSoundclipMedia : public SoundclipMedia { WebRtcVoiceEngine::WebRtcVoiceEngine() : voe_wrapper_(new VoEWrapper()), voe_wrapper_sc_(new VoEWrapper()), + voe_wrapper_sc_initialized_(false), tracing_(new VoETraceWrapper()), adm_(NULL), adm_sc_(NULL), 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(); @@ -315,12 +345,14 @@ WebRtcVoiceEngine::WebRtcVoiceEngine(VoEWrapper* voe_wrapper, VoETraceWrapper* tracing) : voe_wrapper_(voe_wrapper), voe_wrapper_sc_(voe_wrapper_sc), + voe_wrapper_sc_initialized_(false), tracing_(tracing), adm_(NULL), adm_sc_(NULL), 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(); @@ -347,6 +379,11 @@ void WebRtcVoiceEngine::Construct() { rtp_header_extensions_.push_back( RtpHeaderExtension(kRtpAudioLevelHeaderExtension, kRtpAudioLevelHeaderExtensionId)); + options_ = GetDefaultEngineOptions(); + + // Initialize the VoE Configuration to the default ACM. + voe_config_.Set<webrtc::AudioCodingModuleFactory>( + new webrtc::AudioCodingModuleFactory); } static bool IsOpus(const AudioCodec& codec) { @@ -502,11 +539,14 @@ bool WebRtcVoiceEngine::InitInternal() { // Save the default AGC configuration settings. This must happen before // calling SetOptions or the default will be overwritten. if (voe_wrapper_->processing()->GetAgcConfig(default_agc_config_) == -1) { - LOG_RTCERR0(GetAGCConfig); + LOG_RTCERR0(GetAgcConfig); return false; } - if (!SetOptions(MediaEngineInterface::DEFAULT_AUDIO_OPTIONS)) { + // Set defaults for options, so that ApplyOptions applies them explicitly + // when we clear option (channel) overrides. External clients can still + // modify the defaults via SetOptions (on the media engine). + if (!SetOptions(GetDefaultEngineOptions())) { return false; } @@ -517,6 +557,23 @@ bool WebRtcVoiceEngine::InitInternal() { LOG(LS_INFO) << ToString(*it); } + // Disable the DTMF playout when a tone is sent. + // PlayDtmfTone will be used if local playout is needed. + if (voe_wrapper_->dtmf()->SetDtmfFeedbackStatus(false) == -1) { + LOG_RTCERR1(SetDtmfFeedbackStatus, false); + } + + initialized_ = true; + return true; +} + +bool WebRtcVoiceEngine::EnsureSoundclipEngineInit() { + if (voe_wrapper_sc_initialized_) { + return true; + } + // Note that, if initialization fails, voe_wrapper_sc_initialized_ will still + // be false, so subsequent calls to EnsureSoundclipEngineInit will + // probably just fail again. That's acceptable behavior. #if defined(LINUX) && !defined(HAVE_LIBPULSE) voe_wrapper_sc_->hw()->SetAudioDeviceLayer(webrtc::kAudioLinuxAlsa); #endif @@ -550,14 +607,8 @@ bool WebRtcVoiceEngine::InitInternal() { } } #endif - - // Disable the DTMF playout when a tone is sent. - // PlayDtmfTone will be used if local playout is needed. - if (voe_wrapper_->dtmf()->SetDtmfFeedbackStatus(false) == -1) { - LOG_RTCERR1(SetDtmfFeedbackStatus, false); - } - - initialized_ = true; + voe_wrapper_sc_initialized_ = true; + LOG(LS_INFO) << "Initialized WebRtc soundclip engine."; return true; } @@ -567,7 +618,10 @@ void WebRtcVoiceEngine::Terminate() { StopAecDump(); - voe_wrapper_sc_->base()->Terminate(); + if (voe_wrapper_sc_) { + voe_wrapper_sc_initialized_ = false; + voe_wrapper_sc_->base()->Terminate(); + } voe_wrapper_->base()->Terminate(); desired_local_monitor_enable_ = false; } @@ -586,6 +640,11 @@ VoiceMediaChannel *WebRtcVoiceEngine::CreateChannel() { } SoundclipMedia *WebRtcVoiceEngine::CreateSoundclip() { + if (!EnsureSoundclipEngineInit()) { + LOG(LS_ERROR) << "Unable to create soundclip: soundclip engine failed to " + << "initialize."; + return NULL; + } WebRtcSoundclipMedia *soundclip = new WebRtcSoundclipMedia(this); if (!soundclip->Init() || !soundclip->Enable()) { delete soundclip; @@ -594,35 +653,7 @@ SoundclipMedia *WebRtcVoiceEngine::CreateSoundclip() { return soundclip; } -// TODO(zhurunz): Add a comprehensive unittests for SetOptions(). -bool WebRtcVoiceEngine::SetOptions(int flags) { - AudioOptions options; - - // Convert flags to AudioOptions. - options.echo_cancellation.Set( - ((flags & MediaEngineInterface::ECHO_CANCELLATION) != 0)); - options.auto_gain_control.Set( - ((flags & MediaEngineInterface::AUTO_GAIN_CONTROL) != 0)); - options.noise_suppression.Set( - ((flags & MediaEngineInterface::NOISE_SUPPRESSION) != 0)); - options.highpass_filter.Set( - ((flags & MediaEngineInterface::HIGHPASS_FILTER) != 0)); - options.stereo_swapping.Set( - ((flags & MediaEngineInterface::STEREO_FLIPPING) != 0)); - - // Set defaults for flagless options here. Make sure they are all set so that - // ApplyOptions applies all of them when we clear overrides. - options.typing_detection.Set(true); - options.conference_mode.Set(false); - options.adjust_agc_delta.Set(0); - options.experimental_agc.Set(false); - options.experimental_aec.Set(false); - options.aec_dump.Set(false); - - return SetAudioOptions(options); -} - -bool WebRtcVoiceEngine::SetAudioOptions(const AudioOptions& options) { +bool WebRtcVoiceEngine::SetOptions(const AudioOptions& options) { if (!ApplyOptions(options)) { return false; } @@ -666,6 +697,10 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { webrtc::AgcModes agc_mode = webrtc::kAgcAdaptiveAnalog; webrtc::NsModes ns_mode = webrtc::kNsHighSuppression; bool aecm_comfort_noise = false; + if (options.aecm_generate_comfort_noise.Get(&aecm_comfort_noise)) { + LOG(LS_VERBOSE) << "Comfort noise explicitly set to " + << aecm_comfort_noise << " (default is false)."; + } #if defined(IOS) // On iOS, VPIO provides built-in EC and AGC. @@ -684,9 +719,14 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { options.experimental_aec.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; @@ -694,6 +734,9 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { if (voep->SetEcStatus(echo_cancellation, ec_mode) == -1) { LOG_RTCERR2(SetEcStatus, echo_cancellation, ec_mode); return false; + } else { + LOG(LS_VERBOSE) << "Echo control set to " << echo_cancellation + << " with mode " << ec_mode; } #if !defined(ANDROID) // TODO(ajm): Remove the error return on Android from webrtc. @@ -715,6 +758,38 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { if (voep->SetAgcStatus(auto_gain_control, agc_mode) == -1) { LOG_RTCERR2(SetAgcStatus, auto_gain_control, agc_mode); return false; + } else { + LOG(LS_VERBOSE) << "Auto gain set to " << auto_gain_control + << " with mode " << agc_mode; + } + } + + if (options.tx_agc_target_dbov.IsSet() || + options.tx_agc_digital_compression_gain.IsSet() || + options.tx_agc_limiter.IsSet()) { + // Override default_agc_config_. Generally, an unset option means "leave + // the VoE bits alone" in this function, so we want whatever is set to be + // stored as the new "default". If we didn't, then setting e.g. + // tx_agc_target_dbov would reset digital compression gain and limiter + // settings. + // Also, if we don't update default_agc_config_, then adjust_agc_delta + // would be an offset from the original values, and not whatever was set + // explicitly. + default_agc_config_.targetLeveldBOv = + options.tx_agc_target_dbov.GetWithDefaultIfUnset( + default_agc_config_.targetLeveldBOv); + default_agc_config_.digitalCompressionGaindB = + options.tx_agc_digital_compression_gain.GetWithDefaultIfUnset( + default_agc_config_.digitalCompressionGaindB); + default_agc_config_.limiterEnable = + options.tx_agc_limiter.GetWithDefaultIfUnset( + default_agc_config_.limiterEnable); + if (voe_wrapper_->processing()->SetAgcConfig(default_agc_config_) == -1) { + LOG_RTCERR3(SetAgcConfig, + default_agc_config_.targetLeveldBOv, + default_agc_config_.digitalCompressionGaindB, + default_agc_config_.limiterEnable); + return false; } } @@ -723,6 +798,9 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { if (voep->SetNsStatus(noise_suppression, ns_mode) == -1) { LOG_RTCERR2(SetNsStatus, noise_suppression, ns_mode); return false; + } else { + LOG(LS_VERBOSE) << "Noise suppression set to " << noise_suppression + << " with mode " << ns_mode; } } @@ -766,6 +844,34 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { StopAecDump(); } + bool experimental_aec; + if (options.experimental_aec.Get(&experimental_aec)) { + 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) { + webrtc::Config config; + config.Set<webrtc::DelayCorrection>( + new webrtc::DelayCorrection(experimental_aec)); + audioproc->SetExtraOptions(config); + } + } + + uint32 recording_sample_rate; + if (options.recording_sample_rate.Get(&recording_sample_rate)) { + if (voe_wrapper_->hw()->SetRecordingSampleRate(recording_sample_rate)) { + LOG_RTCERR1(SetRecordingSampleRate, recording_sample_rate); + } + } + + uint32 playout_sample_rate; + if (options.playout_sample_rate.Get(&playout_sample_rate)) { + if (voe_wrapper_->hw()->SetPlayoutSampleRate(playout_sample_rate)) { + LOG_RTCERR1(SetPlayoutSampleRate, playout_sample_rate); + } + } + return true; } @@ -796,7 +902,7 @@ struct ResumeEntry { // soundclip device. At that time, reinstate the soundclip pause/resume code. bool WebRtcVoiceEngine::SetDevices(const Device* in_device, const Device* out_device) { -#if !defined(IOS) && !defined(ANDROID) +#if !defined(IOS) int in_id = in_device ? talk_base::FromString<int>(in_device->id) : kDefaultAudioDeviceId; int out_id = out_device ? talk_base::FromString<int>(out_device->id) : @@ -847,7 +953,7 @@ bool WebRtcVoiceEngine::SetDevices(const Device* in_device, } if (ret) { if (voe_wrapper_->hw()->SetRecordingDevice(in_id) == -1) { - LOG_RTCERR2(SetRecordingDevice, in_device->name, in_id); + LOG_RTCERR2(SetRecordingDevice, in_name, in_id); ret = false; } } @@ -859,7 +965,7 @@ bool WebRtcVoiceEngine::SetDevices(const Device* in_device, } if (ret) { if (voe_wrapper_->hw()->SetPlayoutDevice(out_id) == -1) { - LOG_RTCERR2(SetPlayoutDevice, out_device->name, out_id); + LOG_RTCERR2(SetPlayoutDevice, out_name, out_id); ret = false; } } @@ -893,13 +999,13 @@ bool WebRtcVoiceEngine::SetDevices(const Device* in_device, return ret; #else return true; -#endif // !IOS && !ANDROID +#endif // !IOS } bool WebRtcVoiceEngine::FindWebRtcAudioDeviceId( bool is_input, const std::string& dev_name, int dev_id, int* rtc_id) { // In Linux, VoiceEngine uses the same device dev_id as the device manager. -#ifdef LINUX +#if defined(LINUX) || defined(ANDROID) *rtc_id = dev_id; return true; #else @@ -1103,6 +1209,18 @@ void WebRtcVoiceEngine::SetTraceOptions(const std::string& options) { } } + // Allow trace options to override the trace filter. We default + // it to log_filter_ (as a translation of libjingle log levels) + // elsewhere, but this allows clients to explicitly set webrtc + // log levels. + std::vector<std::string>::iterator tracefilter = + std::find(opts.begin(), opts.end(), "tracefilter"); + if (tracefilter != opts.end() && ++tracefilter != opts.end()) { + if (!tracing_->SetTraceFilter(talk_base::FromString<int>(*tracefilter))) { + LOG_RTCERR1(SetTraceFilter, *tracefilter); + } + } + // Set AEC dump file std::vector<std::string>::iterator recordEC = std::find(opts.begin(), opts.end(), "recordEC"); @@ -1143,6 +1261,21 @@ 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; @@ -1475,11 +1608,26 @@ void WebRtcVoiceEngine::StopAecDump() { } } +int WebRtcVoiceEngine::CreateVoiceChannel(VoEWrapper* voice_engine_wrapper) { + return voice_engine_wrapper->base()->CreateChannel(voe_config_); +} + +int WebRtcVoiceEngine::CreateMediaVoiceChannel() { + return CreateVoiceChannel(voe_wrapper_.get()); +} + +int WebRtcVoiceEngine::CreateSoundclipVoiceChannel() { + return CreateVoiceChannel(voe_wrapper_sc_.get()); +} + // WebRtcVoiceMediaChannel WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel(WebRtcVoiceEngine *engine) : WebRtcMediaChannel<VoiceMediaChannel, WebRtcVoiceEngine>( engine, - engine->voe()->base()->CreateChannel()), + engine->CreateMediaVoiceChannel()), + send_bw_setting_(false), + send_autobw_(false), + send_bw_bps_(0), options_(), dtmf_allowed_(false), desired_playout_(false), @@ -1520,6 +1668,9 @@ bool WebRtcVoiceMediaChannel::SetOptions(const AudioOptions& options) { LOG(LS_INFO) << "Setting voice channel options: " << options.ToString(); + // Check if DSCP value is changed from previous. + bool dscp_option_changed = (options_.dscp != options.dscp); + // TODO(xians): Add support to set different options for different send // streams after we support multiple APMs. @@ -1538,6 +1689,64 @@ bool WebRtcVoiceMediaChannel::SetOptions(const AudioOptions& options) { // Will be interpreted when appropriate. } + // Receiver-side auto gain control happens per channel, so set it here from + // options. Note that, like conference mode, setting it on the engine won't + // have the desired effect, since voice channels don't inherit options from + // the media engine when those options are applied per-channel. + bool rx_auto_gain_control; + if (options.rx_auto_gain_control.Get(&rx_auto_gain_control)) { + if (engine()->voe()->processing()->SetRxAgcStatus( + voe_channel(), rx_auto_gain_control, + webrtc::kAgcFixedDigital) == -1) { + LOG_RTCERR1(SetRxAgcStatus, rx_auto_gain_control); + return false; + } else { + LOG(LS_VERBOSE) << "Rx auto gain set to " << rx_auto_gain_control + << " with mode " << webrtc::kAgcFixedDigital; + } + } + if (options.rx_agc_target_dbov.IsSet() || + options.rx_agc_digital_compression_gain.IsSet() || + options.rx_agc_limiter.IsSet()) { + webrtc::AgcConfig config; + // If only some of the options are being overridden, get the current + // settings for the channel and bail if they aren't available. + if (!options.rx_agc_target_dbov.IsSet() || + !options.rx_agc_digital_compression_gain.IsSet() || + !options.rx_agc_limiter.IsSet()) { + if (engine()->voe()->processing()->GetRxAgcConfig( + voe_channel(), config) != 0) { + LOG(LS_ERROR) << "Failed to get default rx agc configuration for " + << "channel " << voe_channel() << ". Since not all rx " + << "agc options are specified, unable to safely set rx " + << "agc options."; + return false; + } + } + config.targetLeveldBOv = + options.rx_agc_target_dbov.GetWithDefaultIfUnset( + config.targetLeveldBOv); + config.digitalCompressionGaindB = + options.rx_agc_digital_compression_gain.GetWithDefaultIfUnset( + config.digitalCompressionGaindB); + config.limiterEnable = options.rx_agc_limiter.GetWithDefaultIfUnset( + config.limiterEnable); + if (engine()->voe()->processing()->SetRxAgcConfig( + voe_channel(), config) == -1) { + LOG_RTCERR4(SetRxAgcConfig, voe_channel(), config.targetLeveldBOv, + config.digitalCompressionGaindB, config.limiterEnable); + return false; + } + } + if (dscp_option_changed) { + talk_base::DiffServCodePoint dscp = talk_base::DSCP_DEFAULT; + if (options.dscp.GetWithDefaultIfUnset(false)) + dscp = kAudioDscpValue; + if (MediaChannel::SetDscp(dscp) != 0) { + LOG(LS_WARNING) << "Failed to set DSCP settings for audio channel"; + } + } + LOG(LS_INFO) << "Set voice channel options. Current options: " << options_.ToString(); return true; @@ -1784,6 +1993,10 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( // 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; } @@ -2045,7 +2258,7 @@ bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) { channel = voe_channel(); } else { // Create a new channel for sending audio data. - channel = engine()->voe()->base()->CreateChannel(); + channel = engine()->CreateMediaVoiceChannel(); if (channel == -1) { LOG_RTCERR0(CreateChannel); return false; @@ -2138,6 +2351,11 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) { return false; uint32 ssrc = sp.first_ssrc(); + if (ssrc == 0) { + LOG(LS_WARNING) << "AddRecvStream with 0 ssrc is not supported."; + return false; + } + if (receive_channels_.find(ssrc) != receive_channels_.end()) { LOG(LS_ERROR) << "Stream already exists with ssrc " << ssrc; return false; @@ -2155,12 +2373,27 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) { } // Create a new channel for receiving audio data. - int channel = engine()->voe()->base()->CreateChannel(); + int channel = engine()->CreateMediaVoiceChannel(); if (channel == -1) { LOG_RTCERR0(CreateChannel); return false; } + if (!ConfigureRecvChannel(channel)) { + DeleteChannel(channel); + return false; + } + + receive_channels_.insert( + std::make_pair(ssrc, WebRtcVoiceChannelInfo(channel, NULL))); + + LOG(LS_INFO) << "New audio stream " << ssrc + << " registered to VoiceEngine channel #" + << channel << "."; + return true; +} + +bool WebRtcVoiceMediaChannel::ConfigureRecvChannel(int channel) { // Configure to use external transport, like our default channel. if (engine()->voe()->network()->RegisterExternalTransport( channel, *this) == -1) { @@ -2217,13 +2450,6 @@ bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) { } SetNack(channel, nack_enabled_); - receive_channels_.insert( - std::make_pair(ssrc, WebRtcVoiceChannelInfo(channel, NULL))); - - // TODO(juberti): We should rollback the add if SetPlayout fails. - LOG(LS_INFO) << "New audio stream " << ssrc - << " registered to VoiceEngine channel #" - << channel << "."; return SetPlayout(channel, playout_); } @@ -2582,7 +2808,8 @@ bool WebRtcVoiceMediaChannel::InsertDtmf(uint32 ssrc, int event, return true; } -void WebRtcVoiceMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { +void WebRtcVoiceMediaChannel::OnPacketReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { // Pick which channel to send this packet to. If this packet doesn't match // any multiplexed streams, just send it to the default channel. Otherwise, // send it to the specific decoder instance for that stream. @@ -2615,7 +2842,8 @@ void WebRtcVoiceMediaChannel::OnPacketReceived(talk_base::Buffer* packet) { static_cast<unsigned int>(packet->length())); } -void WebRtcVoiceMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) { +void WebRtcVoiceMediaChannel::OnRtcpReceived( + talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { // Sending channels need all RTCP packets with feedback information. // Even sender reports can contain attached report blocks. // Receiving channels need sender reports in order to create @@ -2674,9 +2902,20 @@ bool WebRtcVoiceMediaChannel::MuteStream(uint32 ssrc, bool muted) { bool WebRtcVoiceMediaChannel::SetSendBandwidth(bool autobw, int bps) { LOG(LS_INFO) << "WebRtcVoiceMediaChanne::SetSendBandwidth."; + send_bw_setting_ = true; + send_autobw_ = autobw; + send_bw_bps_ = bps; + + return SetSendBandwidthInternal(send_autobw_, send_bw_bps_); +} + +bool WebRtcVoiceMediaChannel::SetSendBandwidthInternal(bool autobw, int bps) { + LOG(LS_INFO) << "WebRtcVoiceMediaChanne::SetSendBandwidthInternal."; + if (!send_codec_) { - LOG(LS_INFO) << "The send codec has not been set up yet."; - return false; + LOG(LS_INFO) << "The send codec has not been set up yet. " + << "The send bandwidth setting will be applied later."; + return true; } // Bandwidth is auto by default. @@ -2738,7 +2977,6 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { } } - webrtc::CallStatistics cs; unsigned int ssrc; webrtc::CodecInst codec; @@ -2757,7 +2995,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { continue; } - sinfo.ssrc = ssrc; + sinfo.add_ssrc(ssrc); sinfo.codec_name = send_codec_.get() ? send_codec_->plname : ""; sinfo.bytes_sent = cs.bytesSent; sinfo.packets_sent = cs.packetsSent; @@ -2779,7 +3017,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { for (iter = receive_blocks.begin(); iter != receive_blocks.end(); ++iter) { // Lookup report for send ssrc only. - if (iter->source_SSRC == sinfo.ssrc) { + if (iter->source_SSRC == sinfo.ssrc()) { // Convert Q8 to floating point. sinfo.fraction_lost = static_cast<float>(iter->fraction_lost) / 256; // Convert samples to milliseconds. @@ -2806,6 +3044,8 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { sinfo.echo_return_loss_enhancement = echo_return_loss_enhancement; sinfo.echo_delay_median_ms = echo_delay_median_ms; sinfo.echo_delay_std_ms = echo_delay_std_ms; + // TODO(ajm): Re-enable this metric once we have a reliable implementation. + sinfo.aec_quality_min = -1; sinfo.typing_noise_detected = typing_noise_detected_; info->senders.push_back(sinfo); @@ -2830,7 +3070,7 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { engine()->voe()->rtp()->GetRTCPStatistics(*it, cs) != -1 && engine()->voe()->codec()->GetRecCodec(*it, codec) != -1) { VoiceReceiverInfo rinfo; - rinfo.ssrc = ssrc; + rinfo.add_ssrc(ssrc); rinfo.bytes_rcvd = cs.bytesReceived; rinfo.packets_rcvd = cs.packetsReceived; // The next four fields are from the most recently sent RTCP report. @@ -2854,9 +3094,12 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info) { static_cast<float>(ns.currentExpandRate) / (1 << 14); } if (engine()->voe()->sync()) { + int jitter_buffer_delay_ms = 0; int playout_buffer_delay_ms = 0; engine()->voe()->sync()->GetDelayEstimate( - *it, &rinfo.delay_estimate_ms, &playout_buffer_delay_ms); + *it, &jitter_buffer_delay_ms, &playout_buffer_delay_ms); + rinfo.delay_estimate_ms = jitter_buffer_delay_ms + + playout_buffer_delay_ms; } // Get speech level. @@ -2915,13 +3158,11 @@ bool WebRtcVoiceMediaChannel::FindSsrc(int channel_num, uint32* ssrc) { } void WebRtcVoiceMediaChannel::OnError(uint32 ssrc, int error) { -#ifdef USE_WEBRTC_DEV_BRANCH if (error == VE_TYPING_NOISE_WARNING) { typing_noise_detected_ = true; } else if (error == VE_TYPING_NOISE_OFF_WARNING) { typing_noise_detected_ = false; } -#endif SignalMediaError(ssrc, WebRtcErrorToChannelError(error)); } 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 62d3bc10a25..6a2c6d80f8f 100644 --- a/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.h +++ b/chromium/third_party/libjingle/source/talk/media/webrtc/webrtcvoiceengine.h @@ -43,6 +43,8 @@ #include "talk/media/webrtc/webrtcexport.h" #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) @@ -105,12 +107,8 @@ class WebRtcVoiceEngine SoundclipMedia* CreateSoundclip(); - // TODO(pthatcher): Rename to SetOptions and replace the old - // flags-based SetOptions. - bool SetAudioOptions(const AudioOptions& options); - // Eventually, we will replace them with AudioOptions. - // In the meantime, we leave this here for backwards compat. - bool SetOptions(int flags); + AudioOptions GetOptions() const { return options_; } + bool SetOptions(const AudioOptions& options); // Overrides, when set, take precedence over the options on a // per-option basis. For example, if AGC is set in options and AEC // is set in overrides, AGC and AEC will be both be set. Overrides @@ -179,6 +177,10 @@ class WebRtcVoiceEngine // Check whether the supplied trace should be ignored. bool ShouldIgnoreTrace(const std::string& trace); + // Create a VoiceEngine Channel. + int CreateMediaVoiceChannel(); + int CreateSoundclipVoiceChannel(); + private: typedef std::vector<WebRtcSoundclipMedia *> SoundclipList; typedef std::vector<WebRtcVoiceMediaChannel *> ChannelList; @@ -188,6 +190,7 @@ class WebRtcVoiceEngine void Construct(); void ConstructCodecs(); bool InitInternal(); + bool EnsureSoundclipEngineInit(); void SetTraceFilter(int filter); void SetTraceOptions(const std::string& options); // Applies either options or overrides. Every option that is "set" @@ -195,6 +198,9 @@ 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 @@ -218,6 +224,7 @@ class WebRtcVoiceEngine void StartAecDump(const std::string& filename); void StopAecDump(); + int CreateVoiceChannel(VoEWrapper* voe); // When a voice processor registers with the engine, it is connected // to either the Rx or Tx signals, based on the direction parameter. @@ -231,6 +238,7 @@ class WebRtcVoiceEngine talk_base::scoped_ptr<VoEWrapper> voe_wrapper_; // A secondary instance, for playing out soundclips (on the 'ring' device). talk_base::scoped_ptr<VoEWrapper> voe_wrapper_sc_; + bool voe_wrapper_sc_initialized_; talk_base::scoped_ptr<VoETraceWrapper> tracing_; // The external audio device manager webrtc::AudioDeviceModule* adm_; @@ -248,6 +256,10 @@ class WebRtcVoiceEngine // callback as well as the RegisterChannel/UnregisterChannel. talk_base::CriticalSection channels_cs_; webrtc::AgcConfig default_agc_config_; + + webrtc::Config voe_config_; + bool use_experimental_acm_; + bool initialized_; // See SetOptions and SetOptionOverrides for a description of the // difference between options and overrides. @@ -344,8 +356,10 @@ class WebRtcVoiceMediaChannel virtual bool CanInsertDtmf(); virtual bool InsertDtmf(uint32 ssrc, int event, int duration, int flags); - virtual void OnPacketReceived(talk_base::Buffer* packet); - virtual void OnRtcpReceived(talk_base::Buffer* packet); + virtual void OnPacketReceived(talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); + virtual void OnRtcpReceived(talk_base::Buffer* packet, + 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); @@ -396,6 +410,7 @@ class WebRtcVoiceMediaChannel bool ChangeSend(SendFlags send); bool ChangeSend(int channel, SendFlags send); void ConfigureSendChannel(int channel); + bool ConfigureRecvChannel(int channel); bool DeleteChannel(int channel); bool InConferenceMode() const { return options_.conference_mode.GetWithDefaultIfUnset(false); @@ -404,12 +419,16 @@ class WebRtcVoiceMediaChannel return channel_id == voe_channel(); } bool SetSendCodecs(int channel, const std::vector<AudioCodec>& codecs); + bool SetSendBandwidthInternal(bool autobw, int bps); talk_base::scoped_ptr<WebRtcSoundclipStream> ringback_tone_; std::set<int> ringback_channels_; // channels playing ringback std::vector<AudioCodec> recv_codecs_; 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_; bool desired_playout_; 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 3710e7ebc93..9bb681a895c 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 @@ -12,6 +12,7 @@ #include "talk/media/base/constants.h" #include "talk/media/base/fakemediaengine.h" #include "talk/media/base/fakemediaprocessor.h" +#include "talk/media/base/fakenetworkinterface.h" #include "talk/media/base/fakertp.h" #include "talk/media/webrtc/fakewebrtcvoiceengine.h" #include "talk/media/webrtc/webrtcvoiceengine.h" @@ -55,9 +56,10 @@ class FakeVoEWrapper : public cricket::VoEWrapper { } }; -class NullVoETraceWrapper : public cricket::VoETraceWrapper { +class FakeVoETraceWrapper : public cricket::VoETraceWrapper { public: virtual int SetTraceFilter(const unsigned int filter) { + filter_ = filter; return 0; } virtual int SetTraceFile(const char* fileNameUTF8) { @@ -66,6 +68,7 @@ class NullVoETraceWrapper : public cricket::VoETraceWrapper { virtual int SetTraceCallback(webrtc::TraceCallback* callback) { return 0; } + unsigned int filter_; }; class WebRtcVoiceEngineTestFake : public testing::Test { @@ -102,9 +105,10 @@ class WebRtcVoiceEngineTestFake : public testing::Test { WebRtcVoiceEngineTestFake() : voe_(kAudioCodecs, ARRAY_SIZE(kAudioCodecs)), voe_sc_(kAudioCodecs, ARRAY_SIZE(kAudioCodecs)), + trace_wrapper_(new FakeVoETraceWrapper()), engine_(new FakeVoEWrapper(&voe_), new FakeVoEWrapper(&voe_sc_), - new NullVoETraceWrapper()), + trace_wrapper_), channel_(NULL), soundclip_(NULL) { options_conference_.conference_mode.Set(true); options_adjust_agc_.adjust_agc_delta.Set(-10); @@ -135,7 +139,7 @@ class WebRtcVoiceEngineTestFake : public testing::Test { } void DeliverPacket(const void* data, int len) { talk_base::Buffer packet(data, len); - channel_->OnPacketReceived(&packet); + channel_->OnPacketReceived(&packet, talk_base::PacketTime()); } virtual void TearDown() { delete soundclip_; @@ -212,13 +216,10 @@ class WebRtcVoiceEngineTestFake : public testing::Test { codecs.push_back(codec); EXPECT_TRUE(channel_->SetSendCodecs(codecs)); - webrtc::CodecInst temp_codec; - EXPECT_FALSE(voe_.GetSendCodec(channel_num, temp_codec)); - EXPECT_EQ(default_bitrate, temp_codec.rate); - bool result = channel_->SetSendBandwidth(auto_bitrate, desired_bitrate); EXPECT_EQ(expected_result, result); + webrtc::CodecInst temp_codec; EXPECT_FALSE(voe_.GetSendCodec(channel_num, temp_codec)); if (result) { @@ -280,6 +281,7 @@ class WebRtcVoiceEngineTestFake : public testing::Test { protected: cricket::FakeWebRtcVoiceEngine voe_; cricket::FakeWebRtcVoiceEngine voe_sc_; + FakeVoETraceWrapper* trace_wrapper_; cricket::WebRtcVoiceEngine engine_; cricket::VoiceMediaChannel* channel_; cricket::SoundclipMedia* soundclip_; @@ -294,7 +296,8 @@ TEST_F(WebRtcVoiceEngineTestFake, StartupShutdown) { EXPECT_FALSE(voe_sc_.IsInited()); EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); EXPECT_TRUE(voe_.IsInited()); - EXPECT_TRUE(voe_sc_.IsInited()); + // The soundclip engine is lazily initialized. + EXPECT_FALSE(voe_sc_.IsInited()); engine_.Terminate(); EXPECT_FALSE(voe_.IsInited()); EXPECT_FALSE(voe_sc_.IsInited()); @@ -588,7 +591,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthAuto) { TestSendBandwidth(kOpusCodec, 64000, true, 96000, true); } -TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthFixedMultiRate) { +TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthFixedMultiRateAsCaller) { EXPECT_TRUE(SetupEngine()); EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs())); @@ -605,6 +608,24 @@ TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthFixedMultiRate) { TestSendBandwidth(kOpusCodec, 64000, false, 96000, true); } +TEST_F(WebRtcVoiceEngineTestFake, SetSendBandwidthFixedMultiRateAsCallee) { + 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_->AddSendStream( + cricket::StreamParams::CreateLegacy(kSsrc1))); + + int channel_num = voe_.GetLastChannel(); + webrtc::CodecInst codec; + EXPECT_EQ(0, voe_.GetSendCodec(channel_num, codec)); + EXPECT_EQ(desired_bitrate, codec.rate); +} + // 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. @@ -1632,7 +1653,7 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStatsWithMultipleSendStreams) { // Verify the statistic information is correct. for (unsigned int i = 0; i < ARRAY_SIZE(kSsrcs4); ++i) { - EXPECT_EQ(kSsrcs4[i], info.senders[i].ssrc); + EXPECT_EQ(kSsrcs4[i], info.senders[i].ssrc()); EXPECT_EQ(kPcmuCodec.name, info.senders[i].codec_name); EXPECT_EQ(cricket::kIntStatValue, info.senders[i].bytes_sent); EXPECT_EQ(cricket::kIntStatValue, info.senders[i].packets_sent); @@ -1857,6 +1878,84 @@ TEST_F(WebRtcVoiceEngineTestFake, CodianSendAndPlayout) { EXPECT_FALSE(voe_.GetPlayout(channel_num)); } +TEST_F(WebRtcVoiceEngineTestFake, TxAgcConfigViaOptions) { + EXPECT_TRUE(SetupEngine()); + webrtc::AgcConfig agc_config; + EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); + EXPECT_EQ(0, agc_config.targetLeveldBOv); + + cricket::AudioOptions options; + options.tx_agc_target_dbov.Set(3); + options.tx_agc_digital_compression_gain.Set(9); + options.tx_agc_limiter.Set(true); + options.auto_gain_control.Set(true); + EXPECT_TRUE(engine_.SetOptions(options)); + + EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); + EXPECT_EQ(3, agc_config.targetLeveldBOv); + EXPECT_EQ(9, agc_config.digitalCompressionGaindB); + EXPECT_TRUE(agc_config.limiterEnable); + + // Check interaction with adjust_agc_delta. Both should be respected, for + // backwards compatibility. + options.adjust_agc_delta.Set(-10); + EXPECT_TRUE(engine_.SetOptions(options)); + + EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); + EXPECT_EQ(13, agc_config.targetLeveldBOv); +} + +TEST_F(WebRtcVoiceEngineTestFake, RxAgcConfigViaOptions) { + EXPECT_TRUE(SetupEngine()); + int channel_num = voe_.GetLastChannel(); + cricket::AudioOptions options; + options.rx_agc_target_dbov.Set(6); + options.rx_agc_digital_compression_gain.Set(0); + options.rx_agc_limiter.Set(true); + options.rx_auto_gain_control.Set(true); + EXPECT_TRUE(channel_->SetOptions(options)); + + webrtc::AgcConfig agc_config; + EXPECT_EQ(0, engine_.voe()->processing()->GetRxAgcConfig( + channel_num, agc_config)); + EXPECT_EQ(6, agc_config.targetLeveldBOv); + EXPECT_EQ(0, agc_config.digitalCompressionGaindB); + EXPECT_TRUE(agc_config.limiterEnable); +} + +TEST_F(WebRtcVoiceEngineTestFake, SampleRatesViaOptions) { + EXPECT_TRUE(SetupEngine()); + cricket::AudioOptions options; + options.recording_sample_rate.Set(48000u); + options.playout_sample_rate.Set(44100u); + EXPECT_TRUE(engine_.SetOptions(options)); + + unsigned int recording_sample_rate, playout_sample_rate; + EXPECT_EQ(0, voe_.RecordingSampleRate(&recording_sample_rate)); + EXPECT_EQ(0, voe_.PlayoutSampleRate(&playout_sample_rate)); + EXPECT_EQ(48000u, recording_sample_rate); + EXPECT_EQ(44100u, playout_sample_rate); +} + +TEST_F(WebRtcVoiceEngineTestFake, TraceFilterViaTraceOptions) { + EXPECT_TRUE(SetupEngine()); + engine_.SetLogging(talk_base::LS_INFO, ""); + EXPECT_EQ( + // Info: + webrtc::kTraceStateInfo | webrtc::kTraceInfo | + // Warning: + webrtc::kTraceTerseInfo | webrtc::kTraceWarning | + // Error: + webrtc::kTraceError | webrtc::kTraceCritical, + static_cast<int>(trace_wrapper_->filter_)); + // Now set it explicitly + std::string filter = + "tracefilter " + talk_base::ToString(webrtc::kTraceDefault); + engine_.SetLogging(talk_base::LS_VERBOSE, filter.c_str()); + EXPECT_EQ(static_cast<unsigned int>(webrtc::kTraceDefault), + trace_wrapper_->filter_); +} + // Test that we can set the outgoing SSRC properly. // SSRC is set in SetupEngine by calling AddSendStream. TEST_F(WebRtcVoiceEngineTestFake, SetSendSsrc) { @@ -1879,7 +1978,7 @@ TEST_F(WebRtcVoiceEngineTestFake, GetStats) { cricket::VoiceMediaInfo info; EXPECT_EQ(true, channel_->GetStats(&info)); EXPECT_EQ(1u, info.senders.size()); - EXPECT_EQ(kSsrc1, info.senders[0].ssrc); + EXPECT_EQ(kSsrc1, info.senders[0].ssrc()); EXPECT_EQ(kPcmuCodec.name, info.senders[0].codec_name); EXPECT_EQ(cricket::kIntStatValue, info.senders[0].bytes_sent); EXPECT_EQ(cricket::kIntStatValue, info.senders[0].packets_sent); @@ -1961,7 +2060,7 @@ TEST_F(WebRtcVoiceEngineTestFake, RecvWithMultipleStreams) { char packets[4][sizeof(kPcmuFrame)]; for (size_t i = 0; i < ARRAY_SIZE(packets); ++i) { memcpy(packets[i], kPcmuFrame, sizeof(kPcmuFrame)); - talk_base::SetBE32(packets[i] + 8, i); + talk_base::SetBE32(packets[i] + 8, static_cast<uint32>(i)); } EXPECT_TRUE(voe_.CheckNoPacket(channel_num1)); EXPECT_TRUE(voe_.CheckNoPacket(channel_num2)); @@ -2044,6 +2143,26 @@ TEST_F(WebRtcVoiceEngineTestFake, StreamCleanup) { EXPECT_EQ(0, voe_.GetNumChannels()); } +TEST_F(WebRtcVoiceEngineTestFake, TestAddRecvStreamFailWithZeroSsrc) { + EXPECT_TRUE(SetupEngine()); + EXPECT_FALSE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(0))); +} + +TEST_F(WebRtcVoiceEngineTestFake, TestNoLeakingWhenAddRecvStreamFail) { + EXPECT_TRUE(SetupEngine()); + // Stream 1 reuses default channel. + EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1))); + // Manually delete default channel to simulate a failure. + int default_channel = voe_.GetLastChannel(); + EXPECT_EQ(0, voe_.DeleteChannel(default_channel)); + // Add recv stream 2 should fail because default channel is gone. + EXPECT_FALSE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2))); + int new_channel = voe_.GetLastChannel(); + EXPECT_NE(default_channel, new_channel); + // The last created channel should have already been deleted. + EXPECT_EQ(-1, voe_.DeleteChannel(new_channel)); +} + // Test the InsertDtmf on default send stream as caller. TEST_F(WebRtcVoiceEngineTestFake, InsertDtmfOnDefaultSendStreamAsCaller) { TestInsertDtmf(0, true); @@ -2073,7 +2192,8 @@ TEST_F(WebRtcVoiceEngineTestFake, PlayRingback) { EXPECT_FALSE(channel_->PlayRingbackTone(0, true, true)); EXPECT_EQ(0, voe_.IsPlayingFileLocally(channel_num)); // Check we can set and play a ringback tone. - EXPECT_TRUE(channel_->SetRingbackTone(kRingbackTone, strlen(kRingbackTone))); + EXPECT_TRUE(channel_->SetRingbackTone( + kRingbackTone, static_cast<int>(strlen(kRingbackTone)))); EXPECT_TRUE(channel_->PlayRingbackTone(0, true, true)); EXPECT_EQ(1, voe_.IsPlayingFileLocally(channel_num)); // Check we can stop the tone manually. @@ -2098,7 +2218,8 @@ TEST_F(WebRtcVoiceEngineTestFake, PlayRingbackWithMultipleStreams) { EXPECT_FALSE(channel_->PlayRingbackTone(2, true, true)); EXPECT_EQ(0, voe_.IsPlayingFileLocally(channel_num)); // Check we can set and play a ringback tone on the correct ssrc. - EXPECT_TRUE(channel_->SetRingbackTone(kRingbackTone, strlen(kRingbackTone))); + EXPECT_TRUE(channel_->SetRingbackTone( + kRingbackTone, static_cast<int>(strlen(kRingbackTone)))); EXPECT_FALSE(channel_->PlayRingbackTone(77, true, true)); EXPECT_TRUE(channel_->PlayRingbackTone(2, true, true)); EXPECT_EQ(1, voe_.IsPlayingFileLocally(channel_num)); @@ -2122,7 +2243,9 @@ TEST_F(WebRtcVoiceEngineTestFake, PlayRingbackWithMultipleStreams) { // Tests creating soundclips, and make sure they come from the right engine. TEST_F(WebRtcVoiceEngineTestFake, CreateSoundclip) { EXPECT_TRUE(engine_.Init(talk_base::Thread::Current())); + EXPECT_FALSE(voe_sc_.IsInited()); soundclip_ = engine_.CreateSoundclip(); + EXPECT_TRUE(voe_sc_.IsInited()); ASSERT_TRUE(soundclip_ != NULL); EXPECT_EQ(0, voe_.GetNumChannels()); EXPECT_EQ(1, voe_sc_.GetNumChannels()); @@ -2131,6 +2254,10 @@ TEST_F(WebRtcVoiceEngineTestFake, CreateSoundclip) { delete soundclip_; soundclip_ = NULL; EXPECT_EQ(0, voe_sc_.GetNumChannels()); + // Make sure the soundclip engine is uninitialized on shutdown, now that + // we've initialized it by creating a soundclip. + engine_.Terminate(); + EXPECT_FALSE(voe_sc_.IsInited()); } // Tests playing out a fake sound. @@ -2321,7 +2448,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) { // Nothing set, so all ignored. cricket::AudioOptions options; - ASSERT_TRUE(engine_.SetAudioOptions(options)); + ASSERT_TRUE(engine_.SetOptions(options)); voe_.GetEcStatus(ec_enabled, ec_mode); voe_.GetEcMetricsStatus(ec_metrics_enabled); voe_.GetAecmMode(aecm_mode, cng_enabled); @@ -2345,14 +2472,14 @@ TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) { // Turn echo cancellation off options.echo_cancellation.Set(false); - ASSERT_TRUE(engine_.SetAudioOptions(options)); + ASSERT_TRUE(engine_.SetOptions(options)); voe_.GetEcStatus(ec_enabled, ec_mode); EXPECT_FALSE(ec_enabled); // Turn echo cancellation back on, with settings, and make sure // nothing else changed. options.echo_cancellation.Set(true); - ASSERT_TRUE(engine_.SetAudioOptions(options)); + ASSERT_TRUE(engine_.SetOptions(options)); voe_.GetEcStatus(ec_enabled, ec_mode); voe_.GetEcMetricsStatus(ec_metrics_enabled); voe_.GetAecmMode(aecm_mode, cng_enabled); @@ -2375,14 +2502,14 @@ TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) { // Turn off AGC options.auto_gain_control.Set(false); - ASSERT_TRUE(engine_.SetAudioOptions(options)); + ASSERT_TRUE(engine_.SetOptions(options)); voe_.GetAgcStatus(agc_enabled, agc_mode); EXPECT_FALSE(agc_enabled); // Turn AGC back on options.auto_gain_control.Set(true); options.adjust_agc_delta.Clear(); - ASSERT_TRUE(engine_.SetAudioOptions(options)); + ASSERT_TRUE(engine_.SetOptions(options)); voe_.GetAgcStatus(agc_enabled, agc_mode); EXPECT_TRUE(agc_enabled); voe_.GetAgcConfig(agc_config); @@ -2393,7 +2520,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) { options.highpass_filter.Set(false); options.typing_detection.Set(false); options.stereo_swapping.Set(true); - ASSERT_TRUE(engine_.SetAudioOptions(options)); + ASSERT_TRUE(engine_.SetOptions(options)); voe_.GetNsStatus(ns_enabled, ns_mode); highpass_filter_enabled = voe_.IsHighPassFilterEnabled(); stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); @@ -2405,7 +2532,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) { // Turn on "conference mode" to ensure it has no impact. options.conference_mode.Set(true); - ASSERT_TRUE(engine_.SetAudioOptions(options)); + ASSERT_TRUE(engine_.SetOptions(options)); voe_.GetEcStatus(ec_enabled, ec_mode); voe_.GetNsStatus(ns_enabled, ns_mode); EXPECT_TRUE(ec_enabled); @@ -2414,7 +2541,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) { EXPECT_EQ(webrtc::kNsHighSuppression, ns_mode); } -TEST_F(WebRtcVoiceEngineTestFake, SetOptions) { +TEST_F(WebRtcVoiceEngineTestFake, DefaultOptions) { EXPECT_TRUE(SetupEngine()); bool ec_enabled; @@ -2428,23 +2555,6 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptions) { bool stereo_swapping_enabled; bool typing_detection_enabled; - ASSERT_TRUE(engine_.SetOptions(0)); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetEcMetricsStatus(ec_metrics_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - highpass_filter_enabled = voe_.IsHighPassFilterEnabled(); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_FALSE(ec_enabled); - EXPECT_FALSE(agc_enabled); - EXPECT_FALSE(ns_enabled); - EXPECT_FALSE(highpass_filter_enabled); - EXPECT_FALSE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); - - ASSERT_TRUE(engine_.SetOptions( - cricket::MediaEngineInterface::ECHO_CANCELLATION)); voe_.GetEcStatus(ec_enabled, ec_mode); voe_.GetEcMetricsStatus(ec_metrics_enabled); voe_.GetAgcStatus(agc_enabled, agc_mode); @@ -2453,107 +2563,11 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptions) { stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); voe_.GetTypingDetectionStatus(typing_detection_enabled); EXPECT_TRUE(ec_enabled); - EXPECT_FALSE(agc_enabled); - EXPECT_FALSE(ns_enabled); - EXPECT_FALSE(highpass_filter_enabled); - EXPECT_FALSE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); - - ASSERT_TRUE(engine_.SetOptions( - cricket::MediaEngineInterface::AUTO_GAIN_CONTROL)); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetEcMetricsStatus(ec_metrics_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - highpass_filter_enabled = voe_.IsHighPassFilterEnabled(); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_FALSE(ec_enabled); EXPECT_TRUE(agc_enabled); - EXPECT_FALSE(ns_enabled); - EXPECT_FALSE(highpass_filter_enabled); - EXPECT_FALSE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); - - ASSERT_TRUE(engine_.SetOptions( - cricket::MediaEngineInterface::NOISE_SUPPRESSION)); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetEcMetricsStatus(ec_metrics_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - highpass_filter_enabled = voe_.IsHighPassFilterEnabled(); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_FALSE(ec_enabled); - EXPECT_FALSE(agc_enabled); EXPECT_TRUE(ns_enabled); - EXPECT_FALSE(highpass_filter_enabled); - EXPECT_FALSE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); - - ASSERT_TRUE(engine_.SetOptions( - cricket::MediaEngineInterface::HIGHPASS_FILTER)); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetEcMetricsStatus(ec_metrics_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - highpass_filter_enabled = voe_.IsHighPassFilterEnabled(); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_FALSE(ec_enabled); - EXPECT_FALSE(agc_enabled); - EXPECT_FALSE(ns_enabled); EXPECT_TRUE(highpass_filter_enabled); - EXPECT_FALSE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); - - ASSERT_TRUE(engine_.SetOptions( - cricket::MediaEngineInterface::STEREO_FLIPPING)); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetEcMetricsStatus(ec_metrics_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - highpass_filter_enabled = voe_.IsHighPassFilterEnabled(); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_FALSE(ec_enabled); - EXPECT_FALSE(agc_enabled); - EXPECT_FALSE(ns_enabled); - EXPECT_FALSE(highpass_filter_enabled); - EXPECT_TRUE(stereo_swapping_enabled); EXPECT_TRUE(typing_detection_enabled); - - ASSERT_TRUE(engine_.SetOptions( - cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS)); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetEcMetricsStatus(ec_metrics_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - highpass_filter_enabled = voe_.IsHighPassFilterEnabled(); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(agc_enabled); - EXPECT_TRUE(ns_enabled); - EXPECT_TRUE(highpass_filter_enabled); EXPECT_FALSE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); - - ASSERT_TRUE(engine_.SetOptions( - cricket::MediaEngineInterface::ALL_AUDIO_OPTIONS)); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetEcMetricsStatus(ec_metrics_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - highpass_filter_enabled = voe_.IsHighPassFilterEnabled(); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(agc_enabled); - EXPECT_TRUE(ns_enabled); - EXPECT_TRUE(highpass_filter_enabled); - EXPECT_TRUE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); } TEST_F(WebRtcVoiceEngineTestFake, InitDoesNotOverwriteDefaultAgcConfig) { @@ -2625,7 +2639,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { ASSERT_TRUE(channel2->GetOptions(&actual_options)); EXPECT_EQ(expected_options, actual_options); - ASSERT_TRUE(engine_.SetAudioOptions(options_all)); + ASSERT_TRUE(engine_.SetOptions(options_all)); bool ec_enabled; webrtc::EcModes ec_mode; bool agc_enabled; @@ -2672,7 +2686,7 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { EXPECT_TRUE(ns_enabled); // Make sure settings take effect while we are sending. - ASSERT_TRUE(engine_.SetAudioOptions(options_all)); + ASSERT_TRUE(engine_.SetOptions(options_all)); cricket::AudioOptions options_no_agc_nor_ns; options_no_agc_nor_ns.auto_gain_control.Set(false); options_no_agc_nor_ns.noise_suppression.Set(false); @@ -2692,6 +2706,33 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { EXPECT_FALSE(ns_enabled); } +// This test verifies DSCP settings are properly applied on voice media channel. +TEST_F(WebRtcVoiceEngineTestFake, TestSetDscpOptions) { + EXPECT_TRUE(SetupEngine()); + talk_base::scoped_ptr<cricket::VoiceMediaChannel> channel( + engine_.CreateChannel()); + talk_base::scoped_ptr<cricket::FakeNetworkInterface> network_interface( + new cricket::FakeNetworkInterface); + channel->SetInterface(network_interface.get()); + cricket::AudioOptions options; + options.dscp.Set(true); + EXPECT_TRUE(channel->SetOptions(options)); + 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()); +} + +TEST(WebRtcVoiceEngineTest, TestDefaultOptionsBeforeInit) { + cricket::WebRtcVoiceEngine engine; + cricket::AudioOptions options = engine.GetOptions(); + // The default options should have at least a few things set. We purposefully + // don't check the option values here, though. + EXPECT_TRUE(options.echo_cancellation.IsSet()); + EXPECT_TRUE(options.auto_gain_control.IsSet()); + EXPECT_TRUE(options.noise_suppression.IsSet()); +} + // Test that GetReceiveChannelNum returns the default channel for the first // recv stream in 1-1 calls. TEST_F(WebRtcVoiceEngineTestFake, TestGetReceiveChannelNumIn1To1Calls) { @@ -2797,7 +2838,7 @@ TEST(WebRtcVoiceEngineTest, HasNoMonitorThread) { size_t size = 0; EXPECT_TRUE(stream->GetSize(&size)); EXPECT_GT(size, 0U); - const std::string logs(stream->GetBuffer()); + const std::string logs(stream->GetBuffer(), size); EXPECT_NE(std::string::npos, logs.find("ProcessThread")); } @@ -2871,9 +2912,9 @@ TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) { EXPECT_EQ(127, it->id); } else if (it->name == "opus") { EXPECT_EQ(111, it->id); - ASSERT_NE(it->params.find("minptime"), it->params.end()); + ASSERT_TRUE(it->params.find("minptime") != it->params.end()); EXPECT_EQ("10", it->params.find("minptime")->second); - ASSERT_NE(it->params.find("maxptime"), it->params.end()); + ASSERT_TRUE(it->params.find("maxptime") != it->params.end()); EXPECT_EQ("60", it->params.find("maxptime")->second); } } @@ -2943,3 +2984,38 @@ 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 ec00c048288..67178f4985e 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/asyncstuntcpsocket.cc @@ -126,7 +126,8 @@ void AsyncStunTCPSocket::ProcessInput(char* data, size_t* len) { return; } - SignalReadPacket(this, data, expected_pkt_len, remote_addr); + SignalReadPacket(this, data, expected_pkt_len, remote_addr, + talk_base::CreatePacketTime(0)); *len -= actual_length; if (*len > 0) { 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 7cb380b0aa4..c6a7b1b6fbc 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 @@ -109,7 +109,8 @@ class AsyncStunTCPSocketTest : public testing::Test, } void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data, - size_t len, const talk_base::SocketAddress& remote_addr) { + size_t len, const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { recv_packets_.push_back(std::string(data, len)); } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/basicpacketsocketfactory.cc b/chromium/third_party/libjingle/source/talk/p2p/base/basicpacketsocketfactory.cc index 565aed3c04e..758d4928995 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/basicpacketsocketfactory.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/basicpacketsocketfactory.cc @@ -30,6 +30,9 @@ #include "talk/base/asyncudpsocket.h" #include "talk/base/asynctcpsocket.h" #include "talk/base/logging.h" +#include "talk/base/nethelpers.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/scoped_ptr.h" #include "talk/base/socketadapters.h" #include "talk/base/thread.h" #include "talk/p2p/base/asyncstuntcpsocket.h" @@ -174,6 +177,10 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket( return tcp_socket; } +AsyncResolverInterface* BasicPacketSocketFactory::CreateAsyncResolver() { + return new talk_base::AsyncResolver(); +} + int BasicPacketSocketFactory::BindSocket( AsyncSocket* socket, const SocketAddress& local_address, int min_port, int max_port) { diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/basicpacketsocketfactory.h b/chromium/third_party/libjingle/source/talk/p2p/base/basicpacketsocketfactory.h index d4e76e7145a..27963c9f454 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/basicpacketsocketfactory.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/basicpacketsocketfactory.h @@ -51,6 +51,8 @@ class BasicPacketSocketFactory : public PacketSocketFactory { const SocketAddress& local_address, const SocketAddress& remote_address, const ProxyInfo& proxy_info, const std::string& user_agent, int opts); + virtual AsyncResolverInterface* CreateAsyncResolver(); + private: int BindSocket(AsyncSocket* socket, const SocketAddress& local_address, int min_port, int max_port); 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 93da1033e8b..7492171ee1e 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransport.h @@ -58,6 +58,13 @@ class DtlsTransport : public Base { virtual void SetIdentity_w(talk_base::SSLIdentity* identity) { identity_ = identity; } + virtual bool GetIdentity_w(talk_base::SSLIdentity** identity) { + if (!identity_) + return false; + + *identity = identity_->GetReference(); + return true; + } virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel) { 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 dead3a550be..472299959a0 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.cc @@ -42,6 +42,7 @@ 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); @@ -173,6 +174,15 @@ bool DtlsTransportChannelWrapper::SetLocalIdentity( return true; } +bool DtlsTransportChannelWrapper::GetLocalIdentity( + talk_base::SSLIdentity** identity) const { + if (!local_identity_) + return false; + + *identity = local_identity_->GetReference(); + return true; +} + bool DtlsTransportChannelWrapper::SetSslRole(talk_base::SSLRole role) { if (dtls_state_ == STATE_OPEN) { if (ssl_role_ != role) { @@ -230,6 +240,14 @@ bool DtlsTransportChannelWrapper::SetRemoteFingerprint( return true; } +bool DtlsTransportChannelWrapper::GetRemoteCertificate( + talk_base::SSLCertificate** cert) const { + if (!dtls_) + return false; + + return dtls_->GetPeerCertificate(cert); +} + bool DtlsTransportChannelWrapper::SetupDtls() { StreamInterfaceChannel* downward = new StreamInterfaceChannel(worker_thread_, channel_); @@ -269,14 +287,37 @@ bool DtlsTransportChannelWrapper::SetupDtls() { return true; } -bool DtlsTransportChannelWrapper::SetSrtpCiphers(const std::vector<std::string>& - ciphers) { - // SRTP ciphers must be set before the DTLS handshake starts. - // TODO(juberti): In multiplex situations, we may end up calling this function - // once for each muxed channel. Depending on the order of calls, this may - // result in slightly undesired results, e.g. 32 vs 80-bit MAC. The right way to - // fix this would be for the TransportProxyChannels to intersect the ciphers - // instead of overwriting, so that "80" followed by "32, 80" results in "80". +bool DtlsTransportChannelWrapper::SetSrtpCiphers( + const std::vector<std::string>& ciphers) { + if (srtp_ciphers_ == ciphers) + return true; + + if (dtls_state_ == STATE_OPEN) { + // We don't support DTLS renegotiation currently. If new set of srtp ciphers + // are different than what's being used currently, we will not use it. + // So for now, let's be happy (or sad) with a warning message. + std::string current_srtp_cipher; + if (!dtls_->GetDtlsSrtpCipher(¤t_srtp_cipher)) { + LOG(LS_ERROR) << "Failed to get the current SRTP cipher for DTLS channel"; + return false; + } + const std::vector<std::string>::const_iterator iter = + std::find(ciphers.begin(), ciphers.end(), current_srtp_cipher); + if (iter == ciphers.end()) { + std::string requested_str; + for (size_t i = 0; i < ciphers.size(); ++i) { + requested_str.append(" "); + requested_str.append(ciphers[i]); + requested_str.append(" "); + } + LOG(LS_WARNING) << "Ignoring new set of SRTP ciphers, as DTLS " + << "renegotiation is not supported currently " + << "current cipher = " << current_srtp_cipher << " and " + << "requested = " << "[" << requested_str << "]"; + } + return true; + } + if (dtls_state_ != STATE_NONE && dtls_state_ != STATE_OFFERED && dtls_state_ != STATE_ACCEPTED) { @@ -405,9 +446,9 @@ void DtlsTransportChannelWrapper::OnWritableState(TransportChannel* channel) { } } -void DtlsTransportChannelWrapper::OnReadPacket(TransportChannel* channel, - const char* data, size_t size, - int flags) { +void DtlsTransportChannelWrapper::OnReadPacket( + TransportChannel* channel, const char* data, size_t size, + const talk_base::PacketTime& packet_time, int flags) { ASSERT(talk_base::Thread::Current() == worker_thread_); ASSERT(channel == channel_); ASSERT(flags == 0); @@ -415,7 +456,7 @@ void DtlsTransportChannelWrapper::OnReadPacket(TransportChannel* channel, switch (dtls_state_) { case STATE_NONE: // We are not doing DTLS - SignalReadPacket(this, data, size, 0); + SignalReadPacket(this, data, size, packet_time, 0); break; case STATE_OFFERED: @@ -459,7 +500,7 @@ void DtlsTransportChannelWrapper::OnReadPacket(TransportChannel* channel, ASSERT(!srtp_ciphers_.empty()); // Signal this upwards as a bypass packet. - SignalReadPacket(this, data, size, PF_SRTP_BYPASS); + SignalReadPacket(this, data, size, packet_time, PF_SRTP_BYPASS); } break; case STATE_CLOSED: @@ -494,7 +535,7 @@ void DtlsTransportChannelWrapper::OnDtlsEvent(talk_base::StreamInterface* dtls, char buf[kMaxDtlsPacketLen]; size_t read; if (dtls_->Read(buf, sizeof(buf), &read, NULL) == talk_base::SR_SUCCESS) { - SignalReadPacket(this, buf, read, 0); + SignalReadPacket(this, buf, read, talk_base::CreatePacketTime(0), 0); } } if (sig & talk_base::SE_CLOSE) { 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 aec8c7ac428..d6b73467486 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/dtlstransportchannel.h @@ -128,6 +128,7 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { return channel_->GetIceRole(); } virtual bool SetLocalIdentity(talk_base::SSLIdentity *identity); + virtual bool GetLocalIdentity(talk_base::SSLIdentity** identity) const; virtual bool SetRemoteFingerprint(const std::string& digest_alg, const uint8* digest, @@ -164,6 +165,10 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { virtual bool GetSslRole(talk_base::SSLRole* role) const; virtual bool SetSslRole(talk_base::SSLRole role); + // Once DTLS has been established, this method retrieves the certificate in + // use by the remote peer, for use in external identity verification. + virtual bool GetRemoteCertificate(talk_base::SSLCertificate** cert) const; + // Once DTLS has established (i.e., this channel is writable), this method // extracts the keys negotiated during the DTLS handshake, for use in external // encryption. DTLS-SRTP uses this to extract the needed SRTP keys. @@ -220,7 +225,7 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl { void OnReadableState(TransportChannel* channel); void OnWritableState(TransportChannel* channel); void OnReadPacket(TransportChannel* channel, const char* data, size_t size, - int flags); + const talk_base::PacketTime& packet_time, int flags); void OnReadyToSend(TransportChannel* channel); void OnDtlsEvent(talk_base::StreamInterface* stream_, int sig, int err); bool SetupDtls(); 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 267d60be167..1fd82d71073 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 @@ -233,7 +233,7 @@ class DtlsTestClient : public sigslot::has_slots<> { void SendPackets(size_t channel, size_t size, size_t count, bool srtp) { ASSERT(channel < channels_.size()); - talk_base::scoped_array<char> packet(new char[size]); + talk_base::scoped_ptr<char[]> packet(new char[size]); size_t sent = 0; do { // Fill the packet with a known value and a sequence number to check @@ -307,6 +307,7 @@ class DtlsTestClient : public sigslot::has_slots<> { void OnTransportChannelReadPacket(cricket::TransportChannel* channel, const char* data, size_t size, + const talk_base::PacketTime& packet_time, int flags) { uint32 packet_num = 0; ASSERT_TRUE(VerifyPacket(data, size, &packet_num)); @@ -320,6 +321,7 @@ class DtlsTestClient : public sigslot::has_slots<> { // Hook into the raw packet stream to make sure DTLS packets are encrypted. void OnFakeTransportChannelReadPacket(cricket::TransportChannel* channel, const char* data, size_t size, + const talk_base::PacketTime& time, int flags) { // Flags shouldn't be set on the underlying TransportChannel packets. ASSERT_EQ(0, flags); @@ -751,3 +753,56 @@ TEST_F(DtlsTransportChannelTest, TestDtlsReOfferWithDifferentSetupAttr) { 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); + PrepareDtls(true, true); + Negotiate(); + + talk_base::scoped_ptr<talk_base::SSLIdentity> identity1; + talk_base::scoped_ptr<talk_base::SSLIdentity> identity2; + talk_base::scoped_ptr<talk_base::SSLCertificate> remote_cert1; + talk_base::scoped_ptr<talk_base::SSLCertificate> remote_cert2; + + // After negotiation, each side has a distinct local certificate, but still no + // remote certificate, because connection has not yet occurred. + ASSERT_TRUE(client1_.transport()->GetIdentity(identity1.accept())); + ASSERT_TRUE(client2_.transport()->GetIdentity(identity2.accept())); + ASSERT_NE(identity1->certificate().ToPEMString(), + identity2->certificate().ToPEMString()); + ASSERT_FALSE( + client1_.transport()->GetRemoteCertificate(remote_cert1.accept())); + ASSERT_FALSE(remote_cert1 != NULL); + ASSERT_FALSE( + client2_.transport()->GetRemoteCertificate(remote_cert2.accept())); + ASSERT_FALSE(remote_cert2 != NULL); +} + +// Test Certificates state after connection. +TEST_F(DtlsTransportChannelTest, TestCertificatesAfterConnect) { + MAYBE_SKIP_TEST(HaveDtls); + PrepareDtls(true, true); + ASSERT_TRUE(Connect()); + + talk_base::scoped_ptr<talk_base::SSLIdentity> identity1; + talk_base::scoped_ptr<talk_base::SSLIdentity> identity2; + talk_base::scoped_ptr<talk_base::SSLCertificate> remote_cert1; + talk_base::scoped_ptr<talk_base::SSLCertificate> remote_cert2; + + // After connection, each side has a distinct local certificate. + ASSERT_TRUE(client1_.transport()->GetIdentity(identity1.accept())); + ASSERT_TRUE(client2_.transport()->GetIdentity(identity2.accept())); + ASSERT_NE(identity1->certificate().ToPEMString(), + identity2->certificate().ToPEMString()); + + // Each side's remote certificate is the other side's local certificate. + ASSERT_TRUE( + client1_.transport()->GetRemoteCertificate(remote_cert1.accept())); + ASSERT_EQ(remote_cert1->ToPEMString(), + identity2->certificate().ToPEMString()); + ASSERT_TRUE( + client2_.transport()->GetRemoteCertificate(remote_cert2.accept())); + ASSERT_EQ(remote_cert2->ToPEMString(), + identity1->certificate().ToPEMString()); +} 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 d162950a3bb..2615f50dffe 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/fakesession.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/fakesession.h @@ -33,6 +33,7 @@ #include <vector> #include "talk/base/buffer.h" +#include "talk/base/fakesslidentity.h" #include "talk/base/sigslot.h" #include "talk/base/sslfingerprint.h" #include "talk/base/messagequeue.h" @@ -203,7 +204,8 @@ class FakeTransportChannel : public TransportChannelImpl, PacketMessageData* data = static_cast<PacketMessageData*>( msg->pdata); dest_->SignalReadPacket(dest_, data->packet.data(), - data->packet.length(), 0); + data->packet.length(), + talk_base::CreatePacketTime(0), 0); delete data; } @@ -212,11 +214,16 @@ class FakeTransportChannel : public TransportChannelImpl, return true; } - bool IsDtlsActive() const { + + void SetRemoteCertificate(talk_base::FakeSSLCertificate* cert) { + remote_cert_ = cert; + } + + virtual bool IsDtlsActive() const { return do_dtls_; } - bool SetSrtpCiphers(const std::vector<std::string>& ciphers) { + virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) { srtp_ciphers_ = ciphers; return true; } @@ -229,6 +236,22 @@ class FakeTransportChannel : public TransportChannelImpl, return false; } + virtual bool GetLocalIdentity(talk_base::SSLIdentity** identity) const { + if (!identity_) + return false; + + *identity = identity_->GetReference(); + return true; + } + + virtual bool GetRemoteCertificate(talk_base::SSLCertificate** cert) const { + if (!remote_cert_) + return false; + + *cert = remote_cert_->GetReference(); + return true; + } + virtual bool ExportKeyingMaterial(const std::string& label, const uint8* context, size_t context_len, @@ -272,6 +295,7 @@ class FakeTransportChannel : public TransportChannelImpl, State state_; bool async_; talk_base::SSLIdentity* identity_; + talk_base::FakeSSLCertificate* remote_cert_; bool do_dtls_; std::vector<std::string> srtp_ciphers_; std::string chosen_srtp_cipher_; @@ -349,6 +373,16 @@ class FakeTransport : public Transport { channels_.erase(channel->component()); delete channel; } + virtual void SetIdentity_w(talk_base::SSLIdentity* identity) { + identity_ = identity; + } + virtual bool GetIdentity_w(talk_base::SSLIdentity** identity) { + if (!identity_) + return false; + + *identity = identity_->GetReference(); + return true; + } private: FakeTransportChannel* GetFakeChannel(int component) { @@ -391,6 +425,12 @@ class FakeSession : public BaseSession { NULL, "", "", initiator), fail_create_channel_(false) { } + FakeSession(talk_base::Thread* worker_thread, bool initiator) + : BaseSession(talk_base::Thread::Current(), + worker_thread, + NULL, "", "", initiator), + fail_create_channel_(false) { + } FakeTransport* GetTransport(const std::string& content_name) { return static_cast<FakeTransport*>( 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 d45a66c40b6..38cc35445ea 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc @@ -518,19 +518,21 @@ void P2PTransportChannel::OnUnknownAddress( // request came from. // There shouldn't be an existing connection with this remote address. - // When ports are muxed, this channel might get multiple unknown addres + // When ports are muxed, this channel might get multiple unknown address // signals. In that case if the connection is already exists, we should // simply ignore the signal othewise send server error. - if (port->GetConnection(new_remote_candidate.address()) && port_muxed) { - LOG(LS_INFO) << "Connection already exist for PeerReflexive candidate: " - << new_remote_candidate.ToString(); - return; - } else if (port->GetConnection(new_remote_candidate.address())) { - ASSERT(false); - port->SendBindingErrorResponse(stun_msg, address, - STUN_ERROR_SERVER_ERROR, - STUN_ERROR_REASON_SERVER_ERROR); - return; + if (port->GetConnection(new_remote_candidate.address())) { + if (port_muxed) { + LOG(LS_INFO) << "Connection already exists for peer reflexive " + << "candidate: " << new_remote_candidate.ToString(); + return; + } else { + ASSERT(false); + port->SendBindingErrorResponse(stun_msg, address, + STUN_ERROR_SERVER_ERROR, + STUN_ERROR_REASON_SERVER_ERROR); + return; + } } Connection* connection = port->CreateConnection( @@ -798,6 +800,7 @@ int P2PTransportChannel::SendPacket(const char *data, size_t len, error_ = EWOULDBLOCK; return -1; } + int sent = best_connection_->Send(data, len, dscp); if (sent <= 0) { ASSERT(sent < 0); @@ -1224,8 +1227,9 @@ void P2PTransportChannel::OnPortDestroyed(PortInterface* port) { } // We data is available, let listeners know -void P2PTransportChannel::OnReadPacket(Connection *connection, const char *data, - size_t len) { +void P2PTransportChannel::OnReadPacket( + Connection *connection, const char *data, size_t len, + const talk_base::PacketTime& packet_time) { ASSERT(worker_thread_ == talk_base::Thread::Current()); // Do not deliver, if packet doesn't belong to the correct transport channel. @@ -1233,7 +1237,7 @@ void P2PTransportChannel::OnReadPacket(Connection *connection, const char *data, return; // Let the client know of an incoming packet - SignalReadPacket(this, data, len, 0); + SignalReadPacket(this, data, len, packet_time, 0); } void P2PTransportChannel::OnReadyToSend(Connection* connection) { 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 2fc718641fb..6f287f369c6 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h @@ -40,6 +40,7 @@ #include <map> #include <vector> #include <string> +#include "talk/base/asyncpacketsocket.h" #include "talk/base/sigslot.h" #include "talk/p2p/base/candidate.h" #include "talk/p2p/base/portinterface.h" @@ -127,6 +128,15 @@ class P2PTransportChannel : public TransportChannelImpl, return false; } + // Returns false because the channel is not encrypted by default. + virtual bool GetLocalIdentity(talk_base::SSLIdentity** identity) const { + return false; + } + + virtual bool GetRemoteCertificate(talk_base::SSLCertificate** cert) const { + return false; + } + // Allows key material to be extracted for external encryption. virtual bool ExportKeyingMaterial( const std::string& label, @@ -198,8 +208,9 @@ class P2PTransportChannel : public TransportChannelImpl, void OnPortDestroyed(PortInterface* port); void OnRoleConflict(PortInterface* port); - void OnConnectionStateChange(Connection *connection); - void OnReadPacket(Connection *connection, const char *data, size_t len); + void OnConnectionStateChange(Connection* connection); + void OnReadPacket(Connection *connection, const char *data, size_t len, + const talk_base::PacketTime& packet_time); void OnReadyToSend(Connection* connection); void OnConnectionDestroyed(Connection *connection); 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 e3cddc0e22e..3c24ded632a 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 @@ -57,6 +57,11 @@ static const int kOnlyLocalPorts = cricket::PORTALLOCATOR_DISABLE_STUN | // Addresses on the public internet. static const SocketAddress kPublicAddrs[2] = { SocketAddress("11.11.11.11", 0), SocketAddress("22.22.22.22", 0) }; +// IPv6 Addresses on the public internet. +static const SocketAddress kIPv6PublicAddrs[2] = { + SocketAddress("2400:4030:1:2c00:be30:abcd:efab:cdef", 0), + SocketAddress("2620:0:1000:1b03:2e41:38ff:fea6:f2a4", 0) +}; // For configuring multihomed clients. static const SocketAddress kAlternateAddrs[2] = { SocketAddress("11.11.11.101", 0), SocketAddress("22.22.22.202", 0) }; @@ -608,7 +613,8 @@ class P2PTransportChannelTestBase : public testing::Test, rch->OnCandidate(c); } void OnReadPacket(cricket::TransportChannel* channel, const char* data, - size_t len, int flags) { + size_t len, const talk_base::PacketTime& packet_time, + int flags) { std::list<std::string>& packets = GetPacketList(channel); packets.push_front(std::string(data, len)); } @@ -1413,6 +1419,34 @@ TEST_F(P2PTransportChannelTest, TestDefaultDscpValue) { GetEndpoint(1)->cd1_.ch_->DefaultDscpValue()); } +// Verify IPv6 connection is preferred over IPv4. +TEST_F(P2PTransportChannelTest, TestIPv6Connections) { + AddAddress(0, kIPv6PublicAddrs[0]); + AddAddress(0, kPublicAddrs[0]); + AddAddress(1, kIPv6PublicAddrs[1]); + AddAddress(1, kPublicAddrs[1]); + + SetAllocationStepDelay(0, kMinimumStepDelay); + SetAllocationStepDelay(1, kMinimumStepDelay); + + // Enable IPv6 + SetAllocatorFlags(0, cricket::PORTALLOCATOR_ENABLE_IPV6); + SetAllocatorFlags(1, cricket::PORTALLOCATOR_ENABLE_IPV6); + + 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() && + LocalCandidate(ep1_ch1())->address().EqualIPs(kIPv6PublicAddrs[0]) && + RemoteCandidate(ep1_ch1())->address().EqualIPs(kIPv6PublicAddrs[1])); + + 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. diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/packetsocketfactory.h b/chromium/third_party/libjingle/source/talk/p2p/base/packetsocketfactory.h index 882a974173b..e985b37faa6 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/packetsocketfactory.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/packetsocketfactory.h @@ -33,6 +33,7 @@ namespace talk_base { class AsyncPacketSocket; +class AsyncResolverInterface; class PacketSocketFactory { public: @@ -57,6 +58,8 @@ class PacketSocketFactory { const SocketAddress& local_address, const SocketAddress& remote_address, const ProxyInfo& proxy_info, const std::string& user_agent, int opts) = 0; + virtual AsyncResolverInterface* CreateAsyncResolver() = 0; + private: DISALLOW_EVIL_CONSTRUCTORS(PacketSocketFactory); }; 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 6e688dace56..24ef4271fb6 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/port.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/port.cc @@ -162,11 +162,11 @@ static std::string ComputeFoundation( return talk_base::ToString<uint32>(talk_base::ComputeCrc32(ost.str())); } -Port::Port(talk_base::Thread* thread, talk_base::Network* network, - const talk_base::IPAddress& ip, +Port::Port(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, + talk_base::Network* network, const talk_base::IPAddress& ip, const std::string& username_fragment, const std::string& password) : thread_(thread), - factory_(NULL), + factory_(factory), send_retransmit_count_attribute_(false), network_(network), ip_(ip), @@ -924,7 +924,8 @@ void Connection::OnSendStunPacket(const void* data, size_t size, } } -void Connection::OnReadPacket(const char* data, size_t size) { +void Connection::OnReadPacket( + const char* data, size_t size, const talk_base::PacketTime& packet_time) { talk_base::scoped_ptr<IceMessage> msg; std::string remote_ufrag; const talk_base::SocketAddress& addr(remote_candidate_.address()); @@ -938,7 +939,7 @@ void Connection::OnReadPacket(const char* data, size_t size) { last_data_received_ = talk_base::Time(); recv_rate_tracker_.Update(size); - SignalReadPacket(this, data, size); + SignalReadPacket(this, data, size, packet_time); // If timed out sending writability checks, start up again if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) { 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 7b89e5546e8..9ea3f0c37aa 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/port.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/port.h @@ -32,6 +32,7 @@ #include <vector> #include <map> +#include "talk/base/asyncpacketsocket.h" #include "talk/base/network.h" #include "talk/base/proxyinfo.h" #include "talk/base/ratetracker.h" @@ -45,10 +46,6 @@ #include "talk/p2p/base/stunrequest.h" #include "talk/p2p/base/transport.h" -namespace talk_base { -class AsyncPacketSocket; -} - namespace cricket { class Connection; @@ -118,8 +115,8 @@ struct ProtocolAddress { class Port : public PortInterface, public talk_base::MessageHandler, public sigslot::has_slots<> { public: - Port(talk_base::Thread* thread, talk_base::Network* network, - const talk_base::IPAddress& ip, + Port(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, + talk_base::Network* network, const talk_base::IPAddress& ip, const std::string& username_fragment, const std::string& password); Port(talk_base::Thread* thread, const std::string& type, talk_base::PacketSocketFactory* factory, @@ -240,7 +237,8 @@ class Port : public PortInterface, public talk_base::MessageHandler, // TODO(mallinath) - Make it pure virtual. virtual bool HandleIncomingPacket( talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& remote_addr) { + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { ASSERT(false); return false; } @@ -470,12 +468,14 @@ class Connection : public talk_base::MessageHandler, // Error if Send() returns < 0 virtual int GetError() = 0; - sigslot::signal3<Connection*, const char*, size_t> SignalReadPacket; + sigslot::signal4<Connection*, const char*, size_t, + const talk_base::PacketTime&> SignalReadPacket; sigslot::signal1<Connection*> SignalReadyToSend; // Called when a packet is received on this connection. - void OnReadPacket(const char* data, size_t size); + void OnReadPacket(const char* data, size_t size, + const talk_base::PacketTime& packet_time); // Called when the socket is currently able to send. void OnReadyToSend(); 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 d3e02ac9f58..1122d8aea01 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 @@ -215,7 +215,7 @@ class TestChannel : public sigslot::has_slots<> { public: TestChannel(Port* p1, Port* p2) : ice_mode_(ICEMODE_FULL), src_(p1), dst_(p2), complete_count_(0), - conn_(NULL), remote_request_(NULL), nominated_(false) { + conn_(NULL), remote_request_(), nominated_(false) { src_->SignalPortComplete.connect( this, &TestChannel::OnPortComplete); src_->SignalUnknownAddress.connect(this, &TestChannel::OnUnknownAddress); @@ -766,6 +766,9 @@ class FakePacketSocketFactory : public talk_base::PacketSocketFactory { void set_next_client_tcp_socket(AsyncPacketSocket* next_client_tcp_socket) { next_client_tcp_socket_ = next_client_tcp_socket; } + talk_base::AsyncResolverInterface* CreateAsyncResolver() { + return NULL; + } private: AsyncPacketSocket* next_udp_socket_; @@ -1046,7 +1049,8 @@ TEST_F(PortTest, TestLoopbackCallAsIce) { IceMessage* msg = lport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); conn->OnReadPacket(lport->last_stun_buf()->Data(), - lport->last_stun_buf()->Length()); + lport->last_stun_buf()->Length(), + talk_base::PacketTime()); ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000); msg = lport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_RESPONSE, msg->type()); @@ -1079,7 +1083,7 @@ TEST_F(PortTest, TestLoopbackCallAsIce) { lport->Reset(); talk_base::scoped_ptr<ByteBuffer> buf(new ByteBuffer()); WriteStunMessage(modified_req.get(), buf.get()); - conn1->OnReadPacket(buf->Data(), buf->Length()); + conn1->OnReadPacket(buf->Data(), buf->Length(), talk_base::PacketTime()); ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000); msg = lport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_ERROR_RESPONSE, msg->type()); @@ -1117,7 +1121,8 @@ TEST_F(PortTest, TestIceRoleConflict) { EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); // Send rport binding request to lport. lconn->OnReadPacket(rport->last_stun_buf()->Data(), - rport->last_stun_buf()->Length()); + rport->last_stun_buf()->Length(), + talk_base::PacketTime()); ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000); EXPECT_EQ(STUN_BINDING_RESPONSE, lport->last_stun_msg()->type()); @@ -1899,7 +1904,8 @@ TEST_F(PortTest, TestHandleStunBindingIndication) { EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); // Send rport binding request to lport. lconn->OnReadPacket(rport->last_stun_buf()->Data(), - rport->last_stun_buf()->Length()); + rport->last_stun_buf()->Length(), + talk_base::PacketTime()); ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000); EXPECT_EQ(STUN_BINDING_RESPONSE, lport->last_stun_msg()->type()); uint32 last_ping_received1 = lconn->last_ping_received(); @@ -1907,7 +1913,7 @@ TEST_F(PortTest, TestHandleStunBindingIndication) { // Adding a delay of 100ms. talk_base::Thread::Current()->ProcessMessages(100); // Pinging lconn using stun indication message. - lconn->OnReadPacket(buf->Data(), buf->Length()); + lconn->OnReadPacket(buf->Data(), buf->Length(), talk_base::PacketTime()); uint32 last_ping_received2 = lconn->last_ping_received(); EXPECT_GT(last_ping_received2, last_ping_received1); } @@ -2269,7 +2275,8 @@ TEST_F(PortTest, TestIceLiteConnectivity) { // Feeding the respone message from litemode to the full mode connection. ch1.conn()->OnReadPacket(ice_lite_port->last_stun_buf()->Data(), - ice_lite_port->last_stun_buf()->Length()); + ice_lite_port->last_stun_buf()->Length(), + talk_base::PacketTime()); // Verifying full mode connection becomes writable from the response. EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, ch1.conn()->write_state(), kTimeout); 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 b647fbf3dec..56aa5b019a0 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc @@ -539,7 +539,7 @@ IPseudoTcpNotify::WriteResult PseudoTcp::packet(uint32 seq, uint8 flags, uint32 now = Now(); - talk_base::scoped_array<uint8> buffer(new uint8[MAX_PACKET]); + talk_base::scoped_ptr<uint8[]> buffer(new uint8[MAX_PACKET]); long_to_bytes(m_conv, buffer.get()); long_to_bytes(seq, buffer.get() + 4); long_to_bytes(m_rcv_nxt, buffer.get() + 8); 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 ec225029b8b..2baef4245ab 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.cc @@ -257,7 +257,7 @@ void RawTransportChannel::OnReadPacket( PortInterface* port, const char* data, size_t size, const talk_base::SocketAddress& addr) { ASSERT(port_ == port); - SignalReadPacket(this, data, size, 0); + SignalReadPacket(this, data, size, talk_base::CreatePacketTime(0), 0); } void RawTransportChannel::OnMessage(talk_base::Message* msg) { 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 2aac2b5edf6..ed38952d561 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/rawtransportchannel.h @@ -128,6 +128,15 @@ class RawTransportChannel : public TransportChannelImpl, return false; } + // Returns false because the channel is not DTLS. + virtual bool GetLocalIdentity(talk_base::SSLIdentity** identity) const { + return false; + } + + virtual bool GetRemoteCertificate(talk_base::SSLCertificate** cert) const { + return false; + } + // Allows key material to be extracted for external encryption. virtual bool ExportKeyingMaterial( const std::string& label, 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 ff8c07c5514..ddfca7114ca 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/relayport.cc @@ -155,10 +155,11 @@ class RelayEntry : public talk_base::MessageHandler, void OnSocketClose(talk_base::AsyncPacketSocket* socket, int error); // Called when a packet is received on this socket. - void OnReadPacket(talk_base::AsyncPacketSocket* socket, - const char* data, size_t size, - const talk_base::SocketAddress& remote_addr); - + void OnReadPacket( + talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); // Called when the socket is currently able to send. void OnReadyToSend(talk_base::AsyncPacketSocket* socket); @@ -393,9 +394,11 @@ int RelayPort::GetError() { void RelayPort::OnReadPacket( const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, ProtocolType proto) { + const talk_base::SocketAddress& remote_addr, + ProtocolType proto, + const talk_base::PacketTime& packet_time) { if (Connection* conn = GetConnection(remote_addr)) { - conn->OnReadPacket(data, size); + conn->OnReadPacket(data, size, packet_time); } else { Port::OnReadPacket(data, size, remote_addr, proto); } @@ -682,9 +685,11 @@ void RelayEntry::OnSocketClose(talk_base::AsyncPacketSocket* socket, HandleConnectFailure(socket); } -void RelayEntry::OnReadPacket(talk_base::AsyncPacketSocket* socket, - const char* data, size_t size, - const talk_base::SocketAddress& remote_addr) { +void RelayEntry::OnReadPacket( + talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { // ASSERT(remote_addr == port_->server_addr()); // TODO: are we worried about this? @@ -698,7 +703,7 @@ void RelayEntry::OnReadPacket(talk_base::AsyncPacketSocket* socket, // by the server, The actual remote address is the one we recorded. if (!port_->HasMagicCookie(data, size)) { if (locked_) { - port_->OnReadPacket(data, size, ext_addr_, PROTO_UDP); + port_->OnReadPacket(data, size, ext_addr_, PROTO_UDP, packet_time); } else { LOG(WARNING) << "Dropping packet: entry not locked"; } @@ -751,7 +756,7 @@ void RelayEntry::OnReadPacket(talk_base::AsyncPacketSocket* socket, // Process the actual data and remote address in the normal manner. port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2, - PROTO_UDP); + PROTO_UDP, packet_time); } void RelayEntry::OnReadyToSend(talk_base::AsyncPacketSocket* socket) { 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 c15e7e01069..08df12f9d28 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/relayport.h @@ -99,7 +99,8 @@ class RelayPort : public Port { // Dispatches the given packet to the port or connection as appropriate. void OnReadPacket(const char* data, size_t size, const talk_base::SocketAddress& remote_addr, - ProtocolType proto); + ProtocolType proto, + const talk_base::PacketTime& packet_time); private: friend class RelayEntry; 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 ced8c589b5c..bd00af86de0 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 @@ -78,7 +78,8 @@ class RelayPortTest : public testing::Test, void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& remote_addr) { + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { received_packet_count_[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 c2cf472d3ba..c2619c03feb 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.cc @@ -198,7 +198,8 @@ void RelayServer::OnReadEvent(talk_base::AsyncSocket* socket) { void RelayServer::OnInternalPacket( talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size, - const talk_base::SocketAddress& remote_addr) { + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { // Get the address of the connection we just received on. talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); @@ -242,7 +243,8 @@ void RelayServer::OnInternalPacket( void RelayServer::OnExternalPacket( talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size, - const talk_base::SocketAddress& remote_addr) { + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { // Get the address of the connection we just received on. talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.h b/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.h index f3bee7eca8d..922a2562211 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/relayserver.h @@ -104,10 +104,12 @@ class RelayServer : public talk_base::MessageHandler, // Called when a packet is received by the server on one of its sockets. void OnInternalPacket(talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size, - const talk_base::SocketAddress& remote_addr); + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); void OnExternalPacket(talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size, - const talk_base::SocketAddress& remote_addr); + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); void OnReadEvent(talk_base::AsyncSocket* socket); 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 3128393c349..e0f8dd3f4f8 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/session.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/session.cc @@ -26,6 +26,8 @@ */ #include "talk/p2p/base/session.h" + +#include "talk/base/bind.h" #include "talk/base/common.h" #include "talk/base/logging.h" #include "talk/base/helpers.h" @@ -44,6 +46,8 @@ namespace cricket { +using talk_base::Bind; + bool BadMessage(const buzz::QName type, const std::string& text, MessageError* err) { @@ -65,11 +69,13 @@ std::string TransportProxy::type() const { } TransportChannel* TransportProxy::GetChannel(int component) { + ASSERT(talk_base::Thread::Current() == worker_thread_); return GetChannelProxy(component); } TransportChannel* TransportProxy::CreateChannel( const std::string& name, int component) { + ASSERT(talk_base::Thread::Current() == worker_thread_); ASSERT(GetChannel(component) == NULL); ASSERT(!transport_->get()->HasChannel(component)); @@ -81,9 +87,9 @@ TransportChannel* TransportProxy::CreateChannel( // If we're already negotiated, create an impl and hook it up to the proxy // channel. If we're connecting, create an impl but don't hook it up yet. if (negotiated_) { - SetChannelProxyImpl(component, channel); + SetupChannelProxy_w(component, channel); } else if (connecting_) { - GetOrCreateChannelProxyImpl(component); + GetOrCreateChannelProxyImpl_w(component); } return channel; } @@ -93,6 +99,7 @@ bool TransportProxy::HasChannel(int component) { } void TransportProxy::DestroyChannel(int component) { + ASSERT(talk_base::Thread::Current() == worker_thread_); TransportChannel* channel = GetChannel(component); if (channel) { // If the state of TransportProxy is not NEGOTIATED @@ -100,7 +107,7 @@ void TransportProxy::DestroyChannel(int component) { // connected. Both must be connected before // deletion. if (!negotiated_) { - SetChannelProxyImpl(component, GetChannelProxy(component)); + SetupChannelProxy_w(component, GetChannelProxy(component)); } channels_.erase(component); @@ -130,7 +137,7 @@ void TransportProxy::CompleteNegotiation() { if (!negotiated_) { for (ChannelMap::iterator iter = channels_.begin(); iter != channels_.end(); ++iter) { - SetChannelProxyImpl(iter->first, iter->second); + SetupChannelProxy(iter->first, iter->second); } negotiated_ = true; } @@ -191,6 +198,13 @@ TransportChannelProxy* TransportProxy::GetChannelProxyByName( TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl( int component) { + return worker_thread_->Invoke<TransportChannelImpl*>(Bind( + &TransportProxy::GetOrCreateChannelProxyImpl_w, this, component)); +} + +TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl_w( + int component) { + ASSERT(talk_base::Thread::Current() == worker_thread_); TransportChannelImpl* impl = transport_->get()->GetChannel(component); if (impl == NULL) { impl = transport_->get()->CreateChannel(component); @@ -198,13 +212,33 @@ TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl( return impl; } -void TransportProxy::SetChannelProxyImpl( +void TransportProxy::SetupChannelProxy( + int component, TransportChannelProxy* transproxy) { + worker_thread_->Invoke<void>(Bind( + &TransportProxy::SetupChannelProxy_w, this, component, transproxy)); +} + +void TransportProxy::SetupChannelProxy_w( int component, TransportChannelProxy* transproxy) { + ASSERT(talk_base::Thread::Current() == worker_thread_); TransportChannelImpl* impl = GetOrCreateChannelProxyImpl(component); ASSERT(impl != NULL); transproxy->SetImplementation(impl); } +void TransportProxy::ReplaceChannelProxyImpl(TransportChannelProxy* proxy, + TransportChannelImpl* impl) { + worker_thread_->Invoke<void>(Bind( + &TransportProxy::ReplaceChannelProxyImpl_w, this, proxy, impl)); +} + +void TransportProxy::ReplaceChannelProxyImpl_w(TransportChannelProxy* proxy, + TransportChannelImpl* impl) { + ASSERT(talk_base::Thread::Current() == worker_thread_); + ASSERT(proxy != NULL); + proxy->SetImplementation(impl); +} + // This function muxes |this| onto |target| by repointing |this| at // |target|'s transport and setting our TransportChannelProxies // to point to |target|'s underlying implementations. @@ -220,12 +254,12 @@ bool TransportProxy::SetupMux(TransportProxy* target) { iter != channels_.end(); ++iter) { if (!target->transport_->get()->HasChannel(iter->first)) { // Remove if channel doesn't exist in |transport_|. - iter->second->SetImplementation(NULL); + ReplaceChannelProxyImpl(iter->second, NULL); } else { // Replace the impl for all the TransportProxyChannels with the channels // from |target|'s transport. Fail if there's not an exact match. - iter->second->SetImplementation( - target->transport_->get()->CreateChannel(iter->first)); + ReplaceChannelProxyImpl( + iter->second, target->transport_->get()->CreateChannel(iter->first)); } } @@ -489,7 +523,7 @@ TransportProxy* BaseSession::GetOrCreateTransportProxy( transport->SignalRoleConflict.connect( this, &BaseSession::OnRoleConflict); - transproxy = new TransportProxy(sid_, content_name, + transproxy = new TransportProxy(worker_thread_, sid_, content_name, new TransportWrapper(transport)); transproxy->SignalCandidatesReady.connect( this, &BaseSession::OnTransportProxyCandidatesReady); @@ -964,10 +998,11 @@ bool Session::TerminateWithReason(const std::string& reason) { return true; } -bool Session::SendInfoMessage(const XmlElements& elems) { +bool Session::SendInfoMessage(const XmlElements& elems, + const std::string& remote_name) { ASSERT(signaling_thread()->IsCurrent()); SessionError error; - if (!SendMessage(ACTION_SESSION_INFO, elems, &error)) { + if (!SendMessage(ACTION_SESSION_INFO, elems, remote_name, &error)) { LOG(LS_ERROR) << "Could not send info message " << error.text; return false; } @@ -1610,11 +1645,16 @@ bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) { bool Session::SendMessage(ActionType type, const XmlElements& action_elems, SessionError* error) { + return SendMessage(type, action_elems, remote_name(), error); +} + +bool Session::SendMessage(ActionType type, const XmlElements& action_elems, + const std::string& remote_name, SessionError* error) { talk_base::scoped_ptr<buzz::XmlElement> stanza( new buzz::XmlElement(buzz::QN_IQ)); SessionMessage msg(current_protocol_, type, id(), initiator_name()); - msg.to = remote_name(); + msg.to = remote_name; WriteSessionMessage(msg, action_elems, stanza.get()); SignalOutgoingMessage(this, stanza.get()); 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 e05e3c0ace6..637c9424b7b 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/session.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/session.h @@ -91,10 +91,12 @@ class TransportProxy : public sigslot::has_slots<>, public CandidateTranslator { public: TransportProxy( + talk_base::Thread* worker_thread, const std::string& sid, const std::string& content_name, TransportWrapper* transport) - : sid_(sid), + : worker_thread_(worker_thread), + sid_(sid), content_name_(content_name), transport_(transport), connecting_(false), @@ -168,12 +170,21 @@ class TransportProxy : public sigslot::has_slots<>, private: TransportChannelProxy* GetChannelProxy(int component) const; TransportChannelProxy* GetChannelProxyByName(const std::string& name) const; - void ReplaceChannelProxyImpl(TransportChannelProxy* channel_proxy, - size_t index); + TransportChannelImpl* GetOrCreateChannelProxyImpl(int component); - void SetChannelProxyImpl(int component, + TransportChannelImpl* GetOrCreateChannelProxyImpl_w(int component); + + // Manipulators of transportchannelimpl in channel proxy. + void SetupChannelProxy(int component, TransportChannelProxy* proxy); + void SetupChannelProxy_w(int component, + TransportChannelProxy* proxy); + void ReplaceChannelProxyImpl(TransportChannelProxy* proxy, + TransportChannelImpl* impl); + void ReplaceChannelProxyImpl_w(TransportChannelProxy* proxy, + TransportChannelImpl* impl); + talk_base::Thread* worker_thread_; std::string sid_; std::string content_name_; talk_base::scoped_refptr<TransportWrapper> transport_; @@ -339,7 +350,7 @@ class BaseSession : public sigslot::has_slots<>, // Returns the transport that has been negotiated or NULL if // negotiation is still in progress. - Transport* GetTransport(const std::string& content_name); + virtual Transport* GetTransport(const std::string& content_name); // Creates a new channel with the given names. This method may be called // immediately after creating the session. However, the actual @@ -573,7 +584,8 @@ class Session : public BaseSession { // arbitrary XML messages, which are called "info" messages. Sending // takes ownership of the given elements. The signal does not; the // parent element will be deleted after the signal. - bool SendInfoMessage(const XmlElements& elems); + bool SendInfoMessage(const XmlElements& elems, + const std::string& remote_name); bool SendDescriptionInfoMessage(const ContentInfos& contents); sigslot::signal2<Session*, const buzz::XmlElement*> SignalInfoMessage; @@ -627,7 +639,7 @@ class Session : public BaseSession { bool ResendAllTransportInfoMessages(SessionError* error); bool SendAllUnsentTransportInfoMessages(SessionError* error); - // Both versions of SendMessage send a message of the given type to + // All versions of SendMessage send a message of the given type to // the other client. Can pass either a set of elements or an // "action", which must have a WriteSessionAction method to go along // with it. Sending with an action supports sending a "hybrid" @@ -637,6 +649,10 @@ class Session : public BaseSession { // Takes ownership of action_elems. bool SendMessage(ActionType type, const XmlElements& action_elems, SessionError* error); + // Sends a messge, but overrides the remote name. + bool SendMessage(ActionType type, const XmlElements& action_elems, + const std::string& remote_name, + SessionError* error); // When passing an action, may be Hybrid protocol. template <typename Action> bool SendMessage(ActionType type, const Action& action, 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 b64e7374221..ab4620f879e 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 @@ -814,7 +814,7 @@ struct ChannelHandler : sigslot::has_slots<> { } void OnReadPacket(cricket::TransportChannel* p, const char* buf, - size_t size, int flags) { + size_t size, const talk_base::PacketTime& time, int flags) { if (memcmp(buf, name.c_str(), name.size()) != 0) return; // drop packet if packet doesn't belong to this channel. This // can happen when transport channels are muxed together. 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 2a0f6d97830..38fc96ee1a4 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stun.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stun.cc @@ -188,7 +188,7 @@ bool StunMessage::ValidateMessageIntegrity(const char* data, size_t size, // Getting length of the message to calculate Message Integrity. size_t mi_pos = current_pos; - talk_base::scoped_array<char> temp_data(new char[current_pos]); + talk_base::scoped_ptr<char[]> temp_data(new char[current_pos]); memcpy(temp_data.get(), data, current_pos); if (size > mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize) { // Stun message has other attributes after message integrity. 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 5e0e5002970..913f9af5f5b 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunport.cc @@ -125,10 +125,11 @@ class StunBindingRequest : public StunRequest { }; UDPPort::UDPPort(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, talk_base::Network* network, talk_base::AsyncPacketSocket* socket, const std::string& username, const std::string& password) - : Port(thread, network, socket->GetLocalAddress().ipaddr(), + : Port(thread, factory, network, socket->GetLocalAddress().ipaddr(), username, password), requests_(thread), socket_(socket), @@ -139,10 +140,10 @@ UDPPort::UDPPort(talk_base::Thread* thread, } UDPPort::UDPPort(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, const std::string& password) + talk_base::PacketSocketFactory* factory, + talk_base::Network* network, + const talk_base::IPAddress& ip, int min_port, int max_port, + const std::string& username, const std::string& password) : Port(thread, LOCAL_PORT_TYPE, factory, network, ip, min_port, max_port, username, password), requests_(thread), @@ -253,9 +254,10 @@ void UDPPort::OnLocalAddressReady(talk_base::AsyncPacketSocket* socket, MaybePrepareStunCandidate(); } -void UDPPort::OnReadPacket(talk_base::AsyncPacketSocket* socket, - const char* data, size_t size, - const talk_base::SocketAddress& remote_addr) { +void UDPPort::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_); // Look for a response from the STUN server. @@ -268,7 +270,7 @@ void UDPPort::OnReadPacket(talk_base::AsyncPacketSocket* socket, } if (Connection* conn = GetConnection(remote_addr)) { - conn->OnReadPacket(data, size); + conn->OnReadPacket(data, size, packet_time); } else { Port::OnReadPacket(data, size, remote_addr, PROTO_UDP); } @@ -287,8 +289,13 @@ void UDPPort::SendStunBindingRequest() { if (server_addr_.IsUnresolved()) { ResolveStunAddress(); } else if (socket_->GetState() == talk_base::AsyncPacketSocket::STATE_BOUND) { - if (server_addr_.family() == ip().family()) { + // Check if |server_addr_| is compatible with the port's ip. + if (IsCompatibleAddress(server_addr_)) { requests_.Send(new StunBindingRequest(this, true, server_addr_)); + } else { + // Since we can't send stun messages to the server, we should mark this + // port ready. + OnStunBindingOrResolveRequestFailed(); } } } @@ -297,21 +304,21 @@ void UDPPort::ResolveStunAddress() { if (resolver_) return; - resolver_ = new talk_base::AsyncResolver(); - resolver_->SignalWorkDone.connect(this, &UDPPort::OnResolveResult); - resolver_->set_address(server_addr_); - resolver_->Start(); + resolver_ = socket_factory()->CreateAsyncResolver(); + resolver_->SignalDone.connect(this, &UDPPort::OnResolveResult); + resolver_->Start(server_addr_); } -void UDPPort::OnResolveResult(talk_base::SignalThread* t) { - ASSERT(t == resolver_); - if (resolver_->error() != 0) { +void UDPPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) { + ASSERT(resolver == resolver_); + if (resolver_->GetError() != 0 || + !resolver_->GetResolvedAddress(ip().family(), &server_addr_)) { LOG_J(LS_WARNING, this) << "StunPort: stun host lookup received error " - << resolver_->error(); + << resolver_->GetError(); OnStunBindingOrResolveRequestFailed(); + return; } - server_addr_ = resolver_->address(); SendStunBindingRequest(); } 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 7cfed4b7cb8..a8b89c3be5d 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunport.h @@ -46,11 +46,13 @@ namespace cricket { class UDPPort : public Port { public: static UDPPort* Create(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, talk_base::Network* network, talk_base::AsyncPacketSocket* socket, const std::string& username, const std::string& password) { - UDPPort* port = new UDPPort(thread, network, socket, username, password); + UDPPort* port = new UDPPort(thread, factory, network, socket, + username, password); if (!port->Init()) { delete port; port = NULL; @@ -66,8 +68,8 @@ class UDPPort : public Port { const std::string& username, const std::string& password) { UDPPort* port = new UDPPort(thread, factory, network, - ip, min_port, max_port, - username, password); + ip, min_port, max_port, + username, password); if (!port->Init()) { delete port; port = NULL; @@ -95,9 +97,10 @@ class UDPPort : public Port { virtual bool HandleIncomingPacket( talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& remote_addr) { + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { // All packets given to UDP port will be consumed. - OnReadPacket(socket, data, size, remote_addr); + OnReadPacket(socket, data, size, remote_addr, packet_time); return true; } @@ -114,8 +117,8 @@ class UDPPort : public Port { int min_port, int max_port, const std::string& username, const std::string& password); - UDPPort(talk_base::Thread* thread, talk_base::Network* network, - talk_base::AsyncPacketSocket* socket, + UDPPort(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, + talk_base::Network* network, talk_base::AsyncPacketSocket* socket, const std::string& username, const std::string& password); bool Init(); @@ -129,7 +132,9 @@ class UDPPort : public Port { const talk_base::SocketAddress& address); void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& remote_addr); + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); + void OnReadyToSend(talk_base::AsyncPacketSocket* socket); // This method will send STUN binding request if STUN server address is set. @@ -141,7 +146,7 @@ class UDPPort : public Port { private: // DNS resolution of the STUN server. void ResolveStunAddress(); - void OnResolveResult(talk_base::SignalThread* thread); + void OnResolveResult(talk_base::AsyncResolverInterface* resolver); // Below methods handles binding request responses. void OnStunBindingRequestSucceeded(const talk_base::SocketAddress& stun_addr); @@ -158,7 +163,7 @@ class UDPPort : public Port { StunRequestManager requests_; talk_base::AsyncPacketSocket* socket_; int error_; - talk_base::AsyncResolver* resolver_; + talk_base::AsyncResolverInterface* resolver_; bool ready_; int stun_keepalive_delay_; 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 3c1c6836f52..2a98a9fdb8b 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 @@ -27,7 +27,10 @@ #include "talk/base/gunit.h" #include "talk/base/helpers.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/scoped_ptr.h" #include "talk/base/socketaddress.h" +#include "talk/base/virtualsocketserver.h" #include "talk/p2p/base/basicpacketsocketfactory.h" #include "talk/p2p/base/stunport.h" #include "talk/p2p/base/teststunserver.h" @@ -48,7 +51,10 @@ class StunPortTest : public testing::Test, public sigslot::has_slots<> { public: StunPortTest() - : network_("unittest", "unittest", talk_base::IPAddress(INADDR_ANY), 32), + : pss_(new talk_base::PhysicalSocketServer), + ss_(new talk_base::VirtualSocketServer(pss_.get())), + ss_scope_(ss_.get()), + network_("unittest", "unittest", talk_base::IPAddress(INADDR_ANY), 32), socket_factory_(talk_base::Thread::Current()), stun_server_(new cricket::TestStunServer( talk_base::Thread::Current(), kStunAddr)), @@ -77,7 +83,8 @@ class StunPortTest : public testing::Test, ASSERT_TRUE(socket_ != NULL); socket_->SignalReadPacket.connect(this, &StunPortTest::OnReadPacket); stun_port_.reset(cricket::UDPPort::Create( - talk_base::Thread::Current(), &network_, socket_.get(), + talk_base::Thread::Current(), &socket_factory_, + &network_, socket_.get(), talk_base::CreateRandomString(16), talk_base::CreateRandomString(22))); ASSERT_TRUE(stun_port_ != NULL); stun_port_->set_server_addr(server_addr); @@ -92,13 +99,16 @@ class StunPortTest : public testing::Test, } void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data, - size_t size, const talk_base::SocketAddress& remote_addr) { - stun_port_->HandleIncomingPacket(socket, data, size, remote_addr); + size_t size, const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { + stun_port_->HandleIncomingPacket( + socket, data, size, remote_addr, talk_base::PacketTime()); } void SendData(const char* data, size_t len) { stun_port_->HandleIncomingPacket( - socket_.get(), data, len, talk_base::SocketAddress("22.22.22.22", 0)); + socket_.get(), data, len, talk_base::SocketAddress("22.22.22.22", 0), + talk_base::PacketTime()); } protected: @@ -120,6 +130,9 @@ class StunPortTest : public testing::Test, } private: + talk_base::scoped_ptr<talk_base::PhysicalSocketServer> pss_; + talk_base::scoped_ptr<talk_base::VirtualSocketServer> ss_; + talk_base::SocketServerScope ss_scope_; talk_base::Network network_; talk_base::BasicPacketSocketFactory socket_factory_; talk_base::scoped_ptr<cricket::UDPPort> stun_port_; 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 80719b4aa5e..062be206876 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.cc @@ -42,7 +42,8 @@ StunServer::~StunServer() { void StunServer::OnPacket( talk_base::AsyncPacketSocket* socket, const char* buf, size_t size, - const talk_base::SocketAddress& remote_addr) { + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { // Parse the STUN message; eat any messages that fail to parse. talk_base::ByteBuffer bbuf(buf, size); StunMessage msg; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.h b/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.h index 6e51ad184be..c5d12e1d15c 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/stunserver.h @@ -47,7 +47,8 @@ class StunServer : public sigslot::has_slots<> { // Slot for AsyncSocket.PacketRead: void OnPacket( talk_base::AsyncPacketSocket* socket, const char* buf, size_t size, - const talk_base::SocketAddress& remote_addr); + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); // Handlers for the different types of STUN/TURN requests: void OnBindingRequest(StunMessage* msg, 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 7f4db3b7212..abb19578696 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 @@ -82,6 +82,10 @@ class StunServerTest : public testing::Test { talk_base::scoped_ptr<talk_base::TestClient> client_; }; +// Disable for TSan v2, see +// https://code.google.com/p/webrtc/issues/detail?id=2517 for details. +#if !defined(THREAD_SANITIZER) + TEST_F(StunServerTest, TestGood) { StunMessage req; std::string transaction_id = "0123456789ab"; @@ -109,6 +113,8 @@ TEST_F(StunServerTest, TestGood) { delete msg; } +#endif // if !defined(THREAD_SANITIZER) + 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 " 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 11334c6225e..2cca82f1946 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.cc @@ -218,7 +218,8 @@ talk_base::AsyncPacketSocket* TCPPort::GetIncoming( void TCPPort::OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& remote_addr) { + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { Port::OnReadPacket(data, size, remote_addr, PROTO_TCP); } @@ -310,11 +311,12 @@ void TCPConnection::OnClose(talk_base::AsyncPacketSocket* socket, int error) { set_write_state(STATE_WRITE_TIMEOUT); } -void TCPConnection::OnReadPacket(talk_base::AsyncPacketSocket* socket, - const char* data, size_t size, - const talk_base::SocketAddress& remote_addr) { +void TCPConnection::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_); - Connection::OnReadPacket(data, size); + Connection::OnReadPacket(data, size, packet_time); } void TCPConnection::OnReadyToSend(talk_base::AsyncPacketSocket* socket) { 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 599d3c66bef..77b177a2d51 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/tcpport.h @@ -102,7 +102,8 @@ class TCPPort : public Port { // Receives packet signal from the local TCP Socket. void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& remote_addr); + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); void OnReadyToSend(talk_base::AsyncPacketSocket* socket); @@ -137,7 +138,8 @@ class TCPConnection : public Connection { void OnClose(talk_base::AsyncPacketSocket* socket, int error); void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& remote_addr); + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); void OnReadyToSend(talk_base::AsyncPacketSocket* socket); talk_base::AsyncPacketSocket* socket_; diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/testturnserver.h b/chromium/third_party/libjingle/source/talk/p2p/base/testturnserver.h index 32251c82c3d..7a3c83f3fa0 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/testturnserver.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/testturnserver.h @@ -47,8 +47,7 @@ class TestTurnServer : public TurnAuthInterface { const talk_base::SocketAddress& udp_int_addr, const talk_base::SocketAddress& udp_ext_addr) : server_(thread) { - server_.AddInternalSocket(talk_base::AsyncUDPSocket::Create( - thread->socketserver(), udp_int_addr), PROTO_UDP); + AddInternalSocket(udp_int_addr, cricket::PROTO_UDP); server_.SetExternalSocketFactory(new talk_base::BasicPacketSocketFactory(), udp_ext_addr); server_.set_realm(kTestRealm); @@ -62,6 +61,23 @@ class TestTurnServer : public TurnAuthInterface { TurnServer* server() { return &server_; } + void AddInternalSocket(const talk_base::SocketAddress& int_addr, + ProtocolType proto) { + talk_base::Thread* thread = talk_base::Thread::Current(); + if (proto == cricket::PROTO_UDP) { + server_.AddInternalSocket(talk_base::AsyncUDPSocket::Create( + thread->socketserver(), int_addr), proto); + } else if (proto == cricket::PROTO_TCP) { + // For TCP we need to create a server socket which can listen for incoming + // new connections. + talk_base::AsyncSocket* socket = + thread->socketserver()->CreateAsyncSocket(SOCK_STREAM); + socket->Bind(int_addr); + socket->Listen(5); + server_.AddInternalServerSocket(socket, proto); + } + } + private: // For this test server, succeed if the password is the same as the username. // Obviously, do not use this in a production environment. 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 3e4ad704068..4404c081a83 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transport.cc @@ -107,6 +107,29 @@ void Transport::SetIdentity(talk_base::SSLIdentity* identity) { worker_thread_->Invoke<void>(Bind(&Transport::SetIdentity_w, this, identity)); } +bool Transport::GetIdentity(talk_base::SSLIdentity** identity) { + // The identity is set on the worker thread, so for safety it must also be + // acquired on the worker thread. + return worker_thread_->Invoke<bool>( + Bind(&Transport::GetIdentity_w, this, identity)); +} + +bool Transport::GetRemoteCertificate(talk_base::SSLCertificate** cert) { + // Channels can be deleted on the worker thread, so for safety the remote + // certificate is acquired on the worker thread. + return worker_thread_->Invoke<bool>( + Bind(&Transport::GetRemoteCertificate_w, this, cert)); +} + +bool Transport::GetRemoteCertificate_w(talk_base::SSLCertificate** cert) { + ASSERT(worker_thread()->IsCurrent()); + if (channels_.empty()) + return false; + + ChannelMap::iterator iter = channels_.begin(); + return iter->second->GetRemoteCertificate(cert); +} + bool Transport::SetLocalTransportDescription( const TransportDescription& description, ContentAction action) { return worker_thread_->Invoke<bool>(Bind( 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 381215f5d1c..f9e9d887457 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transport.h @@ -247,6 +247,12 @@ class Transport : public talk_base::MessageHandler, // Must be called before applying local session description. void SetIdentity(talk_base::SSLIdentity* identity); + // Get a copy of the local identity provided by SetIdentity. + bool GetIdentity(talk_base::SSLIdentity** identity); + + // Get a copy of the remote certificate in use by the specified channel. + bool GetRemoteCertificate(talk_base::SSLCertificate** cert); + TransportProtocol protocol() const { return protocol_; } // Create, destroy, and lookup the channels of this type by their components. @@ -349,6 +355,10 @@ class Transport : public talk_base::MessageHandler, virtual void SetIdentity_w(talk_base::SSLIdentity* identity) {} + virtual bool GetIdentity_w(talk_base::SSLIdentity** identity) { + return false; + } + // 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. @@ -462,6 +472,8 @@ class Transport : public talk_base::MessageHandler, bool SetRemoteTransportDescription_w(const TransportDescription& desc, ContentAction action); bool GetStats_w(TransportStats* infos); + bool GetRemoteCertificate_w(talk_base::SSLCertificate** cert); + talk_base::Thread* signaling_thread_; talk_base::Thread* worker_thread_; 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 85fff7a9f92..47ba990f35f 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannel.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannel.h @@ -31,6 +31,7 @@ #include <string> #include <vector> +#include "talk/base/asyncpacketsocket.h" #include "talk/base/basictypes.h" #include "talk/base/dscp.h" #include "talk/base/sigslot.h" @@ -101,12 +102,18 @@ class TransportChannel : public sigslot::has_slots<> { // Default implementation. virtual bool GetSslRole(talk_base::SSLRole* role) const = 0; - // Set up the ciphers to use for DTLS-SRTP. + // Sets up the ciphers to use for DTLS-SRTP. virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers) = 0; - // Find out which DTLS-SRTP cipher was negotiated + // Finds out which DTLS-SRTP cipher was negotiated virtual bool GetSrtpCipher(std::string* cipher) = 0; + // Gets a copy of the local SSL identity, owned by the caller. + virtual bool GetLocalIdentity(talk_base::SSLIdentity** identity) const = 0; + + // Gets a copy of the remote side's SSL certificate, owned by the caller. + virtual bool GetRemoteCertificate(talk_base::SSLCertificate** cert) const = 0; + // Allows key material to be extracted for external encryption. virtual bool ExportKeyingMaterial(const std::string& label, const uint8* context, @@ -116,8 +123,8 @@ class TransportChannel : public sigslot::has_slots<> { size_t result_len) = 0; // Signalled each time a packet is received on this channel. - sigslot::signal4<TransportChannel*, const char*, - size_t, int> SignalReadPacket; + sigslot::signal5<TransportChannel*, const char*, + size_t, const talk_base::PacketTime&, int> SignalReadPacket; // This signal occurs when there is a change in the way that packets are // being routed, i.e. to a different remote location. The candidate 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 cde2441307b..d8432b73233 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelimpl.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelimpl.h @@ -93,7 +93,10 @@ class TransportChannelImpl : public TransportChannel { virtual void OnCandidate(const Candidate& candidate) = 0; // DTLS methods - // Set DTLS local identity. + // Set DTLS local identity. The identity object is not copied, but the caller + // retains ownership and must delete it after this TransportChannelImpl is + // destroyed. + // TODO(bemasc): Fix the ownership semantics of this method. virtual bool SetLocalIdentity(talk_base::SSLIdentity* identity) = 0; // Set DTLS Remote fingerprint. Must be after local identity set. 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 04b32ce6499..0d8cace2a86 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc @@ -27,6 +27,7 @@ #include "talk/p2p/base/transportchannelproxy.h" #include "talk/base/common.h" +#include "talk/base/logging.h" #include "talk/base/thread.h" #include "talk/p2p/base/transport.h" #include "talk/p2p/base/transportchannelimpl.h" @@ -54,8 +55,15 @@ TransportChannelProxy::~TransportChannelProxy() { } void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) { - // TODO(juberti): Fix this to occur on the correct thread. - // ASSERT(talk_base::Thread::Current() == worker_thread_); + 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."; + return; + } // Destroy any existing impl_. if (impl_) { @@ -172,6 +180,24 @@ bool TransportChannelProxy::GetSrtpCipher(std::string* cipher) { return impl_->GetSrtpCipher(cipher); } +bool TransportChannelProxy::GetLocalIdentity( + talk_base::SSLIdentity** identity) const { + ASSERT(talk_base::Thread::Current() == worker_thread_); + if (!impl_) { + return false; + } + return impl_->GetLocalIdentity(identity); +} + +bool TransportChannelProxy::GetRemoteCertificate( + talk_base::SSLCertificate** cert) const { + ASSERT(talk_base::Thread::Current() == worker_thread_); + if (!impl_) { + return false; + } + return impl_->GetRemoteCertificate(cert); +} + bool TransportChannelProxy::ExportKeyingMaterial(const std::string& label, const uint8* context, size_t context_len, @@ -208,12 +234,12 @@ void TransportChannelProxy::OnWritableState(TransportChannel* channel) { // Note: SignalWritableState fired by set_readable. } -void TransportChannelProxy::OnReadPacket(TransportChannel* channel, - const char* data, size_t size, - int flags) { +void TransportChannelProxy::OnReadPacket( + TransportChannel* channel, const char* data, size_t size, + const talk_base::PacketTime& packet_time, int flags) { ASSERT(talk_base::Thread::Current() == worker_thread_); ASSERT(channel == impl_); - SignalReadPacket(this, data, size, flags); + SignalReadPacket(this, data, size, packet_time, flags); } void TransportChannelProxy::OnReadyToSend(TransportChannel* channel) { 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 29f46634198..196d0f6cfab 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h @@ -75,6 +75,8 @@ class TransportChannelProxy : public TransportChannel, virtual bool SetSslRole(talk_base::SSLRole role); virtual bool SetSrtpCiphers(const std::vector<std::string>& ciphers); virtual bool GetSrtpCipher(std::string* cipher); + virtual bool GetLocalIdentity(talk_base::SSLIdentity** identity) const; + virtual bool GetRemoteCertificate(talk_base::SSLCertificate** cert) const; virtual bool ExportKeyingMaterial(const std::string& label, const uint8* context, size_t context_len, @@ -88,7 +90,7 @@ class TransportChannelProxy : public TransportChannel, void OnReadableState(TransportChannel* channel); void OnWritableState(TransportChannel* channel); void OnReadPacket(TransportChannel* channel, const char* data, size_t size, - int flags); + const talk_base::PacketTime& packet_time, int flags); void OnReadyToSend(TransportChannel* channel); void OnRouteChange(TransportChannel* channel, const Candidate& candidate); diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescription.cc b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescription.cc index 56873427284..2793d6c617a 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescription.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescription.cc @@ -48,5 +48,25 @@ bool StringToConnectionRole(const std::string& role_str, ConnectionRole* role) { return false; } +bool ConnectionRoleToString(const ConnectionRole& role, std::string* role_str) { + switch (role) { + case cricket::CONNECTIONROLE_ACTIVE: + *role_str = cricket::CONNECTIONROLE_ACTIVE_STR; + break; + case cricket::CONNECTIONROLE_ACTPASS: + *role_str = cricket::CONNECTIONROLE_ACTPASS_STR; + break; + case cricket::CONNECTIONROLE_PASSIVE: + *role_str = cricket::CONNECTIONROLE_PASSIVE_STR; + break; + case cricket::CONNECTIONROLE_HOLDCONN: + *role_str = cricket::CONNECTIONROLE_HOLDCONN_STR; + break; + default: + return false; + } + return true; +} + } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescription.h b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescription.h index 64fbb89a808..59dfa0b47de 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescription.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescription.h @@ -94,6 +94,7 @@ extern const char CONNECTIONROLE_ACTPASS_STR[]; extern const char CONNECTIONROLE_HOLDCONN_STR[]; bool StringToConnectionRole(const std::string& role_str, ConnectionRole* role); +bool ConnectionRoleToString(const ConnectionRole& role, std::string* role_str); typedef std::vector<Candidate> Candidates; @@ -159,7 +160,7 @@ struct TransportDescription { void AddOption(const std::string& option) { transport_options.push_back(option); } - bool secure() { return identity_fingerprint != NULL; } + bool secure() const { return identity_fingerprint != NULL; } static talk_base::SSLFingerprint* CopyFingerprint( const talk_base::SSLFingerprint* from) { diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc index 0c129437ccb..c8fb0b34f87 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc @@ -37,13 +37,11 @@ namespace cricket { static TransportProtocol kDefaultProtocol = ICEPROTO_GOOGLE; -static const char* kDefaultDigestAlg = talk_base::DIGEST_SHA_1; TransportDescriptionFactory::TransportDescriptionFactory() : protocol_(kDefaultProtocol), secure_(SEC_DISABLED), - identity_(NULL), - digest_alg_(kDefaultDigestAlg) { + identity_(NULL) { } TransportDescription* TransportDescriptionFactory::CreateOffer( @@ -153,10 +151,20 @@ bool TransportDescriptionFactory::SetSecurityInfo( return false; } + // This digest algorithm is used to produce the a=fingerprint lines in SDP. + // RFC 4572 Section 5 requires that those lines use the same hash function as + // the certificate's signature. + std::string digest_alg; + if (!identity_->certificate().GetSignatureDigestAlgorithm(&digest_alg)) { + LOG(LS_ERROR) << "Failed to retrieve the certificate's digest algorithm"; + return false; + } + desc->identity_fingerprint.reset( - talk_base::SSLFingerprint::Create(digest_alg_, identity_)); + talk_base::SSLFingerprint::Create(digest_alg, identity_)); if (!desc->identity_fingerprint.get()) { - LOG(LS_ERROR) << "Failed to create identity digest, alg=" << digest_alg_; + LOG(LS_ERROR) << "Failed to create identity fingerprint, alg=" + << digest_alg; return false; } diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.h b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.h index ddf27998188..53dd238dbe0 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.h @@ -59,8 +59,6 @@ class TransportDescriptionFactory { void set_secure(SecurePolicy s) { secure_ = s; } // Specifies the identity to use (only used when secure is not SEC_DISABLED). void set_identity(talk_base::SSLIdentity* identity) { identity_ = identity; } - // Specifies the algorithm to use when creating an identity digest. - void set_digest_algorithm(const std::string& alg) { digest_alg_ = alg; } // Creates a transport description suitable for use in an offer. TransportDescription* CreateOffer(const TransportOptions& options, @@ -78,7 +76,6 @@ class TransportDescriptionFactory { TransportProtocol protocol_; SecurePolicy secure_; talk_base::SSLIdentity* identity_; - std::string digest_alg_; }; } // namespace cricket diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory_unittest.cc b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory_unittest.cc index c0c88297f0c..8d9a73f56fc 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory_unittest.cc @@ -39,16 +39,11 @@ using cricket::TransportDescriptionFactory; using cricket::TransportDescription; using cricket::TransportOptions; -// TODO(juberti): Change this to SHA-256 once we have Win32 using OpenSSL. -static const char* kDefaultDigestAlg = talk_base::DIGEST_SHA_1; - class TransportDescriptionFactoryTest : public testing::Test { public: TransportDescriptionFactoryTest() : id1_(new talk_base::FakeSSLIdentity("User1")), id2_(new talk_base::FakeSSLIdentity("User2")) { - f1_.set_digest_algorithm(kDefaultDigestAlg); - f2_.set_digest_algorithm(kDefaultDigestAlg); } void CheckDesc(const TransportDescription* desc, const std::string& type, const std::string& opt, const std::string& ice_ufrag, @@ -167,15 +162,17 @@ TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtls) { f1_.set_protocol(cricket::ICEPROTO_HYBRID); f1_.set_secure(cricket::SEC_ENABLED); f1_.set_identity(id1_.get()); + std::string digest_alg; + ASSERT_TRUE(id1_->certificate().GetSignatureDigestAlgorithm(&digest_alg)); scoped_ptr<TransportDescription> desc(f1_.CreateOffer( TransportOptions(), NULL)); CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "google-ice", "", "", - kDefaultDigestAlg); + digest_alg); // Ensure it also works with SEC_REQUIRED. f1_.set_secure(cricket::SEC_REQUIRED); desc.reset(f1_.CreateOffer(TransportOptions(), NULL)); CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "google-ice", "", "", - kDefaultDigestAlg); + digest_alg); } // Test generating a hybrid offer with DTLS fails with no identity. @@ -187,23 +184,14 @@ TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtlsWithNoIdentity) { ASSERT_TRUE(desc.get() == NULL); } -// Test generating a hybrid offer with DTLS fails with an unsupported digest. -TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtlsWithBadDigestAlg) { - f1_.set_protocol(cricket::ICEPROTO_HYBRID); - f1_.set_secure(cricket::SEC_ENABLED); - f1_.set_identity(id1_.get()); - f1_.set_digest_algorithm("bogus"); - scoped_ptr<TransportDescription> desc(f1_.CreateOffer( - TransportOptions(), NULL)); - ASSERT_TRUE(desc.get() == NULL); -} - // Test updating a hybrid offer with DTLS to pick ICE. // The ICE credentials should stay the same in the new offer. TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtlsReofferIceDtls) { f1_.set_protocol(cricket::ICEPROTO_HYBRID); f1_.set_secure(cricket::SEC_ENABLED); f1_.set_identity(id1_.get()); + std::string digest_alg; + ASSERT_TRUE(id1_->certificate().GetSignatureDigestAlgorithm(&digest_alg)); scoped_ptr<TransportDescription> old_desc(f1_.CreateOffer( TransportOptions(), NULL)); ASSERT_TRUE(old_desc.get() != NULL); @@ -211,7 +199,7 @@ TEST_F(TransportDescriptionFactoryTest, TestOfferHybridDtlsReofferIceDtls) { scoped_ptr<TransportDescription> desc( f1_.CreateOffer(TransportOptions(), old_desc.get())); CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", - old_desc->ice_ufrag, old_desc->ice_pwd, kDefaultDigestAlg); + old_desc->ice_ufrag, old_desc->ice_pwd, digest_alg); } // Test that we can answer a GICE offer with GICE. @@ -358,21 +346,25 @@ TEST_F(TransportDescriptionFactoryTest, TestAnswerHybridDtlsToHybridDtls) { f1_.set_protocol(cricket::ICEPROTO_HYBRID); f1_.set_secure(cricket::SEC_ENABLED); f1_.set_identity(id1_.get()); + f2_.set_protocol(cricket::ICEPROTO_HYBRID); f2_.set_secure(cricket::SEC_ENABLED); f2_.set_identity(id2_.get()); + // f2_ produces the answer that is being checked in this test, so the + // answer must contain fingerprint lines with id2_'s digest algorithm. + std::string digest_alg2; + ASSERT_TRUE(id2_->certificate().GetSignatureDigestAlgorithm(&digest_alg2)); + scoped_ptr<TransportDescription> offer( f1_.CreateOffer(TransportOptions(), NULL)); ASSERT_TRUE(offer.get() != NULL); scoped_ptr<TransportDescription> desc( f2_.CreateAnswer(offer.get(), TransportOptions(), NULL)); - CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", - kDefaultDigestAlg); + CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", digest_alg2); f2_.set_secure(cricket::SEC_REQUIRED); desc.reset(f2_.CreateAnswer(offer.get(), TransportOptions(), NULL)); - CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", - kDefaultDigestAlg); + CheckDesc(desc.get(), cricket::NS_JINGLE_ICE_UDP, "", "", "", digest_alg2); } // Test that ice ufrag and password is changed in an updated offer and answer 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 35e51fc2da8..01d7f9c8998 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/turnport.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/turnport.cc @@ -214,6 +214,14 @@ void TurnPort::PrepareAddress() { if (server_address_.address.IsUnresolved()) { ResolveTurnAddress(server_address_.address); } else { + // If protocol family of server address doesn't match with local, return. + if (!IsCompatibleAddress(server_address_.address)) { + LOG(LS_ERROR) << "Server IP address family does not match with " + << "local host address family type"; + OnAllocateError(); + return; + } + LOG_J(LS_INFO, this) << "Trying to connect to TURN server via " << ProtoToString(server_address_.proto) << " @ " << server_address_.address.ToSensitiveString(); @@ -348,9 +356,10 @@ int TurnPort::SendTo(const void* data, size_t size, return static_cast<int>(size); } -void TurnPort::OnReadPacket(talk_base::AsyncPacketSocket* socket, - const char* data, size_t size, - const talk_base::SocketAddress& remote_addr) { +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(remote_addr == server_address_.address); @@ -365,9 +374,9 @@ void TurnPort::OnReadPacket(talk_base::AsyncPacketSocket* socket, // a response to a previous request. uint16 msg_type = talk_base::GetBE16(data); if (IsTurnChannelData(msg_type)) { - HandleChannelData(msg_type, data, size); + HandleChannelData(msg_type, data, size, packet_time); } else if (msg_type == TURN_DATA_INDICATION) { - HandleDataIndication(data, size); + HandleDataIndication(data, size, packet_time); } else { // This must be a response for one of our requests. // Check success responses, but not errors, for MESSAGE-INTEGRITY. @@ -391,22 +400,21 @@ void TurnPort::ResolveTurnAddress(const talk_base::SocketAddress& address) { if (resolver_) return; - resolver_ = new talk_base::AsyncResolver(); - resolver_->SignalWorkDone.connect(this, &TurnPort::OnResolveResult); - resolver_->set_address(address); - resolver_->Start(); + resolver_ = socket_factory()->CreateAsyncResolver(); + resolver_->SignalDone.connect(this, &TurnPort::OnResolveResult); + resolver_->Start(address); } -void TurnPort::OnResolveResult(talk_base::SignalThread* signal_thread) { - ASSERT(signal_thread == resolver_); - if (resolver_->error() != 0) { +void TurnPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) { + ASSERT(resolver == resolver_); + if (resolver_->GetError() != 0 || + !resolver_->GetResolvedAddress(ip().family(), &server_address_.address)) { LOG_J(LS_WARNING, this) << "TURN host lookup received error " - << resolver_->error(); + << resolver_->GetError(); OnAllocateError(); return; } - server_address_.address = resolver_->address(); PrepareAddress(); } @@ -453,7 +461,8 @@ void TurnPort::OnAllocateRequestTimeout() { OnAllocateError(); } -void TurnPort::HandleDataIndication(const char* data, size_t size) { +void TurnPort::HandleDataIndication(const char* data, size_t size, + const talk_base::PacketTime& packet_time) { // Read in the message, and process according to RFC5766, Section 10.4. talk_base::ByteBuffer buf(data, size); TurnMessage msg; @@ -488,11 +497,13 @@ void TurnPort::HandleDataIndication(const char* data, size_t size) { return; } - DispatchPacket(data_attr->bytes(), data_attr->length(), ext_addr, PROTO_UDP); + DispatchPacket(data_attr->bytes(), data_attr->length(), ext_addr, + PROTO_UDP, packet_time); } void TurnPort::HandleChannelData(int channel_id, const char* data, - size_t size) { + size_t size, + const talk_base::PacketTime& packet_time) { // Read the message, and process according to RFC5766, Section 11.6. // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -524,13 +535,14 @@ void TurnPort::HandleChannelData(int channel_id, const char* data, } DispatchPacket(data + TURN_CHANNEL_HEADER_SIZE, len, entry->address(), - PROTO_UDP); + PROTO_UDP, packet_time); } void TurnPort::DispatchPacket(const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, ProtocolType proto) { + const talk_base::SocketAddress& remote_addr, + ProtocolType proto, const talk_base::PacketTime& packet_time) { if (Connection* conn = GetConnection(remote_addr)) { - conn->OnReadPacket(data, size); + conn->OnReadPacket(data, size, packet_time); } else { Port::OnReadPacket(data, size, remote_addr, proto); } 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 4462b0c8c99..e380a8912f7 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/turnport.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/turnport.h @@ -32,11 +32,11 @@ #include <string> #include <list> +#include "talk/base/asyncpacketsocket.h" #include "talk/p2p/base/port.h" #include "talk/p2p/client/basicportallocator.h" namespace talk_base { -class AsyncPacketSocket; class AsyncResolver; class SignalThread; } @@ -79,9 +79,10 @@ class TurnPort : public Port { 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(talk_base::AsyncPacketSocket* socket, - const char* data, size_t size, - const talk_base::SocketAddress& remote_addr); + 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); @@ -123,7 +124,7 @@ class TurnPort : public Port { } void ResolveTurnAddress(const talk_base::SocketAddress& address); - void OnResolveResult(talk_base::SignalThread* signal_thread); + void OnResolveResult(talk_base::AsyncResolverInterface* resolver); void AddRequestAuthInfo(StunMessage* msg); void OnSendStunPacket(const void* data, size_t size, StunRequest* request); @@ -134,10 +135,13 @@ class TurnPort : public Port { void OnAllocateError(); void OnAllocateRequestTimeout(); - void HandleDataIndication(const char* data, size_t size); - void HandleChannelData(int channel_id, const char* data, size_t size); + void HandleDataIndication(const char* data, size_t size, + const talk_base::PacketTime& packet_time); + void HandleChannelData(int channel_id, const char* data, size_t size, + const talk_base::PacketTime& packet_time); void DispatchPacket(const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, ProtocolType proto); + const talk_base::SocketAddress& remote_addr, + ProtocolType proto, const talk_base::PacketTime& packet_time); bool ScheduleRefresh(int lifetime); void SendRequest(StunRequest* request, int delay); @@ -157,7 +161,7 @@ class TurnPort : public Port { talk_base::scoped_ptr<talk_base::AsyncPacketSocket> socket_; SocketOptionsMap socket_options_; - talk_base::AsyncResolver* resolver_; + talk_base::AsyncResolverInterface* resolver_; int error_; StunRequestManager request_manager_; 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 726175c5c2f..d559894ac56 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 @@ -53,11 +53,17 @@ using cricket::UDPPort; static const SocketAddress kLocalAddr1("11.11.11.11", 0); static const SocketAddress kLocalAddr2("22.22.22.22", 0); +static const SocketAddress kLocalIPv6Addr( + "2401:fa00:4:1000:be30:5bff:fee5:c3", 0); static const SocketAddress kTurnUdpIntAddr("99.99.99.3", cricket::TURN_SERVER_PORT); static const SocketAddress kTurnTcpIntAddr("99.99.99.4", cricket::TURN_SERVER_PORT); static const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0); +static const SocketAddress kTurnUdpIPv6IntAddr( + "2400:4030:1:2c00:be30:abcd:efab:cdef", cricket::TURN_SERVER_PORT); +static const SocketAddress kTurnUdpIPv6ExtAddr( + "2620:0:1000:1b03:2e41:38ff:fea6:f2a4", 0); static const char kIceUfrag1[] = "TESTICEUFRAG0001"; static const char kIceUfrag2[] = "TESTICEUFRAG0002"; @@ -71,6 +77,8 @@ static const cricket::ProtocolAddress kTurnUdpProtoAddr( kTurnUdpIntAddr, cricket::PROTO_UDP); static const cricket::ProtocolAddress kTurnTcpProtoAddr( kTurnTcpIntAddr, cricket::PROTO_TCP); +static const cricket::ProtocolAddress kTurnUdpIPv6ProtoAddr( + kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); class TurnPortTest : public testing::Test, public sigslot::has_slots<> { @@ -110,13 +118,15 @@ class TurnPortTest : public testing::Test, turn_create_permission_success_ = true; } } - void OnTurnReadPacket(Connection* conn, const char* data, size_t size) { + void OnTurnReadPacket(Connection* conn, const char* data, size_t size, + const talk_base::PacketTime& packet_time) { turn_packets_.push_back(talk_base::Buffer(data, size)); } void OnUdpPortComplete(Port* port) { udp_ready_ = true; } - void OnUdpReadPacket(Connection* conn, const char* data, size_t size) { + void OnUdpReadPacket(Connection* conn, const char* data, size_t size, + const talk_base::PacketTime& packet_time) { udp_packets_.push_back(talk_base::Buffer(data, size)); } @@ -130,9 +140,15 @@ class TurnPortTest : public testing::Test, void CreateTurnPort(const std::string& username, const std::string& password, const cricket::ProtocolAddress& server_address) { + CreateTurnPort(kLocalAddr1, username, password, server_address); + } + void CreateTurnPort(const talk_base::SocketAddress& local_address, + const std::string& username, + const std::string& password, + const cricket::ProtocolAddress& server_address) { cricket::RelayCredentials credentials(username, password); turn_port_.reset(TurnPort::Create(main_, &socket_factory_, &network_, - kLocalAddr1.ipaddr(), 0, 0, + local_address.ipaddr(), 0, 0, kIceUfrag1, kIcePwd1, server_address, credentials)); turn_port_->SignalPortComplete.connect(this, @@ -265,10 +281,7 @@ TEST_F(TurnPortTest, TestTurnAllocate) { } TEST_F(TurnPortTest, TestTurnTcpAllocate) { - talk_base::AsyncSocket* tcp_server_socket = - CreateServerSocket(kTurnTcpIntAddr); - turn_server_.server()->AddInternalServerSocket( - tcp_server_socket, cricket::PROTO_TCP); + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); EXPECT_EQ(0, turn_port_->SetOption(talk_base::Socket::OPT_SNDBUF, 10*1024)); turn_port_->PrepareAddress(); @@ -298,10 +311,7 @@ TEST_F(TurnPortTest, TestTurnConnection) { // Test that we can establish a TCP connection with TURN server. TEST_F(TurnPortTest, TestTurnTcpConnection) { - talk_base::AsyncSocket* tcp_server_socket = - CreateServerSocket(kTurnTcpIntAddr); - turn_server_.server()->AddInternalServerSocket( - tcp_server_socket, cricket::PROTO_TCP); + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); TestTurnConnection(); } @@ -327,19 +337,44 @@ TEST_F(TurnPortTest, TestTurnConnectionUsingOTUNonce) { TestTurnConnection(); } -// Do a TURN allocation, establish a connection, and send some data. +// Do a TURN allocation, establish a UDP connection, and send some data. TEST_F(TurnPortTest, TestTurnSendDataTurnUdpToUdp) { // Create ports and prepare addresses. CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); TestTurnSendData(); } +// Do a TURN allocation, establish a TCP connection, and send some data. TEST_F(TurnPortTest, TestTurnSendDataTurnTcpToUdp) { - talk_base::AsyncSocket* tcp_server_socket = - CreateServerSocket(kTurnTcpIntAddr); - turn_server_.server()->AddInternalServerSocket( - tcp_server_socket, cricket::PROTO_TCP); + turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); // Create ports and prepare addresses. CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); TestTurnSendData(); } + +// Test TURN fails to make a connection from IPv6 address to a server which has +// IPv4 address. +TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv4) { + turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); + CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, + kTurnUdpProtoAddr); + turn_port_->PrepareAddress(); + ASSERT_TRUE_WAIT(turn_error_, kTimeout); + EXPECT_TRUE(turn_port_->Candidates().empty()); +} + +// Test TURN make a connection from IPv6 address to a server which has +// IPv6 intenal address. But in this test external address is a IPv4 address, +// hence allocated address will be a IPv4 address. +TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv6ExtenalIPv4) { + turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP); + CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, + kTurnUdpIPv6ProtoAddr); + turn_port_->PrepareAddress(); + EXPECT_TRUE_WAIT(turn_ready_, kTimeout); + ASSERT_EQ(1U, turn_port_->Candidates().size()); + EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), + turn_port_->Candidates()[0].address().ipaddr()); + EXPECT_NE(0, turn_port_->Candidates()[0].address().port()); +} + 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 17ecf3507c6..0bd903abe22 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.cc @@ -109,7 +109,8 @@ class TurnServer::Allocation : public talk_base::MessageHandler, void OnExternalPacket(talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& addr); + const talk_base::SocketAddress& addr, + const talk_base::PacketTime& packet_time); static int ComputeLifetime(const TurnMessage* msg); bool HasPermission(const talk_base::IPAddress& addr); @@ -280,7 +281,8 @@ void TurnServer::OnInternalSocketClose(talk_base::AsyncPacketSocket* socket, void TurnServer::OnInternalPacket(talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& addr) { + const talk_base::SocketAddress& addr, + const talk_base::PacketTime& packet_time) { // Fail if the packet is too small to even contain a channel header. if (size < TURN_CHANNEL_HEADER_SIZE) { return; @@ -838,7 +840,8 @@ void TurnServer::Allocation::HandleChannelData(const char* data, size_t size) { void TurnServer::Allocation::OnExternalPacket( talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& addr) { + const talk_base::SocketAddress& addr, + const talk_base::PacketTime& packet_time) { ASSERT(external_socket_.get() == socket); Channel* channel = FindChannel(addr); if (channel) { diff --git a/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.h b/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.h index 56ce2fcb071..2c33cdb4b7b 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.h +++ b/chromium/third_party/libjingle/source/talk/p2p/base/turnserver.h @@ -33,13 +33,13 @@ #include <set> #include <string> +#include "talk/base/asyncpacketsocket.h" #include "talk/base/messagequeue.h" #include "talk/base/sigslot.h" #include "talk/base/socketaddress.h" #include "talk/p2p/base/portinterface.h" namespace talk_base { -class AsyncPacketSocket; class ByteBuffer; class PacketSocketFactory; class Thread; @@ -123,7 +123,8 @@ class TurnServer : public sigslot::has_slots<> { typedef std::map<Connection, Allocation*> AllocationMap; void OnInternalPacket(talk_base::AsyncPacketSocket* socket, const char* data, - size_t size, const talk_base::SocketAddress& address); + size_t size, const talk_base::SocketAddress& address, + const talk_base::PacketTime& packet_time); void OnNewInternalConnection(talk_base::AsyncSocket* socket); 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 a728d989b1b..dbc2e3342ee 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc +++ b/chromium/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc @@ -149,7 +149,9 @@ class AllocationSequence : public talk_base::MessageHandler, void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& remote_addr); + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); + void OnPortDestroyed(PortInterface* port); BasicPortAllocatorSession* session_; @@ -708,7 +710,7 @@ AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, config_(config), state_(kInit), flags_(flags), - udp_socket_(NULL), + udp_socket_(), phase_(0) { } @@ -857,7 +859,8 @@ void AllocationSequence::CreateUDPPorts() { // is enabled completely. UDPPort* port = NULL; if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET) && udp_socket_) { - port = UDPPort::Create(session_->network_thread(), network_, + port = UDPPort::Create(session_->network_thread(), + session_->socket_factory(), network_, udp_socket_.get(), session_->username(), session_->password()); } else { @@ -1023,13 +1026,15 @@ void AllocationSequence::CreateTurnPort(const RelayServerConfig& config) { void AllocationSequence::OnReadPacket( talk_base::AsyncPacketSocket* socket, const char* data, size_t size, - const talk_base::SocketAddress& remote_addr) { + 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)) { + if ((*iter)->HandleIncomingPacket( + socket, data, size, remote_addr, packet_time)) { break; } } diff --git a/chromium/third_party/libjingle/source/talk/p2p/client/fakeportallocator.h b/chromium/third_party/libjingle/source/talk/p2p/client/fakeportallocator.h index 2368948b073..5375e50156d 100644 --- a/chromium/third_party/libjingle/source/talk/p2p/client/fakeportallocator.h +++ b/chromium/third_party/libjingle/source/talk/p2p/client/fakeportallocator.h @@ -32,7 +32,7 @@ class FakePortAllocatorSession : public PortAllocatorSession { factory_(factory), network_("network", "unittest", talk_base::IPAddress(INADDR_LOOPBACK), 8), - port_(NULL), running_(false), + port_(), running_(false), port_config_count_(0) { network_.AddIP(talk_base::IPAddress(INADDR_LOOPBACK)); } 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 1ee97f11113..6966e44d04e 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 @@ -50,6 +50,8 @@ using talk_base::SocketAddress; 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 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); @@ -740,6 +742,36 @@ TEST_F(PortAllocatorTest, TestEnableSharedSocketNoUdpAllowed) { EXPECT_EQ(1U, candidates_.size()); } +// This test verifies allocator can use IPv6 addresses along with IPv4. +TEST_F(PortAllocatorTest, TestEnableIPv6Addresses) { + allocator().set_flags(allocator().flags() | + cricket::PORTALLOCATOR_DISABLE_RELAY | + cricket::PORTALLOCATOR_ENABLE_IPV6 | + cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | + cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET); + AddInterface(kClientIPv6Addr); + AddInterface(kClientAddr); + allocator_->set_step_delay(cricket::kMinimumStepDelay); + EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + ASSERT_EQ_WAIT(4U, ports_.size(), kDefaultAllocationTimeout); + EXPECT_EQ(4U, candidates_.size()); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + EXPECT_PRED5(CheckCandidate, candidates_[0], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "udp", + kClientIPv6Addr); + EXPECT_PRED5(CheckCandidate, candidates_[1], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "udp", + kClientAddr); + EXPECT_PRED5(CheckCandidate, candidates_[2], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "tcp", + kClientIPv6Addr); + EXPECT_PRED5(CheckCandidate, candidates_[3], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "tcp", + kClientAddr); + EXPECT_EQ(4U, candidates_.size()); +} + // Test that the httpportallocator correctly maintains its lists of stun and // relay servers, by never allowing an empty list. TEST(HttpPortAllocatorTest, TestHttpPortAllocatorHostLists) { 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 c963c36ec36..967846bd214 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/call.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/call.cc @@ -186,7 +186,7 @@ bool Call::SendViewRequest(Session* session, return false; } - return session->SendInfoMessage(elems); + return session->SendInfoMessage(elems, session->remote_name()); } void Call::SetLocalRenderer(VideoRenderer* renderer) { @@ -1079,7 +1079,11 @@ Session* Call::InternalInitiateSession(const std::string& id, const SessionDescription* offer = session_client_->CreateOffer(options); Session* session = session_client_->CreateSession(id, this); - session->set_initiator_name(initiator_name); + // Only override the initiator_name if it was manually supplied. Otherwise, + // session_client_ will supply the local jid as initiator in CreateOffer. + if (!initiator_name.empty()) { + session->set_initiator_name(initiator_name); + } AddSession(session, offer); session->Initiate(to.Str(), offer); 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 9b0a6c9c4a1..efb4ea396f4 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/call.h +++ b/chromium/third_party/libjingle/source/talk/session/media/call.h @@ -117,6 +117,9 @@ class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { MediaStreams* recv_streams = GetMediaStreams(session); return recv_streams ? &recv_streams->audio() : NULL; } + VoiceChannel* GetVoiceChannel(Session* session) const; + VideoChannel* GetVideoChannel(Session* session) const; + DataChannel* GetDataChannel(Session* session) const; // Public just for unit tests VideoContentDescription* CreateVideoStreamUpdate(const StreamParams& stream); // Takes ownership of video. @@ -193,9 +196,6 @@ class Call : public talk_base::MessageHandler, public sigslot::has_slots<> { void OnDataReceived(DataChannel* channel, const ReceiveDataParams& params, const talk_base::Buffer& payload); - VoiceChannel* GetVoiceChannel(Session* session) const; - VideoChannel* GetVideoChannel(Session* session) const; - DataChannel* GetDataChannel(Session* session) const; MediaStreams* GetMediaStreams(Session* session) const; void UpdateRemoteMediaStreams(Session* session, const ContentInfos& updated_contents, 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 f6259e9a34b..9a8559a5b05 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channel.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/channel.cc @@ -81,6 +81,7 @@ enum { MSG_SETSCREENCASTFACTORY, MSG_FIRSTPACKETRECEIVED, MSG_SESSION_ERROR, + MSG_NEWSTREAMRECEIVED, }; // Value specified in RFC 5764. @@ -398,7 +399,6 @@ BaseChannel::BaseChannel(talk_base::Thread* thread, writable_(false), rtp_ready_to_send_(false), rtcp_ready_to_send_(false), - optimistic_data_send_(false), was_ever_writable_(false), local_content_direction_(MD_INACTIVE), remote_content_direction_(MD_INACTIVE), @@ -411,6 +411,7 @@ BaseChannel::BaseChannel(talk_base::Thread* thread, BaseChannel::~BaseChannel() { ASSERT(worker_thread_ == talk_base::Thread::Current()); + Deinit(); StopConnectionMonitor(); FlushRtcpMessages(); // Send any outstanding RTCP packets. Clear(); // eats any outstanding messages or packets @@ -438,7 +439,6 @@ bool BaseChannel::Init(TransportChannel* transport_channel, return false; } - media_channel_->SetInterface(this); transport_channel_->SignalWritableState.connect( this, &BaseChannel::OnWritableState); transport_channel_->SignalReadPacket.connect( @@ -452,9 +452,16 @@ bool BaseChannel::Init(TransportChannel* transport_channel, this, &BaseChannel::OnNewRemoteDescription); set_rtcp_transport_channel(rtcp_transport_channel); + // Both RTP and RTCP channels are set, we can call SetInterface on + // media channel and it can set network options. + media_channel_->SetInterface(this); return true; } +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); @@ -580,11 +587,16 @@ bool BaseChannel::SendRtcp(talk_base::Buffer* packet, int BaseChannel::SetOption(SocketType type, talk_base::Socket::Option opt, int value) { + TransportChannel* channel = NULL; switch (type) { - case ST_RTP: return transport_channel_->SetOption(opt, value); - case ST_RTCP: return rtcp_transport_channel_->SetOption(opt, value); - default: return -1; + case ST_RTP: + channel = transport_channel_; + break; + case ST_RTCP: + channel = rtcp_transport_channel_; + break; } + return channel ? channel->SetOption(opt, value) : -1; } void BaseChannel::OnWritableState(TransportChannel* channel) { @@ -598,7 +610,9 @@ void BaseChannel::OnWritableState(TransportChannel* channel) { } void BaseChannel::OnChannelRead(TransportChannel* channel, - const char* data, size_t len, int flags) { + const char* data, size_t len, + const talk_base::PacketTime& packet_time, + int flags) { // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine ASSERT(worker_thread_ == talk_base::Thread::Current()); @@ -606,7 +620,7 @@ void BaseChannel::OnChannelRead(TransportChannel* channel, // transport. We feed RTP traffic into the demuxer to determine if it is RTCP. bool rtcp = PacketIsRtcp(channel, data, len); talk_base::Buffer packet(data, len); - HandlePacket(rtcp, &packet); + HandlePacket(rtcp, &packet, packet_time); } void BaseChannel::OnReadyToSend(TransportChannel* channel) { @@ -641,12 +655,6 @@ bool BaseChannel::PacketIsRtcp(const TransportChannel* channel, bool BaseChannel::SendPacket(bool rtcp, talk_base::Buffer* packet, talk_base::DiffServCodePoint dscp) { - // Unless we're sending optimistically, we only allow packets through when we - // are completely writable. - if (!optimistic_data_send_ && !writable_) { - return false; - } - // SendPacket gets called from MediaEngine, typically on an encoder thread. // If the thread is not our worker thread, we will post to our worker // so that the real work happens on our worker. This avoids us having to @@ -670,7 +678,7 @@ bool BaseChannel::SendPacket(bool rtcp, talk_base::Buffer* packet, // transport. TransportChannel* channel = (!rtcp || rtcp_mux_filter_.IsActive()) ? transport_channel_ : rtcp_transport_channel_; - if (!channel || (!optimistic_data_send_ && !channel->writable())) { + if (!channel || !channel->writable()) { return false; } @@ -768,7 +776,8 @@ bool BaseChannel::WantsPacket(bool rtcp, talk_base::Buffer* packet) { return true; } -void BaseChannel::HandlePacket(bool rtcp, talk_base::Buffer* packet) { +void BaseChannel::HandlePacket(bool rtcp, talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time) { if (!WantsPacket(rtcp, packet)) { return; } @@ -837,9 +846,9 @@ void BaseChannel::HandlePacket(bool rtcp, talk_base::Buffer* packet) { // Push it down to the media channel. if (!rtcp) { - media_channel_->OnPacketReceived(packet); + media_channel_->OnPacketReceived(packet, packet_time); } else { - media_channel_->OnRtcpReceived(packet); + media_channel_->OnRtcpReceived(packet, packet_time); } } @@ -1082,32 +1091,42 @@ 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) { + *dtls = transport_channel_->IsDtlsActive(); + if (*dtls && !cryptos.empty()) { + LOG(LS_WARNING) << "Cryptos must be empty when DTLS is active."; + return false; + } + return true; +} + bool BaseChannel::SetSrtp_w(const std::vector<CryptoParams>& cryptos, ContentAction action, ContentSource src) { bool ret = false; + bool dtls = false; + ret = CheckSrtpConfig(cryptos, &dtls); switch (action) { case CA_OFFER: - ret = srtp_filter_.SetOffer(cryptos, src); + // If DTLS is already active on the channel, we could be renegotiating + // here. We don't update the srtp filter. + if (ret && !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 (transport_channel_->IsDtlsActive()) { - LOG(LS_INFO) << - "Ignoring SDES answer parameters because we are using DTLS-SRTP"; - ret = true; - } else { + if (ret && !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 (transport_channel_->IsDtlsActive()) { - LOG(LS_INFO) << - "Ignoring SDES answer parameters because we are using DTLS-SRTP"; - ret = true; - } else { + if (ret && !dtls) { ret = srtp_filter_.SetAnswer(cryptos, src); } break; @@ -1472,6 +1491,7 @@ VoiceChannel::~VoiceChannel() { StopMediaMonitor(); // this can't be done in the base class, since it calls a virtual DisableMedia_w(); + Deinit(); } bool VoiceChannel::Init() { @@ -1628,8 +1648,10 @@ void VoiceChannel::GetActiveStreams_w(AudioInfo::StreamList* actives) { } void VoiceChannel::OnChannelRead(TransportChannel* channel, - const char* data, size_t len, int flags) { - BaseChannel::OnChannelRead(channel, data, len, flags); + const char* data, size_t len, + const talk_base::PacketTime& packet_time, + int flags) { + BaseChannel::OnChannelRead(channel, data, len, packet_time, flags); // Set a flag when we've received an RTP packet. If we're waiting for early // media, this will disable the timeout. @@ -1967,6 +1989,8 @@ VideoChannel::~VideoChannel() { StopMediaMonitor(); // this can't be done in the base class, since it calls a virtual DisableMedia_w(); + + Deinit(); } bool VideoChannel::SetRenderer(uint32 ssrc, VideoRenderer* renderer) { @@ -2138,7 +2162,9 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, // Tweak our video processing settings, if needed. VideoOptions video_options; media_channel()->GetOptions(&video_options); - video_options.conference_mode.Set(video->conference_mode()); + if (video->conference_mode()) { + video_options.conference_mode.Set(true); + } video_options.buffered_mode_latency.Set(video->buffered_mode_latency()); if (!media_channel()->SetOptions(video_options)) { @@ -2463,13 +2489,16 @@ DataChannel::DataChannel(talk_base::Thread* thread, bool rtcp) // MediaEngine is NULL : BaseChannel(thread, NULL, media_channel, session, content_name, rtcp), - data_channel_type_(cricket::DCT_NONE) { + data_channel_type_(cricket::DCT_NONE), + ready_to_send_data_(false) { } DataChannel::~DataChannel() { StopMediaMonitor(); // this can't be done in the base class, since it calls a virtual DisableMedia_w(); + + Deinit(); } bool DataChannel::Init() { @@ -2486,6 +2515,8 @@ bool DataChannel::Init() { this, &DataChannel::OnDataChannelError); media_channel()->SignalReadyToSend.connect( this, &DataChannel::OnDataChannelReadyToSend); + media_channel()->SignalNewStreamReceived.connect( + this, &DataChannel::OnDataChannelNewStreamReceived); srtp_filter()->SignalSrtpError.connect( this, &DataChannel::OnSrtpError); return true; @@ -2578,6 +2609,9 @@ bool DataChannel::SetLocalContent_w(const MediaContentDescription* content, ret = UpdateLocalStreams_w(data->streams(), action); 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()); } } else { ret = SetBaseLocalContent_w(content, action); @@ -2616,6 +2650,9 @@ bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content, ret = UpdateRemoteStreams_w(content->streams(), action); 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()); } } else { // If the remote data doesn't have codecs and isn't an update, it @@ -2677,7 +2714,8 @@ void DataChannel::OnMessage(talk_base::Message *pmsg) { case MSG_READYTOSENDDATA: { DataChannelReadyToSendMessageData* data = static_cast<DataChannelReadyToSendMessageData*>(pmsg->pdata); - SignalReadyToSendData(data->data()); + ready_to_send_data_ = data->data(); + SignalReadyToSendData(ready_to_send_data_); delete data; break; } @@ -2702,6 +2740,13 @@ 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); + delete data; + break; + } default: BaseChannel::OnMessage(pmsg); break; @@ -2757,6 +2802,14 @@ 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) { 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 0d66be9a90d..d297ee4b8fd 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channel.h +++ b/chromium/third_party/libjingle/source/talk/session/media/channel.h @@ -31,6 +31,7 @@ #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" @@ -66,6 +67,12 @@ enum SinkType { // BaseChannel contains logic common to voice and video, including // enable/mute, marshaling calls to a worker thread, and // connection and media monitors. +// +// WARNING! SUBCLASSES MUST CALL Deinit() IN THEIR DESTRUCTORS! +// This is required to avoid a data race between the destructor modifying the +// vtable, and the media channel's thread using BaseChannel as the +// NetworkInterface. + class BaseChannel : public talk_base::MessageHandler, public sigslot::has_slots<>, public MediaChannel::NetworkInterface { @@ -76,6 +83,9 @@ class BaseChannel virtual ~BaseChannel(); bool Init(TransportChannel* transport_channel, TransportChannel* rtcp_transport_channel); + // Deinit may be called multiple times and is simply ignored if it's alreay + // done. + void Deinit(); talk_base::Thread* worker_thread() const { return worker_thread_; } BaseSession* session() const { return session_; } @@ -87,10 +97,6 @@ class BaseChannel return rtcp_transport_channel_; } bool enabled() const { return enabled_; } - // Set to true to have the channel optimistically allow data to be sent even - // when the channel isn't fully writable. - void set_optimistic_data_send(bool value) { optimistic_data_send_ = value; } - bool optimistic_data_send() const { return optimistic_data_send_; } // This function returns true if we are using SRTP. bool secure() const { return srtp_filter_.IsActive(); } @@ -259,8 +265,11 @@ class BaseChannel // From TransportChannel void OnWritableState(TransportChannel* channel); - virtual void OnChannelRead(TransportChannel* channel, const char* data, - size_t len, int flags); + virtual void OnChannelRead(TransportChannel* channel, + const char* data, + size_t len, + const talk_base::PacketTime& packet_time, + int flags); void OnReadyToSend(TransportChannel* channel); bool PacketIsRtcp(const TransportChannel* channel, const char* data, @@ -268,7 +277,8 @@ class BaseChannel bool SendPacket(bool rtcp, talk_base::Buffer* packet, talk_base::DiffServCodePoint dscp); virtual bool WantsPacket(bool rtcp, talk_base::Buffer* packet); - void HandlePacket(bool rtcp, talk_base::Buffer* packet); + void HandlePacket(bool rtcp, talk_base::Buffer* packet, + const talk_base::PacketTime& packet_time); // Apply the new local/remote session description. void OnNewLocalDescription(BaseSession* session, ContentAction action); @@ -309,6 +319,7 @@ class BaseChannel 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); @@ -351,7 +362,6 @@ class BaseChannel bool writable_; bool rtp_ready_to_send_; bool rtcp_ready_to_send_; - bool optimistic_data_send_; bool was_ever_writable_; MediaContentDirection local_content_direction_; MediaContentDirection remote_content_direction_; @@ -435,7 +445,9 @@ class VoiceChannel : public BaseChannel { private: // overrides from BaseChannel virtual void OnChannelRead(TransportChannel* channel, - const char* data, size_t len, int flags); + const char* data, size_t len, + const talk_base::PacketTime& packet_time, + int flags); virtual void ChangeState(); virtual const ContentInfo* GetFirstContent(const SessionDescription* sdesc); virtual bool SetLocalContent_w(const MediaContentDescription* content, @@ -604,6 +616,11 @@ class DataChannel : public BaseChannel { void StartMediaMonitor(int cms); void StopMediaMonitor(); + // Should be called on the signaling thread only. + bool ready_to_send_data() const { + return ready_to_send_data_; + } + sigslot::signal2<DataChannel*, const DataMediaInfo&> SignalMediaMonitor; sigslot::signal2<DataChannel*, const std::vector<ConnectionInfo>&> SignalConnectionMonitor; @@ -617,6 +634,11 @@ 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; protected: // downcasts a MediaChannel. @@ -656,6 +678,17 @@ 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 @@ -684,12 +717,15 @@ 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); talk_base::scoped_ptr<DataMediaMonitor> media_monitor_; // TODO(pthatcher): Make a separate SctpDataChannel and // RtpDataChannel instead of using this. DataChannelType data_channel_type_; + bool ready_to_send_data_; }; } // namespace cricket 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 fda89b3c6ed..48a9bdef563 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 @@ -233,6 +233,10 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { this, &ChannelTest<T>::OnMediaChannelError); channel1_->SignalAutoMuted.connect( this, &ChannelTest<T>::OnMediaMuted); + if ((flags1 & DTLS) && (flags2 & DTLS)) { + flags1 = (flags1 & ~SECURE); + flags2 = (flags2 & ~SECURE); + } CreateContent(flags1, kPcmuCodec, kH264Codec, &local_media_content1_); CreateContent(flags2, kPcmuCodec, kH264Codec, @@ -492,11 +496,6 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { // overridden in specialized classes } - void SetOptimisticDataSend(bool optimistic_data_send) { - channel1_->set_optimistic_data_send(optimistic_data_send); - channel2_->set_optimistic_data_send(optimistic_data_send); - } - // Creates a cricket::SessionDescription with one MediaContent and one stream. // kPcmuCodec is used as audio codec and kH264Codec is used as video codec. cricket::SessionDescription* CreateSessionDescriptionWithStream(uint32 ssrc) { @@ -1390,19 +1389,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_TRUE(CheckNoRtp1()); EXPECT_TRUE(CheckNoRtp2()); - // Lose writability, with optimistic send - SetOptimisticDataSend(true); + // Lose writability, which should fail. GetTransport1()->SetWritable(false); - EXPECT_TRUE(media_channel1_->sending()); - EXPECT_TRUE(SendRtp1()); - EXPECT_TRUE(SendRtp2()); - EXPECT_TRUE(CheckRtp1()); - EXPECT_TRUE(CheckRtp2()); - EXPECT_TRUE(CheckNoRtp1()); - EXPECT_TRUE(CheckNoRtp2()); - - // Check again with optimistic send off, which should fail. - SetOptimisticDataSend(false); EXPECT_FALSE(SendRtp1()); EXPECT_TRUE(SendRtp2()); EXPECT_TRUE(CheckRtp1()); @@ -1422,13 +1410,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { GetTransport1()->SetDestination(NULL); EXPECT_TRUE(media_channel1_->sending()); - // Should fail regardless of optimistic send at this point. - SetOptimisticDataSend(true); - EXPECT_FALSE(SendRtp1()); - EXPECT_TRUE(SendRtp2()); - EXPECT_TRUE(CheckRtp1()); - EXPECT_TRUE(CheckNoRtp2()); - SetOptimisticDataSend(false); + // Should fail also. EXPECT_FALSE(SendRtp1()); EXPECT_TRUE(SendRtp2()); EXPECT_TRUE(CheckRtp1()); @@ -1793,7 +1775,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { channel2_->transport_channel(); transport_channel->SignalReadPacket( transport_channel, reinterpret_cast<const char*>(kBadPacket), - sizeof(kBadPacket), 0); + sizeof(kBadPacket), talk_base::PacketTime(), 0); EXPECT_EQ_WAIT(T::MediaChannel::ERROR_PLAY_SRTP_ERROR, error_, 500); } 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 36c71832da3..d4fcc79dfa7 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channelmanager.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/channelmanager.cc @@ -116,9 +116,10 @@ void ChannelManager::Construct(MediaEngineInterface* me, initialized_ = false; main_thread_ = talk_base::Thread::Current(); worker_thread_ = worker_thread; + // Get the default audio options from the media engine. + audio_options_ = media_engine_->GetAudioOptions(); audio_in_device_ = DeviceManagerInterface::kDefaultDeviceName; audio_out_device_ = DeviceManagerInterface::kDefaultDeviceName; - audio_options_ = MediaEngineInterface::DEFAULT_AUDIO_OPTIONS; audio_delay_offset_ = MediaEngineInterface::kDefaultAudioDelayOffset; audio_output_volume_ = kNotSetOutputVolume; local_renderer_ = NULL; @@ -251,7 +252,7 @@ bool ChannelManager::Init() { LOG(LS_WARNING) << "Failed to SetAudioOptions with" << " microphone: " << audio_in_device_ << " speaker: " << audio_out_device_ - << " options: " << audio_options_ + << " options: " << audio_options_.ToString() << " delay: " << audio_delay_offset_; } @@ -502,23 +503,26 @@ void ChannelManager::DestroySoundclip_w(Soundclip* soundclip) { } bool ChannelManager::GetAudioOptions(std::string* in_name, - std::string* out_name, int* opts) { + std::string* out_name, + AudioOptions* options) { if (in_name) *in_name = audio_in_device_; if (out_name) *out_name = audio_out_device_; - if (opts) - *opts = audio_options_; + if (options) + *options = audio_options_; return true; } bool ChannelManager::SetAudioOptions(const std::string& in_name, - const std::string& out_name, int opts) { - return SetAudioOptions(in_name, out_name, opts, audio_delay_offset_); + const std::string& out_name, + const AudioOptions& options) { + return SetAudioOptions(in_name, out_name, options, audio_delay_offset_); } bool ChannelManager::SetAudioOptions(const std::string& in_name, - const std::string& out_name, int opts, + const std::string& out_name, + const AudioOptions& options, int delay_offset) { // Get device ids from DeviceManager. Device in_dev, out_dev; @@ -536,12 +540,12 @@ bool ChannelManager::SetAudioOptions(const std::string& in_name, if (initialized_) { ret = worker_thread_->Invoke<bool>( Bind(&ChannelManager::SetAudioOptions_w, this, - opts, delay_offset, &in_dev, &out_dev)); + options, delay_offset, &in_dev, &out_dev)); } // If all worked well, save the values for use in GetAudioOptions. if (ret) { - audio_options_ = opts; + audio_options_ = options; audio_in_device_ = in_name; audio_out_device_ = out_name; audio_delay_offset_ = delay_offset; @@ -549,13 +553,14 @@ bool ChannelManager::SetAudioOptions(const std::string& in_name, return ret; } -bool ChannelManager::SetAudioOptions_w(int opts, int delay_offset, +bool ChannelManager::SetAudioOptions_w( + const AudioOptions& options, int delay_offset, const Device* in_dev, const Device* out_dev) { ASSERT(worker_thread_ == talk_base::Thread::Current()); ASSERT(initialized_); // Set audio options - bool ret = media_engine_->SetAudioOptions(opts); + bool ret = media_engine_->SetAudioOptions(options); if (ret) { ret = media_engine_->SetAudioDelayOffset(delay_offset); @@ -934,4 +939,12 @@ 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; +} + } // 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 04af5e19632..fdb8f733624 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/channelmanager.h +++ b/chromium/third_party/libjingle/source/talk/session/media/channelmanager.h @@ -137,9 +137,11 @@ class ChannelManager : public talk_base::MessageHandler, // Configures the audio and video devices. A null pointer can be passed to // GetAudioOptions() for any parameter of no interest. bool GetAudioOptions(std::string* wave_in_device, - std::string* wave_out_device, int* opts); + std::string* wave_out_device, + AudioOptions* options); bool SetAudioOptions(const std::string& wave_in_device, - const std::string& wave_out_device, int opts); + const std::string& wave_out_device, + const AudioOptions& options); bool GetOutputVolume(int* level); bool SetOutputVolume(int level); bool IsSameCapturer(const std::string& capturer_name, @@ -223,11 +225,17 @@ class ChannelManager : public talk_base::MessageHandler, // TODO(hellner): Remove this function once the engine capturer has been // 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. bool SetAudioOptions(const std::string& wave_in_device, - const std::string& wave_out_device, int opts, + const std::string& wave_out_device, + const AudioOptions& options, int delay_offset); int audio_delay_offset() const { return audio_delay_offset_; } @@ -256,8 +264,8 @@ class ChannelManager : public talk_base::MessageHandler, void DestroyDataChannel_w(DataChannel* data_channel); Soundclip* CreateSoundclip_w(); void DestroySoundclip_w(Soundclip* soundclip); - bool SetAudioOptions_w(int opts, int delay_offset, const Device* in_dev, - const Device* out_dev); + bool SetAudioOptions_w(const AudioOptions& options, int delay_offset, + const Device* in_dev, const Device* out_dev); bool SetCaptureDevice_w(const Device* cam_device); void OnVideoCaptureStateChange(VideoCapturer* capturer, CaptureState result); @@ -283,7 +291,7 @@ class ChannelManager : public talk_base::MessageHandler, std::string audio_in_device_; std::string audio_out_device_; - int audio_options_; + AudioOptions audio_options_; int audio_delay_offset_; int audio_output_volume_; std::string camera_device_; 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 6f7c7687154..d0d380d3eb8 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 @@ -149,11 +149,12 @@ TEST_F(ChannelManagerTest, CreateDestroyChannels) { } // Test that we can create and destroy a voice and video channel with a worker. -// BUG=https://code.google.com/p/webrtc/issues/detail?id=2355 -TEST_F(ChannelManagerTest, DISABLED_CreateDestroyChannelsOnThread) { +TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) { worker_.Start(); EXPECT_TRUE(cm_->set_worker_thread(&worker_)); EXPECT_TRUE(cm_->Init()); + delete session_; + session_ = new cricket::FakeSession(&worker_, true); cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel( session_, cricket::CN_AUDIO, false); EXPECT_TRUE(voice_channel != NULL); @@ -254,44 +255,43 @@ TEST_F(ChannelManagerTest, SetDefaultVideoCodecBeforeInit) { TEST_F(ChannelManagerTest, SetAudioOptionsBeforeInit) { // Test that values that we set before Init are applied. - EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2)); - EXPECT_TRUE(cm_->Init()); - EXPECT_EQ("audio-in1", fme_->audio_in_device()); - EXPECT_EQ("audio-out1", fme_->audio_out_device()); - EXPECT_EQ(0x2, fme_->audio_options()); - EXPECT_EQ(0, fme_->audio_delay_offset()); - EXPECT_EQ(cricket::MediaEngineInterface::kDefaultAudioDelayOffset, - fme_->audio_delay_offset()); -} - -TEST_F(ChannelManagerTest, GetAudioOptionsBeforeInit) { + AudioOptions options; + options.auto_gain_control.Set(true); + options.echo_cancellation.Set(false); + EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", options)); std::string audio_in, audio_out; - int opts; - // Test that GetAudioOptions works before Init. - EXPECT_TRUE(cm_->SetAudioOptions("audio-in2", "audio-out2", 0x1)); - EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &opts)); - EXPECT_EQ("audio-in2", audio_in); - EXPECT_EQ("audio-out2", audio_out); - EXPECT_EQ(0x1, opts); - // Test that options set before Init can be gotten after Init. - EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2)); + AudioOptions set_options; + // Check options before Init. + EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &set_options)); + EXPECT_EQ("audio-in1", audio_in); + EXPECT_EQ("audio-out1", audio_out); + EXPECT_EQ(options, set_options); EXPECT_TRUE(cm_->Init()); - EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &opts)); + // Check options after Init. + EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &set_options)); EXPECT_EQ("audio-in1", audio_in); EXPECT_EQ("audio-out1", audio_out); - EXPECT_EQ(0x2, opts); + EXPECT_EQ(options, set_options); + // At this point, the media engine should also be initialized. + EXPECT_EQ(options, fme_->audio_options()); + EXPECT_EQ(cricket::MediaEngineInterface::kDefaultAudioDelayOffset, + fme_->audio_delay_offset()); } TEST_F(ChannelManagerTest, GetAudioOptionsWithNullParameters) { std::string audio_in, audio_out; - int opts; - EXPECT_TRUE(cm_->SetAudioOptions("audio-in2", "audio-out2", 0x1)); + AudioOptions options; + options.echo_cancellation.Set(true); + EXPECT_TRUE(cm_->SetAudioOptions("audio-in2", "audio-out2", options)); EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, NULL, NULL)); EXPECT_EQ("audio-in2", audio_in); EXPECT_TRUE(cm_->GetAudioOptions(NULL, &audio_out, NULL)); EXPECT_EQ("audio-out2", audio_out); - EXPECT_TRUE(cm_->GetAudioOptions(NULL, NULL, &opts)); - EXPECT_EQ(0x1, opts); + AudioOptions out_options; + EXPECT_TRUE(cm_->GetAudioOptions(NULL, NULL, &out_options)); + bool echo_cancellation = false; + EXPECT_TRUE(out_options.echo_cancellation.Get(&echo_cancellation)); + EXPECT_TRUE(echo_cancellation); } TEST_F(ChannelManagerTest, SetAudioOptions) { @@ -301,47 +301,22 @@ TEST_F(ChannelManagerTest, SetAudioOptions) { fme_->audio_in_device()); EXPECT_EQ(std::string(cricket::DeviceManagerInterface::kDefaultDeviceName), fme_->audio_out_device()); - EXPECT_EQ(cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS, - fme_->audio_options()); - EXPECT_EQ(cricket::MediaEngineInterface::kDefaultAudioDelayOffset, - fme_->audio_delay_offset()); - // Test setting defaults. - EXPECT_TRUE(cm_->SetAudioOptions("", "", - cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS)); - EXPECT_EQ("", fme_->audio_in_device()); - EXPECT_EQ("", fme_->audio_out_device()); - EXPECT_EQ(cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS, - fme_->audio_options()); EXPECT_EQ(cricket::MediaEngineInterface::kDefaultAudioDelayOffset, fme_->audio_delay_offset()); // Test setting specific values. - EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2)); + AudioOptions options; + options.auto_gain_control.Set(true); + EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", options)); EXPECT_EQ("audio-in1", fme_->audio_in_device()); EXPECT_EQ("audio-out1", fme_->audio_out_device()); - EXPECT_EQ(0x2, fme_->audio_options()); + bool auto_gain_control = false; + EXPECT_TRUE( + fme_->audio_options().auto_gain_control.Get(&auto_gain_control)); + EXPECT_TRUE(auto_gain_control); EXPECT_EQ(cricket::MediaEngineInterface::kDefaultAudioDelayOffset, fme_->audio_delay_offset()); // Test setting bad values. - EXPECT_FALSE(cm_->SetAudioOptions("audio-in9", "audio-out2", 0x1)); -} - -TEST_F(ChannelManagerTest, GetAudioOptions) { - std::string audio_in, audio_out; - int opts; - // Test initial state. - EXPECT_TRUE(cm_->Init()); - EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &opts)); - EXPECT_EQ(std::string(cricket::DeviceManagerInterface::kDefaultDeviceName), - audio_in); - EXPECT_EQ(std::string(cricket::DeviceManagerInterface::kDefaultDeviceName), - audio_out); - EXPECT_EQ(cricket::MediaEngineInterface::DEFAULT_AUDIO_OPTIONS, opts); - // Test that we get back specific values that we set. - EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2)); - EXPECT_TRUE(cm_->GetAudioOptions(&audio_in, &audio_out, &opts)); - EXPECT_EQ("audio-in1", audio_in); - EXPECT_EQ("audio-out1", audio_out); - EXPECT_EQ(0x2, opts); + EXPECT_FALSE(cm_->SetAudioOptions("audio-in9", "audio-out2", options)); } TEST_F(ChannelManagerTest, SetCaptureDeviceBeforeInit) { @@ -380,7 +355,8 @@ TEST_F(ChannelManagerTest, SetCaptureDevice) { // device is plugged back, we use it. TEST_F(ChannelManagerTest, SetAudioOptionsUnplugPlug) { // Set preferences "audio-in1" and "audio-out1" before init. - EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", 0x2)); + AudioOptions options; + EXPECT_TRUE(cm_->SetAudioOptions("audio-in1", "audio-out1", options)); // Unplug device "audio-in1" and "audio-out1". std::vector<std::string> in_device_list, out_device_list; in_device_list.push_back("audio-in2"); 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 1306f894043..84c76185545 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 @@ -118,7 +118,8 @@ TEST_F(CurrentSpeakerMonitorTest, MultipleActiveStreams) { EXPECT_EQ(num_changes_, 1); } -TEST_F(CurrentSpeakerMonitorTest, RapidSpeakerChange) { +// See: https://code.google.com/p/webrtc/issues/detail?id=2409 +TEST_F(CurrentSpeakerMonitorTest, DISABLED_RapidSpeakerChange) { AudioInfo info; InitAudioInfo(&info, 0, 0); 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 85612308211..ba510b94150 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/mediasession.cc +++ b/chromium/third_party/libjingle/source/talk/session/media/mediasession.cc @@ -38,12 +38,17 @@ #include "talk/base/stringutils.h" #include "talk/media/base/constants.h" #include "talk/media/base/cryptoparams.h" -#include "talk/media/sctp/sctpdataengine.h" #include "talk/p2p/base/constants.h" #include "talk/session/media/channelmanager.h" #include "talk/session/media/srtpfilter.h" #include "talk/xmpp/constants.h" +#ifdef HAVE_SCTP +#include "talk/media/sctp/sctpdataengine.h" +#else +static const uint32 kMaxSctpSid = 1023; +#endif + namespace { const char kInline[] = "inline:"; } @@ -237,13 +242,12 @@ static bool GenerateCname(const StreamParamsVec& params_vec, } // Generate random SSRC values that are not already present in |params_vec|. -// Either 2 or 1 ssrcs will be generated based on |include_rtx_stream| being -// true or false. The generated values are added to |ssrcs|. +// The generated values are added to |ssrcs|. +// |num_ssrcs| is the number of the SSRC will be generated. static void GenerateSsrcs(const StreamParamsVec& params_vec, - bool include_rtx_stream, + int num_ssrcs, std::vector<uint32>* ssrcs) { - unsigned int num_ssrcs = include_rtx_stream ? 2 : 1; - for (unsigned int i = 0; i < num_ssrcs; i++) { + for (int i = 0; i < num_ssrcs; i++) { uint32 candidate; do { candidate = talk_base::CreateRandomNonZeroId(); @@ -423,7 +427,8 @@ static bool AddStreamParams( if (IsSctp(content_description)) { GenerateSctpSids(*current_streams, &ssrcs); } else { - GenerateSsrcs(*current_streams, include_rtx_stream, &ssrcs); + int num_ssrcs = include_rtx_stream ? 2 : 1; + GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs); } if (include_rtx_stream) { content_description->AddLegacyStream(ssrcs[0], ssrcs[1]); @@ -457,13 +462,23 @@ static bool AddStreamParams( if (IsSctp(content_description)) { GenerateSctpSids(*current_streams, &ssrcs); } else { - GenerateSsrcs(*current_streams, include_rtx_stream, &ssrcs); + GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs); } StreamParams stream_param; stream_param.id = stream_it->id; - stream_param.ssrcs.push_back(ssrcs[0]); + // Add the generated ssrc. + for (size_t i = 0; i < ssrcs.size(); ++i) { + stream_param.ssrcs.push_back(ssrcs[i]); + } + if (stream_it->num_sim_layers > 1) { + SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs); + stream_param.ssrc_groups.push_back(group); + } + // Generate an extra ssrc for include_rtx_stream case. if (include_rtx_stream) { - stream_param.AddFidSsrc(ssrcs[0], ssrcs[1]); + std::vector<uint32> rtx_ssrc; + GenerateSsrcs(*current_streams, 1, &rtx_ssrc); + stream_param.AddFidSsrc(ssrcs[0], rtx_ssrc[0]); content_description->set_multistream(true); } stream_param.cname = cname; @@ -594,6 +609,7 @@ static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group, return false; } + bool common_cryptos_needed = false; // Get the common cryptos. const ContentNames& content_names = bundle_group.content_names(); CryptoParamsVec common_cryptos; @@ -602,6 +618,11 @@ static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group, if (!IsRtpContent(sdesc, *it)) { continue; } + // The common cryptos are needed if any of the content does not have DTLS + // enabled. + if (!sdesc->GetTransportInfoByName(*it)->description.secure()) { + common_cryptos_needed = true; + } if (it == content_names.begin()) { // Initial the common_cryptos with the first content in the bundle group. if (!GetCryptosByName(sdesc, *it, &common_cryptos)) { @@ -620,7 +641,7 @@ static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group, } } - if (common_cryptos.empty()) { + if (common_cryptos.empty() && common_cryptos_needed) { return false; } @@ -967,10 +988,61 @@ static void SetMediaProtocol(bool secure_transport, desc->set_protocol(kMediaProtocolAvpf); } +// Gets the TransportInfo of the given |content_name| from the +// |current_description|. If doesn't exist, returns a new one. +static const TransportDescription* GetTransportDescription( + const std::string& content_name, + const SessionDescription* current_description) { + const TransportDescription* desc = NULL; + if (current_description) { + const TransportInfo* info = + current_description->GetTransportInfoByName(content_name); + if (info) { + desc = &info->description; + } + } + return desc; +} + +// Gets the current DTLS state from the transport description. +static bool IsDtlsActive( + const std::string& content_name, + const SessionDescription* current_description) { + if (!current_description) + return false; + + const ContentInfo* content = + current_description->GetContentByName(content_name); + if (!content) + return false; + + const TransportDescription* current_tdesc = + GetTransportDescription(content_name, current_description); + if (!current_tdesc) + return false; + + return current_tdesc->secure(); +} + void MediaSessionOptions::AddStream(MediaType type, const std::string& id, const std::string& sync_label) { - streams.push_back(Stream(type, id, sync_label)); + AddStreamInternal(type, id, sync_label, 1); +} + +void MediaSessionOptions::AddVideoStream( + const std::string& id, + const std::string& sync_label, + int num_sim_layers) { + AddStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers); +} + +void MediaSessionOptions::AddStreamInternal( + MediaType type, + const std::string& id, + const std::string& sync_label, + int num_sim_layers) { + streams.push_back(Stream(type, id, sync_label, num_sim_layers)); if (type == MEDIA_TYPE_VIDEO) has_video = true; @@ -1042,13 +1114,17 @@ SessionDescription* MediaSessionDescriptionFactory::CreateOffer( // Handle m=audio. if (options.has_audio) { + cricket::SecurePolicy sdes_policy = + IsDtlsActive(CN_AUDIO, current_description) ? + cricket::SEC_DISABLED : secure(); + scoped_ptr<AudioContentDescription> audio(new AudioContentDescription()); std::vector<std::string> crypto_suites; GetSupportedAudioCryptoSuites(&crypto_suites); if (!CreateMediaContentOffer( options, audio_codecs, - secure(), + sdes_policy, GetCryptos(GetFirstAudioContentDescription(current_description)), crypto_suites, audio_rtp_extensions, @@ -1069,13 +1145,17 @@ SessionDescription* MediaSessionDescriptionFactory::CreateOffer( // Handle m=video. if (options.has_video) { + cricket::SecurePolicy sdes_policy = + IsDtlsActive(CN_VIDEO, current_description) ? + cricket::SEC_DISABLED : secure(); + scoped_ptr<VideoContentDescription> video(new VideoContentDescription()); std::vector<std::string> crypto_suites; GetSupportedVideoCryptoSuites(&crypto_suites); if (!CreateMediaContentOffer( options, video_codecs, - secure(), + sdes_policy, GetCryptos(GetFirstVideoContentDescription(current_description)), crypto_suites, video_rtp_extensions, @@ -1099,8 +1179,10 @@ SessionDescription* MediaSessionDescriptionFactory::CreateOffer( scoped_ptr<DataContentDescription> data(new DataContentDescription()); bool is_sctp = (options.data_channel_type == DCT_SCTP); + cricket::SecurePolicy sdes_policy = + IsDtlsActive(CN_DATA, current_description) ? + cricket::SEC_DISABLED : secure(); std::vector<std::string> crypto_suites; - cricket::SecurePolicy sdes_policy = secure(); if (is_sctp) { // SDES doesn't make sense for SCTP, so we disable it, and we only // get SDES crypto suites for RTP-based data channels. @@ -1360,22 +1442,6 @@ SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( return answer.release(); } -// Gets the TransportInfo of the given |content_name| from the -// |current_description|. If doesn't exist, returns a new one. -static const TransportDescription* GetTransportDescription( - const std::string& content_name, - const SessionDescription* current_description) { - const TransportDescription* desc = NULL; - if (current_description) { - const TransportInfo* info = - current_description->GetTransportInfoByName(content_name); - if (info) { - desc = &info->description; - } - } - return desc; -} - void MediaSessionDescriptionFactory::GetCodecsToOffer( const SessionDescription* current_description, AudioCodecs* audio_codecs, 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 5dfc765e7d2..ff25f5a040e 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/mediasession.h +++ b/chromium/third_party/libjingle/source/talk/session/media/mediasession.h @@ -105,8 +105,18 @@ struct MediaSessionOptions { void AddStream(MediaType type, const std::string& id, const std::string& sync_label); + void AddVideoStream(const std::string& id, + const std::string& sync_label, + int num_sim_layers); void RemoveStream(MediaType type, const std::string& id); + + // Helper function. + void AddStreamInternal(MediaType type, + const std::string& id, + const std::string& sync_label, + int num_sim_layers); + bool has_audio; bool has_video; DataChannelType data_channel_type; @@ -122,12 +132,15 @@ struct MediaSessionOptions { struct Stream { Stream(MediaType type, const std::string& id, - const std::string& sync_label) - : type(type), id(id), sync_label(sync_label) { + const std::string& sync_label, + int num_sim_layers) + : type(type), id(id), sync_label(sync_label), + num_sim_layers(num_sim_layers) { } MediaType type; std::string id; std::string sync_label; + int num_sim_layers; }; typedef std::vector<Stream> Streams; 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 f2e576ca92c..0e645667162 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 @@ -536,8 +536,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, ASSERT_CRYPTO(dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), dcd->protocol()); } -// Create a typical data offer, and ensure it matches what we expect. -TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataOffer) { +// Create a RTP data offer, and ensure it matches what we expect. +TEST_F(MediaSessionDescriptionFactoryTest, TestCreateRtpDataOffer) { MediaSessionOptions opts; opts.data_channel_type = cricket::DCT_RTP; f1_.set_secure(SEC_ENABLED); @@ -571,6 +571,18 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataOffer) { EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), dcd->protocol()); } +// Create an SCTP data offer with bundle without error. +TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSctpDataOffer) { + MediaSessionOptions opts; + opts.has_audio = false; + opts.bundle_enabled = true; + opts.data_channel_type = cricket::DCT_SCTP; + f1_.set_secure(SEC_ENABLED); + talk_base::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); + EXPECT_TRUE(offer.get() != NULL); + EXPECT_TRUE(offer->GetContentByName("data") != NULL); +} + // Create an audio, video offer without legacy StreamParams. TEST_F(MediaSessionDescriptionFactoryTest, TestCreateOfferWithoutLegacyStreams) { @@ -870,8 +882,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerRtcpMux) { answer_opts.data_channel_type = cricket::DCT_RTP; offer_opts.data_channel_type = cricket::DCT_RTP; - talk_base::scoped_ptr<SessionDescription> offer(NULL); - talk_base::scoped_ptr<SessionDescription> answer(NULL); + talk_base::scoped_ptr<SessionDescription> offer; + talk_base::scoped_ptr<SessionDescription> answer; offer_opts.rtcp_mux_enabled = true; answer_opts.rtcp_mux_enabled = true; @@ -1150,6 +1162,28 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) { EXPECT_EQ(updated_data_streams[0].cname, updated_data_streams[1].cname); } +// Create an offer with simulcast video stream. +TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSimulcastVideoOffer) { + MediaSessionOptions opts; + const int num_sim_layers = 3; + opts.AddVideoStream(kVideoTrack1, kMediaStream1, num_sim_layers); + talk_base::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); + + ASSERT_TRUE(offer.get() != NULL); + const ContentInfo* vc = offer->GetContentByName("video"); + ASSERT_TRUE(vc != NULL); + const VideoContentDescription* vcd = + static_cast<const VideoContentDescription*>(vc->description); + + const StreamParamsVec& video_streams = vcd->streams(); + ASSERT_EQ(1U, video_streams.size()); + EXPECT_EQ(kVideoTrack1, video_streams[0].id); + const SsrcGroup* sim_ssrc_group = + video_streams[0].get_ssrc_group(cricket::kSimSsrcGroupSemantics); + ASSERT_TRUE(sim_ssrc_group != NULL); + EXPECT_EQ(static_cast<size_t>(num_sim_layers), sim_ssrc_group->ssrcs.size()); +} + // Create an audio and video answer to a standard video offer with: // - one video track // - two audio tracks @@ -1458,7 +1492,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, GetFirstVideoContentDescription(updated_answer.get()); ASSERT_EQ("H264", updated_vcd->codecs()[0].name); - ASSERT_EQ(cricket::kRtxCodecName, updated_vcd->codecs()[1].name); + ASSERT_EQ(std::string(cricket::kRtxCodecName), updated_vcd->codecs()[1].name); int new_h264_pl_type = updated_vcd->codecs()[0].id; EXPECT_NE(used_pl_type, new_h264_pl_type); VideoCodec rtx = updated_vcd->codecs()[1]; @@ -1802,6 +1836,26 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoDtls) { ASSERT_TRUE(video_trans_desc != NULL); ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() != NULL); ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() != NULL); + + // Try creating offer again. DTLS enabled now, crypto's should be empty + // in new offer. + offer.reset(f1_.CreateOffer(options, offer.get())); + ASSERT_TRUE(offer.get() != NULL); + audio_media_desc = static_cast<const cricket::MediaContentDescription*>( + offer->GetContentDescriptionByName("audio")); + ASSERT_TRUE(audio_media_desc != NULL); + video_media_desc = static_cast<const cricket::MediaContentDescription*>( + offer->GetContentDescriptionByName("video")); + ASSERT_TRUE(video_media_desc != NULL); + EXPECT_TRUE(audio_media_desc->cryptos().empty()); + EXPECT_TRUE(video_media_desc->cryptos().empty()); + + audio_trans_desc = offer->GetTransportDescriptionByName("audio"); + ASSERT_TRUE(audio_trans_desc != NULL); + video_trans_desc = offer->GetTransportDescriptionByName("video"); + ASSERT_TRUE(video_trans_desc != NULL); + ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() != NULL); + ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() != NULL); } // Test that an answer can't be created if cryptos are required but the offer is diff --git a/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient.h b/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient.h index 1ade753f955..d0034cafeb2 100644 --- a/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient.h +++ b/chromium/third_party/libjingle/source/talk/session/media/mediasessionclient.h @@ -112,8 +112,8 @@ class MediaSessionClient : public SessionClient, public sigslot::has_slots<> { } bool SetAudioOptions(const std::string& in_name, const std::string& out_name, - int opts) { - return channel_manager_->SetAudioOptions(in_name, out_name, opts); + const AudioOptions& options) { + return channel_manager_->SetAudioOptions(in_name, out_name, options); } bool SetOutputVolume(int level) { return channel_manager_->SetOutputVolume(level); 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 92e9e0ea5d0..ee88797c1a1 100644 --- a/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc +++ b/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc @@ -340,7 +340,9 @@ void PseudoTcpChannel::OnChannelWritableState(TransportChannel* channel) { } void PseudoTcpChannel::OnChannelRead(TransportChannel* channel, - const char* data, size_t size, int flags) { + const char* data, size_t size, + const talk_base::PacketTime& packet_time, + int flags) { //LOG_F(LS_VERBOSE) << "(" << size << ")"; ASSERT(worker_thread_->IsCurrent()); CritScope lock(&cs_); diff --git a/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h b/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h index a540699a5c7..31cd9a18b64 100644 --- a/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h +++ b/chromium/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h @@ -111,7 +111,7 @@ class PseudoTcpChannel // Worker thread methods void OnChannelWritableState(TransportChannel* channel); void OnChannelRead(TransportChannel* channel, const char* data, size_t size, - int flags); + const talk_base::PacketTime& packet_time, int flags); void OnChannelConnectionChanged(TransportChannel* channel, const Candidate& candidate); diff --git a/chromium/third_party/libjingle/source/talk/sound/alsasoundsystem.cc b/chromium/third_party/libjingle/source/talk/sound/alsasoundsystem.cc index de9e2d67fca..7a8857cdf11 100644 --- a/chromium/third_party/libjingle/source/talk/sound/alsasoundsystem.cc +++ b/chromium/third_party/libjingle/source/talk/sound/alsasoundsystem.cc @@ -342,7 +342,7 @@ class AlsaInputStream : } AlsaStream stream_; - talk_base::scoped_array<char> buffer_; + talk_base::scoped_ptr<char[]> buffer_; size_t buffer_size_; DISALLOW_COPY_AND_ASSIGN(AlsaInputStream); diff --git a/chromium/third_party/libjingle/source/talk/xmllite/xmlbuilder.cc b/chromium/third_party/libjingle/source/talk/xmllite/xmlbuilder.cc index 486b6d54eae..f71e542d678 100644 --- a/chromium/third_party/libjingle/source/talk/xmllite/xmlbuilder.cc +++ b/chromium/third_party/libjingle/source/talk/xmllite/xmlbuilder.cc @@ -37,7 +37,7 @@ namespace buzz { XmlBuilder::XmlBuilder() : pelCurrent_(NULL), - pelRoot_(NULL), + pelRoot_(), pvParents_(new std::vector<XmlElement *>()) { } diff --git a/chromium/third_party/libjingle/source/talk/xmllite/xmlelement_unittest.cc b/chromium/third_party/libjingle/source/talk/xmllite/xmlelement_unittest.cc index 6d488fa75e4..3c31ce491ce 100644 --- a/chromium/third_party/libjingle/source/talk/xmllite/xmlelement_unittest.cc +++ b/chromium/third_party/libjingle/source/talk/xmllite/xmlelement_unittest.cc @@ -235,6 +235,10 @@ class XmlElementCreatorThread : public talk_base::Thread { XmlElementCreatorThread(int count, buzz::QName qname) : count_(count), qname_(qname) {} + virtual ~XmlElementCreatorThread() { + Stop(); + } + virtual void Run() { std::vector<buzz::XmlElement*> elems; for (int i = 0; i < count_; i++) { diff --git a/chromium/third_party/libjingle/source/talk/xmpp/mucroomdiscoverytask.cc b/chromium/third_party/libjingle/source/talk/xmpp/mucroomdiscoverytask.cc index a5055d2dab0..c7477ae3ee6 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/mucroomdiscoverytask.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/mucroomdiscoverytask.cc @@ -56,11 +56,11 @@ void MucRoomDiscoveryTask::HandleResult(const XmlElement* stanza) { const std::string name(identity->Attr(QN_NAME)); // Get the conversation id - const XmlElement* convIdElement = + const XmlElement* conversation = identity->FirstNamed(QN_GOOGLE_MUC_HANGOUT_CONVERSATION_ID); std::string conversation_id; - if (convIdElement != NULL) { - conversation_id = convIdElement->BodyText(); + if (conversation != NULL) { + conversation_id = conversation->BodyText(); } for (const XmlElement* feature = query->FirstNamed(QN_DISCO_FEATURE); diff --git a/chromium/third_party/libjingle/source/talk/xmpp/xmppclient.cc b/chromium/third_party/libjingle/source/talk/xmpp/xmppclient.cc index 9c49a9cae9d..8927dad4e4b 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/xmppclient.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/xmppclient.cc @@ -46,8 +46,8 @@ public: explicit Private(XmppClient* client) : client_(client), - socket_(NULL), - engine_(NULL), + socket_(), + engine_(), proxy_port_(0), pre_engine_error_(XmppEngine::ERROR_NONE), pre_engine_subcode_(0), diff --git a/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc b/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc index 8bcea029a30..d4c9c7d5f67 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/xmppengineimpl.cc @@ -58,12 +58,12 @@ XmppEngineImpl::XmppEngineImpl() encrypted_(false), error_code_(ERROR_NONE), subcode_(0), - stream_error_(NULL), + stream_error_(), raised_reset_(false), output_handler_(NULL), session_handler_(NULL), iq_entries_(new IqEntryVector()), - sasl_handler_(NULL), + sasl_handler_(), output_(new std::stringstream()) { for (int i = 0; i < HL_COUNT; i+= 1) { stanza_handlers_[i].reset(new StanzaHandlerVector()); diff --git a/chromium/third_party/libjingle/source/talk/xmpp/xmpplogintask.cc b/chromium/third_party/libjingle/source/talk/xmpp/xmpplogintask.cc index eec943bec6d..b3a2047cfe9 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/xmpplogintask.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/xmpplogintask.cc @@ -66,11 +66,11 @@ XmppLoginTask::XmppLoginTask(XmppEngineImpl * pctx) : pelStanza_(NULL), isStart_(false), iqId_(STR_EMPTY), - pelFeatures_(NULL), + pelFeatures_(), fullJid_(STR_EMPTY), streamId_(STR_EMPTY), pvecQueuedStanzas_(new std::vector<XmlElement *>()), - sasl_mech_(NULL) { + sasl_mech_() { } XmppLoginTask::~XmppLoginTask() { diff --git a/chromium/third_party/libjingle/source/talk/xmpp/xmppthread.cc b/chromium/third_party/libjingle/source/talk/xmpp/xmppthread.cc index 43dded866c3..716aaf83639 100644 --- a/chromium/third_party/libjingle/source/talk/xmpp/xmppthread.cc +++ b/chromium/third_party/libjingle/source/talk/xmpp/xmppthread.cc @@ -50,6 +50,7 @@ XmppThread::XmppThread() { } XmppThread::~XmppThread() { + Stop(); delete pump_; } |