Update to 0.5.2: improved builds, encryption option on call.
git-svn-id: http://libjingle.googlecode.com/svn/trunk@51 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/CHANGELOG b/CHANGELOG
index d1574d8..e0c746e 100644
--- a/CHANGELOG
+++ b/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/README b/README
index b127b56..f70b726 100644
--- a/README
+++ b/README
@@ -30,6 +30,8 @@
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/talk/base/buffer.h b/talk/base/buffer.h
new file mode 100644
index 0000000..311cfad
--- /dev/null
+++ b/talk/base/buffer.h
@@ -0,0 +1,119 @@
+/*
+ * libjingle
+ * Copyright 2004-2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_BUFFER_H_
+#define TALK_BASE_BUFFER_H_
+
+#include <cstring>
+
+#include "talk/base/scoped_ptr.h"
+
+namespace talk_base {
+
+// Basic buffer class, can be grown and shrunk dynamically.
+// Unlike std::string/vector, does not initialize data when expanding capacity.
+class Buffer {
+ public:
+ Buffer() {
+ Construct(NULL, 0, 0);
+ }
+ Buffer(const void* data, size_t length) {
+ Construct(data, length, length);
+ }
+ Buffer(const void* data, size_t length, size_t capacity) {
+ Construct(data, length, capacity);
+ }
+ Buffer(const Buffer& buf) {
+ Construct(buf.data(), buf.length(), buf.length());
+ }
+
+ const char* data() const { return data_.get(); }
+ char* data() { return data_.get(); }
+ // TODO: should this be size(), like STL?
+ size_t length() const { return length_; }
+ size_t capacity() const { return capacity_; }
+
+ Buffer& operator=(const Buffer& buf) {
+ if (&buf != this) {
+ Construct(buf.data(), buf.length(), buf.length());
+ }
+ return *this;
+ }
+ bool operator==(const Buffer& buf) const {
+ return (length_ == buf.length() &&
+ memcmp(data_.get(), buf.data(), length_) == 0);
+ }
+ bool operator!=(const Buffer& buf) const {
+ return !operator==(buf);
+ }
+
+ void SetData(const void* data, size_t length) {
+ ASSERT(data != NULL || length == 0);
+ SetLength(length);
+ memcpy(data_.get(), data, length);
+ }
+ void AppendData(const void* data, size_t length) {
+ ASSERT(data != NULL || length == 0);
+ size_t old_length = length_;
+ SetLength(length_ + length);
+ memcpy(data_.get() + old_length, data, length);
+ }
+ void SetLength(size_t length) {
+ SetCapacity(length);
+ length_ = length;
+ }
+ void SetCapacity(size_t capacity) {
+ if (capacity > capacity_) {
+ talk_base::scoped_array<char> data(new char[capacity]);
+ memcpy(data.get(), data_.get(), length_);
+ data_.swap(data);
+ capacity_ = capacity;
+ }
+ }
+
+ void TransferTo(Buffer* buf) {
+ ASSERT(buf != NULL);
+ buf->data_.reset(data_.release());
+ buf->length_ = length_;
+ buf->capacity_ = capacity_;
+ Construct(NULL, 0, 0);
+ }
+
+ protected:
+ void Construct(const void* data, size_t length, size_t capacity) {
+ data_.reset(new char[capacity_ = capacity]);
+ SetData(data, length);
+ }
+
+ scoped_array<char> data_;
+ size_t length_;
+ size_t capacity_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_BUFFER_H_
diff --git a/talk/base/latebindingsymboltable.cc b/talk/base/latebindingsymboltable.cc
new file mode 100644
index 0000000..f9d59ab
--- /dev/null
+++ b/talk/base/latebindingsymboltable.cc
@@ -0,0 +1,111 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/latebindingsymboltable.h"
+
+#ifdef LINUX
+#include <dlfcn.h>
+#endif
+
+#include "talk/base/logging.h"
+
+namespace talk_base {
+
+inline static const char *GetDllError() {
+#ifdef LINUX
+ char *err = dlerror();
+ if (err) {
+ return err;
+ } else {
+ return "No error";
+ }
+#else
+#error Not implemented
+#endif
+}
+
+DllHandle InternalLoadDll(const char dll_name[]) {
+#ifdef LINUX
+ DllHandle handle = dlopen(dll_name, RTLD_NOW);
+#else
+#error Not implemented
+#endif
+ if (handle == kInvalidDllHandle) {
+ LOG(LS_WARNING) << "Can't load " << dll_name << ": " << GetDllError();
+ }
+ return handle;
+}
+
+void InternalUnloadDll(DllHandle handle) {
+#ifdef LINUX
+ if (dlclose(handle) != 0) {
+ LOG(LS_ERROR) << GetDllError();
+ }
+#else
+#error Not implemented
+#endif
+}
+
+static bool LoadSymbol(DllHandle handle,
+ const char *symbol_name,
+ void **symbol) {
+#ifdef LINUX
+ *symbol = dlsym(handle, symbol_name);
+ char *err = dlerror();
+ if (err) {
+ LOG(LS_ERROR) << "Error loading symbol " << symbol_name << ": " << err;
+ return false;
+ } else if (!*symbol) {
+ LOG(LS_ERROR) << "Symbol " << symbol_name << " is NULL";
+ return false;
+ }
+ return true;
+#else
+#error Not implemented
+#endif
+}
+
+// This routine MUST assign SOME value for every symbol, even if that value is
+// NULL, or else some symbols may be left with uninitialized data that the
+// caller may later interpret as a valid address.
+bool InternalLoadSymbols(DllHandle handle,
+ int num_symbols,
+ const char *const symbol_names[],
+ void *symbols[]) {
+#ifdef LINUX
+ // Clear any old errors.
+ dlerror();
+#endif
+ for (int i = 0; i < num_symbols; ++i) {
+ if (!LoadSymbol(handle, symbol_names[i], &symbols[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace talk_base
diff --git a/talk/base/latebindingsymboltable.h b/talk/base/latebindingsymboltable.h
new file mode 100644
index 0000000..994c26a
--- /dev/null
+++ b/talk/base/latebindingsymboltable.h
@@ -0,0 +1,193 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_LATEBINDINGSYMBOLTABLE_H_
+#define TALK_BASE_LATEBINDINGSYMBOLTABLE_H_
+
+#include <stddef.h> // for NULL
+#include <string.h>
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+
+// This file provides macros for creating "symbol table" classes to simplify the
+// dynamic loading of symbols from DLLs. Currently the implementation only
+// supports Linux and pure C symbols.
+// See talk/sound/pulseaudiosymboltable.(h|cc) for an example.
+
+namespace talk_base {
+
+#ifdef LINUX
+typedef void *DllHandle;
+
+const DllHandle kInvalidDllHandle = NULL;
+#else
+#error Not implemented
+#endif
+
+// These are helpers for use only by the class below.
+DllHandle InternalLoadDll(const char dll_name[]);
+
+void InternalUnloadDll(DllHandle handle);
+
+bool InternalLoadSymbols(DllHandle handle,
+ int num_symbols,
+ const char *const symbol_names[],
+ void *symbols[]);
+
+template <int SYMBOL_TABLE_SIZE,
+ const char kDllName[],
+ const char *const kSymbolNames[]>
+class LateBindingSymbolTable {
+ public:
+ LateBindingSymbolTable()
+ : handle_(kInvalidDllHandle),
+ undefined_symbols_(false) {
+ memset(symbols_, 0, sizeof(symbols_));
+ }
+
+ ~LateBindingSymbolTable() {
+ Unload();
+ }
+
+ static int NumSymbols() {
+ return SYMBOL_TABLE_SIZE;
+ }
+
+ // We do not use this, but we offer it for theoretical convenience.
+ static const char *GetSymbolName(int index) {
+ ASSERT(index < NumSymbols());
+ return kSymbolNames[index];
+ }
+
+ bool IsLoaded() const {
+ return handle_ != kInvalidDllHandle;
+ }
+
+ // Loads the DLL and the symbol table. Returns true iff the DLL and symbol
+ // table loaded successfully.
+ bool Load() {
+ if (IsLoaded()) {
+ return true;
+ }
+ if (undefined_symbols_) {
+ // We do not attempt to load again because repeated attempts are not
+ // likely to succeed and DLL loading is costly.
+ LOG(LS_ERROR) << "We know there are undefined symbols";
+ return false;
+ }
+ handle_ = InternalLoadDll(kDllName);
+ if (!IsLoaded()) {
+ return false;
+ }
+ if (!InternalLoadSymbols(handle_, NumSymbols(), kSymbolNames, symbols_)) {
+ undefined_symbols_ = true;
+ Unload();
+ return false;
+ }
+ return true;
+ }
+
+ void Unload() {
+ if (!IsLoaded()) {
+ return;
+ }
+ InternalUnloadDll(handle_);
+ handle_ = kInvalidDllHandle;
+ memset(symbols_, 0, sizeof(symbols_));
+ }
+
+ // Retrieves the given symbol. NOTE: Recommended to use LATESYM_GET below
+ // instead of this.
+ void *GetSymbol(int index) const {
+ ASSERT(IsLoaded());
+ ASSERT(index < NumSymbols());
+ return symbols_[index];
+ }
+
+ private:
+ DllHandle handle_;
+ bool undefined_symbols_;
+ void *symbols_[SYMBOL_TABLE_SIZE];
+
+ DISALLOW_COPY_AND_ASSIGN(LateBindingSymbolTable);
+};
+
+// This macro must be invoked in a header to declare a symbol table class.
+#define LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(ClassName) \
+enum {
+
+// This macro must be invoked in the header declaration once for each symbol
+// (recommended to use an X-Macro to avoid duplication).
+// This macro defines an enum with names built from the symbols, which
+// essentially creates a hash table in the compiler from symbol names to their
+// indices in the symbol table class.
+#define LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(ClassName, sym) \
+ ClassName##_SYMBOL_TABLE_INDEX_##sym,
+
+// This macro completes the header declaration.
+#define LATE_BINDING_SYMBOL_TABLE_DECLARE_END(ClassName) \
+ ClassName##_SYMBOL_TABLE_SIZE \
+}; \
+\
+extern const char ClassName##_kDllName[]; \
+extern const char *const \
+ ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE]; \
+\
+typedef ::talk_base::LateBindingSymbolTable<ClassName##_SYMBOL_TABLE_SIZE, \
+ ClassName##_kDllName, \
+ ClassName##_kSymbolNames> \
+ ClassName;
+
+// This macro must be invoked in a .cc file to define a previously-declared
+// symbol table class.
+#define LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(ClassName, dllName) \
+const char ClassName##_kDllName[] = dllName; \
+const char *const ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE] = {
+
+// This macro must be invoked in the .cc definition once for each symbol
+// (recommended to use an X-Macro to avoid duplication).
+// This would have to use the mangled name if we were to ever support C++
+// symbols.
+#define LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(ClassName, sym) \
+ #sym,
+
+#define LATE_BINDING_SYMBOL_TABLE_DEFINE_END(ClassName) \
+};
+
+// Index of a given symbol in the given symbol table class.
+#define LATESYM_INDEXOF(ClassName, sym) \
+ (ClassName##_SYMBOL_TABLE_INDEX_##sym)
+
+// Returns a reference to the given late-binded symbol, with the correct type.
+#define LATESYM_GET(ClassName, inst, sym) \
+ (*reinterpret_cast<typeof(&sym)>( \
+ (inst)->GetSymbol(LATESYM_INDEXOF(ClassName, sym))))
+
+} // namespace talk_base
+
+#endif // TALK_BASE_LATEBINDINGSYMBOLTABLE_H_
diff --git a/talk/base/physicalsocketserver.cc b/talk/base/physicalsocketserver.cc
index d97cba7..adddb0d 100644
--- a/talk/base/physicalsocketserver.cc
+++ b/talk/base/physicalsocketserver.cc
@@ -78,12 +78,6 @@
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 @@
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 @@
udp_ = (SOCK_DGRAM == type);
UpdateLastError();
if (udp_)
- enabled_events_ = kfRead | kfWrite;
+ enabled_events_ = DE_READ | DE_WRITE;
return s_ != INVALID_SOCKET;
}
@@ -226,12 +220,12 @@
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;
}
@@ -292,7 +286,7 @@
//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
if ((sent < 0) && IsBlockingError(error_)) {
- enabled_events_ |= kfWrite;
+ enabled_events_ |= DE_WRITE;
}
return sent;
}
@@ -312,7 +306,7 @@
UpdateLastError();
ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false
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 +321,14 @@
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 +346,7 @@
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 +359,7 @@
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 +375,7 @@
UpdateLastError();
if (s == INVALID_SOCKET)
return NULL;
- enabled_events_ |= kfAccept;
+ enabled_events_ |= DE_ACCEPT;
if (paddr != NULL)
paddr->FromSockAddr(saddr);
return ss_->WrapSocket(s);
@@ -528,16 +522,6 @@
};
#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 +547,7 @@
}
virtual uint32 GetRequestedEvents() {
- return kfRead;
+ return DE_READ;
}
virtual void OnPreEvent(uint32 ff) {
@@ -609,7 +593,7 @@
}
virtual uint32 GetRequestedEvents() {
- return kfRead;
+ return DE_READ;
}
virtual void OnPreEvent(uint32 ff) {
@@ -833,30 +817,30 @@
}
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 +888,28 @@
}
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 +925,15 @@
#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;
}
@@ -1065,36 +1038,36 @@
}
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)) {
+ if (((ff & DE_CLOSE) != 0) && (id_ == cache_id)) {
//LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] OnClose() Error: " << err;
signal_close_ = true;
signal_err_ = err;
@@ -1275,9 +1248,9 @@
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 +1296,12 @@
// 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 +1309,14 @@
// 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;
}
}
@@ -1556,21 +1529,21 @@
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/talk/base/physicalsocketserver.h b/talk/base/physicalsocketserver.h
index 282e14c..d2b40aa 100644
--- a/talk/base/physicalsocketserver.h
+++ b/talk/base/physicalsocketserver.h
@@ -38,14 +38,38 @@
typedef int SOCKET;
#endif // POSIX
-namespace talk_base {
+namespace talk_base {
-class Dispatcher;
+// Event constants for the Dispatcher class.
+enum DispatcherEvent {
+ DE_READ = 0x0001,
+ DE_WRITE = 0x0002,
+ DE_CONNECT = 0x0004,
+ DE_CLOSE = 0x0008,
+ DE_ACCEPT = 0x0010,
+};
+
class Signaler;
#ifdef POSIX
class PosixSignalDeliveryDispatcher;
#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 {
public:
diff --git a/talk/base/ratetracker.cc b/talk/base/ratetracker.cc
new file mode 100644
index 0000000..d5207cd
--- /dev/null
+++ b/talk/base/ratetracker.cc
@@ -0,0 +1,80 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/ratetracker.h"
+#include "talk/base/time.h"
+
+namespace talk_base {
+
+RateTracker::RateTracker()
+ : total_units_(0), units_second_(0),
+ last_units_second_time_(static_cast<uint32>(-1)),
+ last_units_second_calc_(0) {
+}
+
+size_t RateTracker::total_units() const {
+ return total_units_;
+}
+
+size_t RateTracker::units_second() {
+ // Snapshot units / 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 units over that interval.
+ uint32 current_time = Time();
+ if (last_units_second_time_ != static_cast<uint32>(-1)) {
+ int delta = talk_base::TimeDiff(current_time, last_units_second_time_);
+ if (delta >= 1000) {
+ int fraction_time = delta % 1000;
+ int seconds = delta / 1000;
+ int fraction_units =
+ static_cast<int>(total_units_ - last_units_second_calc_) *
+ fraction_time / delta;
+ // Compute "units received during the interval" / "seconds in interval"
+ units_second_ =
+ (total_units_ - last_units_second_calc_ - fraction_units) / seconds;
+ last_units_second_time_ = current_time - fraction_time;
+ last_units_second_calc_ = total_units_ - fraction_units;
+ }
+ }
+ if (last_units_second_time_ == static_cast<uint32>(-1)) {
+ last_units_second_time_ = current_time;
+ last_units_second_calc_ = total_units_;
+ }
+
+ return units_second_;
+}
+
+void RateTracker::Update(size_t units) {
+ total_units_ += units;
+}
+
+uint32 RateTracker::Time() const {
+ return talk_base::Time();
+}
+
+} // namespace talk_base
diff --git a/talk/base/ratetracker.h b/talk/base/ratetracker.h
new file mode 100644
index 0000000..28c7bb3
--- /dev/null
+++ b/talk/base/ratetracker.h
@@ -0,0 +1,59 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_RATETRACKER_H_
+#define TALK_BASE_RATETRACKER_H_
+
+#include <stdlib.h>
+#include "talk/base/basictypes.h"
+
+namespace talk_base {
+
+// Computes instantaneous units per second.
+class RateTracker {
+ public:
+ RateTracker();
+ virtual ~RateTracker() {}
+
+ size_t total_units() const;
+ size_t units_second();
+ void Update(size_t units);
+
+ protected:
+ // overrideable for tests
+ virtual uint32 Time() const;
+
+ private:
+ size_t total_units_;
+ size_t units_second_;
+ uint32 last_units_second_time_;
+ size_t last_units_second_calc_;
+};
+
+} // namespace talk_base
+
+#endif // TALK_BASE_RATETRACKER_H_
diff --git a/talk/base/socket.h b/talk/base/socket.h
index 3094918..a55b3dc 100644
--- a/talk/base/socket.h
+++ b/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 @@
// 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 @@
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/talk/base/stringencode.cc b/talk/base/stringencode.cc
index d588514..bc80e24 100644
--- a/talk/base/stringencode.cc
+++ b/talk/base/stringencode.cc
@@ -512,18 +512,21 @@
}
size_t split(const std::string& source, char delimiter,
- std::vector<std::string>* fields)
-{
+ 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();
}
diff --git a/talk/examples/call/call_main.cc b/talk/examples/call/call_main.cc
index 541aca8..4312399 100644
--- a/talk/examples/call/call_main.cc
+++ b/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 @@
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 @@
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 @@
} 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 @@
client->SetPortAllocatorFlags(portallocator_flags);
client->SetAllowLocalIps(true);
client->SetInitialProtocol(initial_protocol);
+ client->SetSecurePolicy(secure_policy);
console->Start();
if (debug) {
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index eb761d1..ecfc0b3 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -237,12 +237,20 @@
}
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)
+ : 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)
#ifdef USE_TALK_SOUND
, sound_system_factory_(NULL)
#endif
@@ -385,6 +393,7 @@
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/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index a5686b4..846b785 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -34,6 +34,7 @@
#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"
@@ -68,7 +69,6 @@
class Call;
struct CallOptions;
class SessionManagerTask;
-enum SignalingProtocol;
}
struct RosterItem {
@@ -114,6 +114,10 @@
initial_protocol_ = initial_protocol;
}
+ void SetSecurePolicy(cricket::SecureMediaPolicy secure_policy) {
+ secure_policy_ = secure_policy;
+ }
+
typedef std::map<buzz::Jid, buzz::Muc*> MucMap;
@@ -193,6 +197,7 @@
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_;
diff --git a/talk/libjingle.scons b/talk/libjingle.scons
index 09a3bc1..d5ca366 100644
--- a/talk/libjingle.scons
+++ b/talk/libjingle.scons
@@ -100,6 +100,7 @@
"base/httpclient.cc",
"base/httpcommon.cc",
"base/httprequest.cc",
+ "base/latebindingsymboltable.cc",
"base/logging.cc",
"base/md5c.c",
"base/messagehandler.cc",
@@ -111,6 +112,7 @@
"base/physicalsocketserver.cc",
"base/proxydetect.cc",
"base/proxyinfo.cc",
+ "base/ratetracker.cc",
"base/signalthread.cc",
"base/socketadapters.cc",
"base/socketaddress.cc",
@@ -164,11 +166,13 @@
"session/phone/channelmanager.cc",
"session/phone/codec.cc",
"session/phone/devicemanager.cc",
+ "session/phone/libudevsymboltable.cc",
"session/phone/filemediaengine.cc",
"session/phone/mediaengine.cc",
"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 @@
"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/talk/main.scons b/talk/main.scons
index a1348ff..7394066 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -92,6 +92,8 @@
CCPDBFLAGS = '',
CCFLAGS_DEBUG = '',
CCFLAGS_OPTIMIZED = '',
+ # We force a x86 target even when building on x64 Windows platforms.
+ TARGET_ARCH = 'x86',
)
win_env.Append(
@@ -210,6 +212,9 @@
'/MT', # link with LIBCMT.LIB (multi-threaded, static linked crt)
'/GS', # enable security checks
],
+ LINKFLAGS = [
+ '/safeseh',
+ ],
)
envs.append(win_opt_env)
@@ -230,7 +235,6 @@
'_REENTRANT',
],
CCFLAGS = [
- '-m32',
'-Wall',
'-Werror',
'-Wno-switch',
@@ -241,9 +245,6 @@
'-Wno-ctor-dtor-privacy',
'-fno-rtti',
],
- LINKFLAGS = [
- '-m32',
- ],
)
#-------------------------------------------------------------------------------
@@ -262,6 +263,7 @@
'MAC_OS_X_VERSION_MIN_REQUIRED=1040',
],
CCFLAGS = [
+ '-m32',
'-arch', 'i386',
'-isysroot', '/Developer/SDKs/MacOSX10.5.sdk',
'-fasm-blocks',
@@ -274,6 +276,7 @@
# TODO: consider only defining for libs that actually have objc.
'-ObjC',
'-arch', 'i386',
+ '-m32',
],
FRAMEWORKS = [
'CoreServices',
@@ -281,6 +284,10 @@
'Security',
'SystemConfiguration',
'OpenGL',
+ 'CoreAudio',
+ 'QuickTime',
+ 'Cocoa',
+ 'QTKit',
]
)
@@ -373,6 +380,14 @@
# 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',
diff --git a/talk/p2p/base/constants.cc b/talk/p2p/base/constants.cc
index 1becb39..12d26f5 100644
--- a/talk/p2p/base/constants.cc
+++ b/talk/p2p/base/constants.cc
@@ -63,6 +63,8 @@
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 LN_ERROR("error");
const buzz::QName QN_GINGLE_REDIRECT(true, NS_GINGLE, "redirect");
diff --git a/talk/p2p/base/constants.h b/talk/p2p/base/constants.h
index 9f93cbc..55f12c6 100644
--- a/talk/p2p/base/constants.h
+++ b/talk/p2p/base/constants.h
@@ -79,6 +79,8 @@
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 LN_ERROR;
extern const buzz::QName QN_GINGLE_REDIRECT;
diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc
index 2aa66a4..0f38913 100644
--- a/talk/p2p/base/port.cc
+++ b/talk/p2p/base/port.cc
@@ -25,9 +25,7 @@
* 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>
@@ -40,13 +38,6 @@
#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 {
@@ -894,19 +885,19 @@
}
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 +920,4 @@
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/talk/p2p/base/port.h b/talk/p2p/base/port.h
index 88b15c6..af40655 100644
--- a/talk/p2p/base/port.h
+++ b/talk/p2p/base/port.h
@@ -35,6 +35,7 @@
#include "talk/base/network.h"
#include "talk/base/socketaddress.h"
#include "talk/base/proxyinfo.h"
+#include "talk/base/ratetracker.h"
#include "talk/base/sigslot.h"
#include "talk/base/thread.h"
#include "talk/p2p/base/candidate.h"
@@ -67,21 +68,6 @@
: 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.
@@ -384,8 +370,8 @@
// side
std::vector<uint32> pings_since_last_response_;
- RateTracker recv_rate_tracker_;
- RateTracker send_rate_tracker_;
+ talk_base::RateTracker recv_rate_tracker_;
+ talk_base::RateTracker send_rate_tracker_;
// Callbacks from ConnectionRequest
void OnConnectionRequestResponse(StunMessage *response, uint32 rtt);
diff --git a/talk/p2p/base/session.cc b/talk/p2p/base/session.cc
index 38b91ff..8ed4ae8 100644
--- a/talk/p2p/base/session.cc
+++ b/talk/p2p/base/session.cc
@@ -646,6 +646,14 @@
case ACTION_TRANSPORT_ACCEPT:
valid = OnTransportAcceptMessage(msg, &error);
break;
+ case ACTION_NOTIFY:
+ case ACTION_UPDATE:
+ // TODO: Process these non-standard messages, but
+ // only once we figure out how in a jingle-specific way (or
+ // remove the need altogether). For now, just don't send an
+ // error back, because it disrupts call establishment.
+ valid = true;
+ break;
default:
valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST,
"unknown session message type",
diff --git a/talk/p2p/base/sessionmessages.cc b/talk/p2p/base/sessionmessages.cc
index cfd035d..a71c6f0 100644
--- a/talk/p2p/base/sessionmessages.cc
+++ b/talk/p2p/base/sessionmessages.cc
@@ -70,6 +70,10 @@
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;
}
diff --git a/talk/p2p/base/sessionmessages.h b/talk/p2p/base/sessionmessages.h
index eee8452..6ff7c30 100644
--- a/talk/p2p/base/sessionmessages.h
+++ b/talk/p2p/base/sessionmessages.h
@@ -60,6 +60,12 @@
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,
};
// Abstraction of a <jingle> element within an <iq> stanza, per XMPP
diff --git a/talk/p2p/base/transportchannelproxy.cc b/talk/p2p/base/transportchannelproxy.cc
index 405ed76..1be9194 100644
--- a/talk/p2p/base/transportchannelproxy.cc
+++ b/talk/p2p/base/transportchannelproxy.cc
@@ -75,6 +75,13 @@
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/talk/p2p/base/transportchannelproxy.h b/talk/p2p/base/transportchannelproxy.h
index 2184477..4601185 100644
--- a/talk/p2p/base/transportchannelproxy.h
+++ b/talk/p2p/base/transportchannelproxy.h
@@ -55,6 +55,7 @@
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/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index 76c1cb4..1f8b0e1 100644
--- a/talk/session/phone/channel.cc
+++ b/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,40 @@
#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;
+};
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 +101,7 @@
BaseChannel::~BaseChannel() {
ASSERT(worker_thread_ == talk_base::Thread::Current());
StopConnectionMonitor();
- Clear();
+ 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 +187,12 @@
}
}
-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 +219,29 @@
// 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);
+ rtcp_mux_filter_.DemuxRtcp(packet.data(), packet.length()));
+ HandlePacket(rtcp, &packet);
}
-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;
+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 +250,114 @@
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);
+ 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;
+ }
}
- if (!res) {
- LOG(LS_ERROR) << "Failed to unprotect "
- << PacketType(rtcp) << " packet, size " << len;
- 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());
}
}
}
@@ -493,6 +550,14 @@
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;
+ }
}
}
@@ -1007,64 +1072,4 @@
}
-// 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);
-}
-
} // namespace cricket
diff --git a/talk/session/phone/channel.h b/talk/session/phone/channel.h
index b82a4bf..c57f9ab 100644
--- a/talk/session/phone/channel.h
+++ b/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,13 @@
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
};
// 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 {
@@ -174,16 +151,16 @@
talk_base::MessageList* removed = NULL);
// 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);
diff --git a/talk/session/phone/channelmanager.cc b/talk/session/phone/channelmanager.cc
index 901a20a..0d9984e 100644
--- a/talk/session/phone/channelmanager.cc
+++ b/talk/session/phone/channelmanager.cc
@@ -237,8 +237,10 @@
audio_out_device_.clear();
}
if (!SetVideoOptions(camera_device_)) {
- // TODO: Consider resetting to the default cam here.
- camera_device_.clear();
+ // Perhaps it's been unplugged, fall back to default device
+ if (!SetVideoOptions("")) {
+ camera_device_.clear();
+ }
}
// Now apply the default video codec that has been set earlier.
if (default_video_encoder_config_.max_codec.id != 0) {
@@ -501,20 +503,8 @@
// 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);
- }
+ VideoOptions options(&device);
+ ret = (Send(MSG_SETVIDEOOPTIONS, &options) && options.result);
}
// If everything worked, retain the name of the selected camera.
diff --git a/talk/session/phone/devicemanager.cc b/talk/session/phone/devicemanager.cc
index e32ed63..f7f993c 100644
--- a/talk/session/phone/devicemanager.cc
+++ b/talk/session/phone/devicemanager.cc
@@ -43,6 +43,7 @@
#include <CoreAudio/CoreAudio.h>
#include <QuickTime/QuickTime.h>
#elif LINUX
+#include <libudev.h>
#include <unistd.h>
#ifndef USE_TALK_SOUND
#include <alsa/asoundlib.h>
@@ -50,7 +51,9 @@
#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"
#endif
@@ -67,27 +70,52 @@
// 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() { return true; }
- void Stop() {}
+ explicit DeviceWatcher(DeviceManager* dm);
+ bool Start();
+ void Stop();
+ private:
+ DeviceManager* manager_;
+ void* impl_;
};
#endif
@@ -107,7 +135,9 @@
#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);
@@ -202,86 +232,6 @@
#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
@@ -738,6 +688,24 @@
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,6 +868,112 @@
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
diff --git a/talk/session/phone/devicemanager.h b/talk/session/phone/devicemanager.h
index 594e9bd..d9438d4 100644
--- a/talk/session/phone/devicemanager.h
+++ b/talk/session/phone/devicemanager.h
@@ -83,9 +83,6 @@
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
sigslot::signal0<> SignalDevicesChange;
diff --git a/talk/session/phone/devicemanager_mac.mm b/talk/session/phone/devicemanager_mac.mm
index 1a14e95..3537ed9 100644
--- a/talk/session/phone/devicemanager_mac.mm
+++ b/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 @@
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 @@
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/talk/session/phone/filemediaengine.cc b/talk/session/phone/filemediaengine.cc
index 4929933..49d92b6 100644
--- a/talk/session/phone/filemediaengine.cc
+++ b/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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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/talk/session/phone/filemediaengine.h b/talk/session/phone/filemediaengine.h
index cc6788c..2276772 100644
--- a/talk/session/phone/filemediaengine.h
+++ b/talk/session/phone/filemediaengine.h
@@ -144,8 +144,8 @@
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 @@
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/talk/session/phone/libudevsymboltable.cc b/talk/session/phone/libudevsymboltable.cc
new file mode 100644
index 0000000..b312306
--- /dev/null
+++ b/talk/session/phone/libudevsymboltable.cc
@@ -0,0 +1,39 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/phone/libudevsymboltable.h"
+
+namespace cricket {
+
+LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(LibUDevSymbolTable, "libudev.so.0")
+#define X(sym) \
+ LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(LibUDevSymbolTable, sym)
+LIBUDEV_SYMBOLS_LIST
+#undef X
+LATE_BINDING_SYMBOL_TABLE_DEFINE_END(LibUDevSymbolTable)
+
+} // namespace cricket
diff --git a/talk/session/phone/libudevsymboltable.h b/talk/session/phone/libudevsymboltable.h
new file mode 100644
index 0000000..0dbef6c
--- /dev/null
+++ b/talk/session/phone/libudevsymboltable.h
@@ -0,0 +1,58 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_SESSION_PHONE_LIBUDEVSYMBOLTABLE_H_
+#define TALK_SESSION_PHONE_LIBUDEVSYMBOLTABLE_H_
+
+#include "talk/base/latebindingsymboltable.h"
+
+namespace cricket {
+
+// The libudev symbols we need, as an X-Macro list.
+// This list must contain precisely every libudev function that is used in
+// devicemanager.cc.
+#define LIBUDEV_SYMBOLS_LIST \
+ X(udev_device_unref) \
+ X(udev_monitor_enable_receiving) \
+ X(udev_monitor_filter_add_match_subsystem_devtype) \
+ X(udev_monitor_get_fd) \
+ X(udev_monitor_new_from_netlink) \
+ X(udev_monitor_receive_device) \
+ X(udev_monitor_unref) \
+ X(udev_new) \
+ X(udev_unref)
+
+LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(LibUDevSymbolTable)
+#define X(sym) \
+ LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(LibUDevSymbolTable, sym)
+LIBUDEV_SYMBOLS_LIST
+#undef X
+LATE_BINDING_SYMBOL_TABLE_DECLARE_END(LibUDevSymbolTable)
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_LIBUDEVSYMBOLTABLE_H_
diff --git a/talk/session/phone/mediachannel.h b/talk/session/phone/mediachannel.h
index 01bd8b5..d3951c9 100644
--- a/talk/session/phone/mediachannel.h
+++ b/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 VideoMediaChannelOptions {
+ OPT_INTERPOLATE = 0x10000 // Increase the output framerate by 2x by
+ // interpolating frames
};
class MediaChannel : public sigslot::has_slots<> {
@@ -60,8 +70,8 @@
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 @@
}
// 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,24 +111,78 @@
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 VoiceMediaInfo : MediaInfo {
+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 VideoMediaInfo : MediaInfo {
- int receive_framerate;
- int send_framerate;
+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 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 {
@@ -195,7 +259,7 @@
// 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.
diff --git a/talk/session/phone/mediamonitor.h b/talk/session/phone/mediamonitor.h
index 9eda2aa..6b964aa 100644
--- a/talk/session/phone/mediamonitor.h
+++ b/talk/session/phone/mediamonitor.h
@@ -74,10 +74,8 @@
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/talk/session/phone/mediasessionclient.cc b/talk/session/phone/mediasessionclient.cc
index f16b531..c862cd5 100644
--- a/talk/session/phone/mediasessionclient.cc
+++ b/talk/session/phone/mediasessionclient.cc
@@ -562,7 +562,7 @@
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/talk/session/phone/rtcpmuxfilter.cc b/talk/session/phone/rtcpmuxfilter.cc
new file mode 100644
index 0000000..8654214
--- /dev/null
+++ b/talk/session/phone/rtcpmuxfilter.cc
@@ -0,0 +1,92 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/phone/rtcpmuxfilter.h"
+
+#include "talk/base/logging.h"
+
+namespace cricket {
+
+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/rfc5761.
+ // 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);
+}
+
+} // namespace cricket
diff --git a/talk/session/phone/rtcpmuxfilter.h b/talk/session/phone/rtcpmuxfilter.h
new file mode 100644
index 0000000..0224e9f
--- /dev/null
+++ b/talk/session/phone/rtcpmuxfilter.h
@@ -0,0 +1,61 @@
+/*
+ * libjingle
+ * Copyright 2004--2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_SESSION_PHONE_RTCPMUXFILTER_H_
+#define TALK_SESSION_PHONE_RTCPMUXFILTER_H_
+
+#include "talk/base/basictypes.h"
+#include "talk/p2p/base/sessiondescription.h"
+
+namespace cricket {
+
+// RTCP Muxer, as defined in RFC 5761 (http://tools.ietf.org/html/rfc5761)
+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_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_RTCPMUXFILTER_H_
diff --git a/talk/session/phone/rtpdump.cc b/talk/session/phone/rtpdump.cc
index bd67cad..deae20f 100644
--- a/talk/session/phone/rtpdump.cc
+++ b/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 @@
}
// 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 @@
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;
@@ -241,8 +263,9 @@
if (packet->IsValidRtpPacket()) {
// Get the old RTP sequence number and timestamp.
uint16 sequence;
+ packet->GetRtpSeqNum(&sequence);
uint32 timestamp;
- ReadRtpSeqNumAndTimestamp(*packet, &sequence, ×tamp);
+ 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 @@
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/talk/session/phone/rtpdump.h b/talk/session/phone/rtpdump.h
index 66275e6..f87b922 100644
--- a/talk/session/phone/rtpdump.h
+++ b/talk/session/phone/rtpdump.h
@@ -71,9 +71,12 @@
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 @@
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 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/talk/session/phone/srtpfilter.cc b/talk/session/phone/srtpfilter.cc
index f8d2dd4..71f1991 100644
--- a/talk/session/phone/srtpfilter.cc
+++ b/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::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::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::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::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 @@
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::ResetParams() {
offer_params_.clear();
state_ = ST_INIT;
+ LOG(LS_INFO) << "SRTP reset to init state";
return true;
}
@@ -252,11 +269,18 @@
}
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::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::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::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::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 @@
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 @@
}
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/talk/session/phone/testdata/video.rtpdump b/talk/session/phone/testdata/video.rtpdump
index 3b0139b..7be863e 100644
--- a/talk/session/phone/testdata/video.rtpdump
+++ b/talk/session/phone/testdata/video.rtpdump
Binary files differ
diff --git a/talk/session/phone/testdata/voice.rtpdump b/talk/session/phone/testdata/voice.rtpdump
index 5920d1d..8f0ec15 100644
--- a/talk/session/phone/testdata/voice.rtpdump
+++ b/talk/session/phone/testdata/voice.rtpdump
Binary files differ
diff --git a/talk/session/phone/videocommon.h b/talk/session/phone/videocommon.h
index 230b1ff..5354dbe 100644
--- a/talk/session/phone/videocommon.h
+++ b/talk/session/phone/videocommon.h
@@ -85,7 +85,7 @@
FOURCC_YUVS = FOURCC('y', 'u', 'v', 's'), // Alias for YUY2
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_RGB1 = FOURCC('R', 'G', 'B', '1'), // Alias for ABGR
FOURCC_RGB2 = FOURCC('R', 'G', 'B', '2'), // Alias for BGRA
FOURCC_BA81 = FOURCC('B', 'A', '8', '1'), // Alias for BGGR
diff --git a/talk/site_scons/talk.py b/talk/site_scons/talk.py
index b9e93ef..757a425 100644
--- a/talk/site_scons/talk.py
+++ b/talk/site_scons/talk.py
@@ -185,7 +185,7 @@
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 @@
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 @@
elif env.Bit('mac'):
gips_lib = 'VoiceEngine_mac_universal_gcc'
elif env.Bit('linux'):
- gips_lib = 'VoiceEngine_Linux_external_gcc'
+ gips_lib = 'VoiceEngine_Linux_external_gcc'
AddToDict(kwargs, 'libs', [
@@ -253,14 +244,6 @@
'LmiUtils',
'LmiVideoCommon',
'LmiXml',
- 'ippsmerged',
- 'ippsemerged',
- 'ippvcmerged',
- 'ippvcemerged',
- 'ippimerged',
- 'ippiemerged',
- 'ippsrmerged',
- 'ippsremerged',
])
if env.Bit('windows'):
@@ -268,32 +251,21 @@
'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 @@
# 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 @@
'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/talk/third_party/libudev/libudev.h b/talk/third_party/libudev/libudev.h
new file mode 100644
index 0000000..5bc42df
--- /dev/null
+++ b/talk/third_party/libudev/libudev.h
@@ -0,0 +1,175 @@
+/*
+ * libudev - interface to udev device information
+ *
+ * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LIBUDEV_H_
+#define _LIBUDEV_H_
+
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * udev - library context
+ *
+ * reads the udev config and system environment
+ * allows custom logging
+ */
+struct udev;
+struct udev *udev_ref(struct udev *udev);
+void udev_unref(struct udev *udev);
+struct udev *udev_new(void);
+void udev_set_log_fn(struct udev *udev,
+ void (*log_fn)(struct udev *udev,
+ int priority, const char *file, int line, const char *fn,
+ const char *format, va_list args));
+int udev_get_log_priority(struct udev *udev);
+void udev_set_log_priority(struct udev *udev, int priority);
+const char *udev_get_sys_path(struct udev *udev);
+const char *udev_get_dev_path(struct udev *udev);
+void *udev_get_userdata(struct udev *udev);
+void udev_set_userdata(struct udev *udev, void *userdata);
+
+/*
+ * udev_list
+ *
+ * access to libudev generated lists
+ */
+struct udev_list_entry;
+struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry);
+struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name);
+const char *udev_list_entry_get_name(struct udev_list_entry *list_entry);
+const char *udev_list_entry_get_value(struct udev_list_entry *list_entry);
+/**
+ * udev_list_entry_foreach:
+ * @list_entry: entry to store the current position
+ * @first_entry: first entry to start with
+ *
+ * Helper to iterate over all entries of a list.
+ */
+#define udev_list_entry_foreach(list_entry, first_entry) \
+ for (list_entry = first_entry; \
+ list_entry != NULL; \
+ list_entry = udev_list_entry_get_next(list_entry))
+
+/*
+ * udev_device
+ *
+ * access to sysfs/kernel devices
+ */
+struct udev_device;
+struct udev_device *udev_device_ref(struct udev_device *udev_device);
+void udev_device_unref(struct udev_device *udev_device);
+struct udev *udev_device_get_udev(struct udev_device *udev_device);
+struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath);
+struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum);
+struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname);
+struct udev_device *udev_device_new_from_environment(struct udev *udev);
+/* udev_device_get_parent_*() does not take a reference on the returned device, it is automatically unref'd with the parent */
+struct udev_device *udev_device_get_parent(struct udev_device *udev_device);
+struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device,
+ const char *subsystem, const char *devtype);
+/* retrieve device properties */
+const char *udev_device_get_devpath(struct udev_device *udev_device);
+const char *udev_device_get_subsystem(struct udev_device *udev_device);
+const char *udev_device_get_devtype(struct udev_device *udev_device);
+const char *udev_device_get_syspath(struct udev_device *udev_device);
+const char *udev_device_get_sysname(struct udev_device *udev_device);
+const char *udev_device_get_sysnum(struct udev_device *udev_device);
+const char *udev_device_get_devnode(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device);
+const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key);
+const char *udev_device_get_driver(struct udev_device *udev_device);
+dev_t udev_device_get_devnum(struct udev_device *udev_device);
+const char *udev_device_get_action(struct udev_device *udev_device);
+unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device);
+const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr);
+
+/*
+ * udev_monitor
+ *
+ * access to kernel uevents and udev events
+ */
+struct udev_monitor;
+struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor);
+void udev_monitor_unref(struct udev_monitor *udev_monitor);
+struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor);
+/* kernel and udev generated events over netlink */
+struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name);
+/* custom socket (use netlink and filters instead) */
+struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path);
+/* bind socket */
+int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor);
+int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size);
+int udev_monitor_get_fd(struct udev_monitor *udev_monitor);
+struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor);
+/* in-kernel socket filters to select messages that get delivered to a listener */
+int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor,
+ const char *subsystem, const char *devtype);
+int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag);
+int udev_monitor_filter_update(struct udev_monitor *udev_monitor);
+int udev_monitor_filter_remove(struct udev_monitor *udev_monitor);
+
+/*
+ * udev_enumerate
+ *
+ * search sysfs for specific devices and provide a sorted list
+ */
+struct udev_enumerate;
+struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate);
+void udev_enumerate_unref(struct udev_enumerate *udev_enumerate);
+struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate);
+struct udev_enumerate *udev_enumerate_new(struct udev *udev);
+/* device properties filter */
+int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
+int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem);
+int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
+int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
+int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value);
+int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname);
+int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag);
+int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath);
+/* run enumeration with active filters */
+int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate);
+int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate);
+/* return device list */
+struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate);
+
+/*
+ * udev_queue
+ *
+ * access to the currently running udev events
+ */
+struct udev_queue;
+struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue);
+void udev_queue_unref(struct udev_queue *udev_queue);
+struct udev *udev_queue_get_udev(struct udev_queue *udev_queue);
+struct udev_queue *udev_queue_new(struct udev *udev);
+unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue);
+unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue);
+int udev_queue_get_udev_is_active(struct udev_queue *udev_queue);
+int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue);
+int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum);
+int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
+ unsigned long long int start, unsigned long long int end);
+struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue);
+struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif