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, &timestamp);
+    packet->GetRtpTimestamp(&timestamp);
     // 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