diff options
author | Kristian Monsen <kristianm@google.com> | 2011-06-09 11:47:42 +0100 |
---|---|---|
committer | Kristian Monsen <kristianm@google.com> | 2011-06-29 14:33:03 +0100 |
commit | dc0f95d653279beabeb9817299e2902918ba123e (patch) | |
tree | 32eb121cd532053a5b9cb0c390331349af8d6baa /third_party | |
parent | ba160cd4054d13d0cb0b1b46e61c3bed67095811 (diff) | |
download | external_chromium-dc0f95d653279beabeb9817299e2902918ba123e.zip external_chromium-dc0f95d653279beabeb9817299e2902918ba123e.tar.gz external_chromium-dc0f95d653279beabeb9817299e2902918ba123e.tar.bz2 |
Merge Chromium at r11.0.696.0: Initial merge by git
Change-Id: I273dde2843af0839dfc08b419bb443fbd449532d
Diffstat (limited to 'third_party')
92 files changed, 3026 insertions, 2126 deletions
diff --git a/third_party/libjingle/libjingle.gyp b/third_party/libjingle/libjingle.gyp index 16b9d11..18b235f 100644 --- a/third_party/libjingle/libjingle.gyp +++ b/third_party/libjingle/libjingle.gyp @@ -1,4 +1,4 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# 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. @@ -155,7 +155,6 @@ 'source/talk/base/asyncfile.h', 'source/talk/base/asynchttprequest.cc', 'source/talk/base/asynchttprequest.h', - 'source/talk/base/asyncpacketsocket.cc', 'source/talk/base/asyncpacketsocket.h', 'source/talk/base/asyncsocket.cc', 'source/talk/base/asyncsocket.h', @@ -168,6 +167,8 @@ 'source/talk/base/base64.cc', 'source/talk/base/base64.h', 'source/talk/base/basicdefs.h', + 'source/talk/base/basicpacketsocketfactory.cc', + 'source/talk/base/basicpacketsocketfactory.h', 'source/talk/base/bytebuffer.cc', 'source/talk/base/bytebuffer.h', 'source/talk/base/byteorder.h', @@ -183,7 +184,6 @@ 'source/talk/base/event.h', 'source/talk/base/fileutils.cc', 'source/talk/base/fileutils.h', - 'source/talk/base/fileutils_mock.h', 'source/talk/base/firewallsocketserver.cc', 'source/talk/base/firewallsocketserver.h', 'source/talk/base/flags.cc', @@ -201,7 +201,6 @@ 'source/talk/base/httpcommon.h', 'source/talk/base/httprequest.cc', 'source/talk/base/httprequest.h', - 'source/talk/base/icftypes.h', 'source/talk/base/linked_ptr.h', 'source/talk/base/logging.cc', 'source/talk/base/md5.h', @@ -222,6 +221,8 @@ 'source/talk/base/proxydetect.h', 'source/talk/base/proxyinfo.cc', 'source/talk/base/proxyinfo.h', + 'source/talk/base/ratetracker.cc', + 'source/talk/base/ratetracker.h', 'source/talk/base/sec_buffer.h', 'source/talk/base/signalthread.cc', 'source/talk/base/signalthread.h', diff --git a/third_party/libjingle/overrides/talk/base/basictypes.h b/third_party/libjingle/overrides/talk/base/basictypes.h index d6f90cf..4ea193e 100644 --- a/third_party/libjingle/overrides/talk/base/basictypes.h +++ b/third_party/libjingle/overrides/talk/base/basictypes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -50,4 +50,14 @@ template<class T> inline T _max(T a, T b) { return (a < b) ? b : a; } const int kForever = -1; } +#ifdef WIN32 +#define alignof(t) __alignof(t) +#else // !WIN32 +#define alignof(t) __alignof__(t) +#endif // !WIN32 +#define IS_ALIGNED(p, a) (0==(reinterpret_cast<uintptr_t>(p) & ((a)-1))) +#define ALIGNP(p, t) \ + (reinterpret_cast<uint8*>(((reinterpret_cast<uintptr_t>(p) + \ + ((t)-1)) & ~((t)-1)))) + #endif // OVERRIDES_TALK_BASE_BASICTYPES_H__ diff --git a/third_party/libjingle/source/CHANGELOG b/third_party/libjingle/source/CHANGELOG index d1574d8..e0c746e 100644 --- a/third_party/libjingle/source/CHANGELOG +++ b/third_party/libjingle/source/CHANGELOG @@ -1,5 +1,13 @@ Libjingle +0.5.2 - Jan 11, 2010 + - Fixed build on Windows 7 with VS 2010 + - Fixed build on Windows x64 + - Fixed build on Mac OSX + - Added option to examples/call to enable encryption + - Improved logging + - Bug fixes + 0.5.1 - Nov 2, 2010 - Added support for call encryption. - Added addtional XEP-166 and XEP-167 features: diff --git a/third_party/libjingle/source/README b/third_party/libjingle/source/README index b127b56..f70b726 100644 --- a/third_party/libjingle/source/README +++ b/third_party/libjingle/source/README @@ -30,6 +30,8 @@ illustrate the basic concepts of how the provided classes work. Libjingle is built with swtoolkit (http://code.google.com/p/swtoolkit/), which is a set of extensions to the open-source SCons build tool (www.scons.org). * First, install Python 2.4 or later from http://www.python.org/. + Please note that since swtoolkit only works with Python 2.x, you will + not be able to use Python 3.x. * Second, install the stand alone scons-local package 2.0.0 or later from http://www.scons.org/download.php and set an environment variable, diff --git a/third_party/libjingle/source/talk/base/asyncpacketsocket.cc b/third_party/libjingle/source/talk/base/asyncpacketsocket.cc deleted file mode 100644 index 7628c12..0000000 --- a/third_party/libjingle/source/talk/base/asyncpacketsocket.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif - -#include "talk/base/asyncpacketsocket.h" - -namespace talk_base { - -AsyncPacketSocket::AsyncPacketSocket(AsyncSocket* socket) : socket_(socket) { -} - -AsyncPacketSocket::~AsyncPacketSocket() { - delete socket_; -} - -SocketAddress AsyncPacketSocket::GetLocalAddress() const { - return socket_->GetLocalAddress(); -} - -SocketAddress AsyncPacketSocket::GetRemoteAddress() const { - return socket_->GetRemoteAddress(); -} - -int AsyncPacketSocket::Bind(const SocketAddress& addr) { - return socket_->Bind(addr); -} - -int AsyncPacketSocket::Connect(const SocketAddress& addr) { - return socket_->Connect(addr); -} - -int AsyncPacketSocket::Send(const void *pv, size_t cb) { - return socket_->Send(pv, cb); -} - -int AsyncPacketSocket::SendTo( - const void *pv, size_t cb, const SocketAddress& addr) { - return socket_->SendTo(pv, cb, addr); -} - -int AsyncPacketSocket::Close() { - return socket_->Close(); -} - -Socket::ConnState AsyncPacketSocket::GetState() const { - return socket_->GetState(); -} - -int AsyncPacketSocket::GetOption(Socket::Option opt, int* value) { - return socket_->GetOption(opt, value); -} - -int AsyncPacketSocket::SetOption(Socket::Option opt, int value) { - return socket_->SetOption(opt, value); -} - -int AsyncPacketSocket::GetError() const { - return socket_->GetError(); -} - -void AsyncPacketSocket::SetError(int error) { - return socket_->SetError(error); -} - -} // namespace talk_base diff --git a/third_party/libjingle/source/talk/base/asyncpacketsocket.h b/third_party/libjingle/source/talk/base/asyncpacketsocket.h index 9cb82ec..47a1bf3 100644 --- a/third_party/libjingle/source/talk/base/asyncpacketsocket.h +++ b/third_party/libjingle/source/talk/base/asyncpacketsocket.h @@ -28,38 +28,61 @@ #ifndef TALK_BASE_ASYNCPACKETSOCKET_H_ #define TALK_BASE_ASYNCPACKETSOCKET_H_ -#include "talk/base/asyncsocket.h" +#include "talk/base/sigslot.h" +#include "talk/base/socket.h" namespace talk_base { -// Provides the ability to receive packets asynchronously. Sends are not +// 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<> { public: - explicit AsyncPacketSocket(AsyncSocket* socket); - virtual ~AsyncPacketSocket(); - - // Relevant socket methods: - virtual SocketAddress GetLocalAddress() const; - virtual SocketAddress GetRemoteAddress() const; - virtual int Bind(const SocketAddress& addr); - virtual int Connect(const SocketAddress& addr); - virtual int Send(const void *pv, size_t cb); - virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); - virtual int Close(); - - virtual Socket::ConnState GetState() const; - virtual int GetOption(Socket::Option opt, int* value); - virtual int SetOption(Socket::Option opt, int value); - virtual int GetError() const; - virtual void SetError(int error); - - // Emitted each time a packet is read. - sigslot::signal4<const char*, size_t, - const SocketAddress&, AsyncPacketSocket*> SignalReadPacket; - - protected: - AsyncSocket* socket_; + AsyncPacketSocket() { } + virtual ~AsyncPacketSocket() { } + + // Returns current local address. If port or IP address is not + // assigned yet, then they set to 0 in the result and |allocated| is + // set to false. Otherwise |allocated| is set to true. + virtual SocketAddress GetLocalAddress(bool* allocated) const = 0; + + // Returns remote address. Returns zeroes if this is not a client TCP socket. + virtual SocketAddress GetRemoteAddress() const = 0; + + // Send a packet. + virtual int Send(const void *pv, size_t cb) = 0; + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) = 0; + + // Close the socket. + virtual int Close() = 0; + + // Returns current state of the socket. + virtual Socket::ConnState GetState() const = 0; + + // Get/set options. + virtual int GetOption(Socket::Option opt, int* value) = 0; + virtual int SetOption(Socket::Option opt, int value) = 0; + + // Get/Set current error. + // TODO: Do we really need SetError() here? + virtual int GetError() const = 0; + virtual void SetError(int error) = 0; + + // Emitted after address for the socket is allocated. + sigslot::signal2<AsyncPacketSocket*, const SocketAddress&> SignalAddressReady; + + // 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; + + // Used only for connected TCP sockets. + sigslot::signal1<AsyncPacketSocket*> SignalConnect; + sigslot::signal2<AsyncPacketSocket*, int> SignalClose; + + // Used only for listening TCP sockets. + sigslot::signal2<AsyncPacketSocket*, AsyncPacketSocket*> SignalNewConnection; + + private: DISALLOW_EVIL_CONSTRUCTORS(AsyncPacketSocket); }; diff --git a/third_party/libjingle/source/talk/base/asynctcpsocket.cc b/third_party/libjingle/source/talk/base/asynctcpsocket.cc index 37c7a6c..03fc01a 100644 --- a/third_party/libjingle/source/talk/base/asynctcpsocket.cc +++ b/third_party/libjingle/source/talk/base/asynctcpsocket.cc @@ -39,20 +39,30 @@ namespace talk_base { -const size_t MAX_PACKET_SIZE = 64 * 1024; +static const size_t MAX_PACKET_SIZE = 64 * 1024; typedef uint16 PacketLength; -const size_t PKT_LEN_SIZE = sizeof(PacketLength); +static const size_t PKT_LEN_SIZE = sizeof(PacketLength); -const size_t BUF_SIZE = MAX_PACKET_SIZE + PKT_LEN_SIZE; +static const size_t BUF_SIZE = MAX_PACKET_SIZE + PKT_LEN_SIZE; -AsyncTCPSocket* AsyncTCPSocket::Create(SocketFactory* factory) { +static const int LISTEN_BACKLOG = 5; + +AsyncTCPSocket* AsyncTCPSocket::Create(SocketFactory* factory, bool listen) { AsyncSocket* sock = factory->CreateAsyncSocket(SOCK_STREAM); - return (sock) ? new AsyncTCPSocket(sock) : NULL; + // This will still return a socket even if we failed to listen on + // it. It is neccessary because even if we can't accept new + // connections on this socket, the corresponding port is still + // useful for outgoing connections. + // + // TODO: It might be better to pass listen() error to the + // upper layer and let it handle the problem. + return (sock) ? new AsyncTCPSocket(sock, listen) : NULL; } -AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket) - : AsyncPacketSocket(socket), +AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket, bool listen) + : socket_(socket), + listen_(listen), insize_(BUF_SIZE), inpos_(0), outsize_(BUF_SIZE), @@ -60,11 +70,17 @@ AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket) inbuf_ = new char[insize_]; outbuf_ = new char[outsize_]; - ASSERT(socket_ != NULL); + ASSERT(socket_.get() != NULL); socket_->SignalConnectEvent.connect(this, &AsyncTCPSocket::OnConnectEvent); socket_->SignalReadEvent.connect(this, &AsyncTCPSocket::OnReadEvent); socket_->SignalWriteEvent.connect(this, &AsyncTCPSocket::OnWriteEvent); socket_->SignalCloseEvent.connect(this, &AsyncTCPSocket::OnCloseEvent); + + if (listen_) { + if (socket_->Listen(LISTEN_BACKLOG) < 0) { + LOG(LS_ERROR) << "Listen() failed with error " << socket_->GetError(); + } + } } AsyncTCPSocket::~AsyncTCPSocket() { @@ -72,6 +88,16 @@ AsyncTCPSocket::~AsyncTCPSocket() { delete [] outbuf_; } +SocketAddress AsyncTCPSocket::GetLocalAddress(bool* allocated) const { + if (allocated) + *allocated = true; + return socket_->GetLocalAddress(); +} + +SocketAddress AsyncTCPSocket::GetRemoteAddress() const { + return socket_->GetRemoteAddress(); +} + int AsyncTCPSocket::Send(const void *pv, size_t cb) { if (cb > MAX_PACKET_SIZE) { socket_->SetError(EMSGSIZE); @@ -108,6 +134,30 @@ int AsyncTCPSocket::SendTo(const void *pv, size_t cb, return -1; } +int AsyncTCPSocket::Close() { + return socket_->Close(); +} + +Socket::ConnState AsyncTCPSocket::GetState() const { + return socket_->GetState(); +} + +int AsyncTCPSocket::GetOption(Socket::Option opt, int* value) { + return socket_->GetOption(opt, value); +} + +int AsyncTCPSocket::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int AsyncTCPSocket::GetError() const { + return socket_->GetError(); +} + +void AsyncTCPSocket::SetError(int error) { + return socket_->SetError(error); +} + int AsyncTCPSocket::SendRaw(const void * pv, size_t cb) { if (outpos_ + cb > outsize_) { socket_->SetError(EMSGSIZE); @@ -134,7 +184,7 @@ void AsyncTCPSocket::ProcessInput(char * data, size_t& len) { if (len < PKT_LEN_SIZE + pkt_len) return; - SignalReadPacket(data + PKT_LEN_SIZE, pkt_len, remote_addr, this); + SignalReadPacket(this, data + PKT_LEN_SIZE, pkt_len, remote_addr); len -= PKT_LEN_SIZE + pkt_len; if (len > 0) { @@ -165,30 +215,46 @@ void AsyncTCPSocket::OnConnectEvent(AsyncSocket* socket) { } void AsyncTCPSocket::OnReadEvent(AsyncSocket* socket) { - ASSERT(socket == socket_); + ASSERT(socket_.get() == socket); + + if (listen_) { + talk_base::SocketAddress address; + talk_base::AsyncSocket* new_socket = socket->Accept(&address); + if (!new_socket) { + // TODO: Do something better like forwarding the error + // to the user. + LOG(LS_ERROR) << "TCP accept failed with error " << socket_->GetError(); + return; + } + + SignalNewConnection(this, new AsyncTCPSocket(new_socket, false)); - int len = socket_->Recv(inbuf_ + inpos_, insize_ - inpos_); - if (len < 0) { - // TODO: Do something better like forwarding the error to the user. - if (!socket_->IsBlocking()) { - LOG_ERR(LS_ERROR) << "recvfrom"; + // Prime a read event in case data is waiting. + new_socket->SignalReadEvent(new_socket); + } else { + int len = socket_->Recv(inbuf_ + inpos_, insize_ - inpos_); + if (len < 0) { + // TODO: Do something better like forwarding the error to the user. + if (!socket_->IsBlocking()) { + LOG(LS_ERROR) << "Recv() returned error: " << socket_->GetError(); + } + return; } - return; - } - inpos_ += len; + inpos_ += len; - ProcessInput(inbuf_, inpos_); + ProcessInput(inbuf_, inpos_); - if (inpos_ >= insize_) { - LOG(INFO) << "input buffer overflow"; - ASSERT(false); - inpos_ = 0; + if (inpos_ >= insize_) { + LOG(LS_ERROR) << "input buffer overflow"; + ASSERT(false); + inpos_ = 0; + } } } void AsyncTCPSocket::OnWriteEvent(AsyncSocket* socket) { - ASSERT(socket == socket_); + ASSERT(socket_.get() == socket); if (outpos_ > 0) { Flush(); diff --git a/third_party/libjingle/source/talk/base/asynctcpsocket.h b/third_party/libjingle/source/talk/base/asynctcpsocket.h index 8f24861..06bbbf4 100644 --- a/third_party/libjingle/source/talk/base/asynctcpsocket.h +++ b/third_party/libjingle/source/talk/base/asynctcpsocket.h @@ -29,6 +29,7 @@ #define TALK_BASE_ASYNCTCPSOCKET_H_ #include "talk/base/asyncpacketsocket.h" +#include "talk/base/scoped_ptr.h" #include "talk/base/socketfactory.h" namespace talk_base { @@ -38,15 +39,21 @@ namespace talk_base { // buffer them in user space. class AsyncTCPSocket : public AsyncPacketSocket { public: - static AsyncTCPSocket* Create(SocketFactory* factory); - explicit AsyncTCPSocket(AsyncSocket* socket); + static AsyncTCPSocket* Create(SocketFactory* factory, bool listen); + explicit AsyncTCPSocket(AsyncSocket* socket, bool listen); virtual ~AsyncTCPSocket(); - virtual int Send(const void* pv, size_t cb); - virtual int SendTo(const void* pv, size_t cb, const SocketAddress& addr); + virtual SocketAddress GetLocalAddress(bool* allocated) const; + virtual SocketAddress GetRemoteAddress() const; + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + virtual int Close(); - sigslot::signal1<AsyncTCPSocket*> SignalConnect; - sigslot::signal2<AsyncTCPSocket*, int> SignalClose; + virtual Socket::ConnState GetState() const; + virtual int GetOption(Socket::Option opt, int* value); + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError() const; + virtual void SetError(int error); protected: int SendRaw(const void* pv, size_t cb); @@ -61,8 +68,12 @@ class AsyncTCPSocket : public AsyncPacketSocket { void OnWriteEvent(AsyncSocket* socket); void OnCloseEvent(AsyncSocket* socket, int error); + scoped_ptr<AsyncSocket> socket_; + bool listen_; char* inbuf_, * outbuf_; size_t insize_, inpos_, outsize_, outpos_; + + DISALLOW_EVIL_CONSTRUCTORS(AsyncTCPSocket); }; } // namespace talk_base diff --git a/third_party/libjingle/source/talk/base/asyncudpsocket.cc b/third_party/libjingle/source/talk/base/asyncudpsocket.cc index 2899606..dd2dc64 100644 --- a/third_party/libjingle/source/talk/base/asyncudpsocket.cc +++ b/third_party/libjingle/source/talk/base/asyncudpsocket.cc @@ -34,11 +34,23 @@ namespace talk_base { -const int BUF_SIZE = 64 * 1024; +static const int BUF_SIZE = 64 * 1024; + +AsyncUDPSocket* AsyncUDPSocket::Create(SocketFactory* factory, + const SocketAddress& address) { + scoped_ptr<AsyncSocket> socket(factory->CreateAsyncSocket(SOCK_DGRAM)); + if (!socket.get()) + return NULL; + if (socket->Bind(address)) { + LOG(LS_INFO) << "Failed to bind UDP socket " << socket->GetError(); + return NULL; + } + return new AsyncUDPSocket(socket.release()); +} AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket) - : AsyncPacketSocket(socket) { - ASSERT(socket_ != NULL); + : socket_(socket) { + ASSERT(socket_.get() != NULL); size_ = BUF_SIZE; buf_ = new char[size_]; @@ -50,8 +62,51 @@ AsyncUDPSocket::~AsyncUDPSocket() { delete [] buf_; } +SocketAddress AsyncUDPSocket::GetLocalAddress(bool* allocated) const { + if (allocated) + *allocated = true; + return socket_->GetLocalAddress(); +} + +SocketAddress AsyncUDPSocket::GetRemoteAddress() const { + return socket_->GetRemoteAddress(); +} + +int AsyncUDPSocket::Send(const void *pv, size_t cb) { + return socket_->Send(pv, cb); +} + +int AsyncUDPSocket::SendTo( + const void *pv, size_t cb, const SocketAddress& addr) { + return socket_->SendTo(pv, cb, addr); +} + +int AsyncUDPSocket::Close() { + return socket_->Close(); +} + +Socket::ConnState AsyncUDPSocket::GetState() const { + return socket_->GetState(); +} + +int AsyncUDPSocket::GetOption(Socket::Option opt, int* value) { + return socket_->GetOption(opt, value); +} + +int AsyncUDPSocket::SetOption(Socket::Option opt, int value) { + return socket_->SetOption(opt, value); +} + +int AsyncUDPSocket::GetError() const { + return socket_->GetError(); +} + +void AsyncUDPSocket::SetError(int error) { + return socket_->SetError(error); +} + void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) { - ASSERT(socket == socket_); + ASSERT(socket_.get() == socket); SocketAddress remote_addr; int len = socket_->RecvFrom(buf_, size_, &remote_addr); @@ -68,7 +123,7 @@ 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(buf_, (size_t)len, remote_addr, this); + SignalReadPacket(this, buf_, (size_t)len, remote_addr); } } // namespace talk_base diff --git a/third_party/libjingle/source/talk/base/asyncudpsocket.h b/third_party/libjingle/source/talk/base/asyncudpsocket.h index 3a6960c..e3012a5 100644 --- a/third_party/libjingle/source/talk/base/asyncudpsocket.h +++ b/third_party/libjingle/source/talk/base/asyncudpsocket.h @@ -29,6 +29,7 @@ #define TALK_BASE_ASYNCUDPSOCKET_H_ #include "talk/base/asyncpacketsocket.h" +#include "talk/base/scoped_ptr.h" #include "talk/base/socketfactory.h" namespace talk_base { @@ -39,26 +40,32 @@ class AsyncUDPSocket : public AsyncPacketSocket { public: // Creates a new socket for sending asynchronous UDP packets using an // asynchronous socket from the given factory. - static AsyncUDPSocket* Create(SocketFactory* factory) { - AsyncSocket* sock = factory->CreateAsyncSocket(SOCK_DGRAM); - return (sock) ? new AsyncUDPSocket(sock) : NULL; - } + static AsyncUDPSocket* Create(SocketFactory* factory, + const SocketAddress& address); explicit AsyncUDPSocket(AsyncSocket* socket); virtual ~AsyncUDPSocket(); + virtual SocketAddress GetLocalAddress(bool* allocated) const; + virtual SocketAddress GetRemoteAddress() const; + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr); + virtual int Close(); + + virtual Socket::ConnState GetState() const; + virtual int GetOption(Socket::Option opt, int* value); + virtual int SetOption(Socket::Option opt, int value); + virtual int GetError() const; + virtual void SetError(int error); + private: // Called when the underlying socket is ready to be read from. void OnReadEvent(AsyncSocket* socket); + scoped_ptr<AsyncSocket> socket_; char* buf_; size_t size_; }; -// TODO: This is now deprecated. Remove it. -inline AsyncUDPSocket* CreateAsyncUDPSocket(SocketFactory* factory) { - return AsyncUDPSocket::Create(factory); -} - } // namespace talk_base #endif // TALK_BASE_ASYNCUDPSOCKET_H_ diff --git a/third_party/libjingle/source/talk/base/basictypes.h b/third_party/libjingle/source/talk/base/basictypes.h index 781d428..a26a3ff 100644 --- a/third_party/libjingle/source/talk/base/basictypes.h +++ b/third_party/libjingle/source/talk/base/basictypes.h @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2005, Google Inc. + * Copyright 2011, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -88,6 +88,12 @@ namespace talk_base { const int kForever = -1; } +// Detect compiler is for x86 or x64. +#if defined(__x86_64__) || defined(_M_X64) || \ + defined(__i386__) || defined(_M_IX86) +#define CPU_X86 1 +#endif + #ifdef WIN32 #define alignof(t) __alignof(t) #else // !WIN32 diff --git a/third_party/libjingle/source/talk/base/linux.cc b/third_party/libjingle/source/talk/base/linux.cc index 03d486b..5ae103e 100644 --- a/third_party/libjingle/source/talk/base/linux.cc +++ b/third_party/libjingle/source/talk/base/linux.cc @@ -1,6 +1,29 @@ -// Copyright 2008 Google Inc. All Rights Reserved. - -// +/* + * libjingle + * Copyright 2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #ifdef LINUX #include "talk/base/linux.h" @@ -26,25 +49,50 @@ ProcCpuInfo::~ProcCpuInfo() { bool ProcCpuInfo::LoadFromSystem() { ConfigParser procfs; - if (!procfs.Open(kCpuInfoFile)) + if (!procfs.Open(kCpuInfoFile)) { return false; + } return procfs.Parse(&cpu_info_); }; bool ProcCpuInfo::GetNumCpus(int *num) { - if (cpu_info_.size() == 0) + if (cpu_info_.size() == 0) { return false; + } *num = cpu_info_.size(); return true; } +bool ProcCpuInfo::GetNumPhysicalCpus(int *num) { + if (cpu_info_.size() == 0) { + return false; + } + int total_cores = 0; + int physical_id_prev = -1; + int cpus = static_cast<int>(cpu_info_.size()); + for (int i = 0; i < cpus; ++i) { + int physical_id; + if (GetCpuIntValue(i, "physical id", &physical_id)) { + if (physical_id != physical_id_prev) { + physical_id_prev = physical_id; + int cores; + if (GetCpuIntValue(i, "cpu cores", &cores)) { + total_cores += cores; + } + } + } + } + return total_cores; +} + bool ProcCpuInfo::GetCpuStringValue(int cpu_id, const std::string& key, std::string *result) { if (cpu_id >= static_cast<int>(cpu_info_.size())) return false; ConfigParser::SimpleMap::iterator iter = cpu_info_[cpu_id].find(key); - if (iter == cpu_info_[cpu_id].end()) + if (iter == cpu_info_[cpu_id].end()) { return false; + } *result = iter->second; return true; } @@ -68,8 +116,9 @@ ConfigParser::~ConfigParser() {} bool ConfigParser::Open(const std::string& filename) { FileStream *fs = new FileStream(); - if (!fs->Open(filename, "r")) + if (!fs->Open(filename, "r")) { return false; + } instream_.reset(fs); return true; } @@ -102,20 +151,24 @@ bool ConfigParser::ParseLine(std::string *key, std::string *value) { // Parses the next line in the filestream and places the found key-value // pair into key and val. std::string line; - if ((instream_->ReadLine(&line)) == EOF) + if ((instream_->ReadLine(&line)) == EOF) { return false; + } std::vector<std::string> tokens; - if (2 != split(line, ':', &tokens)) + if (2 != split(line, ':', &tokens)) { return false; + } // Removes whitespace at the end of Key name size_t pos = tokens[0].length() - 1; - while ((pos > 0) && isspace(tokens[0][pos])) + while ((pos > 0) && isspace(tokens[0][pos])) { pos--; + } tokens[0].erase(pos + 1); // Removes whitespace at the start of value pos = 0; - while (pos < tokens[1].length() && isspace(tokens[1][pos])) + while (pos < tokens[1].length() && isspace(tokens[1][pos])) { pos++; + } tokens[1].erase(0, pos); *key = tokens[0]; *value = tokens[1]; diff --git a/third_party/libjingle/source/talk/base/linux.h b/third_party/libjingle/source/talk/base/linux.h index 37b845b..e56eb95 100644 --- a/third_party/libjingle/source/talk/base/linux.h +++ b/third_party/libjingle/source/talk/base/linux.h @@ -1,5 +1,29 @@ -// Copyright 2008 Google Inc. All Rights Reserved. - +/* + * libjingle + * Copyright 2011, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #ifndef TALK_BASE_LINUX_H_ #define TALK_BASE_LINUX_H_ @@ -68,9 +92,12 @@ class ProcCpuInfo { // returns false; if it succeeds, it returns true. virtual bool LoadFromSystem(); - // Obtains the number of CPUs and places the value num. + // Obtains the number of logical CPU threads and places the value num. virtual bool GetNumCpus(int *num); + // Obtains the number of physical CPU cores and places the value num. + virtual bool GetNumPhysicalCpus(int *num); + // Looks for the CPU proc item with the given name for the given CPU number // and places the string value in result. virtual bool GetCpuStringValue(int cpu_id, const std::string& key, diff --git a/third_party/libjingle/source/talk/base/logging.cc b/third_party/libjingle/source/talk/base/logging.cc index d632a2c..3515312 100644 --- a/third_party/libjingle/source/talk/base/logging.cc +++ b/third_party/libjingle/source/talk/base/logging.cc @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2005, Google Inc. + * Copyright 2011, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,6 +37,10 @@ #elif defined(ANDROID) #include <android/log.h> static const char kLibjingle[] = "libjingle"; +// Android has a 1024 limit on log inputs. We use 60 chars as an +// approx for the header/tag portion. +// See android/system/core/liblog/logd_write.c +static const int kMaxLogLineSize = 1024 - 60; #endif // OSX || ANDROID #include <iostream> @@ -270,7 +274,7 @@ void LogMessage::ConfigureLogging(const char* params, const char* filename) { int file_level = GetLogToStream(); std::vector<std::string> tokens; - split(params, ' ', &tokens); + tokenize(params, ' ', &tokens); for (size_t i = 0; i < tokens.size(); ++i) { if (tokens[i].empty()) @@ -429,6 +433,10 @@ void LogMessage::OutputToDebug(const std::string& str, switch (severity) { case LS_SENSITIVE: __android_log_write(ANDROID_LOG_INFO, kLibjingle, "SENSITIVE"); + if (log_to_stderr) { + std::cerr << "SENSITIVE"; + std::cerr.flush(); + } return; case LS_VERBOSE: prio = ANDROID_LOG_VERBOSE; @@ -445,8 +453,26 @@ void LogMessage::OutputToDebug(const std::string& str, default: prio = ANDROID_LOG_UNKNOWN; } - // Use the size of the string in the format (str may have \0 in the middle). - __android_log_print(prio, kLibjingle, "%.*s", str.size(), str.c_str()); + + int size = str.size(); + int line = 0; + int idx = 0; + const int max_lines = size / kMaxLogLineSize + 1; + if (max_lines == 1) { + __android_log_print(prio, kLibjingle, "%.*s", size, str.c_str()); + } else { + while (size > 0) { + const int len = std::min(size, kMaxLogLineSize); + // Use the size of the string in the format (str may have \0 in the + // middle). + __android_log_print(prio, kLibjingle, "[%d/%d] %.*s", + line + 1, max_lines, + len, str.c_str() + idx); + idx += len; + size -= len; + ++line; + } + } #endif // ANDROID if (log_to_stderr) { std::cerr << str; diff --git a/third_party/libjingle/source/talk/base/nethelpers.cc b/third_party/libjingle/source/talk/base/nethelpers.cc index 5740dc3..b877d78 100644 --- a/third_party/libjingle/source/talk/base/nethelpers.cc +++ b/third_party/libjingle/source/talk/base/nethelpers.cc @@ -32,6 +32,11 @@ namespace talk_base { +#if defined(LINUX) || defined(ANDROID) +static const size_t kInitHostentLen = 1024; +static const size_t kMaxHostentLen = kInitHostentLen * 8; +#endif + // AsyncResolver AsyncResolver::AsyncResolver() : result_(NULL), error_(0) { @@ -55,17 +60,16 @@ void AsyncResolver::OnWorkDone() { // The functions below are used to do gethostbyname, but with an allocated // instead of a static buffer. hostent* SafeGetHostByName(const char* hostname, int* herrno) { + if (NULL == hostname || NULL == herrno) { + return NULL; + } hostent* result = NULL; -#if defined(WIN32) || (defined(POSIX) && !defined(OSX)) +#if defined(WIN32) // On Windows we have to allocate a buffer, and manually copy the hostent, // along with its embedded pointers. hostent* ent = gethostbyname(hostname); if (!ent) { -#ifdef WIN32 *herrno = WSAGetLastError(); -#else // POSIX - *herrno = h_errno; -#endif return NULL; } @@ -85,6 +89,9 @@ hostent* SafeGetHostByName(const char* hostname, int* herrno) { total_len += sizeof(char*); result = static_cast<hostent*>(malloc(total_len)); + if (NULL == result) { + return NULL; + } char* p = reinterpret_cast<char*>(result) + sizeof(hostent); // Copy the hostent into it, along with its embedded pointers. @@ -114,7 +121,37 @@ hostent* SafeGetHostByName(const char* hostname, int* herrno) { result->h_addr_list[num_addrs] = NULL; *herrno = 0; -#elif defined(OSX) +#elif defined(LINUX) || defined(ANDROID) + // gethostbyname() is not thread safe, so we need to call gethostbyname_r() + // which is a reentrant version of gethostbyname(). + ASSERT(kInitHostentLen > sizeof(hostent)); + size_t size = kInitHostentLen; + int ret; + void* buf = malloc(size); + if (NULL == buf) { + return NULL; + } + char* aux = static_cast<char*>(buf) + sizeof(hostent); + size_t aux_len = size - sizeof(hostent); + while ((ret = gethostbyname_r(hostname, reinterpret_cast<hostent*>(buf), aux, + aux_len, &result, herrno)) == ERANGE) { + size *= 2; + if (size > kMaxHostentLen) { + break; // Just to be safe. + } + buf = realloc(buf, size); + if (NULL == buf) { + return NULL; + } + aux = static_cast<char*>(buf) + sizeof(hostent); + aux_len = size - sizeof(hostent); + } + if (ret != 0 || buf != result) { + free(buf); + return NULL; + } + *herrno = 0; +#elif defined(OSX) || defined(IOS) // Mac OS returns an object with everything allocated. result = getipnodebyname(hostname, AF_INET, AI_DEFAULT, herrno); #else @@ -126,10 +163,10 @@ hostent* SafeGetHostByName(const char* hostname, int* herrno) { // This function should mirror the above function, and free any resources // allocated by the above. void FreeHostEnt(hostent* host) { -#if defined(WIN32) || (defined(POSIX) && !defined(OSX)) - free(host); -#elif defined(OSX) +#if defined(OSX) || defined(IOS) freehostent(host); +#elif defined(WIN32) || defined(POSIX) + free(host); #else #error "I don't know how to free a hostent on your system." #endif diff --git a/third_party/libjingle/source/talk/base/physicalsocketserver.cc b/third_party/libjingle/source/talk/base/physicalsocketserver.cc index d97cba7..9cb5f4f 100644 --- a/third_party/libjingle/source/talk/base/physicalsocketserver.cc +++ b/third_party/libjingle/source/talk/base/physicalsocketserver.cc @@ -78,12 +78,6 @@ typedef char* SockOptArg; namespace talk_base { -const int kfRead = 0x0001; -const int kfWrite = 0x0002; -const int kfConnect = 0x0004; -const int kfClose = 0x0008; -const int kfAccept = 0x0010; - // Standard MTUs, from RFC 1191 const uint16 PACKET_MAXIMUMS[] = { 65535, // Theoretical maximum, Hyperchannel @@ -125,7 +119,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { EnsureWinsockInit(); #endif if (s_ != INVALID_SOCKET) { - enabled_events_ = kfRead | kfWrite; + enabled_events_ = DE_READ | DE_WRITE; int type = SOCK_STREAM; socklen_t len = sizeof(type); @@ -145,7 +139,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { udp_ = (SOCK_DGRAM == type); UpdateLastError(); if (udp_) - enabled_events_ = kfRead | kfWrite; + enabled_events_ = DE_READ | DE_WRITE; return s_ != INVALID_SOCKET; } @@ -221,17 +215,16 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { addr.ToSockAddr(&saddr); int err = ::connect(s_, (sockaddr*)&saddr, sizeof(saddr)); UpdateLastError(); - //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Connect(" << addr2.ToString() << ") Ret: " << err << " Error: " << error_; if (err == 0) { state_ = CS_CONNECTED; } else if (IsBlockingError(error_)) { state_ = CS_CONNECTING; - enabled_events_ |= kfConnect; + enabled_events_ |= DE_CONNECT; } else { return SOCKET_ERROR; } - enabled_events_ |= kfRead | kfWrite; + enabled_events_ |= DE_READ | DE_WRITE; return 0; } @@ -289,10 +282,10 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { #endif ); UpdateLastError(); - //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Send(" << cb << ") Ret: " << sent << " Error: " << error_; - ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false + // We have seen minidumps where this may be false. + ASSERT(sent <= static_cast<int>(cb)); if ((sent < 0) && IsBlockingError(error_)) { - enabled_events_ |= kfWrite; + enabled_events_ |= DE_WRITE; } return sent; } @@ -310,11 +303,11 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { #endif (sockaddr*)&saddr, sizeof(saddr)); UpdateLastError(); - ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false + // We have seen minidumps where this may be false. + ASSERT(sent <= static_cast<int>(cb)); if ((sent < 0) && IsBlockingError(error_)) { - enabled_events_ |= kfWrite; + enabled_events_ |= DE_WRITE; } - //LOG_F(LS_INFO) << cb << ":" << addr.ToString() << ":" << sent << ":" << error_; return sent; } @@ -327,14 +320,14 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { LOG(LS_WARNING) << "EOF from socket; deferring close event"; // Must turn this back on so that the select() loop will notice the close // event. - enabled_events_ |= kfRead; + enabled_events_ |= DE_READ; error_ = EWOULDBLOCK; return SOCKET_ERROR; } UpdateLastError(); bool success = (received >= 0) || IsBlockingError(error_); if (udp_ || success) { - enabled_events_ |= kfRead; + enabled_events_ |= DE_READ; } if (!success) { LOG_F(LS_VERBOSE) << "Error = " << error_; @@ -352,7 +345,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { paddr->FromSockAddr(saddr); bool success = (received >= 0) || IsBlockingError(error_); if (udp_ || success) { - enabled_events_ |= kfRead; + enabled_events_ |= DE_READ; } if (!success) { LOG_F(LS_VERBOSE) << "Error = " << error_; @@ -365,7 +358,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { UpdateLastError(); if (err == 0) { state_ = CS_CONNECTING; - enabled_events_ |= kfAccept; + enabled_events_ |= DE_ACCEPT; #ifdef _DEBUG dbg_addr_ = "Listening @ "; dbg_addr_.append(GetLocalAddress().ToString()); @@ -381,7 +374,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { UpdateLastError(); if (s == INVALID_SOCKET) return NULL; - enabled_events_ |= kfAccept; + enabled_events_ |= DE_ACCEPT; if (paddr != NULL) paddr->FromSockAddr(saddr); return ss_->WrapSocket(s); @@ -392,7 +385,6 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { return 0; int err = ::closesocket(s_); UpdateLastError(); - //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Close() Ret: " << err << " Error: " << error_; s_ = INVALID_SOCKET; state_ = CS_CLOSED; enabled_events_ = 0; @@ -432,14 +424,14 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { ASSERT(false); return -1; -#elif defined(OSX) +#elif defined(IOS) || defined(OSX) // No simple way to do this on Mac OS X. // 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; return -1; -#elif defined(LINUX) +#elif defined(LINUX) || defined(ANDROID) // Gets the path MTU. int value; socklen_t vlen = sizeof(value); @@ -487,7 +479,7 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { *slevel = IPPROTO_IP; *sopt = IP_DONTFRAGMENT; break; -#elif defined(OSX) || defined(BSD) +#elif defined(IOS) || defined(OSX) || defined(BSD) LOG(LS_WARNING) << "Socket::OPT_DONTFRAGMENT not supported."; return -1; #elif defined(POSIX) @@ -528,16 +520,6 @@ class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> { }; #ifdef POSIX -class Dispatcher { - public: - virtual ~Dispatcher() { } - virtual uint32 GetRequestedEvents() = 0; - virtual void OnPreEvent(uint32 ff) = 0; - virtual void OnEvent(uint32 ff, int err) = 0; - virtual int GetDescriptor() = 0; - virtual bool IsDescriptorClosed() = 0; -}; - class EventDispatcher : public Dispatcher { public: EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) { @@ -563,7 +545,7 @@ class EventDispatcher : public Dispatcher { } virtual uint32 GetRequestedEvents() { - return kfRead; + return DE_READ; } virtual void OnPreEvent(uint32 ff) { @@ -597,71 +579,43 @@ class EventDispatcher : public Dispatcher { CriticalSection crit_; }; -// This is a class customized to use the self-pipe trick to deliver POSIX -// signals. This is the only safe, reliable, cross-platform way to do -// non-trivial things with a POSIX signal (until proper pselect() -// implementations become ubiquitous). -class PosixSignalDeliveryDispatcher : public Dispatcher { +// These two classes use the self-pipe trick to deliver POSIX signals to our +// select loop. This is the only safe, reliable, cross-platform way to do +// non-trivial things with a POSIX signal in an event-driven program (until +// proper pselect() implementations become ubiquitous). + +class PosixSignalHandler { public: - virtual ~PosixSignalDeliveryDispatcher() { - close(afd_[0]); - close(afd_[1]); - } + // POSIX only specifies 32 signals, but in principle the system might have + // more and the programmer might choose to use them, so we size our array + // for 128. + static const int kNumPosixSignals = 128; - virtual uint32 GetRequestedEvents() { - return kfRead; - } + static PosixSignalHandler *Instance() { return &instance_; } - virtual void OnPreEvent(uint32 ff) { - // Events might get grouped if signals come very fast, so we read out up to - // 16 bytes to make sure we keep the pipe empty. - uint8 b[16]; - ssize_t ret = read(afd_[0], b, sizeof(b)); - if (ret < 0) { - LOG_ERR(LS_WARNING) << "Error in read()"; - } else if (ret == 0) { - LOG(LS_WARNING) << "Should have read at least one byte"; + // Returns true if the given signal number is set. + bool IsSignalSet(int signum) const { + ASSERT(signum < ARRAY_SIZE(received_signal_)); + if (signum < ARRAY_SIZE(received_signal_)) { + return received_signal_[signum]; + } else { + return false; } } - virtual void OnEvent(uint32 ff, int err) { - for (int signum = 0; signum < ARRAY_SIZE(received_signal_); ++signum) { - if (received_signal_[signum]) { - received_signal_[signum] = false; - HandlerMap::iterator i = handlers_.find(signum); - if (i == handlers_.end()) { - // This can happen if a signal is delivered to our process at around - // the same time as we unset our handler for it. It is not an error - // condidion, but it's unusual enough to be worth logging. - LOG(LS_INFO) << "Received signal with no handler: " << signum; - } else { - // Otherwise, execute the handler. - (*i->second)(signum); - } - } + // Clears the given signal number. + void ClearSignal(int signum) { + ASSERT(signum < ARRAY_SIZE(received_signal_)); + if (signum < ARRAY_SIZE(received_signal_)) { + received_signal_[signum] = false; } } - virtual int GetDescriptor() { + // Returns the file descriptor to monitor for signal events. + int GetDescriptor() const { return afd_[0]; } - virtual bool IsDescriptorClosed() { - return false; - } - - void SetHandler(int signum, void (*handler)(int)) { - handlers_[signum] = handler; - } - - void ClearHandler(int signum) { - handlers_.erase(signum); - } - - bool HasHandlers() { - return !handlers_.empty(); - } - // This is called directly from our real signal handler, so it must be // signal-handler-safe. That means it cannot assume anything about the // user-level state of the process, since the handler could be executed at any @@ -673,7 +627,7 @@ class PosixSignalDeliveryDispatcher : public Dispatcher { } // Set a flag saying we've seen this signal. received_signal_[signum] = true; - // Tell the thread running our PhysicalSocketServer that we got a signal. + // Notify application code that we got a signal. const uint8 b[1] = { 0 }; if (-1 == write(afd_[1], b, sizeof(b))) { // Nothing we can do here. If there's an error somehow then there's @@ -685,49 +639,8 @@ class PosixSignalDeliveryDispatcher : public Dispatcher { } } - // Sets a PhysicalSocketServer to own signal delivery, or fails if already - // owned. - bool SetOwner(PhysicalSocketServer *owner) { - CritScope cs(&owner_critsec_); - if (owner == owner_) { - return true; - } else if (owner_) { - return false; - } else { - owner_ = owner; - owner_->Add(this); - return true; - } - } - - bool IsOwner(PhysicalSocketServer *ss) { - CritScope cs(&owner_critsec_); - return owner_ == ss; - } - - void ClearOwner(PhysicalSocketServer *ss) { - CritScope cs(&owner_critsec_); - if (owner_ != ss) { - return; - } - owner_->Remove(this); - owner_ = NULL; - } - - // There is just a single global instance. (Signal handlers do not get any - // sort of user-defined void * parameter, so they can't access anything that - // isn't global.) - static PosixSignalDeliveryDispatcher instance_; - private: - // POSIX only specifies 32 signals, but in principle the system might have - // more and the programmer might choose to use them, so we size our array - // for 128. - static const int kNumPosixSignals = 128; - - typedef std::map<int, void (*)(int)> HandlerMap; - - PosixSignalDeliveryDispatcher() : owner_(NULL) { + PosixSignalHandler() { if (pipe(afd_) < 0) { LOG_ERR(LS_ERROR) << "pipe failed"; return; @@ -743,8 +656,30 @@ class PosixSignalDeliveryDispatcher : public Dispatcher { sizeof(received_signal_)); } + ~PosixSignalHandler() { + int fd1 = afd_[0]; + int fd2 = afd_[1]; + // We clobber the stored file descriptor numbers here or else in principle + // a signal that happens to be delivered during application termination + // could erroneously write a zero byte to an unrelated file handle in + // OnPosixSignalReceived() if some other file happens to be opened later + // during shutdown and happens to be given the same file descriptor number + // as our pipe had. Unfortunately even with this precaution there is still a + // race where that could occur if said signal happens to be handled + // concurrently with this code and happens to have already read the value of + // afd_[1] from memory before we clobber it, but that's unlikely. + afd_[0] = -1; + afd_[1] = -1; + close(fd1); + close(fd2); + } + + // There is just a single global instance. (Signal handlers do not get any + // sort of user-defined void * parameter, so they can't access anything that + // isn't global.) + static PosixSignalHandler instance_; + int afd_[2]; - HandlerMap handlers_; // These are boolean flags that will be set in our signal handler and read // and cleared from Wait(). There is a race involved in this, but it is // benign. The signal handler sets the flag before signaling the pipe, so @@ -756,14 +691,83 @@ class PosixSignalDeliveryDispatcher : public Dispatcher { // Volatile is not necessary here for correctness, but this data _is_ volatile // so I've marked it as such. volatile uint8 received_signal_[kNumPosixSignals]; +}; + +PosixSignalHandler PosixSignalHandler::instance_; + +class PosixSignalDispatcher : public Dispatcher { + public: + PosixSignalDispatcher(PhysicalSocketServer *owner) : owner_(owner) { + owner_->Add(this); + } + + virtual ~PosixSignalDispatcher() { + owner_->Remove(this); + } + + virtual uint32 GetRequestedEvents() { + return DE_READ; + } + + virtual void OnPreEvent(uint32 ff) { + // Events might get grouped if signals come very fast, so we read out up to + // 16 bytes to make sure we keep the pipe empty. + uint8 b[16]; + ssize_t ret = read(GetDescriptor(), b, sizeof(b)); + if (ret < 0) { + LOG_ERR(LS_WARNING) << "Error in read()"; + } else if (ret == 0) { + LOG(LS_WARNING) << "Should have read at least one byte"; + } + } + + virtual void OnEvent(uint32 ff, int err) { + for (int signum = 0; signum < PosixSignalHandler::kNumPosixSignals; + ++signum) { + if (PosixSignalHandler::Instance()->IsSignalSet(signum)) { + PosixSignalHandler::Instance()->ClearSignal(signum); + HandlerMap::iterator i = handlers_.find(signum); + if (i == handlers_.end()) { + // This can happen if a signal is delivered to our process at around + // the same time as we unset our handler for it. It is not an error + // condition, but it's unusual enough to be worth logging. + LOG(LS_INFO) << "Received signal with no handler: " << signum; + } else { + // Otherwise, execute our handler. + (*i->second)(signum); + } + } + } + } + + virtual int GetDescriptor() { + return PosixSignalHandler::Instance()->GetDescriptor(); + } + + virtual bool IsDescriptorClosed() { + return false; + } + + void SetHandler(int signum, void (*handler)(int)) { + handlers_[signum] = handler; + } + + void ClearHandler(int signum) { + handlers_.erase(signum); + } + + bool HasHandlers() { + return !handlers_.empty(); + } + + private: + typedef std::map<int, void (*)(int)> HandlerMap; + + HandlerMap handlers_; // Our owner. PhysicalSocketServer *owner_; - // To synchronize ownership changes. - CriticalSection owner_critsec_; }; -PosixSignalDeliveryDispatcher PosixSignalDeliveryDispatcher::instance_; - class SocketDispatcher : public Dispatcher, public PhysicalSocket { public: explicit SocketDispatcher(PhysicalSocketServer *ss) : PhysicalSocket(ss) { @@ -833,30 +837,30 @@ class SocketDispatcher : public Dispatcher, public PhysicalSocket { } virtual void OnPreEvent(uint32 ff) { - if ((ff & kfConnect) != 0) + if ((ff & DE_CONNECT) != 0) state_ = CS_CONNECTED; - if ((ff & kfClose) != 0) + if ((ff & DE_CLOSE) != 0) state_ = CS_CLOSED; } virtual void OnEvent(uint32 ff, int err) { - if ((ff & kfRead) != 0) { - enabled_events_ &= ~kfRead; + if ((ff & DE_READ) != 0) { + enabled_events_ &= ~DE_READ; SignalReadEvent(this); } - if ((ff & kfWrite) != 0) { - enabled_events_ &= ~kfWrite; + if ((ff & DE_WRITE) != 0) { + enabled_events_ &= ~DE_WRITE; SignalWriteEvent(this); } - if ((ff & kfConnect) != 0) { - enabled_events_ &= ~kfConnect; + if ((ff & DE_CONNECT) != 0) { + enabled_events_ &= ~DE_CONNECT; SignalConnectEvent(this); } - if ((ff & kfAccept) != 0) { - enabled_events_ &= ~kfAccept; + if ((ff & DE_ACCEPT) != 0) { + enabled_events_ &= ~DE_ACCEPT; SignalReadEvent(this); } - if ((ff & kfClose) != 0) { + if ((ff & DE_CLOSE) != 0) { // The socket is now dead to us, so stop checking it. enabled_events_ = 0; SignalCloseEvent(this, err); @@ -904,28 +908,28 @@ class FileDispatcher: public Dispatcher, public AsyncFile { } virtual void OnEvent(uint32 ff, int err) { - if ((ff & kfRead) != 0) + if ((ff & DE_READ) != 0) SignalReadEvent(this); - if ((ff & kfWrite) != 0) + if ((ff & DE_WRITE) != 0) SignalWriteEvent(this); - if ((ff & kfClose) != 0) + if ((ff & DE_CLOSE) != 0) SignalCloseEvent(this, err); } virtual bool readable() { - return (flags_ & kfRead) != 0; + return (flags_ & DE_READ) != 0; } virtual void set_readable(bool value) { - flags_ = value ? (flags_ | kfRead) : (flags_ & ~kfRead); + flags_ = value ? (flags_ | DE_READ) : (flags_ & ~DE_READ); } virtual bool writable() { - return (flags_ & kfWrite) != 0; + return (flags_ & DE_WRITE) != 0; } virtual void set_writable(bool value) { - flags_ = value ? (flags_ | kfWrite) : (flags_ & ~kfWrite); + flags_ = value ? (flags_ | DE_WRITE) : (flags_ & ~DE_WRITE); } private: @@ -941,26 +945,15 @@ AsyncFile* PhysicalSocketServer::CreateFile(int fd) { #endif // POSIX #ifdef WIN32 -class Dispatcher { - public: - virtual ~Dispatcher() {} - virtual uint32 GetRequestedEvents() = 0; - virtual void OnPreEvent(uint32 ff) = 0; - virtual void OnEvent(uint32 ff, int err) = 0; - virtual WSAEVENT GetWSAEvent() = 0; - virtual SOCKET GetSocket() = 0; - virtual bool CheckSignalClose() = 0; -}; - static uint32 FlagsToEvents(uint32 events) { uint32 ffFD = FD_CLOSE; - if (events & kfRead) + if (events & DE_READ) ffFD |= FD_READ; - if (events & kfWrite) + if (events & DE_WRITE) ffFD |= FD_WRITE; - if (events & kfConnect) + if (events & DE_CONNECT) ffFD |= FD_CONNECT; - if (events & kfAccept) + if (events & DE_ACCEPT) ffFD |= FD_ACCEPT; return ffFD; } @@ -1020,9 +1013,16 @@ class SocketDispatcher : public Dispatcher, public PhysicalSocket { bool signal_close_; int signal_err_; - SocketDispatcher(PhysicalSocketServer* ss) : PhysicalSocket(ss), id_(0), signal_close_(false) { + SocketDispatcher(PhysicalSocketServer* ss) + : PhysicalSocket(ss), + id_(0), + signal_close_(false) { } - SocketDispatcher(SOCKET s, PhysicalSocketServer* ss) : PhysicalSocket(ss, s), id_(0), signal_close_(false) { + + SocketDispatcher(SOCKET s, PhysicalSocketServer* ss) + : PhysicalSocket(ss, s), + id_(0), + signal_close_(false) { } virtual ~SocketDispatcher() { @@ -1065,37 +1065,36 @@ class SocketDispatcher : public Dispatcher, public PhysicalSocket { } virtual void OnPreEvent(uint32 ff) { - if ((ff & kfConnect) != 0) + if ((ff & DE_CONNECT) != 0) state_ = CS_CONNECTED; // We set CS_CLOSED from CheckSignalClose. } virtual void OnEvent(uint32 ff, int err) { int cache_id = id_; - if ((ff & kfRead) != 0) { - enabled_events_ &= ~kfRead; + if ((ff & DE_READ) != 0) { + enabled_events_ &= ~DE_READ; SignalReadEvent(this); } - if (((ff & kfWrite) != 0) && (id_ == cache_id)) { - enabled_events_ &= ~kfWrite; + if (((ff & DE_WRITE) != 0) && (id_ == cache_id)) { + enabled_events_ &= ~DE_WRITE; SignalWriteEvent(this); } - if (((ff & kfConnect) != 0) && (id_ == cache_id)) { - if (ff != kfConnect) - LOG(LS_VERBOSE) << "Signalled with kfConnect: " << ff; - enabled_events_ &= ~kfConnect; + if (((ff & DE_CONNECT) != 0) && (id_ == cache_id)) { + if (ff != DE_CONNECT) + LOG(LS_VERBOSE) << "Signalled with DE_CONNECT: " << ff; + enabled_events_ &= ~DE_CONNECT; #ifdef _DEBUG dbg_addr_ = "Connected @ "; dbg_addr_.append(GetRemoteAddress().ToString()); #endif // _DEBUG SignalConnectEvent(this); } - if (((ff & kfAccept) != 0) && (id_ == cache_id)) { - enabled_events_ &= ~kfAccept; + if (((ff & DE_ACCEPT) != 0) && (id_ == cache_id)) { + enabled_events_ &= ~DE_ACCEPT; SignalReadEvent(this); } - if (((ff & kfClose) != 0) && (id_ == cache_id)) { - //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] OnClose() Error: " << err; + if (((ff & DE_CLOSE) != 0) && (id_ == cache_id)) { signal_close_ = true; signal_err_ = err; } @@ -1160,7 +1159,7 @@ PhysicalSocketServer::~PhysicalSocketServer() { WSACloseEvent(socket_ev_); #endif #ifdef POSIX - PosixSignalDeliveryDispatcher::instance_.ClearOwner(this); + signal_dispatcher_.reset(); #endif delete signal_wakeup_; ASSERT(dispatchers_.empty()); @@ -1275,9 +1274,9 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { fdmax = fd; uint32 ff = pdispatcher->GetRequestedEvents(); - if (ff & (kfRead | kfAccept)) + if (ff & (DE_READ | DE_ACCEPT)) FD_SET(fd, &fdsRead); - if (ff & (kfWrite | kfConnect)) + if (ff & (DE_WRITE | DE_CONNECT)) FD_SET(fd, &fdsWrite); } } @@ -1323,12 +1322,12 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { // TODO: Only peek at TCP descriptors. if (FD_ISSET(fd, &fdsRead)) { FD_CLR(fd, &fdsRead); - if (pdispatcher->GetRequestedEvents() & kfAccept) { - ff |= kfAccept; + if (pdispatcher->GetRequestedEvents() & DE_ACCEPT) { + ff |= DE_ACCEPT; } else if (errcode || pdispatcher->IsDescriptorClosed()) { - ff |= kfClose; + ff |= DE_CLOSE; } else { - ff |= kfRead; + ff |= DE_READ; } } @@ -1336,14 +1335,14 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { // success versus failure by the reaped error code. if (FD_ISSET(fd, &fdsWrite)) { FD_CLR(fd, &fdsWrite); - if (pdispatcher->GetRequestedEvents() & kfConnect) { + if (pdispatcher->GetRequestedEvents() & DE_CONNECT) { if (!errcode) { - ff |= kfConnect; + ff |= DE_CONNECT; } else { - ff |= kfClose; + ff |= DE_CLOSE; } } else { - ff |= kfWrite; + ff |= DE_WRITE; } } @@ -1381,7 +1380,7 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { } static void GlobalSignalHandler(int signum) { - PosixSignalDeliveryDispatcher::instance_.OnPosixSignalReceived(signum); + PosixSignalHandler::Instance()->OnPosixSignalReceived(signum); } bool PhysicalSocketServer::SetPosixSignalHandler(int signum, @@ -1392,19 +1391,17 @@ bool PhysicalSocketServer::SetPosixSignalHandler(int signum, if (!InstallSignal(signum, handler)) { return false; } - if (PosixSignalDeliveryDispatcher::instance_.IsOwner(this)) { - PosixSignalDeliveryDispatcher::instance_.ClearHandler(signum); - if (!PosixSignalDeliveryDispatcher::instance_.HasHandlers()) { - PosixSignalDeliveryDispatcher::instance_.ClearOwner(this); + if (signal_dispatcher_.get()) { + signal_dispatcher_->ClearHandler(signum); + if (!signal_dispatcher_->HasHandlers()) { + signal_dispatcher_.reset(); } } } else { - if (!PosixSignalDeliveryDispatcher::instance_.SetOwner(this)) { - LOG(LS_ERROR) << - "Cannot do POSIX signal delivery on more than one PhysicalSocketServer"; - return false; + if (!signal_dispatcher_.get()) { + signal_dispatcher_.reset(new PosixSignalDispatcher(this)); } - PosixSignalDeliveryDispatcher::instance_.SetHandler(signum, handler); + signal_dispatcher_->SetHandler(signum, handler); if (!InstallSignal(signum, &GlobalSignalHandler)) { return false; } @@ -1465,7 +1462,9 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { if (disp->CheckSignalClose()) { // We just signalled close, don't poll this socket } else if (s != INVALID_SOCKET) { - WSAEventSelect(s, events[0], FlagsToEvents(disp->GetRequestedEvents())); + WSAEventSelect(s, + events[0], + FlagsToEvents(disp->GetRequestedEvents())); } else { events.push_back(disp->GetWSAEvent()); event_owners.push_back(disp); @@ -1485,14 +1484,19 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { } // Wait for one of the events to signal - DWORD dw = WSAWaitForMultipleEvents(static_cast<DWORD>(events.size()), &events[0], false, cmsNext, false); + DWORD dw = WSAWaitForMultipleEvents(static_cast<DWORD>(events.size()), + &events[0], + false, + cmsNext, + false); #if 0 // LOGGING // we track this information purely for logging purposes. last_tick_dispatch_count_++; if (last_tick_dispatch_count_ >= 1000) { int32 elapsed = TimeSince(last_tick_tracked_); - LOG(INFO) << "PhysicalSocketServer took " << elapsed << "ms for 1000 events"; + LOG(INFO) << "PhysicalSocketServer took " << elapsed + << "ms for 1000 events"; // If we get more than 1000 events in a second, we are spinning badly // (normally it should take about 8-20 seconds). @@ -1536,41 +1540,51 @@ bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { #if LOGGING { - if ((wsaEvents.lNetworkEvents & FD_READ) && wsaEvents.iErrorCode[FD_READ_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error " << wsaEvents.iErrorCode[FD_READ_BIT]; + if ((wsaEvents.lNetworkEvents & FD_READ) && + wsaEvents.iErrorCode[FD_READ_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error " + << wsaEvents.iErrorCode[FD_READ_BIT]; } - if ((wsaEvents.lNetworkEvents & FD_WRITE) && wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " << wsaEvents.iErrorCode[FD_WRITE_BIT]; + if ((wsaEvents.lNetworkEvents & FD_WRITE) && + wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " + << wsaEvents.iErrorCode[FD_WRITE_BIT]; } - if ((wsaEvents.lNetworkEvents & FD_CONNECT) && wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error " << wsaEvents.iErrorCode[FD_CONNECT_BIT]; + if ((wsaEvents.lNetworkEvents & FD_CONNECT) && + wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error " + << wsaEvents.iErrorCode[FD_CONNECT_BIT]; } - if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " << wsaEvents.iErrorCode[FD_ACCEPT_BIT]; + if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && + wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " + << wsaEvents.iErrorCode[FD_ACCEPT_BIT]; } - if ((wsaEvents.lNetworkEvents & FD_CLOSE) && wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) { - LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " << wsaEvents.iErrorCode[FD_CLOSE_BIT]; + if ((wsaEvents.lNetworkEvents & FD_CLOSE) && + wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) { + LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " + << wsaEvents.iErrorCode[FD_CLOSE_BIT]; } } #endif uint32 ff = 0; int errcode = 0; if (wsaEvents.lNetworkEvents & FD_READ) - ff |= kfRead; + ff |= DE_READ; if (wsaEvents.lNetworkEvents & FD_WRITE) - ff |= kfWrite; + ff |= DE_WRITE; if (wsaEvents.lNetworkEvents & FD_CONNECT) { if (wsaEvents.iErrorCode[FD_CONNECT_BIT] == 0) { - ff |= kfConnect; + ff |= DE_CONNECT; } else { - ff |= kfClose; + ff |= DE_CLOSE; errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT]; } } if (wsaEvents.lNetworkEvents & FD_ACCEPT) - ff |= kfAccept; + ff |= DE_ACCEPT; if (wsaEvents.lNetworkEvents & FD_CLOSE) { - ff |= kfClose; + ff |= DE_CLOSE; errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT]; } if (ff != 0) { diff --git a/third_party/libjingle/source/talk/base/physicalsocketserver.h b/third_party/libjingle/source/talk/base/physicalsocketserver.h index 282e14c..aeb8348 100644 --- a/third_party/libjingle/source/talk/base/physicalsocketserver.h +++ b/third_party/libjingle/source/talk/base/physicalsocketserver.h @@ -31,6 +31,7 @@ #include <vector> #include "talk/base/asyncfile.h" +#include "talk/base/scoped_ptr.h" #include "talk/base/socketserver.h" #include "talk/base/criticalsection.h" @@ -38,13 +39,37 @@ typedef int SOCKET; #endif // POSIX -namespace talk_base { +namespace talk_base { + +// Event constants for the Dispatcher class. +enum DispatcherEvent { + DE_READ = 0x0001, + DE_WRITE = 0x0002, + DE_CONNECT = 0x0004, + DE_CLOSE = 0x0008, + DE_ACCEPT = 0x0010, +}; -class Dispatcher; class Signaler; #ifdef POSIX -class PosixSignalDeliveryDispatcher; +class PosixSignalDispatcher; +#endif + +class Dispatcher { + public: + virtual ~Dispatcher() {} + virtual uint32 GetRequestedEvents() = 0; + virtual void OnPreEvent(uint32 ff) = 0; + virtual void OnEvent(uint32 ff, int err) = 0; +#ifdef WIN32 + virtual WSAEVENT GetWSAEvent() = 0; + virtual SOCKET GetSocket() = 0; + virtual bool CheckSignalClose() = 0; +#elif POSIX + virtual int GetDescriptor() = 0; + virtual bool IsDescriptorClosed() = 0; #endif +}; // A socket server that provides the real sockets of the underlying OS. class PhysicalSocketServer : public SocketServer { @@ -75,9 +100,8 @@ public: // manipulate user-level data structures. // "handler" may be SIG_IGN, SIG_DFL, or a user-specified function, just like // with signal(2). - // Only one PhysicalSocketServer may have user-level signal handlers. - // Attempting to install a signal handler for a PhysicalSocketServer when - // another already owns some will fail. + // Only one PhysicalSocketServer should have user-level signal handlers. + // Dispatching signals on multiple PhysicalSocketServers is not reliable. // The signal mask is not modified. It is the caller's responsibily to // maintain it as desired. bool SetPosixSignalHandler(int signum, void (*handler)(int)); @@ -89,8 +113,9 @@ private: #ifdef POSIX static bool InstallSignal(int signum, void (*handler)(int)); -#endif + scoped_ptr<PosixSignalDispatcher> signal_dispatcher_; +#endif DispatcherList dispatchers_; IteratorList iterators_; Signaler* signal_wakeup_; diff --git a/third_party/libjingle/source/talk/base/socket.h b/third_party/libjingle/source/talk/base/socket.h index 3094918..a55b3dc 100644 --- a/third_party/libjingle/source/talk/base/socket.h +++ b/third_party/libjingle/source/talk/base/socket.h @@ -50,55 +50,89 @@ // Win32 compatibility. #ifdef WIN32 +#undef EWOULDBLOCK // Remove errno.h's definition for each macro below. #define EWOULDBLOCK WSAEWOULDBLOCK +#undef EINPROGRESS #define EINPROGRESS WSAEINPROGRESS +#undef EALREADY #define EALREADY WSAEALREADY +#undef ENOTSOCK #define ENOTSOCK WSAENOTSOCK +#undef EDESTADDRREQ #define EDESTADDRREQ WSAEDESTADDRREQ +#undef EMSGSIZE #define EMSGSIZE WSAEMSGSIZE +#undef EPROTOTYPE #define EPROTOTYPE WSAEPROTOTYPE +#undef ENOPROTOOPT #define ENOPROTOOPT WSAENOPROTOOPT +#undef EPROTONOSUPPORT #define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#undef ESOCKTNOSUPPORT #define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#undef EOPNOTSUPP #define EOPNOTSUPP WSAEOPNOTSUPP +#undef EPFNOSUPPORT #define EPFNOSUPPORT WSAEPFNOSUPPORT +#undef EAFNOSUPPORT #define EAFNOSUPPORT WSAEAFNOSUPPORT +#undef EADDRINUSE #define EADDRINUSE WSAEADDRINUSE +#undef EADDRNOTAVAIL #define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#undef ENETDOWN #define ENETDOWN WSAENETDOWN +#undef ENETUNREACH #define ENETUNREACH WSAENETUNREACH +#undef ENETRESET #define ENETRESET WSAENETRESET +#undef ECONNABORTED #define ECONNABORTED WSAECONNABORTED +#undef ECONNRESET #define ECONNRESET WSAECONNRESET +#undef ENOBUFS #define ENOBUFS WSAENOBUFS +#undef EISCONN #define EISCONN WSAEISCONN +#undef ENOTCONN #define ENOTCONN WSAENOTCONN +#undef ESHUTDOWN #define ESHUTDOWN WSAESHUTDOWN +#undef ETOOMANYREFS #define ETOOMANYREFS WSAETOOMANYREFS -#undef ETIMEDOUT // remove pthreads.h's definition +#undef ETIMEDOUT #define ETIMEDOUT WSAETIMEDOUT +#undef ECONNREFUSED #define ECONNREFUSED WSAECONNREFUSED +#undef ELOOP #define ELOOP WSAELOOP -#undef ENAMETOOLONG // remove errno.h's definition +#undef ENAMETOOLONG #define ENAMETOOLONG WSAENAMETOOLONG +#undef EHOSTDOWN #define EHOSTDOWN WSAEHOSTDOWN +#undef EHOSTUNREACH #define EHOSTUNREACH WSAEHOSTUNREACH -#undef ENOTEMPTY // remove errno.h's definition +#undef ENOTEMPTY #define ENOTEMPTY WSAENOTEMPTY +#undef EPROCLIM #define EPROCLIM WSAEPROCLIM +#undef EUSERS #define EUSERS WSAEUSERS +#undef EDQUOT #define EDQUOT WSAEDQUOT +#undef ESTALE #define ESTALE WSAESTALE +#undef EREMOTE #define EREMOTE WSAEREMOTE #undef EACCES #define SOCKET_EACCES WSAEACCES -#endif // WIN32 +#endif // WIN32 #ifdef POSIX #define INVALID_SOCKET (-1) #define SOCKET_ERROR (-1) #define closesocket(s) close(s) -#endif // POSIX +#endif // POSIX namespace talk_base { @@ -109,9 +143,9 @@ inline bool IsBlockingError(int e) { // General interface for the socket implementations of various networks. The // methods match those of normal UNIX sockets very closely. class Socket { -public: + public: virtual ~Socket() {} - + // Returns the address to which the socket is bound. If the socket is not // bound, then the any-address is returned. virtual SocketAddress GetLocalAddress() const = 0; @@ -146,20 +180,20 @@ public: enum Option { OPT_DONTFRAGMENT, - OPT_RCVBUF, // receive buffer size - OPT_SNDBUF, // send buffer size - OPT_NODELAY // whether Nagle algorithm is enabled + OPT_RCVBUF, // receive buffer size + OPT_SNDBUF, // send buffer size + OPT_NODELAY // whether Nagle algorithm is enabled }; virtual int GetOption(Option opt, int* value) = 0; virtual int SetOption(Option opt, int value) = 0; -protected: + protected: Socket() {} -private: + private: DISALLOW_EVIL_CONSTRUCTORS(Socket); }; -} // namespace talk_base +} // namespace talk_base -#endif // TALK_BASE_SOCKET_H__ +#endif // TALK_BASE_SOCKET_H__ diff --git a/third_party/libjingle/source/talk/base/socketpool.cc b/third_party/libjingle/source/talk/base/socketpool.cc index 6e89964..a5e3bcc 100644 --- a/third_party/libjingle/source/talk/base/socketpool.cc +++ b/third_party/libjingle/source/talk/base/socketpool.cc @@ -218,11 +218,25 @@ ReuseSocketPool::ReturnConnectedStream(StreamInterface* stream) { void ReuseSocketPool::OnStreamEvent(StreamInterface* stream, int events, int err) { - LOG_F(LS_VERBOSE) << "Connection closed with error: " << err; ASSERT(stream == stream_); ASSERT(!checked_out_); - ASSERT(0 != (events & SE_CLOSE)); - // Socket has closed. We'll reconnect it the next time it is used. + + // If the stream was written to and then immediately returned to us then + // we may get a writable notification for it, which we should ignore. + if (events == SE_WRITE) { + LOG_F(LS_VERBOSE) << "Pooled Socket unexpectedly writable: ignoring"; + return; + } + + // If the peer sent data, we can't process it, so drop the connection. + // If the socket has closed, clean it up. + // In either case, we'll reconnect it the next time it is used. + ASSERT(0 != (events & (SE_READ|SE_CLOSE))); + if (0 != (events & SE_CLOSE)) { + LOG_F(LS_VERBOSE) << "Connection closed with error: " << err; + } else { + LOG_F(LS_VERBOSE) << "Pooled Socket unexpectedly readable: closing"; + } stream_->Close(); } diff --git a/third_party/libjingle/source/talk/base/socketpool.h b/third_party/libjingle/source/talk/base/socketpool.h index 3bfd108..847d8ff 100644 --- a/third_party/libjingle/source/talk/base/socketpool.h +++ b/third_party/libjingle/source/talk/base/socketpool.h @@ -25,8 +25,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_BASE_SOCKETPOOL_H__ -#define TALK_BASE_SOCKETPOOL_H__ +#ifndef TALK_BASE_SOCKETPOOL_H_ +#define TALK_BASE_SOCKETPOOL_H_ #include <deque> #include <list> @@ -155,6 +155,6 @@ private: ////////////////////////////////////////////////////////////////////// -} // namespace talk_base +} // namespace talk_base -#endif // TALK_BASE_SOCKETPOOL_H__ +#endif // TALK_BASE_SOCKETPOOL_H_ diff --git a/third_party/libjingle/source/talk/base/stream.cc b/third_party/libjingle/source/talk/base/stream.cc index 45ba7ad..755fb08 100644 --- a/third_party/libjingle/source/talk/base/stream.cc +++ b/third_party/libjingle/source/talk/base/stream.cc @@ -1,6 +1,6 @@ /* * libjingle - * Copyright 2004--2010, Google Inc. + * Copyright 2011, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -551,7 +551,8 @@ void POpenStream::DoClose() { /////////////////////////////////////////////////////////////////////////////// MemoryStreamBase::MemoryStreamBase() - : buffer_(NULL), buffer_length_(0), data_length_(0), seek_position_(0) { + : buffer_(NULL), buffer_length_(0), data_length_(0), + seek_position_(0) { } StreamState MemoryStreamBase::GetState() const { @@ -646,25 +647,29 @@ StreamResult MemoryStreamBase::DoReserve(size_t size, int* error) { /////////////////////////////////////////////////////////////////////////////// -MemoryStream::MemoryStream() { +MemoryStream::MemoryStream() + : buffer_alloc_(NULL) { } -MemoryStream::MemoryStream(const char* data) { +MemoryStream::MemoryStream(const char* data) + : buffer_alloc_(NULL) { SetData(data, strlen(data)); } -MemoryStream::MemoryStream(const void* data, size_t length) { +MemoryStream::MemoryStream(const void* data, size_t length) + : buffer_alloc_(NULL) { SetData(data, length); } MemoryStream::~MemoryStream() { - delete [] buffer_; + delete [] buffer_alloc_; } void MemoryStream::SetData(const void* data, size_t length) { data_length_ = buffer_length_ = length; - delete [] buffer_; - buffer_ = new char[buffer_length_]; + delete [] buffer_alloc_; + buffer_alloc_ = new char[buffer_length_ + kAlignment]; + buffer_ = reinterpret_cast<char*>(ALIGNP(buffer_alloc_, kAlignment)); memcpy(buffer_, data, data_length_); seek_position_ = 0; } @@ -673,9 +678,12 @@ StreamResult MemoryStream::DoReserve(size_t size, int* error) { if (buffer_length_ >= size) return SR_SUCCESS; - if (char* new_buffer = new char[size]) { + if (char* new_buffer_alloc = new char[size + kAlignment]) { + char* new_buffer = reinterpret_cast<char*>( + ALIGNP(new_buffer_alloc, kAlignment)); memcpy(new_buffer, buffer_, data_length_); - delete [] buffer_; + delete [] buffer_alloc_; + buffer_alloc_ = new_buffer_alloc; buffer_ = new_buffer; buffer_length_ = size; return SR_SUCCESS; diff --git a/third_party/libjingle/source/talk/base/stream.h b/third_party/libjingle/source/talk/base/stream.h index ea91eac..a8d399d 100644 --- a/third_party/libjingle/source/talk/base/stream.h +++ b/third_party/libjingle/source/talk/base/stream.h @@ -530,6 +530,9 @@ class MemoryStream : public MemoryStreamBase { protected: virtual StreamResult DoReserve(size_t size, int* error); + // Memory Streams are aligned for efficiency. + static const int kAlignment = 16; + char* buffer_alloc_; }; // ExternalMemoryStream adapts an external memory buffer, so writes which would diff --git a/third_party/libjingle/source/talk/base/stringencode.cc b/third_party/libjingle/source/talk/base/stringencode.cc index d588514..7433bdd 100644 --- a/third_party/libjingle/source/talk/base/stringencode.cc +++ b/third_party/libjingle/source/talk/base/stringencode.cc @@ -1,27 +1,27 @@ /* * libjingle - * Copyright 2004--2005, Google Inc. + * Copyright 2011, 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. */ @@ -168,7 +168,7 @@ const unsigned char HTML_UNSAFE = 0x2; // "&'<> // ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 6 5 7 8 9 : ; < = > ? //@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ -//` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ +//` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ const unsigned char ASCII_CLASS[128] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, @@ -251,7 +251,7 @@ size_t utf8_decode(const char* source, size_t srclen, unsigned long* value) { return 0; } value16 = (value16 << 6) | (s[2] & 0x3F); - if ((s[0] & 0xF0) == 0xE0) { // Check s[0] == 1110xxxx + if ((s[0] & 0xF0) == 0xE0) { // Check s[0] == 1110xxxx *value = ((s[0] & 0x0F) << 12) | value16; return 3; } @@ -259,7 +259,7 @@ size_t utf8_decode(const char* source, size_t srclen, unsigned long* value) { return 0; } value16 = (value16 << 6) | (s[3] & 0x3F); - if ((s[0] & 0xF8) == 0xF0) { // Check s[0] == 11110xxx + if ((s[0] & 0xF8) == 0xF0) { // Check s[0] == 11110xxx *value = ((s[0] & 0x07) << 18) | value16; return 4; } @@ -511,36 +511,38 @@ std::string s_transform(const std::string& source, Transform t) { return result; } -size_t split(const std::string& source, char delimiter, - std::vector<std::string>* fields) -{ +size_t tokenize(const std::string& source, char delimiter, + std::vector<std::string>* fields) { ASSERT(NULL != fields); fields->clear(); size_t last = 0; - for (size_t i=0; i<source.length(); ++i) { + for (size_t i = 0; i < source.length(); ++i) { if (source[i] == delimiter) { - fields->push_back(source.substr(last, i - last)); - last = i+1; + if (i != last) { + fields->push_back(source.substr(last, i - last)); + } + last = i + 1; } } - fields->push_back(source.substr(last, source.length() - last)); + if (last != source.length()) { + fields->push_back(source.substr(last, source.length() - last)); + } return fields->size(); } -std::string split_one(const std::string& source, char delimiter, int* index) { - std::string substring; - size_t start = source.find_first_not_of(delimiter, *index); - size_t end = source.find_first_of(delimiter, start); - if (start != std::string::npos) { - if (end == std::string::npos) { - substring = source.substr(start); - *index = source.length(); - } else { - substring = source.substr(start, end - start); - *index = source.find_first_not_of(delimiter, end); +size_t split(const std::string& source, char delimiter, + std::vector<std::string>* fields) { + ASSERT(NULL != fields); + fields->clear(); + size_t last = 0; + for (size_t i = 0; i < source.length(); ++i) { + if (source[i] == delimiter) { + fields->push_back(source.substr(last, i - last)); + last = i + 1; } } - return substring; + fields->push_back(source.substr(last, source.length() - last)); + return fields->size(); } char make_char_safe_for_filename(char c) { diff --git a/third_party/libjingle/source/talk/base/stringencode.h b/third_party/libjingle/source/talk/base/stringencode.h index 143b220..fd29f87 100644 --- a/third_party/libjingle/source/talk/base/stringencode.h +++ b/third_party/libjingle/source/talk/base/stringencode.h @@ -1,27 +1,27 @@ /* * libjingle - * Copyright 2004--2005, Google Inc. + * Copyright 2011, 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. */ @@ -58,7 +58,7 @@ size_t escape(char * buffer, size_t buflen, // Note: in-place unescaping (buffer == source) is allowed. size_t unescape(char * buffer, size_t buflen, const char * source, size_t srclen, - char escape); + char escape); // Encoding replaces illegal characters with the escape character and 2 hex // chars, so it's a little less compact than escape, but completely removes @@ -125,14 +125,15 @@ inline std::string s_url_decode(const std::string& source) { return s_transform(source, url_decode); } -// Splits the source string into multiple fields separated by delimiter. +// Splits the source string into multiple fields separated by delimiter, +// with duplicates of delimiter creating empty fields. size_t split(const std::string& source, char delimiter, std::vector<std::string>* fields); -// Returns the first part of a string separated by delimiter. -// Index indicates the location to start parsing in the string and -// is increased to the start of the next substring. -std::string split_one(const std::string& source, char delimiter, int* index); +// Splits the source string into multiple fields separated by delimiter, +// with duplicates of delimiter ignored. Trailing delimiter ignored. +size_t tokenize(const std::string& source, char delimiter, + std::vector<std::string>* fields); // Safe sprintf to std::string //void sprintf(std::string& value, size_t maxlen, const char * format, ...) diff --git a/third_party/libjingle/source/talk/base/stringutils.cc b/third_party/libjingle/source/talk/base/stringutils.cc index 44dc7c8..8d8b7e0 100644 --- a/third_party/libjingle/source/talk/base/stringutils.cc +++ b/third_party/libjingle/source/talk/base/stringutils.cc @@ -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. */ @@ -33,7 +33,7 @@ namespace talk_base { bool memory_check(const void* memory, int c, size_t count) { const char* char_memory = static_cast<const char*>(memory); char char_c = static_cast<char>(c); - for (size_t i=0; i<count; ++i) { + for (size_t i = 0; i < count; ++i) { if (char_memory[i] != char_c) { return false; } @@ -88,7 +88,7 @@ size_t asccpyn(wchar_t* buffer, size_t buflen, const char* source, size_t srclen) { if (buflen <= 0) return 0; - + if (srclen == SIZE_UNKNOWN) { srclen = strlenn(source, buflen - 1); } else if (srclen >= buflen) { @@ -129,4 +129,17 @@ bool starts_with(const char *s1, const char *s2) { return true; } +static const std::string kWhitespace(" \n\r\t"); + +std::string string_trim(const std::string& s) { + std::string::size_type first = s.find_first_not_of(kWhitespace); + std::string::size_type last = s.find_last_not_of(kWhitespace); + + if (first == std::string::npos || last == std::string::npos) { + return std::string(""); + } + + return s.substr(first, last - first + 1); +} + } // namespace talk_base diff --git a/third_party/libjingle/source/talk/base/stringutils.h b/third_party/libjingle/source/talk/base/stringutils.h index 3e1771f..6aa9b18 100644 --- a/third_party/libjingle/source/talk/base/stringutils.h +++ b/third_party/libjingle/source/talk/base/stringutils.h @@ -332,6 +332,9 @@ void replace_substrs(const char *search, // True iff s1 starts with s2. bool starts_with(const char *s1, const char *s2); +// Remove leading and trailing whitespaces. +std::string string_trim(const std::string& s); + } // namespace talk_base #endif // TALK_BASE_STRINGUTILS_H__ diff --git a/third_party/libjingle/source/talk/examples/call/call_main.cc b/third_party/libjingle/source/talk/examples/call/call_main.cc index 541aca8..4312399 100644 --- a/third_party/libjingle/source/talk/examples/call/call_main.cc +++ b/third_party/libjingle/source/talk/examples/call/call_main.cc @@ -44,6 +44,7 @@ #include "talk/examples/call/callclient.h" #include "talk/examples/call/console.h" #include "talk/session/phone/filemediaengine.h" +#include "talk/session/phone/mediasessionclient.h" class DebugLog : public sigslot::has_slots<> { public: @@ -218,6 +219,9 @@ int main(int argc, char **argv) { DEFINE_string( protocol, "hybrid", "Initial signaling protocol to use: jingle, gingle, or hybrid."); + DEFINE_string( + secure, "disable", + "Disable or enable encryption: disable, enable, require."); DEFINE_bool(testserver, false, "Use test server"); DEFINE_bool(plainserver, false, "Turn off tls and allow plain password."); DEFINE_int(portallocator, 0, "Filter out unwanted connection types."); @@ -245,6 +249,7 @@ int main(int argc, char **argv) { int32 portallocator_flags = FLAG_portallocator; std::string pmuc_domain = FLAG_pmuc; std::string server = FLAG_s; + std::string secure = FLAG_secure; cricket::SignalingProtocol initial_protocol = cricket::PROTOCOL_HYBRID; if (protocol == "jingle") { @@ -254,7 +259,19 @@ int main(int argc, char **argv) { } else if (protocol == "hybrid") { initial_protocol = cricket::PROTOCOL_HYBRID; } else { - printf("Invalid protocol. Must be jingle, gingle, or hybrid."); + printf("Invalid protocol. Must be jingle, gingle, or hybrid.\n"); + return 1; + } + + cricket::SecureMediaPolicy secure_policy = cricket::SEC_DISABLED; + if (secure == "disable") { + secure_policy = cricket::SEC_DISABLED; + } else if (secure == "enable") { + secure_policy = cricket::SEC_ENABLED; + } else if (secure == "require") { + secure_policy = cricket::SEC_REQUIRED; + } else { + printf("Invalid encryption. Must be enable, disable, or require.\n"); return 1; } @@ -354,6 +371,7 @@ int main(int argc, char **argv) { client->SetPortAllocatorFlags(portallocator_flags); client->SetAllowLocalIps(true); client->SetInitialProtocol(initial_protocol); + client->SetSecurePolicy(secure_policy); console->Start(); if (debug) { diff --git a/third_party/libjingle/source/talk/examples/call/callclient.cc b/third_party/libjingle/source/talk/examples/call/callclient.cc index eb761d1..94581e7 100644 --- a/third_party/libjingle/source/talk/examples/call/callclient.cc +++ b/third_party/libjingle/source/talk/examples/call/callclient.cc @@ -29,19 +29,14 @@ #include <string> -#include "talk/xmpp/constants.h" +#include "talk/base/basicpacketsocketfactory.h" #include "talk/base/helpers.h" -#include "talk/base/thread.h" +#include "talk/base/logging.h" #include "talk/base/network.h" #include "talk/base/socketaddress.h" -#include "talk/base/stringutils.h" #include "talk/base/stringencode.h" -#include "talk/p2p/base/sessionmanager.h" -#include "talk/p2p/client/basicportallocator.h" -#include "talk/p2p/client/sessionmanagertask.h" -#include "talk/session/phone/devicemanager.h" -#include "talk/session/phone/mediaengine.h" -#include "talk/session/phone/mediasessionclient.h" +#include "talk/base/stringutils.h" +#include "talk/base/thread.h" #include "talk/examples/call/console.h" #include "talk/examples/call/presencepushtask.h" #include "talk/examples/call/presenceouttask.h" @@ -50,11 +45,14 @@ #include "talk/examples/call/friendinvitesendtask.h" #include "talk/examples/call/muc.h" #include "talk/examples/call/voicemailjidrequester.h" -#ifdef USE_TALK_SOUND -#include "talk/sound/platformsoundsystemfactory.h" -#endif +#include "talk/p2p/base/sessionmanager.h" +#include "talk/p2p/client/basicportallocator.h" +#include "talk/p2p/client/sessionmanagertask.h" +#include "talk/session/phone/devicemanager.h" +#include "talk/session/phone/mediaengine.h" +#include "talk/session/phone/mediasessionclient.h" +#include "talk/xmpp/constants.h" -#include "talk/base/logging.h" class NullRenderer : public cricket::VideoRenderer { public: @@ -237,16 +235,20 @@ void CallClient::ParseLine(const std::string& line) { } CallClient::CallClient(buzz::XmppClient* xmpp_client) - : xmpp_client_(xmpp_client), media_engine_(NULL), media_client_(NULL), - call_(NULL), incoming_call_(false), - auto_accept_(false), pmuc_domain_("groupchat.google.com"), - local_renderer_(NULL), remote_renderer_(NULL), - roster_(new RosterMap), portallocator_flags_(0), - allow_local_ips_(false), initial_protocol_(cricket::PROTOCOL_HYBRID) -#ifdef USE_TALK_SOUND - , sound_system_factory_(NULL) -#endif - { + : xmpp_client_(xmpp_client), + media_engine_(NULL), + media_client_(NULL), + call_(NULL), + incoming_call_(false), + auto_accept_(false), + pmuc_domain_("groupchat.google.com"), + local_renderer_(NULL), + remote_renderer_(NULL), + roster_(new RosterMap), + portallocator_flags_(0), + allow_local_ips_(false), + initial_protocol_(cricket::PROTOCOL_HYBRID), + secure_policy_(cricket::SEC_DISABLED) { xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange); } @@ -334,14 +336,19 @@ void CallClient::InitPhone() { // dispatched by it. worker_thread_->Start(); + // TODO: It looks like we are leaking many + // objects. E.g. |network_manager_| and |socket_factory_| are never + // deleted. + network_manager_ = new talk_base::NetworkManager(); + socket_factory_ = new talk_base::BasicPacketSocketFactory(worker_thread_); // TODO: Decide if the relay address should be specified here. talk_base::SocketAddress stun_addr("stun.l.google.com", 19302); - port_allocator_ = - new cricket::BasicPortAllocator(network_manager_, stun_addr, - talk_base::SocketAddress(), talk_base::SocketAddress(), - talk_base::SocketAddress()); + port_allocator_ = new cricket::BasicPortAllocator( + network_manager_, socket_factory_, stun_addr, + talk_base::SocketAddress(), talk_base::SocketAddress(), + talk_base::SocketAddress()); if (portallocator_flags_ != 0) { port_allocator_->set_flags(portallocator_flags_); @@ -359,32 +366,19 @@ void CallClient::InitPhone() { session_manager_task_->EnableOutgoingMessages(); session_manager_task_->Start(); -#ifdef USE_TALK_SOUND - if (!sound_system_factory_) { - sound_system_factory_ = new cricket::PlatformSoundSystemFactory(); - } -#endif - if (!media_engine_) { - media_engine_ = cricket::MediaEngine::Create( -#ifdef USE_TALK_SOUND - sound_system_factory_ -#endif - ); + media_engine_ = cricket::MediaEngine::Create(); } media_client_ = new cricket::MediaSessionClient( xmpp_client_->jid(), session_manager_, media_engine_, - new cricket::DeviceManager( -#ifdef USE_TALK_SOUND - sound_system_factory_ -#endif - )); + new cricket::DeviceManager()); media_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate); media_client_->SignalDevicesChange.connect(this, &CallClient::OnDevicesChange); + media_client_->set_secure(secure_policy_); } void CallClient::OnRequestSignaling() { diff --git a/third_party/libjingle/source/talk/examples/call/callclient.h b/third_party/libjingle/source/talk/examples/call/callclient.h index a5686b4..d2b806e 100644 --- a/third_party/libjingle/source/talk/examples/call/callclient.h +++ b/third_party/libjingle/source/talk/examples/call/callclient.h @@ -34,12 +34,10 @@ #include "talk/p2p/base/session.h" #include "talk/session/phone/mediachannel.h" +#include "talk/session/phone/mediasessionclient.h" #include "talk/xmpp/xmppclient.h" #include "talk/examples/call/status.h" #include "talk/examples/call/console.h" -#ifdef USE_TALK_SOUND -#include "talk/sound/soundsystemfactory.h" -#endif namespace buzz { class PresencePushTask; @@ -68,7 +66,6 @@ class Receiver; class Call; struct CallOptions; class SessionManagerTask; -enum SignalingProtocol; } struct RosterItem { @@ -92,7 +89,7 @@ class CallClient: public sigslot::has_slots<> { auto_accept_ = auto_accept; } void SetPmucDomain(const std::string &pmuc_domain) { - pmuc_domain_ = pmuc_domain; + pmuc_domain_ = pmuc_domain; } void SetConsole(Console *console) { console_ = console; @@ -114,6 +111,10 @@ class CallClient: public sigslot::has_slots<> { initial_protocol_ = initial_protocol; } + void SetSecurePolicy(cricket::SecureMediaPolicy secure_policy) { + secure_policy_ = secure_policy; + } + typedef std::map<buzz::Jid, buzz::Muc*> MucMap; @@ -167,6 +168,7 @@ class CallClient: public sigslot::has_slots<> { buzz::XmppClient* xmpp_client_; talk_base::Thread* worker_thread_; talk_base::NetworkManager* network_manager_; + talk_base::PacketSocketFactory* socket_factory_; cricket::PortAllocator* port_allocator_; cricket::SessionManager* session_manager_; cricket::SessionManagerTask* session_manager_task_; @@ -193,10 +195,8 @@ class CallClient: public sigslot::has_slots<> { bool allow_local_ips_; cricket::SignalingProtocol initial_protocol_; + cricket::SecureMediaPolicy secure_policy_; std::string last_sent_to_; -#ifdef USE_TALK_SOUND - cricket::SoundSystemFactory* sound_system_factory_; -#endif }; #endif // TALK_EXAMPLES_CALL_CALLCLIENT_H_ diff --git a/third_party/libjingle/source/talk/libjingle.scons b/third_party/libjingle/source/talk/libjingle.scons index 09a3bc1..870a5d8 100644 --- a/third_party/libjingle/source/talk/libjingle.scons +++ b/third_party/libjingle/source/talk/libjingle.scons @@ -56,7 +56,9 @@ talk.Library(env, name = "libsrtp", ) talk.Library(env, name = "libjingle", lin_srcs = [ + "base/latebindingsymboltable.cc", "base/linux.cc", + "session/phone/libudevsymboltable.cc", "session/phone/v4llookup.cc", ], mac_srcs = [ @@ -80,12 +82,12 @@ talk.Library(env, name = "libjingle", srcs = [ "base/asyncfile.cc", "base/asynchttprequest.cc", - "base/asyncpacketsocket.cc", "base/asyncsocket.cc", "base/asynctcpsocket.cc", "base/asyncudpsocket.cc", "base/autodetectproxy.cc", "base/base64.cc", + "base/basicpacketsocketfactory.cc", "base/bytebuffer.cc", "base/checks.cc", "base/common.cc", @@ -111,6 +113,7 @@ talk.Library(env, name = "libjingle", "base/physicalsocketserver.cc", "base/proxydetect.cc", "base/proxyinfo.cc", + "base/ratetracker.cc", "base/signalthread.cc", "base/socketadapters.cc", "base/socketaddress.cc", @@ -169,6 +172,7 @@ talk.Library(env, name = "libjingle", "session/phone/mediamonitor.cc", "session/phone/mediasessionclient.cc", "session/phone/rtpdump.cc", + "session/phone/rtcpmuxfilter.cc", "session/phone/soundclip.cc", "session/phone/srtpfilter.cc", "xmllite/qname.cc", @@ -190,6 +194,7 @@ talk.Library(env, name = "libjingle", "xmpp/xmpptask.cc", ], includedirs = [ + "third_party/libudev", "third_party/expat-2.0.1/", "third_party/srtp/include", "third_party/srtp/crypto/include", diff --git a/third_party/libjingle/source/talk/main.scons b/third_party/libjingle/source/talk/main.scons index a1348ff..b8705d1 100644 --- a/third_party/libjingle/source/talk/main.scons +++ b/third_party/libjingle/source/talk/main.scons @@ -92,6 +92,8 @@ win_env = root_env.Clone( CCPDBFLAGS = '', CCFLAGS_DEBUG = '', CCFLAGS_OPTIMIZED = '', + # We force a x86 target even when building on x64 Windows platforms. + TARGET_ARCH = 'x86', ) win_env.Append( @@ -186,7 +188,7 @@ win_dbg_env = win_env.Clone( ) win_dbg_env.Prepend( - CCFLAGS=[ + CCFLAGS = [ '/ZI', # enable debugging '/Od', # disable optimizations '/MTd', # link with LIBCMTD.LIB debug lib @@ -196,6 +198,21 @@ win_dbg_env.Prepend( envs.append(win_dbg_env) +win_coverage_env = win_dbg_env.Clone( + tools = ['code_coverage'], + BUILD_TYPE = 'coverage', + BUILD_TYPE_DESCRIPTION = 'Windows code coverage build', + BUILD_GROUPS = ['all'], +) + +win_coverage_env.Append( + CPPDEFINES = [ + 'COVERAGE_ENABLED', + ], +) + +envs.append(win_coverage_env) + win_opt_env = win_env.Clone( BUILD_TYPE = 'opt', BUILD_TYPE_DESCRIPTION = 'Windows opt build', @@ -210,6 +227,9 @@ win_opt_env.Prepend( '/MT', # link with LIBCMT.LIB (multi-threaded, static linked crt) '/GS', # enable security checks ], + LINKFLAGS = [ + '/safeseh', + ], ) envs.append(win_opt_env) @@ -230,7 +250,6 @@ posix_env.Append( '_REENTRANT', ], CCFLAGS = [ - '-m32', '-Wall', '-Werror', '-Wno-switch', @@ -241,9 +260,6 @@ posix_env.Append( '-Wno-ctor-dtor-privacy', '-fno-rtti', ], - LINKFLAGS = [ - '-m32', - ], ) #------------------------------------------------------------------------------- @@ -262,6 +278,7 @@ mac_env.Append( 'MAC_OS_X_VERSION_MIN_REQUIRED=1040', ], CCFLAGS = [ + '-m32', '-arch', 'i386', '-isysroot', '/Developer/SDKs/MacOSX10.5.sdk', '-fasm-blocks', @@ -274,6 +291,7 @@ mac_env.Append( # TODO: consider only defining for libs that actually have objc. '-ObjC', '-arch', 'i386', + '-m32', ], FRAMEWORKS = [ 'CoreServices', @@ -281,6 +299,11 @@ mac_env.Append( 'Security', 'SystemConfiguration', 'OpenGL', + 'CoreAudio', + 'Quartz', + 'QuickTime', + 'Cocoa', + 'QTKit', ] ) @@ -327,9 +350,7 @@ linux_common_env.Append( CPPDEFINES = [ 'LINUX', 'HAVE_GLIB', - # TODO() Enable once we figure out multiple defines with gips lib - # Also consider other linux flags: 64bit, no-strict-aliasing, wrap, etc - #'USE_TALK_SOUND', +# 'HAVE_DBUS_GLIB', ], CCFLAGS = [ # TODO: Some or all of this may be desirable for Mac too. @@ -373,6 +394,14 @@ linux_env = linux_common_env.Clone() # product would end up being broken on any computer with a different version # installed. So instead we build it ourself and statically link to it. linux_env.SetBits('use_static_openssl') +linux_env.Append( + CCFLAGS = [ + '-m32', + ], + LINKFLAGS = [ + '-m32', + ], +) linux_dbg_env = linux_env.Clone( BUILD_TYPE = 'dbg', @@ -454,6 +483,7 @@ if win_env.Bit('vsproj'): # Solution and target projects s = vs_env.ComponentVSSolution( + # 'libjingle', # Please uncomment this line if you build VS proj files. ['all_libraries', 'all_programs', 'all_test_programs'], projects = [p], ) diff --git a/third_party/libjingle/source/talk/p2p/base/common.h b/third_party/libjingle/source/talk/p2p/base/common.h index 72f8cfa..5a38180 100644 --- a/third_party/libjingle/source/talk/p2p/base/common.h +++ b/third_party/libjingle/source/talk/p2p/base/common.h @@ -25,12 +25,13 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CRICKET_P2P_BASE_COMMON_H__ -#define CRICKET_P2P_BASE_COMMON_H__ +#ifndef TALK_P2P_BASE_COMMON_H_ +#define TALK_P2P_BASE_COMMON_H_ #include "talk/base/logging.h" // Common log description format for jingle messages -#define LOG_J(sev,obj) LOG(sev) << "Jingle:" << obj->ToString() << ": " +#define LOG_J(sev, obj) LOG(sev) << "Jingle:" << obj->ToString() << ": " +#define LOG_JV(sev, obj) LOG_V(sev) << "Jingle:" << obj->ToString() << ": " -#endif // CRICKET_P2P_BASE_COMMON_H__ +#endif // TALK_P2P_BASE_COMMON_H_ diff --git a/third_party/libjingle/source/talk/p2p/base/constants.cc b/third_party/libjingle/source/talk/p2p/base/constants.cc index 1becb39..f44ce21 100644 --- a/third_party/libjingle/source/talk/p2p/base/constants.cc +++ b/third_party/libjingle/source/talk/p2p/base/constants.cc @@ -63,6 +63,9 @@ const std::string GINGLE_ACTION_ACCEPT("accept"); const std::string GINGLE_ACTION_REJECT("reject"); const std::string GINGLE_ACTION_TERMINATE("terminate"); const std::string GINGLE_ACTION_CANDIDATES("candidates"); +const std::string GINGLE_ACTION_NOTIFY("notify"); +const std::string GINGLE_ACTION_UPDATE("update"); +const std::string GINGLE_ACTION_VIEW("view"); const std::string LN_ERROR("error"); const buzz::QName QN_GINGLE_REDIRECT(true, NS_GINGLE, "redirect"); @@ -73,6 +76,7 @@ const std::string STR_REDIRECT_PREFIX("xmpp:"); const std::string LN_DESCRIPTION("description"); const std::string LN_PAYLOADTYPE("payload-type"); const buzz::QName QN_ID(true, NS_EMPTY, "id"); +const buzz::QName QN_SID(true, NS_EMPTY, "sid"); const buzz::QName QN_NAME(true, NS_EMPTY, "name"); const buzz::QName QN_CLOCKRATE(true, NS_EMPTY, "clockrate"); const buzz::QName QN_BITRATE(true, NS_EMPTY, "bitrate"); @@ -188,6 +192,34 @@ const std::string STR_TERMINATE_PROTOCOL_ERROR("protocol-error"); const std::string STR_TERMINATE_INTERNAL_SERVER_ERROR("internal-server-error"); const std::string STR_TERMINATE_UNKNOWN_ERROR("unknown-error"); +// Session notify messages +const buzz::QName QN_GINGLE_NOTIFY(true, NS_GINGLE, "notify"); +const buzz::QName QN_GINGLE_NOTIFY_NICK(true, cricket::NS_EMPTY, "nick"); +const buzz::QName QN_GINGLE_NOTIFY_SOURCE(true, NS_GINGLE, "source"); +const buzz::QName QN_GINGLE_NOTIFY_SOURCE_MTYPE( + true, cricket::NS_EMPTY, "mtype"); +const buzz::QName QN_GINGLE_NOTIFY_SOURCE_SSRC(true, cricket::NS_EMPTY, "ssrc"); +const std::string GINGLE_NOTIFY_SOURCE_MTYPE_AUDIO("audio"); +const std::string GINGLE_NOTIFY_SOURCE_MTYPE_VIDEO("video"); + +// Session view messages +const buzz::QName QN_GINGLE_VIEW(true, cricket::NS_EMPTY, "view"); +const buzz::QName QN_GINGLE_VIEW_TYPE(true, cricket::NS_EMPTY, "type"); +const buzz::QName QN_GINGLE_VIEW_NICK(true, cricket::NS_EMPTY, "nick"); +const buzz::QName QN_GINGLE_VIEW_MEDIA_TYPE(true, cricket::NS_EMPTY, "mtype"); +const buzz::QName QN_GINGLE_VIEW_SSRC(true, cricket::NS_EMPTY, "ssrc"); +const std::string GINGLE_VIEW_TYPE_STATIC("static"); +const std::string GINGLE_VIEW_TYPE_DYNAMIC("dynamic"); +const std::string GINGLE_VIEW_MEDIA_TYPE_AUDIO("audio"); +const std::string GINGLE_VIEW_MEDIA_TYPE_VIDEO("video"); +const buzz::QName QN_GINGLE_VIEW_PARAMS(true, cricket::NS_EMPTY, "params"); +const buzz::QName QN_GINGLE_VIEW_PARAMS_WIDTH(true, cricket::NS_EMPTY, "width"); +const buzz::QName QN_GINGLE_VIEW_PARAMS_HEIGHT( + true, cricket::NS_EMPTY, "height"); +const buzz::QName QN_GINGLE_VIEW_PARAMS_FRAMERATE( + true, cricket::NS_EMPTY, "framerate"); + + // old stuff #ifdef FEATURE_ENABLE_VOICEMAIL const std::string NS_VOICEMAIL("http://www.google.com/session/voicemail"); diff --git a/third_party/libjingle/source/talk/p2p/base/constants.h b/third_party/libjingle/source/talk/p2p/base/constants.h index 9f93cbc..cd2e2c5 100644 --- a/third_party/libjingle/source/talk/p2p/base/constants.h +++ b/third_party/libjingle/source/talk/p2p/base/constants.h @@ -79,6 +79,9 @@ extern const std::string GINGLE_ACTION_ACCEPT; extern const std::string GINGLE_ACTION_REJECT; extern const std::string GINGLE_ACTION_TERMINATE; extern const std::string GINGLE_ACTION_CANDIDATES; +extern const std::string GINGLE_ACTION_NOTIFY; +extern const std::string GINGLE_ACTION_UPDATE; +extern const std::string GINGLE_ACTION_VIEW; extern const std::string LN_ERROR; extern const buzz::QName QN_GINGLE_REDIRECT; @@ -89,6 +92,7 @@ extern const std::string STR_REDIRECT_PREFIX; extern const std::string LN_DESCRIPTION; extern const std::string LN_PAYLOADTYPE; extern const buzz::QName QN_ID; +extern const buzz::QName QN_SID; extern const buzz::QName QN_NAME; extern const buzz::QName QN_CLOCKRATE; extern const buzz::QName QN_BITRATE; @@ -203,6 +207,30 @@ extern const std::string STR_TERMINATE_PROTOCOL_ERROR; extern const std::string STR_TERMINATE_INTERNAL_SERVER_ERROR; extern const std::string STR_TERMINATE_UNKNOWN_ERROR; +// Session notify messages +extern const buzz::QName QN_GINGLE_NOTIFY; +extern const buzz::QName QN_GINGLE_NOTIFY_NICK; +extern const buzz::QName QN_GINGLE_NOTIFY_SOURCE; +extern const buzz::QName QN_GINGLE_NOTIFY_SOURCE_MTYPE; +extern const buzz::QName QN_GINGLE_NOTIFY_SOURCE_SSRC; +extern const std::string GINGLE_NOTIFY_SOURCE_MTYPE_AUDIO; +extern const std::string GINGLE_NOTIFY_SOURCE_MTYPE_VIDEO; + +// Session view messages +extern const buzz::QName QN_GINGLE_VIEW; +extern const buzz::QName QN_GINGLE_VIEW_TYPE; +extern const buzz::QName QN_GINGLE_VIEW_NICK; +extern const buzz::QName QN_GINGLE_VIEW_MEDIA_TYPE; +extern const buzz::QName QN_GINGLE_VIEW_SSRC; +extern const std::string GINGLE_VIEW_TYPE_STATIC; +extern const std::string GINGLE_VIEW_TYPE_DYNAMIC; +extern const std::string GINGLE_VIEW_MEDIA_TYPE_AUDIO; +extern const std::string GINGLE_VIEW_MEDIA_TYPE_VIDEO; +extern const buzz::QName QN_GINGLE_VIEW_PARAMS; +extern const buzz::QName QN_GINGLE_VIEW_PARAMS_WIDTH; +extern const buzz::QName QN_GINGLE_VIEW_PARAMS_HEIGHT; +extern const buzz::QName QN_GINGLE_VIEW_PARAMS_FRAMERATE; + // old stuff #ifdef FEATURE_ENABLE_VOICEMAIL extern const std::string NS_VOICEMAIL; diff --git a/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc b/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc index 0cd9b71..1b2bec2 100644 --- a/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc +++ b/third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.cc @@ -621,12 +621,21 @@ void P2PTransportChannel::SortConnections() { // Track the best connection, and let listeners know void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) { - // Note: the previous best_connection_ may be destroyed by now, so don't + // Note: if conn is NULL, the previous best_connection_ has been destroyed, + // so don't use it. // use it. + Connection* old_best_connection = best_connection_; best_connection_ = conn; if (best_connection_) { - LOG_J(LS_INFO, this) << "New best connection: " << conn->ToString(); + if (old_best_connection) { + LOG_J(LS_INFO, this) << "Previous best connection: " + << old_best_connection->ToString(); + } + LOG_J(LS_INFO, this) << "New best connection: " + << best_connection_->ToString(); SignalRouteChange(this, best_connection_->remote_candidate().address()); + } else { + LOG_J(LS_INFO, this) << "No best connection"; } } diff --git a/third_party/libjingle/source/talk/p2p/base/port.cc b/third_party/libjingle/source/talk/p2p/base/port.cc index 2aa66a4..edf52c5 100644 --- a/third_party/libjingle/source/talk/p2p/base/port.cc +++ b/third_party/libjingle/source/talk/p2p/base/port.cc @@ -25,28 +25,16 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif +#include "talk/p2p/base/port.h" #include <algorithm> #include <vector> -#include "talk/base/asyncudpsocket.h" -#include "talk/base/asynctcpsocket.h" #include "talk/base/helpers.h" #include "talk/base/logging.h" #include "talk/base/scoped_ptr.h" -#include "talk/base/socketadapters.h" #include "talk/base/stringutils.h" #include "talk/p2p/base/common.h" -#include "talk/p2p/base/port.h" - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std { - using ::memcmp; -} -#endif namespace { @@ -137,11 +125,19 @@ bool StringToProto(const char* value, ProtocolType* proto) { } Port::Port(talk_base::Thread* thread, const std::string& type, - talk_base::SocketFactory* factory, talk_base::Network* network) - : thread_(thread), factory_(factory), type_(type), network_(network), - preference_(-1), lifetime_(LT_PRESTART), enable_port_packets_(false) { - if (factory_ == NULL) - factory_ = thread_->socketserver(); + talk_base::PacketSocketFactory* factory, talk_base::Network* network, + uint32 ip, int min_port, int max_port) + : thread_(thread), + factory_(factory), + type_(type), + network_(network), + ip_(ip), + min_port_(min_port), + max_port_(max_port), + preference_(-1), + lifetime_(LT_PRESTART), + enable_port_packets_(false) { + ASSERT(factory_ != NULL); set_username_fragment(talk_base::CreateRandomString(16)); set_password(talk_base::CreateRandomString(16)); @@ -209,7 +205,7 @@ void Port::OnReadPacket( // send back a proper binding response. StunMessage* msg; std::string remote_username; - if (!GetStunMessage(data, size, addr, msg, remote_username)) { + if (!GetStunMessage(data, size, addr, &msg, &remote_username)) { LOG_J(LS_ERROR, this) << "Received non-STUN packet from unknown address (" << addr.ToString() << ")"; } else if (!msg) { @@ -228,34 +224,16 @@ void Port::OnReadPacket( } } -void Port::SendBindingRequest(Connection* conn) { - // Construct the request message. - StunMessage request; - request.SetType(STUN_BINDING_REQUEST); - request.SetTransactionID(talk_base::CreateRandomString(16)); - - StunByteStringAttribute* username_attr = - StunAttribute::CreateByteString(STUN_ATTR_USERNAME); - std::string username = conn->remote_candidate().username(); - username.append(username_frag_); - username_attr->CopyBytes(username.c_str(), username.size()); - request.AddAttribute(username_attr); - - // Send the request message. - // NOTE: If we wanted to, this is where we would add the HMAC. - talk_base::ByteBuffer buf; - request.Write(&buf); - SendTo(buf.Data(), buf.Length(), conn->remote_candidate().address(), false); -} - bool Port::GetStunMessage(const char* data, size_t size, const talk_base::SocketAddress& addr, - StunMessage *& msg, std::string& remote_username) { + StunMessage** out_msg, std::string* out_username) { // NOTE: This could clearly be optimized to avoid allocating any memory. // However, at the data rates we'll be looking at on the client side, // this probably isn't worth worrying about. - - msg = 0; + ASSERT(out_msg != NULL); + ASSERT(out_username != NULL); + *out_msg = NULL; + out_username->clear(); // Parse the request message. If the packet is not a complete and correct // STUN message, then ignore it. @@ -277,68 +255,73 @@ bool Port::GetStunMessage(const char* data, size_t size, if (stun_msg->type() == STUN_BINDING_REQUEST) { if (remote_frag_len < 0) { // Username not present or corrupted, don't reply. - LOG_J(LS_ERROR, this) << "Received STUN request without username"; + LOG_J(LS_ERROR, this) << "Received STUN request without username from " + << addr.ToString(); return true; } else if (std::memcmp(username_attr->bytes(), username_frag_.c_str(), username_frag_.size()) != 0) { - LOG_J(LS_ERROR, this) << "Received STUN request with bad username"; + LOG_J(LS_ERROR, this) << "Received STUN request with bad local username " + << std::string(username_attr->bytes(), + username_attr->length()) << " from " + << addr.ToString(); SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST, STUN_ERROR_REASON_BAD_REQUEST); return true; } - remote_username.assign(username_attr->bytes() + username_frag_.size(), - username_attr->bytes() + username_attr->length()); + out_username->assign(username_attr->bytes() + username_frag_.size(), + username_attr->bytes() + username_attr->length()); } else if ((stun_msg->type() == STUN_BINDING_RESPONSE) || (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) { if (remote_frag_len < 0) { - // NOTE(tschmelcher): This is benign. It occurs when the response to a - // StunBindingRequest to the real STUN server (which involves no - // usernames) took too long to reach us and so the base StunRequest - // re-sent itself, resulting in us getting an extraneous second response - // that gets forwarded on to this code and correctly discarded. - LOG_J(LS_ERROR, this) << "Received STUN response without username"; + LOG_J(LS_ERROR, this) << "Received STUN response without username from " + << addr.ToString(); // Do not send error response to a response return true; } else if (std::memcmp(username_attr->bytes() + remote_frag_len, username_frag_.c_str(), username_frag_.size()) != 0) { - LOG_J(LS_ERROR, this) << "Received STUN response with bad username"; + LOG_J(LS_ERROR, this) << "Received STUN response with bad local username " + << std::string(username_attr->bytes(), + username_attr->length()) << " from " + << addr.ToString(); // Do not send error response to a response return true; } - remote_username.assign(username_attr->bytes(), - username_attr->bytes() + remote_frag_len); + out_username->assign(username_attr->bytes(), + username_attr->bytes() + remote_frag_len); if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) { if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) { LOG_J(LS_ERROR, this) << "Received STUN binding error:" - << " class=" << error_code->error_class() - << " number=" << error_code->number() - << " reason='" << error_code->reason() << "'"; + << " class=" + << static_cast<int>(error_code->error_class()) + << " number=" + << static_cast<int>(error_code->number()) + << " reason='" << error_code->reason() << "'" + << " from " << addr.ToString(); // Return message to allow error-specific processing } else { - LOG_J(LS_ERROR, this) - << "Received STUN error response with no error code"; + LOG_J(LS_ERROR, this) << "Received STUN binding error without a error " + << "code from " << addr.ToString(); // Drop corrupt message return true; } } } else { LOG_J(LS_ERROR, this) << "Received STUN packet with invalid type (" - << stun_msg->type() << ")"; + << stun_msg->type() << ") from " << addr.ToString(); return true; } // Return the STUN message found. - msg = stun_msg.release(); + *out_msg = stun_msg.release(); return true; } -void Port::SendBindingResponse( - StunMessage* request, const talk_base::SocketAddress& addr) { - +void Port::SendBindingResponse(StunMessage* request, + const talk_base::SocketAddress& addr) { ASSERT(request->type() == STUN_BINDING_REQUEST); // Retrieve the username from the request. @@ -371,7 +354,10 @@ void Port::SendBindingResponse( // NOTE: If we wanted to, this is where we would add the HMAC. talk_base::ByteBuffer buf; response.Write(&buf); - SendTo(buf.Data(), buf.Length(), addr, false); + if (SendTo(buf.Data(), buf.Length(), addr, false) < 0) { + LOG_J(LS_ERROR, this) << "Failed to send STUN ping response to " + << addr.ToString(); + } // The fact that we received a successful request means that this connection // (if one exists) should now be readable. @@ -381,13 +367,12 @@ void Port::SendBindingResponse( conn->ReceivedPing(); } -void Port::SendBindingErrorResponse( - StunMessage* request, const talk_base::SocketAddress& addr, int error_code, - const std::string& reason) { - +void Port::SendBindingErrorResponse(StunMessage* request, + const talk_base::SocketAddress& addr, + int error_code, const std::string& reason) { ASSERT(request->type() == STUN_BINDING_REQUEST); - // Retrieve the username from the request. If it didn't have one, we + // Retrieve the username from the request. If it didn't have one, we // shouldn't be responding at all. const StunByteStringAttribute* username_attr = request->GetByteString(STUN_ATTR_USERNAME); @@ -417,41 +402,8 @@ void Port::SendBindingErrorResponse( talk_base::ByteBuffer buf; response.Write(&buf); SendTo(buf.Data(), buf.Length(), addr, false); -} - -talk_base::AsyncPacketSocket* Port::CreatePacketSocket(ProtocolType proto) { - if (proto == PROTO_UDP) { - // UDP sockets are simple. - return talk_base::AsyncUDPSocket::Create(factory_); - } else if (proto == PROTO_TCP || proto == PROTO_SSLTCP) { - // Create the base TCP socket. Bail out if this fails. - talk_base::AsyncSocket* socket = factory_->CreateAsyncSocket(SOCK_STREAM); - if (!socket) { - return NULL; - } - - // If using a proxy, wrap the socket in a proxy socket. - if (proxy().type == talk_base::PROXY_SOCKS5) { - socket = new talk_base::AsyncSocksProxySocket( - socket, proxy().address, proxy().username, proxy().password); - } else if (proxy().type == talk_base::PROXY_HTTPS) { - socket = new talk_base::AsyncHttpsProxySocket( - socket, user_agent(), proxy().address, - proxy().username, proxy().password); - } - - // If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket. - if (proto == PROTO_SSLTCP) { - socket = new talk_base::AsyncSSLSocket(socket); - } - - // Finally, wrap that socket in a TCP packet socket. - // [Insert obligatory Taco Town reference here] - return new talk_base::AsyncTCPSocket(socket); - } else { - LOG_J(LS_ERROR, this) << "Unknown protocol (" << proto << ")"; - return NULL; - } + LOG_J(LS_INFO, this) << "Sending STUN binding error: reason=" << reason + << " to " << addr.ToString(); } void Port::OnMessage(talk_base::Message *pmsg) { @@ -528,15 +480,15 @@ class ConnectionRequest : public StunRequest { } virtual void OnResponse(StunMessage* response) { - connection_->OnConnectionRequestResponse(response, Elapsed()); + connection_->OnConnectionRequestResponse(this, response); } virtual void OnErrorResponse(StunMessage* response) { - connection_->OnConnectionRequestErrorResponse(response, Elapsed()); + connection_->OnConnectionRequestErrorResponse(this, response); } virtual void OnTimeout() { - LOG_J(LS_VERBOSE, connection_) << "Timing-out STUN ping " << id(); + connection_->OnConnectionRequestTimeout(this); } virtual int GetNextDelay() { @@ -560,7 +512,8 @@ Connection::Connection(Port* port, size_t index, remote_candidate_(remote_candidate), read_state_(STATE_READ_TIMEOUT), write_state_(STATE_WRITE_CONNECT), connected_(true), pruned_(false), requests_(port->thread()), rtt_(DEFAULT_RTT), - last_ping_sent_(0), last_ping_received_(0), reported_(false) { + last_ping_sent_(0), last_ping_received_(0), last_data_received_(0), + reported_(false) { // Wire up to send stun packets requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket); LOG_J(LS_INFO, this) << "Connection created"; @@ -605,16 +558,18 @@ void Connection::set_connected(bool value) { } } -void Connection::OnSendStunPacket( - const void* data, size_t size, StunRequest* req) { - port_->SendTo(data, size, remote_candidate_.address(), false); +void Connection::OnSendStunPacket(const void* data, size_t size, + StunRequest* req) { + if (port_->SendTo(data, size, remote_candidate_.address(), false) < 0) { + LOG_J(LS_WARNING, this) << "Failed to send STUN ping " << req->id(); + } } void Connection::OnReadPacket(const char* data, size_t size) { StunMessage* msg; std::string remote_username; const talk_base::SocketAddress& addr(remote_candidate_.address()); - if (!port_->GetStunMessage(data, size, addr, msg, remote_username)) { + if (!port_->GetStunMessage(data, size, addr, &msg, &remote_username)) { // The packet did not parse as a valid STUN message // If this connection is readable, then pass along the packet. @@ -622,6 +577,7 @@ void Connection::OnReadPacket(const char* data, size_t size) { // readable means data from this address is acceptable // Send it on! + last_data_received_ = talk_base::Time(); recv_rate_tracker_.Update(size); SignalReadPacket(this, data, size); @@ -629,24 +585,30 @@ void Connection::OnReadPacket(const char* data, size_t size) { if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) set_write_state(STATE_WRITE_CONNECT); } else { - // Not readable means the remote address hasn't send a valid + // Not readable means the remote address hasn't sent a valid // binding request yet. LOG_J(LS_WARNING, this) << "Received non-STUN packet from an unreadable connection."; } } else if (!msg) { - // The packet was STUN, but was already handled + // The packet was STUN, but was already handled internally. } else if (remote_username != remote_candidate_.username()) { - // Not destined this connection - LOG_J(LS_ERROR, this) << "Received STUN packet on wrong address."; + // The packet had the right local username, but the remote username was + // not the right one for the remote address. if (msg->type() == STUN_BINDING_REQUEST) { + LOG_J(LS_ERROR, this) << "Received STUN request with bad remote username " + << remote_username; port_->SendBindingErrorResponse(msg, addr, STUN_ERROR_BAD_REQUEST, STUN_ERROR_REASON_BAD_REQUEST); + } else if (msg->type() == STUN_BINDING_RESPONSE || + msg->type() == STUN_BINDING_ERROR_RESPONSE) { + LOG_J(LS_ERROR, this) << "Received STUN response with bad remote username" + " " << remote_username; } delete msg; } else { - // The packet is STUN, with the current username + // The packet is STUN, with the right username. // If this is a STUN request, then update the readable bit and respond. // If this is a STUN response, then update the writable bit. @@ -697,6 +659,18 @@ void Connection::Destroy() { } void Connection::UpdateState(uint32 now) { + uint32 rtt = ConservativeRTTEstimate(rtt_); + + std::string pings; + for (size_t i = 0; i < pings_since_last_response_.size(); ++i) { + char buf[32]; + talk_base::sprintfn(buf, sizeof(buf), "%u", + pings_since_last_response_[i]); + pings.append(buf).append(" "); + } + LOG_J(LS_VERBOSE, this) << "UpdateState(): pings_since_last_response_=" << + pings << ", rtt=" << rtt << ", now=" << now; + // Check the readable state. // // Since we don't know how many pings the other side has attempted, the best @@ -704,6 +678,9 @@ void Connection::UpdateState(uint32 now) { if ((read_state_ == STATE_READABLE) && (last_ping_received_ + CONNECTION_READ_TIMEOUT <= now)) { + LOG_J(LS_INFO, this) << "Unreadable after " + << now - last_ping_received_ + << " ms without a ping, rtt=" << rtt; set_read_state(STATE_READ_TIMEOUT); } @@ -716,18 +693,6 @@ void Connection::UpdateState(uint32 now) { // Before timing out writability, we give a fixed amount of time. This is to // allow for changes in network conditions. - uint32 rtt = ConservativeRTTEstimate(rtt_); - - std::string pings; - for (size_t i = 0; i < pings_since_last_response_.size(); ++i) { - char buf[32]; - talk_base::sprintfn(buf, sizeof(buf), "%u", - pings_since_last_response_[i]); - pings.append(buf).append(" "); - } - LOG_J(LS_VERBOSE, this) << "UpdateState(): pings_since_last_response_ = " << - pings << ", rtt = " << rtt << ", now = " << now; - if ((write_state_ == STATE_WRITABLE) && TooManyFailures(pings_since_last_response_, CONNECTION_WRITE_CONNECT_FAILURES, @@ -736,6 +701,16 @@ void Connection::UpdateState(uint32 now) { TooLongWithoutResponse(pings_since_last_response_, CONNECTION_WRITE_CONNECT_TIMEOUT, now)) { + uint32 max_pings = CONNECTION_WRITE_CONNECT_FAILURES; + LOG_J(LS_INFO, this) << "Unwritable after " << max_pings + << " ping failures and " + << now - pings_since_last_response_[0] + << " ms without a response," + << " ms since last received ping=" + << now - last_ping_received_ + << " ms since last received data=" + << now - last_data_received_ + << " rtt=" << rtt; set_write_state(STATE_WRITE_CONNECT); } @@ -743,6 +718,9 @@ void Connection::UpdateState(uint32 now) { TooLongWithoutResponse(pings_since_last_response_, CONNECTION_WRITE_TIMEOUT, now)) { + LOG_J(LS_INFO, this) << "Timed out after " + << now - pings_since_last_response_[0] + << " ms without a response, rtt=" << rtt; set_write_state(STATE_WRITE_TIMEOUT); } } @@ -787,79 +765,46 @@ std::string Connection::ToString() const { << CONNECT_STATE_ABBREV[connected()] << READ_STATE_ABBREV[read_state()] << WRITE_STATE_ABBREV[write_state()] - << "|" << rtt_ << "]"; + << "|"; + if (rtt_ < DEFAULT_RTT) { + ss << rtt_ << "]"; + } else { + ss << "-]"; + } return ss.str(); } -void Connection::OnConnectionRequestResponse(StunMessage* response, - uint32 rtt) { - // We have a potentially valid reply from the remote address. - // The packet must include a username that ends with our fragment, - // since it is a response. +void Connection::OnConnectionRequestResponse(ConnectionRequest* request, + StunMessage* response) { + // We've already validated that this is a STUN binding response with + // the correct local and remote username for this connection. + // So if we're not already, become writable. We may be bringing a pruned + // connection back to life, but if we don't really want it, we can always + // prune it again. + uint32 rtt = request->Elapsed(); + set_write_state(STATE_WRITABLE); - // Check exact message type - bool valid = true; - if (response->type() != STUN_BINDING_RESPONSE) - valid = false; - - // Must have username attribute - const StunByteStringAttribute* username_attr = - response->GetByteString(STUN_ATTR_USERNAME); - if (valid) { - if (!username_attr) { - LOG_J(LS_ERROR, this) << "Received likely STUN packet with no username"; - valid = false; - } - } - - // Length must be at least the size of our fragment (actually, should - // be bigger since our fragment is at the end!) - if (valid) { - if (username_attr->length() <= port_->username_fragment().size()) { - LOG_J(LS_ERROR, this) << "Received likely STUN packet with short username"; - valid = false; - } - } - - // Compare our fragment with the end of the username - must be exact match - if (valid) { - std::string username_fragment = port_->username_fragment(); - int offset = (int)(username_attr->length() - username_fragment.size()); - if (std::memcmp(username_attr->bytes() + offset, - username_fragment.c_str(), username_fragment.size()) != 0) { - LOG_J(LS_ERROR, this) << "Received STUN response with bad username"; - valid = false; - } + std::string pings; + for (size_t i = 0; i < pings_since_last_response_.size(); ++i) { + char buf[32]; + talk_base::sprintfn(buf, sizeof(buf), "%u", + pings_since_last_response_[i]); + pings.append(buf).append(" "); } - if (valid) { - // Valid response. If we're not already, become writable. We may be - // bringing a pruned connection back to life, but if we don't really want - // it, we can always prune it again. - set_write_state(STATE_WRITABLE); - - std::string pings; - for (size_t i = 0; i < pings_since_last_response_.size(); ++i) { - char buf[32]; - talk_base::sprintfn(buf, sizeof(buf), "%u", - pings_since_last_response_[i]); - pings.append(buf).append(" "); - } - LOG_J(LS_VERBOSE, this) << "OnConnectionRequestResponse(): " - "pings_since_last_response_ = " << pings << ", rtt = " << rtt; - - pings_since_last_response_.clear(); - rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1); + LOG_J(LS_VERBOSE, this) << "Received STUN ping response " << request->id() + << ", pings_since_last_response_=" << pings + << ", rtt=" << rtt; - LOG_J(LS_VERBOSE, this) << "Received STUN ping response " << - response->transaction_id() << " after rtt = " << rtt; - } + pings_since_last_response_.clear(); + rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1); } -void Connection::OnConnectionRequestErrorResponse(StunMessage *response, - uint32 rtt) { +void Connection::OnConnectionRequestErrorResponse(ConnectionRequest* request, + StunMessage* response) { const StunErrorCodeAttribute* error = response->GetErrorCode(); - uint32 error_code = error ? error->error_code() : STUN_ERROR_GLOBAL_FAILURE; + uint32 error_code = error ? + error->error_code() : static_cast<uint32>(STUN_ERROR_GLOBAL_FAILURE); if ((error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE) || (error_code == STUN_ERROR_SERVER_ERROR) @@ -869,16 +814,33 @@ void Connection::OnConnectionRequestErrorResponse(StunMessage *response, // Race failure, retry } else { // This is not a valid connection. - LOG_J(LS_ERROR, this) << "Received STUN error response; killing connection"; + LOG_J(LS_ERROR, this) << "Received STUN error response, code=" + << error_code << "; killing connection"; set_write_state(STATE_WRITE_TIMEOUT); } } +void Connection::OnConnectionRequestTimeout(ConnectionRequest* request) { + // Log at LS_INFO if we miss a ping on a writable connection. + talk_base::LoggingSeverity sev = (write_state_ == STATE_WRITABLE) ? + talk_base::LS_INFO : talk_base::LS_VERBOSE; + uint32 when = talk_base::Time() - request->Elapsed(); + size_t failures; + for (failures = 0; failures < pings_since_last_response_.size(); ++failures) { + if (pings_since_last_response_[failures] > when) { + break; + } + } + LOG_JV(sev, this) << "Timing-out STUN ping " << request->id() + << " after " << request->Elapsed() + << " ms, failures=" << failures; +} + void Connection::CheckTimeout() { // If both read and write have timed out, then this connection can contribute // no more to p2p socket unless at some later date readability were to come // back. However, we gave readability a long time to timeout, so at this - // point, it seems fair to get rid of this connectoin. + // point, it seems fair to get rid of this connection. if ((read_state_ == STATE_READ_TIMEOUT) && (write_state_ == STATE_WRITE_TIMEOUT)) { port_->thread()->Post(this, MSG_DELETE); @@ -894,19 +856,19 @@ void Connection::OnMessage(talk_base::Message *pmsg) { } size_t Connection::recv_bytes_second() { - return recv_rate_tracker_.bytes_second(); + return recv_rate_tracker_.units_second(); } size_t Connection::recv_total_bytes() { - return recv_rate_tracker_.total_bytes(); + return recv_rate_tracker_.total_units(); } size_t Connection::sent_bytes_second() { - return send_rate_tracker_.bytes_second(); + return send_rate_tracker_.units_second(); } size_t Connection::sent_total_bytes() { - return send_rate_tracker_.total_bytes(); + return send_rate_tracker_.total_units(); } ProxyConnection::ProxyConnection(Port* port, size_t index, @@ -929,48 +891,4 @@ int ProxyConnection::Send(const void* data, size_t size) { return sent; } -RateTracker::RateTracker() - : total_bytes_(0), bytes_second_(0), - last_bytes_second_time_(static_cast<uint32>(-1)), - last_bytes_second_calc_(0) { -} - -size_t RateTracker::total_bytes() const { - return total_bytes_; -} - -size_t RateTracker::bytes_second() { - // Snapshot bytes / second calculator. Determine how many seconds have - // elapsed since our last reference point. If over 1 second, establish - // a new reference point that is an integer number of seconds since the - // last one, and compute the bytes over that interval. - - uint32 current_time = talk_base::Time(); - if (last_bytes_second_time_ != static_cast<uint32>(-1)) { - int delta = talk_base::TimeDiff(current_time, last_bytes_second_time_); - if (delta >= 1000) { - int fraction_time = delta % 1000; - int seconds = delta / 1000; - int fraction_bytes = - static_cast<int>(total_bytes_ - last_bytes_second_calc_) * - fraction_time / delta; - // Compute "bytes received during the interval" / "seconds in interval" - bytes_second_ = - (total_bytes_ - last_bytes_second_calc_ - fraction_bytes) / seconds; - last_bytes_second_time_ = current_time - fraction_time; - last_bytes_second_calc_ = total_bytes_ - fraction_bytes; - } - } - if (last_bytes_second_time_ == static_cast<uint32>(-1)) { - last_bytes_second_time_ = current_time; - last_bytes_second_calc_ = total_bytes_; - } - - return bytes_second_; -} - -void RateTracker::Update(size_t bytes) { - total_bytes_ += bytes; -} - } // namespace cricket diff --git a/third_party/libjingle/source/talk/p2p/base/port.h b/third_party/libjingle/source/talk/p2p/base/port.h index 88b15c6..8304146 100644 --- a/third_party/libjingle/source/talk/p2p/base/port.h +++ b/third_party/libjingle/source/talk/p2p/base/port.h @@ -33,9 +33,11 @@ #include <map> #include "talk/base/network.h" -#include "talk/base/socketaddress.h" +#include "talk/base/packetsocketfactory.h" #include "talk/base/proxyinfo.h" +#include "talk/base/ratetracker.h" #include "talk/base/sigslot.h" +#include "talk/base/socketaddress.h" #include "talk/base/thread.h" #include "talk/p2p/base/candidate.h" #include "talk/p2p/base/stun.h" @@ -48,6 +50,7 @@ class AsyncPacketSocket; namespace cricket { class Connection; +class ConnectionRequest; enum ProtocolType { PROTO_UDP, @@ -67,37 +70,24 @@ struct ProtocolAddress { : address(a), proto(p) { } }; -// Computes instantaneous bytes per second. -class RateTracker { - public: - RateTracker(); - size_t total_bytes() const; - size_t bytes_second(); - void Update(size_t bytes); - - private: - size_t total_bytes_; - size_t bytes_second_; - uint32 last_bytes_second_time_; - size_t last_bytes_second_calc_; -}; - // Represents a local communication mechanism that can be used to create // connections to similar mechanisms of the other client. Subclasses of this // one add support for specific mechanisms like local UDP ports. class Port : public talk_base::MessageHandler, public sigslot::has_slots<> { public: - Port(talk_base::Thread* thread, const std::string &type, - talk_base::SocketFactory* factory, talk_base::Network* network); + Port(talk_base::Thread* thread, const std::string& type, + talk_base::PacketSocketFactory* factory, talk_base::Network* network, + uint32 ip, int min_port, int max_port); virtual ~Port(); // The thread on which this port performs its I/O. talk_base::Thread* thread() { return thread_; } // The factory used to create the sockets of this port. - talk_base::SocketFactory* socket_factory() const { return factory_; } - void set_socket_factory(talk_base::SocketFactory* factory) - { factory_ = factory; } + talk_base::PacketSocketFactory* socket_factory() const { return factory_; } + void set_socket_factory(talk_base::PacketSocketFactory* factory) { + factory_ = factory; + } // Each port is identified by a name (for debugging purposes). const std::string& name() const { return name_; } @@ -124,7 +114,6 @@ class Port : public talk_base::MessageHandler, public sigslot::has_slots<> { void set_preference(float preference) { preference_ = preference; } // Identifies the port type. - //const std::string& protocol() const { return proto_; } const std::string& type() const { return type_; } // Identifies network that this port was allocated on. @@ -199,8 +188,6 @@ class Port : public talk_base::MessageHandler, public sigslot::has_slots<> { const std::string& user_agent() { return user_agent_; } const talk_base::ProxyInfo& proxy() { return proxy_; } - talk_base::AsyncPacketSocket * CreatePacketSocket(ProtocolType proto); - // Normally, packets arrive through a connection (or they result signaling of // unknown address). Calling this method turns off delivery of packets // through their respective connection and instead delivers every packet @@ -226,20 +213,6 @@ class Port : public talk_base::MessageHandler, public sigslot::has_slots<> { std::string ToString() const; protected: - talk_base::Thread* thread_; - talk_base::SocketFactory* factory_; - std::string type_; - talk_base::Network* network_; - uint32 generation_; - std::string name_; - std::string username_frag_; - std::string password_; - float preference_; - std::vector<Candidate> candidates_; - AddressMap connections_; - enum Lifetime { LT_PRESTART, LT_PRETIMEOUT, LT_POSTTIMEOUT } lifetime_; - bool enable_port_packets_; - // Fills in the local address of the port. void AddAddress(const talk_base::SocketAddress& address, const std::string& protocol, bool final); @@ -253,8 +226,6 @@ class Port : public talk_base::MessageHandler, public sigslot::has_slots<> { void OnReadPacket(const char* data, size_t size, const talk_base::SocketAddress& addr); - // Constructs a STUN binding request for the given connection and sends it. - void SendBindingRequest(Connection* conn); // If the given data comprises a complete and correct STUN message then the // return value is true, otherwise false. If the message username corresponds @@ -263,9 +234,25 @@ class Port : public talk_base::MessageHandler, public sigslot::has_slots<> { // remote_username contains the remote fragment of the STUN username. bool GetStunMessage(const char* data, size_t size, const talk_base::SocketAddress& addr, - StunMessage *& msg, std::string& remote_username); + StunMessage** out_msg, std::string* out_username); - friend class Connection; + // TODO: make these members private + talk_base::Thread* thread_; + talk_base::PacketSocketFactory* factory_; + std::string type_; + talk_base::Network* network_; + uint32 ip_; + int min_port_; + int max_port_; + uint32 generation_; + std::string name_; + std::string username_frag_; + std::string password_; + float preference_; + std::vector<Candidate> candidates_; + AddressMap connections_; + enum Lifetime { LT_PRESTART, LT_PRETIMEOUT, LT_POSTTIMEOUT } lifetime_; + bool enable_port_packets_; private: // Called when one of our connections deletes itself. @@ -277,6 +264,8 @@ class Port : public talk_base::MessageHandler, public sigslot::has_slots<> { // Information to use when going through a proxy. std::string user_agent_; talk_base::ProxyInfo proxy_; + + friend class Connection; }; // Represents a communication link between a port on the local client and a @@ -345,7 +334,7 @@ class Connection : public talk_base::MessageHandler, // still keep it around in case the other side wants to use it. But we can // safely stop pinging on it and we can allow it to time out if the other // side stops using it as well. - bool pruned() { return pruned_; } + bool pruned() const { return pruned_; } void Prune(); // Makes the connection go away. @@ -356,7 +345,7 @@ class Connection : public talk_base::MessageHandler, void UpdateState(uint32 now); // Called when this connection should try checking writability again. - uint32 last_ping_sent() { return last_ping_sent_; } + uint32 last_ping_sent() const { return last_ping_sent_; } void Ping(uint32 now); // Called whenever a valid ping is received on this connection. This is @@ -366,36 +355,22 @@ class Connection : public talk_base::MessageHandler, // Debugging description of this connection std::string ToString() const; - bool reported() { return reported_; } + bool reported() const { return reported_; } void set_reported(bool reported) { reported_ = reported;} protected: - Port* port_; - size_t local_candidate_index_; - Candidate remote_candidate_; - ReadState read_state_; - WriteState write_state_; - bool connected_; - bool pruned_; - StunRequestManager requests_; - uint32 rtt_; - uint32 last_ping_sent_; // last time we sent a ping to the other side - uint32 last_ping_received_; // last time we received a ping from the other - // side - std::vector<uint32> pings_since_last_response_; - - RateTracker recv_rate_tracker_; - RateTracker send_rate_tracker_; - - // Callbacks from ConnectionRequest - void OnConnectionRequestResponse(StunMessage *response, uint32 rtt); - void OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt); + // Constructs a new connection to the given remote port. + Connection(Port* port, size_t index, const Candidate& candidate); // Called back when StunRequestManager has a stun packet to send void OnSendStunPacket(const void* data, size_t size, StunRequest* req); - // Constructs a new connection to the given remote port. - Connection(Port* port, size_t index, const Candidate& candidate); + // Callbacks from ConnectionRequest + void OnConnectionRequestResponse(ConnectionRequest* req, + StunMessage* response); + void OnConnectionRequestErrorResponse(ConnectionRequest* req, + StunMessage* response); + void OnConnectionRequestTimeout(ConnectionRequest* req); // Changes the state and signals if necessary. void set_read_state(ReadState value); @@ -407,11 +382,29 @@ class Connection : public talk_base::MessageHandler, void OnMessage(talk_base::Message *pmsg); - friend class Port; - friend class ConnectionRequest; + Port* port_; + size_t local_candidate_index_; + Candidate remote_candidate_; + ReadState read_state_; + WriteState write_state_; + bool connected_; + bool pruned_; + StunRequestManager requests_; + uint32 rtt_; + uint32 last_ping_sent_; // last time we sent a ping to the other side + uint32 last_ping_received_; // last time we received a ping from the other + // side + uint32 last_data_received_; + std::vector<uint32> pings_since_last_response_; + + talk_base::RateTracker recv_rate_tracker_; + talk_base::RateTracker send_rate_tracker_; private: bool reported_; + + friend class Port; + friend class ConnectionRequest; }; // ProxyConnection defers all the interesting work to the port diff --git a/third_party/libjingle/source/talk/p2p/base/portallocator.h b/third_party/libjingle/source/talk/p2p/base/portallocator.h index 332326e..175bdbc 100644 --- a/third_party/libjingle/source/talk/p2p/base/portallocator.h +++ b/third_party/libjingle/source/talk/p2p/base/portallocator.h @@ -25,13 +25,14 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _PORTALLOCATOR_H_ -#define _PORTALLOCATOR_H_ +#ifndef TALK_P2P_BASE_PORTALLOCATOR_H_ +#define TALK_P2P_BASE_PORTALLOCATOR_H_ + +#include <string> +#include <vector> #include "talk/base/sigslot.h" #include "talk/p2p/base/port.h" -#include <string> -#undef SetPort namespace cricket { @@ -50,8 +51,8 @@ const uint32 PORTALLOCATOR_ENABLE_SHAKER = 0x10; const uint32 kDefaultPortAllocatorFlags = 0; class PortAllocatorSession : public sigslot::has_slots<> { -public: - PortAllocatorSession(uint32 flags) : flags_(flags) {} + public: + explicit PortAllocatorSession(uint32 flags) : flags_(flags) {} // Subclasses should clean up any ports created. virtual ~PortAllocatorSession() {} @@ -68,22 +69,28 @@ public: virtual bool IsGettingAllPorts() = 0; sigslot::signal2<PortAllocatorSession*, Port*> SignalPortReady; - sigslot::signal2<PortAllocatorSession*, const std::vector<Candidate>&> SignalCandidatesReady; + sigslot::signal2<PortAllocatorSession*, + const std::vector<Candidate>&> SignalCandidatesReady; uint32 generation() { return generation_; } void set_generation(uint32 generation) { generation_ = generation; } -private: + private: uint32 flags_; uint32 generation_; }; class PortAllocator { -public: - PortAllocator() : flags_(kDefaultPortAllocatorFlags) {} + public: + PortAllocator() : + flags_(kDefaultPortAllocatorFlags), + min_port_(0), + max_port_(0) { + } virtual ~PortAllocator() {} - virtual PortAllocatorSession *CreateSession(const std::string &name, const std::string &session_type) = 0; + virtual PortAllocatorSession *CreateSession(const std::string &name, + const std::string &session_type) = 0; uint32 flags() const { return flags_; } void set_flags(uint32 flags) { flags_ = flags; } @@ -91,15 +98,31 @@ public: const std::string& user_agent() const { return agent_; } const talk_base::ProxyInfo& proxy() const { return proxy_; } void set_proxy(const std::string& agent, const talk_base::ProxyInfo& proxy) { - agent_ = agent; proxy_ = proxy; + agent_ = agent; + proxy_ = proxy; + } + + // Gets/Sets the port range to use when choosing client ports. + int min_port() const { return min_port_; } + int max_port() const { return max_port_; } + bool SetPortRange(int min_port, int max_port) { + if (min_port > max_port) { + return false; + } + + min_port_ = min_port; + max_port_ = max_port; + return true; } -protected: + protected: uint32 flags_; std::string agent_; talk_base::ProxyInfo proxy_; + int min_port_; + int max_port_; }; -} // namespace cricket +} // namespace cricket -#endif // _PORTALLOCATOR_H_ +#endif // TALK_P2P_BASE_PORTALLOCATOR_H_ diff --git a/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc b/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc index c342dbc..955584e 100644 --- a/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc +++ b/third_party/libjingle/source/talk/p2p/base/pseudotcp.cc @@ -118,7 +118,7 @@ const uint32 PACKET_OVERHEAD = HEADER_SIZE + UDP_HEADER_SIZE + IP_HEADER_SIZE + const uint32 MIN_RTO = 250; // 250 ms (RFC1122, Sec 4.2.3.1 "fractions of a second") const uint32 DEF_RTO = 3000; // 3 seconds (RFC1122, Sec 4.2.3.1) const uint32 MAX_RTO = 60000; // 60 seconds -const uint32 ACK_DELAY = 100; // 100 milliseconds +const uint32 DEF_ACK_DELAY = 100; // 100 milliseconds const uint8 FLAG_CTL = 0x02; const uint8 FLAG_RST = 0x04; @@ -256,6 +256,9 @@ PseudoTcp::PseudoTcp(IPseudoTcpNotify* notify, uint32 conv) m_rx_rto = DEF_RTO; m_rx_srtt = m_rx_rttvar = 0; + + m_use_nagling = true; + m_ack_delay = DEF_ACK_DELAY; } PseudoTcp::~PseudoTcp() { @@ -337,7 +340,7 @@ void PseudoTcp::NotifyClock(uint32 now) { } // Check if it's time to send delayed acks - if (m_t_ack && (talk_base::TimeDiff(m_t_ack + ACK_DELAY, now) <= 0)) { + if (m_t_ack && (talk_base::TimeDiff(m_t_ack + m_ack_delay, now) <= 0)) { packet(m_snd_nxt, 0, 0, 0); } @@ -367,6 +370,26 @@ bool PseudoTcp::GetNextClock(uint32 now, long& timeout) { return clock_check(now, timeout); } +void PseudoTcp::GetOption(Option opt, int* value) { + if (opt == OPT_NODELAY) { + *value = m_use_nagling ? 0 : 1; + } else if (opt == OPT_ACKDELAY) { + *value = m_ack_delay; + } else { + ASSERT(false); + } +} + +void PseudoTcp::SetOption(Option opt, int value) { + if (opt == OPT_NODELAY) { + m_use_nagling = value == 0; + } else if (opt == OPT_ACKDELAY) { + m_ack_delay = value; + } else { + ASSERT(false); + } +} + // // IPStream Implementation // @@ -554,7 +577,7 @@ bool PseudoTcp::clock_check(uint32 now, long& nTimeout) { if (m_t_ack) { nTimeout = talk_base::_min<int32>(nTimeout, - talk_base::TimeDiff(m_t_ack + ACK_DELAY, now)); + talk_base::TimeDiff(m_t_ack + m_ack_delay, now)); } if (m_rto_base) { nTimeout = talk_base::_min<int32>(nTimeout, @@ -774,7 +797,11 @@ bool PseudoTcp::process(Segment& seg) { if (seg.seq != m_rcv_nxt) { sflags = sfImmediateAck; // (Fast Recovery) } else if (seg.len != 0) { - sflags = sfDelayedAck; + if (m_ack_delay == 0) { + sflags = sfImmediateAck; + } else { + sflags = sfDelayedAck; + } } #if _DEBUGMSG >= _DBG_NORMAL if (sflags == sfImmediateAck) { @@ -992,8 +1019,11 @@ void PseudoTcp::attemptSend(SendFlags sflags) { return; } - // Nagle algorithm - if ((m_snd_nxt > m_snd_una) && (nAvailable < m_mss)) { + // Nagle's algorithm. + // If there is data already in-flight, and we haven't a full segment of + // data ready to send then hold off until we get more to send, or the + // in-flight data is acknowledged. + if (m_use_nagling && (m_snd_nxt > m_snd_una) && (nAvailable < m_mss)) { return; } diff --git a/third_party/libjingle/source/talk/p2p/base/pseudotcp.h b/third_party/libjingle/source/talk/p2p/base/pseudotcp.h index 1446201..e8e0d3a 100644 --- a/third_party/libjingle/source/talk/p2p/base/pseudotcp.h +++ b/third_party/libjingle/source/talk/p2p/base/pseudotcp.h @@ -92,6 +92,18 @@ class PseudoTcp { // Returns false if the socket is ready to be destroyed. bool GetNextClock(uint32 now, long& timeout); + // Call these to get/set option values to tailor this PseudoTcp + // instance's behaviour for the kind of data it will carry. + // If an unrecognized option is set or got, an assertion will fire. + enum Option { + OPT_NODELAY, // Whether to enable Nagle's algorithm (0 == off) + OPT_ACKDELAY, // The Delayed ACK timeout (0 == off). + //kOptRcvBuf, // Set the receive buffer size, in bytes. + //kOptSndBuf, // Set the send buffer size, in bytes. + }; + void GetOption(Option opt, int* value); + void SetOption(Option opt, int value); + protected: enum SendFlags { sfNone, sfDelayedAck, sfImmediateAck }; enum { @@ -180,6 +192,10 @@ class PseudoTcp { uint8 m_dup_acks; uint32 m_recover; uint32 m_t_ack; + + // Configuration options + bool m_use_nagling; + uint32 m_ack_delay; }; } // namespace cricket diff --git a/third_party/libjingle/source/talk/p2p/base/relayport.cc b/third_party/libjingle/source/talk/p2p/base/relayport.cc index ff6d55a..2d3be61 100644 --- a/third_party/libjingle/source/talk/p2p/base/relayport.cc +++ b/third_party/libjingle/source/talk/p2p/base/relayport.cc @@ -25,11 +25,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif - -#include "talk/base/asynctcpsocket.h" +#include "talk/base/asyncpacketsocket.h" #include "talk/base/helpers.h" #include "talk/base/logging.h" #include "talk/p2p/base/relayport.h" @@ -96,8 +92,7 @@ class RelayConnection : public sigslot::has_slots<> { class RelayEntry : public talk_base::MessageHandler, public sigslot::has_slots<> { public: - RelayEntry(RelayPort* port, const talk_base::SocketAddress& ext_addr, - const talk_base::SocketAddress& local_addr); + RelayEntry(RelayPort* port, const talk_base::SocketAddress& ext_addr); ~RelayEntry(); RelayPort* port() { return port_; } @@ -148,21 +143,20 @@ class RelayEntry : public talk_base::MessageHandler, private: RelayPort* port_; - talk_base::SocketAddress ext_addr_, local_addr_; + talk_base::SocketAddress ext_addr_; size_t server_index_; bool connected_; bool locked_; RelayConnection* current_connection_; // Called when a TCP connection is established or fails - void OnSocketConnect(talk_base::AsyncTCPSocket* socket); - void OnSocketClose(talk_base::AsyncTCPSocket* socket, int error); + void OnSocketConnect(talk_base::AsyncPacketSocket* socket); + void OnSocketClose(talk_base::AsyncPacketSocket* socket, int error); // Called when a packet is received on this socket. - void OnReadPacket( - const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket); + void OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr); // Sends the given data on the socket to the server with no wrapping. This // returns the number of bytes written or -1 if an error occurred. @@ -192,14 +186,16 @@ class AllocateRequest : public StunRequest { const std::string RELAY_PORT_TYPE("relay"); RelayPort::RelayPort( - talk_base::Thread* thread, talk_base::SocketFactory* factory, - talk_base::Network* network, const talk_base::SocketAddress& local_addr, + talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, + talk_base::Network* network, uint32 ip, int min_port, int max_port, const std::string& username, const std::string& password, const std::string& magic_cookie) - : Port(thread, RELAY_PORT_TYPE, factory, network), local_addr_(local_addr), - ready_(false), magic_cookie_(magic_cookie), error_(0) { + : Port(thread, RELAY_PORT_TYPE, factory, network, ip, min_port, max_port), + ready_(false), + magic_cookie_(magic_cookie), + error_(0) { entries_.push_back( - new RelayEntry(this, talk_base::SocketAddress(), local_addr_)); + new RelayEntry(this, talk_base::SocketAddress())); set_username_fragment(username); set_password(password); @@ -316,7 +312,7 @@ int RelayPort::SendTo(const void* data, size_t size, // If we did not find one, then we make a new one. This will not be useable // until it becomes connected, however. if (!entry && payload) { - entry = new RelayEntry(this, addr, local_addr_); + entry = new RelayEntry(this, addr); if (!entries_.empty()) { entry->SetServerIndex(entries_[0]->ServerIndex()); } @@ -345,7 +341,7 @@ int RelayPort::SendTo(const void* data, size_t size, } // The caller of the function is expecting the number of user data bytes, // rather than the size of the packet. - return (int)size; + return size; } int RelayPort::SetOption(talk_base::Socket::Option opt, int value) { @@ -420,9 +416,8 @@ void RelayConnection::SendAllocateRequest(RelayEntry* entry, int delay) { } RelayEntry::RelayEntry(RelayPort* port, - const talk_base::SocketAddress& ext_addr, - const talk_base::SocketAddress& local_addr) - : port_(port), ext_addr_(ext_addr), local_addr_(local_addr), + const talk_base::SocketAddress& ext_addr) + : port_(port), ext_addr_(ext_addr), server_index_(0), connected_(false), locked_(false), current_connection_(NULL) { } @@ -455,13 +450,23 @@ void RelayEntry::Connect() { LOG(LS_INFO) << "Connecting to relay via " << ProtoToString(ra->proto) << " @ " << ra->address.ToString(); - talk_base::AsyncPacketSocket* socket = port_->CreatePacketSocket(ra->proto); + talk_base::AsyncPacketSocket* socket = NULL; + + if (ra->proto == PROTO_UDP) { + // UDP sockets are simple. + socket = port_->socket_factory()->CreateUdpSocket( + talk_base::SocketAddress(port_->ip_, 0), + port_->min_port_, port_->max_port_); + } else if (ra->proto == PROTO_TCP || ra->proto == PROTO_SSLTCP) { + socket = port_->socket_factory()->CreateClientTcpSocket( + talk_base::SocketAddress(port_->ip_, 0), ra->address, + port_->proxy(), port_->user_agent(), ra->proto == PROTO_SSLTCP); + } else { + LOG(LS_WARNING) << "Unknown protocol (" << ra->proto << ")"; + } + if (!socket) { LOG(LS_WARNING) << "Socket creation failed"; - } else if (socket->Bind(local_addr_) < 0) { - LOG(LS_WARNING) << "Socket bind failed with error " << socket->GetError(); - delete socket; - socket = NULL; } // If we failed to get a socket, move on to the next protocol. @@ -479,13 +484,10 @@ void RelayEntry::Connect() { } // If we're trying UDP, start binding requests. - // If we're trying TCP, initiate a connection with a fixed timeout. + // If we're trying TCP, wait for connection with a fixed timeout. if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) { - talk_base::AsyncTCPSocket* tcp = - static_cast<talk_base::AsyncTCPSocket*>(socket); - tcp->SignalClose.connect(this, &RelayEntry::OnSocketClose); - tcp->SignalConnect.connect(this, &RelayEntry::OnSocketConnect); - tcp->Connect(ra->address); + socket->SignalClose.connect(this, &RelayEntry::OnSocketClose); + socket->SignalConnect.connect(this, &RelayEntry::OnSocketConnect); port()->thread()->PostDelayed(kSoftConnectTimeoutMs, this, kMessageConnectTimeout); } else { @@ -538,13 +540,13 @@ int RelayEntry::SendTo(const void* data, size_t size, StunByteStringAttribute* magic_cookie_attr = StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); magic_cookie_attr->CopyBytes(port_->magic_cookie().c_str(), - (uint16)port_->magic_cookie().size()); + port_->magic_cookie().size()); request.AddAttribute(magic_cookie_attr); StunByteStringAttribute* username_attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME); username_attr->CopyBytes(port_->username_fragment().c_str(), - (uint16)port_->username_fragment().size()); + port_->username_fragment().size()); request.AddAttribute(username_attr); StunAddressAttribute* addr_attr = @@ -564,7 +566,7 @@ int RelayEntry::SendTo(const void* data, size_t size, StunByteStringAttribute* data_attr = StunAttribute::CreateByteString(STUN_ATTR_DATA); - data_attr->CopyBytes(data, (uint16)size); + data_attr->CopyBytes(data, size); request.AddAttribute(data_attr); // TODO: compute the HMAC. @@ -616,8 +618,8 @@ void RelayEntry::OnMessage(talk_base::Message *pmsg) { // the next address, otherwise give this connection more time and // await the real timeout. // - // TODO: Connect to servers in pararel to speed up connect time - // and to avoid giving up to early. + // TODO: Connect to servers in parallel to speed up connect time + // and to avoid giving up too early. port_->SignalSoftTimeout(ra); HandleConnectFailure(current_connection_->socket()); } else { @@ -625,7 +627,7 @@ void RelayEntry::OnMessage(talk_base::Message *pmsg) { } } -void RelayEntry::OnSocketConnect(talk_base::AsyncTCPSocket* socket) { +void RelayEntry::OnSocketConnect(talk_base::AsyncPacketSocket* socket) { LOG(INFO) << "relay tcp connected to " << socket->GetRemoteAddress().ToString(); if (current_connection_ != NULL) { @@ -633,14 +635,15 @@ void RelayEntry::OnSocketConnect(talk_base::AsyncTCPSocket* socket) { } } -void RelayEntry::OnSocketClose(talk_base::AsyncTCPSocket* socket, int error) { +void RelayEntry::OnSocketClose(talk_base::AsyncPacketSocket* socket, + int error) { PLOG(LERROR, error) << "Relay connection failed: socket closed"; HandleConnectFailure(socket); } -void RelayEntry::OnReadPacket(const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket) { +void RelayEntry::OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr) { // ASSERT(remote_addr == port_->server_addr()); // TODO: are we worried about this? @@ -732,14 +735,14 @@ void AllocateRequest::Prepare(StunMessage* request) { StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE); magic_cookie_attr->CopyBytes( entry_->port()->magic_cookie().c_str(), - (uint16)entry_->port()->magic_cookie().size()); + entry_->port()->magic_cookie().size()); request->AddAttribute(magic_cookie_attr); StunByteStringAttribute* username_attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME); username_attr->CopyBytes( entry_->port()->username_fragment().c_str(), - (uint16)entry_->port()->username_fragment().size()); + entry_->port()->username_fragment().size()); request->AddAttribute(username_attr); } diff --git a/third_party/libjingle/source/talk/p2p/base/relayport.h b/third_party/libjingle/source/talk/p2p/base/relayport.h index fd0f83d..025668a 100644 --- a/third_party/libjingle/source/talk/p2p/base/relayport.h +++ b/third_party/libjingle/source/talk/p2p/base/relayport.h @@ -28,8 +28,11 @@ #ifndef TALK_P2P_BASE_RELAYPORT_H_ #define TALK_P2P_BASE_RELAYPORT_H_ +#include <deque> #include <string> +#include <utility> #include <vector> + #include "talk/p2p/base/port.h" #include "talk/p2p/base/stunrequest.h" @@ -51,18 +54,13 @@ class RelayPort : public Port { // RelayPort doesn't yet do anything fancy in the ctor. static RelayPort* Create( - talk_base::Thread* thread, talk_base::SocketFactory* factory, - talk_base::Network* network, const talk_base::SocketAddress& local_addr, - const std::string& username, const std::string& password, - const std::string& magic_cookie) { - return new RelayPort(thread, factory, network, local_addr, + talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, + talk_base::Network* network, uint32 ip, int min_port, int max_port, + const std::string& username, const std::string& password, + const std::string& magic_cookie) { + return new RelayPort(thread, factory, network, ip, min_port, max_port, username, password, magic_cookie); } - RelayPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, - talk_base::Network*, const talk_base::SocketAddress& local_addr, - const std::string& username, const std::string& password, - const std::string& magic_cookie); - bool Init(); virtual ~RelayPort(); void AddServerAddress(const ProtocolAddress& addr); @@ -86,6 +84,12 @@ class RelayPort : public Port { sigslot::signal1<const ProtocolAddress*> SignalSoftTimeout; protected: + RelayPort(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, + talk_base::Network*, uint32 ip, int min_port, int max_port, + const std::string& username, const std::string& password, + const std::string& magic_cookie); + bool Init(); + void SetReady(); virtual int SendTo(const void* data, size_t size, @@ -98,7 +102,6 @@ class RelayPort : public Port { private: friend class RelayEntry; - talk_base::SocketAddress local_addr_; std::deque<ProtocolAddress> server_addr_; bool ready_; std::vector<RelayEntry*> entries_; diff --git a/third_party/libjingle/source/talk/p2p/base/relayserver.cc b/third_party/libjingle/source/talk/p2p/base/relayserver.cc index 525da34..111b340 100644 --- a/third_party/libjingle/source/talk/p2p/base/relayserver.cc +++ b/third_party/libjingle/source/talk/p2p/base/relayserver.cc @@ -159,12 +159,24 @@ void RelayServer::RemoveInternalServerSocket( socket->SignalReadEvent.disconnect(this); } -int RelayServer::GetConnectionCount() { +int RelayServer::GetConnectionCount() const { return connections_.size(); } -bool RelayServer::HasConnection(const talk_base::SocketAddress& address) { - for (ConnectionMap::iterator it = connections_.begin(); +talk_base::SocketAddressPair RelayServer::GetConnection(int connection) const { + int i = 0; + for (ConnectionMap::const_iterator it = connections_.begin(); + it != connections_.end(); ++it) { + if (i == connection) { + return it->second->addr_pair(); + } + ++i; + } + return talk_base::SocketAddressPair(); +} + +bool RelayServer::HasConnection(const talk_base::SocketAddress& address) const { + for (ConnectionMap::const_iterator it = connections_.begin(); it != connections_.end(); ++it) { if (it->second->addr_pair().destination() == address) { return true; @@ -180,11 +192,14 @@ void RelayServer::OnReadEvent(talk_base::AsyncSocket* socket) { } void RelayServer::OnInternalPacket( - const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket) { + talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size, + const talk_base::SocketAddress& remote_addr) { // Get the address of the connection we just received on. - talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); + bool allocated; + talk_base::SocketAddressPair ap( + remote_addr, socket->GetLocalAddress(&allocated)); + ASSERT(allocated); ASSERT(!ap.destination().IsAny()); // If this did not come from an existing connection, it should be a STUN @@ -224,11 +239,14 @@ void RelayServer::OnInternalPacket( } void RelayServer::OnExternalPacket( - const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket) { + talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size, + const talk_base::SocketAddress& remote_addr) { // Get the address of the connection we just received on. - talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress()); + bool allocated; + talk_base::SocketAddressPair ap( + remote_addr, socket->GetLocalAddress(&allocated)); + ASSERT(allocated); ASSERT(!ap.destination().IsAny()); // If this connection already exists, then forward the traffic. @@ -425,8 +443,10 @@ void RelayServer::HandleStunAllocate( response.AddAttribute(magic_cookie_attr); size_t index = rand() % external_sockets_.size(); + bool allocated; talk_base::SocketAddress ext_addr = - external_sockets_[index]->GetLocalAddress(); + external_sockets_[index]->GetLocalAddress(&allocated); + ASSERT(allocated); StunAddressAttribute* addr_attr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); @@ -472,7 +492,10 @@ void RelayServer::HandleStunSend( // Create a new connection to establish the relationship with this binding. ASSERT(external_sockets_.size() == 1); talk_base::AsyncPacketSocket* socket = external_sockets_[0]; - talk_base::SocketAddressPair ap(ext_addr, socket->GetLocalAddress()); + bool allocated; + talk_base::SocketAddressPair ap( + ext_addr, socket->GetLocalAddress(&allocated)); + ASSERT(allocated); ext_conn = new RelayServerConnection(int_conn->binding(), ap, socket); ext_conn->binding()->AddExternalConnection(ext_conn); AddConnection(ext_conn); @@ -559,7 +582,7 @@ void RelayServer::AcceptConnection(talk_base::AsyncSocket* server_socket) { accepted_socket = new talk_base::AsyncSSLServerSocket(accepted_socket); } talk_base::AsyncTCPSocket* tcp_socket = - new talk_base::AsyncTCPSocket(accepted_socket); + new talk_base::AsyncTCPSocket(accepted_socket, false); // Finally add the socket so it can start communicating with the client. AddInternalSocket(tcp_socket); diff --git a/third_party/libjingle/source/talk/p2p/base/relayserver.h b/third_party/libjingle/source/talk/p2p/base/relayserver.h index 3337991..d34c099 100644 --- a/third_party/libjingle/source/talk/p2p/base/relayserver.h +++ b/third_party/libjingle/source/talk/p2p/base/relayserver.h @@ -25,8 +25,12 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __RELAYSERVER_H__ -#define __RELAYSERVER_H__ +#ifndef TALK_P2P_BASE_RELAYSERVER_H_ +#define TALK_P2P_BASE_RELAYSERVER_H_ + +#include <string> +#include <vector> +#include <map> #include "talk/base/asyncudpsocket.h" #include "talk/base/socketaddresspair.h" @@ -35,10 +39,6 @@ #include "talk/p2p/base/port.h" #include "talk/p2p/base/stun.h" -#include <string> -#include <vector> -#include <map> - namespace cricket { class RelayServerBinding; @@ -48,9 +48,9 @@ class RelayServerConnection; // All connections created with the same username/password are bound together. class RelayServer : public talk_base::MessageHandler, public sigslot::has_slots<> { -public: + public: // Creates a server, which will use this thread to post messages to itself. - RelayServer(talk_base::Thread* thread); + explicit RelayServer(talk_base::Thread* thread); ~RelayServer(); talk_base::Thread* thread() { return thread_; } @@ -79,16 +79,18 @@ public: // Removes this server socket from the list. void RemoveInternalServerSocket(talk_base::AsyncSocket* socket); - int GetConnectionCount(); + // Methods for testing and debuging. + int GetConnectionCount() const; + talk_base::SocketAddressPair GetConnection(int connection) const; + bool HasConnection(const talk_base::SocketAddress& address) const; - bool HasConnection(const talk_base::SocketAddress& address); - -private: + private: typedef std::vector<talk_base::AsyncPacketSocket*> SocketList; typedef std::map<talk_base::AsyncSocket*, cricket::ProtocolType> ServerSocketMap; - typedef std::map<std::string,RelayServerBinding*> BindingMap; - typedef std::map<talk_base::SocketAddressPair,RelayServerConnection*> ConnectionMap; + typedef std::map<std::string, RelayServerBinding*> BindingMap; + typedef std::map<talk_base::SocketAddressPair, + RelayServerConnection*> ConnectionMap; talk_base::Thread* thread_; bool log_bindings_; @@ -99,18 +101,19 @@ private: ConnectionMap connections_; // Called when a packet is received by the server on one of its sockets. - void OnInternalPacket( - const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket); - void OnExternalPacket( - const char* bytes, size_t size, const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket); + void OnInternalPacket(talk_base::AsyncPacketSocket* socket, + const char* bytes, size_t size, + const talk_base::SocketAddress& remote_addr); + void OnExternalPacket(talk_base::AsyncPacketSocket* socket, + const char* bytes, size_t size, + const talk_base::SocketAddress& remote_addr); void OnReadEvent(talk_base::AsyncSocket* socket); // Processes the relevant STUN request types from the client. bool HandleStun(const char* bytes, size_t size, - const talk_base::SocketAddress& remote_addr, talk_base::AsyncPacketSocket* socket, + const talk_base::SocketAddress& remote_addr, + talk_base::AsyncPacketSocket* socket, std::string* username, StunMessage* msg); void HandleStunAllocate(const char* bytes, size_t size, const talk_base::SocketAddressPair& ap, @@ -142,7 +145,7 @@ private: // Maintains information about a connection to the server. Each connection is // part of one and only one binding. class RelayServerConnection { -public: + public: RelayServerConnection(RelayServerBinding* binding, const talk_base::SocketAddressPair& addrs, talk_base::AsyncPacketSocket* socket); @@ -158,7 +161,8 @@ public: // Sends a packet to the connected client. If an address is provided, then // we make sure the internal client receives it, wrapping if necessary. void Send(const char* data, size_t size); - void Send(const char* data, size_t size, const talk_base::SocketAddress& ext_addr); + void Send(const char* data, size_t size, + const talk_base::SocketAddress& ext_addr); // Sends a STUN message to the connected client with no wrapping. void SendStun(const StunMessage& msg); @@ -172,12 +176,14 @@ public: // Records the address that raw packets should be forwarded to (for internal // packets only; for external, we already know where they go). - const talk_base::SocketAddress& default_destination() const { return default_dest_; } + const talk_base::SocketAddress& default_destination() const { + return default_dest_; + } void set_default_destination(const talk_base::SocketAddress& addr) { default_dest_ = addr; } -private: + private: RelayServerBinding* binding_; talk_base::SocketAddressPair addr_pair_; talk_base::AsyncPacketSocket* socket_; @@ -188,7 +194,7 @@ private: // Records a set of internal and external connections that we relay between, // or in other words, that are "bound" together. class RelayServerBinding : public talk_base::MessageHandler { -public: + public: RelayServerBinding( RelayServer* server, const std::string& username, const std::string& password, uint32 lifetime); @@ -215,13 +221,15 @@ public: // Determines the connection to use to send packets to or from the given // external address. - RelayServerConnection* GetInternalConnection(const talk_base::SocketAddress& ext_addr); - RelayServerConnection* GetExternalConnection(const talk_base::SocketAddress& ext_addr); + RelayServerConnection* GetInternalConnection( + const talk_base::SocketAddress& ext_addr); + RelayServerConnection* GetExternalConnection( + const talk_base::SocketAddress& ext_addr); // MessageHandler: void OnMessage(talk_base::Message *pmsg); -private: + private: RelayServer* server_; std::string username_; @@ -236,6 +244,6 @@ private: // TODO: bandwidth }; -} // namespace cricket +} // namespace cricket -#endif // __RELAYSERVER_H__ +#endif // TALK_P2P_BASE_RELAYSERVER_H_ diff --git a/third_party/libjingle/source/talk/p2p/base/relayserver_main.cc b/third_party/libjingle/source/talk/p2p/base/relayserver_main.cc index 2f07458..2de4247 100644 --- a/third_party/libjingle/source/talk/p2p/base/relayserver_main.cc +++ b/third_party/libjingle/source/talk/p2p/base/relayserver_main.cc @@ -53,18 +53,18 @@ int main(int argc, char **argv) { talk_base::Thread *pthMain = talk_base::Thread::Current(); talk_base::scoped_ptr<talk_base::AsyncUDPSocket> int_socket( - talk_base::CreateAsyncUDPSocket(pthMain->socketserver())); - if (int_socket->Bind(int_addr) < 0) { - std::cerr << "Internal socket bind(" << int_addr.ToString() << "): " - << std::strerror(int_socket->GetError()) << std::endl; + talk_base::AsyncUDPSocket::Create(pthMain->socketserver(), int_addr)); + if (!int_socket.get()) { + std::cerr << "Failed to create a UDP socket bound at" + << int_addr.ToString() << std::endl; return 1; } talk_base::scoped_ptr<talk_base::AsyncUDPSocket> ext_socket( - talk_base::CreateAsyncUDPSocket(pthMain->socketserver())); - if (ext_socket->Bind(ext_addr) < 0) { - std::cerr << "External socket bind(" << ext_addr.ToString() << "): " - << std::strerror(ext_socket->GetError()) << std::endl; + talk_base::AsyncUDPSocket::Create(pthMain->socketserver(), ext_addr)); + if (ext_socket.get()) { + std::cerr << "Failed to create a UDP socket bound at" + << ext_addr.ToString() << std::endl; return 1; } diff --git a/third_party/libjingle/source/talk/p2p/base/session.cc b/third_party/libjingle/source/talk/p2p/base/session.cc index 38b91ff..27b39dd 100644 --- a/third_party/libjingle/source/talk/p2p/base/session.cc +++ b/third_party/libjingle/source/talk/p2p/base/session.cc @@ -180,8 +180,6 @@ void BaseSession::SetError(Error error) { if (error != error_) { error_ = error; SignalError(this, error); - if (error_ != ERROR_NONE) - signaling_thread_->Post(this, MSG_ERROR); } } @@ -646,6 +644,12 @@ void Session::OnIncomingMessage(const SessionMessage& msg) { case ACTION_TRANSPORT_ACCEPT: valid = OnTransportAcceptMessage(msg, &error); break; + case ACTION_NOTIFY: + valid = OnNotifyMessage(msg, &error); + break; + case ACTION_UPDATE: + valid = OnUpdateMessage(msg, &error); + break; default: valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST, "unknown session message type", @@ -689,13 +693,16 @@ void Session::OnFailedSend(const buzz::XmlElement* orig_stanza, std::string error_type = "cancel"; const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR); - ASSERT(error != NULL); if (error) { ASSERT(error->HasAttr(buzz::QN_TYPE)); error_type = error->Attr(buzz::QN_TYPE); LOG(LS_ERROR) << "Session error:\n" << error->Str() << "\n" << "in response to:\n" << orig_stanza->Str(); + } else { + // don't crash if <error> is missing + LOG(LS_ERROR) << "Session error without <error/> element, ignoring"; + return; } if (msg.type == ACTION_TRANSPORT_INFO) { @@ -824,6 +831,30 @@ bool Session::OnTransportAcceptMessage(const SessionMessage& msg, return true; } +bool Session::OnNotifyMessage(const SessionMessage& msg, + MessageError* error) { + SessionNotify notify; + if (!ParseSessionNotify(msg.action_elem, ¬ify, error)) { + return false; + } + + SignalMediaSources(notify.nickname_to_sources); + + return true; +} + +bool Session::OnUpdateMessage(const SessionMessage& msg, + MessageError* error) { + SessionUpdate update; + if (!ParseSessionUpdate(msg.action_elem, &update, error)) { + return false; + } + + // TODO: Process this message appropriately. + + return true; +} + bool BareJidsEqual(const std::string& name1, const std::string& name2) { buzz::Jid jid1(name1); @@ -860,6 +891,12 @@ bool Session::CheckState(State state, MessageError* error) { return true; } +void Session::SetError(Error error) { + BaseSession::SetError(error); + if (error_ != ERROR_NONE) + signaling_thread_->Post(this, MSG_ERROR); +} + void Session::OnMessage(talk_base::Message *pmsg) { // preserve this because BaseSession::OnMessage may modify it BaseSession::State orig_state = state_; @@ -901,6 +938,16 @@ bool Session::WriteSessionAction( elems, error); } +bool Session::SetVideoView( + const std::vector<VideoViewRequest>& view_requests) { + SessionView view; + SessionError error; + + view.view_requests = view_requests; + + return !SendViewMessage(view, &error); +} + bool Session::SendAcceptMessage(const SessionDescription* sdesc, SessionError* error) { XmlElements elems; @@ -950,6 +997,12 @@ bool Session::WriteSessionAction(SignalingProtocol protocol, elems, error); } +bool Session::SendViewMessage(const SessionView& view, SessionError* error) { + XmlElements elems; + WriteSessionView(view, &elems); + return SendMessage(ACTION_VIEW, elems, error); +} + bool Session::ResendAllTransportInfoMessages(SessionError* error) { for (TransportMap::iterator iter = transports_.begin(); iter != transports_.end(); ++iter) { diff --git a/third_party/libjingle/source/talk/p2p/base/session.h b/third_party/libjingle/source/talk/p2p/base/session.h index ddbc663..b00e809 100644 --- a/third_party/libjingle/source/talk/p2p/base/session.h +++ b/third_party/libjingle/source/talk/p2p/base/session.h @@ -42,8 +42,6 @@ #include "talk/xmllite/xmlelement.h" #include "talk/xmpp/constants.h" -class JingleMessageHandler; - namespace cricket { class P2PTransportChannel; @@ -163,6 +161,7 @@ class BaseSession : public sigslot::has_slots<>, ERROR_TIME = 1, // no response to signaling ERROR_RESPONSE = 2, // error during signaling ERROR_NETWORK = 3, // network error, could not allocate network resources + ERROR_CONTENT = 4, // channel errors in SetLocalContent/SetRemoteContent }; explicit BaseSession(talk_base::Thread *signaling_thread); @@ -172,7 +171,7 @@ class BaseSession : public sigslot::has_slots<>, void SetState(State state); // Updates the error state, signaling if necessary. - void SetError(Error error); + virtual void SetError(Error error); // Handles messages posted to us. virtual void OnMessage(talk_base::Message *pmsg); @@ -364,9 +363,20 @@ class Session : public BaseSession { virtual void DestroyChannel(const std::string& content_name, const std::string& channel_name); + // Updates the error state, signaling if necessary. + virtual void SetError(Error error); + // Handles messages posted to us. virtual void OnMessage(talk_base::Message *pmsg); + // Fired when notification of media sources is received from the server. + // Passes a map whose keys are strings containing nick names for users + // in the session and whose values contain the SSRCs for each user. + sigslot::signal1<const StringToMediaSourcesMap&> SignalMediaSources; + + // Sets the video streams to receive from the server. + bool SetVideoView(const VideoViewRequestVector& view_requests); + private: // Creates or destroys a session. (These are called only SessionManager.) Session(SessionManager *session_manager, @@ -439,6 +449,7 @@ class Session : public BaseSession { bool SendTerminateMessage(const std::string& reason, SessionError* error); bool SendTransportInfoMessage(const TransportInfo& tinfo, SessionError* error); + bool SendViewMessage(const SessionView& view, SessionError* error); bool ResendAllTransportInfoMessages(SessionError* error); // Both versions of SendMessage send a message of the given type to @@ -510,6 +521,8 @@ class Session : public BaseSession { bool OnTerminateMessage(const SessionMessage& msg, MessageError* error); bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error); bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error); + bool OnNotifyMessage(const SessionMessage& msg, MessageError* error); + bool OnUpdateMessage(const SessionMessage& msg, MessageError* error); bool OnRedirectError(const SessionRedirect& redirect, SessionError* error); // Verifies that we are in the appropriate state to receive this message. diff --git a/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc b/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc index cfd035d..d61b666 100644 --- a/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc +++ b/third_party/libjingle/source/talk/p2p/base/sessionmessages.cc @@ -25,11 +25,13 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <stdio.h> #include <string> #include "talk/p2p/base/sessionmessages.h" #include "talk/base/logging.h" #include "talk/base/scoped_ptr.h" +#include "talk/base/stringutils.h" #include "talk/xmllite/xmlconstants.h" #include "talk/xmpp/constants.h" #include "talk/p2p/base/constants.h" @@ -38,6 +40,7 @@ #include "talk/p2p/base/sessionclient.h" #include "talk/p2p/base/sessiondescription.h" #include "talk/p2p/base/transport.h" +#include "talk/xmllite/xmlconstants.h" namespace cricket { @@ -70,6 +73,10 @@ ActionType ToActionType(const std::string& type) { return ACTION_TRANSPORT_INFO; if (type == JINGLE_ACTION_TRANSPORT_ACCEPT) return ACTION_TRANSPORT_ACCEPT; + if (type == GINGLE_ACTION_NOTIFY) + return ACTION_NOTIFY; + if (type == GINGLE_ACTION_UPDATE) + return ACTION_UPDATE; return ACTION_UNKNOWN; } @@ -108,6 +115,8 @@ std::string ToGingleString(ActionType type) { return GINGLE_ACTION_REJECT; case ACTION_SESSION_TERMINATE: return GINGLE_ACTION_TERMINATE; + case ACTION_VIEW: + return GINGLE_ACTION_VIEW; case ACTION_TRANSPORT_INFO: return GINGLE_ACTION_CANDIDATES; default: @@ -122,7 +131,12 @@ bool IsJingleMessage(const buzz::XmlElement* stanza) { return false; return (jingle->HasAttr(buzz::QN_ACTION) && - jingle->HasAttr(buzz::QN_ID)); + (jingle->HasAttr(QN_SID) + // TODO: This works around a bug in old jingle + // clients that set QN_ID instead of QN_SID. Once we know + // there are no clients which have this bug, we can remove + // this code. + || jingle->HasAttr(QN_ID))); } bool IsGingleMessage(const buzz::XmlElement* stanza) { @@ -164,7 +178,13 @@ bool ParseJingleSessionMessage(const buzz::XmlElement* jingle, msg->protocol = PROTOCOL_JINGLE; std::string type_string = jingle->Attr(buzz::QN_ACTION); msg->type = ToActionType(type_string); - msg->sid = jingle->Attr(buzz::QN_ID); + msg->sid = jingle->Attr(QN_SID); + // TODO: This works around a bug in old jingle clients + // that set QN_ID instead of QN_SID. Once we know there are no + // clients which have this bug, we can remove this code. + if (msg->sid.empty()) { + msg->sid = jingle->Attr(buzz::QN_ID); + } msg->initiator = GetXmlAttr(jingle, QN_INITIATOR, buzz::STR_EMPTY); msg->action_elem = jingle; @@ -217,7 +237,11 @@ buzz::XmlElement* WriteJingleAction(const SessionMessage& msg, const XmlElements& action_elems) { buzz::XmlElement* jingle = new buzz::XmlElement(QN_JINGLE, true); jingle->AddAttr(buzz::QN_ACTION, ToJingleString(msg.type)); - jingle->AddAttr(buzz::QN_ID, msg.sid); + jingle->AddAttr(QN_SID, msg.sid); + // TODO: This works around a bug in old jingle clinets + // that expected QN_ID instead of QN_SID. Once we know there are no + // clients which have this bug, we can remove this code. + jingle->AddAttr(QN_ID, msg.sid); // TODO: Right now, the XMPP server rejects a jingle-only // (non hybrid) message with "feature-not-implemented" if there is // no initiator. Fix the server, and then only set the initiator on @@ -820,6 +844,40 @@ bool WriteTransportInfos(SignalingProtocol protocol, } } +bool ParseSessionNotify(const buzz::XmlElement* action_elem, + SessionNotify* notify, ParseError* error) { + const buzz::XmlElement* notify_elem; + for (notify_elem = action_elem->FirstNamed(QN_GINGLE_NOTIFY); + notify_elem != NULL; + notify_elem = notify_elem->NextNamed(QN_GINGLE_NOTIFY)) { + // Note that a subsequent notify element for the same user will override a + // previous. We don't merge them. + std::string nick(notify_elem->Attr(QN_GINGLE_NOTIFY_NICK)); + if (nick != buzz::STR_EMPTY) { + MediaSources sources; + const buzz::XmlElement* source_elem; + for (source_elem = notify_elem->FirstNamed(QN_GINGLE_NOTIFY_SOURCE); + source_elem != NULL; + source_elem = source_elem->NextNamed(QN_GINGLE_NOTIFY_SOURCE)) { + std::string ssrc = source_elem->Attr(QN_GINGLE_NOTIFY_SOURCE_SSRC); + if (ssrc != buzz::STR_EMPTY) { + std::string mtype = source_elem->Attr(QN_GINGLE_NOTIFY_SOURCE_MTYPE); + if (mtype == GINGLE_NOTIFY_SOURCE_MTYPE_AUDIO) { + sources.audio_ssrc = strtoul(ssrc.c_str(), NULL, 10); + } else if (mtype == GINGLE_NOTIFY_SOURCE_MTYPE_VIDEO) { + sources.video_ssrc = strtoul(ssrc.c_str(), NULL, 10); + } + } + } + + notify->nickname_to_sources.insert( + std::pair<std::string, MediaSources>(nick, sources)); + } + } + + return true; +} + bool GetUriTarget(const std::string& prefix, const std::string& str, std::string* after) { size_t pos = str.find(prefix); @@ -830,6 +888,65 @@ bool GetUriTarget(const std::string& prefix, const std::string& str, return true; } +bool ParseSessionUpdate(const buzz::XmlElement* action_elem, + SessionUpdate* update, ParseError* error) { + // TODO: Parse the update message. + return true; +} + +void WriteSessionView(const SessionView& view, XmlElements* elems) { + std::vector<VideoViewRequest>::const_iterator it; + for (it = view.view_requests.begin(); it != view.view_requests.end(); it++) { + talk_base::scoped_ptr<buzz::XmlElement> view_elem( + new buzz::XmlElement(QN_GINGLE_VIEW)); + if (view_elem.get() == NULL) { + return; + } + + view_elem->SetAttr(QN_GINGLE_VIEW_TYPE, GINGLE_VIEW_TYPE_STATIC); + view_elem->SetAttr(QN_GINGLE_VIEW_NICK, it->nick_name); + view_elem->SetAttr(QN_GINGLE_VIEW_MEDIA_TYPE, + GINGLE_VIEW_MEDIA_TYPE_VIDEO); + + // A 32-bit uint, expressed as decimal, has a max of 10 digits, plus one + // for the null. + char str[11]; + int result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->ssrc); + if (result < 0 || result >= ARRAY_SIZE(str)) { + continue; + } + view_elem->SetAttr(QN_GINGLE_VIEW_SSRC, str); + + // Include video-specific parameters in a child <params> element. + talk_base::scoped_ptr<buzz::XmlElement> params_elem( + new buzz::XmlElement(QN_GINGLE_VIEW_PARAMS)); + if (params_elem.get() == NULL) { + return; + } + + result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->width); + if (result < 0 || result >= ARRAY_SIZE(str)) { + continue; + } + params_elem->SetAttr(QN_GINGLE_VIEW_PARAMS_WIDTH, str); + + result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->height); + if (result < 0 || result >= ARRAY_SIZE(str)) { + continue; + } + params_elem->SetAttr(QN_GINGLE_VIEW_PARAMS_HEIGHT, str); + + result = talk_base::sprintfn(str, ARRAY_SIZE(str), "%u", it->framerate); + if (result < 0 || result >= ARRAY_SIZE(str)) { + continue; + } + params_elem->SetAttr(QN_GINGLE_VIEW_PARAMS_FRAMERATE, str); + + view_elem->AddElement(params_elem.release()); + elems->push_back(view_elem.release()); + } +} + bool FindSessionRedirect(const buzz::XmlElement* stanza, SessionRedirect* redirect) { const buzz::XmlElement* error_elem = GetXmlChild(stanza, LN_ERROR); diff --git a/third_party/libjingle/source/talk/p2p/base/sessionmessages.h b/third_party/libjingle/source/talk/p2p/base/sessionmessages.h index eee8452..affb4d9 100644 --- a/third_party/libjingle/source/talk/p2p/base/sessionmessages.h +++ b/third_party/libjingle/source/talk/p2p/base/sessionmessages.h @@ -35,6 +35,7 @@ #include "talk/xmllite/xmlelement.h" #include "talk/p2p/base/constants.h" #include "talk/p2p/base/sessiondescription.h" // Needed to delete contents. +#include "talk/base/basictypes.h" namespace cricket { @@ -60,6 +61,13 @@ enum ActionType { ACTION_TRANSPORT_INFO, ACTION_TRANSPORT_ACCEPT, + + // TODO: Make better names for these when we think of a + // "jingley" way of signaling them. Even better, remove them from + // being needed at all. + ACTION_NOTIFY, + ACTION_UPDATE, + ACTION_VIEW, }; // Abstraction of a <jingle> element within an <iq> stanza, per XMPP @@ -153,6 +161,45 @@ struct SessionRedirect { std::string target; }; +// Holds the ssrcs for a user's media streams. +struct MediaSources { + uint32 audio_ssrc; + uint32 video_ssrc; + MediaSources() : audio_ssrc(0), video_ssrc(0) {} +}; + +typedef std::map<std::string, MediaSources> StringToMediaSourcesMap; + +struct SessionNotify { + // A mapping of room users (identified by their nicknames) to their ssrcs. + StringToMediaSourcesMap nickname_to_sources; +}; + +// TODO: Populate the update message. +struct SessionUpdate { +}; + +// Represents an individual <view> element in the <session type="view"> +// message. +struct VideoViewRequest { + std::string nick_name; + uint32 ssrc; + uint32 width; + uint32 height; + uint32 framerate; + + VideoViewRequest(const std::string& nick_name, uint32 ssrc, uint32 width, + uint32 height, uint32 framerate) : + nick_name(nick_name), ssrc(ssrc), width(width), height(height), + framerate(framerate) {} +}; + +typedef std::vector<VideoViewRequest> VideoViewRequestVector; + +struct SessionView { + VideoViewRequestVector view_requests; +}; + bool IsSessionMessage(const buzz::XmlElement* stanza); bool ParseSessionMessage(const buzz::XmlElement* stanza, SessionMessage* msg, @@ -212,6 +259,11 @@ bool WriteTransportInfos(SignalingProtocol protocol, const TransportParserMap& trans_parsers, XmlElements* elems, WriteError* error); +bool ParseSessionNotify(const buzz::XmlElement* action_elem, + SessionNotify* notify, ParseError* error); +bool ParseSessionUpdate(const buzz::XmlElement* action_elem, + SessionUpdate* update, ParseError* error); +void WriteSessionView(const SessionView& view, XmlElements* elems); // Handles both Gingle and Jingle syntax. bool FindSessionRedirect(const buzz::XmlElement* stanza, SessionRedirect* redirect); diff --git a/third_party/libjingle/source/talk/p2p/base/stunport.cc b/third_party/libjingle/source/talk/p2p/base/stunport.cc index 7f5dad7..aa6e1d3 100644 --- a/third_party/libjingle/source/talk/p2p/base/stunport.cc +++ b/third_party/libjingle/source/talk/p2p/base/stunport.cc @@ -102,8 +102,8 @@ class StunPortBindingRequest : public StunRequest { virtual void OnTimeout() { LOG(LS_ERROR) << "Binding request timed out from " - << port_->GetLocalAddress().ToString() - << " (" << port_->network()->name() << ")"; + << port_->socket_->GetLocalAddress(NULL).ToString() + << " (" << port_->network()->name() << ")"; port_->SignalAddressError(port_); @@ -124,26 +124,27 @@ class StunPortBindingRequest : public StunRequest { const std::string STUN_PORT_TYPE("stun"); -StunPort::StunPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, +StunPort::StunPort(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, talk_base::Network* network, + uint32 ip, int min_port, int max_port, const talk_base::SocketAddress& server_addr) - : Port(thread, STUN_PORT_TYPE, factory, network), - server_addr_(server_addr), requests_(thread), socket_(NULL), error_(0), + : Port(thread, STUN_PORT_TYPE, factory, network, ip, min_port, max_port), + server_addr_(server_addr), + requests_(thread), + socket_(NULL), + error_(0), resolver_(NULL) { requests_.SignalSendPacket.connect(this, &StunPort::OnSendPacket); } -bool StunPort::Init(const talk_base::SocketAddress& local_addr) { - socket_ = CreatePacketSocket(PROTO_UDP); +bool StunPort::Init() { + socket_ = factory_->CreateUdpSocket( + talk_base::SocketAddress(ip_, 0), min_port_, max_port_); if (!socket_) { LOG_J(LS_WARNING, this) << "UDP socket creation failed"; return false; } - if (socket_->Bind(local_addr) < 0) { - LOG_J(LS_WARNING, this) << "UDP bind failed with error " - << socket_->GetError(); - return false; - } socket_->SignalReadPacket.connect(this, &StunPort::OnReadPacket); return true; } @@ -200,14 +201,20 @@ int StunPort::GetError() { return error_; } -void StunPort::OnReadPacket( - const char* data, size_t size, const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket) { +void StunPort::OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr) { ASSERT(socket == socket_); - // Look for a response to a binding request. - if (requests_.CheckResponse(data, size)) + // Look for a response from the STUN server. + // Even if the response doesn't match one of our outstanding requests, we + // will eat it because it might be a response to a retransmitted packet, and + // we already cleared the request when we got the first response. + ASSERT(!server_addr_.IsUnresolved()); + if (remote_addr == server_addr_ || remote_addr == server_addr2_) { + requests_.CheckResponse(data, size); return; + } if (Connection* conn = GetConnection(remote_addr)) { conn->OnReadPacket(data, size); diff --git a/third_party/libjingle/source/talk/p2p/base/stunport.h b/third_party/libjingle/source/talk/p2p/base/stunport.h index 569e229..0b41724 100644 --- a/third_party/libjingle/source/talk/p2p/base/stunport.h +++ b/third_party/libjingle/source/talk/p2p/base/stunport.h @@ -29,7 +29,8 @@ #define TALK_P2P_BASE_STUNPORT_H_ #include <string> -#include "talk/base/asyncudpsocket.h" + +#include "talk/base/asyncpacketsocket.h" #include "talk/p2p/base/udpport.h" #include "talk/p2p/base/stunrequest.h" @@ -46,27 +47,20 @@ extern const std::string STUN_PORT_TYPE; class StunPort : public Port { public: static StunPort* Create(talk_base::Thread* thread, - talk_base::SocketFactory* factory, + talk_base::PacketSocketFactory* factory, talk_base::Network* network, - const talk_base::SocketAddress& local_addr, + uint32 ip, int min_port, int max_port, const talk_base::SocketAddress& server_addr) { - StunPort* port = new StunPort(thread, factory, network, server_addr); - if (!port->Init(local_addr)) { + StunPort* port = new StunPort(thread, factory, network, + ip, min_port, max_port, server_addr); + if (!port->Init()) { delete port; port = NULL; } return port; } - StunPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, - talk_base::Network* network, - const talk_base::SocketAddress& server_addr); - bool Init(const talk_base::SocketAddress& local_addr); virtual ~StunPort(); - talk_base::SocketAddress GetLocalAddress() const { - return socket_->GetLocalAddress(); - } - const talk_base::SocketAddress& server_addr() const { return server_addr_; } void set_server_addr(const talk_base::SocketAddress& addr) { server_addr_ = addr; @@ -89,13 +83,17 @@ class StunPort : public Port { virtual int GetError(); protected: + StunPort(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, + talk_base::Network* network, uint32 ip, int min_port, int max_port, + const talk_base::SocketAddress& server_addr); + bool Init(); + virtual int SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, bool payload); - void OnReadPacket( - const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket); + void OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr); private: // DNS resolution of the STUN server. diff --git a/third_party/libjingle/source/talk/p2p/base/stunserver.cc b/third_party/libjingle/source/talk/p2p/base/stunserver.cc index 1ff02ee..a0686a1 100644 --- a/third_party/libjingle/source/talk/p2p/base/stunserver.cc +++ b/third_party/libjingle/source/talk/p2p/base/stunserver.cc @@ -44,8 +44,8 @@ StunServer::~StunServer() { } void StunServer::OnPacket( - const char* buf, size_t size, const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket) { + talk_base::AsyncPacketSocket* socket, const char* buf, size_t size, + const talk_base::SocketAddress& remote_addr) { // TODO: If appropriate, look for the magic cookie before parsing. @@ -97,7 +97,12 @@ void StunServer::OnBindingRequest( response.AddAttribute(mapped_addr); // Tell the user the address that we are sending the response from. - talk_base::SocketAddress local_addr = socket_->GetLocalAddress(); + // This method should not be called if socket address is not + // allocated yet. + bool allocated; + talk_base::SocketAddress local_addr = socket_->GetLocalAddress(&allocated); + ASSERT(allocated); + StunAddressAttribute* source_addr = StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS); source_addr->SetFamily(1); diff --git a/third_party/libjingle/source/talk/p2p/base/stunserver.h b/third_party/libjingle/source/talk/p2p/base/stunserver.h index 83ac174..6e51ad1 100644 --- a/third_party/libjingle/source/talk/p2p/base/stunserver.h +++ b/third_party/libjingle/source/talk/p2p/base/stunserver.h @@ -46,8 +46,8 @@ class StunServer : public sigslot::has_slots<> { protected: // Slot for AsyncSocket.PacketRead: void OnPacket( - const char* buf, size_t size, const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket); + talk_base::AsyncPacketSocket* socket, const char* buf, size_t size, + const talk_base::SocketAddress& remote_addr); // Handlers for the different types of STUN/TURN requests: void OnBindingRequest(StunMessage* msg, diff --git a/third_party/libjingle/source/talk/p2p/base/stunserver_main.cc b/third_party/libjingle/source/talk/p2p/base/stunserver_main.cc index e4a2df6..e697728 100644 --- a/third_party/libjingle/source/talk/p2p/base/stunserver_main.cc +++ b/third_party/libjingle/source/talk/p2p/base/stunserver_main.cc @@ -52,9 +52,9 @@ int main(int argc, char* argv[]) { talk_base::Thread *pthMain = talk_base::Thread::Current(); talk_base::AsyncUDPSocket* server_socket = - talk_base::CreateAsyncUDPSocket(pthMain->socketserver()); - if (server_socket->Bind(server_addr) < 0) { - std::cerr << "bind: " << std::strerror(errno) << std::endl; + talk_base::AsyncUDPSocket::Create(pthMain->socketserver(), server_addr); + if (!server_socket) { + std::cerr << "Failed to create a UDP socket" << std::endl; return 1; } diff --git a/third_party/libjingle/source/talk/p2p/base/tcpport.cc b/third_party/libjingle/source/talk/p2p/base/tcpport.cc index 806c258..41e04d2 100644 --- a/third_party/libjingle/source/talk/p2p/base/tcpport.cc +++ b/third_party/libjingle/source/talk/p2p/base/tcpport.cc @@ -25,10 +25,6 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if defined(_MSC_VER) && _MSC_VER < 1300 -#pragma warning(disable:4786) -#endif - #include "talk/p2p/base/tcpport.h" #include "talk/base/common.h" @@ -37,30 +33,28 @@ namespace cricket { -TCPPort::TCPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, - talk_base::Network* network, - const talk_base::SocketAddress& address, - bool allow_listen) - : Port(thread, LOCAL_PORT_TYPE, factory, network), address_(address), - incoming_only_(address_.port() != 0), allow_listen_(allow_listen), - socket_(NULL), error_(0) { +TCPPort::TCPPort(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, + talk_base::Network* network, uint32 ip, + int min_port, int max_port, bool allow_listen) + : Port(thread, LOCAL_PORT_TYPE, factory, network, ip, min_port, max_port), + incoming_only_(false), + allow_listen_(allow_listen), + socket_(NULL), + error_(0) { } bool TCPPort::Init() { - // We don't use CreatePacketSocket here since we're creating a listen socket. - // However we will treat failure to create or bind a TCP socket as fatal. - // This should never happen. - socket_ = factory_->CreateAsyncSocket(SOCK_STREAM); + // Treat failure to create or bind a TCP socket as fatal. This + // should never happen. + socket_ = factory_->CreateServerTcpSocket( + talk_base::SocketAddress(ip_, 0), min_port_, max_port_, allow_listen_, + false /* ssl */); if (!socket_) { LOG_J(LS_ERROR, this) << "TCP socket creation failed."; return false; } - if (socket_->Bind(address_) < 0) { - LOG_J(LS_ERROR, this) << "TCP bind failed with error " - << socket_->GetError(); - return false; - } - socket_->SignalReadEvent.connect(this, &TCPPort::OnAcceptEvent); + socket_->SignalNewConnection.connect(this, &TCPPort::OnNewConnection); return true; } @@ -87,8 +81,8 @@ Connection* TCPPort::CreateConnection(const Candidate& address, return NULL; TCPConnection* conn = NULL; - if (talk_base::AsyncTCPSocket * socket - = GetIncoming(address.address(), true)) { + if (talk_base::AsyncPacketSocket* socket = + GetIncoming(address.address(), true)) { socket->SignalReadPacket.disconnect(this); conn = new TCPConnection(this, address, socket); } else { @@ -101,18 +95,21 @@ Connection* TCPPort::CreateConnection(const Candidate& address, void TCPPort::PrepareAddress() { if (!allow_listen_) { LOG_J(LS_INFO, this) << "Not listening due to firewall restrictions."; - } else if (socket_->Listen(5) < 0) { - LOG_J(LS_WARNING, this) << "TCP listen failed with error " - << socket_->GetError(); } // Note: We still add the address, since otherwise the remote side won't // recognize our incoming TCP connections. - AddAddress(socket_->GetLocalAddress(), "tcp", true); + bool allocated; + talk_base::SocketAddress address = socket_->GetLocalAddress(&allocated); + if (allocated) { + AddAddress(address, "tcp", true); + } else { + socket_->SignalAddressReady.connect(this, &TCPPort::OnAddresReady); + } } int TCPPort::SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, bool payload) { - talk_base::AsyncTCPSocket * socket = NULL; + talk_base::AsyncPacketSocket * socket = NULL; if (TCPConnection * conn = static_cast<TCPConnection*>(GetConnection(addr))) { socket = conn->socket(); } else { @@ -141,31 +138,23 @@ int TCPPort::GetError() { return error_; } -void TCPPort::OnAcceptEvent(talk_base::AsyncSocket* socket) { +void TCPPort::OnNewConnection(talk_base::AsyncPacketSocket* socket, + talk_base::AsyncPacketSocket* new_socket) { ASSERT(socket == socket_); Incoming incoming; - talk_base::AsyncSocket* newsocket = socket->Accept(&incoming.addr); - if (!newsocket) { - // TODO: Do something better like forwarding the error to the user. - LOG_J(LS_ERROR, this) << "TCP accept failed with error " - << socket_->GetError(); - return; - } - incoming.socket = new talk_base::AsyncTCPSocket(newsocket); + incoming.addr = new_socket->GetRemoteAddress(); + incoming.socket = new_socket; incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket); LOG_J(LS_VERBOSE, this) << "Accepted connection from " << incoming.addr.ToString(); incoming_.push_back(incoming); - - // Prime a read event in case data is waiting - newsocket->SignalReadEvent(newsocket); } -talk_base::AsyncTCPSocket* TCPPort::GetIncoming( +talk_base::AsyncPacketSocket* TCPPort::GetIncoming( const talk_base::SocketAddress& addr, bool remove) { - talk_base::AsyncTCPSocket* socket = NULL; + talk_base::AsyncPacketSocket* socket = NULL; for (std::list<Incoming>::iterator it = incoming_.begin(); it != incoming_.end(); ++it) { if (it->addr == addr) { @@ -178,34 +167,46 @@ talk_base::AsyncTCPSocket* TCPPort::GetIncoming( return socket; } -void TCPPort::OnReadPacket(const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket) { +void TCPPort::OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr) { Port::OnReadPacket(data, size, remote_addr); } +void TCPPort::OnAddresReady(talk_base::AsyncPacketSocket* socket, + const talk_base::SocketAddress& address) { + AddAddress(address, "tcp", true); +} + TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate, - talk_base::AsyncTCPSocket* socket) + talk_base::AsyncPacketSocket* socket) : Connection(port, 0, candidate), socket_(socket), error_(0) { bool outgoing = (socket_ == NULL); if (outgoing) { - // TODO: Handle failures here (unlikely since TCP) - socket_ = static_cast<talk_base::AsyncTCPSocket*>(port->CreatePacketSocket( - (candidate.protocol() == "ssltcp") ? PROTO_SSLTCP : PROTO_TCP)); + // TODO: Handle failures here (unlikely since TCP). + + socket_ = port->socket_factory()->CreateClientTcpSocket( + talk_base::SocketAddress(port_->network()->ip(), 0), + candidate.address(), port->proxy(), port->user_agent(), + candidate.protocol() == "ssltcp"); + if (socket_) { + LOG_J(LS_VERBOSE, this) << "Connecting from " + << socket_->GetLocalAddress(NULL).ToString() + << " to " << candidate.address().ToString(); + set_connected(false); + socket_->SignalConnect.connect(this, &TCPConnection::OnConnect); + } else { + LOG_J(LS_WARNING, this) << "Failed to create connection to " + << candidate.address().ToString(); + } } else { - // Incoming connections should match the network address - ASSERT(socket_->GetLocalAddress().EqualIPs(port->address_)); + // Incoming connections should match the network address. + ASSERT(socket_->GetLocalAddress(NULL).ip() == port->ip_); } - socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket); - socket_->SignalClose.connect(this, &TCPConnection::OnClose); - if (outgoing) { - set_connected(false); - talk_base::SocketAddress local_address(port->address_.ip(), 0); - socket_->SignalConnect.connect(this, &TCPConnection::OnConnect); - socket_->Bind(local_address); - socket_->Connect(candidate.address()); - LOG_J(LS_VERBOSE, this) << "Connecting from " << local_address.ToString() - << " to " << candidate.address().ToString(); + + if (socket_) { + socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket); + socket_->SignalClose.connect(this, &TCPConnection::OnClose); } } @@ -214,6 +215,11 @@ TCPConnection::~TCPConnection() { } int TCPConnection::Send(const void* data, size_t size) { + if (!socket_) { + error_ = ENOTCONN; + return SOCKET_ERROR; + } + if (write_state() != STATE_WRITABLE) { // TODO: Should STATE_WRITE_TIMEOUT return a non-blocking error? error_ = EWOULDBLOCK; @@ -232,23 +238,23 @@ int TCPConnection::GetError() { return error_; } -void TCPConnection::OnConnect(talk_base::AsyncTCPSocket* socket) { +void TCPConnection::OnConnect(talk_base::AsyncPacketSocket* socket) { ASSERT(socket == socket_); LOG_J(LS_VERBOSE, this) << "Connection established to " << socket->GetRemoteAddress().ToString(); set_connected(true); } -void TCPConnection::OnClose(talk_base::AsyncTCPSocket* socket, int error) { +void TCPConnection::OnClose(talk_base::AsyncPacketSocket* socket, int error) { ASSERT(socket == socket_); LOG_J(LS_VERBOSE, this) << "Connection closed with error " << error; set_connected(false); set_write_state(STATE_WRITE_TIMEOUT); } -void TCPConnection::OnReadPacket(const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket) { +void TCPConnection::OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr) { ASSERT(socket == socket_); Connection::OnReadPacket(data, size); } diff --git a/third_party/libjingle/source/talk/p2p/base/tcpport.h b/third_party/libjingle/source/talk/p2p/base/tcpport.h index d4ce575..0de3561 100644 --- a/third_party/libjingle/source/talk/p2p/base/tcpport.h +++ b/third_party/libjingle/source/talk/p2p/base/tcpport.h @@ -30,7 +30,7 @@ #include <string> #include <list> -#include "talk/base/asynctcpsocket.h" +#include "talk/base/asyncpacketsocket.h" #include "talk/p2p/base/port.h" namespace cricket { @@ -48,19 +48,18 @@ extern const std::string LOCAL_PORT_TYPE; // type of TCP ports class TCPPort : public Port { public: static TCPPort* Create(talk_base::Thread* thread, - talk_base::SocketFactory* factory, + talk_base::PacketSocketFactory* factory, talk_base::Network* network, - const talk_base::SocketAddress& local_addr, + uint32 ip, int min_port, int max_port, bool allow_listen) { - TCPPort* port = new TCPPort(thread, factory, network, local_addr, - allow_listen); + TCPPort* port = new TCPPort(thread, factory, network, + ip, min_port, max_port, allow_listen); if (!port->Init()) { delete port; port = NULL; } return port; } - bool Init(); virtual ~TCPPort(); virtual Connection* CreateConnection(const Candidate& address, @@ -72,36 +71,40 @@ class TCPPort : public Port { virtual int GetError(); protected: - TCPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, - talk_base::Network* network, const talk_base::SocketAddress& address, + TCPPort(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, + talk_base::Network* network, uint32 ip, int min_port, int max_port, bool allow_listen); + bool Init(); // Handles sending using the local TCP socket. virtual int SendTo(const void* data, size_t size, const talk_base::SocketAddress& addr, bool payload); - // Creates TCPConnection for incoming sockets - void OnAcceptEvent(talk_base::AsyncSocket* socket); + // Accepts incoming TCP connection. + void OnNewConnection(talk_base::AsyncPacketSocket* socket, + talk_base::AsyncPacketSocket* new_socket); private: struct Incoming { talk_base::SocketAddress addr; - talk_base::AsyncTCPSocket * socket; + talk_base::AsyncPacketSocket* socket; }; - talk_base::AsyncTCPSocket* GetIncoming(const talk_base::SocketAddress& addr, - bool remove = false); + talk_base::AsyncPacketSocket* GetIncoming( + const talk_base::SocketAddress& addr, bool remove = false); // Receives packet signal from the local TCP Socket. - void OnReadPacket(const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket); + void OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr); + + void OnAddresReady(talk_base::AsyncPacketSocket* socket, + const talk_base::SocketAddress& address); - // Note: use this until Network ips are stable, then use network->ip - talk_base::SocketAddress address_; + // TODO: Is this still needed? bool incoming_only_; bool allow_listen_; - talk_base::AsyncSocket* socket_; + talk_base::AsyncPacketSocket* socket_; int error_; std::list<Incoming> incoming_; @@ -112,22 +115,22 @@ class TCPConnection : public Connection { public: // Connection is outgoing unless socket is specified TCPConnection(TCPPort* port, const Candidate& candidate, - talk_base::AsyncTCPSocket* socket = 0); + talk_base::AsyncPacketSocket* socket = 0); virtual ~TCPConnection(); virtual int Send(const void* data, size_t size); virtual int GetError(); - talk_base::AsyncTCPSocket * socket() { return socket_; } + talk_base::AsyncPacketSocket* socket() { return socket_; } private: - void OnConnect(talk_base::AsyncTCPSocket* socket); - void OnClose(talk_base::AsyncTCPSocket* socket, int error); - void OnReadPacket(const char* data, size_t size, - const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket); + void OnConnect(talk_base::AsyncPacketSocket* socket); + 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); - talk_base::AsyncTCPSocket* socket_; + talk_base::AsyncPacketSocket* socket_; int error_; friend class TCPPort; diff --git a/third_party/libjingle/source/talk/p2p/base/transport.cc b/third_party/libjingle/source/talk/p2p/base/transport.cc index 9d8fc32..dc571d4 100644 --- a/third_party/libjingle/source/talk/p2p/base/transport.cc +++ b/third_party/libjingle/source/talk/p2p/base/transport.cc @@ -28,6 +28,7 @@ #include "talk/p2p/base/transport.h" #include "talk/base/common.h" +#include "talk/base/logging.h" #include "talk/p2p/base/candidate.h" #include "talk/p2p/base/constants.h" #include "talk/p2p/base/sessionmanager.h" @@ -145,11 +146,13 @@ void Transport::DestroyChannel(const std::string& name) { void Transport::DestroyChannel_w(const std::string& name) { ASSERT(worker_thread()->IsCurrent()); + TransportChannelImpl* impl = NULL; { talk_base::CritScope cs(&crit_); ChannelMap::iterator iter = channels_.find(name); - ASSERT(iter != channels_.end()); + if (iter == channels_.end()) + return; impl = iter->second; channels_.erase(iter); } @@ -173,7 +176,7 @@ void Transport::ConnectChannels() { void Transport::ConnectChannels_w() { ASSERT(worker_thread()->IsCurrent()); - if (connect_requested_) + if (connect_requested_ || channels_.empty()) return; connect_requested_ = true; signaling_thread()->Post( @@ -192,6 +195,8 @@ void Transport::OnConnecting_s() { void Transport::DestroyAllChannels() { ASSERT(signaling_thread()->IsCurrent()); worker_thread()->Send(this, MSG_DESTROYALLCHANNELS, NULL); + worker_thread()->Clear(this); + signaling_thread()->Clear(this); destroyed_ = true; } @@ -233,6 +238,8 @@ void Transport::ResetChannels_w() { void Transport::OnSignalingReady() { ASSERT(signaling_thread()->IsCurrent()); + if (destroyed_) return; + worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL); // Notify the subclass. @@ -283,7 +290,13 @@ void Transport::OnRemoteCandidates(const std::vector<Candidate>& candidates) { void Transport::OnRemoteCandidate(const Candidate& candidate) { ASSERT(signaling_thread()->IsCurrent()); - ASSERT(HasChannel(candidate.name())); + if (destroyed_) return; + if (!HasChannel(candidate.name())) { + LOG(LS_WARNING) << "Ignoring candidate for unknown channel " + << candidate.name(); + return; + } + // new candidate deleted when params is deleted ChannelParams* params = new ChannelParams(new Candidate(candidate)); ChannelMessage* msg = new ChannelMessage(params); @@ -445,7 +458,7 @@ bool TransportParser::ParseAddress(const buzz::XmlElement* elem, address->SetIP(elem->Attr(address_name)); std::istringstream ist(elem->Attr(port_name)); - int port; + int port = 0; ist >> port; address->SetPort(port); diff --git a/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc b/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc index 405ed76..1be9194 100644 --- a/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc +++ b/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc @@ -75,6 +75,13 @@ int TransportChannelProxy::GetError() { return impl_->GetError(); } +P2PTransportChannel* TransportChannelProxy::GetP2PChannel() { + if (impl_) { + return impl_->GetP2PChannel(); + } + return NULL; +} + void TransportChannelProxy::OnReadableState(TransportChannel* channel) { ASSERT(channel == impl_); set_readable(impl_->readable()); diff --git a/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h b/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h index 2184477..4601185 100644 --- a/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h +++ b/third_party/libjingle/source/talk/p2p/base/transportchannelproxy.h @@ -55,6 +55,7 @@ class TransportChannelProxy: public TransportChannel { virtual int SendPacket(const char *data, size_t len); virtual int SetOption(talk_base::Socket::Option opt, int value); virtual int GetError(); + virtual P2PTransportChannel* GetP2PChannel(); private: typedef std::pair<talk_base::Socket::Option, int> OptionPair; diff --git a/third_party/libjingle/source/talk/p2p/base/udpport.cc b/third_party/libjingle/source/talk/p2p/base/udpport.cc index a34c17e..28cf5d2 100644 --- a/third_party/libjingle/source/talk/p2p/base/udpport.cc +++ b/third_party/libjingle/source/talk/p2p/base/udpport.cc @@ -27,6 +27,7 @@ #include "talk/p2p/base/udpport.h" +#include "talk/base/asyncpacketsocket.h" #include "talk/base/logging.h" #include "talk/p2p/base/common.h" @@ -34,23 +35,22 @@ namespace cricket { const std::string LOCAL_PORT_TYPE("local"); -UDPPort::UDPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, - talk_base::Network* network) - : Port(thread, LOCAL_PORT_TYPE, factory, network), - socket_(NULL), error_(0) { +UDPPort::UDPPort(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, + talk_base::Network* network, + uint32 ip, int min_port, int max_port) + : Port(thread, LOCAL_PORT_TYPE, factory, network, ip, min_port, max_port), + socket_(NULL), + error_(0) { } -bool UDPPort::Init(const talk_base::SocketAddress& local_addr) { - socket_ = CreatePacketSocket(PROTO_UDP); +bool UDPPort::Init() { + socket_ = factory_->CreateUdpSocket( + talk_base::SocketAddress(ip_, 0), min_port_, max_port_); if (!socket_) { LOG_J(LS_WARNING, this) << "UDP socket creation failed"; return false; } - if (socket_->Bind(local_addr) < 0) { - LOG_J(LS_WARNING, this) << "UDP bind failed with error " - << socket_->GetError(); - return false; - } socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacket); return true; } @@ -60,7 +60,13 @@ UDPPort::~UDPPort() { } void UDPPort::PrepareAddress() { - AddAddress(socket_->GetLocalAddress(), "udp", true); + bool allocated; + talk_base::SocketAddress address = socket_->GetLocalAddress(&allocated); + if (allocated) { + AddAddress(address, "udp", true); + } else { + socket_->SignalAddressReady.connect(this, &UDPPort::OnAddresReady); + } } Connection* UDPPort::CreateConnection(const Candidate& address, @@ -80,7 +86,7 @@ int UDPPort::SendTo(const void* data, size_t size, error_ = socket_->GetError(); LOG_J(LS_ERROR, this) << "UDP send of " << size << " bytes failed with error " << error_; - } + } return sent; } @@ -92,9 +98,14 @@ int UDPPort::GetError() { return error_; } +void UDPPort::OnAddresReady(talk_base::AsyncPacketSocket* socket, + const talk_base::SocketAddress& address) { + AddAddress(address, "udp", true); +} + void UDPPort::OnReadPacket( - const char* data, size_t size, const talk_base::SocketAddress& remote_addr, - talk_base::AsyncPacketSocket* socket) { + talk_base::AsyncPacketSocket* socket, const char* data, size_t size, + const talk_base::SocketAddress& remote_addr) { ASSERT(socket == socket_); if (Connection* conn = GetConnection(remote_addr)) { conn->OnReadPacket(data, size); diff --git a/third_party/libjingle/source/talk/p2p/base/udpport.h b/third_party/libjingle/source/talk/p2p/base/udpport.h index 7fcad6c..f7588a6 100644 --- a/third_party/libjingle/source/talk/p2p/base/udpport.h +++ b/third_party/libjingle/source/talk/p2p/base/udpport.h @@ -30,7 +30,6 @@ #include <string> -#include "talk/base/asyncudpsocket.h" #include "talk/p2p/base/port.h" namespace talk_base { @@ -47,19 +46,17 @@ extern const std::string LOCAL_PORT_TYPE; // type of UDP ports class UDPPort : public Port { public: static UDPPort* Create(talk_base::Thread* thread, - talk_base::SocketFactory* factory, + talk_base::PacketSocketFactory* factory, talk_base::Network* network, - const talk_base::SocketAddress& local_addr) { - UDPPort* port = new UDPPort(thread, factory, network); - if (!port->Init(local_addr)) { + uint32 ip, int min_port, int max_port) { + UDPPort* port = new UDPPort(thread, factory, network, + ip, min_port, max_port); + if (!port->Init()) { delete port; port = NULL; } return port; } - UDPPort(talk_base::Thread* thread, talk_base::SocketFactory* factory, - talk_base::Network* network); - bool Init(const talk_base::SocketAddress& local_addr); virtual ~UDPPort(); virtual void PrepareAddress(); @@ -70,14 +67,21 @@ class UDPPort : public Port { virtual int GetError(); protected: + UDPPort(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, + talk_base::Network* network, uint32 ip, int min_port, int max_port); + bool Init(); + // Handles sending using the local UDP socket. virtual int SendTo(const void* data, size_t size, const talk_base::SocketAddress& remote_addr, bool payload); + void OnAddresReady(talk_base::AsyncPacketSocket* socket, + const talk_base::SocketAddress& address); + // 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, - talk_base::AsyncPacketSocket* socket); + void OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr); private: talk_base::AsyncPacketSocket* socket_; diff --git a/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc b/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc index 3762b34..c023e9e 100644 --- a/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc +++ b/third_party/libjingle/source/talk/p2p/client/basicportallocator.cc @@ -166,17 +166,24 @@ class AllocationSequence : public talk_base::MessageHandler { // BasicPortAllocator BasicPortAllocator::BasicPortAllocator( - talk_base::NetworkManager* network_manager) - : network_manager_(network_manager), best_writable_phase_(-1) { + talk_base::NetworkManager* network_manager, + talk_base::PacketSocketFactory* socket_factory) + : network_manager_(network_manager), + socket_factory_(socket_factory), + best_writable_phase_(-1), + allow_tcp_listen_(true) { + ASSERT(socket_factory_ != NULL); } BasicPortAllocator::BasicPortAllocator( talk_base::NetworkManager* network_manager, + talk_base::PacketSocketFactory* socket_factory, const talk_base::SocketAddress& stun_address, const talk_base::SocketAddress& relay_address_udp, const talk_base::SocketAddress& relay_address_tcp, const talk_base::SocketAddress& relay_address_ssl) : network_manager_(network_manager), + socket_factory_(socket_factory), stun_address_(stun_address), relay_address_udp_(relay_address_udp), relay_address_tcp_(relay_address_tcp), @@ -656,8 +663,11 @@ void AllocationSequence::CreateUDPPorts() { return; } - Port* port = UDPPort::Create(session_->network_thread(), NULL, network_, - talk_base::SocketAddress(ip_, 0)); + Port* port = UDPPort::Create(session_->network_thread(), + session_->allocator()->socket_factory(), + network_, ip_, + session_->allocator()->min_port(), + session_->allocator()->max_port()); if (port) session_->AddAllocatedPort(port, this, PREF_LOCAL_UDP); } @@ -668,8 +678,11 @@ void AllocationSequence::CreateTCPPorts() { return; } - Port* port = TCPPort::Create(session_->network_thread(), NULL, network_, - talk_base::SocketAddress(ip_, 0), + Port* port = TCPPort::Create(session_->network_thread(), + session_->allocator()->socket_factory(), + network_, ip_, + session_->allocator()->min_port(), + session_->allocator()->max_port(), session_->allocator()->allow_tcp_listen()); if (port) session_->AddAllocatedPort(port, this, PREF_LOCAL_TCP); @@ -690,8 +703,11 @@ void AllocationSequence::CreateStunPorts() { return; } - Port* port = StunPort::Create(session_->network_thread(), NULL, network_, - talk_base::SocketAddress(ip_, 0), + Port* port = StunPort::Create(session_->network_thread(), + session_->allocator()->socket_factory(), + network_, ip_, + session_->allocator()->min_port(), + session_->allocator()->max_port(), config_->stun_address); if (port) session_->AddAllocatedPort(port, this, PREF_LOCAL_STUN); @@ -715,9 +731,11 @@ void AllocationSequence::CreateRelayPorts() { PortConfiguration::RelayList::const_iterator relay; for (relay = config_->relays.begin(); relay != config_->relays.end(); ++relay) { - RelayPort* port = RelayPort::Create(session_->network_thread(), NULL, - network_, - talk_base::SocketAddress(ip_, 0), + RelayPort* port = RelayPort::Create(session_->network_thread(), + session_->allocator()->socket_factory(), + network_, ip_, + session_->allocator()->min_port(), + session_->allocator()->max_port(), config_->username, config_->password, config_->magic_cookie); if (port) { diff --git a/third_party/libjingle/source/talk/p2p/client/basicportallocator.h b/third_party/libjingle/source/talk/p2p/client/basicportallocator.h index 0891e86..b2c1b8c 100644 --- a/third_party/libjingle/source/talk/p2p/client/basicportallocator.h +++ b/third_party/libjingle/source/talk/p2p/client/basicportallocator.h @@ -40,8 +40,10 @@ namespace cricket { class BasicPortAllocator : public PortAllocator { public: - explicit BasicPortAllocator(talk_base::NetworkManager* network_manager); BasicPortAllocator(talk_base::NetworkManager* network_manager, + talk_base::PacketSocketFactory* socket_factory); + BasicPortAllocator(talk_base::NetworkManager* network_manager, + talk_base::PacketSocketFactory* socket_factory, const talk_base::SocketAddress& stun_server, const talk_base::SocketAddress& relay_server_udp, const talk_base::SocketAddress& relay_server_tcp, @@ -50,6 +52,8 @@ class BasicPortAllocator : public PortAllocator { talk_base::NetworkManager* network_manager() { return network_manager_; } + talk_base::PacketSocketFactory* socket_factory() { return socket_factory_; } + const talk_base::SocketAddress& stun_address() const { return stun_address_; } @@ -84,6 +88,7 @@ class BasicPortAllocator : public PortAllocator { private: talk_base::NetworkManager* network_manager_; + talk_base::PacketSocketFactory* socket_factory_; const talk_base::SocketAddress stun_address_; const talk_base::SocketAddress relay_address_udp_; const talk_base::SocketAddress relay_address_tcp_; diff --git a/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc b/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc index c9cce12..4ef0825 100644 --- a/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc +++ b/third_party/libjingle/source/talk/p2p/client/httpportallocator.cc @@ -96,9 +96,11 @@ const int HttpPortAllocator::kNumRetries = 5; const std::string HttpPortAllocator::kCreateSessionURL = "/create_session"; -HttpPortAllocator::HttpPortAllocator(talk_base::NetworkManager* network_manager, - const std::string &user_agent) - : BasicPortAllocator(network_manager), agent_(user_agent) { +HttpPortAllocator::HttpPortAllocator( + talk_base::NetworkManager* network_manager, + talk_base::PacketSocketFactory* socket_factory, + const std::string &user_agent) + : BasicPortAllocator(network_manager, socket_factory), agent_(user_agent) { relay_hosts_.push_back("relay.google.com"); stun_hosts_.push_back( talk_base::SocketAddress("stun.l.google.com", 19302)); diff --git a/third_party/libjingle/source/talk/p2p/client/httpportallocator.h b/third_party/libjingle/source/talk/p2p/client/httpportallocator.h index d695062..478f278 100644 --- a/third_party/libjingle/source/talk/p2p/client/httpportallocator.h +++ b/third_party/libjingle/source/talk/p2p/client/httpportallocator.h @@ -50,6 +50,7 @@ class HttpPortAllocator : public BasicPortAllocator { static const std::string kCreateSessionURL; HttpPortAllocator(talk_base::NetworkManager* network_manager, + talk_base::PacketSocketFactory* socket_factory, const std::string& user_agent); virtual ~HttpPortAllocator(); diff --git a/third_party/libjingle/source/talk/session/phone/channel.cc b/third_party/libjingle/source/talk/session/phone/channel.cc index 76c1cb4..1083ec7 100644 --- a/third_party/libjingle/source/talk/session/phone/channel.cc +++ b/third_party/libjingle/source/talk/session/phone/channel.cc @@ -26,6 +26,8 @@ */ #include "talk/session/phone/channel.h" + +#include "talk/base/buffer.h" #include "talk/base/byteorder.h" #include "talk/base/common.h" #include "talk/base/logging.h" @@ -33,15 +35,58 @@ #include "talk/session/phone/channelmanager.h" #include "talk/session/phone/mediasessionclient.h" #include "talk/session/phone/mediasink.h" +#include "talk/session/phone/rtcpmuxfilter.h" namespace cricket { -static const size_t kMaxPacketLen = 2048; +struct PacketMessageData : public talk_base::MessageData { + talk_base::Buffer packet; +}; + +struct VoiceChannelErrorMessageData : public talk_base::MessageData { + VoiceChannelErrorMessageData(uint32 in_ssrc, + VoiceMediaChannel::Error in_error) + : ssrc(in_ssrc), + error(in_error) {} + uint32 ssrc; + VoiceMediaChannel::Error error; +}; + +struct VideoChannelErrorMessageData : public talk_base::MessageData { + VideoChannelErrorMessageData(uint32 in_ssrc, + VideoMediaChannel::Error in_error) + : ssrc(in_ssrc), + error(in_error) {} + uint32 ssrc; + VideoMediaChannel::Error error; +}; static const char* PacketType(bool rtcp) { return (!rtcp) ? "RTP" : "RTCP"; } +static bool ValidPacket(bool rtcp, const talk_base::Buffer* packet) { + // Check the packet size. We could check the header too if needed. + return (packet && + packet->length() >= (!rtcp ? kMinRtpPacketLen : kMinRtcpPacketLen) && + packet->length() <= kMaxRtpPacketLen); +} + +static uint16 GetRtpSeqNum(const talk_base::Buffer* packet) { + return (packet->length() >= kMinRtpPacketLen) ? + talk_base::GetBE16(packet->data() + 2) : 0; +} + +static uint32 GetRtpSsrc(const talk_base::Buffer* packet) { + return (packet->length() >= kMinRtpPacketLen) ? + talk_base::GetBE32(packet->data() + 8) : 0; +} + +static int GetRtcpType(const talk_base::Buffer* packet) { + return (packet->length() >= kMinRtcpPacketLen) ? + static_cast<int>(packet->data()[1]) : 0; +} + BaseChannel::BaseChannel(talk_base::Thread* thread, MediaEngine* media_engine, MediaChannel* media_channel, BaseSession* session, const std::string& content_name, @@ -74,7 +119,8 @@ BaseChannel::BaseChannel(talk_base::Thread* thread, MediaEngine* media_engine, BaseChannel::~BaseChannel() { ASSERT(worker_thread_ == talk_base::Thread::Current()); StopConnectionMonitor(); - Clear(); + FlushRtcpMessages(); // Send any outstanding RTCP packets. + Clear(); // eats any outstanding messages or packets // We must destroy the media channel before the transport channel, otherwise // the media channel may try to send on the dead transport channel. NULLing // is not an effective strategy since the sends will come on another thread. @@ -160,17 +206,12 @@ void BaseChannel::set_rtcp_transport_channel(TransportChannel* channel) { } } -int BaseChannel::SendPacket(const void *data, size_t len) { - // SendPacket gets called from MediaEngine; send to socket - // MediaEngine will call us on a random thread. The Send operation on the - // socket is special in that it can handle this. - // TODO: Actually, SendPacket cannot handle this. Need to fix ASAP. - - return SendPacket(false, data, len); +bool BaseChannel::SendPacket(talk_base::Buffer* packet) { + return SendPacket(false, packet); } -int BaseChannel::SendRtcp(const void *data, size_t len) { - return SendPacket(true, data, len); +bool BaseChannel::SendRtcp(talk_base::Buffer* packet) { + return SendPacket(true, packet); } int BaseChannel::SetOption(SocketType type, talk_base::Socket::Option opt, @@ -197,19 +238,29 @@ void BaseChannel::OnChannelRead(TransportChannel* channel, // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine ASSERT(worker_thread_ == talk_base::Thread::Current()); + talk_base::Buffer packet(data, len); // When using RTCP multiplexing we might get RTCP packets on the RTP // transport. We feed RTP traffic into the demuxer to determine if it is RTCP. bool rtcp = (channel == rtcp_transport_channel_ || - rtcp_mux_filter_.DemuxRtcp(data, len)); - HandlePacket(rtcp, data, len); -} - -int BaseChannel::SendPacket(bool rtcp, const void* data, size_t len) { - // Protect ourselves against crazy data. - if (len > kMaxPacketLen) { - LOG(LS_ERROR) << "Dropping outgoing large " - << PacketType(rtcp) << " packet, size " << len; - return -1; + rtcp_mux_filter_.DemuxRtcp(packet.data(), packet.length())); + HandlePacket(rtcp, &packet); +} + +bool BaseChannel::SendPacket(bool rtcp, talk_base::Buffer* packet) { + // 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 + // synchronize access to all the pieces of the send path, including + // SRTP and the inner workings of the transport channels. + // The only downside is that we can't return a proper failure code if + // needed. Since UDP is unreliable anyway, this should be a non-issue. + if (talk_base::Thread::Current() != worker_thread_) { + // Avoid a copy by transferring the ownership of the packet data. + int message_id = (!rtcp) ? MSG_RTPPACKET : MSG_RTCPPACKET; + PacketMessageData* data = new PacketMessageData; + packet->TransferTo(&data->packet); + worker_thread_->Post(this, message_id, data); + return true; } // Make sure we have a place to send this packet before doing anything. @@ -218,89 +269,114 @@ int BaseChannel::SendPacket(bool rtcp, const void* data, size_t len) { TransportChannel* channel = (!rtcp || rtcp_mux_filter_.IsActive()) ? transport_channel_ : rtcp_transport_channel_; if (!channel) { - return -1; + return false; } - // Protect if needed. - uint8 work[kMaxPacketLen]; - const char* real_data = static_cast<const char*>(data); - int real_len = len; - if (srtp_filter_.IsActive()) { - bool res; - memcpy(work, data, len); - if (!rtcp) { - res = srtp_filter_.ProtectRtp(work, len, sizeof(work), &real_len); - } else { - res = srtp_filter_.ProtectRtcp(work, len, sizeof(work), &real_len); - } - if (!res) { - LOG(LS_ERROR) << "Failed to protect " - << PacketType(rtcp) << " packet, size " << len; - return -1; - } - - real_data = reinterpret_cast<const char*>(work); + // Protect ourselves against crazy data. + if (!ValidPacket(rtcp, packet)) { + LOG(LS_ERROR) << "Dropping outgoing " << content_name_ << " " + << PacketType(rtcp) << " packet: wrong size=" + << packet->length(); + return false; } + // Push the packet down to the media sink. + // Need to do this before protecting the packet. { talk_base::CritScope cs(&sink_critical_section_); if (sent_media_sink_) { - // Put the sent RTP or RTCP packet to the sink. if (!rtcp) { - sent_media_sink_->OnRtpPacket(real_data, real_len); + sent_media_sink_->OnRtpPacket(packet->data(), packet->length()); } else { - sent_media_sink_->OnRtcpPacket(real_data, real_len); + sent_media_sink_->OnRtcpPacket(packet->data(), packet->length()); } } } - // Bon voyage. Return a number that the caller can understand. - return (channel->SendPacket(real_data, real_len) == real_len) ? len : -1; + // Protect if needed. + if (srtp_filter_.IsActive()) { + bool res; + char* data = packet->data(); + int len = packet->length(); + if (!rtcp) { + res = srtp_filter_.ProtectRtp(data, len, packet->capacity(), &len); + if (!res) { + LOG(LS_ERROR) << "Failed to protect " << content_name_ + << " RTP packet: size=" << len + << ", seqnum=" << GetRtpSeqNum(packet) + << ", SSRC=" << GetRtpSsrc(packet); + return false; + } + } else { + res = srtp_filter_.ProtectRtcp(data, len, packet->capacity(), &len); + if (!res) { + LOG(LS_ERROR) << "Failed to protect " << content_name_ + << " RTCP packet: size=" << len + << ", type=" << GetRtcpType(packet); + return false; + } + } + + // Update the length of the packet now that we've added the auth tag. + packet->SetLength(len); + } + + // Bon voyage. + return (channel->SendPacket(packet->data(), packet->length()) + == static_cast<int>(packet->length())); } -void BaseChannel::HandlePacket(bool rtcp, const char* data, size_t len) { +void BaseChannel::HandlePacket(bool rtcp, talk_base::Buffer* packet) { // Protect ourselvs against crazy data. - if (len > kMaxPacketLen) { - LOG(LS_ERROR) << "Dropping incoming large " - << PacketType(rtcp) << " packet, size " << len; + if (!ValidPacket(rtcp, packet)) { + LOG(LS_ERROR) << "Dropping incoming " << content_name_ << " " + << PacketType(rtcp) << " packet: wrong size=" + << packet->length(); return; } // Unprotect the packet, if needed. - uint8 work[kMaxPacketLen]; - const char* real_data = data; - int real_len = len; if (srtp_filter_.IsActive()) { + char* data = packet->data(); + int len = packet->length(); bool res; - memcpy(work, data, len); if (!rtcp) { - res = srtp_filter_.UnprotectRtp(work, len, &real_len); + res = srtp_filter_.UnprotectRtp(data, len, &len); + if (!res) { + LOG(LS_ERROR) << "Failed to unprotect " << content_name_ + << " RTP packet: size=" << len + << ", seqnum=" << GetRtpSeqNum(packet) + << ", SSRC=" << GetRtpSsrc(packet); + return; + } } else { - res = srtp_filter_.UnprotectRtcp(work, len, &real_len); - } - if (!res) { - LOG(LS_ERROR) << "Failed to unprotect " - << PacketType(rtcp) << " packet, size " << len; - return; + res = srtp_filter_.UnprotectRtcp(data, len, &len); + if (!res) { + LOG(LS_ERROR) << "Failed to unprotect " << content_name_ + << " RTCP packet: size=" << len + << ", type=" << GetRtcpType(packet); + return; + } } - real_data = reinterpret_cast<const char*>(work); + + packet->SetLength(len); } // Push it down to the media channel. if (!rtcp) { - media_channel_->OnPacketReceived(real_data, real_len); + media_channel_->OnPacketReceived(packet); } else { - media_channel_->OnRtcpReceived(real_data, real_len); + media_channel_->OnRtcpReceived(packet); } + // Push it down to the media sink. { talk_base::CritScope cs(&sink_critical_section_); if (received_media_sink_) { - // Put the received RTP or RTCP packet to the sink. if (!rtcp) { - received_media_sink_->OnRtpPacket(real_data, real_len); + received_media_sink_->OnRtpPacket(packet->data(), packet->length()); } else { - received_media_sink_->OnRtcpPacket(real_data, real_len); + received_media_sink_->OnRtcpPacket(packet->data(), packet->length()); } } } @@ -308,32 +384,34 @@ void BaseChannel::HandlePacket(bool rtcp, const char* data, size_t len) { void BaseChannel::OnSessionState(BaseSession* session, BaseSession::State state) { - // TODO: tear down the call via session->SetError() if the - // SetXXXXDescription calls fail. const MediaContentDescription* content = NULL; switch (state) { case Session::STATE_SENTINITIATE: content = GetFirstContent(session->local_description()); - if (content) { - SetLocalContent(content, CA_OFFER); + if (content && !SetLocalContent(content, CA_OFFER)) { + LOG(LS_ERROR) << "Failure in SetLocalContent with CA_OFFER"; + session->SetError(BaseSession::ERROR_CONTENT); } break; case Session::STATE_SENTACCEPT: content = GetFirstContent(session->local_description()); - if (content) { - SetLocalContent(content, CA_ANSWER); + if (content && !SetLocalContent(content, CA_ANSWER)) { + LOG(LS_ERROR) << "Failure in SetLocalContent with CA_ANSWER"; + session->SetError(BaseSession::ERROR_CONTENT); } break; case Session::STATE_RECEIVEDINITIATE: content = GetFirstContent(session->remote_description()); - if (content) { - SetRemoteContent(content, CA_OFFER); + if (content && !SetRemoteContent(content, CA_OFFER)) { + LOG(LS_ERROR) << "Failure in SetRemoteContent with CA_OFFER"; + session->SetError(BaseSession::ERROR_CONTENT); } break; case Session::STATE_RECEIVEDACCEPT: content = GetFirstContent(session->remote_description()); - if (content) { - SetRemoteContent(content, CA_ANSWER); + if (content && !SetRemoteContent(content, CA_ANSWER)) { + LOG(LS_ERROR) << "Failure in SetRemoteContent with CA_ANSWER"; + session->SetError(BaseSession::ERROR_CONTENT); } break; default: @@ -493,6 +571,14 @@ void BaseChannel::OnMessage(talk_base::Message *pmsg) { data->result = SetMaxSendBandwidth_w(data->value); break; } + + case MSG_RTPPACKET: + case MSG_RTCPPACKET: { + PacketMessageData* data = static_cast<PacketMessageData*>(pmsg->pdata); + SendPacket(pmsg->message_id == MSG_RTCPPACKET, &data->packet); + delete data; // because it is Posted + break; + } } } @@ -513,6 +599,18 @@ void BaseChannel::Clear(uint32 id, talk_base::MessageList* removed) { worker_thread_->Clear(this, id, removed); } +void BaseChannel::FlushRtcpMessages() { + // Flush all remaining RTCP messages. This should only be called in + // destructor. + ASSERT(talk_base::Thread::Current() == worker_thread_); + talk_base::MessageList rtcp_messages; + Clear(MSG_RTCPPACKET, &rtcp_messages); + for (talk_base::MessageList::iterator it = rtcp_messages.begin(); + it != rtcp_messages.end(); ++it) { + Send(MSG_RTCPPACKET, it->pdata); + } +} + VoiceChannel::VoiceChannel(talk_base::Thread* thread, MediaEngine* media_engine, VoiceMediaChannel* media_channel, @@ -528,6 +626,9 @@ VoiceChannel::VoiceChannel(talk_base::Thread* thread, // Can't go in BaseChannel because certain session states will // trigger pure virtual functions, such as GetFirstContent(). OnSessionState(session, session->state()); + + media_channel->SignalMediaError.connect( + this, &VoiceChannel::OnVoiceChannelError); } VoiceChannel::~VoiceChannel() { @@ -627,8 +728,10 @@ void VoiceChannel::OnChannelRead(TransportChannel* channel, // If we were playing out our local ringback, make sure it is stopped to // prevent it from interfering with the incoming media. if (!received_media_) { - received_media_ = false; - PlayRingbackTone_w(false, false); + if (!PlayRingbackTone_w(false, false)) { + LOG(LS_ERROR) << "Failed to stop ringback tone."; + SendLastMediaError(); + } } } @@ -636,13 +739,19 @@ void VoiceChannel::ChangeState() { // render incoming data if we are the active call // we receive data on the default channel and multiplexed streams bool recv = enabled(); - media_channel()->SetPlayout(recv); + if (!media_channel()->SetPlayout(recv)) { + SendLastMediaError(); + } // send outgoing data if we are the active call, have the // remote party's codec, and have a writable transport // we only send data on the default channel bool send = enabled() && has_codec() && writable(); - media_channel()->SetSend(send ? SEND_MICROPHONE : SEND_NOTHING); + SendFlags send_flag = send ? SEND_MICROPHONE : SEND_NOTHING; + if (!media_channel()->SetSend(send_flag)) { + LOG(LS_ERROR) << "Failed to SetSend " << send_flag << " on voice channel"; + SendLastMediaError(); + } LOG(LS_INFO) << "Changing voice state, recv=" << recv << " send=" << send; } @@ -704,7 +813,10 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, ret = media_channel()->SetSendCodecs(audio->codecs()); } - int audio_options = audio->conference_mode() ? OPT_CONFERENCE : 0; + int audio_options = 0; + if (audio->conference_mode()) { + audio_options |= OPT_CONFERENCE; + } if (!media_channel()->SetOptions(audio_options)) { // Log an error on failure, but don't abort the call. LOG(LS_ERROR) << "Failed to set voice channel options"; @@ -785,6 +897,13 @@ void VoiceChannel::OnMessage(talk_base::Message *pmsg) { data->result = PressDTMF_w(data->digit, data->playout); break; } + case MSG_CHANNEL_ERROR: { + VoiceChannelErrorMessageData* data = + static_cast<VoiceChannelErrorMessageData*>(pmsg->pdata); + SignalMediaError(this, data->ssrc, data->error); + delete data; + break; + } default: BaseChannel::OnMessage(pmsg); @@ -808,6 +927,13 @@ void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor* monitor, SignalAudioMonitor(this, info); } +void VoiceChannel::OnVoiceChannelError( + uint32 ssrc, VoiceMediaChannel::Error error) { + VoiceChannelErrorMessageData *data = new VoiceChannelErrorMessageData( + ssrc, error); + signaling_thread()->Post(this, MSG_CHANNEL_ERROR, data); +} + VideoChannel::VideoChannel(talk_base::Thread* thread, MediaEngine* media_engine, VideoMediaChannel* media_channel, @@ -826,6 +952,15 @@ VideoChannel::VideoChannel(talk_base::Thread* thread, // trigger pure virtual functions, such as GetFirstContent() OnSessionState(session, session->state()); + media_channel->SignalMediaError.connect( + this, &VideoChannel::OnVideoChannelError); +} + +void VoiceChannel::SendLastMediaError() { + uint32 ssrc; + VoiceMediaChannel::Error error; + media_channel()->GetLastMediaError(&ssrc, &error); + SignalMediaError(this, ssrc, error); } VideoChannel::~VideoChannel() { @@ -861,13 +996,19 @@ void VideoChannel::ChangeState() { // render incoming data if we are the active call // we receive data on the default channel and multiplexed streams bool recv = enabled(); - media_channel()->SetRender(recv); + if (!media_channel()->SetRender(recv)) { + LOG(LS_ERROR) << "Failed to SetRender on video channel"; + // TODO: Report error back to server. + } // send outgoing data if we are the active call, have the // remote party's codec, and have a writable transport // we only send data on the default channel bool send = enabled() && has_codec() && writable(); - media_channel()->SetSend(send); + if (!media_channel()->SetSend(send)) { + LOG(LS_ERROR) << "Failed to SetSend on video channel"; + // TODO: Report error back to server. + } LOG(LS_INFO) << "Changing video state, recv=" << recv << " send=" << send; } @@ -988,10 +1129,16 @@ void VideoChannel::OnMessage(talk_base::Message *pmsg) { case MSG_REQUESTINTRAFRAME: RequestIntraFrame_w(); break; - - default: - BaseChannel::OnMessage(pmsg); - break; + case MSG_CHANNEL_ERROR: { + const VideoChannelErrorMessageData* data = + static_cast<VideoChannelErrorMessageData*>(pmsg->pdata); + SignalMediaError(this, data->ssrc, data->error); + delete data; + break; + } + default: + BaseChannel::OnMessage(pmsg); + break; } } @@ -1007,64 +1154,11 @@ void VideoChannel::OnMediaMonitorUpdate( } -// TODO: Move to own file in a future CL. -// Leaving here for now to avoid having to mess with the Mac build. -RtcpMuxFilter::RtcpMuxFilter() : state_(ST_INIT), offer_enable_(false) { -} - -bool RtcpMuxFilter::IsActive() const { - // We can receive muxed media prior to the accept, so we have to be able to - // deal with that. - return (state_ == ST_SENTOFFER || state_ == ST_ACTIVE); -} - -bool RtcpMuxFilter::SetOffer(bool offer_enable, ContentSource source) { - bool ret = false; - if (state_ == ST_INIT) { - offer_enable_ = offer_enable; - state_ = (source == CS_LOCAL) ? ST_SENTOFFER : ST_RECEIVEDOFFER; - ret = true; - } else { - LOG(LS_ERROR) << "Invalid state for RTCP mux offer"; - } - return ret; -} - -bool RtcpMuxFilter::SetAnswer(bool answer_enable, ContentSource source) { - bool ret = false; - if ((state_ == ST_SENTOFFER && source == CS_REMOTE) || - (state_ == ST_RECEIVEDOFFER && source == CS_LOCAL)) { - if (offer_enable_) { - state_ = (answer_enable) ? ST_ACTIVE : ST_INIT; - ret = true; - } else { - // If the offer didn't specify RTCP mux, the answer shouldn't either. - if (!answer_enable) { - ret = true; - state_ = ST_INIT; - } else { - LOG(LS_WARNING) << "Invalid parameters in RTCP mux answer"; - } - } - } else { - LOG(LS_ERROR) << "Invalid state for RTCP mux answer"; - } - return ret; -} - -bool RtcpMuxFilter::DemuxRtcp(const char* data, int len) { - // If we're muxing RTP/RTCP, we must inspect each packet delivered and - // determine whether it is RTP or RTCP. We do so by checking the packet type, - // and assuming RTP if type is 0-63 or 96-127. For additional details, see - // http://tools.ietf.org/html/draft-ietf-avt-rtp-and-rtcp-mux-07. - // Note that if we offer RTCP mux, we may receive muxed RTCP before we - // receive the answer, so we operate in that state too. - if (!IsActive()) { - return false; - } - - int type = (len >= 2) ? (static_cast<uint8>(data[1]) & 0x7F) : 0; - return (type >= 64 && type < 96); +void VideoChannel::OnVideoChannelError(uint32 ssrc, + VideoMediaChannel::Error error) { + VideoChannelErrorMessageData* data = new VideoChannelErrorMessageData( + ssrc, error); + signaling_thread()->Post(this, MSG_CHANNEL_ERROR, data); } } // namespace cricket diff --git a/third_party/libjingle/source/talk/session/phone/channel.h b/third_party/libjingle/source/talk/session/phone/channel.h index b82a4bf..e7bee83 100644 --- a/third_party/libjingle/source/talk/session/phone/channel.h +++ b/third_party/libjingle/source/talk/session/phone/channel.h @@ -41,6 +41,7 @@ #include "talk/session/phone/mediaengine.h" #include "talk/session/phone/mediachannel.h" #include "talk/session/phone/mediamonitor.h" +#include "talk/session/phone/rtcpmuxfilter.h" #include "talk/session/phone/srtpfilter.h" namespace cricket { @@ -67,37 +68,14 @@ enum { MSG_SETRTCPCNAME = 18, MSG_SENDINTRAFRAME = 19, MSG_REQUESTINTRAFRAME = 20, -}; - -// TODO: Move to own file. -class RtcpMuxFilter { - public: - RtcpMuxFilter(); - - // Whether the filter is active, i.e. has RTCP mux been properly negotiated. - bool IsActive() const; - - // Specifies whether the offer indicates the use of RTCP mux. - bool SetOffer(bool offer_enable, ContentSource src); - - // Specifies whether the answer indicates the use of RTCP mux. - bool SetAnswer(bool answer_enable, ContentSource src); - - // Determines whether the specified packet is RTCP. - bool DemuxRtcp(const char* data, int len); - - private: - enum State { ST_INIT, ST_SENTOFFER, ST_RECEIVEDOFFER, ST_ACTIVE }; - State state_; - bool offer_enable_; + MSG_RTPPACKET = 22, + MSG_RTCPPACKET = 23, + MSG_CHANNEL_ERROR = 24 }; // BaseChannel contains logic common to voice and video, including // enable/mute, marshaling calls to a worker thread, and // connection and media monitors. -// TODO: Break the dependency on BaseSession. The only thing we need -// it for is to Create/Destroy TransportChannels, and set codecs, both of which -// could be done by the calling class. class BaseChannel : public talk_base::MessageHandler, public sigslot::has_slots<>, public MediaChannel::NetworkInterface { @@ -106,7 +84,7 @@ class BaseChannel MediaChannel* channel, BaseSession* session, const std::string& content_name, TransportChannel* transport_channel); - ~BaseChannel(); + virtual ~BaseChannel(); talk_base::Thread* worker_thread() const { return worker_thread_; } BaseSession* session() const { return session_; } @@ -117,6 +95,7 @@ class BaseChannel TransportChannel* rtcp_transport_channel() const { return rtcp_transport_channel_; } + bool enabled() const { return enabled_; } bool secure() const { return srtp_filter_.IsActive(); } // Channel control @@ -159,7 +138,6 @@ class BaseChannel MediaEngine* media_engine() const { return media_engine_; } virtual MediaChannel* media_channel() const { return media_channel_; } void set_rtcp_transport_channel(TransportChannel* transport); - bool enabled() const { return enabled_; } bool writable() const { return writable_; } bool has_codec() const { return has_codec_; } void set_has_codec(bool has_codec) { has_codec_ = has_codec; } @@ -172,18 +150,19 @@ class BaseChannel talk_base::MessageData *pdata = NULL); void Clear(uint32 id = talk_base::MQID_ANY, talk_base::MessageList* removed = NULL); + void FlushRtcpMessages(); // NetworkInterface implementation, called by MediaEngine - virtual int SendPacket(const void *data, size_t len); - virtual int SendRtcp(const void *data, size_t len); + virtual bool SendPacket(talk_base::Buffer* packet); + virtual bool SendRtcp(talk_base::Buffer* packet); virtual int SetOption(SocketType type, talk_base::Socket::Option o, int val); // From TransportChannel void OnWritableState(TransportChannel* channel); void OnChannelRead(TransportChannel* channel, const char *data, size_t len); - int SendPacket(bool rtcp, const void* data, size_t len); - void HandlePacket(bool rtcp, const char* data, size_t len); + bool SendPacket(bool rtcp, talk_base::Buffer* packet); + void HandlePacket(bool rtcp, talk_base::Buffer* packet); // Setting the send codec based on the remote description. void OnSessionState(BaseSession* session, BaseSession::State state); @@ -313,6 +292,11 @@ class VoiceChannel : public BaseChannel { int GetOutputLevel_w(); void GetActiveStreams_w(AudioInfo::StreamList* actives); + // Signal errors from VoiceMediaChannel. Arguments are: + // ssrc(uint32), and error(VoiceMediaChannel::Error). + sigslot::signal3<VoiceChannel*, uint32, VoiceMediaChannel::Error> + SignalMediaError; + private: struct SetRingbackToneMessageData : public talk_base::MessageData { SetRingbackToneMessageData(const void* b, int l) @@ -368,6 +352,8 @@ class VoiceChannel : public BaseChannel { virtual void OnMediaMonitorUpdate( VoiceMediaChannel *media_channel, const VoiceMediaInfo& info); void OnAudioMonitorUpdate(AudioMonitor *monitor, const AudioInfo& info); + void OnVoiceChannelError(uint32 ssrc, VoiceMediaChannel::Error error); + void SendLastMediaError(); static const int kEarlyMediaTimeout = 1000; bool received_media_; @@ -405,6 +391,9 @@ class VideoChannel : public BaseChannel { bool SendIntraFrame(); bool RequestIntraFrame(); + sigslot::signal3<VideoChannel*, uint32, VideoMediaChannel::Error> + SignalMediaError; + private: // overrides from BaseChannel virtual void ChangeState(); @@ -440,6 +429,8 @@ class VideoChannel : public BaseChannel { SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos); virtual void OnMediaMonitorUpdate( VideoMediaChannel *media_channel, const VideoMediaInfo& info); + void OnVideoChannelError(uint32 ssrc, VideoMediaChannel::Error error); + VoiceChannel *voice_channel_; VideoRenderer *renderer_; talk_base::scoped_ptr<VideoMediaMonitor> media_monitor_; diff --git a/third_party/libjingle/source/talk/session/phone/channelmanager.cc b/third_party/libjingle/source/talk/session/phone/channelmanager.cc index 901a20a..d8c97cd 100644 --- a/third_party/libjingle/source/talk/session/phone/channelmanager.cc +++ b/third_party/libjingle/source/talk/session/phone/channelmanager.cc @@ -39,10 +39,6 @@ #include "talk/base/stringencode.h" #include "talk/session/phone/mediaengine.h" #include "talk/session/phone/soundclip.h" -#ifdef USE_TALK_SOUND -#include "talk/sound/platformsoundsystemfactory.h" -#include "talk/sound/soundsysteminterface.h" -#endif namespace cricket { @@ -134,23 +130,13 @@ struct CaptureParams : public talk_base::MessageData { }; ChannelManager::ChannelManager(talk_base::Thread* worker_thread) - : -#ifdef USE_TALK_SOUND - sound_system_factory_(new PlatformSoundSystemFactory()), -#endif - media_engine_(MediaEngine::Create( -#ifdef USE_TALK_SOUND - sound_system_factory_.get() -#endif - )), - device_manager_(new DeviceManager( -#ifdef USE_TALK_SOUND - sound_system_factory_.get() -#endif - )), + : media_engine_(MediaEngine::Create()), + device_manager_(new DeviceManager()), initialized_(false), main_thread_(talk_base::Thread::Current()), worker_thread_(worker_thread), + audio_in_device_(DeviceManager::kDefaultDeviceName), + audio_out_device_(DeviceManager::kDefaultDeviceName), audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS), local_renderer_(NULL), capturing_(false), @@ -160,15 +146,13 @@ ChannelManager::ChannelManager(talk_base::Thread* worker_thread) ChannelManager::ChannelManager(MediaEngine* me, DeviceManager* dm, talk_base::Thread* worker_thread) - : -#ifdef USE_TALK_SOUND - sound_system_factory_(NULL), -#endif - media_engine_(me), + : media_engine_(me), device_manager_(dm), initialized_(false), main_thread_(talk_base::Thread::Current()), worker_thread_(worker_thread), + audio_in_device_(DeviceManager::kDefaultDeviceName), + audio_out_device_(DeviceManager::kDefaultDeviceName), audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS), local_renderer_(NULL), capturing_(false), @@ -180,7 +164,8 @@ void ChannelManager::Construct() { // Init the device manager immediately, and set up our default video device. SignalDevicesChange.repeat(device_manager_->SignalDevicesChange); device_manager_->Init(); - SetVideoOptions(""); + // Set camera_device_ to the name of the default video capturer. + SetVideoOptions(DeviceManager::kDefaultDeviceName); // Camera is started asynchronously, request callbacks when startup // completes to be able to forward them to the rendering manager. @@ -230,16 +215,47 @@ bool ChannelManager::Init() { if (media_engine_->Init()) { initialized_ = true; - // Now that we're initialized, apply any stored preferences. + // Now that we're initialized, apply any stored preferences. A preferred + // device might have been unplugged. In this case, we fallback to the + // default device but keep the user preferences. The preferences are + // changed only when the Javascript FE changes them. + const std::string preferred_audio_in_device = audio_in_device_; + const std::string preferred_audio_out_device = audio_out_device_; + const std::string preferred_camera_device = camera_device_; + Device device; + if (!device_manager_->GetAudioInputDevice(audio_in_device_, &device)) { + LOG(LS_WARNING) << "The preferred microphone '" << audio_in_device_ + << "' is unavailable. Fall back to the default."; + audio_in_device_ = DeviceManager::kDefaultDeviceName; + } + if (!device_manager_->GetAudioOutputDevice(audio_out_device_, &device)) { + LOG(LS_WARNING) << "The preferred speaker '" << audio_out_device_ + << "' is unavailable. Fall back to the default."; + audio_out_device_ = DeviceManager::kDefaultDeviceName; + } + if (!device_manager_->GetVideoCaptureDevice(camera_device_, &device)) { + LOG(LS_WARNING) << "The preferred camera '" << camera_device_ + << "' is unavailable. Fall back to the default."; + camera_device_ = DeviceManager::kDefaultDeviceName; + } + if (!SetAudioOptions(audio_in_device_, audio_out_device_, audio_options_)) { - audio_in_device_.clear(); - audio_out_device_.clear(); + LOG(LS_WARNING) << "Failed to SetAudioOptions with" + << " microphone: " << audio_in_device_ + << " speaker: " << audio_out_device_ + << " options: " << audio_options_; } if (!SetVideoOptions(camera_device_)) { - // TODO: Consider resetting to the default cam here. - camera_device_.clear(); + LOG(LS_WARNING) << "Failed to SetVideoOptions with camera: " + << camera_device_; } + + // Restore the user preferences. + audio_in_device_ = preferred_audio_in_device; + audio_out_device_ = preferred_audio_out_device; + camera_device_ = preferred_camera_device; + // Now apply the default video codec that has been set earlier. if (default_video_encoder_config_.max_codec.id != 0) { SetDefaultVideoEncoderConfig(default_video_encoder_config_); @@ -478,43 +494,17 @@ bool ChannelManager::GetVideoOptions(std::string* cam_name) { } bool ChannelManager::SetVideoOptions(const std::string& cam_name) { - bool ret; Device device; - - if (cam_name.empty()) { - // If we're passed the default device name, get the default device. - ret = device_manager_->GetDefaultVideoCaptureDevice(&device); - } else { - // Convert the camera name to a device, fail if it can't be found. - std::vector<Device> devices; - ret = device_manager_->GetVideoCaptureDevices(&devices); - if (ret) { - for (size_t i = 0; i < devices.size(); ++i) { - if (devices[i].name == cam_name) { - device = devices[i]; - break; - } - } - ret = !device.name.empty(); - } + if (!device_manager_->GetVideoCaptureDevice(cam_name, &device)) { + LOG(LS_WARNING) << "Device manager can't find camera: " << cam_name; + return false; } // If we're running, tell the media engine about it. - if (ret && initialized_) { -#ifdef OSX - Device sg_device; - ret = device_manager_->QtKitToSgDevice(device.name, &sg_device); - if (ret) { - device = sg_device; - } else { - LOG(LS_ERROR) << "Unable to find SG Component for qtkit device " - << device.name; - } -#endif - if (ret) { - VideoOptions options(&device); - ret = (Send(MSG_SETVIDEOOPTIONS, &options) && options.result); - } + bool ret = true; + if (initialized_) { + VideoOptions options(&device); + ret = (Send(MSG_SETVIDEOOPTIONS, &options) && options.result); } // If everything worked, retain the name of the selected camera. @@ -638,10 +628,10 @@ bool ChannelManager::Send(uint32 id, talk_base::MessageData* data) { return true; } -void ChannelManager::OnVideoCaptureResult(bool result) { - capturing_ = result; +void ChannelManager::OnVideoCaptureResult(CaptureResult result) { + capturing_ = result == CR_SUCCESS; main_thread_->Post(this, MSG_CAMERASTARTED, - new talk_base::TypedMessageData<bool>(result)); + new talk_base::TypedMessageData<CaptureResult>(result)); } void ChannelManager::OnMessage(talk_base::Message* message) { @@ -728,8 +718,9 @@ void ChannelManager::OnMessage(talk_base::Message* message) { break; } case MSG_CAMERASTARTED: { - talk_base::TypedMessageData<bool> *data = - static_cast<talk_base::TypedMessageData<bool>*>(message->pdata); + talk_base::TypedMessageData<CaptureResult>* data = + static_cast<talk_base::TypedMessageData<CaptureResult>*>( + message->pdata); SignalVideoCaptureResult(data->data()); delete data; break; diff --git a/third_party/libjingle/source/talk/session/phone/channelmanager.h b/third_party/libjingle/source/talk/session/phone/channelmanager.h index c5ff45a..dfcfffe 100644 --- a/third_party/libjingle/source/talk/session/phone/channelmanager.h +++ b/third_party/libjingle/source/talk/session/phone/channelmanager.h @@ -38,9 +38,6 @@ #include "talk/session/phone/voicechannel.h" #include "talk/session/phone/mediaengine.h" #include "talk/session/phone/devicemanager.h" -#ifdef USE_TALK_SOUND -#include "talk/sound/soundsystemfactory.h" -#endif namespace cricket { @@ -157,7 +154,7 @@ class ChannelManager : public talk_base::MessageHandler, bool GetAudioOutputDevices(std::vector<std::string>* names); bool GetVideoCaptureDevices(std::vector<std::string>* names); sigslot::repeater0<> SignalDevicesChange; - sigslot::signal1<bool> SignalVideoCaptureResult; + sigslot::signal1<CaptureResult> SignalVideoCaptureResult; private: typedef std::vector<VoiceChannel*> VoiceChannels; @@ -185,13 +182,10 @@ class ChannelManager : public talk_base::MessageHandler, CaptureResult SetVideoCapture_w(bool capture); void SetMediaLogging(bool video, int level, const char* filter); void SetMediaLogging_w(bool video, int level, const char* filter); - void OnVideoCaptureResult(bool result); + void OnVideoCaptureResult(CaptureResult result); void OnMessage(talk_base::Message *message); talk_base::CriticalSection crit_; -#ifdef USE_TALK_SOUND - talk_base::scoped_ptr<SoundSystemFactory> sound_system_factory_; -#endif talk_base::scoped_ptr<MediaEngine> media_engine_; talk_base::scoped_ptr<DeviceManager> device_manager_; bool initialized_; diff --git a/third_party/libjingle/source/talk/session/phone/cryptoparams.h b/third_party/libjingle/source/talk/session/phone/cryptoparams.h index 8891659..15e94a0 100644 --- a/third_party/libjingle/source/talk/session/phone/cryptoparams.h +++ b/third_party/libjingle/source/talk/session/phone/cryptoparams.h @@ -32,7 +32,7 @@ namespace cricket { -// Parameters for SRTP negotiation, as described in RFC 4586. +// Parameters for SRTP negotiation, as described in RFC 4568. struct CryptoParams { CryptoParams() : tag(0) {} CryptoParams(int t, const std::string& cs, diff --git a/third_party/libjingle/source/talk/session/phone/devicemanager.cc b/third_party/libjingle/source/talk/session/phone/devicemanager.cc index e32ed63..cb97301 100644 --- a/third_party/libjingle/source/talk/session/phone/devicemanager.cc +++ b/third_party/libjingle/source/talk/session/phone/devicemanager.cc @@ -43,46 +43,78 @@ #include <CoreAudio/CoreAudio.h> #include <QuickTime/QuickTime.h> #elif LINUX +#include <libudev.h> #include <unistd.h> -#ifndef USE_TALK_SOUND -#include <alsa/asoundlib.h> -#endif #include "talk/base/linux.h" #include "talk/base/fileutils.h" #include "talk/base/pathutils.h" +#include "talk/base/physicalsocketserver.h" #include "talk/base/stream.h" +#include "talk/session/phone/libudevsymboltable.h" #include "talk/session/phone/v4llookup.h" +#include "talk/sound/platformsoundsystem.h" +#include "talk/sound/platformsoundsystemfactory.h" +#include "talk/sound/sounddevicelocator.h" +#include "talk/sound/soundsysteminterface.h" #endif #include "talk/base/logging.h" #include "talk/base/stringutils.h" #include "talk/session/phone/mediaengine.h" -#if USE_TALK_SOUND -#include "talk/sound/platformsoundsystem.h" -#include "talk/sound/sounddevicelocator.h" -#include "talk/sound/soundsysteminterface.h" -#endif namespace cricket { // Initialize to empty string. const std::string DeviceManager::kDefaultDeviceName; -#if WIN32 +#ifdef WIN32 class DeviceWatcher : public talk_base::Win32Window { public: explicit DeviceWatcher(DeviceManager* dm); bool Start(); void Stop(); + private: HDEVNOTIFY Register(REFGUID guid); void Unregister(HDEVNOTIFY notify); virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result); + DeviceManager* manager_; HDEVNOTIFY audio_notify_; HDEVNOTIFY video_notify_; }; -#else -// TODO: Implement this for other platforms. +#elif defined(LINUX) +class DeviceWatcher : private talk_base::Dispatcher { + public: + explicit DeviceWatcher(DeviceManager* dm); + bool Start(); + void Stop(); + + private: + virtual uint32 GetRequestedEvents(); + virtual void OnPreEvent(uint32 ff); + virtual void OnEvent(uint32 ff, int err); + virtual int GetDescriptor(); + virtual bool IsDescriptorClosed(); + + DeviceManager* manager_; + LibUDevSymbolTable libudev_; + struct udev* udev_; + struct udev_monitor* udev_monitor_; + bool registered_; +}; +#define LATE(sym) LATESYM_GET(LibUDevSymbolTable, &libudev_, sym) +#elif defined(OSX) +class DeviceWatcher { + public: + explicit DeviceWatcher(DeviceManager* dm); + bool Start(); + void Stop(); + private: + DeviceManager* manager_; + void* impl_; +}; +#elif defined(IOS) || defined(ANDROID) +// We don't use DeviceWatcher on iOS or Android, so just stub out a noop class. class DeviceWatcher { public: explicit DeviceWatcher(DeviceManager* dm) {} @@ -91,7 +123,7 @@ class DeviceWatcher { }; #endif -#ifndef LINUX +#if !defined(LINUX) && !defined(IOS) static bool ShouldDeviceBeIgnored(const std::string& device_name); #endif #ifndef OSX @@ -107,21 +139,22 @@ static bool GetWaveDevices(bool input, std::vector<Device>* devs); #elif OSX static const int kVideoDeviceOpenAttempts = 3; static const UInt32 kAudioDeviceNameLength = 64; -// Obj-C function defined in devicemanager-mac.mm +// Obj-C functions defined in devicemanager-mac.mm +extern void* CreateDeviceWatcherCallback(DeviceManager* dm); +extern void ReleaseDeviceWatcherCallback(void* impl); extern bool GetQTKitVideoDevices(std::vector<Device>* out); static bool GetAudioDeviceIDs(bool inputs, std::vector<AudioDeviceID>* out); static bool GetAudioDeviceName(AudioDeviceID id, bool input, std::string* out); #endif -DeviceManager::DeviceManager( -#ifdef USE_TALK_SOUND - SoundSystemFactory *factory -#endif - ) +DeviceManager::DeviceManager() : initialized_(false), +#if defined(WIN32) + need_couninitialize_(false), +#endif watcher_(new DeviceWatcher(this)) -#ifdef USE_TALK_SOUND - , sound_system_(factory) +#ifdef LINUX + , sound_system_(new PlatformSoundSystemFactory()) #endif { } @@ -135,6 +168,16 @@ DeviceManager::~DeviceManager() { bool DeviceManager::Init() { if (!initialized_) { +#if defined(WIN32) + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + need_couninitialize_ = SUCCEEDED(hr); + if (FAILED(hr)) { + LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr; + if (hr != RPC_E_CHANGED_MODE) { + return false; + } + } +#endif if (!watcher_->Start()) { return false; } @@ -146,6 +189,12 @@ bool DeviceManager::Init() { void DeviceManager::Terminate() { if (initialized_) { watcher_->Stop(); +#if defined(WIN32) + if (need_couninitialize_) { + CoUninitialize(); + need_couninitialize_ = false; + } +#endif initialized_ = false; } } @@ -202,86 +251,6 @@ bool DeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) { #endif } -#ifdef OSX -bool DeviceManager::QtKitToSgDevice(const std::string& qtkit_name, - Device* out) { - out->name.clear(); - - ComponentDescription only_vdig; - memset(&only_vdig, 0, sizeof(only_vdig)); - only_vdig.componentType = videoDigitizerComponentType; - only_vdig.componentSubType = kAnyComponentSubType; - only_vdig.componentManufacturer = kAnyComponentManufacturer; - - // Enumerate components (drivers). - Component component = 0; - while ((component = FindNextComponent(component, &only_vdig)) && - out->name.empty()) { - // Get the name of the component and see if we want to open it. - Handle name_handle = NewHandle(0); - GetComponentInfo(component, NULL, name_handle, NULL, NULL); - Ptr name_ptr = *name_handle; - std::string comp_name(name_ptr + 1, static_cast<size_t>(*name_ptr)); - DisposeHandle(name_handle); - - if (!ShouldDeviceBeIgnored(comp_name)) { - // Try to open the component. - // DV Video will fail with err=-9408 (deviceCantMeetRequest) - // IIDC FireWire Video and USB Video Class Video will fail with err=704 - // if no cameras are present, or there is contention for the camera. - // We can't tell the scenarios apart, so we will retry a few times if - // we get a 704 to make sure we detect the cam if one is really there. - int attempts = 0; - ComponentInstance vdig; - OSErr err; - do { - err = OpenAComponent(component, &vdig); - ++attempts; - } while (!vdig && err == 704 && attempts < kVideoDeviceOpenAttempts); - - if (vdig) { - // We were able to open the component. - LOG(LS_INFO) << "Opened component \"" << comp_name - << "\", tries=" << attempts; - - // Enumerate cameras on the component. - // Note, that due to QT strangeness VDGetNumberOfInputs really returns - // the number of inputs minus one. If no inputs are available -1 is - // returned. - short num_inputs; // NOLINT - VideoDigitizerError err = VDGetNumberOfInputs(vdig, &num_inputs); - if (err == 0 && num_inputs >= 0) { - LOG(LS_INFO) << "Found " << num_inputs + 1 << " webcams attached."; - Str255 pname; - for (int i = 0; i <= num_inputs; ++i) { - err = VDGetInputName(vdig, i, pname); - if (err == 0) { - // The format for camera ids is <component>:<camera index>. - char id_buf[256]; - talk_base::sprintfn(id_buf, ARRAY_SIZE(id_buf), "%s:%d", - comp_name.c_str(), i); - std::string name(reinterpret_cast<const char*>(pname + 1), - static_cast<size_t>(*pname)), id(id_buf); - LOG(LS_INFO) << " Webcam " << i << ": " << name; - if (name == qtkit_name) { - out->name = name; - out->id = id; - break; - } - } - } - } - CloseComponent(vdig); - } else { - LOG(LS_INFO) << "Failed to open component \"" << comp_name - << "\", err=" << err; - } - } - } - return !out->name.empty(); -} -#endif - bool DeviceManager::GetDefaultVideoCaptureDevice(Device* device) { bool ret = false; #if WIN32 @@ -310,6 +279,28 @@ bool DeviceManager::GetDefaultVideoCaptureDevice(Device* device) { return ret; } +bool DeviceManager::GetVideoCaptureDevice(const std::string& name, + Device* out) { + // If the name is empty, return the default device. + if (name.empty() || name == kDefaultDeviceName) { + return GetDefaultVideoCaptureDevice(out); + } + + std::vector<Device> devices; + if (!GetVideoCaptureDevices(&devices)) { + return false; + } + + for (std::vector<Device>::const_iterator it = devices.begin(); + it != devices.end(); ++it) { + if (name == it->name) { + *out = *it; + return true; + } + } + + return false; +} bool DeviceManager::GetAudioDevice(bool is_input, const std::string& name, Device* out) { @@ -338,7 +329,8 @@ bool DeviceManager::GetAudioDevice(bool is_input, const std::string& name, bool DeviceManager::GetAudioDevicesByPlatform(bool input, std::vector<Device>* devs) { devs->clear(); -#if defined(USE_TALK_SOUND) + +#if defined(LINUX) if (!sound_system_.get()) { return false; } @@ -384,51 +376,6 @@ bool DeviceManager::GetAudioDevicesByPlatform(bool input, } return ret; -#elif defined(LINUX) - int card = -1, dev = -1; - snd_ctl_t *handle = NULL; - snd_pcm_info_t *pcminfo = NULL; - - snd_pcm_info_malloc(&pcminfo); - - while (true) { - if (snd_card_next(&card) != 0 || card < 0) - break; - - char *card_name; - if (snd_card_get_name(card, &card_name) != 0) - continue; - - char card_string[7]; - snprintf(card_string, sizeof(card_string), "hw:%d", card); - if (snd_ctl_open(&handle, card_string, 0) != 0) - continue; - - while (true) { - if (snd_ctl_pcm_next_device(handle, &dev) < 0 || dev < 0) - break; - snd_pcm_info_set_device(pcminfo, dev); - snd_pcm_info_set_subdevice(pcminfo, 0); - snd_pcm_info_set_stream(pcminfo, input ? SND_PCM_STREAM_CAPTURE : - SND_PCM_STREAM_PLAYBACK); - if (snd_ctl_pcm_info(handle, pcminfo) != 0) - continue; - - char name[128]; - talk_base::sprintfn(name, sizeof(name), "%s (%s)", card_name, - snd_pcm_info_get_name(pcminfo)); - // TODO: We might want to identify devices with something - // more specific than just their card number (e.g., the PCM names that - // aplay -L prints out). - devs->push_back(Device(name, card)); - - LOG(LS_INFO) << "Found device: id = " << card << ", name = " - << name; - } - snd_ctl_close(handle); - } - snd_pcm_info_free(pcminfo); - return true; #else return false; #endif @@ -436,20 +383,7 @@ bool DeviceManager::GetAudioDevicesByPlatform(bool input, #if defined(WIN32) bool GetVideoDevices(std::vector<Device>* devices) { - // TODO: Move the CoInit stuff to Initialize/Terminate. - HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if (FAILED(hr)) { - LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr; - if (hr != RPC_E_CHANGED_MODE) { - return false; - } - } - - bool ret = GetDevices(CLSID_VideoInputDeviceCategory, devices); - if (SUCCEEDED(hr)) { - CoUninitialize(); - } - return ret; + return GetDevices(CLSID_VideoInputDeviceCategory, devices); } bool GetDevices(const CLSID& catid, std::vector<Device>* devices) { @@ -738,6 +672,24 @@ static bool GetAudioDeviceName(AudioDeviceID id, return true; } +DeviceWatcher::DeviceWatcher(DeviceManager* manager) + : manager_(manager), impl_(NULL) { +} + +bool DeviceWatcher::Start() { + if (!impl_) { + impl_ = CreateDeviceWatcherCallback(manager_); + } + return impl_ != NULL; +} + +void DeviceWatcher::Stop() { + if (impl_) { + ReleaseDeviceWatcherCallback(impl_); + impl_ = NULL; + } +} + #elif defined(LINUX) static const std::string kVideoMetaPathK2_4("/proc/video/dev/"); static const std::string kVideoMetaPathK2_6("/sys/class/video4linux/"); @@ -900,11 +852,117 @@ static bool GetVideoDevices(std::vector<Device>* devices) { ScanV4L2Devices(devices); return true; } + +DeviceWatcher::DeviceWatcher(DeviceManager* dm) + : manager_(dm), udev_(NULL), udev_monitor_(NULL), registered_(false) {} + +bool DeviceWatcher::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 DeviceManager initialization in such a case. + if (!libudev_.Load()) { + LOG(LS_WARNING) << "libudev not present/usable; DeviceWatcher disabled"; + return true; + } + udev_ = LATE(udev_new)(); + if (!udev_) { + LOG_ERR(LS_ERROR) << "udev_new()"; + return true; + } + // The second argument here is the event source. It can be either "kernel" or + // "udev", but "udev" is the only correct choice. Apps listen on udev and the + // udev daemon in turn listens on the kernel. + udev_monitor_ = LATE(udev_monitor_new_from_netlink)(udev_, "udev"); + if (!udev_monitor_) { + LOG_ERR(LS_ERROR) << "udev_monitor_new_from_netlink()"; + return true; + } + // We only listen for changes in the video devices. Audio devices are more or + // less unimportant because receiving device change notifications really only + // matters for broadcasting updated send/recv capabilities based on whether + // there is at least one device available, and almost all computers have at + // least one audio device. Also, PulseAudio device notifications don't come + // from the udev daemon, they come from the PulseAudio daemon, so we'd only + // want to listen for audio device changes from udev if using ALSA. For + // simplicity, we don't bother with any audio stuff at all. + if (LATE(udev_monitor_filter_add_match_subsystem_devtype)(udev_monitor_, + "video4linux", + NULL) < 0) { + LOG_ERR(LS_ERROR) << "udev_monitor_filter_add_match_subsystem_devtype()"; + return true; + } + if (LATE(udev_monitor_enable_receiving)(udev_monitor_) < 0) { + LOG_ERR(LS_ERROR) << "udev_monitor_enable_receiving()"; + return true; + } + static_cast<talk_base::PhysicalSocketServer*>( + talk_base::Thread::Current()->socketserver())->Add(this); + registered_ = true; + return true; +} + +void DeviceWatcher::Stop() { + if (registered_) { + static_cast<talk_base::PhysicalSocketServer*>( + talk_base::Thread::Current()->socketserver())->Remove(this); + registered_ = false; + } + if (udev_monitor_) { + LATE(udev_monitor_unref)(udev_monitor_); + udev_monitor_ = NULL; + } + if (udev_) { + LATE(udev_unref)(udev_); + udev_ = NULL; + } + libudev_.Unload(); +} + +uint32 DeviceWatcher::GetRequestedEvents() { + return talk_base::DE_READ; +} + +void DeviceWatcher::OnPreEvent(uint32 ff) { + // Nothing to do. +} + +void DeviceWatcher::OnEvent(uint32 ff, int err) { + udev_device* device = LATE(udev_monitor_receive_device)(udev_monitor_); + if (!device) { + // Probably the socket connection to the udev daemon was terminated (perhaps + // the daemon crashed or is being restarted?). + LOG_ERR(LS_WARNING) << "udev_monitor_receive_device()"; + // Stop listening to avoid potential livelock (an fd with EOF in it is + // always considered readable). + static_cast<talk_base::PhysicalSocketServer*>( + talk_base::Thread::Current()->socketserver())->Remove(this); + registered_ = false; + return; + } + // Else we read the device successfully. + + // Since we already have our own filesystem-based device enumeration code, we + // simply re-enumerate rather than inspecting the device event. + LATE(udev_device_unref)(device); + manager_->OnDevicesChange(); +} + +int DeviceWatcher::GetDescriptor() { + return LATE(udev_monitor_get_fd)(udev_monitor_); +} + +bool DeviceWatcher::IsDescriptorClosed() { + // If it is closed then we will just get an error in + // udev_monitor_receive_device and unregister, so we don't need to check for + // it separately. + return false; +} + #endif // TODO: Try to get hold of a copy of Final Cut to understand why we // crash while scanning their components on OS X. -#ifndef LINUX +#if !defined(LINUX) && !defined(IOS) static bool ShouldDeviceBeIgnored(const std::string& device_name) { static const char* const kFilteredDevices[] = { "Google Camera Adapter", // Our own magiccams diff --git a/third_party/libjingle/source/talk/session/phone/devicemanager.h b/third_party/libjingle/source/talk/session/phone/devicemanager.h index 594e9bd..cd41f6f 100644 --- a/third_party/libjingle/source/talk/session/phone/devicemanager.h +++ b/third_party/libjingle/source/talk/session/phone/devicemanager.h @@ -33,7 +33,7 @@ #include "talk/base/sigslot.h" #include "talk/base/stringencode.h" -#ifdef USE_TALK_SOUND +#ifdef LINUX #include "talk/sound/soundsystemfactory.h" #endif @@ -59,11 +59,7 @@ struct Device { // Methods are virtual to allow for easy stubbing/mocking in tests. class DeviceManager { public: - DeviceManager( -#ifdef USE_TALK_SOUND - SoundSystemFactory *factory -#endif - ); + DeviceManager(); virtual ~DeviceManager(); // Initialization @@ -82,10 +78,7 @@ class DeviceManager { bool GetAudioOutputDevice(const std::string& name, Device* out); virtual bool GetVideoCaptureDevices(std::vector<Device>* devs); - virtual bool GetDefaultVideoCaptureDevice(Device* device); -#ifdef OSX - virtual bool QtKitToSgDevice(const std::string& qtkit_name, Device* out); -#endif + bool GetVideoCaptureDevice(const std::string& name, Device* out); sigslot::signal0<> SignalDevicesChange; @@ -96,12 +89,17 @@ class DeviceManager { protected: virtual bool GetAudioDevice(bool is_input, const std::string& name, Device* out); + virtual bool GetDefaultVideoCaptureDevice(Device* device); + private: bool GetAudioDevicesByPlatform(bool input, std::vector<Device>* devs); bool initialized_; +#ifdef WIN32 + bool need_couninitialize_; +#endif DeviceWatcher* watcher_; -#ifdef USE_TALK_SOUND +#ifdef LINUX SoundSystemHandle sound_system_; #endif }; diff --git a/third_party/libjingle/source/talk/session/phone/devicemanager_mac.mm b/third_party/libjingle/source/talk/session/phone/devicemanager_mac.mm index 1a14e95..3537ed9 100644 --- a/third_party/libjingle/source/talk/session/phone/devicemanager_mac.mm +++ b/third_party/libjingle/source/talk/session/phone/devicemanager_mac.mm @@ -31,8 +31,48 @@ #include "talk/base/logging.h" +@interface DeviceWatcherImpl : NSObject { +@private + cricket::DeviceManager* manager_; +} +- (id)init:(cricket::DeviceManager*) dm; +- (void)onDevicesChanged:(NSNotification *)notification; +@end + +@implementation DeviceWatcherImpl +- (id)init:(cricket::DeviceManager*) dm { + if ((self = [super init])) { + manager_ = dm; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(onDevicesChanged:) + name:QTCaptureDeviceWasConnectedNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(onDevicesChanged:) + name:QTCaptureDeviceWasDisconnectedNotification + object:nil]; + } + return self; +} +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} +- (void)onDevicesChanged:(NSNotification *)notification { + manager_->OnDevicesChange(); +} +@end + namespace cricket { +void* CreateDeviceWatcherCallback(DeviceManager* dm) { + return [[DeviceWatcherImpl alloc] init:dm]; +} +void ReleaseDeviceWatcherCallback(void* watcher) { + DeviceWatcherImpl* watcher_impl = static_cast<DeviceWatcherImpl*>(watcher); + [watcher_impl release]; +} + bool GetQTKitVideoDevices(std::vector<Device>* devices) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; @@ -44,11 +84,12 @@ bool GetQTKitVideoDevices(std::vector<Device>* devices) { QTCaptureDevice* qt_capture_device = [qt_capture_devices objectAtIndex:i]; static const NSString* kFormat = @"localizedDisplayName: \"%@\", " - "modelUniqueID: \"%@\", isConnected: %d, isOpen: %d, " + "modelUniqueID: \"%@\", uniqueID \"%@\", isConnected: %d, isOpen: %d, " "isInUseByAnotherApplication: %d"; NSString* info = [NSString stringWithFormat:kFormat, [qt_capture_device localizedDisplayName], [qt_capture_device modelUniqueID], + [qt_capture_device uniqueID], [qt_capture_device isConnected], [qt_capture_device isOpen], [qt_capture_device isInUseByAnotherApplication]]; @@ -57,7 +98,7 @@ bool GetQTKitVideoDevices(std::vector<Device>* devices) { std::string name([[qt_capture_device localizedDisplayName] cStringUsingEncoding:NSUTF8StringEncoding]); devices->push_back(Device(name, - [[qt_capture_device modelUniqueID] + [[qt_capture_device uniqueID] cStringUsingEncoding:NSUTF8StringEncoding])); } diff --git a/third_party/libjingle/source/talk/session/phone/filemediaengine.cc b/third_party/libjingle/source/talk/session/phone/filemediaengine.cc index 4929933..49d92b6 100644 --- a/third_party/libjingle/source/talk/session/phone/filemediaengine.cc +++ b/third_party/libjingle/source/talk/session/phone/filemediaengine.cc @@ -25,6 +25,9 @@ #include "talk/session/phone/filemediaengine.h" +#include <climits> + +#include "talk/base/buffer.h" #include "talk/base/event.h" #include "talk/base/logging.h" #include "talk/base/pathutils.h" @@ -81,12 +84,15 @@ class RtpSenderReceiver // Called by media channel. Context: media channel thread. bool SetSend(bool send); - void OnPacketReceived(const void* data, int len); + void OnPacketReceived(talk_base::Buffer* packet); // Override virtual method of parent MessageHandler. Context: Worker Thread. virtual void OnMessage(talk_base::Message* pmsg); private: + // Read the next RTP dump packet, whose RTP SSRC is the same as first_ssrc_. + // Return true if successful. + bool ReadNextPacket(RtpDumpPacket* packet); // Send a RTP packet to the network. The input parameter data points to the // start of the RTP packet and len is the packet size. Return true if the sent // size is equal to len. @@ -99,8 +105,10 @@ class RtpSenderReceiver talk_base::scoped_ptr<RtpDumpWriter> rtp_dump_writer_; // RTP dump packet read from the input stream. RtpDumpPacket rtp_dump_packet_; + uint32 start_send_time_; bool sending_; bool first_packet_; + uint32 first_ssrc_; DISALLOW_COPY_AND_ASSIGN(RtpSenderReceiver); }; @@ -136,13 +144,14 @@ bool RtpSenderReceiver::SetSend(bool send) { sending_ = send; if (!was_sending && sending_) { PostDelayed(0, this); // Wake up the send thread. + start_send_time_ = talk_base::Time(); } return true; } -void RtpSenderReceiver::OnPacketReceived(const void* data, int len) { +void RtpSenderReceiver::OnPacketReceived(talk_base::Buffer* packet) { if (rtp_dump_writer_.get()) { - rtp_dump_writer_->WriteRtpPacket(data, len); + rtp_dump_writer_->WriteRtpPacket(packet->data(), packet->length()); } } @@ -153,32 +162,45 @@ void RtpSenderReceiver::OnMessage(talk_base::Message* pmsg) { return; } - uint32 prev_elapsed_time = 0xFFFFFFFF; if (!first_packet_) { - prev_elapsed_time = rtp_dump_packet_.elapsed_time; + // Send the previously read packet. SendRtpPacket(&rtp_dump_packet_.data[0], rtp_dump_packet_.data.size()); - } else { - first_packet_ = false; } - // Read a dump packet and wait for the elapsed time. - if (talk_base::SR_SUCCESS == - rtp_dump_reader_->ReadPacket(&rtp_dump_packet_)) { - int waiting_time_ms = rtp_dump_packet_.elapsed_time > prev_elapsed_time ? - rtp_dump_packet_.elapsed_time - prev_elapsed_time : 0; - PostDelayed(waiting_time_ms, this); + if (ReadNextPacket(&rtp_dump_packet_)) { + int wait = talk_base::TimeUntil( + start_send_time_ + rtp_dump_packet_.elapsed_time); + wait = talk_base::_max(0, wait); + PostDelayed(wait, this); } else { Quit(); } } +bool RtpSenderReceiver::ReadNextPacket(RtpDumpPacket* packet) { + while (talk_base::SR_SUCCESS == rtp_dump_reader_->ReadPacket(packet)) { + uint32 ssrc; + if (!packet->GetRtpSsrc(&ssrc)) { + return false; + } + if (first_packet_) { + first_packet_ = false; + first_ssrc_ = ssrc; + } + if (ssrc == first_ssrc_) { + return true; + } + } + return false; +} + bool RtpSenderReceiver::SendRtpPacket(const void* data, size_t len) { if (!media_channel_ || !media_channel_->network_interface()) { return false; } - return media_channel_->network_interface()->SendPacket(data, len) == - static_cast<int>(len); + talk_base::Buffer packet(data, len, kMaxRtpPacketLen); + return media_channel_->network_interface()->SendPacket(&packet); } /////////////////////////////////////////////////////////////////////////// @@ -200,8 +222,8 @@ bool FileVoiceChannel::SetSend(SendFlags flag) { return rtp_sender_receiver_->SetSend(flag != SEND_NOTHING); } -void FileVoiceChannel::OnPacketReceived(const void* data, int len) { - rtp_sender_receiver_->OnPacketReceived(data, len); +void FileVoiceChannel::OnPacketReceived(talk_base::Buffer* packet) { + rtp_sender_receiver_->OnPacketReceived(packet); } /////////////////////////////////////////////////////////////////////////// @@ -223,8 +245,8 @@ bool FileVideoChannel::SetSend(bool send) { return rtp_sender_receiver_->SetSend(send); } -void FileVideoChannel::OnPacketReceived(const void* data, int len) { - rtp_sender_receiver_->OnPacketReceived(data, len); +void FileVideoChannel::OnPacketReceived(talk_base::Buffer* packet) { + rtp_sender_receiver_->OnPacketReceived(packet); } } // namespace cricket diff --git a/third_party/libjingle/source/talk/session/phone/filemediaengine.h b/third_party/libjingle/source/talk/session/phone/filemediaengine.h index cc6788c..2276772 100644 --- a/third_party/libjingle/source/talk/session/phone/filemediaengine.h +++ b/third_party/libjingle/source/talk/session/phone/filemediaengine.h @@ -144,8 +144,8 @@ class FileVoiceChannel : public VoiceMediaChannel { virtual bool GetStats(VoiceMediaInfo* info) { return true; } // Implement pure virtual methods of MediaChannel. - virtual void OnPacketReceived(const void* data, int len); - virtual void OnRtcpReceived(const void* data, int len) {} + virtual void OnPacketReceived(talk_base::Buffer* packet); + virtual void OnRtcpReceived(talk_base::Buffer* packet) {} virtual void SetSendSsrc(uint32 id) {} // TODO: change RTP packet? virtual bool SetRtcpCName(const std::string& cname) { return true; } virtual bool Mute(bool on) { return false; } @@ -179,8 +179,8 @@ class FileVideoChannel : public VideoMediaChannel { virtual bool RequestIntraFrame() { return false; } // Implement pure virtual methods of MediaChannel. - virtual void OnPacketReceived(const void* data, int len); - virtual void OnRtcpReceived(const void* data, int len) {} + virtual void OnPacketReceived(talk_base::Buffer* packet); + virtual void OnRtcpReceived(talk_base::Buffer* packet) {} virtual void SetSendSsrc(uint32 id) {} // TODO: change RTP packet? virtual bool SetRtcpCName(const std::string& cname) { return true; } virtual bool Mute(bool on) { return false; } diff --git a/third_party/libjingle/source/talk/session/phone/mediachannel.h b/third_party/libjingle/source/talk/session/phone/mediachannel.h index 01bd8b5..bb17db2 100644 --- a/third_party/libjingle/source/talk/session/phone/mediachannel.h +++ b/third_party/libjingle/source/talk/session/phone/mediachannel.h @@ -38,12 +38,20 @@ // TODO: re-evaluate this include #include "talk/session/phone/audiomonitor.h" +namespace talk_base { +class Buffer; +} + namespace flute { - class MagicCamVideoRenderer; +class MagicCamVideoRenderer; } namespace cricket { +const size_t kMinRtpPacketLen = 12; +const size_t kMinRtcpPacketLen = 4; +const size_t kMaxRtpPacketLen = 2048; + enum VoiceMediaChannelOptions { OPT_CONFERENCE = 0x10000, // tune the audio stream for conference mode OPT_ENERGYLEVEL = 0x20000, // include the energy level in RTP packets, as @@ -53,6 +61,8 @@ enum VoiceMediaChannelOptions { }; enum VideoMediaChannelOptions { + OPT_INTERPOLATE = 0x10000 // Increase the output framerate by 2x by + // interpolating frames }; class MediaChannel : public sigslot::has_slots<> { @@ -60,8 +70,8 @@ class MediaChannel : public sigslot::has_slots<> { class NetworkInterface { public: enum SocketType { ST_RTP, ST_RTCP }; - virtual int SendPacket(const void *data, size_t len) = 0; - virtual int SendRtcp(const void *data, size_t len) = 0; + virtual bool SendPacket(talk_base::Buffer* packet) = 0; + virtual bool SendRtcp(talk_base::Buffer* packet) = 0; virtual int SetOption(SocketType type, talk_base::Socket::Option opt, int option) = 0; virtual ~NetworkInterface() {} @@ -77,9 +87,9 @@ class MediaChannel : public sigslot::has_slots<> { } // Called when a RTP packet is received. - virtual void OnPacketReceived(const void *data, int len) = 0; + virtual void OnPacketReceived(talk_base::Buffer* packet) = 0; // Called when a RTCP packet is received. - virtual void OnRtcpReceived(const void *data, int len) = 0; + virtual void OnRtcpReceived(talk_base::Buffer* packet) = 0; // Sets the SSRC to be used for outgoing data. virtual void SetSendSsrc(uint32 id) = 0; // Set the CNAME of RTCP @@ -101,28 +111,100 @@ enum SendFlags { SEND_MICROPHONE }; -struct MediaInfo { - int fraction_lost; - int cum_lost; - int ext_max; - int jitter; - int RTT; - int bytesSent; - int packetsSent; - int bytesReceived; - int packetsReceived; +struct VoiceSenderInfo { + uint32 ssrc; + int bytes_sent; + int packets_sent; + int packets_lost; + float fraction_lost; + int ext_seqnum; + int rtt_ms; + int jitter_ms; + int audio_level; +}; + +struct VoiceReceiverInfo { + uint32 ssrc; + int bytes_rcvd; + int packets_rcvd; + int packets_lost; + float fraction_lost; + int ext_seqnum; + int jitter_ms; + int audio_level; }; -struct VoiceMediaInfo : MediaInfo { +struct VideoSenderInfo { + uint32 ssrc; + int 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; + int framerate_sent; }; -struct VideoMediaInfo : MediaInfo { - int receive_framerate; - int send_framerate; +struct VideoReceiverInfo { + uint32 ssrc; + int 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; + int frame_height; + int framerate_rcvd; + int framerate_decoded; + int framerate_output; +}; + +struct VoiceMediaInfo { + void Clear() { + senders.clear(); + receivers.clear(); + } + std::vector<VoiceSenderInfo> senders; + std::vector<VoiceReceiverInfo> receivers; +}; + +struct VideoMediaInfo { + void Clear() { + senders.clear(); + receivers.clear(); + } + std::vector<VideoSenderInfo> senders; + std::vector<VideoReceiverInfo> receivers; }; class VoiceMediaChannel : public MediaChannel { public: + enum Error { + ERROR_NONE = 0, // No error. + ERROR_OTHER, // Other errors. + ERROR_REC_DEVICE_OPEN_FAILED = 100, // Could not open mic. + ERROR_REC_DEVICE_MUTED, // Mic was muted by OS. + ERROR_REC_DEVICE_SILENT, // No background noise picked up. + ERROR_REC_DEVICE_SATURATION, // Mic input is clipping. + ERROR_REC_DEVICE_REMOVED, // Mic was removed while active. + ERROR_REC_RUNTIME_ERROR, // Processing is encountering errors. + ERROR_REC_SRTP_ERROR, // Generic SRTP failure. + ERROR_PLAY_DEVICE_OPEN_FAILED = 200, // Could not open playout. + ERROR_PLAY_DEVICE_MUTED, // Playout muted by OS. + ERROR_PLAY_DEVICE_REMOVED, // Playout removed while active. + ERROR_PLAY_RUNTIME_ERROR, // Errors in voice processing. + ERROR_PLAY_SRTP_ERROR, // Generic SRTP failure. + ERROR_PLAY_SRTP_AUTH_FAILED, // Failed to authenticate packets. + }; + VoiceMediaChannel() {} virtual ~VoiceMediaChannel() {} // Sets the codecs/payload types to be used for incoming media. @@ -149,6 +231,16 @@ class VoiceMediaChannel : public MediaChannel { virtual bool PressDTMF(int event, bool playout) = 0; // Gets quality stats for the channel. virtual bool GetStats(VoiceMediaInfo* info) = 0; + // Gets last reported error for this media channel. + virtual void GetLastMediaError(uint32* ssrc, + VoiceMediaChannel::Error* error) { + ASSERT(error != NULL); + *error = ERROR_NONE; + } + + // Signal errors from MediaChannel. Arguments are: + // ssrc(uint32), and error(VoiceMediaChannel::Error). + sigslot::signal2<uint32, VoiceMediaChannel::Error> SignalMediaError; }; // Represents a YUV420 (a.k.a. I420) video frame. @@ -195,7 +287,7 @@ class VideoFrame { // nothing is written. virtual size_t CopyToBuffer(uint8 *buffer, size_t size) const = 0; - // Converts the I420 data to RGB of a certain type such as BGRA and RGBA. + // Converts the I420 data to RGB of a certain type such as ARGB and ABGR. // Returns the frame's actual size, regardless of whether it was written or // not (like snprintf). Parameters size and pitch_rgb are in units of bytes. // If there is insufficient space, nothing is written. @@ -326,6 +418,18 @@ class NullVideoRenderer : public VideoRenderer { class VideoMediaChannel : public MediaChannel { public: + enum Error { + ERROR_NONE = 0, // No error. + ERROR_OTHER, // Other errors. + ERROR_REC_DEVICE_OPEN_FAILED = 100, // Could not open camera. + ERROR_REC_DEVICE_NO_DEVICE, // No camera. + ERROR_REC_DEVICE_IN_USE, // Device is in already use. + ERROR_REC_DEVICE_REMOVED, // Device is removed. + ERROR_REC_SRTP_ERROR, // Generic sender SRTP failure. + ERROR_PLAY_SRTP_ERROR = 200, // Generic receiver SRTP failure. + ERROR_PLAY_SRTP_AUTH_FAILED, // Failed to authenticate packets. + }; + VideoMediaChannel() { renderer_ = NULL; } virtual ~VideoMediaChannel() {} // Sets the codecs/payload types to be used for incoming media. @@ -351,6 +455,7 @@ class VideoMediaChannel : public MediaChannel { // Reuqest each of the remote senders to send an intra frame. virtual bool RequestIntraFrame() = 0; + sigslot::signal2<uint32, Error> SignalMediaError; protected: VideoRenderer *renderer_; diff --git a/third_party/libjingle/source/talk/session/phone/mediaengine.cc b/third_party/libjingle/source/talk/session/phone/mediaengine.cc index 66eb18e..42b0954 100644 --- a/third_party/libjingle/source/talk/session/phone/mediaengine.cc +++ b/third_party/libjingle/source/talk/session/phone/mediaengine.cc @@ -33,11 +33,7 @@ namespace cricket { // TODO: according to thaloun, HAVE_GIPSVIDEO will always // be false, so we can get rid of it. -MediaEngine* MediaEngine::Create( -#ifdef USE_TALK_SOUND - SoundSystemFactory *factory -#endif - ) { +MediaEngine* MediaEngine::Create() { return new NullMediaEngine(); } diff --git a/third_party/libjingle/source/talk/session/phone/mediaengine.h b/third_party/libjingle/source/talk/session/phone/mediaengine.h index c42e8ef..234a668 100644 --- a/third_party/libjingle/source/talk/session/phone/mediaengine.h +++ b/third_party/libjingle/source/talk/session/phone/mediaengine.h @@ -39,9 +39,6 @@ #include "talk/session/phone/codec.h" #include "talk/session/phone/devicemanager.h" #include "talk/session/phone/mediachannel.h" -#ifdef USE_TALK_SOUND -#include "talk/sound/soundsystemfactory.h" -#endif #include "talk/session/phone/videocommon.h" namespace cricket { @@ -88,11 +85,7 @@ class MediaEngine { }; virtual ~MediaEngine() {} - static MediaEngine* Create( -#ifdef USE_TALK_SOUND - SoundSystemFactory *factory -#endif - ); + static MediaEngine* Create(); // Initialization // Starts the engine. @@ -154,7 +147,7 @@ class MediaEngine { virtual void SetVoiceLogging(int min_sev, const char* filter) = 0; virtual void SetVideoLogging(int min_sev, const char* filter) = 0; - sigslot::repeater1<bool> SignalVideoCaptureResult; + sigslot::repeater1<CaptureResult> SignalVideoCaptureResult; }; // CompositeMediaEngine constructs a MediaEngine from separate @@ -162,11 +155,6 @@ class MediaEngine { template<class VOICE, class VIDEO> class CompositeMediaEngine : public MediaEngine { public: -#ifdef USE_TALK_SOUND - explicit CompositeMediaEngine(SoundSystemFactory *factory) - : voice_(factory) { - } -#endif CompositeMediaEngine() {} virtual bool Init() { if (!voice_.Init()) @@ -257,6 +245,37 @@ class CompositeMediaEngine : public MediaEngine { VIDEO video_; }; +class NullVoiceMediaChannel : public VoiceMediaChannel { + public: + explicit NullVoiceMediaChannel() {} + ~NullVoiceMediaChannel() {} + // MediaChannel implementations + virtual void OnPacketReceived(talk_base::Buffer* packet) {} + virtual void OnRtcpReceived(talk_base::Buffer* packet) {} + virtual void SetSendSsrc(uint32 id) {} + virtual bool SetRtcpCName(const std::string& cname) { return true; } + virtual bool Mute(bool on) { return true; } + virtual bool SetSendBandwidth(bool autobw, int bps) { return true; } + virtual bool SetOptions(int options) { return true; } + // VoiceMediaChannel implementations + virtual bool SetRecvCodecs(const std::vector<AudioCodec> &codecs) { + return true; + } + virtual bool SetSendCodecs(const std::vector<AudioCodec> &codecs) { + return true; + } + virtual bool SetPlayout(bool playout) { return true; } + virtual bool SetSend(SendFlags flag) { return true; } + virtual bool AddStream(uint32 ssrc) { return true; } + virtual bool RemoveStream(uint32 ssrc) { return true; } + virtual bool GetActiveStreams(AudioInfo::StreamList* streams) { return true; } + virtual int GetOutputLevel() { return 0; } + virtual void SetRingbackTone(const char *buf, int len) {} + virtual bool PlayRingbackTone(bool play, bool loop) { return true; } + virtual bool PressDTMF(int event, bool playout) { return true; } + virtual bool GetStats(VoiceMediaInfo* info) { return false; } +}; + // NullVoiceEngine can be used with CompositeMediaEngine in the case where only // a video engine is desired. class NullVoiceEngine { @@ -265,7 +284,9 @@ class NullVoiceEngine { void Terminate() {} int GetCapabilities() { return 0; } VoiceMediaChannel* CreateChannel() { - return NULL; + // TODO: See if we can make things work without requiring + // allocation of a channel. + return new NullVoiceMediaChannel(); } SoundclipMedia* CreateSoundclip() { return NULL; @@ -304,7 +325,7 @@ class NullVideoEngine { const std::vector<VideoCodec>& codecs() { return codecs_; } bool FindCodec(const VideoCodec&) { return false; } void SetLogging(int min_sev, const char* filter) {} - sigslot::signal1<bool> SignalCaptureResult; + sigslot::signal1<CaptureResult> SignalCaptureResult; private: std::vector<VideoCodec> codecs_; }; diff --git a/third_party/libjingle/source/talk/session/phone/mediamonitor.h b/third_party/libjingle/source/talk/session/phone/mediamonitor.h index 9eda2aa..6b964aa 100644 --- a/third_party/libjingle/source/talk/session/phone/mediamonitor.h +++ b/third_party/libjingle/source/talk/session/phone/mediamonitor.h @@ -74,10 +74,8 @@ class MediaMonitorT : public MediaMonitor { protected: // These routines assume the crit_ lock is held by the calling thread. virtual void GetStats() { - media_info_.packetsReceived = -1; - if (!media_channel_->GetStats(&media_info_)) { - media_info_.packetsReceived = -1; - } + media_info_.Clear(); + media_channel_->GetStats(&media_info_); } virtual void Update() { MI stats(media_info_); diff --git a/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc b/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc index f16b531..c862cd5 100644 --- a/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc +++ b/third_party/libjingle/source/talk/session/phone/mediasessionclient.cc @@ -562,7 +562,7 @@ bool ParseGingleAudioContent(const buzz::XmlElement* content_elem, audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0)); } - ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, audio); + ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio); if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE, audio, error)) { diff --git a/third_party/libjingle/source/talk/session/phone/rtpdump.cc b/third_party/libjingle/source/talk/session/phone/rtpdump.cc index bd67cad..37d664b 100644 --- a/third_party/libjingle/source/talk/session/phone/rtpdump.cc +++ b/third_party/libjingle/source/talk/session/phone/rtpdump.cc @@ -30,6 +30,7 @@ #include <string> #include "talk/base/bytebuffer.h" +#include "talk/base/byteorder.h" #include "talk/base/logging.h" #include "talk/base/time.h" @@ -55,13 +56,35 @@ void RtpDumpFileHeader::WriteToByteBuffer(talk_base::ByteBuffer* buf) { } // RTP packet format (http://www.networksorcery.com/enp/protocol/rtp.htm). -static const int kRtpSeqNumOffset = 2; -static const int kRtpSeqNumAndTimestampSize = 6; +static const size_t kMinimumRtpHeaderSize = 12; static const uint32 kDefaultTimeIncrease = 30; bool RtpDumpPacket::IsValidRtpPacket() const { - return !is_rtcp && - data.size() >= kRtpSeqNumOffset + kRtpSeqNumAndTimestampSize; + return !is_rtcp && data.size() >= kMinimumRtpHeaderSize; +} + +bool RtpDumpPacket::GetRtpSeqNum(uint16* seq_num) const { + if (!seq_num || !IsValidRtpPacket()) { + return false; + } + *seq_num = talk_base::GetBE16(&data[2]); + return true; +} + +bool RtpDumpPacket::GetRtpTimestamp(uint32* ts) const { + if (!ts || !IsValidRtpPacket()) { + return false; + } + *ts = talk_base::GetBE32(&data[4]); + return true; +} + +bool RtpDumpPacket::GetRtpSsrc(uint32* ssrc) const { + if (!ssrc || !IsValidRtpPacket()) { + return false; + } + *ssrc = talk_base::GetBE32(&data[8]); + return true; } /////////////////////////////////////////////////////////////////////////// @@ -197,12 +220,11 @@ talk_base::StreamResult RtpDumpLoopReader::ReadPacket(RtpDumpPacket* packet) { void RtpDumpLoopReader::UpdateStreamStatistics(const RtpDumpPacket& packet) { // Get the RTP sequence number and timestamp of the dump packet. uint16 rtp_seq_num = 0; + packet.GetRtpSeqNum(&rtp_seq_num); uint32 rtp_timestamp = 0; - if (packet.IsValidRtpPacket()) { - ReadRtpSeqNumAndTimestamp(packet, &rtp_seq_num, &rtp_timestamp); - } + packet.GetRtpTimestamp(&rtp_timestamp); - // Get the timestamps and sequence number for the first dump packet. + // Set the timestamps and sequence number for the first dump packet. if (0 == packet_count_++) { first_elapsed_time_ = packet.elapsed_time; first_rtp_seq_num_ = rtp_seq_num; @@ -240,9 +262,10 @@ void RtpDumpLoopReader::UpdateDumpPacket(RtpDumpPacket* packet) { if (packet->IsValidRtpPacket()) { // Get the old RTP sequence number and timestamp. - uint16 sequence; - uint32 timestamp; - ReadRtpSeqNumAndTimestamp(*packet, &sequence, ×tamp); + uint16 sequence = 0; + packet->GetRtpSeqNum(&sequence); + uint32 timestamp = 0; + packet->GetRtpTimestamp(×tamp); // Increase the RTP sequence number and timestamp. sequence += loop_count_ * rtp_seq_num_increase_; timestamp += loop_count_ * rtp_timestamp_increase_; @@ -250,29 +273,27 @@ void RtpDumpLoopReader::UpdateDumpPacket(RtpDumpPacket* packet) { talk_base::ByteBuffer buffer; buffer.WriteUInt16(sequence); buffer.WriteUInt32(timestamp); - memcpy(&packet->data[0] + kRtpSeqNumOffset, buffer.Data(), buffer.Length()); + memcpy(&packet->data[2], buffer.Data(), buffer.Length()); } } -void RtpDumpLoopReader::ReadRtpSeqNumAndTimestamp( - const RtpDumpPacket& packet, uint16* sequence, uint32* timestamp) { - talk_base::ByteBuffer buffer( - reinterpret_cast<const char*>(&packet.data[0] + kRtpSeqNumOffset), - kRtpSeqNumAndTimestampSize); - buffer.ReadUInt16(sequence); - buffer.ReadUInt32(timestamp); -} - /////////////////////////////////////////////////////////////////////////// // Implementation of RtpDumpWriter. /////////////////////////////////////////////////////////////////////////// + +RtpDumpWriter::RtpDumpWriter(talk_base::StreamInterface* stream) + : stream_(stream), + file_header_written_(false), + start_time_ms_(talk_base::Time()) { + } + uint32 RtpDumpWriter::GetElapsedTime() const { return talk_base::TimeSince(start_time_ms_); } talk_base::StreamResult RtpDumpWriter::WritePacket( const void* data, size_t data_len, uint32 elapsed, bool rtcp) { - if (!data || 0 == data_len) return talk_base::SR_ERROR; + if (!stream_ || !data || 0 == data_len) return talk_base::SR_ERROR; talk_base::StreamResult res = talk_base::SR_SUCCESS; // Write the file header if it has not been written yet. diff --git a/third_party/libjingle/source/talk/session/phone/rtpdump.h b/third_party/libjingle/source/talk/session/phone/rtpdump.h index 66275e6..f87b922 100644 --- a/third_party/libjingle/source/talk/session/phone/rtpdump.h +++ b/third_party/libjingle/source/talk/session/phone/rtpdump.h @@ -71,9 +71,12 @@ struct RtpDumpPacket { memcpy(&data[0], d, s); } - // Check if the dumped packet is a valid RTP packet with the sequence number - // and timestamp. bool IsValidRtpPacket() const; + // Get the sequence number, timestampe, and SSRC of the RTP packet. Return + // true and set the output parameter if successful. + bool GetRtpSeqNum(uint16* seq_num) const; + bool GetRtpTimestamp(uint32* ts) const; + bool GetRtpSsrc(uint32* ssrc) const; static const size_t kHeaderLength = 8; uint32 elapsed_time; // Milliseconds since the start of recording. @@ -121,10 +124,6 @@ class RtpDumpLoopReader : public RtpDumpReader { virtual talk_base::StreamResult ReadPacket(RtpDumpPacket* packet); private: - // Read the sequence number and timestamp from the RTP dump packet. - static void ReadRtpSeqNumAndTimestamp(const RtpDumpPacket& packet, - uint16* seq_num, uint32* timestamp); - // During the first loop, update the statistics, including packet count, frame // count, timestamps, and sequence number, of the input stream. void UpdateStreamStatistics(const RtpDumpPacket& packet); @@ -164,11 +163,8 @@ class RtpDumpLoopReader : public RtpDumpReader { class RtpDumpWriter { public: - explicit RtpDumpWriter(talk_base::StreamInterface* stream) - : stream_(stream), - file_header_written_(false), - start_time_ms_(0) { - } + explicit RtpDumpWriter(talk_base::StreamInterface* stream); + // Write a RTP or RTCP packet. The parameters data points to the packet and // data_len is its length. talk_base::StreamResult WriteRtpPacket(const void* data, size_t data_len) { diff --git a/third_party/libjingle/source/talk/session/phone/srtpfilter.cc b/third_party/libjingle/source/talk/session/phone/srtpfilter.cc index f8d2dd4..71f1991 100644 --- a/third_party/libjingle/source/talk/session/phone/srtpfilter.cc +++ b/third_party/libjingle/source/talk/session/phone/srtpfilter.cc @@ -25,8 +25,22 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#undef HAVE_CONFIG_H // talk's config.h conflicts with the one included by the - // libsrtp headers. Don't use it. +// talk's config.h, generated from mac_config_dot_h for OSX, conflicts with the +// one included by the libsrtp headers. Don't use it. Instead, we keep HAVE_SRTP +// and LOGGING defined in config.h. +#undef HAVE_CONFIG_H + +#ifdef OSX +// TODO: For the XCode build, we force SRTP (b/2500074) +#ifndef HAVE_SRTP +#define HAVE_SRTP 1 +#endif // HAVE_SRTP +// If LOGGING is not defined, define it to 1 (b/3245816) +#ifndef LOGGING +#define LOGGING 1 +#endif // HAVE_SRTP +#endif + #include "talk/session/phone/srtpfilter.h" #include <algorithm> @@ -35,11 +49,6 @@ #include "talk/base/base64.h" #include "talk/base/logging.h" -// TODO: For the XCode build, we force SRTP (b/2500074) -#if defined(OSX) && !defined(HAVE_SRTP) -#define HAVE_SRTP 1 -#endif - // Enable this line to turn on SRTP debugging // #define SRTP_DEBUG @@ -113,6 +122,7 @@ bool SrtpFilter::SetAnswer(const std::vector<CryptoParams>& answer_params, bool SrtpFilter::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { if (!IsActive()) { + LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active"; return false; } return send_session_.ProtectRtp(p, in_len, max_len, out_len); @@ -120,6 +130,7 @@ bool SrtpFilter::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { bool SrtpFilter::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { if (!IsActive()) { + LOG(LS_WARNING) << "Failed to ProtectRtcp: SRTP not active"; return false; } return send_session_.ProtectRtcp(p, in_len, max_len, out_len); @@ -127,6 +138,7 @@ bool SrtpFilter::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { bool SrtpFilter::UnprotectRtp(void* p, int in_len, int* out_len) { if (!IsActive()) { + LOG(LS_WARNING) << "Failed to UnprotectRtp: SRTP not active"; return false; } return recv_session_.UnprotectRtp(p, in_len, out_len); @@ -134,6 +146,7 @@ bool SrtpFilter::UnprotectRtp(void* p, int in_len, int* out_len) { bool SrtpFilter::UnprotectRtcp(void* p, int in_len, int* out_len) { if (!IsActive()) { + LOG(LS_WARNING) << "Failed to UnprotectRtcp: SRTP not active"; return false; } return recv_session_.UnprotectRtcp(p, in_len, out_len); @@ -190,6 +203,9 @@ bool SrtpFilter::ApplyParams(const CryptoParams& send_params, if (ret) { offer_params_.clear(); state_ = ST_ACTIVE; + LOG(LS_INFO) << "SRTP activated with negotiated parameters:" + << " send cipher_suite " << send_params.cipher_suite + << " recv cipher_suite " << recv_params.cipher_suite; } else { LOG(LS_WARNING) << "Failed to apply negotiated SRTP parameters"; } @@ -199,6 +215,7 @@ bool SrtpFilter::ApplyParams(const CryptoParams& send_params, bool SrtpFilter::ResetParams() { offer_params_.clear(); state_ = ST_INIT; + LOG(LS_INFO) << "SRTP reset to init state"; return true; } @@ -252,11 +269,18 @@ bool SrtpSession::SetRecv(const std::string& cs, const uint8* key, int len) { } bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { - if (!session_) + if (!session_) { + LOG(LS_WARNING) << "Failed to protect SRTP packet: no SRTP Session"; return false; + } + int need_len = in_len + rtp_auth_tag_len_; // NOLINT - if (max_len < need_len) + if (max_len < need_len) { + LOG(LS_WARNING) << "Failed to protect SRTP packet: The buffer length " + << max_len << " is less than the needed " << need_len; return false; + } + *out_len = in_len; int err = srtp_protect(session_, p, out_len); if (err != err_status_ok) { @@ -267,11 +291,18 @@ bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { } bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { - if (!session_) + if (!session_) { + LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session"; return false; + } + int need_len = in_len + sizeof(uint32) + rtcp_auth_tag_len_; // NOLINT - if (max_len < need_len) + if (max_len < need_len) { + LOG(LS_WARNING) << "Failed to protect SRTCP packet: The buffer length " + << max_len << " is less than the needed " << need_len; return false; + } + *out_len = in_len; int err = srtp_protect_rtcp(session_, p, out_len); if (err != err_status_ok) { @@ -282,8 +313,11 @@ bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { } bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) { - if (!session_) + if (!session_) { + LOG(LS_WARNING) << "Failed to unprotect SRTP packet: no SRTP Session"; return false; + } + *out_len = in_len; int err = srtp_unprotect(session_, p, out_len); if (err != err_status_ok) { @@ -294,8 +328,11 @@ bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) { } bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) { - if (!session_) + if (!session_) { + LOG(LS_WARNING) << "Failed to unprotect SRTCP packet: no SRTP Session"; return false; + } + *out_len = in_len; int err = srtp_unprotect_rtcp(session_, p, out_len); if (err != err_status_ok) { @@ -308,6 +345,8 @@ bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) { bool SrtpSession::SetKey(int type, const std::string& cs, const uint8* key, int len) { if (session_) { + LOG(LS_ERROR) << "Failed to create SRTP session: " + << "SRTP session already created"; return false; } @@ -325,10 +364,13 @@ bool SrtpSession::SetKey(int type, const std::string& cs, crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); // rtp is 32, crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // rtcp still 80 } else { + LOG(LS_WARNING) << "Failed to create SRTP session: unsupported" + << " cipher_suite " << cs.c_str(); return false; } if (!key || len != SRTP_MASTER_KEY_LEN) { + LOG(LS_WARNING) << "Failed to create SRTP session: invalid key"; return false; } @@ -376,7 +418,23 @@ bool SrtpSession::Init() { } void SrtpSession::HandleEvent(const srtp_event_data_t* ev) { - // TODO: Do something about events. + switch (ev->event) { + case event_ssrc_collision: + LOG(LS_INFO) << "SRTP event: SSRC collision"; + break; + case event_key_soft_limit: + LOG(LS_INFO) << "SRTP event: reached soft key usage limit"; + break; + case event_key_hard_limit: + LOG(LS_INFO) << "SRTP event: reached hard key usage limit"; + break; + case event_packet_index_limit: + LOG(LS_INFO) << "SRTP event: reached hard packet limit (2^48 packets)"; + break; + default: + LOG(LS_INFO) << "SRTP event: unknown " << ev->event; + break; + } } void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) { diff --git a/third_party/libjingle/source/talk/session/phone/testdata/video.rtpdump b/third_party/libjingle/source/talk/session/phone/testdata/video.rtpdump Binary files differindex 3b0139b..7be863e 100644 --- a/third_party/libjingle/source/talk/session/phone/testdata/video.rtpdump +++ b/third_party/libjingle/source/talk/session/phone/testdata/video.rtpdump diff --git a/third_party/libjingle/source/talk/session/phone/testdata/voice.rtpdump b/third_party/libjingle/source/talk/session/phone/testdata/voice.rtpdump Binary files differindex 5920d1d..8f0ec15 100644 --- a/third_party/libjingle/source/talk/session/phone/testdata/voice.rtpdump +++ b/third_party/libjingle/source/talk/session/phone/testdata/voice.rtpdump diff --git a/third_party/libjingle/source/talk/session/phone/videocommon.h b/third_party/libjingle/source/talk/session/phone/videocommon.h index 230b1ff..4fb311a 100644 --- a/third_party/libjingle/source/talk/session/phone/videocommon.h +++ b/third_party/libjingle/source/talk/session/phone/videocommon.h @@ -1,5 +1,5 @@ // libjingle -// Copyright 2004--2005, Google Inc. +// Copyright 2011, Google Inc. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: @@ -59,34 +59,35 @@ inline std::string GetFourccName(uint32 fourcc) { // http://developer.apple.com/quicktime/icefloe/dispatch020.html // http://www.fourcc.org/yuv.php enum FourCC { - // Canonical fourccs used in our code. + // Canonical fourcc codes used in our code. FOURCC_I420 = FOURCC('I', '4', '2', '0'), FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'), FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'), + FOURCC_M420 = FOURCC('M', '4', '2', '0'), FOURCC_24BG = FOURCC('2', '4', 'B', 'G'), - FOURCC_RGBA = FOURCC('R', 'G', 'B', 'A'), + FOURCC_ABGR = FOURCC('A', 'B', 'G', 'R'), FOURCC_BGRA = FOURCC('B', 'G', 'R', 'A'), FOURCC_ARGB = FOURCC('A', 'R', 'G', 'B'), FOURCC_MJPG = FOURCC('M', 'J', 'P', 'G'), - FOURCC_JPEG = FOURCC('J', 'P', 'E', 'G'), FOURCC_RAW = FOURCC('r', 'a', 'w', ' '), - // Next five are Bayer RGB formats. The four characters define the order of + FOURCC_NV21 = FOURCC('N', 'V', '2', '1'), + FOURCC_NV12 = FOURCC('N', 'V', '1', '2'), + // Next four are Bayer RGB formats. The four characters define the order of // the colours in each 2x2 pixel grid, going left-to-right and top-to-bottom. FOURCC_RGGB = FOURCC('R', 'G', 'G', 'B'), FOURCC_BGGR = FOURCC('B', 'G', 'G', 'R'), FOURCC_GRBG = FOURCC('G', 'R', 'B', 'G'), FOURCC_GBRG = FOURCC('G', 'B', 'R', 'G'), - // Aliases for canonical fourccs, replaced with their canonical equivalents by - // CanonicalFourCC(). + // Aliases for canonical fourcc codes, replaced with their canonical + // equivalents by CanonicalFourCC(). FOURCC_IYUV = FOURCC('I', 'Y', 'U', 'V'), // Alias for I420 FOURCC_YU12 = FOURCC('Y', 'U', '1', '2'), // Alias for I420 FOURCC_YUYV = FOURCC('Y', 'U', 'Y', 'V'), // Alias for YUY2 - FOURCC_YUVS = FOURCC('y', 'u', 'v', 's'), // Alias for YUY2 + FOURCC_YUVS = FOURCC('y', 'u', 'v', 's'), // Alias for YUY2 on Mac FOURCC_HDYC = FOURCC('H', 'D', 'Y', 'C'), // Alias for UYVY FOURCC_2VUY = FOURCC('2', 'v', 'u', 'y'), // Alias for UYVY - FOURCC_RGB1 = FOURCC('R', 'G', 'B', '1'), // Alias for RGBA - FOURCC_RGB2 = FOURCC('R', 'G', 'B', '2'), // Alias for BGRA + FOURCC_JPEG = FOURCC('J', 'P', 'E', 'G'), // Alias for MJPG FOURCC_BA81 = FOURCC('B', 'A', '8', '1'), // Alias for BGGR // Match any fourcc. diff --git a/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc b/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc index 0448853..b5e6397 100644 --- a/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc +++ b/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.cc @@ -198,6 +198,26 @@ void PseudoTcpChannel::OnSessionTerminate(Session* session) { if (stream_ != NULL) stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, -1)); } + + // Even though session_ is being destroyed, we mustn't clear the pointer, + // since we'll need it to tear down channel_. + // + // TODO(wez): Is it always the case that if channel_ != NULL then we'll get + // a channel-destroyed notification? +} + +void PseudoTcpChannel::GetOption(PseudoTcp::Option opt, int* value) { + ASSERT(signal_thread_->IsCurrent()); + CritScope lock(&cs_); + ASSERT(tcp_ != NULL); + tcp_->GetOption(opt, value); +} + +void PseudoTcpChannel::SetOption(PseudoTcp::Option opt, int value) { + ASSERT(signal_thread_->IsCurrent()); + CritScope lock(&cs_); + ASSERT(tcp_ != NULL); + tcp_->SetOption(opt, value); } // diff --git a/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h b/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h index fbcb611..e7ff8f0 100644 --- a/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h +++ b/third_party/libjingle/source/talk/session/tunnel/pseudotcpchannel.h @@ -43,18 +43,22 @@ namespace cricket { class TransportChannel; /////////////////////////////////////////////////////////////////////////////// -// ChannelStream -// Note: The lifetime of TunnelSession is complicated. It needs to survive -// until the following three conditions are true: -// 1) TunnelStream has called Close (tracked via non-null stream_) -// 2) PseudoTcp has completed (tracked via non-null tcp_) -// 3) Session has been destroyed (tracked via non-null session_) -// This is accomplished by calling CheckDestroy after these indicators change. +// PseudoTcpChannel +// Note: The PseudoTcpChannel must persist until both of: +// 1) The StreamInterface provided via GetStream has been closed. +// This is tracked via non-null stream_. +// 2) The PseudoTcp session has completed. +// This is tracked via non-null worker_thread_. When PseudoTcp is done, +// the TransportChannel is signalled to tear-down. Once the channel is +// torn down, the worker thread is purged. +// These indicators are checked by CheckDestroy, invoked whenever one of them +// changes. /////////////////////////////////////////////////////////////////////////////// -// TunnelStream -// Note: Because TunnelStream provides a stream interface, it's lifetime is -// controlled by the owner of the stream pointer. As a result, we must support -// both the TunnelSession disappearing before TunnelStream, and vice versa. +// PseudoTcpChannel::GetStream +// Note: The stream pointer returned by GetStream is owned by the caller. +// They can close & immediately delete the stream while PseudoTcpChannel still +// has cleanup work to do. They can also close the stream but not delete it +// until long after PseudoTcpChannel has finished. We must cope with both. /////////////////////////////////////////////////////////////////////////////// class PseudoTcpChannel @@ -72,8 +76,14 @@ public: sigslot::signal1<PseudoTcpChannel*> SignalChannelClosed; + // Call this when the Session used to create this channel is being torn + // down, to ensure that things get cleaned up properly. void OnSessionTerminate(Session* session); + // See the PseudoTcp class for available options. + void GetOption(PseudoTcp::Option opt, int* value); + void SetOption(PseudoTcp::Option opt, int value); + private: class InternalStream; friend class InternalStream; diff --git a/third_party/libjingle/source/talk/site_scons/talk.py b/third_party/libjingle/source/talk/site_scons/talk.py index b9e93ef..09e4dd9 100644 --- a/third_party/libjingle/source/talk/site_scons/talk.py +++ b/third_party/libjingle/source/talk/site_scons/talk.py @@ -185,7 +185,7 @@ def ExpandSconsPath(path): def AddMediaLibs(env, **kwargs): - lmi_libdir = '$GOOGLE3/third_party/lmi/files/merged/lib/' + lmi_libdir = '$GOOGLE3/../googleclient/third_party/lmi/files/lib/' if env.Bit('windows'): if env.get('COVERAGE_ENABLED'): lmi_libdir += 'win32/c_only' @@ -194,20 +194,11 @@ def AddMediaLibs(env, **kwargs): elif env.Bit('mac'): lmi_libdir += 'macos' elif env.Bit('linux'): - lmi_libdir += 'linux/x86' - - ipp_libdir = '$GOOGLE3/third_party/Intel_ipp/%s/ia32/lib' - if env.Bit('windows'): - ipp_libdir %= 'v_5_2_windows' - elif env.Bit('mac'): - ipp_libdir %= 'v_5_3_mac_os_x' - elif env.Bit('linux'): - ipp_libdir %= 'v_5_2_linux' + lmi_libdir += 'linux/x86' AddToDict(kwargs, 'libdirs', [ '$MAIN_DIR/third_party/gips/Libraries/', - ipp_libdir, lmi_libdir, ]) @@ -220,7 +211,7 @@ def AddMediaLibs(env, **kwargs): elif env.Bit('mac'): gips_lib = 'VoiceEngine_mac_universal_gcc' elif env.Bit('linux'): - gips_lib = 'VoiceEngine_Linux_external_gcc' + gips_lib = 'VoiceEngine_Linux_gcc' AddToDict(kwargs, 'libs', [ @@ -253,14 +244,6 @@ def AddMediaLibs(env, **kwargs): 'LmiUtils', 'LmiVideoCommon', 'LmiXml', - 'ippsmerged', - 'ippsemerged', - 'ippvcmerged', - 'ippvcemerged', - 'ippimerged', - 'ippiemerged', - 'ippsrmerged', - 'ippsremerged', ]) if env.Bit('windows'): @@ -268,32 +251,21 @@ def AddMediaLibs(env, **kwargs): 'dsound', 'd3d9', 'gdi32', - 'ippcorel', - 'ippscmerged', - 'ippscemerged', 'strmiids', ]) - else: - AddToDict(kwargs, 'libs', [ - 'ippcore', - 'ippacmerged', - 'ippacemerged', - 'ippccmerged', - 'ippccemerged', - 'ippchmerged', - 'ippchemerged', - 'ippcvmerged', - 'ippcvemerged', - 'ippdcmerged', - 'ippdcemerged', - 'ippjmerged', - 'ippjemerged', - 'ippmmerged', - 'ippmemerged', - 'ipprmerged', - 'ippremerged', - ]) + if env.Bit('mac'): + AddToDict(kwargs, 'FRAMEWORKS', [ + 'AudioToolbox', + 'AudioUnit', + 'Cocoa', + 'CoreAudio', + 'CoreFoundation', + 'IOKit', + 'QTKit', + 'QuickTime', + 'QuartzCore', + ]) return kwargs @@ -387,10 +359,9 @@ def MergeAndFilterByPlatform(env, params): # only build 32 bit. For 32 bit debian installer a 32 bit host is required. # ChromeOS (linux) ebuild don't support 64 bit and requires 32 bit build only # for now. -# TODO: Detect ChromeOS chroot board for ChromeOS x64 build. def Allow64BitCompile(env): - return (env.Bit('linux') and env.Bit('platform_arch_64bit') and - not env.Bit('linux_chromeos')) + return (env.Bit('linux') and env.Bit('platform_arch_64bit') + ) def MergeSettingsFromLibraryDependencies(env, params): if params.has_key('libs'): @@ -465,9 +436,13 @@ def ExtendComponent(env, component, **kwargs): 'libs' : 'LIBS', 'FRAMEWORKS' : 'FRAMEWORKS', } - prepends = { - 'ccflags' : 'CCFLAGS', - } + prepends = {} + if env.Bit('windows'): + # MSVC compile flags have precedence at the beginning ... + prepends['ccflags'] = 'CCFLAGS' + else: + # ... while GCC compile flags have precedence at the end + appends['ccflags'] = 'CCFLAGS' if GetEntry(params, 'prepend_includedirs'): prepends['includedirs'] = 'CPPPATH' else: diff --git a/third_party/modp_b64/LICENSE.html b/third_party/modp_b64/LICENSE.html deleted file mode 100644 index e156351..0000000 --- a/third_party/modp_b64/LICENSE.html +++ /dev/null @@ -1,296 +0,0 @@ - - - - -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<!-- ViewVC :: http://www.viewvc.org/ --> -<head> -<title>[chrome] Log of /trunk/src/third_party/modp_b64/LICENSE</title> -<meta name="generator" content="ViewVC 1.0.9" /> -<link rel="stylesheet" href="/viewvc/*docroot*/styles.css" type="text/css" /> - -</head> -<body> -<div class="vc_navheader"> - -<form method="get" action="/viewvc/"> - -<table style="padding:0.1em;"> -<tr> -<td> -<strong> - -<a href="/viewvc/chrome/?pathrev=74924"> - -[chrome]</a> -/ - -<a href="/viewvc/chrome/trunk/?pathrev=74924"> - -trunk</a> -/ - -<a href="/viewvc/chrome/trunk/src/?pathrev=74924"> - -src</a> -/ - -<a href="/viewvc/chrome/trunk/src/third_party/?pathrev=74924"> - -third_party</a> -/ - -<a href="/viewvc/chrome/trunk/src/third_party/modp_b64/?pathrev=74924"> - -modp_b64</a> -/ - - - -LICENSE - - -</strong> - -</td> -<td style="text-align:right;"> - - -<strong>Repository:</strong> -<select name="root" onchange="submit()"> - - -<option value="*viewroots*">Repository Listing</option> - - - - - - - - - - - - - - - - - - - - - - - -<optgroup label="Subversion Repositories"><option selected="selected">chrome</option><option>native_client</option></optgroup> - -</select> -<input type="submit" value="Go" /> - -</td> -</tr> -</table> - -</form> - -</div> -<div style="float: right; padding: 5px;"><a href="http://www.viewvc.org/"><img src="/viewvc/*docroot*/images/logo.png" alt="ViewVC logotype" width="128" height="48" /></a></div> -<h1>Log of /trunk/src/third_party/modp_b64/LICENSE</h1> - -<p style="margin:0;"> - -<a href="/viewvc/chrome/trunk/src/third_party/modp_b64/?pathrev=74924"><img src="/viewvc/*docroot*/images/back_small.png" width="16" height="16" alt="Parent Directory" /> Parent Directory</a> - - - - -</p> - -<hr /> -<table class="auto"> - - - -<tr> -<td>Links to HEAD:</td> -<td> -(<a href="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE?view=markup">view</a>) -(<a href="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE">download</a>) - -(<a href="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE?view=annotate">annotate</a>) -</td> -</tr> - - - -<tr> -<td>Sticky Revision:</td> -<td><form method="get" action="/viewvc/chrome" style="display: inline"> -<div style="display: inline"> -<input type="hidden" name="orig_pathrev" value="74924" /><input type="hidden" name="orig_pathtype" value="FILE" /><input type="hidden" name="orig_view" value="log" /><input type="hidden" name="orig_path" value="trunk/src/third_party/modp_b64/LICENSE" /><input type="hidden" name="view" value="redirect_pathrev" /> - -<input type="text" name="pathrev" value="74924" size="6"/> - -<input type="submit" value="Set" /> -</div> -</form> - -<form method="get" action="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE" style="display: inline"> -<div style="display: inline"> -<input type="hidden" name="view" value="log" /> - -<input type="submit" value="Clear" /> - -</div> -</form> - -</td> -</tr> -</table> - - - - - - - - - -<div> -<hr /> - -<a name="rev44656"></a> - - -Revision <a href="/viewvc/chrome?view=rev&revision=44656"><strong>44656</strong></a> - - -(<a href="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE?revision=44656&view=markup&pathrev=74924">view</a>) - -(<a href="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE?revision=44656&pathrev=74924">download</a>) - -(<a href="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE?annotate=44656&pathrev=74924">annotate</a>) - - - -- <a href="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE?view=log&r1=44656&pathrev=74924">[select for diffs]</a> - - - - -<br /> - -Added - -<em>Thu Apr 15 15:29:44 2010 UTC</em> (13 months, 2 weeks ago) by <em>evan@chromium.org</em> - - - - - - - -<br />File length: 1721 byte(s) - - - - - - - - - - -<pre class="vc_log">Pass tools/licenses.py for more directories. - -We're now down to only 4 dirs that don't pass the license checker. -They will require separate changes. - -Modify the license checker to only print failing dirs. - -BUG=39240 - -Review URL: <a href="http://codereview.chromium.org/1530040">http://codereview.chromium.org/1530040</a></pre> -</div> - - - - - - <hr /> -<p><a name="diff"></a> -This form allows you to request diffs between any two revisions of this file. -For each of the two "sides" of the diff, - -enter a numeric revision. - -</p> -<form method="get" action="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE" id="diff_select"> -<table cellpadding="2" cellspacing="0" class="auto"> -<tr> -<td> </td> -<td> -<input type="hidden" name="pathrev" value="74924" /><input type="hidden" name="view" value="diff" /> -Diffs between - -<input type="text" size="12" name="r1" -value="44656" /> - -and - -<input type="text" size="12" name="r2" value="44656" /> - -</td> -</tr> -<tr> -<td> </td> -<td> -Type of Diff should be a -<select name="diff_format" onchange="submit()"> -<option value="h" selected="selected">Colored Diff</option> -<option value="l" >Long Colored Diff</option> -<option value="u" >Unidiff</option> -<option value="c" >Context Diff</option> -<option value="s" >Side by Side</option> -</select> -<input type="submit" value=" Get Diffs " /> -</td> -</tr> -</table> -</form> - - -<form method="get" action="/viewvc/chrome/trunk/src/third_party/modp_b64/LICENSE"> -<div> -<hr /> -<a name="logsort"></a> -<input type="hidden" name="view" value="log" /><input type="hidden" name="pathrev" value="74924" /> -Sort log by: -<select name="logsort" onchange="submit()"> -<option value="cvs" >Not sorted</option> -<option value="date" selected="selected">Commit date</option> -<option value="rev" >Revision</option> -</select> -<input type="submit" value=" Sort " /> -</div> -</form> - - -<hr /> -<table> -<tr> -<td><address><a href="mailto:cvs-admin@insert.your.domain.here">No admin address has been configured</a></address></td> -<td style="text-align: right;"><strong><a href="/viewvc/*docroot*/help_log.html">ViewVC Help</a></strong></td> -</tr> -<tr> -<td>Powered by <a href="http://viewvc.tigris.org/">ViewVC 1.0.9</a></td> -<td style="text-align: right;"> </td> -</tr> -</table> -</body> -</html> - - diff --git a/third_party/modp_b64/README.chromium b/third_party/modp_b64/README.chromium index 16588b5..fc30450 100644 --- a/third_party/modp_b64/README.chromium +++ b/third_party/modp_b64/README.chromium @@ -1,9 +1,8 @@ Name: modp base64 decoder -Short Name: stringencoders -URL: http://code.google.com/p/stringencoders/ -Version: unknown +URL: http://modp.com/release/base64/ + +See the header of modp_b64.cc for the license terms. -Description: The modp_b64.c file was modified to remove the inclusion of modp's config.h and to fix compilation errors that occur under VC8. The file was renamed modp_b64.cc to force it to be compiled as C++ so that the inclusion of diff --git a/third_party/modp_b64/modp_b64.gyp b/third_party/modp_b64/modp_b64.gyp index 1c346e0..4ed34f4 100644 --- a/third_party/modp_b64/modp_b64.gyp +++ b/third_party/modp_b64/modp_b64.gyp @@ -6,7 +6,7 @@ 'targets': [ { 'target_name': 'modp_b64', - 'type': 'static_library', + 'type': '<(library)', 'msvs_guid': '7100F41F-868D-4E99-80A2-AF8E6574749D', 'sources': [ 'modp_b64.cc', |