New snapshot with the latest fixes.

git-svn-id: http://libjingle.googlecode.com/svn/trunk@78 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/base/base64.cc b/talk/base/base64.cc
index 363c378..0c9ee73 100644
--- a/talk/base/base64.cc
+++ b/talk/base/base64.cc
@@ -27,10 +27,10 @@
 static const unsigned char sp = 0xFE;  // Whitespace
 static const unsigned char il = 0xFF;  // Illegal base64 character
 
-const string Base64::Base64Table(
+const char Base64::Base64Table[] =
 // 0000000000111111111122222222223333333333444444444455555555556666
 // 0123456789012345678901234567890123456789012345678901234567890123
-  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 // Decode Table gives the index of any valid base64 character in the
 // Base64 table
diff --git a/talk/base/base64.h b/talk/base/base64.h
index 73904dc..2e37bc3 100644
--- a/talk/base/base64.h
+++ b/talk/base/base64.h
@@ -79,7 +79,7 @@
   }
 
 private:
-  static const std::string Base64Table;
+  static const char Base64Table[];
   static const unsigned char DecodeTable[];
 
   static size_t GetNextQuantum(DecodeFlags parse_flags, bool illegal_pads,
diff --git a/talk/base/bytebuffer.cc b/talk/base/bytebuffer.cc
index 036b34c..12c8bc0 100644
--- a/talk/base/bytebuffer.cc
+++ b/talk/base/bytebuffer.cc
@@ -213,20 +213,22 @@
   bytes_ = new_bytes;
 }
 
-void ByteBuffer::Consume(size_t size) {
+bool ByteBuffer::Consume(size_t size) {
   if (size > Length())
-    return;
+    return false;
 
   start_ += size;
+  return true;
 }
 
-void ByteBuffer::Shift(size_t size) {
+bool ByteBuffer::Shift(size_t size) {
   if (size > Length())
-    return;
+    return false;
 
   end_ = Length() - size;
   memmove(bytes_, bytes_ + start_ + size, end_);
   start_ = 0;
+  return true;
 }
 
 }  // namespace talk_base
diff --git a/talk/base/bytebuffer.h b/talk/base/bytebuffer.h
index f5d2640..1a494cb 100644
--- a/talk/base/bytebuffer.h
+++ b/talk/base/bytebuffer.h
@@ -39,29 +39,40 @@
  public:
 
   enum ByteOrder {
-    ORDER_NETWORK = 0,  // Default, use network byte order (big endian)
-    ORDER_HOST,         // Use the native order of the host
+    ORDER_NETWORK = 0,  // Default, use network byte order (big endian).
+    ORDER_HOST,         // Use the native order of the host.
   };
 
+  // |byte_order| defines order of bytes in the buffer.
   ByteBuffer();
-  ByteBuffer(ByteOrder byte_order); // convert to/from network format
+  explicit ByteBuffer(ByteOrder byte_order);
   ByteBuffer(const char* bytes, size_t len);
   ByteBuffer(const char* bytes, size_t len, ByteOrder byte_order);
-  explicit ByteBuffer(const char* bytes);  // uses strlen
+
+  // Initializes buffer from a zero-terminated string.
+  explicit ByteBuffer(const char* bytes);
+
   ~ByteBuffer();
 
   const char* Data() const { return bytes_ + start_; }
   size_t Length() const { return end_ - start_; }
   size_t Capacity() const { return size_ - start_; }
 
+  // Read a next value from the buffer. Return false if there isn't
+  // enough data left for the specified type.
   bool ReadUInt8(uint8* val);
   bool ReadUInt16(uint16* val);
   bool ReadUInt24(uint32* val);
   bool ReadUInt32(uint32* val);
   bool ReadUInt64(uint64* val);
-  bool ReadString(std::string* val, size_t len);  // append to val
   bool ReadBytes(char* val, size_t len);
 
+  // Appends next |len| bytes from the buffer to |val|. Returns false
+  // if there is less than |len| bytes left.
+  bool ReadString(std::string* val, size_t len);
+
+  // Write value to the buffer. Resizes the buffer when it is
+  // neccessary.
   void WriteUInt8(uint8 val);
   void WriteUInt16(uint16 val);
   void WriteUInt24(uint32 val);
@@ -70,9 +81,16 @@
   void WriteString(const std::string& val);
   void WriteBytes(const char* val, size_t len);
 
+  // Resize the buffer to the specified |size|.
   void Resize(size_t size);
-  void Consume(size_t size);
-  void Shift(size_t size);
+
+  // Moves current position |size| bytes forward. Return false if
+  // there is less than |size| bytes left in the buffer.
+  bool Consume(size_t size);
+
+  // Drops |size| bytes from the front of the buffer. Return false if
+  // there is less than |size| bytes left in the buffer.
+  bool Shift(size_t size);
 
  private:
   void Construct(const char* bytes, size_t size, ByteOrder byte_order);
diff --git a/talk/base/criticalsection.h b/talk/base/criticalsection.h
index 07cf5b2..fd0f06c 100644
--- a/talk/base/criticalsection.h
+++ b/talk/base/criticalsection.h
@@ -132,6 +132,15 @@
 // compile rules.
 class AtomicOps {
  public:
+#ifdef WIN32
+  // Assumes sizeof(int) == sizeof(LONG), which it is on Win32 and Win64.
+  static int Increment(int* i) {
+    return ::InterlockedIncrement(reinterpret_cast<LONG*>(i));
+  }
+  static int Decrement(int* i) {
+    return ::InterlockedDecrement(reinterpret_cast<LONG*>(i));
+  }
+#else
   static int Increment(int* i) {
     // Could be faster, and less readable:
     // static CriticalSection* crit = StaticCrit();
@@ -153,6 +162,7 @@
     static CriticalSection* crit = new CriticalSection();
     return crit;
   }
+#endif
 };
 
 } // namespace talk_base
diff --git a/talk/base/helpers.cc b/talk/base/helpers.cc
index e2dabde..5b0e447 100644
--- a/talk/base/helpers.cc
+++ b/talk/base/helpers.cc
@@ -27,6 +27,8 @@
 
 #include "talk/base/helpers.h"
 
+#include <limits>
+
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -42,6 +44,9 @@
 #include "talk/base/scoped_ptr.h"
 #include "talk/base/time.h"
 
+// Protect against max macro inclusion.
+#undef max
+
 namespace talk_base {
 
 // Base class for RNG implementations.
@@ -196,13 +201,26 @@
   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
 };
 
-static scoped_ptr<RandomGenerator> g_rng(new SecureRandomGenerator());
+namespace {
+
+// This round about way of creating a global RNG is to safe-guard against
+// indeterminant static initialization order.
+scoped_ptr<RandomGenerator>& GetGlobalRng() {
+  static scoped_ptr<RandomGenerator> g_rng(new SecureRandomGenerator());
+  return g_rng;
+}
+
+RandomGenerator& Rng() {
+  return *GetGlobalRng();
+}
+
+}  // namespace
 
 void SetRandomTestMode(bool test) {
   if (!test) {
-    g_rng.reset(new SecureRandomGenerator());
+    GetGlobalRng().reset(new SecureRandomGenerator());
   } else {
-    g_rng.reset(new TestRandomGenerator());
+    GetGlobalRng().reset(new TestRandomGenerator());
   }
 }
 
@@ -211,7 +229,7 @@
 }
 
 bool InitRandom(const char* seed, size_t len) {
-  if (!g_rng->Init(seed, len)) {
+  if (!Rng().Init(seed, len)) {
     LOG(LS_ERROR) << "Failed to init random generator!";
     return false;
   }
@@ -229,7 +247,7 @@
                         std::string* str) {
   str->clear();
   scoped_array<uint8> bytes(new uint8[len]);
-  if (!g_rng->Generate(bytes.get(), len)) {
+  if (!Rng().Generate(bytes.get(), len)) {
     LOG(LS_ERROR) << "Failed to generate random string!";
     return false;
   }
@@ -251,7 +269,7 @@
 
 uint32 CreateRandomId() {
   uint32 id;
-  if (!g_rng->Generate(&id, sizeof(id))) {
+  if (!Rng().Generate(&id, sizeof(id))) {
     LOG(LS_ERROR) << "Failed to generate random id!";
   }
   return id;
@@ -265,4 +283,9 @@
   return id;
 }
 
+double CreateRandomDouble() {
+  return CreateRandomId() / (std::numeric_limits<uint32>::max() +
+      std::numeric_limits<double>::epsilon());
+}
+
 }  // namespace talk_base
diff --git a/talk/base/helpers.h b/talk/base/helpers.h
index 74867c0..3e3eea7 100644
--- a/talk/base/helpers.h
+++ b/talk/base/helpers.h
@@ -62,6 +62,9 @@
 // Generates a random id > 0.
 uint32 CreateRandomNonZeroId();
 
+// Generates a random double between 0.0 (inclusive) and 1.0 (exclusive).
+double CreateRandomDouble();
+
 }  // namespace talk_base
 
 #endif  // TALK_BASE_HELPERS_H_
diff --git a/talk/base/json.cc b/talk/base/json.cc
index 1c84b26..721fb94 100644
--- a/talk/base/json.cc
+++ b/talk/base/json.cc
@@ -115,6 +115,25 @@
   return ret;
 }
 
+bool GetDoubleFromJson(const Json::Value& in, double* out) {
+  bool ret;
+  if (!in.isString()) {
+    ret = in.isConvertibleTo(Json::realValue);
+    if (ret) {
+      *out = in.asDouble();
+    }
+  } else {
+    double val;
+    const char* c_str = in.asCString();
+    char* end_ptr;
+    errno = 0;
+    val = strtod(c_str, &end_ptr);
+    ret = (end_ptr != c_str && *end_ptr == '\0' && !errno);
+    *out = val;
+  }
+  return ret;
+}
+
 bool GetValueFromJsonArray(const Json::Value& in, size_t n,
                            Json::Value* out) {
   if (!in.isArray() || !in.isValidIndex(n)) {
@@ -149,6 +168,12 @@
   return GetValueFromJsonArray(in, n, &x) && GetBoolFromJson(x, out);
 }
 
+bool GetDoubleFromJsonArray(const Json::Value& in, size_t n,
+                            double* out) {
+  Json::Value x;
+  return GetValueFromJsonArray(in, n, &x) && GetDoubleFromJson(x, out);
+}
+
 bool GetValueFromJsonObject(const Json::Value& in, const std::string& k,
                             Json::Value* out) {
   if (!in.isObject() || !in.isMember(k)) {
@@ -159,7 +184,6 @@
   return true;
 }
 
-
 bool GetIntFromJsonObject(const Json::Value& in, const std::string& k,
                           int* out) {
   Json::Value x;
@@ -184,6 +208,12 @@
   return GetValueFromJsonObject(in, k, &x) && GetBoolFromJson(x, out);
 }
 
+bool GetDoubleFromJsonObject(const Json::Value& in, const std::string& k,
+                             double* out) {
+  Json::Value x;
+  return GetValueFromJsonObject(in, k, &x) && GetDoubleFromJson(x, out);
+}
+
 Json::Value StringVectorToJsonValue(const std::vector<std::string>& strings) {
   Json::Value result(Json::arrayValue);
   for (size_t i = 0; i < strings.size(); ++i) {
diff --git a/talk/base/json.h b/talk/base/json.h
index 73d0f69..5685503 100644
--- a/talk/base/json.h
+++ b/talk/base/json.h
@@ -44,6 +44,7 @@
 bool GetUIntFromJson(const Json::Value& in, unsigned int* out);
 bool GetStringFromJson(const Json::Value& in, std::string* out);
 bool GetBoolFromJson(const Json::Value& in, bool* out);
+bool GetDoubleFromJson(const Json::Value& in, double* out);
 
 // Pull values out of a JSON array.
 bool GetValueFromJsonArray(const Json::Value& in, size_t n,
@@ -56,6 +57,8 @@
                             std::string* out);
 bool GetBoolFromJsonArray(const Json::Value& in, size_t n,
                           bool* out);
+bool GetDoubleFromJsonArray(const Json::Value& in, size_t n,
+                            double* out);
 
 // Pull values out of a JSON object.
 bool GetValueFromJsonObject(const Json::Value& in, const std::string& k,
@@ -68,6 +71,8 @@
                              std::string* out);
 bool GetBoolFromJsonObject(const Json::Value& in, const std::string& k,
                            bool* out);
+bool GetDoubleFromJsonObject(const Json::Value& in, const std::string& k,
+                             double* out);
 
 // Converts vectors of strings to/from JSON arrays.
 Json::Value StringVectorToJsonValue(const std::vector<std::string>& strings);
diff --git a/talk/base/md5c.c b/talk/base/md5c.c
index eb2c034..ef9f862 100644
--- a/talk/base/md5c.c
+++ b/talk/base/md5c.c
@@ -148,7 +148,7 @@
 	MD5Transform(ctx->buf, (uint32 *)ctx->in);
 	byteReverse((unsigned char *)ctx->buf, 4);
 	memcpy(digest, ctx->buf, 16);
-	memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+	memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
 }
 
 #ifndef ASM_MD5
diff --git a/talk/base/pathutils.cc b/talk/base/pathutils.cc
index d56373b..02aba7f 100644
--- a/talk/base/pathutils.cc
+++ b/talk/base/pathutils.cc
@@ -41,7 +41,7 @@
 
 namespace talk_base {
 
-std::string const EMPTY_STR = "";
+static const char EMPTY_STR[] = "";
 
 // EXT_DELIM separates a file basename from extension
 const char EXT_DELIM = '.';
@@ -224,7 +224,7 @@
     extension_.insert(extension_.begin(), EXT_DELIM);
   }
   return true;
-} 
+}
 
 std::string Pathname::filename() const {
   std::string filename(basename_);
@@ -246,16 +246,16 @@
   return GetDrive(drive, bytes, folder_);
 }
 
-// static 
+// static
 bool Pathname::GetDrive(char *drive, uint32 bytes,
                         const std::string& pathname) {
   // need at lease 4 bytes to save c:
   if (bytes < 4 || pathname.size() < 3) {
     return false;
   }
-  
+
   memcpy(drive, pathname.c_str(), 3);
-  drive[3] = 0; 
+  drive[3] = 0;
   // sanity checking
   return (isalpha(drive[0]) &&
           drive[1] == ':' &&
diff --git a/talk/base/stringutils.cc b/talk/base/stringutils.cc
index 8d8b7e0..c4c2b2f 100644
--- a/talk/base/stringutils.cc
+++ b/talk/base/stringutils.cc
@@ -119,17 +119,22 @@
 }
 
 bool starts_with(const char *s1, const char *s2) {
-  while (*s2 != '\0') {
-    if (*s1 != *s2) {
-      return false;
-    }
-    s1++;
-    s2++;
-  }
-  return true;
+  return strncmp(s1, s2, strlen(s2)) == 0;
 }
 
-static const std::string kWhitespace(" \n\r\t");
+bool ends_with(const char *s1, const char *s2) {
+  size_t s1_length = strlen(s1);
+  size_t s2_length = strlen(s2);
+
+  if (s2_length > s1_length) {
+    return false;
+  }
+
+  const char* start = s1 + (s1_length - s2_length);
+  return strncmp(start, s2, s2_length) == 0;
+}
+
+static const char kWhitespace[] = " \n\r\t";
 
 std::string string_trim(const std::string& s) {
   std::string::size_type first = s.find_first_not_of(kWhitespace);
diff --git a/talk/base/stringutils.h b/talk/base/stringutils.h
index 6aa9b18..661a343 100644
--- a/talk/base/stringutils.h
+++ b/talk/base/stringutils.h
@@ -332,6 +332,9 @@
 // True iff s1 starts with s2.
 bool starts_with(const char *s1, const char *s2);
 
+// True iff s1 ends with s2.
+bool ends_with(const char *s1, const char *s2);
+
 // Remove leading and trailing whitespaces.
 std::string string_trim(const std::string& s);
 
diff --git a/talk/base/taskparent.h b/talk/base/taskparent.h
index a6c5795..e2093d6 100644
--- a/talk/base/taskparent.h
+++ b/talk/base/taskparent.h
@@ -47,16 +47,6 @@
   TaskParent *GetParent() { return parent_; }
   TaskRunner *GetRunner() { return runner_; }
 
-  // Retrieves a parent that corresponds to the given "code".  The code
-  // should be defined in a unique manner for the given subtree.  This
-  // method will crash (when the parent_ is NULL) if there is no corresponding
-  // parent.
-  // 
-  // Example use:
-  //  XmppClient* client =
-  //      static_cast<XmppClient*>(parent->GetParent(XMPP_CLIENT_TASK_CODE));
-  virtual TaskParent *GetParent(int code) { return parent_->GetParent(code); }
-
   bool AllChildrenDone();
   bool AnyChildError();
 #ifdef _DEBUG
diff --git a/talk/base/thread.cc b/talk/base/thread.cc
index 1603ef6..5e0eec4 100644
--- a/talk/base/thread.cc
+++ b/talk/base/thread.cc
@@ -109,20 +109,8 @@
   Thread* result = CurrentThread();
   if (NULL == result) {
     result = new Thread();
-#if defined(WIN32)
-    // We explicitly ask for no rights other than synchronization.
-    // This gives us the best chance of succeeding.
-    result->thread_ = OpenThread(SYNCHRONIZE, FALSE, GetCurrentThreadId());
-    if (!result->thread_)
-      LOG_GLE(LS_ERROR) << "Unable to get handle to thread.";
-#elif defined(POSIX)
-    result->thread_ = pthread_self();
-#endif
-    result->owned_ = false;
-    result->started_ = true;
-    SetCurrent(result);
+    result->WrapCurrent();
   }
-
   return result;
 }
 
@@ -130,14 +118,7 @@
 void ThreadManager::UnwrapCurrentThread() {
   Thread* t = CurrentThread();
   if (t && !(t->IsOwned())) {
-    // Clears the platform-specific thread-specific storage.
-    SetCurrent(NULL);
-#ifdef WIN32
-    if (!CloseHandle(t->thread_)) {
-      LOG_GLE(LS_ERROR) << "When unwrapping thread, failed to close handle.";
-    }
-#endif
-    t->started_ = false;
+    t->UnwrapCurrent();
     delete t;
   }
 }
@@ -527,6 +508,38 @@
   }
 }
 
+bool Thread::WrapCurrent() {
+  if (started_)
+    return false;
+#if defined(WIN32)
+  // We explicitly ask for no rights other than synchronization.
+  // This gives us the best chance of succeeding.
+  thread_ = OpenThread(SYNCHRONIZE, FALSE, GetCurrentThreadId());
+  if (!thread_) {
+    LOG_GLE(LS_ERROR) << "Unable to get handle to thread.";
+    return false;
+  }
+#elif defined(POSIX)
+  thread_ = pthread_self();
+#endif
+  owned_ = false;
+  started_ = true;
+  ThreadManager::SetCurrent(this);
+  return true;
+}
+
+void Thread::UnwrapCurrent() {
+  // Clears the platform-specific thread-specific storage.
+  ThreadManager::SetCurrent(NULL);
+#ifdef WIN32
+  if (!CloseHandle(thread_)) {
+    LOG_GLE(LS_ERROR) << "When unwrapping thread, failed to close handle.";
+  }
+#endif
+  started_ = false;
+}
+
+
 AutoThread::AutoThread(SocketServer* ss) : Thread(ss) {
   if (!ThreadManager::CurrentThread()) {
     ThreadManager::SetCurrent(this);
diff --git a/talk/base/thread.h b/talk/base/thread.h
index 50d8eeb..04ceeef 100644
--- a/talk/base/thread.h
+++ b/talk/base/thread.h
@@ -199,6 +199,15 @@
   }
 #endif
 
+  // This method should be called when thread is created using non standard
+  // method, like derived implementation of talk_base::Thread and it can not be
+  // started by calling Start(). This will set started flag to true and
+  // owned to false. This must be called from the current thread.
+  // NOTE: These methods should be used by the derived classes only, added here
+  // only for testing.
+  bool WrapCurrent();
+  void UnwrapCurrent();
+
  protected:
   // Blocks the calling thread until this thread has terminated.
   void Join();
diff --git a/talk/examples/call/call_main.cc b/talk/examples/call/call_main.cc
index f8d66bc..5af5474 100644
--- a/talk/examples/call/call_main.cc
+++ b/talk/examples/call/call_main.cc
@@ -27,6 +27,7 @@
 
 #include <time.h>
 #include <iomanip>
+#include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <vector>
@@ -177,10 +178,10 @@
 static const int DEFAULT_PORT = 5222;
 
 
-cricket::MediaEngine* CreateFileMediaEngine(const char* voice_in,
-                                            const char* voice_out,
-                                            const char* video_in,
-                                            const char* video_out) {
+cricket::MediaEngineInterface* CreateFileMediaEngine(const char* voice_in,
+                                                     const char* voice_out,
+                                                     const char* video_in,
+                                                     const char* video_out) {
   cricket::FileMediaEngine* file_media_engine = new cricket::FileMediaEngine;
   // Set the RTP dump file names.
   if (voice_in) {
@@ -250,6 +251,7 @@
   DEFINE_string(videoinput, NULL, "RTP dump file for video input.");
   DEFINE_string(yuvvideoinput, NULL, "YUV file for video input.");
   DEFINE_string(videooutput, NULL, "RTP dump file for video output.");
+  DEFINE_bool(render, true, "Renders the video.");
   DEFINE_bool(debugsrtp, false, "Enable debugging for srtp.");
   DEFINE_bool(help, false, "Prints this message");
 
@@ -270,6 +272,7 @@
   std::string server = FLAG_s;
   std::string secure = FLAG_secure;
   bool debugsrtp = FLAG_debugsrtp;
+  bool render = FLAG_render;
 
   if (debugsrtp) {
     cricket::EnableSrtpDebugging();
@@ -384,10 +387,8 @@
   if (FLAG_voiceinput || FLAG_voiceoutput ||
       FLAG_videoinput || FLAG_videooutput) {
     // If any dump file is specified, we use FileMediaEngine.
-    cricket::MediaEngine* engine = CreateFileMediaEngine(FLAG_voiceinput,
-                                                         FLAG_voiceoutput,
-                                                         FLAG_videoinput,
-                                                         FLAG_videooutput);
+    cricket::MediaEngineInterface* engine = CreateFileMediaEngine(
+        FLAG_voiceinput, FLAG_voiceoutput, FLAG_videoinput, FLAG_videooutput);
     // The engine will be released by the client later.
     client->SetMediaEngine(engine);
   }
@@ -399,6 +400,7 @@
   client->SetAllowLocalIps(true);
   client->SetInitialProtocol(initial_protocol);
   client->SetSecurePolicy(secure_policy);
+  client->SetRender(render);
   console->Start();
 
   if (debug) {
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 943ccb5..8b9a834 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -48,6 +48,7 @@
 #include "talk/p2p/client/basicportallocator.h"
 #include "talk/p2p/client/sessionmanagertask.h"
 #include "talk/session/phone/devicemanager.h"
+#include "talk/session/phone/mediacommon.h"
 #include "talk/session/phone/mediaengine.h"
 #include "talk/session/phone/mediamessages.h"
 #include "talk/session/phone/mediasessionclient.h"
@@ -225,12 +226,14 @@
 
 CallClient::CallClient(buzz::XmppClient* xmpp_client)
     : xmpp_client_(xmpp_client),
+      worker_thread_(NULL),
       media_engine_(NULL),
       media_client_(NULL),
       call_(NULL),
       incoming_call_(false),
       auto_accept_(false),
       pmuc_domain_("groupchat.google.com"),
+      render_(true),
       local_renderer_(NULL),
       remote_renderer_(NULL),
       static_views_accumulated_count_(0),
@@ -245,6 +248,7 @@
 CallClient::~CallClient() {
   delete media_client_;
   delete roster_;
+  delete worker_thread_;
 }
 
 const std::string CallClient::strerror(buzz::XmppEngine::Error err) {
@@ -354,7 +358,7 @@
   session_manager_task_->Start();
 
   if (!media_engine_) {
-    media_engine_ = cricket::MediaEngine::Create();
+    media_engine_ = cricket::MediaEngineFactory::Create();
   }
 
   media_client_ = new cricket::MediaSessionClient(
@@ -385,15 +389,15 @@
 }
 
 void CallClient::OnSessionState(cricket::Call* call,
-                                cricket::BaseSession* session,
-                                cricket::BaseSession::State state) {
+                                cricket::Session* session,
+                                cricket::Session::State state) {
   if (state == cricket::Session::STATE_RECEIVEDINITIATE) {
     buzz::Jid jid(session->remote_name());
     console_->PrintLine("Incoming call from '%s'", jid.Str().c_str());
     call_ = call;
     session_ = session;
     incoming_call_ = true;
-    if (call->video()) {
+    if (call->video() && render_) {
       local_renderer_ =
           cricket::VideoRendererFactory::CreateGuiVideoRenderer(160, 100);
       remote_renderer_ =
@@ -404,7 +408,7 @@
       Accept(options);
     }
   } else if (state == cricket::Session::STATE_SENTINITIATE) {
-    if (call->video()) {
+    if (call->video() && render_) {
       local_renderer_ =
           cricket::VideoRendererFactory::CreateGuiVideoRenderer(160, 100);
       remote_renderer_ =
@@ -425,7 +429,7 @@
 }
 
 void CallClient::OnSpeakerChanged(cricket::Call* call,
-                                  cricket::BaseSession* session,
+                                  cricket::Session* session,
                                   const cricket::NamedSource& speaker) {
   if (speaker.ssrc == 0) {
     console_->PrintLine("Session %s has no current speaker.",
@@ -475,11 +479,11 @@
   my_status_.set_know_capabilities(true);
   my_status_.set_pmuc_capability(true);
   my_status_.set_voice_capability(
-      (media_caps & cricket::MediaEngine::AUDIO_RECV) != 0);
+      (media_caps & cricket::AUDIO_RECV) != 0);
   my_status_.set_video_capability(
-      (media_caps & cricket::MediaEngine::VIDEO_RECV) != 0);
+      (media_caps & cricket::VIDEO_RECV) != 0);
   my_status_.set_camera_capability(
-      (media_caps & cricket::MediaEngine::VIDEO_SEND) != 0);
+      (media_caps & cricket::VIDEO_SEND) != 0);
   my_status_.set_is_google_client(true);
   my_status_.set_version("1.0.0.67");
   presence_out_->Send(my_status_);
@@ -604,7 +608,7 @@
     session_ = call_->InitiateSession(jid, options);
   }
   media_client_->SetFocus(call_);
-  if (call_->video()) {
+  if (call_->video() && render_) {
     if (!options.is_muc) {
       call_->SetLocalRenderer(local_renderer_);
       call_->SetVideoRenderer(session_, 0, remote_renderer_);
@@ -642,7 +646,7 @@
   ASSERT(call_->sessions().size() == 1);
   call_->AcceptSession(call_->sessions()[0], options);
   media_client_->SetFocus(call_);
-  if (call_->video()) {
+  if (call_->video() && render_) {
     call_->SetLocalRenderer(local_renderer_);
     // The client never does an accept for multiway, so this must be 1:1,
     // so there's no SSRC.
@@ -740,8 +744,11 @@
 }
 
 void CallClient::OnRoomLookupError(const buzz::XmlElement* stanza) {
-  console_->PrintLine("Failed to look up the room_jid. %s",
-                    stanza->Str().c_str());
+  if (stanza == NULL) {
+    console_->PrintLine("Room lookup failed.");
+  } else {
+    console_->PrintLine("Room lookup error: ", stanza->Str().c_str());
+  }
 }
 
 void CallClient::OnMucInviteReceived(const buzz::Jid& inviter,
@@ -924,10 +931,12 @@
     if (it->removed) {
       RemoveStaticRenderedView(it->ssrc);
     } else {
-      // TODO: Make dimensions and positions more configurable.
-      int offset = (50 * static_views_accumulated_count_) % 300;
-      AddStaticRenderedView(session, it->ssrc, 640, 400, 30,
-                            offset, offset);
+      if (render_) {
+        // TODO: Make dimensions and positions more configurable.
+        int offset = (50 * static_views_accumulated_count_) % 300;
+        AddStaticRenderedView(session, it->ssrc, 640, 400, 30,
+                              offset, offset);
+      }
     }
   }
 
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index 3af2b39..fb7878e 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -64,7 +64,7 @@
 
 namespace cricket {
 class PortAllocator;
-class MediaEngine;
+class MediaEngineInterface;
 class MediaSessionClient;
 class Receiver;
 class Call;
@@ -98,7 +98,7 @@
   ~CallClient();
 
   cricket::MediaSessionClient* media_client() const { return media_client_; }
-  void SetMediaEngine(cricket::MediaEngine* media_engine) {
+  void SetMediaEngine(cricket::MediaEngineInterface* media_engine) {
     media_engine_ = media_engine;
   }
   void SetAutoAccept(bool auto_accept) {
@@ -107,6 +107,9 @@
   void SetPmucDomain(const std::string &pmuc_domain) {
     pmuc_domain_ = pmuc_domain;
   }
+  void SetRender(bool render) {
+    render_ = render;
+  }
   void SetConsole(Console *console) {
     console_ = console;
   }
@@ -154,8 +157,8 @@
   void OnCallCreate(cricket::Call* call);
   void OnCallDestroy(cricket::Call* call);
   void OnSessionState(cricket::Call* call,
-                      cricket::BaseSession* session,
-                      cricket::BaseSession::State state);
+                      cricket::Session* session,
+                      cricket::Session::State state);
   void OnStatusUpdate(const buzz::Status& status);
   void OnMucInviteReceived(const buzz::Jid& inviter, const buzz::Jid& room,
       const std::vector<buzz::AvailableMediaEntry>& avail);
@@ -169,7 +172,7 @@
                             cricket::Session* session,
                             const cricket::MediaSources& sources);
   void OnSpeakerChanged(cricket::Call* call,
-                        cricket::BaseSession* session,
+                        cricket::Session* session,
                         const cricket::NamedSource& speaker_source);
   void OnRoomLookupResponse(const buzz::MucRoomInfo& room_info);
   void OnRoomLookupError(const buzz::XmlElement* stanza);
@@ -208,15 +211,16 @@
   cricket::PortAllocator* port_allocator_;
   cricket::SessionManager* session_manager_;
   cricket::SessionManagerTask* session_manager_task_;
-  cricket::MediaEngine* media_engine_;
+  cricket::MediaEngineInterface* media_engine_;
   cricket::MediaSessionClient* media_client_;
   MucMap mucs_;
 
   cricket::Call* call_;
-  cricket::BaseSession *session_;
+  cricket::Session *session_;
   bool incoming_call_;
   bool auto_accept_;
   std::string pmuc_domain_;
+  bool render_;
   cricket::VideoRenderer* local_renderer_;
   cricket::VideoRenderer* remote_renderer_;
   StaticRenderedViews static_rendered_views_;
diff --git a/talk/examples/call/discoitemsquerytask.cc b/talk/examples/call/discoitemsquerytask.cc
index df6d96d..6bbfb1d 100644
--- a/talk/examples/call/discoitemsquerytask.cc
+++ b/talk/examples/call/discoitemsquerytask.cc
@@ -36,8 +36,8 @@
 const int kDiscoItemsTimeout = 60;
 } // namespace
 
-DiscoItemsQueryTask::DiscoItemsQueryTask(Task* parent,
-                                         const std::string node,
+DiscoItemsQueryTask::DiscoItemsQueryTask(XmppTaskParentInterface* parent,
+                                         const std::string& node,
                                          const Jid& to)
     : XmppTask(parent, XmppEngine::HL_SINGLE), node_(node) {
   set_timeout_seconds(kDiscoItemsTimeout);
diff --git a/talk/examples/call/discoitemsquerytask.h b/talk/examples/call/discoitemsquerytask.h
index 6be8c35..27912bc 100644
--- a/talk/examples/call/discoitemsquerytask.h
+++ b/talk/examples/call/discoitemsquerytask.h
@@ -59,7 +59,8 @@
  public:
   // TODO: Currently, this only supports one query stanza - we may eventually
   // need it to support multiple
-  DiscoItemsQueryTask(Task* parent, const std::string node, const Jid& to);
+  DiscoItemsQueryTask(XmppTaskParentInterface* parent,
+                      const std::string& node, const Jid& to);
 
   virtual int ProcessStart();
   virtual int ProcessResponse();
diff --git a/talk/examples/call/friendinvitesendtask.h b/talk/examples/call/friendinvitesendtask.h
index 3f776bb..625f077 100644
--- a/talk/examples/call/friendinvitesendtask.h
+++ b/talk/examples/call/friendinvitesendtask.h
@@ -35,7 +35,8 @@
 
 class FriendInviteSendTask : public XmppTask {
 public:
-  FriendInviteSendTask(Task* parent) : XmppTask(parent) {}
+  explicit FriendInviteSendTask(XmppTaskParentInterface* parent)
+    : XmppTask(parent) {}
   virtual ~FriendInviteSendTask() {}
 
   XmppReturnStatus Send(const Jid& user);
diff --git a/talk/examples/call/mucinviterecvtask.h b/talk/examples/call/mucinviterecvtask.h
index 892b484..24f05e0 100644
--- a/talk/examples/call/mucinviterecvtask.h
+++ b/talk/examples/call/mucinviterecvtask.h
@@ -65,15 +65,16 @@
 
 class MucInviteRecvTask : public XmppTask {
  public:
-  MucInviteRecvTask(Task* parent) : XmppTask(parent, XmppEngine::HL_TYPE) {}
+  explicit MucInviteRecvTask(XmppTaskParentInterface* parent)
+      : XmppTask(parent, XmppEngine::HL_TYPE) {}
   virtual int ProcessStart();
- 
+
   // First arg is inviter's JID; second is MUC's JID.
   sigslot::signal3<const Jid&, const Jid&, const std::vector<AvailableMediaEntry>& > SignalInviteReceived;
 
  protected:
   virtual bool HandleStanza(const XmlElement* stanza);
-  
+
 };
 
 }
diff --git a/talk/examples/call/mucinvitesendtask.h b/talk/examples/call/mucinvitesendtask.h
index 8afd361..2429b31 100644
--- a/talk/examples/call/mucinvitesendtask.h
+++ b/talk/examples/call/mucinvitesendtask.h
@@ -36,7 +36,8 @@
 
 class MucInviteSendTask : public XmppTask {
 public:
-  MucInviteSendTask(Task* parent) : XmppTask(parent) {}
+  explicit MucInviteSendTask(XmppTaskParentInterface* parent)
+      : XmppTask(parent) {}
   virtual ~MucInviteSendTask() {}
 
   XmppReturnStatus Send(const Jid& to, const Jid& invitee);
diff --git a/talk/examples/call/presenceouttask.h b/talk/examples/call/presenceouttask.h
index 36e7f15..5735ce2 100644
--- a/talk/examples/call/presenceouttask.h
+++ b/talk/examples/call/presenceouttask.h
@@ -36,7 +36,8 @@
 
 class PresenceOutTask : public XmppTask {
 public:
-  PresenceOutTask(Task * parent) : XmppTask(parent) {}
+  explicit PresenceOutTask(XmppTaskParentInterface* parent)
+      : XmppTask(parent) {}
   virtual ~PresenceOutTask() {}
 
   XmppReturnStatus Send(const Status & s);
diff --git a/talk/examples/call/presencepushtask.h b/talk/examples/call/presencepushtask.h
index 998a98e..dccc827 100644
--- a/talk/examples/call/presencepushtask.h
+++ b/talk/examples/call/presencepushtask.h
@@ -40,7 +40,7 @@
 
 class PresencePushTask : public XmppTask {
  public:
-  PresencePushTask(Task * parent, CallClient* client)
+  PresencePushTask(XmppTaskParentInterface* parent, CallClient* client)
     : XmppTask(parent, XmppEngine::HL_TYPE),
       client_(client) {}
   virtual int ProcessStart();
diff --git a/talk/examples/call/voicemailjidrequester.cc b/talk/examples/call/voicemailjidrequester.cc
index 81f3dbc..7287700 100644
--- a/talk/examples/call/voicemailjidrequester.cc
+++ b/talk/examples/call/voicemailjidrequester.cc
@@ -25,20 +25,21 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "talk/base/scoped_ptr.h"
 #include "talk/examples/call/discoitemsquerytask.h"
 #include "talk/examples/call/voicemailjidrequester.h"
-#include "talk/base/scoped_ptr.h"
 #include "talk/xmpp/constants.h"
+#include "talk/xmpp/xmpptask.h"
 
 namespace buzz {
 
-VoicemailJidRequester::VoicemailJidRequester(talk_base::Task* parent,
+VoicemailJidRequester::VoicemailJidRequester(XmppTaskParentInterface* parent,
                                              const Jid& their_jid,
-                                             const Jid& my_jid) : Task(parent),
-                                             their_jid_(their_jid),
-                                             my_jid_(my_jid),
-                                             done_with_query_(false) {
-  parent_ = parent;
+                                             const Jid& my_jid) :
+    XmppTaskBase(parent),
+    their_jid_(their_jid),
+    my_jid_(my_jid),
+    done_with_query_(false) {
 }
 
 int VoicemailJidRequester::ProcessStart() {
diff --git a/talk/examples/call/voicemailjidrequester.h b/talk/examples/call/voicemailjidrequester.h
index 34b3c4b..686f763 100644
--- a/talk/examples/call/voicemailjidrequester.h
+++ b/talk/examples/call/voicemailjidrequester.h
@@ -73,9 +73,10 @@
 class Task;
 
 class VoicemailJidRequester : public sigslot::has_slots<>,
-                              public talk_base::Task {
+                              public XmppTaskBase {
  public:
-  VoicemailJidRequester(talk_base::Task* parent, const Jid& their_jid, const Jid& my_jid);
+  VoicemailJidRequester(XmppTaskParentInterface* parent,
+                        const Jid& their_jid, const Jid& my_jid);
 
   // Provides the target jid and the voicemail to reach it
   sigslot::signal2<const Jid&, const Jid&> SignalGotVoicemailJid;
@@ -110,8 +111,6 @@
   // the first query fails.
   void StartSecondQuery();
 
-  talk_base::Task* parent_;
-
   Jid their_jid_;
 
   // Your own jid (not the other user's)
diff --git a/talk/examples/login/xmppthread.h b/talk/examples/login/xmppthread.h
index 247b7bd..bfed5e0 100644
--- a/talk/examples/login/xmppthread.h
+++ b/talk/examples/login/xmppthread.h
@@ -32,7 +32,6 @@
 #include "talk/base/thread.h"
 #include "talk/examples/login/xmpppump.h"
 #include "talk/examples/login/xmppsocket.h"
-#include <iostream>
 
 class XmppThread:
     public talk_base::Thread, XmppPumpNotify, talk_base::MessageHandler {
diff --git a/talk/libjingle.scons b/talk/libjingle.scons
index 10b401a..03239d4 100644
--- a/talk/libjingle.scons
+++ b/talk/libjingle.scons
@@ -20,7 +20,7 @@
                "HAVE_EXPAT_CONFIG_H",
              ],
 )
-talk.Library(env, name = "libsrtp",
+talk.Library(env, name = "srtp",
              srcs = [
                "third_party/srtp/crypto/cipher/aes.c",
                "third_party/srtp/crypto/cipher/aes_cbc.c",
@@ -54,7 +54,7 @@
                "/wd4702",
              ],
 )
-talk.Library(env, name = "libjingle",
+talk.Library(env, name = "jingle",
              lin_srcs = [
                "base/latebindingsymboltable.cc",
                "base/linux.cc",
@@ -195,6 +195,7 @@
                "session/phone/mediaengine.cc",
                "session/phone/mediamessages.cc",
                "session/phone/mediamonitor.cc",
+               "session/phone/mediasession.cc",
                "session/phone/mediasessionclient.cc",
                "session/phone/rtpdump.cc",
                "session/phone/rtputils.cc",
@@ -243,9 +244,9 @@
                "base/json.cc",
              ],
 )
-talk.Library(env, name = "libxmpphelp",
+talk.Library(env, name = "xmpphelp",
              libs = [
-               "libjingle",
+               "jingle",
              ],
              srcs = [
                "examples/login/xmppauth.cc",
@@ -263,9 +264,9 @@
 )
 talk.App(env, name = "login",
          libs = [
-           "libjingle",
+           "jingle",
            "expat",
-           "libxmpphelp",
+           "xmpphelp",
          ],
          srcs = [
            "examples/login/xmppthread.cc",
@@ -320,15 +321,15 @@
            "examples/call/voicemailjidrequester.cc",
          ],
          libs = [
-           "libjingle",
+           "jingle",
            "expat",
-           "libsrtp",
-           "libxmpphelp",
+           "srtp",
+           "xmpphelp",
          ],
 )
 talk.App(env, name = "relayserver",
          libs = [
-           "libjingle",
+           "jingle",
          ],
          srcs = [
            "p2p/base/relayserver_main.cc",
@@ -336,7 +337,7 @@
 )
 talk.App(env, name = "stunserver",
          libs = [
-           "libjingle",
+           "jingle",
          ],
          srcs = [
            "p2p/base/stunserver_main.cc",
diff --git a/talk/main.scons b/talk/main.scons
index 0464252..ae71b1d 100644
--- a/talk/main.scons
+++ b/talk/main.scons
@@ -175,7 +175,6 @@
     '$PLATFORM_SDK_VISTA_6_0_DIR/Lib'
   ],
   LINKFLAGS = [
-    '-opt:ref', # Remove unused references (functions/data).
     '-manifest' # TODO: Why do we need this?
   ],
   MIDLFLAGS = [
@@ -248,7 +247,8 @@
       '/GS',     # enable security checks
   ],
   LINKFLAGS = [
-    '/safeseh',
+    '/safeseh',  # protect against attacks against exception handlers
+    '/opt:ref',  # Remove unused references (functions/data).
   ],
 )
 
@@ -302,7 +302,7 @@
   CCFLAGS = [
     '-m32',
     '-arch', 'i386',
-    '-isysroot', '/Developer/SDKs/MacOSX10.5.sdk',
+    '-isysroot', '/Developer/SDKs/MacOSX10.6.sdk',
     '-fasm-blocks',
   ],
   LINKFLAGS = [
@@ -313,9 +313,10 @@
     # TODO: consider only defining for libs that actually have objc.
     '-ObjC',
     '-arch', 'i386',
+    '-mmacosx-version-min=10.5',
+    '-isysroot', '/Developer/SDKs/MacOSX10.6.sdk',
     '-m32',
-    # Enable dead-code removal.
-    '-dead_strip',
+    '-dead-strip'
   ],
   FRAMEWORKS = [
     'CoreServices',
diff --git a/talk/p2p/base/constants.cc b/talk/p2p/base/constants.cc
index e7025a0..178bd59 100644
--- a/talk/p2p/base/constants.cc
+++ b/talk/p2p/base/constants.cc
@@ -106,6 +106,7 @@
     true, NS_JINGLE_RTP, LN_PAYLOADTYPE);
 const buzz::QName QN_JINGLE_RTP_BANDWIDTH(
     true, NS_JINGLE_RTP, LN_BANDWIDTH);
+const buzz::QName QN_JINGLE_RTCP_MUX(true, NS_JINGLE_RTP, "rtcp-mux");
 const buzz::QName QN_PARAMETER(true, NS_JINGLE_RTP, "parameter");
 
 const std::string NS_GINGLE_AUDIO("http://www.google.com/session/phone");
diff --git a/talk/p2p/base/constants.h b/talk/p2p/base/constants.h
index 5ffeca8..ab0ed2c 100644
--- a/talk/p2p/base/constants.h
+++ b/talk/p2p/base/constants.h
@@ -125,6 +125,7 @@
 extern const buzz::QName QN_JINGLE_SSRC;
 extern const buzz::QName QN_JINGLE_RTP_PAYLOADTYPE;
 extern const buzz::QName QN_JINGLE_RTP_BANDWIDTH;
+extern const buzz::QName QN_JINGLE_RTCP_MUX;
 
 extern const std::string NS_GINGLE_AUDIO;
 extern const buzz::QName QN_GINGLE_AUDIO_CONTENT;
diff --git a/talk/p2p/base/parsing.cc b/talk/p2p/base/parsing.cc
index 70e00d7..a54d379 100644
--- a/talk/p2p/base/parsing.cc
+++ b/talk/p2p/base/parsing.cc
@@ -32,8 +32,8 @@
 #include "talk/base/stringutils.h"
 
 namespace {
-std::string kTrue = "true";
-std::string kOne = "1";
+static const char kTrue[] = "true";
+static const char kOne[] = "1";
 }
 
 namespace cricket {
diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc
index 7740336..bb4ab26 100644
--- a/talk/p2p/base/port.cc
+++ b/talk/p2p/base/port.cc
@@ -350,7 +350,6 @@
 
   StunAddressAttribute* addr_attr =
       StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
-  addr_attr->SetFamily(1);
   addr_attr->SetPort(addr.port());
   addr_attr->SetIP(addr.ip());
   response.AddAttribute(addr_attr);
diff --git a/talk/p2p/base/pseudotcp.cc b/talk/p2p/base/pseudotcp.cc
index 2e8cea3..fc903e0 100644
--- a/talk/p2p/base/pseudotcp.cc
+++ b/talk/p2p/base/pseudotcp.cc
@@ -537,6 +537,7 @@
                                                        len,
                                                        offset,
                                                        &bytes_read);
+    UNUSED(result);
     ASSERT(result == talk_base::SR_SUCCESS);
     ASSERT(static_cast<uint32>(bytes_read) == len);
   }
@@ -897,6 +898,7 @@
       talk_base::StreamResult result = m_rbuf.WriteOffset(seg.data, seg.len,
                                                           nOffset, NULL);
       ASSERT(result == talk_base::SR_SUCCESS);
+      UNUSED(result);
 
       if (seg.seq == m_rcv_nxt) {
         m_rbuf.ConsumeWriteBuffer(seg.len);
@@ -1188,7 +1190,8 @@
     }
 
     // Length of this option.
-    ASSERT(len);
+    ASSERT(len != 0);
+    UNUSED(len);
     uint8 opt_len = 0;
     buf.ReadUInt8(&opt_len);
 
@@ -1262,6 +1265,7 @@
   // before connection is established or when peers are exchanging connect
   // messages.
   ASSERT(result);
+  UNUSED(result);
   m_rbuf_len = new_size;
   m_rwnd_scale = scale_factor;
   m_ssthresh = new_size;
diff --git a/talk/p2p/base/relayport.cc b/talk/p2p/base/relayport.cc
index 5348fe5..76bd5ff 100644
--- a/talk/p2p/base/relayport.cc
+++ b/talk/p2p/base/relayport.cc
@@ -183,7 +183,7 @@
   uint32 start_time_;
 };
 
-const std::string RELAY_PORT_TYPE("relay");
+const char RELAY_PORT_TYPE[] = "relay";
 
 RelayPort::RelayPort(
     talk_base::Thread* thread, talk_base::PacketSocketFactory* factory,
@@ -554,7 +554,6 @@
 
   StunAddressAttribute* addr_attr =
       StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
-  addr_attr->SetFamily(1);
   addr_attr->SetIP(addr.ip());
   addr_attr->SetPort(addr.port());
   request.AddAttribute(addr_attr);
diff --git a/talk/p2p/base/relayport.h b/talk/p2p/base/relayport.h
index 025668a..62bb758 100644
--- a/talk/p2p/base/relayport.h
+++ b/talk/p2p/base/relayport.h
@@ -38,7 +38,7 @@
 
 namespace cricket {
 
-extern const std::string RELAY_PORT_TYPE;
+extern const char RELAY_PORT_TYPE[];
 class RelayEntry;
 class RelayConnection;
 
diff --git a/talk/p2p/base/relayserver.cc b/talk/p2p/base/relayserver.cc
index 82f382a..554337f 100644
--- a/talk/p2p/base/relayserver.cc
+++ b/talk/p2p/base/relayserver.cc
@@ -444,7 +444,6 @@
 
   StunAddressAttribute* addr_attr =
       StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
-  addr_attr->SetFamily(1);
   addr_attr->SetIP(ext_addr.ip());
   addr_attr->SetPort(ext_addr.port());
   response.AddAttribute(addr_attr);
@@ -621,7 +620,6 @@
 
   StunAddressAttribute* addr_attr =
       StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2);
-  addr_attr->SetFamily(1);
   addr_attr->SetIP(from_addr.ip());
   addr_attr->SetPort(from_addr.port());
   msg.AddAttribute(addr_attr);
diff --git a/talk/p2p/base/session.cc b/talk/p2p/base/session.cc
index 54ae1bc..ef6737a 100644
--- a/talk/p2p/base/session.cc
+++ b/talk/p2p/base/session.cc
@@ -159,20 +159,138 @@
   proxy->SetImplementation(impl);
 }
 
-
-
-
-BaseSession::BaseSession(talk_base::Thread *signaling_thread)
-    : state_(STATE_INIT), error_(ERROR_NONE),
-      local_description_(NULL), remote_description_(NULL),
-      signaling_thread_(signaling_thread) {
+BaseSession::BaseSession(talk_base::Thread* signaling_thread,
+                         talk_base::Thread* worker_thread,
+                         PortAllocator* port_allocator,
+                         const std::string& sid,
+                         const std::string& content_type,
+                         bool initiator)
+    : state_(STATE_INIT),
+      error_(ERROR_NONE),
+      signaling_thread_(signaling_thread),
+      worker_thread_(worker_thread),
+      port_allocator_(port_allocator),
+      sid_(sid),
+      content_type_(content_type),
+      transport_type_(NS_GINGLE_P2P),
+      initiator_(initiator),
+      local_description_(NULL),
+      remote_description_(NULL) {
+  ASSERT(signaling_thread->IsCurrent());
 }
 
 BaseSession::~BaseSession() {
+  ASSERT(signaling_thread()->IsCurrent());
+
+  ASSERT(state_ != STATE_DEINIT);
+  state_ = STATE_DEINIT;
+  SignalState(this, state_);
+
+  for (TransportMap::iterator iter = transports_.begin();
+       iter != transports_.end(); ++iter) {
+    delete iter->second;
+  }
+
   delete remote_description_;
   delete local_description_;
 }
 
+void BaseSession::set_allow_local_ips(bool allow) {
+  allow_local_ips_ = allow;
+  for (TransportMap::iterator iter = transports_.begin();
+       iter != transports_.end(); ++iter) {
+    iter->second->impl()->set_allow_local_ips(allow);
+  }
+}
+
+TransportChannel* BaseSession::CreateChannel(const std::string& content_name,
+                                             const std::string& channel_name) {
+  // We create the proxy "on demand" here because we need to support
+  // creating channels at any time, even before we send or receive
+  // initiate messages, which is before we create the transports.
+  TransportProxy* transproxy = GetOrCreateTransportProxy(content_name);
+  return transproxy->CreateChannel(channel_name, content_type_);
+}
+
+TransportChannel* BaseSession::GetChannel(const std::string& content_name,
+                                          const std::string& channel_name) {
+  TransportProxy* transproxy = GetTransportProxy(content_name);
+  if (transproxy == NULL)
+    return NULL;
+  else
+    return transproxy->GetChannel(channel_name);
+}
+
+void BaseSession::DestroyChannel(const std::string& content_name,
+                                 const std::string& channel_name) {
+  TransportProxy* transproxy = GetTransportProxy(content_name);
+  ASSERT(transproxy != NULL);
+  transproxy->DestroyChannel(channel_name);
+}
+
+TransportProxy* BaseSession::GetOrCreateTransportProxy(
+    const std::string& content_name) {
+  TransportProxy* transproxy = GetTransportProxy(content_name);
+  if (transproxy)
+    return transproxy;
+
+  Transport* transport = CreateTransport();
+  transport->set_allow_local_ips(allow_local_ips_);
+  transport->SignalConnecting.connect(
+      this, &BaseSession::OnTransportConnecting);
+  transport->SignalWritableState.connect(
+      this, &BaseSession::OnTransportWritable);
+  transport->SignalRequestSignaling.connect(
+      this, &BaseSession::OnTransportRequestSignaling);
+  transport->SignalCandidatesReady.connect(
+      this, &BaseSession::OnTransportCandidatesReady);
+  transport->SignalTransportError.connect(
+      this, &BaseSession::OnTransportSendError);
+  transport->SignalChannelGone.connect(
+      this, &BaseSession::OnTransportChannelGone);
+
+  transproxy = new TransportProxy(content_name, transport);
+  transports_[content_name] = transproxy;
+
+  return transproxy;
+}
+
+Transport* BaseSession::GetTransport(const std::string& content_name) {
+  TransportProxy* transproxy = GetTransportProxy(content_name);
+  if (transproxy == NULL)
+    return NULL;
+  return transproxy->impl();
+}
+
+TransportProxy* BaseSession::GetTransportProxy(
+    const std::string& content_name) {
+  TransportMap::iterator iter = transports_.find(content_name);
+  return (iter != transports_.end()) ? iter->second : NULL;
+}
+
+TransportProxy* BaseSession::GetTransportProxy(const Transport* transport) {
+  for (TransportMap::iterator iter = transports_.begin();
+       iter != transports_.end(); ++iter) {
+    TransportProxy* transproxy = iter->second;
+    if (transproxy->impl() == transport) {
+      return transproxy;
+    }
+  }
+  return NULL;
+}
+
+TransportProxy* BaseSession::GetFirstTransportProxy() {
+  if (transports_.empty())
+    return NULL;
+  return transports_.begin()->second;
+}
+
+cricket::Transport* BaseSession::CreateTransport() {
+  ASSERT(transport_type_ == NS_GINGLE_P2P);
+  return new cricket::P2PTransport(
+      signaling_thread(), worker_thread(), port_allocator());
+}
+
 void BaseSession::SetState(State state) {
   ASSERT(signaling_thread_->IsCurrent());
   if (state != state_) {
@@ -190,6 +308,21 @@
   }
 }
 
+void BaseSession::OnSignalingReady() {
+  ASSERT(signaling_thread()->IsCurrent());
+  for (TransportMap::iterator iter = transports_.begin();
+       iter != transports_.end(); ++iter) {
+    iter->second->impl()->OnSignalingReady();
+  }
+}
+
+void BaseSession::SpeculativelyConnectAllTransportChannels() {
+  for (TransportMap::iterator iter = transports_.begin();
+       iter != transports_.end(); ++iter) {
+    iter->second->SpeculativelyConnectChannels();
+  }
+}
+
 void BaseSession::OnMessage(talk_base::Message *pmsg) {
   switch (pmsg->message_id) {
   case MSG_TIMEOUT:
@@ -197,10 +330,6 @@
     SetError(ERROR_TIME);
     break;
 
-  case MSG_ERROR:
-    TerminateWithReason(STR_TERMINATE_ERROR);
-    break;
-
   case MSG_STATE:
     switch (state_) {
     case STATE_SENTACCEPT:
@@ -208,12 +337,6 @@
       SetState(STATE_INPROGRESS);
       break;
 
-    case STATE_SENTREJECT:
-    case STATE_RECEIVEDREJECT:
-      // Assume clean termination.
-      Terminate();
-      break;
-
     default:
       // Explicitly ignoring some states here.
       break;
@@ -222,74 +345,41 @@
   }
 }
 
-
-Session::Session(SessionManager *session_manager,
+Session::Session(SessionManager* session_manager,
                  const std::string& local_name,
                  const std::string& initiator_name,
-                 const std::string& sid, const std::string& content_type,
-                 SessionClient* client) :
-    BaseSession(session_manager->signaling_thread()) {
-  ASSERT(session_manager->signaling_thread()->IsCurrent());
+                 const std::string& sid,
+                 const std::string& content_type,
+                 SessionClient* client)
+    : BaseSession(session_manager->signaling_thread(),
+                  session_manager->worker_thread(),
+                  session_manager->port_allocator(),
+                  sid, content_type, initiator_name == local_name) {
   ASSERT(client != NULL);
   session_manager_ = session_manager;
   local_name_ = local_name;
-  sid_ = sid;
   initiator_name_ = initiator_name;
-  content_type_ = content_type;
-  // TODO: Once we support different transport types,
-  // don't hard code this here.
-  transport_type_ = NS_GINGLE_P2P;
   transport_parser_ = new P2PTransportParser();
   client_ = client;
-  error_ = ERROR_NONE;
-  state_ = STATE_INIT;
-  initiator_ = false;
   initiate_acked_ = false;
   current_protocol_ = PROTOCOL_HYBRID;
 }
 
 Session::~Session() {
-  ASSERT(signaling_thread_->IsCurrent());
-
-  ASSERT(state_ != STATE_DEINIT);
-  state_ = STATE_DEINIT;
-  SignalState(this, state_);
-
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    delete iter->second;
-  }
-
   delete transport_parser_;
 }
 
-Transport* Session::GetTransport(const std::string& content_name) {
-  TransportProxy* transproxy = GetTransportProxy(content_name);
-  if (transproxy == NULL)
-    return NULL;
-  return transproxy->impl();
-}
-
-void Session::set_allow_local_ips(bool allow) {
-  allow_local_ips_ = allow;
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    iter->second->impl()->set_allow_local_ips(allow);
-  }
-}
-
 bool Session::Initiate(const std::string &to,
                        const SessionDescription* sdesc) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
   SessionError error;
 
   // Only from STATE_INIT
-  if (state_ != STATE_INIT)
+  if (state() != STATE_INIT)
     return false;
 
   // Setup for signaling.
-  remote_name_ = to;
-  initiator_ = true;
+  set_remote_name(to);
   set_local_description(sdesc);
   if (!CreateTransportProxies(GetEmptyTransportInfos(sdesc->contents()),
                               &error)) {
@@ -309,14 +399,13 @@
 }
 
 bool Session::Accept(const SessionDescription* sdesc) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
 
   // Only if just received initiate
-  if (state_ != STATE_RECEIVEDINITIATE)
+  if (state() != STATE_RECEIVEDINITIATE)
     return false;
 
   // Setup for signaling.
-  initiator_ = false;
   set_local_description(sdesc);
 
   SessionError error;
@@ -330,16 +419,13 @@
 }
 
 bool Session::Reject(const std::string& reason) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
 
   // Reject is sent in response to an initiate or modify, to reject the
   // request
-  if (state_ != STATE_RECEIVEDINITIATE && state_ != STATE_RECEIVEDMODIFY)
+  if (state() != STATE_RECEIVEDINITIATE && state() != STATE_RECEIVEDMODIFY)
     return false;
 
-  // Setup for signaling.
-  initiator_ = false;
-
   SessionError error;
   if (!SendRejectMessage(reason, &error)) {
     LOG(LS_ERROR) << "Could not send reject message: " << error.text;
@@ -351,10 +437,10 @@
 }
 
 bool Session::TerminateWithReason(const std::string& reason) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
 
   // Either side can terminate, at any time.
-  switch (state_) {
+  switch (state()) {
     case STATE_SENTTERMINATE:
     case STATE_RECEIVEDTERMINATE:
       return false;
@@ -379,7 +465,7 @@
 }
 
 bool Session::SendInfoMessage(const XmlElements& elems) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
   SessionError error;
   if (!SendMessage(ACTION_SESSION_INFO, elems, &error)) {
     LOG(LS_ERROR) << "Could not send info message " << error.text;
@@ -388,41 +474,17 @@
   return true;
 }
 
-
-TransportProxy* Session::GetTransportProxy(const Transport* transport) {
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    TransportProxy* transproxy = iter->second;
-    if (transproxy->impl() == transport) {
-      return transproxy;
-    }
-  }
-  return NULL;
-}
-
-TransportProxy* Session::GetTransportProxy(const std::string& content_name) {
-  TransportMap::iterator iter = transports_.find(content_name);
-  return (iter != transports_.end()) ? iter->second : NULL;
-}
-
-TransportProxy* Session::GetFirstTransportProxy() {
-  if (transports_.empty())
-    return NULL;
-  return transports_.begin()->second;
-}
-
 TransportInfos Session::GetEmptyTransportInfos(
     const ContentInfos& contents) const {
   TransportInfos tinfos;
   for (ContentInfos::const_iterator content = contents.begin();
        content != contents.end(); ++content) {
     tinfos.push_back(
-        TransportInfo(content->name, transport_type_, Candidates()));
+        TransportInfo(content->name, transport_type(), Candidates()));
   }
   return tinfos;
 }
 
-
 bool Session::OnRemoteCandidates(
     const TransportInfos& tinfos, ParseError* error) {
   for (TransportInfos::const_iterator tinfo = tinfos.begin();
@@ -457,42 +519,11 @@
   return true;
 }
 
-
-TransportProxy* Session::GetOrCreateTransportProxy(
-    const std::string& content_name) {
-  TransportProxy* transproxy = GetTransportProxy(content_name);
-  if (transproxy)
-    return transproxy;
-
-  Transport* transport =
-      new P2PTransport(signaling_thread_,
-                       session_manager_->worker_thread(),
-                       session_manager_->port_allocator());
-  transport->set_allow_local_ips(allow_local_ips_);
-  transport->SignalConnecting.connect(
-      this, &Session::OnTransportConnecting);
-  transport->SignalWritableState.connect(
-      this, &Session::OnTransportWritable);
-  transport->SignalRequestSignaling.connect(
-      this, &Session::OnTransportRequestSignaling);
-  transport->SignalCandidatesReady.connect(
-      this, &Session::OnTransportCandidatesReady);
-  transport->SignalTransportError.connect(
-      this, &Session::OnTransportSendError);
-  transport->SignalChannelGone.connect(
-      this, &Session::OnTransportChannelGone);
-
-  transproxy = new TransportProxy(content_name, transport);
-  transports_[content_name] = transproxy;
-
-  return transproxy;
-}
-
 bool Session::CreateTransportProxies(const TransportInfos& tinfos,
                                      SessionError* error) {
   for (TransportInfos::const_iterator tinfo = tinfos.begin();
        tinfo != tinfos.end(); ++tinfo) {
-    if (tinfo->transport_type != transport_type_) {
+    if (tinfo->transport_type != transport_type()) {
       error->SetText("No supported transport in offer.");
       return false;
     }
@@ -502,56 +533,21 @@
   return true;
 }
 
-void Session::SpeculativelyConnectAllTransportChannels() {
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    iter->second->SpeculativelyConnectChannels();
-  }
-}
-
 TransportParserMap Session::GetTransportParsers() {
   TransportParserMap parsers;
-  parsers[transport_type_] = transport_parser_;
+  parsers[transport_type()] = transport_parser_;
   return parsers;
 }
 
 ContentParserMap Session::GetContentParsers() {
   ContentParserMap parsers;
-  parsers[content_type_] = client_;
+  parsers[content_type()] = client_;
   return parsers;
 }
 
-TransportChannel* Session::CreateChannel(const std::string& content_name,
-                                         const std::string& channel_name) {
-  // We create the proxy "on demand" here because we need to support
-  // creating channels at any time, even before we send or receive
-  // initiate messages, which is before we create the transports.
-  TransportProxy* transproxy = GetOrCreateTransportProxy(content_name);
-  return transproxy->CreateChannel(channel_name, content_type_);
-}
-
-TransportChannel* Session::GetChannel(const std::string& content_name,
-                                      const std::string& channel_name) {
-  TransportProxy* transproxy = GetTransportProxy(content_name);
-  if (transproxy == NULL)
-    return NULL;
-  else
-    return transproxy->GetChannel(channel_name);
-}
-
-void Session::DestroyChannel(const std::string& content_name,
-                             const std::string& channel_name) {
-  TransportProxy* transproxy = GetTransportProxy(content_name);
-  ASSERT(transproxy != NULL);
-  transproxy->DestroyChannel(channel_name);
-}
-
-void Session::OnSignalingReady() {
-  ASSERT(signaling_thread_->IsCurrent());
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    iter->second->impl()->OnSignalingReady();
-  }
+void Session::OnTransportRequestSignaling(Transport* transport) {
+  ASSERT(signaling_thread()->IsCurrent());
+  SignalRequestSignaling(this);
 }
 
 void Session::OnTransportConnecting(Transport* transport) {
@@ -561,31 +557,26 @@
 }
 
 void Session::OnTransportWritable(Transport* transport) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
 
   // If the transport is not writable, start a timer to make sure that it
   // becomes writable within a reasonable amount of time.  If it does not, we
   // terminate since we can't actually send data.  If the transport is writable,
   // cancel the timer.  Note that writability transitions may occur repeatedly
   // during the lifetime of the session.
-  signaling_thread_->Clear(this, MSG_TIMEOUT);
+  signaling_thread()->Clear(this, MSG_TIMEOUT);
   if (transport->HasChannels() && !transport->writable()) {
-    signaling_thread_->PostDelayed(
+    signaling_thread()->PostDelayed(
         session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
   }
 }
 
-void Session::OnTransportRequestSignaling(Transport* transport) {
-  ASSERT(signaling_thread_->IsCurrent());
-  SignalRequestSignaling(this);
-}
-
 void Session::OnTransportCandidatesReady(Transport* transport,
                                          const Candidates& candidates) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
   TransportProxy* transproxy = GetTransportProxy(transport);
   if (transproxy != NULL) {
-    if (initiator_ && !initiate_acked_) {
+    if (initiator() && !initiate_acked_) {
       // TODO: This is to work around server re-ordering
       // messages.  We send the candidates once the session-initiate
       // is acked.  Once we have fixed the server to guarantee message
@@ -611,19 +602,19 @@
                                    const std::string& type,
                                    const std::string& text,
                                    const buzz::XmlElement* extra_info) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
   SignalErrorMessage(this, stanza, name, type, text, extra_info);
 }
 
 void Session::OnTransportChannelGone(Transport* transport,
                                      const std::string& name) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
   SignalChannelGone(this, name);
 }
 
 void Session::OnIncomingMessage(const SessionMessage& msg) {
-  ASSERT(signaling_thread_->IsCurrent());
-  ASSERT(state_ == STATE_INIT || msg.from == remote_name_);
+  ASSERT(signaling_thread()->IsCurrent());
+  ASSERT(state() == STATE_INIT || msg.from == remote_name());
 
   if (current_protocol_== PROTOCOL_HYBRID) {
     if (msg.protocol == PROTOCOL_GINGLE) {
@@ -677,7 +668,7 @@
 void Session::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
                                  const buzz::XmlElement* response_stanza,
                                  const SessionMessage& msg) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
 
   if (msg.type == ACTION_SESSION_INITIATE) {
     OnInitiateAcked();
@@ -698,7 +689,7 @@
 
 void Session::OnFailedSend(const buzz::XmlElement* orig_stanza,
                            const buzz::XmlElement* error_stanza) {
-  ASSERT(signaling_thread_->IsCurrent());
+  ASSERT(signaling_thread()->IsCurrent());
 
   SessionMessage msg;
   ParseError parse_error;
@@ -744,7 +735,7 @@
     // which we pass on to the respective transport.
 
     // TODO: This is only used for unknown channel name.
-    // For Jingle, find a stanard-compliant way of doing this.  For
+    // For Jingle, find a standard-compliant way of doing this.  For
     // Gingle, guess the content name based on the channel name.
     for (const buzz::XmlElement* elem = error->FirstElement();
          NULL != elem; elem = elem->NextElement()) {
@@ -777,13 +768,12 @@
                       session_error.text, error);
   }
 
-  initiator_ = false;
-  remote_name_ = msg.from;
+  set_remote_name(msg.from);
   set_remote_description(new SessionDescription(init.ClearContents()));
   SetState(STATE_RECEIVEDINITIATE);
 
   // Users of Session may listen to state change and call Reject().
-  if (state_ != STATE_SENTREJECT) {
+  if (state() != STATE_SENTREJECT) {
     if (!OnRemoteCandidates(init.transports, error))
       return false;
   }
@@ -809,7 +799,7 @@
   SetState(STATE_RECEIVEDACCEPT);
 
   // Users of Session may listen to state change and call Reject().
-  if (state_ != STATE_SENTREJECT) {
+  if (state() != STATE_SENTREJECT) {
     if (!OnRemoteCandidates(accept.transports, error))
       return false;
   }
@@ -883,7 +873,7 @@
 
   // First, ensure all updates are valid before modifying remote_description_.
   for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
-    if (remote_description_->GetContentByName(it->name) == NULL) {
+    if (remote_description()->GetContentByName(it->name) == NULL) {
       return false;
     }
 
@@ -894,8 +884,8 @@
   // Merge the updates into the remote description.
   for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
     LOG(LS_INFO) << "Updating content " << it->name;
-    remote_description_->RemoveContentByName(it->name);
-    remote_description_->AddContent(it->name, it->type, it->description);
+    remote_description()->RemoveContentByName(it->name);
+    remote_description()->AddContent(it->name, it->type, it->description);
   }
 
   SignalRemoteDescriptionUpdate(this);
@@ -918,20 +908,20 @@
     return BadWrite(message_error.text, error);
   }
 
-  if (!BareJidsEqual(remote_name_, redirect.target))
+  if (!BareJidsEqual(remote_name(), redirect.target))
     return BadWrite("Redirection not allowed: must be the same bare jid.",
                     error);
 
   // When we receive a redirect, we point the session at the new JID
   // and resend the candidates.
-  remote_name_ = redirect.target;
+  set_remote_name(redirect.target);
   return (SendInitiateMessage(local_description(), error) &&
           ResendAllTransportInfoMessages(error));
 }
 
-bool Session::CheckState(State state, MessageError* error) {
-  ASSERT(state_ == state);
-  if (state_ != state) {
+bool Session::CheckState(State expected, MessageError* error) {
+  ASSERT(state() == expected);
+  if (state() != expected) {
     return BadMessage(buzz::QN_STANZA_NOT_ALLOWED,
                       "message not allowed in current state",
                       error);
@@ -941,19 +931,29 @@
 
 void Session::SetError(Error error) {
   BaseSession::SetError(error);
-  if (error_ != ERROR_NONE)
-    signaling_thread_->Post(this, MSG_ERROR);
+  if (error != ERROR_NONE)
+    signaling_thread()->Post(this, MSG_ERROR);
 }
 
 void Session::OnMessage(talk_base::Message *pmsg) {
   // preserve this because BaseSession::OnMessage may modify it
-  BaseSession::State orig_state = state_;
+  State orig_state = state();
 
   BaseSession::OnMessage(pmsg);
 
   switch (pmsg->message_id) {
+  case MSG_ERROR:
+    TerminateWithReason(STR_TERMINATE_ERROR);
+    break;
+
   case MSG_STATE:
     switch (orig_state) {
+    case STATE_SENTREJECT:
+    case STATE_RECEIVEDREJECT:
+      // Assume clean termination.
+      Terminate();
+      break;
+
     case STATE_SENTTERMINATE:
     case STATE_RECEIVEDTERMINATE:
       session_manager_->DestroySession(this);
@@ -1045,8 +1045,8 @@
 }
 
 bool Session::ResendAllTransportInfoMessages(SessionError* error) {
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
+  for (TransportMap::const_iterator iter = transport_proxies().begin();
+       iter != transport_proxies().end(); ++iter) {
     TransportProxy* transproxy = iter->second;
     if (transproxy->sent_candidates().size() > 0) {
       if (!SendTransportInfoMessage(
@@ -1062,8 +1062,8 @@
 }
 
 bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) {
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
+  for (TransportMap::const_iterator iter = transport_proxies().begin();
+       iter != transport_proxies().end(); ++iter) {
     TransportProxy* transproxy = iter->second;
     if (transproxy->unsent_candidates().size() > 0) {
       if (!SendTransportInfoMessage(
@@ -1083,8 +1083,8 @@
   talk_base::scoped_ptr<buzz::XmlElement> stanza(
       new buzz::XmlElement(buzz::QN_IQ));
 
-  SessionMessage msg(current_protocol_, type, sid_, initiator_name_);
-  msg.to = remote_name_;
+  SessionMessage msg(current_protocol_, type, id(), initiator_name());
+  msg.to = remote_name();
   WriteSessionMessage(msg, action_elems, stanza.get());
 
   SignalOutgoingMessage(this, stanza.get());
@@ -1127,8 +1127,8 @@
   if (!WriteSessionAction(protocol, action, &action_elems, error))
     return false;
 
-  SessionMessage msg(protocol, type, sid_, initiator_name_);
-  msg.to = remote_name_;
+  SessionMessage msg(protocol, type, id(), initiator_name());
+  msg.to = remote_name();
 
   WriteSessionMessage(msg, action_elems, stanza);
   return true;
@@ -1137,7 +1137,7 @@
 void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) {
   talk_base::scoped_ptr<buzz::XmlElement> ack(
       new buzz::XmlElement(buzz::QN_IQ));
-  ack->SetAttr(buzz::QN_TO, remote_name_);
+  ack->SetAttr(buzz::QN_TO, remote_name());
   ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
   ack->SetAttr(buzz::QN_TYPE, "result");
 
diff --git a/talk/p2p/base/session.h b/talk/p2p/base/session.h
index bfafe77..cb3c1fc 100644
--- a/talk/p2p/base/session.h
+++ b/talk/p2p/base/session.h
@@ -127,12 +127,6 @@
 
 typedef std::map<std::string, TransportProxy*> TransportMap;
 
-// TODO: Consider simplifying the dependency from Voice/VideoChannel
-// on Session. Right now the Channel class requires a BaseSession, but it only
-// uses CreateChannel/DestroyChannel. Perhaps something like a
-// TransportChannelFactory could be hoisted up out of BaseSession, or maybe
-// the transports could be passed in directly.
-
 // A BaseSession manages general session state. This includes negotiation
 // of both the application-level and network-level protocols:  the former
 // defines what will be sent and the latter defines how it will be sent.  Each
@@ -168,17 +162,71 @@
     ERROR_CONTENT = 4,   // channel errors in SetLocalContent/SetRemoteContent
   };
 
-  explicit BaseSession(talk_base::Thread *signaling_thread);
+  BaseSession(talk_base::Thread* signaling_thread,
+              talk_base::Thread* worker_thread,
+              PortAllocator* port_allocator,
+              const std::string& sid,
+              const std::string& content_type,
+              bool initiator);
   virtual ~BaseSession();
 
-  // Updates the state, signaling if necessary.
-  void SetState(State state);
+  talk_base::Thread* signaling_thread() { return signaling_thread_; }
+  talk_base::Thread* worker_thread() { return worker_thread_; }
+  PortAllocator* port_allocator() { return port_allocator_; }
 
-  // Updates the error state, signaling if necessary.
-  virtual void SetError(Error error);
+  // The ID of this session.
+  const std::string& id() const { return sid_; }
 
-  // Handles messages posted to us.
-  virtual void OnMessage(talk_base::Message *pmsg);
+  // Returns the XML namespace identifying the type of this session.
+  const std::string& content_type() const { return content_type_; }
+
+  // Returns the XML namespace identifying the transport used for this session.
+  const std::string& transport_type() const { return transport_type_; }
+
+  // Indicates whether we initiated this session.
+  bool initiator() const { return initiator_; }
+
+  // Returns the application-level description given by our client.
+  // If we are the recipient, this will be NULL until we send an accept.
+  const SessionDescription* local_description() const {
+    return local_description_;
+  }
+  // Returns the application-level description given by the other client.
+  // If we are the initiator, this will be NULL until we receive an accept.
+  const SessionDescription* remote_description() const {
+    return remote_description_;
+  }
+  SessionDescription* remote_description() {
+    return remote_description_;
+  }
+
+  // Takes ownership of SessionDescription*
+  bool set_local_description(const SessionDescription* sdesc) {
+    if (sdesc != local_description_) {
+      delete local_description_;
+      local_description_ = sdesc;
+    }
+    return true;
+  }
+
+  // Takes ownership of SessionDescription*
+  bool set_remote_description(SessionDescription* sdesc) {
+    if (sdesc != remote_description_) {
+      delete remote_description_;
+      remote_description_ = sdesc;
+    }
+    return true;
+  }
+
+  const SessionDescription* initiator_description() const {
+    if (initiator_) {
+      return local_description_;
+    } else {
+      return remote_description_;
+    }
+  }
+
+  void set_allow_local_ips(bool allow);
 
   // Returns the current state of the session.  See the enum above for details.
   // Each time the state changes, we will fire this signal.
@@ -190,6 +238,19 @@
   Error error() const { return error_; }
   sigslot::signal2<BaseSession *, Error> SignalError;
 
+  // Updates the state, signaling if necessary.
+  virtual void SetState(State state);
+
+  // Updates the error state, signaling if necessary.
+  virtual void SetError(Error error);
+
+  // Fired when the remote description is updated.
+  sigslot::signal1<BaseSession *> SignalRemoteDescriptionUpdate;
+
+  // Returns the transport that has been negotiated or NULL if
+  // negotiation is still in progress.
+  Transport* GetTransport(const std::string& content_name);
+
   // Creates a new channel with the given names.  This method may be called
   // immediately after creating the session.  However, the actual
   // implementation may not be fixed until transport negotiation completes.
@@ -197,68 +258,102 @@
   // shouldn't be an issue since the main thread will be blocked in
   // Send when doing so.
   virtual TransportChannel* CreateChannel(const std::string& content_name,
-                                          const std::string& channel_name) = 0;
+                                          const std::string& channel_name);
 
   // Returns the channel with the given names.
   virtual TransportChannel* GetChannel(const std::string& content_name,
-                                       const std::string& channel_name) = 0;
+                                       const std::string& channel_name);
 
   // Destroys the channel with the given names.
   // This will usually be called from the worker thread, but that
   // shouldn't be an issue since the main thread will be blocked in
   // Send when doing so.
   virtual void DestroyChannel(const std::string& content_name,
-                              const std::string& channel_name) = 0;
+                              const std::string& channel_name);
+ protected:
+  const TransportMap& transport_proxies() const { return transports_; }
+  // Get a TransportProxy by content_name or transport. NULL if not found.
+  TransportProxy* GetTransportProxy(const std::string& content_name);
+  TransportProxy* GetTransportProxy(const Transport* transport);
+  TransportProxy* GetFirstTransportProxy();
+  // TransportProxy is owned by session.  Return proxy just for convenience.
+  TransportProxy* GetOrCreateTransportProxy(const std::string& content_name);
+  // Creates the actual transport object. Overridable for testing.
+  virtual Transport* CreateTransport();
 
-  // Invoked when we notice that there is no matching channel on our peer.
-  sigslot::signal2<Session*, const std::string&> SignalChannelGone;
+  void OnSignalingReady();
+  void SpeculativelyConnectAllTransportChannels();
 
-  // Returns the application-level description given by our client.
-  // If we are the recipient, this will be NULL until we send an accept.
-  const SessionDescription* local_description() const {
-    return local_description_;
-  }
-  // Takes ownership of SessionDescription*
-  bool set_local_description(const SessionDescription* sdesc) {
-    if (sdesc != local_description_) {
-      delete local_description_;
-      local_description_ = sdesc;
-    }
-    return true;
+  // Called when a transport requests signaling.
+  virtual void OnTransportRequestSignaling(Transport* transport) {
   }
 
-  // Returns the application-level description given by the other client.
-  // If we are the initiator, this will be NULL until we receive an accept.
-  const SessionDescription* remote_description() const {
-    return remote_description_;
-  }
-  // Takes ownership of SessionDescription*
-  bool set_remote_description(SessionDescription* sdesc) {
-    if (sdesc != remote_description_) {
-      delete remote_description_;
-      remote_description_ = sdesc;
-    }
-    return true;
+  // Called when the first channel of a transport begins connecting.  We use
+  // this to start a timer, to make sure that the connection completes in a
+  // reasonable amount of time.
+  virtual void OnTransportConnecting(Transport* transport) {
   }
 
-  // When we receive an initiate, we create a session in the
-  // RECEIVEDINITIATE state and respond by accepting or rejecting.
-  // Takes ownership of session description.
-  virtual bool Accept(const SessionDescription* sdesc) = 0;
-  virtual bool Reject(const std::string& reason) = 0;
-  bool Terminate() {
-    return TerminateWithReason(STR_TERMINATE_SUCCESS);
-  }
-  virtual bool TerminateWithReason(const std::string& reason) = 0;
-
-  // The worker thread used by the session manager
-  virtual talk_base::Thread *worker_thread() = 0;
-
-  talk_base::Thread *signaling_thread() {
-    return signaling_thread_;
+  // Called when a transport changes its writable state.  We track this to make
+  // sure that the transport becomes writable within a reasonable amount of
+  // time.  If this does not occur, we signal an error.
+  virtual void OnTransportWritable(Transport* transport) {
   }
 
-  // Returns the JID of this client.
+  // Called when a transport signals that it has new candidates.
+  virtual void OnTransportCandidatesReady(Transport* transport,
+                                          const Candidates& candidates) {
+  }
+
+  // Called when a transport signals that it found an error in an incoming
+  // message.
+  virtual void OnTransportSendError(Transport* transport,
+                                    const buzz::XmlElement* stanza,
+                                    const buzz::QName& name,
+                                    const std::string& type,
+                                    const std::string& text,
+                                    const buzz::XmlElement* extra_info) {
+  }
+
+  // Called when we notice that one of our local channels has no peer, so it
+  // should be destroyed.
+  virtual void OnTransportChannelGone(Transport* transport,
+                                      const std::string& name) {
+  }
+
+  // Handles messages posted to us.
+  virtual void OnMessage(talk_base::Message *pmsg);
+
+ protected:
+// private:
+  State state_;
+  Error error_;
+ private:
+  talk_base::Thread* signaling_thread_;
+  talk_base::Thread* worker_thread_;
+  PortAllocator* port_allocator_;
+  std::string sid_;
+  std::string content_type_;
+  std::string transport_type_;
+  bool initiator_;
+  const SessionDescription* local_description_;
+  SessionDescription* remote_description_;
+  // This is transport-specific but required so much by unit tests
+  // that it's much easier to put it here.
+  bool allow_local_ips_;
+  TransportMap transports_;
+};
+
+// A specific Session created by the SessionManager, using XMPP for protocol.
+class Session : public BaseSession {
+ public:
+  // Returns the manager that created and owns this session.
+  SessionManager* session_manager() const { return session_manager_; }
+
+  // Returns the client that is handling the application data of this session.
+  SessionClient* client() const { return client_; }
+
+    // Returns the JID of this client.
   const std::string& local_name() const { return local_name_; }
 
   // Returns the JID of the other peer in this session.
@@ -271,41 +366,9 @@
   // explicitly.
   void set_remote_name(const std::string& name) { remote_name_ = name; }
 
-  const std::string& id() const { return sid_; }
-
-  // Fired when the remote description is updated.
-  sigslot::signal1<BaseSession *> SignalRemoteDescriptionUpdate;
-
- protected:
-  State state_;
-  Error error_;
-  const SessionDescription* local_description_;
-  SessionDescription* remote_description_;
-  std::string sid_;
-  // We don't use buzz::Jid because changing to buzz:Jid here has a
-  // cascading effect that requires an enormous number places to
-  // change to buzz::Jid as well.
-  std::string local_name_;
-  std::string remote_name_;
-  talk_base::Thread *signaling_thread_;
-};
-
-// A specific Session created by the SessionManager, using XMPP for protocol.
-class Session : public BaseSession {
- public:
-  // Returns the manager that created and owns this session.
-  SessionManager* session_manager() const { return session_manager_; }
-
-  // the worker thread used by the session manager
-  talk_base::Thread *worker_thread() {
-    return session_manager_->worker_thread();
-  }
-
-  // Returns the XML namespace identifying the type of this session.
-  const std::string& content_type() const { return content_type_; }
-
-  // Returns the client that is handling the application data of this session.
-  SessionClient* client() const { return client_; }
+  // Indicates the JID of the entity who initiated this session.
+  // In special cases, may be different than both local_name and remote_name.
+  const std::string& initiator_name() const { return initiator_name_; }
 
   SignalingProtocol current_protocol() const { return current_protocol_; }
 
@@ -313,25 +376,18 @@
     current_protocol_ = protocol;
   }
 
-  // Indicates whether we initiated this session.
-  bool initiator() const { return initiator_; }
+  // Updates the error state, signaling if necessary.
+  virtual void SetError(Error error);
 
-  const SessionDescription* initiator_description() const {
-    if (initiator_) {
-      return local_description_;
-    } else {
-      return remote_description_;
-    }
-  }
+  // When the session needs to send signaling messages, it beings by requesting
+  // signaling.  The client should handle this by calling OnSignalingReady once
+  // it is ready to send the messages.
+  // (These are called only by SessionManager.)
+  sigslot::signal1<Session*> SignalRequestSignaling;
+  void OnSignalingReady() { BaseSession::OnSignalingReady(); }
 
-  // Fired whenever we receive a terminate message along with a reason
-  sigslot::signal2<Session*, const std::string&> SignalReceivedTerminateReason;
-
-  void set_allow_local_ips(bool allow);
-
-  // Returns the transport that has been negotiated or NULL if
-  // negotiation is still in progress.
-  Transport* GetTransport(const std::string& content_name);
+  // Invoked when we notice that there is no matching channel on our peer.
+  sigslot::signal2<Session*, const std::string&> SignalChannelGone;
 
   // Takes ownership of session description.
   // TODO: Add an error argument to pass back to the caller.
@@ -342,9 +398,14 @@
   // RECEIVEDINITIATE state and respond by accepting or rejecting.
   // Takes ownership of session description.
   // TODO: Add an error argument to pass back to the caller.
-  virtual bool Accept(const SessionDescription* sdesc);
-  virtual bool Reject(const std::string& reason);
-  virtual bool TerminateWithReason(const std::string& reason);
+  bool Accept(const SessionDescription* sdesc);
+  bool Reject(const std::string& reason);
+  bool Terminate() {
+    return TerminateWithReason(STR_TERMINATE_SUCCESS);
+  }
+  bool TerminateWithReason(const std::string& reason);
+  // Fired whenever we receive a terminate message along with a reason
+  sigslot::signal2<Session*, const std::string&> SignalReceivedTerminateReason;
 
   // The two clients in the session may also send one another
   // arbitrary XML messages, which are called "info" messages. Sending
@@ -353,30 +414,6 @@
   bool SendInfoMessage(const XmlElements& elems);
   sigslot::signal2<Session*, const buzz::XmlElement*> SignalInfoMessage;
 
-  // Maps passed to serialization functions.
-  TransportParserMap GetTransportParsers();
-  ContentParserMap GetContentParsers();
-
-  // Creates a new channel with the given names.  This method may be called
-  // immediately after creating the session.  However, the actual
-  // implementation may not be fixed until transport negotiation completes.
-  virtual TransportChannel* CreateChannel(const std::string& content_name,
-                                          const std::string& channel_name);
-
-  // Returns the channel with the given names.
-  virtual TransportChannel* GetChannel(const std::string& content_name,
-                                       const std::string& channel_name);
-
-  // Destroys the channel with the given names.
-  virtual void DestroyChannel(const std::string& content_name,
-                              const std::string& channel_name);
-
-  // Updates the error state, signaling if necessary.
-  virtual void SetError(Error error);
-
-  // Handles messages posted to us.
-  virtual void OnMessage(talk_base::Message *pmsg);
-
  private:
   // Creates or destroys a session.  (These are called only SessionManager.)
   Session(SessionManager *session_manager,
@@ -384,62 +421,35 @@
           const std::string& sid, const std::string& content_type,
           SessionClient* client);
   ~Session();
-
-  // Get a TransportProxy by content_name or transport. NULL if not found.
-  TransportProxy* GetTransportProxy(const std::string& content_name);
-  TransportProxy* GetTransportProxy(const Transport* transport);
-  TransportProxy* GetFirstTransportProxy();
-  // TransportProxy is owned by session.  Return proxy just for convenience.
-  TransportProxy* GetOrCreateTransportProxy(const std::string& content_name);
   // For each transport info, create a transport proxy.  Can fail for
   // incompatible transport types.
   bool CreateTransportProxies(const TransportInfos& tinfos,
                               SessionError* error);
-  void SpeculativelyConnectAllTransportChannels();
   bool OnRemoteCandidates(const TransportInfos& tinfos,
                           ParseError* error);
   // Returns a TransportInfo without candidates for each content name.
   // Uses the transport_type_ of the session.
   TransportInfos GetEmptyTransportInfos(const ContentInfos& contents) const;
 
-  // Called when the first channel of a transport begins connecting.  We use
-  // this to start a timer, to make sure that the connection completes in a
-  // reasonable amount of time.
-  void OnTransportConnecting(Transport* transport);
+    // Maps passed to serialization functions.
+  TransportParserMap GetTransportParsers();
+  ContentParserMap GetContentParsers();
 
-  // Called when a transport changes its writable state.  We track this to make
-  // sure that the transport becomes writable within a reasonable amount of
-  // time.  If this does not occur, we signal an error.
-  void OnTransportWritable(Transport* transport);
+  virtual void OnTransportRequestSignaling(Transport* transport);
+  virtual void OnTransportConnecting(Transport* transport);
+  virtual void OnTransportWritable(Transport* transport);
+  virtual void OnTransportCandidatesReady(Transport* transport,
+                                          const Candidates& candidates);
+  virtual void OnTransportSendError(Transport* transport,
+                                    const buzz::XmlElement* stanza,
+                                    const buzz::QName& name,
+                                    const std::string& type,
+                                    const std::string& text,
+                                    const buzz::XmlElement* extra_info);
+  virtual void OnTransportChannelGone(Transport* transport,
+                                      const std::string& name);
 
-  // Called when a transport requests signaling.
-  void OnTransportRequestSignaling(Transport* transport);
-
-  // Called when a transport signals that it has a message to send.   Note that
-  // these messages are just the transport part of the stanza; they need to be
-  // wrapped in the appropriate session tags.
-  void OnTransportCandidatesReady(Transport* transport,
-                                  const Candidates& candidates);
-
-  // Called when a transport signals that it found an error in an incoming
-  // message.
-  void OnTransportSendError(Transport* transport,
-                            const buzz::XmlElement* stanza,
-                            const buzz::QName& name,
-                            const std::string& type,
-                            const std::string& text,
-                            const buzz::XmlElement* extra_info);
-
-  // Called when we notice that one of our local channels has no peer, so it
-  // should be destroyed.
-  void OnTransportChannelGone(Transport* transport, const std::string& name);
-
-  // When the session needs to send signaling messages, it beings by requesting
-  // signaling.  The client should handle this by calling OnSignalingReady once
-  // it is ready to send the messages.
-  // (These are called only by SessionManager.)
-  sigslot::signal1<Session*> SignalRequestSignaling;
-  void OnSignalingReady();
+  virtual void OnMessage(talk_base::Message *pmsg);
 
   // Send various kinds of session messages.
   bool SendInitiateMessage(const SessionDescription* sdesc,
@@ -535,18 +545,13 @@
   // Verifies that we are in the appropriate state to receive this message.
   bool CheckState(State state, MessageError* error);
 
-  SessionManager *session_manager_;
-  bool initiator_;
+  SessionManager* session_manager_;
   bool initiate_acked_;
+  std::string local_name_;
   std::string initiator_name_;
-  std::string content_type_;
+  std::string remote_name_;
   SessionClient* client_;
-  std::string transport_type_;
   TransportParser* transport_parser_;
-  // This is transport-specific but required so much by unit tests
-  // that it's much easier to put it here.
-  bool allow_local_ips_;
-  TransportMap transports_;
   // Keeps track of what protocol we are speaking.
   SignalingProtocol current_protocol_;
 
diff --git a/talk/p2p/base/stun.cc b/talk/p2p/base/stun.cc
index e324d7e..fab14af 100644
--- a/talk/p2p/base/stun.cc
+++ b/talk/p2p/base/stun.cc
@@ -29,6 +29,7 @@
 
 #include <cstring>
 
+#include "talk/base/byteorder.h"
 #include "talk/base/common.h"
 #include "talk/base/logging.h"
 
@@ -36,18 +37,9 @@
 
 namespace cricket {
 
-const std::string STUN_ERROR_REASON_BAD_REQUEST = "BAD REQUEST";
-const std::string STUN_ERROR_REASON_UNAUTHORIZED = "UNAUTHORIZED";
-const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE = "UNKNOWN ATTRIBUTE";
-const std::string STUN_ERROR_REASON_STALE_CREDENTIALS = "STALE CREDENTIALS";
-const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE =
-    "INTEGRITY CHECK FAILURE";
-const std::string STUN_ERROR_REASON_MISSING_USERNAME = "MISSING USERNAME";
-const std::string STUN_ERROR_REASON_USE_TLS = "USE TLS";
-const std::string STUN_ERROR_REASON_SERVER_ERROR = "SERVER ERROR";
-const std::string STUN_ERROR_REASON_GLOBAL_FAILURE = "GLOBAL FAILURE";
-
-const char kStunMagicCookie[] = { '\x21', '\x12', '\xA4', '\x42' };
+const char STUN_ERROR_REASON_BAD_REQUEST[] = "BAD REQUEST";
+const char STUN_ERROR_REASON_STALE_CREDENTIALS[] = "STALE CREDENTIALS";
+const char STUN_ERROR_REASON_SERVER_ERROR[] = "SERVER ERROR";
 
 const char TURN_MAGIC_COOKIE_VALUE[] = { '\x72', '\xC6', '\x4B', '\xC6' };
 
@@ -84,50 +76,54 @@
 const StunAddressAttribute*
 StunMessage::GetAddress(StunAttributeType type) const {
   switch (type) {
-  case STUN_ATTR_MAPPED_ADDRESS:
-  case STUN_ATTR_RESPONSE_ADDRESS:
-  case STUN_ATTR_SOURCE_ADDRESS:
-  case STUN_ATTR_CHANGED_ADDRESS:
-  case STUN_ATTR_REFLECTED_FROM:
-  case STUN_ATTR_ALTERNATE_SERVER:
-  case STUN_ATTR_DESTINATION_ADDRESS:
-  case STUN_ATTR_SOURCE_ADDRESS2:
-    return reinterpret_cast<const StunAddressAttribute*>(GetAttribute(type));
+    case STUN_ATTR_MAPPED_ADDRESS: {
+      // Return XOR-MAPPED-ADDRESS when MAPPED-ADDRESS attribute is
+      // missing.
+      const StunAttribute* mapped_address =
+          GetAttribute(STUN_ATTR_MAPPED_ADDRESS);
+      if (!mapped_address)
+        mapped_address = GetAttribute(STUN_ATTR_XOR_MAPPED_ADDRESS);
+      return reinterpret_cast<const StunAddressAttribute*>(mapped_address);
+    }
 
-  default:
-    ASSERT(0);
-    return 0;
+    case STUN_ATTR_DESTINATION_ADDRESS:
+    case STUN_ATTR_SOURCE_ADDRESS2:
+    case STUN_ATTR_XOR_MAPPED_ADDRESS:
+      return reinterpret_cast<const StunAddressAttribute*>(GetAttribute(type));
+
+    default:
+      ASSERT(0);
+      return NULL;
   }
 }
 
 const StunUInt32Attribute*
 StunMessage::GetUInt32(StunAttributeType type) const {
   switch (type) {
-  case STUN_ATTR_CHANGE_REQUEST:
-  case STUN_ATTR_LIFETIME:
-  case STUN_ATTR_BANDWIDTH:
-  case STUN_ATTR_OPTIONS:
-    return reinterpret_cast<const StunUInt32Attribute*>(GetAttribute(type));
+    case STUN_ATTR_LIFETIME:
+    case STUN_ATTR_BANDWIDTH:
+    case STUN_ATTR_OPTIONS:
+      return reinterpret_cast<const StunUInt32Attribute*>(GetAttribute(type));
 
-  default:
-    ASSERT(0);
-    return 0;
+    default:
+      ASSERT(0);
+      return NULL;
   }
 }
 
 const StunByteStringAttribute*
 StunMessage::GetByteString(StunAttributeType type) const {
   switch (type) {
-  case STUN_ATTR_USERNAME:
-  case STUN_ATTR_PASSWORD:
-  case STUN_ATTR_MESSAGE_INTEGRITY:
-  case STUN_ATTR_DATA:
-  case STUN_ATTR_MAGIC_COOKIE:
-    return reinterpret_cast<const StunByteStringAttribute*>(GetAttribute(type));
+    case STUN_ATTR_USERNAME:
+    case STUN_ATTR_MESSAGE_INTEGRITY:
+    case STUN_ATTR_DATA:
+    case STUN_ATTR_MAGIC_COOKIE:
+      return reinterpret_cast<const StunByteStringAttribute*>(
+          GetAttribute(type));
 
-  default:
-    ASSERT(0);
-    return 0;
+    default:
+      ASSERT(0);
+      return NULL;
   }
 }
 
@@ -141,17 +137,12 @@
       GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
 }
 
-const StunTransportPrefsAttribute* StunMessage::GetTransportPrefs() const {
-  return reinterpret_cast<const StunTransportPrefsAttribute*>(
-      GetAttribute(STUN_ATTR_TRANSPORT_PREFERENCES));
-}
-
 const StunAttribute* StunMessage::GetAttribute(StunAttributeType type) const {
   for (unsigned i = 0; i < attrs_->size(); i++) {
     if ((*attrs_)[i]->type() == type)
       return (*attrs_)[i];
   }
-  return 0;
+  return NULL;
 }
 
 bool StunMessage::Read(ByteBuffer* buf) {
@@ -174,8 +165,10 @@
   std::string transaction_id;
   if (!buf->ReadString(&transaction_id, kStunTransactionIdLength))
     return false;
-  if (magic_cookie != std::string(kStunMagicCookie,
-                                  kStunMagicCookie + kStunMagicCookieLength)) {
+
+  uint32 magic_cookie_int =
+      *reinterpret_cast<const uint32*>(magic_cookie.data());
+  if (talk_base::NetworkToHost32(magic_cookie_int) != kStunMagicCookie) {
     // If magic cookie is invalid it means that the peer implements
     // RFC3489 instead of RFC5389.
     transaction_id.insert(0, magic_cookie);
@@ -197,18 +190,18 @@
       return false;
 
     StunAttribute* attr = StunAttribute::Create(attr_type, attr_length);
-    if (!attr || !attr->Read(buf))
-      return false;
-
-    attrs_->push_back(attr);
+    if (!attr) {
+      // Skip an unknown attribute.
+      if (!buf->Consume(attr_length))
+        return false;
+    } else {
+      if (!attr->Read(buf))
+        return false;
+      attrs_->push_back(attr);
+    }
   }
 
-  if (buf->Length() != rest) {
-    // fixme: shouldn't be doing this
-    LOG(LERROR) << "wrong message length (" << rest << " != " << buf->Length()
-                << ")";
-    return false;
-  }
+  ASSERT(buf->Length() == rest);
 
   return true;
 }
@@ -217,7 +210,7 @@
   buf->WriteUInt16(type_);
   buf->WriteUInt16(length_);
   if (!IsLegacy())
-    buf->WriteBytes(kStunMagicCookie, kStunMagicCookieLength);
+    buf->WriteUInt32(kStunMagicCookie);
   buf->WriteString(transaction_id_);
 
   for (unsigned i = 0; i < attrs_->size(); i++) {
@@ -238,77 +231,72 @@
 
 StunAttribute* StunAttribute::Create(uint16 type, uint16 length) {
   switch (type) {
-  case STUN_ATTR_MAPPED_ADDRESS:
-  case STUN_ATTR_RESPONSE_ADDRESS:
-  case STUN_ATTR_SOURCE_ADDRESS:
-  case STUN_ATTR_CHANGED_ADDRESS:
-  case STUN_ATTR_REFLECTED_FROM:
-  case STUN_ATTR_ALTERNATE_SERVER:
-  case STUN_ATTR_DESTINATION_ADDRESS:
-  case STUN_ATTR_SOURCE_ADDRESS2:
-    if (length != StunAddressAttribute::SIZE)
-      return 0;
-    return new StunAddressAttribute(type);
+    case STUN_ATTR_MAPPED_ADDRESS:
+    case STUN_ATTR_DESTINATION_ADDRESS:
+    case STUN_ATTR_SOURCE_ADDRESS2:
+      // TODO: Addresses may be different size for IPv6
+      // addresses, but we don't support IPv6 yet. Fix address parsing
+      // when IPv6 support is implemented.
+      if (length != StunAddressAttribute::SIZE)
+        return NULL;
+      return new StunAddressAttribute(type);
 
-  case STUN_ATTR_CHANGE_REQUEST:
-  case STUN_ATTR_LIFETIME:
-  case STUN_ATTR_BANDWIDTH:
-  case STUN_ATTR_OPTIONS:
-    if (length != StunUInt32Attribute::SIZE)
-      return 0;
-    return new StunUInt32Attribute(type);
+    case STUN_ATTR_LIFETIME:
+    case STUN_ATTR_BANDWIDTH:
+    case STUN_ATTR_OPTIONS:
+      if (length != StunUInt32Attribute::SIZE)
+        return NULL;
+      return new StunUInt32Attribute(type);
 
-  case STUN_ATTR_USERNAME:
-  case STUN_ATTR_PASSWORD:
-  case STUN_ATTR_MAGIC_COOKIE:
-    return (length % 4 == 0) ? new StunByteStringAttribute(type, length) : 0;
+    case STUN_ATTR_USERNAME:
+    case STUN_ATTR_MAGIC_COOKIE:
+      return (length % 4 == 0) ? new StunByteStringAttribute(type, length) : 0;
 
-  case STUN_ATTR_MESSAGE_INTEGRITY:
-    return (length == 20) ? new StunByteStringAttribute(type, length) : 0;
+    case STUN_ATTR_MESSAGE_INTEGRITY:
+      return (length == 20) ? new StunByteStringAttribute(type, length) : 0;
 
-  case STUN_ATTR_DATA:
-    return new StunByteStringAttribute(type, length);
+    case STUN_ATTR_DATA:
+      return new StunByteStringAttribute(type, length);
 
-  case STUN_ATTR_ERROR_CODE:
-    if (length < StunErrorCodeAttribute::MIN_SIZE)
-      return 0;
-    return new StunErrorCodeAttribute(type, length);
+    case STUN_ATTR_ERROR_CODE:
+      if (length < StunErrorCodeAttribute::MIN_SIZE)
+        return NULL;
+      return new StunErrorCodeAttribute(type, length);
 
-  case STUN_ATTR_UNKNOWN_ATTRIBUTES:
-    return (length % 2 == 0) ? new StunUInt16ListAttribute(type, length) : 0;
+    case STUN_ATTR_UNKNOWN_ATTRIBUTES:
+      return (length % 2 == 0) ? new StunUInt16ListAttribute(type, length) : 0;
 
-  case STUN_ATTR_TRANSPORT_PREFERENCES:
-    if ((length != StunTransportPrefsAttribute::SIZE1) &&
-        (length != StunTransportPrefsAttribute::SIZE2))
-      return 0;
-    return new StunTransportPrefsAttribute(type, length);
+    case STUN_ATTR_XOR_MAPPED_ADDRESS:
+      // TODO: Addresses may be different size for IPv6
+      // addresses, but we don't support IPv6 yet. Fix address parsing
+      // when IPv6 support is implemented.
+      if (length != StunAddressAttribute::SIZE)
+        return NULL;
+      return new StunXorAddressAttribute(type);
 
-  default:
-    return 0;
+    default:
+      return NULL;
   }
 }
 
 StunAddressAttribute* StunAttribute::CreateAddress(uint16 type) {
   switch (type) {
-  case STUN_ATTR_MAPPED_ADDRESS:
-  case STUN_ATTR_RESPONSE_ADDRESS:
-  case STUN_ATTR_SOURCE_ADDRESS:
-  case STUN_ATTR_CHANGED_ADDRESS:
-  case STUN_ATTR_REFLECTED_FROM:
-  case STUN_ATTR_ALTERNATE_SERVER:
-  case STUN_ATTR_DESTINATION_ADDRESS:
-  case STUN_ATTR_SOURCE_ADDRESS2:
-    return new StunAddressAttribute(type);
+    case STUN_ATTR_MAPPED_ADDRESS:
+    case STUN_ATTR_DESTINATION_ADDRESS:
+    case STUN_ATTR_SOURCE_ADDRESS2:
+      return new StunAddressAttribute(type);
+
+    case STUN_ATTR_XOR_MAPPED_ADDRESS:
+      return new StunXorAddressAttribute(type);
 
   default:
     ASSERT(false);
-    return 0;
+    return NULL;
   }
 }
 
 StunUInt32Attribute* StunAttribute::CreateUInt32(uint16 type) {
   switch (type) {
-  case STUN_ATTR_CHANGE_REQUEST:
   case STUN_ATTR_LIFETIME:
   case STUN_ATTR_BANDWIDTH:
   case STUN_ATTR_OPTIONS:
@@ -316,22 +304,21 @@
 
   default:
     ASSERT(false);
-    return 0;
+    return NULL;
   }
 }
 
 StunByteStringAttribute* StunAttribute::CreateByteString(uint16 type) {
   switch (type) {
-  case STUN_ATTR_USERNAME:
-  case STUN_ATTR_PASSWORD:
-  case STUN_ATTR_MESSAGE_INTEGRITY:
-  case STUN_ATTR_DATA:
-  case STUN_ATTR_MAGIC_COOKIE:
-    return new StunByteStringAttribute(type, 0);
+    case STUN_ATTR_USERNAME:
+    case STUN_ATTR_MESSAGE_INTEGRITY:
+    case STUN_ATTR_DATA:
+    case STUN_ATTR_MAGIC_COOKIE:
+      return new StunByteStringAttribute(type, 0);
 
-  default:
-    ASSERT(false);
-    return 0;
+    default:
+      ASSERT(false);
+      return NULL;
   }
 }
 
@@ -344,35 +331,70 @@
   return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
 }
 
-StunTransportPrefsAttribute* StunAttribute::CreateTransportPrefs() {
-  return new StunTransportPrefsAttribute(
-      STUN_ATTR_TRANSPORT_PREFERENCES, StunTransportPrefsAttribute::SIZE1);
+StunAddressAttribute::StunAddressAttribute(uint16 type)
+    : StunAttribute(type, SIZE), family_(STUN_ADDRESS_IPV4), port_(0), ip_(0) {
 }
 
-StunAddressAttribute::StunAddressAttribute(uint16 type)
-    : StunAttribute(type, SIZE), family_(0), port_(0), ip_(0) {
+void StunAddressAttribute::SetFamily(StunAddressFamily family) {
+  family_ = family;
 }
 
 bool StunAddressAttribute::Read(ByteBuffer* buf) {
   uint8 dummy;
   if (!buf->ReadUInt8(&dummy))
     return false;
-  if (!buf->ReadUInt8(&family_))
+
+  uint8 family;
+  // We don't expect IPv6 address here because IPv6 addresses would
+  // not pass the attribute size check in StunAttribute::Create().
+  if (!buf->ReadUInt8(&family) || family != STUN_ADDRESS_IPV4) {
     return false;
+  }
+  family_ = static_cast<StunAddressFamily>(family);
+
   if (!buf->ReadUInt16(&port_))
     return false;
+
   if (!buf->ReadUInt32(&ip_))
     return false;
+
   return true;
 }
 
 void StunAddressAttribute::Write(ByteBuffer* buf) const {
+  // Only IPv4 address family is currently supported.
+  ASSERT(family_ == STUN_ADDRESS_IPV4);
+
   buf->WriteUInt8(0);
   buf->WriteUInt8(family_);
   buf->WriteUInt16(port_);
   buf->WriteUInt32(ip_);
 }
 
+StunXorAddressAttribute::StunXorAddressAttribute(uint16 type)
+    : StunAddressAttribute(type) {
+}
+
+bool StunXorAddressAttribute::Read(ByteBuffer* buf) {
+  if (!StunAddressAttribute::Read(buf))
+    return false;
+
+  SetPort(port() ^ (kStunMagicCookie >> 16));
+  SetIP(ip() ^ kStunMagicCookie);
+
+  return true;
+}
+
+void StunXorAddressAttribute::Write(ByteBuffer* buf) const {
+  // Only IPv4 address family is currently supported.
+  ASSERT(family() == STUN_ADDRESS_IPV4);
+
+  buf->WriteUInt8(0);
+  buf->WriteUInt8(family());
+  buf->WriteUInt16(port() ^ (kStunMagicCookie >> 16));
+  buf->WriteUInt32(ip() ^ kStunMagicCookie);
+}
+
 StunUInt32Attribute::StunUInt32Attribute(uint16 type)
     : StunAttribute(type, SIZE), bits_(0) {
 }
@@ -524,86 +546,29 @@
     buf->WriteUInt16((*attr_types_)[i]);
 }
 
-StunTransportPrefsAttribute::StunTransportPrefsAttribute(
-    uint16 type, uint16 length)
-    : StunAttribute(type, length), preallocate_(false), prefs_(0), addr_(0) {
-}
-
-StunTransportPrefsAttribute::~StunTransportPrefsAttribute() {
-  delete addr_;
-}
-
-void StunTransportPrefsAttribute::SetPreallocateAddress(
-    StunAddressAttribute* addr) {
-  if (!addr) {
-    preallocate_ = false;
-    addr_ = 0;
-    SetLength(SIZE1);
-  } else {
-    preallocate_ = true;
-    addr_ = addr;
-    SetLength(SIZE2);
-  }
-}
-
-bool StunTransportPrefsAttribute::Read(ByteBuffer* buf) {
-  uint32 val;
-  if (!buf->ReadUInt32(&val))
-    return false;
-
-  if ((val >> 3) != 0)
-    LOG(LERROR) << "transport-preferences bits not zero";
-
-  preallocate_ = static_cast<bool>((val >> 2) & 0x1);
-  prefs_ = (uint8)(val & 0x3);
-
-  if (preallocate_ && (prefs_ == 3))
-    LOG(LERROR) << "transport-preferences imcompatible P and Typ";
-
-  if (!preallocate_) {
-    if (length() != StunUInt32Attribute::SIZE)
-      return false;
-  } else {
-    if (length() != StunUInt32Attribute::SIZE + StunAddressAttribute::SIZE)
-      return false;
-
-    addr_ = new StunAddressAttribute(STUN_ATTR_SOURCE_ADDRESS);
-    addr_->Read(buf);
-  }
-
-  return true;
-}
-
-void StunTransportPrefsAttribute::Write(ByteBuffer* buf) const {
-  buf->WriteUInt32((preallocate_ ? 4 : 0) | prefs_);
-
-  if (preallocate_)
-    addr_->Write(buf);
-}
-
 StunMessageType GetStunResponseType(StunMessageType request_type) {
   switch (request_type) {
-  case STUN_SHARED_SECRET_REQUEST:
-    return STUN_SHARED_SECRET_RESPONSE;
-  case STUN_ALLOCATE_REQUEST:
-    return STUN_ALLOCATE_RESPONSE;
-  case STUN_SEND_REQUEST:
-    return STUN_SEND_RESPONSE;
-  default:
-    return STUN_BINDING_RESPONSE;
+    case STUN_SHARED_SECRET_REQUEST:
+      return STUN_SHARED_SECRET_RESPONSE;
+    case STUN_ALLOCATE_REQUEST:
+      return STUN_ALLOCATE_RESPONSE;
+    case STUN_SEND_REQUEST:
+      return STUN_SEND_RESPONSE;
+    default:
+      return STUN_BINDING_RESPONSE;
   }
 }
 
 StunMessageType GetStunErrorResponseType(StunMessageType request_type) {
   switch (request_type) {
-  case STUN_SHARED_SECRET_REQUEST:
-    return STUN_SHARED_SECRET_ERROR_RESPONSE;
-  case STUN_ALLOCATE_REQUEST:
-    return STUN_ALLOCATE_ERROR_RESPONSE;
-  case STUN_SEND_REQUEST:
-    return STUN_SEND_ERROR_RESPONSE;
-  default:
-    return STUN_BINDING_ERROR_RESPONSE;
+    case STUN_SHARED_SECRET_REQUEST:
+      return STUN_SHARED_SECRET_ERROR_RESPONSE;
+    case STUN_ALLOCATE_REQUEST:
+      return STUN_ALLOCATE_ERROR_RESPONSE;
+    case STUN_SEND_REQUEST:
+      return STUN_SEND_ERROR_RESPONSE;
+    default:
+      return STUN_BINDING_ERROR_RESPONSE;
   }
 }
 
diff --git a/talk/p2p/base/stun.h b/talk/p2p/base/stun.h
index cc0e972..efc5f2c 100644
--- a/talk/p2p/base/stun.h
+++ b/talk/p2p/base/stun.h
@@ -2,26 +2,26 @@
  * libjingle
  * Copyright 2004--2005, Google Inc.
  *
- * Redistribution and use in source and binary forms, with or without 
+ * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
  *
- *  1. Redistributions of source code must retain the above copyright notice, 
+ *  1. Redistributions of source code must retain the above copyright notice,
  *     this list of conditions and the following disclaimer.
  *  2. Redistributions in binary form must reproduce the above copyright notice,
  *     this list of conditions and the following disclaimer in the documentation
  *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products 
+ *  3. The name of the author may not be used to endorse or promote products
  *     derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
@@ -39,9 +39,6 @@
 
 namespace cricket {
 
-// TODO: Remove message types and attributes that are no
-// longer suppored in RFC5389 (see section 18.2).
-
 // These are the types of STUN & TURN messages as of last check.
 enum StunMessageType {
   STUN_BINDING_REQUEST              = 0x0001,
@@ -61,26 +58,23 @@
 
 // These are the types of attributes defined in STUN & TURN.  Next to each is
 // the name of the class (T is StunTAttribute) that implements that type.
+//
+// TODO: Some attributes defined in RFC5389 are not
+// implemented yet, particularly REALM, NONCE, SOFTWARE,
+// ALTERNATE-SERVE and FINGEPRINT. Implement them.
 enum StunAttributeType {
   STUN_ATTR_MAPPED_ADDRESS        = 0x0001, // Address
-  STUN_ATTR_RESPONSE_ADDRESS      = 0x0002, // Address
-  STUN_ATTR_CHANGE_REQUEST        = 0x0003, // UInt32
-  STUN_ATTR_SOURCE_ADDRESS        = 0x0004, // Address
-  STUN_ATTR_CHANGED_ADDRESS       = 0x0005, // Address
   STUN_ATTR_USERNAME              = 0x0006, // ByteString, multiple of 4 bytes
-  STUN_ATTR_PASSWORD              = 0x0007, // ByteString, multiple of 4 bytes
   STUN_ATTR_MESSAGE_INTEGRITY     = 0x0008, // ByteString, 20 bytes
   STUN_ATTR_ERROR_CODE            = 0x0009, // ErrorCode
   STUN_ATTR_UNKNOWN_ATTRIBUTES    = 0x000a, // UInt16List
-  STUN_ATTR_REFLECTED_FROM        = 0x000b, // Address
-  STUN_ATTR_TRANSPORT_PREFERENCES = 0x000c, // TransportPrefs
   STUN_ATTR_LIFETIME              = 0x000d, // UInt32
-  STUN_ATTR_ALTERNATE_SERVER      = 0x000e, // Address
   STUN_ATTR_MAGIC_COOKIE          = 0x000f, // ByteString, 4 bytes
   STUN_ATTR_BANDWIDTH             = 0x0010, // UInt32
   STUN_ATTR_DESTINATION_ADDRESS   = 0x0011, // Address
   STUN_ATTR_SOURCE_ADDRESS2       = 0x0012, // Address
   STUN_ATTR_DATA                  = 0x0013, // ByteString
+  STUN_ATTR_XOR_MAPPED_ADDRESS    = 0x0020, // XorAddress
   STUN_ATTR_OPTIONS               = 0x8001  // UInt32
 };
 
@@ -96,20 +90,19 @@
   STUN_ERROR_GLOBAL_FAILURE       = 600
 };
 
-extern const std::string STUN_ERROR_REASON_BAD_REQUEST;
-extern const std::string STUN_ERROR_REASON_UNAUTHORIZED;
-extern const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE;
-extern const std::string STUN_ERROR_REASON_STALE_CREDENTIALS;
-extern const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE;
-extern const std::string STUN_ERROR_REASON_MISSING_USERNAME;
-extern const std::string STUN_ERROR_REASON_USE_TLS;
-extern const std::string STUN_ERROR_REASON_SERVER_ERROR;
-extern const std::string STUN_ERROR_REASON_GLOBAL_FAILURE;
+enum StunAddressFamily {
+  STUN_ADDRESS_IPV4 = 1,
+  STUN_ADDRESS_IPV6 = 2
+};
+
+extern const char STUN_ERROR_REASON_BAD_REQUEST[];
+extern const char STUN_ERROR_REASON_STALE_CREDENTIALS[];
+extern const char STUN_ERROR_REASON_SERVER_ERROR[];
 
 // Following values correspond to RFC5389.
 const size_t kStunTransactionIdOffset = 8;
 const size_t kStunTransactionIdLength = 12;
-extern const char kStunMagicCookie[4];
+const uint32 kStunMagicCookie = 0x2112A442;
 const size_t kStunMagicCookieLength = sizeof(kStunMagicCookie);
 
 // Following value corresponds to an earlier version of STUN from
@@ -217,39 +210,40 @@
 public:
   explicit StunAddressAttribute(uint16 type);
 
-#if (_MSC_VER < 1300)
-  enum { SIZE = 8 };
-#else
   static const uint16 SIZE = 8;
-#endif
 
-  uint8 family() const { return family_; }
+  StunAddressFamily family() const { return family_; }
   uint16 port() const { return port_; }
   uint32 ip() const { return ip_; }
 
-  void SetFamily(uint8 family) { family_ = family; }
+  void SetFamily(StunAddressFamily family);
   void SetIP(uint32 ip) { ip_ = ip; }
   void SetPort(uint16 port) { port_ = port; }
 
-  bool Read(talk_base::ByteBuffer* buf);
-  void Write(talk_base::ByteBuffer* buf) const;
+  virtual bool Read(talk_base::ByteBuffer* buf);
+  virtual void Write(talk_base::ByteBuffer* buf) const;
 
 private:
-  uint8 family_;
+  StunAddressFamily family_;
   uint16 port_;
   uint32 ip_;
 };
 
+// Implements STUN/TURN attributes that record an Internet address.
+class StunXorAddressAttribute : public StunAddressAttribute {
+public:
+  explicit StunXorAddressAttribute(uint16 type);
+
+  virtual bool Read(talk_base::ByteBuffer* buf);
+  virtual void Write(talk_base::ByteBuffer* buf) const;
+};
+
 // Implements STUN/TURN attributs that record a 32-bit integer.
 class StunUInt32Attribute : public StunAttribute {
 public:
   explicit StunUInt32Attribute(uint16 type);
 
-#if (_MSC_VER < 1300)
-  enum { SIZE = 4 };
-#else
   static const uint16 SIZE = 4;
-#endif
 
   uint32 value() const { return bits_; }
 
@@ -294,11 +288,7 @@
   StunErrorCodeAttribute(uint16 type, uint16 length);
   ~StunErrorCodeAttribute();
 
-#if (_MSC_VER < 1300)
-  enum { MIN_SIZE = 4 };
-#else
   static const uint16 MIN_SIZE = 4;
-#endif
 
   uint32 error_code() const { return (class_ << 8) | number_; }
   uint8 error_class() const { return class_; }
@@ -337,39 +327,6 @@
   std::vector<uint16>* attr_types_;
 };
 
-// Implements the TURN TRANSPORT-PREFS attribute, which provides information
-// about the ports to allocate.
-class StunTransportPrefsAttribute : public StunAttribute {
-public:
-  StunTransportPrefsAttribute(uint16 type, uint16 length);
-  ~StunTransportPrefsAttribute();
-
-#if (_MSC_VER < 1300)
-  enum { SIZE1 = 4, SIZE2 = 12 };
-#else
-  static const uint16 SIZE1 = 4;
-  static const uint16 SIZE2 = 12;
-#endif
-
-  bool preallocate() const { return preallocate_; }
-  uint8 preference_type() const { return prefs_; }
-  const StunAddressAttribute* address() const { return addr_; }
-
-  void SetPreferenceType(uint8 prefs) { prefs_ = prefs; }
-
-  // Sets the preallocate address to the given value, or if 0 is given, it sets
-  // to not preallocate.
-  void SetPreallocateAddress(StunAddressAttribute* addr);
-
-  bool Read(talk_base::ByteBuffer* buf);
-  void Write(talk_base::ByteBuffer* buf) const;
-
-private:
-  bool preallocate_;
-  uint8 prefs_;
-  StunAddressAttribute* addr_;
-};
-
 // The special MAGIC-COOKIE attribute is used to distinguish TURN packets from
 // other kinds of traffic.
 // TODO: This value has nothing to do with STUN. Move it to a
diff --git a/talk/p2p/base/stunport.cc b/talk/p2p/base/stunport.cc
index 2b81fee..ba797ff 100644
--- a/talk/p2p/base/stunport.cc
+++ b/talk/p2p/base/stunport.cc
@@ -122,7 +122,7 @@
   uint32 start_time_;
 };
 
-const std::string STUN_PORT_TYPE("stun");
+const char STUN_PORT_TYPE[] = "stun";
 
 StunPort::StunPort(talk_base::Thread* thread,
                    talk_base::PacketSocketFactory* factory,
diff --git a/talk/p2p/base/stunport.h b/talk/p2p/base/stunport.h
index 7db3b04..26f46a5 100644
--- a/talk/p2p/base/stunport.h
+++ b/talk/p2p/base/stunport.h
@@ -41,7 +41,7 @@
 
 namespace cricket {
 
-extern const std::string STUN_PORT_TYPE;
+extern const char STUN_PORT_TYPE[];
 
 // Communicates using the address on the outside of a NAT.
 class StunPort : public Port {
diff --git a/talk/p2p/base/stunrequest.cc b/talk/p2p/base/stunrequest.cc
index 9c1ada7..f1fd2ac 100644
--- a/talk/p2p/base/stunrequest.cc
+++ b/talk/p2p/base/stunrequest.cc
@@ -78,8 +78,11 @@
   for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i)
     requests.push_back(i->second);
 
-  for (uint32 i = 0; i < requests.size(); ++i)
-    Remove(requests[i]);
+  for (uint32 i = 0; i < requests.size(); ++i) {
+    // StunRequest destructor calls Remove() which deletes requests
+    // from |requests_|.
+    delete requests[i];
+  }
 }
 
 bool StunRequestManager::CheckResponse(StunMessage* msg) {
diff --git a/talk/p2p/base/stunserver.cc b/talk/p2p/base/stunserver.cc
index a3c4bde..f34fd2d 100644
--- a/talk/p2p/base/stunserver.cc
+++ b/talk/p2p/base/stunserver.cc
@@ -89,26 +89,18 @@
   response.SetTransactionID(msg->transaction_id());
 
   // Tell the user the address that we received their request from.
-  StunAddressAttribute* mapped_addr =
-      StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
-  mapped_addr->SetFamily(1);
+  StunAddressAttribute* mapped_addr;
+  if (!msg->IsLegacy()) {
+    mapped_addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
+  } else {
+    mapped_addr = StunAttribute::CreateAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
+  }
   mapped_addr->SetPort(remote_addr.port());
   mapped_addr->SetIP(remote_addr.ip());
   response.AddAttribute(mapped_addr);
 
-  // Tell the user the address that we are sending the response from.
-  talk_base::SocketAddress local_addr = socket_->GetLocalAddress();
-  StunAddressAttribute* source_addr =
-      StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS);
-  source_addr->SetFamily(1);
-  source_addr->SetPort(local_addr.port());
-  source_addr->SetIP(local_addr.ip());
-  response.AddAttribute(source_addr);
-
   // TODO: Add username and message-integrity.
 
-  // TODO: Add changed-address.  (Keep information about three other servers.)
-
   SendResponse(response, remote_addr);
 }
 
diff --git a/talk/p2p/base/tcpport.h b/talk/p2p/base/tcpport.h
index fcc4aa2..8345b12 100644
--- a/talk/p2p/base/tcpport.h
+++ b/talk/p2p/base/tcpport.h
@@ -37,7 +37,7 @@
 
 class TCPConnection;
 
-extern const std::string LOCAL_PORT_TYPE;  // type of TCP ports
+extern const char LOCAL_PORT_TYPE[];  // type of TCP ports
 
 // Communicates using a local TCP port.
 //
diff --git a/talk/p2p/base/udpport.cc b/talk/p2p/base/udpport.cc
index e7644f4..8355b00 100644
--- a/talk/p2p/base/udpport.cc
+++ b/talk/p2p/base/udpport.cc
@@ -33,7 +33,7 @@
 
 namespace cricket {
 
-const std::string LOCAL_PORT_TYPE("local");
+const char LOCAL_PORT_TYPE[] = "local";
 
 UDPPort::UDPPort(talk_base::Thread* thread,
                  talk_base::PacketSocketFactory* factory,
diff --git a/talk/p2p/base/udpport.h b/talk/p2p/base/udpport.h
index 6663dc6..d74d4a3 100644
--- a/talk/p2p/base/udpport.h
+++ b/talk/p2p/base/udpport.h
@@ -2,26 +2,26 @@
  * libjingle
  * Copyright 2004--2005, Google Inc.
  *
- * Redistribution and use in source and binary forms, with or without 
+ * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
  *
- *  1. Redistributions of source code must retain the above copyright notice, 
+ *  1. Redistributions of source code must retain the above copyright notice,
  *     this list of conditions and the following disclaimer.
  *  2. Redistributions in binary form must reproduce the above copyright notice,
  *     this list of conditions and the following disclaimer in the documentation
  *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products 
+ *  3. The name of the author may not be used to endorse or promote products
  *     derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
@@ -40,7 +40,7 @@
 
 namespace cricket {
 
-extern const std::string LOCAL_PORT_TYPE;  // type of UDP ports
+extern const char LOCAL_PORT_TYPE[];  // type of UDP ports
 
 // Communicates using a local UDP port.
 class UDPPort : public Port {
diff --git a/talk/p2p/client/httpportallocator.cc b/talk/p2p/client/httpportallocator.cc
index 1e5e256..1a25287 100644
--- a/talk/p2p/client/httpportallocator.cc
+++ b/talk/p2p/client/httpportallocator.cc
@@ -94,7 +94,7 @@
 const int HttpPortAllocator::kHostPort = 80;
 const int HttpPortAllocator::kNumRetries = 5;
 
-const std::string HttpPortAllocator::kCreateSessionURL = "/create_session";
+const char HttpPortAllocator::kCreateSessionURL[] = "/create_session";
 
 HttpPortAllocator::HttpPortAllocator(
     talk_base::NetworkManager* network_manager,
diff --git a/talk/p2p/client/httpportallocator.h b/talk/p2p/client/httpportallocator.h
index 82e9110..e7211c5 100644
--- a/talk/p2p/client/httpportallocator.h
+++ b/talk/p2p/client/httpportallocator.h
@@ -47,7 +47,7 @@
   static const int kNumRetries;
 
   // Records the URL that we will GET in order to create a session.
-  static const std::string kCreateSessionURL;
+  static const char kCreateSessionURL[];
 
   HttpPortAllocator(talk_base::NetworkManager* network_manager,
                     const std::string& user_agent);
diff --git a/talk/p2p/client/sessionmanagertask.h b/talk/p2p/client/sessionmanagertask.h
index 2026bf6..d7d9733 100644
--- a/talk/p2p/client/sessionmanagertask.h
+++ b/talk/p2p/client/sessionmanagertask.h
@@ -2,26 +2,26 @@
  * libjingle
  * Copyright 2004--2005, Google Inc.
  *
- * Redistribution and use in source and binary forms, with or without 
+ * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
  *
- *  1. Redistributions of source code must retain the above copyright notice, 
+ *  1. Redistributions of source code must retain the above copyright notice,
  *     this list of conditions and the following disclaimer.
  *  2. Redistributions in binary form must reproduce the above copyright notice,
  *     this list of conditions and the following disclaimer in the documentation
  *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products 
+ *  3. The name of the author may not be used to endorse or promote products
  *     derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
@@ -40,9 +40,10 @@
 
 class SessionManagerTask : public buzz::XmppTask {
  public:
-  SessionManagerTask(TaskParent *parent, SessionManager *session_manager)
-      : buzz::XmppTask(parent, buzz::XmppEngine::HL_SINGLE) {
-    session_manager_ = session_manager;
+  SessionManagerTask(buzz::XmppTaskParentInterface* parent,
+                     SessionManager* session_manager)
+      : buzz::XmppTask(parent, buzz::XmppEngine::HL_SINGLE),
+        session_manager_(session_manager) {
   }
 
   ~SessionManagerTask() {
@@ -76,15 +77,15 @@
   }
 
  private:
-  SessionManager* session_manager_;
-
   void OnOutgoingMessage(SessionManager* manager,
                          const buzz::XmlElement* stanza) {
     cricket::SessionSendTask* sender =
-        new cricket::SessionSendTask(GetParent(), session_manager_);
+        new cricket::SessionSendTask(parent_, session_manager_);
     sender->Send(stanza);
     sender->Start();
   }
+
+  SessionManager* session_manager_;
 };
 
 }  // namespace cricket
diff --git a/talk/p2p/client/sessionsendtask.h b/talk/p2p/client/sessionsendtask.h
index 5466e47..ea1e4f0 100644
--- a/talk/p2p/client/sessionsendtask.h
+++ b/talk/p2p/client/sessionsendtask.h
@@ -2,26 +2,26 @@
  * libjingle
  * Copyright 2004--2005, Google Inc.
  *
- * Redistribution and use in source and binary forms, with or without 
+ * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
  *
- *  1. Redistributions of source code must retain the above copyright notice, 
+ *  1. Redistributions of source code must retain the above copyright notice,
  *     this list of conditions and the following disclaimer.
  *  2. Redistributions in binary form must reproduce the above copyright notice,
  *     this list of conditions and the following disclaimer in the documentation
  *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products 
+ *  3. The name of the author may not be used to endorse or promote products
  *     derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
@@ -45,7 +45,8 @@
 
 class SessionSendTask : public buzz::XmppTask {
  public:
-  SessionSendTask(TaskParent *parent, SessionManager *session_manager)
+  SessionSendTask(buzz::XmppTaskParentInterface* parent,
+                  SessionManager* session_manager)
     : buzz::XmppTask(parent, buzz::XmppEngine::HL_SINGLE),
       session_manager_(session_manager) {
     set_timeout_seconds(15);
@@ -107,7 +108,7 @@
   }
 
   virtual int ProcessResponse() {
-    if (GetClient()->GetState() != buzz::XmppEngine::STATE_OPEN) {
+    if (parent_->GetState() != buzz::XmppEngine::STATE_OPEN) {
       return STATE_DONE;
     }
 
diff --git a/talk/session/phone/call.cc b/talk/session/phone/call.cc
index 5925e30..e01e937 100644
--- a/talk/session/phone/call.cc
+++ b/talk/session/phone/call.cc
@@ -54,6 +54,7 @@
       local_renderer_(NULL),
       video_(false),
       muted_(false),
+      video_muted_(false),
       send_to_voicemail_(true),
       playing_dtmf_(false) {
 }
@@ -94,7 +95,7 @@
   SignalSessionState(this, session, Session::STATE_RECEIVEDINITIATE);
 }
 
-void Call::AcceptSession(BaseSession* session,
+void Call::AcceptSession(Session* session,
                          const cricket::CallOptions& options) {
   std::vector<Session *>::iterator it;
   it = std::find(sessions_.begin(), sessions_.end(), session);
@@ -105,7 +106,7 @@
   }
 }
 
-void Call::RejectSession(BaseSession *session) {
+void Call::RejectSession(Session *session) {
   std::vector<Session *>::iterator it;
   it = std::find(sessions_.begin(), sessions_.end(), session);
   ASSERT(it != sessions_.end());
@@ -114,7 +115,7 @@
     session->Reject(STR_TERMINATE_DECLINE);
 }
 
-void Call::TerminateSession(BaseSession *session) {
+void Call::TerminateSession(Session *session) {
   ASSERT(std::find(sessions_.begin(), sessions_.end(), session)
          != sessions_.end());
   std::vector<Session *>::iterator it;
@@ -165,7 +166,7 @@
   }
 }
 
-void Call::SetVideoRenderer(BaseSession *session, uint32 ssrc,
+void Call::SetVideoRenderer(Session *session, uint32 ssrc,
                             VideoRenderer* renderer) {
   VideoChannel *video_channel = GetVideoChannel(session);
   if (video_channel) {
@@ -177,14 +178,14 @@
   }
 }
 
-void Call::AddVoiceStream(BaseSession *session, uint32 voice_ssrc) {
+void Call::AddVoiceStream(Session *session, uint32 voice_ssrc) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
   if (voice_channel && voice_ssrc) {
     voice_channel->AddStream(voice_ssrc);
   }
 }
 
-void Call::AddVideoStream(BaseSession *session, uint32 video_ssrc) {
+void Call::AddVideoStream(Session *session, uint32 video_ssrc) {
   VideoChannel *video_channel = GetVideoChannel(session);
   if (video_channel && video_ssrc) {
     // TODO: Do we need the audio_ssrc here?
@@ -193,14 +194,14 @@
   }
 }
 
-void Call::RemoveVoiceStream(BaseSession *session, uint32 voice_ssrc) {
+void Call::RemoveVoiceStream(Session *session, uint32 voice_ssrc) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
   if (voice_channel && voice_ssrc) {
     voice_channel->RemoveStream(voice_ssrc);
   }
 }
 
-void Call::RemoveVideoStream(BaseSession *session, uint32 video_ssrc) {
+void Call::RemoveVideoStream(Session *session, uint32 video_ssrc) {
   VideoChannel *video_channel = GetVideoChannel(session);
   if (video_channel && video_ssrc) {
     video_channel->RemoveStream(video_ssrc);
@@ -238,32 +239,39 @@
   VoiceChannel *voice_channel = NULL;
   VideoChannel *video_channel = NULL;
 
+  // Generate a random string for the RTCP CNAME, as stated in RFC 6222.
+  // This string is only used for synchronization, and therefore is opaque.
+  std::string rtcp_cname;
+  if (!talk_base::CreateRandomString(16, &rtcp_cname)) {
+    return false;
+  }
+
   const ContentInfo* audio_offer = GetFirstAudioContent(offer);
   const ContentInfo* video_offer = GetFirstVideoContent(offer);
   video_ = (video_offer != NULL);
 
   ASSERT(audio_offer != NULL);
-  // Create voice channel and start a media monitor
+  // Create voice channel and start a media monitor.
   voice_channel = session_client_->channel_manager()->CreateVoiceChannel(
       session, audio_offer->name, video_);
   // voice_channel can be NULL in case of NullVoiceEngine.
   if (voice_channel) {
     voice_channel_map_[session->id()] = voice_channel;
-
+    voice_channel->SetRtcpCName(rtcp_cname);
     voice_channel->SignalMediaMonitor.connect(this, &Call::OnMediaMonitor);
     voice_channel->StartMediaMonitor(kMediaMonitorInterval);
   } else {
     succeeded = false;
   }
 
-  // If desired, create video channel and start a media monitor
+  // If desired, create video channel and start a media monitor.
   if (video_ && succeeded) {
     video_channel = session_client_->channel_manager()->CreateVideoChannel(
         session, video_offer->name, true, voice_channel);
     // video_channel can be NULL in case of NullVideoEngine.
     if (video_channel) {
       video_channel_map_[session->id()] = video_channel;
-
+      video_channel->SetRtcpCName(rtcp_cname);
       video_channel->SignalMediaMonitor.connect(this, &Call::OnMediaMonitor);
       video_channel->StartMediaMonitor(kMediaMonitorInterval);
     } else {
@@ -272,7 +280,7 @@
   }
 
   if (succeeded) {
-    // Add session to list, create channels for this session
+    // Add session to list, create channels for this session.
     sessions_.push_back(session);
     session->SignalState.connect(this, &Call::OnSessionState);
     session->SignalError.connect(this, &Call::OnSessionError);
@@ -280,7 +288,7 @@
     session->SignalReceivedTerminateReason
       .connect(this, &Call::OnReceivedTerminateReason);
 
-    // If this call has the focus, enable this channel
+    // If this call has the focus, enable this channel.
     if (session_client_->GetFocus() == this) {
       voice_channel->Enable(true);
       if (video_channel) {
@@ -288,7 +296,7 @@
       }
     }
 
-    // Signal client
+    // Signal client.
     SignalAddSession(this, session);
   }
 
@@ -331,13 +339,13 @@
   talk_base::Thread::Current()->Post(this, MSG_CHECKAUTODESTROY);
 }
 
-VoiceChannel* Call::GetVoiceChannel(BaseSession* session) {
+VoiceChannel* Call::GetVoiceChannel(Session* session) {
   std::map<std::string, VoiceChannel *>::iterator it
     = voice_channel_map_.find(session->id());
   return (it != voice_channel_map_.end()) ? it->second : NULL;
 }
 
-VideoChannel* Call::GetVideoChannel(BaseSession* session) {
+VideoChannel* Call::GetVideoChannel(Session* session) {
   std::map<std::string, VideoChannel *>::iterator it
     = video_channel_map_.find(session->id());
   return (it != video_channel_map_.end()) ? it->second : NULL;
@@ -367,6 +375,16 @@
   }
 }
 
+void Call::MuteVideo(bool mute) {
+  video_muted_ = mute;
+  std::vector<Session *>::iterator it;
+  for (it = sessions_.begin(); it != sessions_.end(); it++) {
+    VideoChannel *video_channel = video_channel_map_[(*it)->id()];
+    if (video_channel != NULL)
+      video_channel->Mute(mute);
+  }
+}
+
 void Call::PressDTMF(int event) {
   // Queue up this digit
   if (queued_dtmf_.size() < kMaxDTMFDigits) {
@@ -438,7 +456,7 @@
   }
 }
 
-void Call::StartConnectionMonitor(BaseSession *session, int cms) {
+void Call::StartConnectionMonitor(Session *session, int cms) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
   if (voice_channel) {
     voice_channel->SignalConnectionMonitor.connect(this,
@@ -454,7 +472,7 @@
   }
 }
 
-void Call::StopConnectionMonitor(BaseSession *session) {
+void Call::StopConnectionMonitor(Session *session) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
   if (voice_channel) {
     voice_channel->StopConnectionMonitor();
@@ -468,7 +486,7 @@
   }
 }
 
-void Call::StartAudioMonitor(BaseSession *session, int cms) {
+void Call::StartAudioMonitor(Session *session, int cms) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
   if (voice_channel) {
     voice_channel->SignalAudioMonitor.connect(this, &Call::OnAudioMonitor);
@@ -476,7 +494,7 @@
   }
 }
 
-void Call::StopAudioMonitor(BaseSession *session) {
+void Call::StopAudioMonitor(Session *session) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
   if (voice_channel) {
     voice_channel->StopAudioMonitor();
@@ -484,7 +502,7 @@
   }
 }
 
-bool Call::IsAudioMonitorRunning(BaseSession *session) {
+bool Call::IsAudioMonitorRunning(Session *session) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
   if (voice_channel) {
     return voice_channel->IsAudioMonitorRunning();
@@ -493,7 +511,7 @@
   }
 }
 
-void Call::StartSpeakerMonitor(BaseSession *session) {
+void Call::StartSpeakerMonitor(Session *session) {
   if (speaker_monitor_map_.find(session->id()) == speaker_monitor_map_.end()) {
     if (!IsAudioMonitorRunning(session)) {
       StartAudioMonitor(session, kAudioMonitorPollPeriodMillis);
@@ -509,7 +527,7 @@
   }
 }
 
-void Call::StopSpeakerMonitor(BaseSession *session) {
+void Call::StopSpeakerMonitor(Session *session) {
   if (speaker_monitor_map_.find(session->id()) == speaker_monitor_map_.end()) {
     LOG(LS_WARNING) << "Speaker monitor for session "
                     << session->id() << " already stopped.";
@@ -538,7 +556,8 @@
   NamedSource source;
   source.ssrc = ssrc;
   media_sources_.GetAudioSourceBySsrc(ssrc, &source);
-  SignalSpeakerMonitor(this, monitor->session(), source);
+  SignalSpeakerMonitor(this, static_cast<Session *>(monitor->session()),
+                       source);
 }
 
 void Call::OnConnectionMonitor(VideoChannel *channel,
@@ -565,13 +584,13 @@
     default:
       break;
   }
-  SignalSessionState(this, session, state);
+  SignalSessionState(this, static_cast<Session *>(session), state);
 }
 
 void Call::OnSessionError(BaseSession *session, Session::Error error) {
   session_client_->session_manager()->signaling_thread()->Clear(this,
       MSG_TERMINATECALL);
-  SignalSessionError(this, session, error);
+  SignalSessionError(this, static_cast<Session *>(session), error);
 }
 
 void Call::OnSessionInfo(Session *session,
diff --git a/talk/session/phone/call.h b/talk/session/phone/call.h
index 19dc59a..357f884 100644
--- a/talk/session/phone/call.h
+++ b/talk/session/phone/call.h
@@ -39,42 +39,49 @@
 #include "talk/session/phone/audiomonitor.h"
 #include "talk/session/phone/currentspeakermonitor.h"
 #include "talk/session/phone/mediamessages.h"
-#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/mediasession.h"
 
 namespace cricket {
 
 class MediaSessionClient;
-struct CallOptions;
+class VoiceChannel;
+class VideoChannel;
+
+// Can't typedef this easily since it's forward declared as struct elsewhere.
+struct CallOptions : public MediaSessionOptions {
+};
 
 class Call : public talk_base::MessageHandler, public sigslot::has_slots<> {
  public:
-  Call(MediaSessionClient* session_client);
+  explicit Call(MediaSessionClient* session_client);
   ~Call();
 
   Session *InitiateSession(const buzz::Jid &jid, const CallOptions& options);
-  void AcceptSession(BaseSession *session, const CallOptions& options);
-  void RejectSession(BaseSession *session);
-  void TerminateSession(BaseSession *session);
+  void AcceptSession(Session *session, const CallOptions& options);
+  void RejectSession(Session *session);
+  void TerminateSession(Session *session);
   void Terminate();
   bool SendViewRequest(Session* session,
                        const ViewRequest& view_request);
   void SetLocalRenderer(VideoRenderer* renderer);
-  void SetVideoRenderer(BaseSession *session, uint32 ssrc,
+  void SetVideoRenderer(Session *session, uint32 ssrc,
                         VideoRenderer* renderer);
-  void StartConnectionMonitor(BaseSession *session, int cms);
-  void StopConnectionMonitor(BaseSession *session);
-  void StartAudioMonitor(BaseSession *session, int cms);
-  void StopAudioMonitor(BaseSession *session);
-  bool IsAudioMonitorRunning(BaseSession *session);
-  void StartSpeakerMonitor(BaseSession *session);
-  void StopSpeakerMonitor(BaseSession *session);
+  void StartConnectionMonitor(Session *session, int cms);
+  void StopConnectionMonitor(Session *session);
+  void StartAudioMonitor(Session *session, int cms);
+  void StopAudioMonitor(Session *session);
+  bool IsAudioMonitorRunning(Session *session);
+  void StartSpeakerMonitor(Session *session);
+  void StopSpeakerMonitor(Session *session);
   void Mute(bool mute);
+  void MuteVideo(bool mute);
   void PressDTMF(int event);
 
   const std::vector<Session *> &sessions();
   uint32 id();
   bool video() const { return video_; }
   bool muted() const { return muted_; }
+  bool video_muted() const { return video_muted_; }
 
   // Setting this to false will cause the call to have a longer timeout and
   // for the SignalSetupToCallVoicemail to never fire.
@@ -88,9 +95,9 @@
   sigslot::signal0<> SignalSetupToCallVoicemail;
   sigslot::signal2<Call *, Session *> SignalAddSession;
   sigslot::signal2<Call *, Session *> SignalRemoveSession;
-  sigslot::signal3<Call *, BaseSession *, BaseSession::State>
+  sigslot::signal3<Call *, Session *, Session::State>
       SignalSessionState;
-  sigslot::signal3<Call *, BaseSession *, Session::Error>
+  sigslot::signal3<Call *, Session *, Session::Error>
       SignalSessionError;
   sigslot::signal3<Call *, Session *, const std::string &>
       SignalReceivedTerminateReason;
@@ -101,7 +108,7 @@
   // Empty nick on NamedSource means "unknown".
   // Ssrc of 0 on NamedSource means "no current speaker".
   sigslot::signal3<Call *,
-                   BaseSession *,
+                   Session *,
                    const NamedSource&> SignalSpeakerMonitor;
   sigslot::signal2<Call *, const std::vector<ConnectionInfo> &>
       SignalVideoConnectionMonitor;
@@ -130,12 +137,12 @@
   void OnConnectionMonitor(VideoChannel *channel,
                            const std::vector<ConnectionInfo> &infos);
   void OnMediaMonitor(VideoChannel *channel, const VideoMediaInfo& info);
-  VoiceChannel* GetVoiceChannel(BaseSession* session);
-  VideoChannel* GetVideoChannel(BaseSession* session);
-  void AddVoiceStream(BaseSession *session, uint32 voice_ssrc);
-  void AddVideoStream(BaseSession *session, uint32 video_ssrc);
-  void RemoveVoiceStream(BaseSession *session, uint32 voice_ssrc);
-  void RemoveVideoStream(BaseSession *session, uint32 video_ssrc);
+  VoiceChannel* GetVoiceChannel(Session* session);
+  VideoChannel* GetVideoChannel(Session* session);
+  void AddVoiceStream(Session *session, uint32 voice_ssrc);
+  void AddVideoStream(Session *session, uint32 video_ssrc);
+  void RemoveVoiceStream(Session *session, uint32 voice_ssrc);
+  void RemoveVideoStream(Session *session, uint32 video_ssrc);
   void ContinuePlayDTMF();
 
   uint32 id_;
@@ -148,6 +155,7 @@
   VideoRenderer* local_renderer_;
   bool video_;
   bool muted_;
+  bool video_muted_;
   bool send_to_voicemail_;
 
   // DTMF tones have to be queued up so that we don't flood the call.  We
diff --git a/talk/session/phone/carbonvideorenderer.cc b/talk/session/phone/carbonvideorenderer.cc
index 9b9a01c..f34fce5 100644
--- a/talk/session/phone/carbonvideorenderer.cc
+++ b/talk/session/phone/carbonvideorenderer.cc
@@ -29,6 +29,7 @@
 
 #include "talk/base/logging.h"
 #include "talk/session/phone/videocommon.h"
+#include "talk/session/phone/videoframe.h"
 
 namespace cricket {
 
diff --git a/talk/session/phone/carbonvideorenderer.h b/talk/session/phone/carbonvideorenderer.h
index 8865349..20cf328 100644
--- a/talk/session/phone/carbonvideorenderer.h
+++ b/talk/session/phone/carbonvideorenderer.h
@@ -33,7 +33,7 @@
 
 #include "talk/base/criticalsection.h"
 #include "talk/base/scoped_ptr.h"
-#include "talk/session/phone/mediachannel.h"
+#include "talk/session/phone/videorenderer.h"
 
 namespace cricket {
 
diff --git a/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index f0b8e79..32ff5c1 100644
--- a/talk/session/phone/channel.cc
+++ b/talk/session/phone/channel.cc
@@ -72,7 +72,8 @@
       packet->length() <= kMaxRtpPacketLen);
 }
 
-BaseChannel::BaseChannel(talk_base::Thread* thread, MediaEngine* media_engine,
+BaseChannel::BaseChannel(talk_base::Thread* thread,
+                         MediaEngineInterface* media_engine,
                          MediaChannel* media_channel, BaseSession* session,
                          const std::string& content_name,
                          TransportChannel* transport_channel)
@@ -618,7 +619,7 @@
 }
 
 VoiceChannel::VoiceChannel(talk_base::Thread* thread,
-                           MediaEngine* media_engine,
+                           MediaEngineInterface* media_engine,
                            VoiceMediaChannel* media_channel,
                            BaseSession* session,
                            const std::string& content_name,
@@ -685,6 +686,12 @@
   return data.result;
 }
 
+bool VoiceChannel::SetOutputScaling(uint32 ssrc, double left, double right) {
+  ScaleVolumeMessageData data(ssrc, left, right);
+  Send(MSG_SCALEVOLUME, &data);
+  return data.result;
+}
+
 void VoiceChannel::StartMediaMonitor(int cms) {
   media_monitor_.reset(new VoiceMediaMonitor(media_channel(), worker_thread(),
       talk_base::Thread::Current()));
@@ -886,6 +893,10 @@
   return media_channel()->PressDTMF(digit, playout);
 }
 
+bool VoiceChannel::SetOutputScaling_w(uint32 ssrc, double left, double right) {
+  return media_channel()->SetOutputScaling(ssrc, left, right);
+}
+
 void VoiceChannel::OnMessage(talk_base::Message *pmsg) {
   switch (pmsg->message_id) {
     case MSG_ADDSTREAM: {
@@ -913,6 +924,12 @@
       data->result = PressDTMF_w(data->digit, data->playout);
       break;
     }
+    case MSG_SCALEVOLUME: {
+      ScaleVolumeMessageData* data =
+          static_cast<ScaleVolumeMessageData*>(pmsg->pdata);
+      data->result = SetOutputScaling_w(data->ssrc, data->left, data->right);
+      break;
+    }
     case MSG_CHANNEL_ERROR: {
       VoiceChannelErrorMessageData* data =
           static_cast<VoiceChannelErrorMessageData*>(pmsg->pdata);
@@ -974,7 +991,7 @@
 }
 
 VideoChannel::VideoChannel(talk_base::Thread* thread,
-                           MediaEngine* media_engine,
+                           MediaEngineInterface* media_engine,
                            VideoMediaChannel* media_channel,
                            BaseSession* session,
                            const std::string& content_name,
diff --git a/talk/session/phone/channel.h b/talk/session/phone/channel.h
index baf07a9..157999d 100644
--- a/talk/session/phone/channel.h
+++ b/talk/session/phone/channel.h
@@ -38,8 +38,8 @@
 #include "talk/p2p/client/socketmonitor.h"
 #include "talk/p2p/base/session.h"
 #include "talk/session/phone/audiomonitor.h"
-#include "talk/session/phone/mediaengine.h"
 #include "talk/session/phone/mediachannel.h"
+#include "talk/session/phone/mediaengine.h"
 #include "talk/session/phone/mediamonitor.h"
 #include "talk/session/phone/rtcpmuxfilter.h"
 #include "talk/session/phone/srtpfilter.h"
@@ -71,7 +71,8 @@
   MSG_RTCPPACKET = 23,
   MSG_CHANNEL_ERROR = 24,
   MSG_ENABLECPUADAPTATION = 25,
-  MSG_DISABLECPUADAPTATION = 26
+  MSG_DISABLECPUADAPTATION = 26,
+  MSG_SCALEVOLUME = 27
 };
 
 // BaseChannel contains logic common to voice and video, including
@@ -81,7 +82,7 @@
     : public talk_base::MessageHandler, public sigslot::has_slots<>,
       public MediaChannel::NetworkInterface {
  public:
-  BaseChannel(talk_base::Thread* thread, MediaEngine* media_engine,
+  BaseChannel(talk_base::Thread* thread, MediaEngineInterface* media_engine,
               MediaChannel* channel, BaseSession* session,
               const std::string& content_name,
               TransportChannel* transport_channel);
@@ -158,7 +159,7 @@
   }
 
  protected:
-  MediaEngine* media_engine() const { return media_engine_; }
+  MediaEngineInterface* media_engine() const { return media_engine_; }
   virtual MediaChannel* media_channel() const { return media_channel_; }
   void set_rtcp_transport_channel(TransportChannel* transport);
   bool writable() const { return writable_; }
@@ -262,7 +263,7 @@
   talk_base::CriticalSection signal_recv_packet_cs_;
 
   talk_base::Thread *worker_thread_;
-  MediaEngine *media_engine_;
+  MediaEngineInterface *media_engine_;
   BaseSession *session_;
   MediaChannel *media_channel_;
 
@@ -283,7 +284,7 @@
 // and input/output level monitoring.
 class VoiceChannel : public BaseChannel {
  public:
-  VoiceChannel(talk_base::Thread *thread, MediaEngine *media_engine,
+  VoiceChannel(talk_base::Thread *thread, MediaEngineInterface *media_engine,
                VoiceMediaChannel *channel, BaseSession *session,
                const std::string& content_name, bool rtcp);
   ~VoiceChannel();
@@ -305,6 +306,7 @@
 
   bool PlayRingbackTone(uint32 ssrc, bool play, bool loop);
   bool PressDTMF(int digit, bool playout);
+  bool SetOutputScaling(uint32 ssrc, double left, double right);
 
   // Monitoring functions
   sigslot::signal2<VoiceChannel*, const std::vector<ConnectionInfo> &>
@@ -361,6 +363,18 @@
     bool playout;
     bool result;
   };
+  struct ScaleVolumeMessageData : public talk_base::MessageData {
+    ScaleVolumeMessageData(uint32 s, double l, double r)
+        : ssrc(s),
+          left(l),
+          right(r),
+          result(false) {
+    }
+    uint32 ssrc;
+    double left;
+    double right;
+    bool result;
+  };
 
   // overrides from BaseChannel
   virtual void OnChannelRead(TransportChannel* channel,
@@ -380,6 +394,7 @@
   bool PlayRingbackTone_w(uint32 ssrc, bool play, bool loop);
   void HandleEarlyMediaTimeout();
   bool PressDTMF_w(int digit, bool playout);
+  bool SetOutputScaling_w(uint32 ssrc, double left, double right);
 
   virtual void OnMessage(talk_base::Message *pmsg);
   virtual void OnConnectionMonitorUpdate(
@@ -400,7 +415,7 @@
 // VideoChannel is a specialization for video.
 class VideoChannel : public BaseChannel {
  public:
-  VideoChannel(talk_base::Thread *thread, MediaEngine *media_engine,
+  VideoChannel(talk_base::Thread *thread, MediaEngineInterface *media_engine,
                VideoMediaChannel *channel, BaseSession *session,
                const std::string& content_name, bool rtcp,
                VoiceChannel *voice_channel);
diff --git a/talk/session/phone/channelmanager.cc b/talk/session/phone/channelmanager.cc
index 6ff76ce..97419cb 100644
--- a/talk/session/phone/channelmanager.cc
+++ b/talk/session/phone/channelmanager.cc
@@ -37,7 +37,6 @@
 #include "talk/base/logging.h"
 #include "talk/base/sigslotrepeater.h"
 #include "talk/base/stringencode.h"
-#include "talk/session/phone/mediaengine.h"
 #include "talk/session/phone/soundclip.h"
 
 namespace cricket {
@@ -132,21 +131,21 @@
 };
 
 ChannelManager::ChannelManager(talk_base::Thread* worker_thread)
-    : media_engine_(MediaEngine::Create()),
+    : media_engine_(MediaEngineFactory::Create()),
       device_manager_(new DeviceManager()),
       initialized_(false),
       main_thread_(talk_base::Thread::Current()),
       worker_thread_(worker_thread),
       audio_in_device_(DeviceManager::kDefaultDeviceName),
       audio_out_device_(DeviceManager::kDefaultDeviceName),
-      audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS),
+      audio_options_(MediaEngineInterface::DEFAULT_AUDIO_OPTIONS),
       local_renderer_(NULL),
       capturing_(false),
       monitoring_(false) {
   Construct();
 }
 
-ChannelManager::ChannelManager(MediaEngine* me, DeviceManager* dm,
+ChannelManager::ChannelManager(MediaEngineInterface* me, DeviceManager* dm,
                                talk_base::Thread* worker_thread)
     : media_engine_(me),
       device_manager_(dm),
@@ -155,7 +154,7 @@
       worker_thread_(worker_thread),
       audio_in_device_(DeviceManager::kDefaultDeviceName),
       audio_out_device_(DeviceManager::kDefaultDeviceName),
-      audio_options_(MediaEngine::DEFAULT_AUDIO_OPTIONS),
+      audio_options_(MediaEngineInterface::DEFAULT_AUDIO_OPTIONS),
       local_renderer_(NULL),
       capturing_(false),
       monitoring_(false) {
@@ -653,7 +652,10 @@
   return true;
 }
 
-void ChannelManager::OnVideoCaptureResult(CaptureResult result) {
+void ChannelManager::OnVideoCaptureResult(VideoCapturer* capturer,
+                                          CaptureResult result) {
+  // TODO: Check capturer and signal failure only for camera video, not
+  // screencast.
   capturing_ = result == CR_SUCCESS;
   main_thread_->Post(this, MSG_CAMERASTARTED,
                      new talk_base::TypedMessageData<CaptureResult>(result));
diff --git a/talk/session/phone/channelmanager.h b/talk/session/phone/channelmanager.h
index 3b34e62..cada408 100644
--- a/talk/session/phone/channelmanager.h
+++ b/talk/session/phone/channelmanager.h
@@ -37,7 +37,6 @@
 #include "talk/p2p/base/session.h"
 #include "talk/session/phone/voicechannel.h"
 #include "talk/session/phone/mediaengine.h"
-#include "talk/session/phone/devicemanager.h"
 
 namespace cricket {
 
@@ -59,7 +58,8 @@
   explicit ChannelManager(talk_base::Thread* worker);
   // For testing purposes. Allows the media engine and dev manager to be mocks.
   // The ChannelManager takes ownership of these objects.
-  ChannelManager(MediaEngine* me, DeviceManager* dm, talk_base::Thread* worker);
+  ChannelManager(MediaEngineInterface* me, DeviceManager* dm,
+                 talk_base::Thread* worker);
   ~ChannelManager();
 
   // Accessors for the worker thread, allowing it to be set after construction,
@@ -175,11 +175,12 @@
   CaptureResult SetVideoCapture_w(bool capture);
   void SetMediaLogging(bool video, int level, const char* filter);
   void SetMediaLogging_w(bool video, int level, const char* filter);
-  void OnVideoCaptureResult(CaptureResult result);
+  void OnVideoCaptureResult(VideoCapturer* capturer,
+                            CaptureResult result);
   void OnMessage(talk_base::Message *message);
 
   talk_base::CriticalSection crit_;
-  talk_base::scoped_ptr<MediaEngine> media_engine_;
+  talk_base::scoped_ptr<MediaEngineInterface> media_engine_;
   talk_base::scoped_ptr<DeviceManager> device_manager_;
   bool initialized_;
   talk_base::Thread* main_thread_;
diff --git a/talk/session/phone/devicemanager.cc b/talk/session/phone/devicemanager.cc
index 98aaf42..376b0e9 100644
--- a/talk/session/phone/devicemanager.cc
+++ b/talk/session/phone/devicemanager.cc
@@ -60,11 +60,12 @@
 
 #include "talk/base/logging.h"
 #include "talk/base/stringutils.h"
-#include "talk/session/phone/mediaengine.h"
+#include "talk/base/thread.h"
+#include "talk/session/phone/mediacommon.h"
 
 namespace cricket {
 // Initialize to empty string.
-const std::string DeviceManager::kDefaultDeviceName;
+const char DeviceManager::kDefaultDeviceName[] = "";
 
 #ifdef WIN32
 class DeviceWatcher : public talk_base::Win32Window {
@@ -196,15 +197,15 @@
 
 int DeviceManager::GetCapabilities() {
   std::vector<Device> devices;
-  int caps = MediaEngine::VIDEO_RECV;
+  int caps = VIDEO_RECV;
   if (GetAudioInputDevices(&devices) && !devices.empty()) {
-    caps |= MediaEngine::AUDIO_SEND;
+    caps |= AUDIO_SEND;
   }
   if (GetAudioOutputDevices(&devices) && !devices.empty()) {
-    caps |= MediaEngine::AUDIO_RECV;
+    caps |= AUDIO_RECV;
   }
   if (GetVideoCaptureDevices(&devices) && !devices.empty()) {
-    caps |= MediaEngine::VIDEO_SEND;
+    caps |= VIDEO_SEND;
   }
   return caps;
 }
diff --git a/talk/session/phone/devicemanager.h b/talk/session/phone/devicemanager.h
index cd41f6f..04e494e 100644
--- a/talk/session/phone/devicemanager.h
+++ b/talk/session/phone/devicemanager.h
@@ -84,7 +84,7 @@
 
   void OnDevicesChange() { SignalDevicesChange(); }
 
-  static const std::string kDefaultDeviceName;
+  static const char kDefaultDeviceName[];
 
  protected:
   virtual bool GetAudioDevice(bool is_input, const std::string& name,
diff --git a/talk/session/phone/filemediaengine.cc b/talk/session/phone/filemediaengine.cc
index 3e7e517..f7a0461 100644
--- a/talk/session/phone/filemediaengine.cc
+++ b/talk/session/phone/filemediaengine.cc
@@ -43,16 +43,16 @@
 int FileMediaEngine::GetCapabilities() {
   int capabilities = 0;
   if (!voice_input_filename_.empty()) {
-    capabilities |= MediaEngine::AUDIO_SEND;
+    capabilities |= AUDIO_SEND;
   }
   if (!voice_output_filename_.empty()) {
-    capabilities |= MediaEngine::AUDIO_RECV;
+    capabilities |= AUDIO_RECV;
   }
   if (!video_input_filename_.empty()) {
-    capabilities |= MediaEngine::VIDEO_SEND;
+    capabilities |= VIDEO_SEND;
   }
   if (!video_output_filename_.empty()) {
-    capabilities |= MediaEngine::VIDEO_RECV;
+    capabilities |= VIDEO_RECV;
   }
   return capabilities;
 }
diff --git a/talk/session/phone/filemediaengine.h b/talk/session/phone/filemediaengine.h
index 62e28f9..18f62ca 100644
--- a/talk/session/phone/filemediaengine.h
+++ b/talk/session/phone/filemediaengine.h
@@ -48,7 +48,7 @@
 // stream. Depending on the parameters of the constructor, FileMediaEngine can
 // act as file voice engine, file video engine, or both. Currently, we use
 // only the RTP dump packets. TODO: Enable RTCP packets.
-class FileMediaEngine : public MediaEngine {
+class FileMediaEngine : public MediaEngineInterface {
  public:
   FileMediaEngine() {}
   virtual ~FileMediaEngine() {}
@@ -149,6 +149,12 @@
   virtual bool RemoveStream(uint32 ssrc) { return true; }
   virtual bool GetActiveStreams(AudioInfo::StreamList* actives) { return true; }
   virtual int GetOutputLevel() { return 0; }
+  virtual bool SetOutputScaling(uint32 ssrc, double left, double right) {
+    return false;
+  }
+  virtual bool GetOutputScaling(uint32 ssrc, double* left, double* right) {
+    return false;
+  }
   virtual bool SetRingbackTone(const char* buf, int len) { return true; }
   virtual bool PlayRingbackTone(uint32 ssrc, bool play, bool loop) {
     return true;
diff --git a/talk/session/phone/gdivideorenderer.cc b/talk/session/phone/gdivideorenderer.cc
index 6afb764..0ee75ab 100644
--- a/talk/session/phone/gdivideorenderer.cc
+++ b/talk/session/phone/gdivideorenderer.cc
@@ -33,6 +33,7 @@
 #include "talk/base/thread.h"
 #include "talk/base/win32window.h"
 #include "talk/session/phone/videocommon.h"
+#include "talk/session/phone/videoframe.h"
 
 namespace cricket {
 
diff --git a/talk/session/phone/gdivideorenderer.h b/talk/session/phone/gdivideorenderer.h
index d2e363e..7be1cca 100644
--- a/talk/session/phone/gdivideorenderer.h
+++ b/talk/session/phone/gdivideorenderer.h
@@ -30,7 +30,8 @@
 #define TALK_SESSION_PHONE_GDIVIDEORENDERER_H_
 
 #ifdef WIN32
-#include "talk/session/phone/mediachannel.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/session/phone/videorenderer.h"
 
 namespace cricket {
 
diff --git a/talk/session/phone/gtkvideorenderer.cc b/talk/session/phone/gtkvideorenderer.cc
index 059c688..9b17c78 100644
--- a/talk/session/phone/gtkvideorenderer.cc
+++ b/talk/session/phone/gtkvideorenderer.cc
@@ -30,9 +30,21 @@
 #include <gtk/gtk.h>
 
 #include "talk/session/phone/videocommon.h"
+#include "talk/session/phone/videoframe.h"
 
 namespace cricket {
 
+class ScopedGdkLock {
+ public:
+  ScopedGdkLock() {
+    gdk_threads_enter();
+  }
+
+  ~ScopedGdkLock() {
+    gdk_threads_leave();
+  }
+};
+
 GtkVideoRenderer::GtkVideoRenderer(int x, int y)
     : window_(NULL),
       draw_area_(NULL),
@@ -44,28 +56,25 @@
 
 GtkVideoRenderer::~GtkVideoRenderer() {
   if (window_) {
-    gdk_threads_enter();
+    ScopedGdkLock lock;
     gtk_widget_destroy(window_);
     // Run the Gtk main loop to tear down the window.
     Pump();
-    gdk_threads_leave();
   }
   // Don't need to destroy draw_area_ because it is not top-level, so it is
   // implicitly destroyed by the above.
 }
 
 bool GtkVideoRenderer::SetSize(int width, int height, int reserved) {
-  gdk_threads_enter();
+  ScopedGdkLock lock;
 
   // For the first frame, initialize the GTK window
-  if (!window_ && !Initialize(width, height)) {
-    gdk_threads_leave();
+  if ((!window_ && !Initialize(width, height)) || IsClosed()) {
     return false;
   }
 
   image_.reset(new uint8[width * height * 4]);
-  gtk_window_resize(GTK_WINDOW(window_), width, height);
-  gdk_threads_leave();
+  gtk_widget_set_size_request(draw_area_, width, height);
   return true;
 }
 
@@ -74,18 +83,18 @@
     return false;
   }
 
-  if (!GTK_IS_WINDOW(window_) || !GTK_IS_DRAWING_AREA(draw_area_)) {
-    // window was closed
-    return false;
-  }
-
   // convert I420 frame to ABGR format, which is accepted by GTK
   frame->ConvertToRgbBuffer(cricket::FOURCC_ABGR,
                             image_.get(),
                             frame->GetWidth() * frame->GetHeight() * 4,
                             frame->GetWidth() * 4);
 
-  gdk_threads_enter();
+  ScopedGdkLock lock;
+
+  if (IsClosed()) {
+    return false;
+  }
+
   // draw the ABGR image
   gdk_draw_rgb_32_image(draw_area_->window,
                         draw_area_->style->fg_gc[GTK_STATE_NORMAL],
@@ -99,7 +108,6 @@
 
   // Run the Gtk main loop to refresh the window.
   Pump();
-  gdk_threads_leave();
   return true;
 }
 
@@ -113,6 +121,7 @@
 
   gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER);
   gtk_window_set_title(GTK_WINDOW(window_), "Video Renderer");
+  gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
   gtk_widget_set_size_request(draw_area_, width, height);
   gtk_container_add(GTK_CONTAINER(window_), draw_area_);
   gtk_widget_show_all(window_);
@@ -128,4 +137,17 @@
   }
 }
 
+bool GtkVideoRenderer::IsClosed() const {
+  if (!window_) {
+    // Not initialized yet, so hasn't been closed.
+    return false;
+  }
+
+  if (!GTK_IS_WINDOW(window_) || !GTK_IS_DRAWING_AREA(draw_area_)) {
+    return true;
+  }
+
+  return false;
+}
+
 }  // namespace cricket
diff --git a/talk/session/phone/gtkvideorenderer.h b/talk/session/phone/gtkvideorenderer.h
index 6bdc606..8c36a79 100644
--- a/talk/session/phone/gtkvideorenderer.h
+++ b/talk/session/phone/gtkvideorenderer.h
@@ -29,8 +29,9 @@
 #ifndef TALK_SESSION_PHONE_GTKVIDEORENDERER_H_
 #define TALK_SESSION_PHONE_GTKVIDEORENDERER_H_
 
+#include "talk/base/basictypes.h"
 #include "talk/base/scoped_ptr.h"
-#include "talk/session/phone/mediachannel.h"
+#include "talk/session/phone/videorenderer.h"
 
 typedef struct _GtkWidget GtkWidget;  // forward declaration, defined in gtk.h
 
@@ -52,6 +53,8 @@
   bool Initialize(int width, int height);
   // Pump the Gtk event loop until there are no events left.
   void Pump();
+  // Check if the window has been closed.
+  bool IsClosed() const;
 
   talk_base::scoped_array<uint8> image_;
   GtkWidget* window_;
diff --git a/talk/session/phone/linphonemediaengine.cc b/talk/session/phone/linphonemediaengine.cc
index 161f05b..3188efa 100644
--- a/talk/session/phone/linphonemediaengine.cc
+++ b/talk/session/phone/linphonemediaengine.cc
@@ -96,8 +96,8 @@
 
 int LinphoneMediaEngine::GetCapabilities() {
   int capabilities = 0;
-  capabilities |= MediaEngine::AUDIO_SEND;
-  capabilities |= MediaEngine::AUDIO_RECV;
+  capabilities |= AUDIO_SEND;
+  capabilities |= AUDIO_RECV;
   return capabilities;
 }
 
diff --git a/talk/session/phone/linphonemediaengine.h b/talk/session/phone/linphonemediaengine.h
index 81df92b..883580c 100644
--- a/talk/session/phone/linphonemediaengine.h
+++ b/talk/session/phone/linphonemediaengine.h
@@ -48,7 +48,7 @@
 
 namespace cricket {
 
-class LinphoneMediaEngine : public MediaEngine {
+class LinphoneMediaEngine : public MediaEngineInterface {
  public:
   LinphoneMediaEngine(const std::string& ringWav,  const std::string& callWav);
   virtual ~LinphoneMediaEngine() {}
@@ -128,6 +128,12 @@
   virtual bool RemoveStream(uint32 ssrc) { return true; }
   virtual bool GetActiveStreams(AudioInfo::StreamList* actives) { return true; }
   virtual int GetOutputLevel() { return 0; }
+  virtual bool SetOutputScaling(uint32 ssrc, double left, double right) {
+    return false;
+  }
+  virtual bool GetOutputScaling(uint32 ssrc, double* left, double* right) {
+    return false;
+  }
   virtual void SetRingbackTone(const char* buf, int len) {}
   virtual bool PlayRingbackTone(bool play, bool loop) { return true; }
   virtual bool PressDTMF(int event, bool playout) { return true; }
diff --git a/talk/session/phone/mediachannel.h b/talk/session/phone/mediachannel.h
index 7c22b70..aa32671 100644
--- a/talk/session/phone/mediachannel.h
+++ b/talk/session/phone/mediachannel.h
@@ -42,15 +42,29 @@
 class Buffer;
 }
 
-namespace flute {
-class MagicCamVideoRenderer;
-}
-
 namespace cricket {
 
+class VideoRenderer;
+
 const int kMinRtpHeaderExtensionId = 1;
 const int kMaxRtpHeaderExtensionId = 255;
 
+// A class for playing out soundclips.
+class SoundclipMedia {
+ public:
+  enum SoundclipFlags {
+    SF_LOOP = 1,
+  };
+
+  virtual ~SoundclipMedia() {}
+
+  // Plays a sound out to the speakers with the given audio stream. The stream
+  // must be 16-bit little-endian 16 kHz PCM. If a stream is already playing
+  // on this SoundclipMedia, it is stopped. If clip is NULL, nothing is played.
+  // Returns whether it was successful.
+  virtual bool PlaySound(const char *clip, int len, int flags) = 0;
+};
+
 struct RtpHeaderExtension {
   RtpHeaderExtension(const std::string& u, int i) : uri(u), id(i) {}
   std::string uri;
@@ -253,8 +267,12 @@
   virtual bool RemoveStream(uint32 ssrc) = 0;
   // Gets current energy levels for all incoming streams.
   virtual bool GetActiveStreams(AudioInfo::StreamList* actives) = 0;
-  // Get the current energy level for the outgoing stream.
+  // Get the current energy level of the stream sent to the speaker.
   virtual int GetOutputLevel() = 0;
+  // Set left and right scale for speaker output volume of the specified ssrc.
+  virtual bool SetOutputScaling(uint32 ssrc, double left, double right) = 0;
+  // Get left and right scale for speaker output volume of the specified ssrc.
+  virtual bool GetOutputScaling(uint32 ssrc, double* left, double* right) = 0;
   // Specifies a ringback tone to be played during call setup.
   virtual bool SetRingbackTone(const char *buf, int len) = 0;
   // Plays or stops the aforementioned ringback tone
@@ -275,179 +293,6 @@
   sigslot::signal2<uint32, VoiceMediaChannel::Error> SignalMediaError;
 };
 
-// Represents a YUV420 (a.k.a. I420) video frame.
-class VideoFrame {
-  friend class flute::MagicCamVideoRenderer;
-
- public:
-  VideoFrame() : rendered_(false) {}
-
-  virtual ~VideoFrame() {}
-
-  virtual size_t GetWidth() const = 0;
-  virtual size_t GetHeight() const = 0;
-  virtual const uint8 *GetYPlane() const = 0;
-  virtual const uint8 *GetUPlane() const = 0;
-  virtual const uint8 *GetVPlane() const = 0;
-  virtual uint8 *GetYPlane() = 0;
-  virtual uint8 *GetUPlane() = 0;
-  virtual uint8 *GetVPlane() = 0;
-  virtual int32 GetYPitch() const = 0;
-  virtual int32 GetUPitch() const = 0;
-  virtual int32 GetVPitch() const = 0;
-
-  // For retrieving the aspect ratio of each pixel. Usually this is 1x1, but
-  // the aspect_ratio_idc parameter of H.264 can specify non-square pixels.
-  virtual size_t GetPixelWidth() const = 0;
-  virtual size_t GetPixelHeight() const = 0;
-
-  // TODO: Add a fourcc format here and probably combine VideoFrame
-  // with CapturedFrame.
-  virtual int64 GetElapsedTime() const = 0;
-  virtual int64 GetTimeStamp() const = 0;
-  virtual void SetElapsedTime(int64 elapsed_time) = 0;
-  virtual void SetTimeStamp(int64 time_stamp) = 0;
-
-  // Make a copy of the frame. The frame buffer itself may not be copied,
-  // in which case both the current and new VideoFrame will share a single
-  // reference-counted frame buffer.
-  virtual VideoFrame *Copy() const = 0;
-
-  // Writes the frame into the given frame buffer, provided that it is of
-  // sufficient size. Returns the frame's actual size, regardless of whether
-  // it was written or not (like snprintf). If there is insufficient space,
-  // 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 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.
-  virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8 *buffer,
-                                    size_t size, size_t pitch_rgb) const = 0;
-
-  // Writes the frame into the given planes, stretched to the given width and
-  // height. The parameter "interpolate" controls whether to interpolate or just
-  // take the nearest-point. The parameter "crop" controls whether to crop this
-  // frame to the aspect ratio of the given dimensions before stretching.
-  virtual void StretchToPlanes(uint8 *y, uint8 *u, uint8 *v,
-                               int32 pitchY, int32 pitchU, int32 pitchV,
-                               size_t width, size_t height,
-                               bool interpolate, bool crop) const = 0;
-
-  // Writes the frame into the given frame buffer, stretched to the given width
-  // and height, provided that it is of sufficient size. Returns the frame's
-  // actual size, regardless of whether it was written or not (like snprintf).
-  // If there is insufficient space, nothing is written. The parameter
-  // "interpolate" controls whether to interpolate or just take the
-  // nearest-point. The parameter "crop" controls whether to crop this frame to
-  // the aspect ratio of the given dimensions before stretching.
-  virtual size_t StretchToBuffer(size_t w, size_t h, uint8 *buffer, size_t size,
-                                 bool interpolate, bool crop) const = 0;
-
-  // Writes the frame into the target VideoFrame, stretched to the size of that
-  // frame. The parameter "interpolate" controls whether to interpolate or just
-  // take the nearest-point. The parameter "crop" controls whether to crop this
-  // frame to the aspect ratio of the target frame before stretching.
-  virtual void StretchToFrame(VideoFrame *target, bool interpolate,
-                              bool crop) const = 0;
-
-  // Stretches the frame to the given size, creating a new VideoFrame object to
-  // hold it. The parameter "interpolate" controls whether to interpolate or
-  // just take the nearest-point. The parameter "crop" controls whether to crop
-  // this frame to the aspect ratio of the given dimensions before stretching.
-  virtual VideoFrame *Stretch(size_t w, size_t h, bool interpolate,
-                              bool crop) const = 0;
-
-  // Size of an I420 image of given dimensions when stored as a frame buffer.
-  static size_t SizeOf(size_t w, size_t h) {
-    return w * h + ((w + 1) / 2) * ((h + 1) / 2) * 2;
-  }
-
- protected:
-  // The frame needs to be rendered to magiccam only once.
-  // TODO: Remove this flag once magiccam rendering is fully replaced
-  // by client3d rendering.
-  mutable bool rendered_;
-};
-
-// Simple subclass for use in mocks.
-class NullVideoFrame : public VideoFrame {
- public:
-  virtual size_t GetWidth() const { return 0; }
-  virtual size_t GetHeight() const { return 0; }
-  virtual const uint8 *GetYPlane() const { return NULL; }
-  virtual const uint8 *GetUPlane() const { return NULL; }
-  virtual const uint8 *GetVPlane() const { return NULL; }
-  virtual uint8 *GetYPlane() { return NULL; }
-  virtual uint8 *GetUPlane() { return NULL; }
-  virtual uint8 *GetVPlane() { return NULL; }
-  virtual int32 GetYPitch() const { return 0; }
-  virtual int32 GetUPitch() const { return 0; }
-  virtual int32 GetVPitch() const { return 0; }
-
-  virtual size_t GetPixelWidth() const { return 1; }
-  virtual size_t GetPixelHeight() const { return 1; }
-  virtual int64 GetElapsedTime() const { return 0; }
-  virtual int64 GetTimeStamp() const { return 0; }
-  virtual void SetElapsedTime(int64 elapsed_time) {}
-  virtual void SetTimeStamp(int64 time_stamp) {}
-
-  virtual VideoFrame *Copy() const {
-    return NULL;
-  }
-
-  virtual size_t CopyToBuffer(uint8 *buffer, size_t size) const {
-    return 0;
-  }
-
-  virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8 *buffer,
-                                    size_t size, size_t pitch_rgb) const {
-    return 0;
-  }
-
-  virtual void StretchToPlanes(uint8 *y, uint8 *u, uint8 *v,
-                               int32 pitchY, int32 pitchU, int32 pitchV,
-                               size_t width, size_t height,
-                               bool interpolate, bool crop) const {
-  }
-
-  virtual size_t StretchToBuffer(size_t w, size_t h, uint8 *buffer, size_t size,
-                                 bool interpolate, bool crop) const {
-    return 0;
-  }
-
-  virtual void StretchToFrame(VideoFrame *target, bool interpolate,
-                              bool crop) const {
-  }
-
-  virtual VideoFrame *Stretch(size_t w, size_t h, bool interpolate,
-                              bool crop) const {
-    return NULL;
-  }
-};
-
-// Abstract interface for rendering VideoFrames.
-class VideoRenderer {
- public:
-  virtual ~VideoRenderer() {}
-  // Called when the video has changed size.
-  virtual bool SetSize(int width, int height, int reserved) = 0;
-  // Called when a new frame is available for display.
-  virtual bool RenderFrame(const VideoFrame *frame) = 0;
-};
-
-// Simple implementation for use in tests.
-class NullVideoRenderer : public VideoRenderer {
-  virtual bool SetSize(int width, int height, int reserved) {
-    return true;
-  }
-  // Called when a new frame is available for display.
-  virtual bool RenderFrame(const VideoFrame *frame) {
-    return true;
-  }
-};
-
 class VideoMediaChannel : public MediaChannel {
  public:
   enum Error {
diff --git a/talk/session/phone/mediaengine.cc b/talk/session/phone/mediaengine.cc
index e6b663d..5190232 100644
--- a/talk/session/phone/mediaengine.cc
+++ b/talk/session/phone/mediaengine.cc
@@ -29,23 +29,19 @@
 
 #if defined(HAVE_LINPHONE)
 #include "talk/session/phone/linphonemediaengine.h"
-#elif defined(HAVE_WEBRTC)
-#include "talk/session/phone/webrtcvoiceengine.h"
-#include "talk/session/phone/webrtcvideoengine.h"
 #else
 #endif  // HAVE_LINPHONE
 
 namespace cricket {
-MediaEngine* MediaEngine::Create() {
+
+MediaEngineInterface* MediaEngineFactory::Create() {
 #if defined(HAVE_LINPHONE)
   return new LinphoneMediaEngine("", "");
-#elif defined(HAVE_WEBRTC)
-  return new CompositeMediaEngine<WebRtcVoiceEngine, WebRtcVideoEngine>();
 #elif defined(ANDROID)
   return AndroidMediaEngineFactory::Create();
 #else
   return new NullMediaEngine();
-#endif  // HAVE_LINPHONE HAVE_WEBRTC ANDROID
+#endif  // HAVE_LINPHONE ANDROID HAVE_LMI HAVE_GIPS HAVE_WEBRTC_VOICE/VIDEO
 }
 
 };  // namespace cricket
diff --git a/talk/session/phone/mediaengine.h b/talk/session/phone/mediaengine.h
index 4383408..9ea808d 100644
--- a/talk/session/phone/mediaengine.h
+++ b/talk/session/phone/mediaengine.h
@@ -39,53 +39,32 @@
 #include "talk/session/phone/codec.h"
 #include "talk/session/phone/devicemanager.h"
 #include "talk/session/phone/mediachannel.h"
+#include "talk/session/phone/mediacommon.h"
 #include "talk/session/phone/videocommon.h"
 
 namespace cricket {
 
-// A class for playing out soundclips.
-class SoundclipMedia {
+class VideoCapturer;
+
+// MediaEngineInterface is an abstraction of a media engine which can be
+// subclassed to support different media componentry backends.
+// It supports voice and video operations in the same class to facilitate
+// proper synchronization between both media types.
+class MediaEngineInterface {
  public:
-  enum SoundclipFlags {
-    SF_LOOP = 1,
-  };
-
-  virtual ~SoundclipMedia() {}
-
-  // Plays a sound out to the speakers with the given audio stream. The stream
-  // must be 16-bit little-endian 16 kHz PCM. If a stream is already playing
-  // on this SoundclipMedia, it is stopped. If clip is NULL, nothing is played.
-  // Returns whether it was successful.
-  virtual bool PlaySound(const char *clip, int len, int flags) = 0;
-};
-
-// MediaEngine is an abstraction of a media engine which can be subclassed
-// to support different media componentry backends. It supports voice and
-// video operations in the same class to facilitate proper synchronization
-// between both media types.
-class MediaEngine {
- public:
-  // TODO: Move this to a global location (also used in DeviceManager)
-  // Capabilities of the media engine.
-  enum Capabilities {
-    AUDIO_RECV = 1 << 0,
-    AUDIO_SEND = 1 << 1,
-    VIDEO_RECV = 1 << 2,
-    VIDEO_SEND = 1 << 3,
-  };
-
   // Bitmask flags for options that may be supported by the media engine
   // implementation
   enum AudioOptions {
     ECHO_CANCELLATION = 1 << 0,
     AUTO_GAIN_CONTROL = 1 << 1,
+    NOISE_SUPPRESSION = 1 << 2,
+    TYPING_DETECTION = 1 << 3,
     DEFAULT_AUDIO_OPTIONS = ECHO_CANCELLATION | AUTO_GAIN_CONTROL
   };
   enum VideoOptions {
   };
 
-  virtual ~MediaEngine() {}
-  static MediaEngine* Create();
+  virtual ~MediaEngineInterface() {}
 
   // Initialization
   // Starts the engine.
@@ -147,13 +126,21 @@
   virtual void SetVoiceLogging(int min_sev, const char* filter) = 0;
   virtual void SetVideoLogging(int min_sev, const char* filter) = 0;
 
-  sigslot::repeater1<CaptureResult> SignalVideoCaptureResult;
+  sigslot::repeater2<VideoCapturer*, CaptureResult>
+      SignalVideoCaptureResult;
 };
 
+
+class MediaEngineFactory {
+ public:
+  static MediaEngineInterface* Create();
+};
+
+
 // CompositeMediaEngine constructs a MediaEngine from separate
 // voice and video engine classes.
 template<class VOICE, class VIDEO>
-class CompositeMediaEngine : public MediaEngine {
+class CompositeMediaEngine : public MediaEngineInterface {
  public:
   CompositeMediaEngine() {}
   virtual ~CompositeMediaEngine() {}
@@ -260,12 +247,16 @@
   bool SetDevices(const Device* in_device, const Device* out_device) {
     return true;
   }
-  bool GetOutputVolume(int* level) { *level = 0; return true; }
+  bool GetOutputVolume(int* level) {
+    *level = 0;
+    return true;
+  }
   bool SetOutputVolume(int level) { return true; }
   int GetInputLevel() { return 0; }
   bool SetLocalMonitor(bool enable) { return true; }
   const std::vector<AudioCodec>& codecs() { return codecs_; }
   void SetLogging(int min_sev, const char* filter) {}
+
  private:
   std::vector<AudioCodec> codecs_;
 };
@@ -291,7 +282,7 @@
   CaptureResult SetCapture(bool capture) { return CR_SUCCESS;  }
   const std::vector<VideoCodec>& codecs() { return codecs_; }
   void SetLogging(int min_sev, const char* filter) {}
-  sigslot::signal1<CaptureResult> SignalCaptureResult;
+  sigslot::signal2<VideoCapturer*, CaptureResult> SignalCaptureResult;
  private:
   std::vector<VideoCodec> codecs_;
 };
diff --git a/talk/session/phone/mediamonitor.cc b/talk/session/phone/mediamonitor.cc
index 909b536..0160077 100644
--- a/talk/session/phone/mediamonitor.cc
+++ b/talk/session/phone/mediamonitor.cc
@@ -28,7 +28,6 @@
 #include "talk/base/common.h"
 #include "talk/session/phone/mediamonitor.h"
 #include "talk/session/phone/channelmanager.h"
-#include "talk/session/phone/mediaengine.h"
 
 namespace cricket {
 
diff --git a/talk/session/phone/mediasession.cc b/talk/session/phone/mediasession.cc
new file mode 100644
index 0000000..d70ab16
--- /dev/null
+++ b/talk/session/phone/mediasession.cc
@@ -0,0 +1,307 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/phone/mediasession.h"
+
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/session/phone/cryptoparams.h"
+#include "talk/session/phone/srtpfilter.h"
+#include "talk/xmpp/constants.h"
+
+namespace {
+const char kInline[] = "inline:";
+}
+
+namespace cricket {
+
+static bool CreateCryptoParams(int tag, const std::string& cipher,
+                               CryptoParams *out) {
+  std::string key;
+  key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
+
+  if (!talk_base::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
+    return false;
+  }
+  out->tag = tag;
+  out->cipher_suite = cipher;
+  out->key_params = kInline;
+  out->key_params += key;
+  return true;
+}
+
+static bool AddCryptoParams(const std::string& cipher_suite,
+                            CryptoParamsVec *out) {
+  int size = out->size();
+
+  out->resize(size + 1);
+  return CreateCryptoParams(size, cipher_suite, &out->at(size));
+}
+
+// For audio, HMAC 32 is prefered because of the low overhead.
+static bool GetSupportedAudioCryptos(CryptoParamsVec* cryptos) {
+#ifdef HAVE_SRTP
+  return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_32, cryptos) &&
+      AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
+#else
+  return false;
+#endif
+}
+
+static bool GetSupportedVideoCryptos(CryptoParamsVec* cryptos) {
+#ifdef HAVE_SRTP
+  return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
+#else
+  return false;
+#endif
+}
+
+// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
+// tolerated because it is low overhead. Pick the crypto in the list
+// that is supported.
+static bool SelectCrypto(const MediaContentDescription* offer,
+                         CryptoParams *crypto) {
+  bool audio = offer->type() == MEDIA_TYPE_AUDIO;
+  const CryptoParamsVec& cryptos = offer->cryptos();
+
+  for (CryptoParamsVec::const_iterator i = cryptos.begin();
+       i != cryptos.end(); ++i) {
+    if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
+        (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio)) {
+      return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
+    }
+  }
+  return false;
+}
+
+MediaSessionDescriptionFactory::MediaSessionDescriptionFactory()
+    : secure_(SEC_DISABLED) {
+}
+
+MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
+    ChannelManager* channel_manager)
+    : secure_(SEC_DISABLED) {
+  channel_manager->GetSupportedAudioCodecs(&audio_codecs_);
+  channel_manager->GetSupportedVideoCodecs(&video_codecs_);
+}
+
+SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
+    const MediaSessionOptions& options) {
+  SessionDescription* offer = new SessionDescription();
+
+  if (true) {  // TODO: Allow audio to be optional
+    AudioContentDescription* audio = new AudioContentDescription();
+    for (AudioCodecs::const_iterator codec = audio_codecs_.begin();
+         codec != audio_codecs_.end(); ++codec) {
+      audio->AddCodec(*codec);
+    }
+    audio->SortCodecs();
+    audio->set_ssrc(talk_base::CreateRandomNonZeroId());
+    audio->set_rtcp_mux(true);
+    audio->set_lang(lang_);
+
+    if (secure() != SEC_DISABLED) {
+      CryptoParamsVec audio_cryptos;
+      if (GetSupportedAudioCryptos(&audio_cryptos)) {
+        for (CryptoParamsVec::const_iterator crypto = audio_cryptos.begin();
+             crypto != audio_cryptos.end(); ++crypto) {
+          audio->AddCrypto(*crypto);
+        }
+      }
+      if (secure() == SEC_REQUIRED) {
+        if (audio->cryptos().empty()) {
+          return NULL;  // Abort, crypto required but none found.
+        }
+        audio->set_crypto_required(true);
+      }
+    }
+
+    offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio);
+  }
+
+  // add video codecs, if this is a video call
+  if (options.is_video) {
+    VideoContentDescription* video = new VideoContentDescription();
+    for (VideoCodecs::const_iterator codec = video_codecs_.begin();
+         codec != video_codecs_.end(); ++codec) {
+      video->AddCodec(*codec);
+    }
+
+    video->SortCodecs();
+    video->set_ssrc(talk_base::CreateRandomNonZeroId());
+    video->set_bandwidth(options.video_bandwidth);
+    video->set_rtcp_mux(true);
+
+    if (secure() != SEC_DISABLED) {
+      CryptoParamsVec video_cryptos;
+      if (GetSupportedVideoCryptos(&video_cryptos)) {
+        for (CryptoParamsVec::const_iterator crypto = video_cryptos.begin();
+             crypto != video_cryptos.end(); ++crypto) {
+          video->AddCrypto(*crypto);
+        }
+      }
+      if (secure() == SEC_REQUIRED) {
+        if (video->cryptos().empty()) {
+          return NULL;  // Abort, crypto required but none found.
+        }
+        video->set_crypto_required(true);
+      }
+    }
+
+    offer->AddContent(CN_VIDEO, NS_JINGLE_RTP, video);
+  }
+
+  return offer;
+}
+
+SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
+    const SessionDescription* offer, const MediaSessionOptions& options) {
+  // The answer contains the intersection of the codecs in the offer with the
+  // codecs we support, ordered by our local preference. As indicated by
+  // XEP-0167, we retain the same payload ids from the offer in the answer.
+  SessionDescription* accept = new SessionDescription();
+
+  const ContentInfo* audio_content = GetFirstAudioContent(offer);
+  if (audio_content) {
+    const AudioContentDescription* audio_offer =
+        static_cast<const AudioContentDescription*>(audio_content->description);
+    AudioContentDescription* audio_accept = new AudioContentDescription();
+    for (AudioCodecs::const_iterator ours = audio_codecs_.begin();
+        ours != audio_codecs_.end(); ++ours) {
+      for (AudioCodecs::const_iterator theirs = audio_offer->codecs().begin();
+          theirs != audio_offer->codecs().end(); ++theirs) {
+        if (ours->Matches(*theirs)) {
+          AudioCodec negotiated(*ours);
+          negotiated.id = theirs->id;
+          audio_accept->AddCodec(negotiated);
+        }
+      }
+    }
+
+    audio_accept->SortCodecs();
+    audio_accept->set_ssrc(talk_base::CreateRandomNonZeroId());
+    audio_accept->set_rtcp_mux(audio_offer->rtcp_mux());
+
+    if (secure() != SEC_DISABLED) {
+      CryptoParams crypto;
+
+      if (SelectCrypto(audio_offer, &crypto)) {
+        audio_accept->AddCrypto(crypto);
+      }
+    }
+
+    if (audio_accept->cryptos().empty() &&
+        (audio_offer->crypto_required() || secure() == SEC_REQUIRED)) {
+      return NULL;  // Fails the session setup.
+    }
+    accept->AddContent(audio_content->name, audio_content->type, audio_accept);
+  }
+
+  const ContentInfo* video_content = GetFirstVideoContent(offer);
+  if (video_content && options.is_video) {
+    const VideoContentDescription* video_offer =
+        static_cast<const VideoContentDescription*>(video_content->description);
+    VideoContentDescription* video_accept = new VideoContentDescription();
+    for (VideoCodecs::const_iterator ours = video_codecs_.begin();
+        ours != video_codecs_.end(); ++ours) {
+      for (VideoCodecs::const_iterator theirs = video_offer->codecs().begin();
+          theirs != video_offer->codecs().end(); ++theirs) {
+        if (ours->Matches(*theirs)) {
+          VideoCodec negotiated(*ours);
+          negotiated.id = theirs->id;
+          video_accept->AddCodec(negotiated);
+        }
+      }
+    }
+
+    video_accept->set_ssrc(talk_base::CreateRandomNonZeroId());
+    video_accept->set_bandwidth(options.video_bandwidth);
+    video_accept->set_rtcp_mux(video_offer->rtcp_mux());
+    video_accept->SortCodecs();
+
+    if (secure() != SEC_DISABLED) {
+      CryptoParams crypto;
+
+      if (SelectCrypto(video_offer, &crypto)) {
+        video_accept->AddCrypto(crypto);
+      }
+    }
+
+    if (video_accept->cryptos().empty() &&
+        (video_offer->crypto_required() || secure() == SEC_REQUIRED)) {
+      return NULL;  // Fails the session setup.
+    }
+    accept->AddContent(video_content->name, video_content->type, video_accept);
+  }
+
+  return accept;
+}
+
+static bool IsMediaContent(const ContentInfo* content, MediaType media_type) {
+  if (content == NULL || content->type != NS_JINGLE_RTP) {
+    return false;
+  }
+
+  const MediaContentDescription* media =
+      static_cast<const MediaContentDescription*>(content->description);
+  return media->type() == media_type;
+}
+
+bool IsAudioContent(const ContentInfo* content) {
+  return IsMediaContent(content, MEDIA_TYPE_AUDIO);
+}
+
+bool IsVideoContent(const ContentInfo* content) {
+  return IsMediaContent(content, MEDIA_TYPE_VIDEO);
+}
+
+static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
+                                               MediaType media_type) {
+  if (sdesc == NULL)
+    return NULL;
+
+  const ContentInfos& contents = sdesc->contents();
+  for (ContentInfos::const_iterator content = contents.begin();
+       content != contents.end(); content++) {
+    if (IsMediaContent(&*content, media_type)) {
+      return &*content;
+    }
+  }
+  return NULL;
+}
+
+const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
+  return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
+}
+
+const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
+  return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
+}
+
+}  // namespace cricket
diff --git a/talk/session/phone/mediasession.h b/talk/session/phone/mediasession.h
new file mode 100644
index 0000000..9bfd84f
--- /dev/null
+++ b/talk/session/phone/mediasession.h
@@ -0,0 +1,236 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Types and classes used in media session descriptions.
+
+#ifndef TALK_SESSION_PHONE_MEDIASESSION_H_
+#define TALK_SESSION_PHONE_MEDIASESSION_H_
+
+#include <string>
+#include <vector>
+#include <algorithm>
+
+#include "talk/session/phone/codec.h"
+#include "talk/session/phone/cryptoparams.h"
+#include "talk/session/phone/mediachannel.h"
+#include "talk/p2p/base/sessiondescription.h"
+
+namespace cricket {
+
+class ChannelManager;
+typedef std::vector<AudioCodec> AudioCodecs;
+typedef std::vector<VideoCodec> VideoCodecs;
+typedef std::vector<CryptoParams> CryptoParamsVec;
+
+// SEC_ENABLED and SEC_REQUIRED should only be used if the session
+// was negotiated over TLS, to protect the inline crypto material
+// exchange.
+// SEC_DISABLED: No crypto in outgoing offer and answer. Fail any
+//               offer with crypto required.
+// SEC_ENABLED: Crypto in outgoing offer and answer. Fail any offer
+//              with unsupported required crypto. Crypto set but not
+//              required in outgoing offer.
+// SEC_REQUIRED: Crypto in outgoing offer and answer with
+//               required='true'. Fail any offer with no or
+//               unsupported crypto (implicit crypto required='true'
+//               in the offer.)
+enum SecureMediaPolicy {
+  SEC_DISABLED,
+  SEC_ENABLED,
+  SEC_REQUIRED
+};
+
+// Options to control how session descriptions are generated.
+const int kAutoBandwidth = -1;
+struct MediaSessionOptions {
+  MediaSessionOptions() :
+      is_video(false),
+      is_muc(false),
+      video_bandwidth(kAutoBandwidth) {
+  }
+
+  bool is_video;
+  bool is_muc;
+  // bps. -1 == auto.
+  int video_bandwidth;
+};
+
+enum MediaType {
+  MEDIA_TYPE_AUDIO,
+  MEDIA_TYPE_VIDEO
+};
+
+// "content" (as used in XEP-0166) descriptions for voice and video.
+class MediaContentDescription : public ContentDescription {
+ public:
+  MediaContentDescription()
+      : ssrc_(0),
+        ssrc_set_(false),
+        rtcp_mux_(false),
+        bandwidth_(kAutoBandwidth),
+        crypto_required_(false),
+        rtp_header_extensions_set_(false) {
+  }
+
+  virtual MediaType type() const = 0;
+
+  uint32 ssrc() const { return ssrc_; }
+  bool ssrc_set() const { return ssrc_set_; }
+  void set_ssrc(uint32 ssrc) {
+    ssrc_ = ssrc;
+    ssrc_set_ = true;
+  }
+
+  bool rtcp_mux() const { return rtcp_mux_; }
+  void set_rtcp_mux(bool mux) { rtcp_mux_ = mux; }
+
+  int bandwidth() const { return bandwidth_; }
+  void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; }
+
+  const std::vector<CryptoParams>& cryptos() const { return cryptos_; }
+  void AddCrypto(const CryptoParams& params) {
+    cryptos_.push_back(params);
+  }
+  bool crypto_required() const { return crypto_required_; }
+  void set_crypto_required(bool crypto) {
+    crypto_required_ = crypto;
+  }
+
+  const std::vector<RtpHeaderExtension>& rtp_header_extensions() const {
+    return rtp_header_extensions_;
+  }
+  void AddRtpHeaderExtension(const RtpHeaderExtension& ext) {
+    rtp_header_extensions_.push_back(ext);
+    rtp_header_extensions_set_ = true;
+  }
+  void ClearRtpHeaderExtensions() {
+    rtp_header_extensions_.clear();
+    rtp_header_extensions_set_ = true;
+  }
+  // We can't always tell if an empty list of header extensions is
+  // because the other side doesn't support them, or just isn't hooked up to
+  // signal them. For now we assume an empty list means no signaling, but
+  // provide the ClearRtpHeaderExtensions method to allow "no support" to be
+  // clearly indicated (i.e. when derived from other information).
+  bool rtp_header_extensions_set() const {
+    return rtp_header_extensions_set_;
+  }
+
+ protected:
+  uint32 ssrc_;
+  bool ssrc_set_;
+  bool rtcp_mux_;
+  int bandwidth_;
+  std::vector<CryptoParams> cryptos_;
+  bool crypto_required_;
+  std::vector<RtpHeaderExtension> rtp_header_extensions_;
+  bool rtp_header_extensions_set_;
+};
+
+template <class C>
+class MediaContentDescriptionImpl : public MediaContentDescription {
+ public:
+  struct PreferenceSort {
+    bool operator()(C a, C b) { return a.preference > b.preference; }
+  };
+
+  const std::vector<C>& codecs() const { return codecs_; }
+  void AddCodec(const C& codec) {
+    codecs_.push_back(codec);
+  }
+  void SortCodecs() {
+    std::sort(codecs_.begin(), codecs_.end(), PreferenceSort());
+  }
+
+ private:
+  std::vector<C> codecs_;
+};
+
+class AudioContentDescription : public MediaContentDescriptionImpl<AudioCodec> {
+ public:
+  AudioContentDescription() :
+      conference_mode_(false) {}
+
+  virtual MediaType type() const { return MEDIA_TYPE_AUDIO; }
+
+  bool conference_mode() const { return conference_mode_; }
+  void set_conference_mode(bool enable) {
+    conference_mode_ = enable;
+  }
+
+  const std::string &lang() const { return lang_; }
+  void set_lang(const std::string &lang) { lang_ = lang; }
+
+
+ private:
+  bool conference_mode_;
+  std::string lang_;
+};
+
+class VideoContentDescription : public MediaContentDescriptionImpl<VideoCodec> {
+ public:
+  virtual MediaType type() const { return MEDIA_TYPE_VIDEO; }
+};
+
+// Creates media session descriptions according to the supplied codecs and
+// other fields, as well as the supplied per-call options.
+// When creating answers, performs the appropriate negotiation
+// of the various fields to determine the proper result.
+class MediaSessionDescriptionFactory {
+ public:
+  // Default ctor; use methods below to set configuration.
+  MediaSessionDescriptionFactory();
+  // Helper, to allow configuration to be loaded from a ChannelManager.
+  explicit MediaSessionDescriptionFactory(ChannelManager* manager);
+
+  const AudioCodecs& audio_codecs() const { return audio_codecs_; }
+  void set_audio_codecs(const AudioCodecs& codecs) { audio_codecs_ = codecs; }
+  const VideoCodecs& video_codecs() const { return video_codecs_; }
+  void set_video_codecs(const VideoCodecs& codecs) { video_codecs_ = codecs; }
+  SecureMediaPolicy secure() const { return secure_; }
+  void set_secure(SecureMediaPolicy s) { secure_ = s; }
+
+  SessionDescription* CreateOffer(const MediaSessionOptions& options);
+  SessionDescription* CreateAnswer(const SessionDescription* offer,
+                                   const MediaSessionOptions& options);
+
+ private:
+  AudioCodecs audio_codecs_;
+  VideoCodecs video_codecs_;
+  SecureMediaPolicy secure_;
+  std::string lang_;
+};
+
+// Convenience functions.
+bool IsAudioContent(const ContentInfo* content);
+bool IsVideoContent(const ContentInfo* content);
+const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc);
+const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc);
+
+}  // namespace cricket
+
+#endif  // TALK_SESSION_PHONE_MEDIASESSION_H_
diff --git a/talk/session/phone/mediasessionclient.cc b/talk/session/phone/mediasessionclient.cc
index b4d440c..e709479 100644
--- a/talk/session/phone/mediasessionclient.cc
+++ b/talk/session/phone/mediasessionclient.cc
@@ -41,35 +41,30 @@
 #include "talk/xmllite/qname.h"
 #include "talk/xmllite/xmlconstants.h"
 
-using namespace talk_base;
-
-namespace {
-const std::string kInline = "inline:";
-}
-
 namespace cricket {
 
-typedef std::vector<CryptoParams> CryptoParamsVec;
-
 MediaSessionClient::MediaSessionClient(
     const buzz::Jid& jid, SessionManager *manager)
-    : jid_(jid), session_manager_(manager), focus_call_(NULL),
+    : jid_(jid),
+      session_manager_(manager),
+      focus_call_(NULL),
       channel_manager_(new ChannelManager(session_manager_->worker_thread())),
-      secure_(SEC_DISABLED) {
+      desc_factory_(channel_manager_) {
   Construct();
 }
 
 MediaSessionClient::MediaSessionClient(
     const buzz::Jid& jid, SessionManager *manager,
-    MediaEngine* media_engine, DeviceManager* device_manager)
-    : jid_(jid), session_manager_(manager), focus_call_(NULL),
+    MediaEngineInterface* media_engine, DeviceManager* device_manager)
+    : jid_(jid),
+      session_manager_(manager),
+      focus_call_(NULL),
       channel_manager_(new ChannelManager(
           media_engine, device_manager, session_manager_->worker_thread())),
-      secure_(SEC_DISABLED) {
+      desc_factory_(channel_manager_) {
   Construct();
 }
 
-
 void MediaSessionClient::Construct() {
   // Register ourselves as the handler of audio and video sessions.
   session_manager_->AddClient(NS_JINGLE_RTP, this);
@@ -96,257 +91,6 @@
   session_manager_->RemoveClient(NS_JINGLE_RTP);
 }
 
-bool CreateCryptoParams(int tag, const std::string& cipher, CryptoParams *out) {
-  std::string key;
-  key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
-
-  if (!CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
-    return false;
-  }
-  out->tag = tag;
-  out->cipher_suite = cipher;
-  out->key_params = kInline + key;
-  return true;
-}
-
-bool AddCryptoParams(const std::string& cipher_suite, CryptoParamsVec *out) {
-  int size = out->size();
-
-  out->resize(size + 1);
-  return CreateCryptoParams(size, cipher_suite, &out->at(size));
-}
-
-// For audio, HMAC 32 is prefered because of the low overhead.
-bool GetSupportedAudioCryptos(CryptoParamsVec* cryptos) {
-#ifdef HAVE_SRTP
-  return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_32, cryptos) &&
-      AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
-#else
-  return false;
-#endif
-}
-
-bool GetSupportedVideoCryptos(CryptoParamsVec* cryptos) {
-#ifdef HAVE_SRTP
-  return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
-#else
-  return false;
-#endif
-}
-
-SessionDescription* MediaSessionClient::CreateOffer(
-    const CallOptions& options) {
-  SessionDescription* offer = new SessionDescription();
-  AudioContentDescription* audio = new AudioContentDescription();
-
-
-  AudioCodecs audio_codecs;
-  channel_manager_->GetSupportedAudioCodecs(&audio_codecs);
-  for (AudioCodecs::const_iterator codec = audio_codecs.begin();
-       codec != audio_codecs.end(); ++codec) {
-    audio->AddCodec(*codec);
-  }
-  if (options.is_muc) {
-    audio->set_ssrc(talk_base::CreateRandomNonZeroId());
-  }
-  audio->SortCodecs();
-
-  if (secure() != SEC_DISABLED) {
-    CryptoParamsVec audio_cryptos;
-    if (GetSupportedAudioCryptos(&audio_cryptos)) {
-      for (CryptoParamsVec::const_iterator crypto = audio_cryptos.begin();
-           crypto != audio_cryptos.end(); ++crypto) {
-        audio->AddCrypto(*crypto);
-      }
-    }
-    if (secure() == SEC_REQUIRED) {
-      if (audio->cryptos().empty()) {
-        return NULL;  // Abort, crypto required but none found.
-      }
-      audio->set_crypto_required(true);
-    }
-  }
-
-  offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio);
-
-  // add video codecs, if this is a video call
-  if (options.is_video) {
-    VideoContentDescription* video = new VideoContentDescription();
-    VideoCodecs video_codecs;
-    channel_manager_->GetSupportedVideoCodecs(&video_codecs);
-    for (VideoCodecs::const_iterator codec = video_codecs.begin();
-         codec != video_codecs.end(); ++codec) {
-      video->AddCodec(*codec);
-    }
-    if (options.is_muc) {
-      video->set_ssrc(talk_base::CreateRandomNonZeroId());
-    }
-    video->set_bandwidth(options.video_bandwidth);
-    video->SortCodecs();
-
-    if (secure() != SEC_DISABLED) {
-      CryptoParamsVec video_cryptos;
-      if (GetSupportedVideoCryptos(&video_cryptos)) {
-        for (CryptoParamsVec::const_iterator crypto = video_cryptos.begin();
-             crypto != video_cryptos.end(); ++crypto) {
-          video->AddCrypto(*crypto);
-        }
-      }
-      if (secure() == SEC_REQUIRED) {
-        if (video->cryptos().empty()) {
-          return NULL;  // Abort, crypto required but none found.
-        }
-        video->set_crypto_required(true);
-      }
-    }
-
-    offer->AddContent(CN_VIDEO, NS_JINGLE_RTP, video);
-  }
-
-  return offer;
-}
-
-bool IsMediaContent(const ContentInfo* content, MediaType media_type) {
-  if (content == NULL || content->type != NS_JINGLE_RTP) {
-    return false;
-  }
-
-  const MediaContentDescription* media =
-      static_cast<const MediaContentDescription*>(content->description);
-  return media->type() == media_type;
-}
-
-bool IsAudioContent(const ContentInfo* content) {
-  return IsMediaContent(content, MEDIA_TYPE_AUDIO);
-}
-
-bool IsVideoContent(const ContentInfo* content) {
-  return IsMediaContent(content, MEDIA_TYPE_VIDEO);
-}
-
-const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
-                                        MediaType media_type) {
-  if (sdesc == NULL)
-    return NULL;
-
-  const ContentInfos& contents = sdesc->contents();
-  for (ContentInfos::const_iterator content = contents.begin();
-       content != contents.end(); content++) {
-    if (IsMediaContent(&*content, media_type)) {
-      return &*content;
-    }
-  }
-  return NULL;
-}
-
-const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
-  return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
-}
-
-const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
-  return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
-}
-
-// For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
-// tolerated because it is low overhead. Pick the crypto in the list
-// that is supported.
-bool SelectCrypto(const MediaContentDescription* offer, CryptoParams *crypto) {
-  bool audio = offer->type() == MEDIA_TYPE_AUDIO;
-  const CryptoParamsVec& cryptos = offer->cryptos();
-
-  for (CryptoParamsVec::const_iterator i = cryptos.begin();
-       i != cryptos.end(); ++i) {
-    if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
-        (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio)) {
-      return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
-    }
-  }
-  return false;
-}
-
-SessionDescription* MediaSessionClient::CreateAnswer(
-    const SessionDescription* offer, const CallOptions& options) {
-  // The answer contains the intersection of the codecs in the offer with the
-  // codecs we support, ordered by our local preference. As indicated by
-  // XEP-0167, we retain the same payload ids from the offer in the answer.
-  SessionDescription* accept = new SessionDescription();
-
-  const ContentInfo* audio_content = GetFirstAudioContent(offer);
-  if (audio_content) {
-    const AudioContentDescription* audio_offer =
-        static_cast<const AudioContentDescription*>(audio_content->description);
-    AudioContentDescription* audio_accept = new AudioContentDescription();
-    AudioCodecs audio_codecs;
-    channel_manager_->GetSupportedAudioCodecs(&audio_codecs);
-    for (AudioCodecs::const_iterator ours = audio_codecs.begin();
-        ours != audio_codecs.end(); ++ours) {
-      for (AudioCodecs::const_iterator theirs = audio_offer->codecs().begin();
-          theirs != audio_offer->codecs().end(); ++theirs) {
-        if (ours->Matches(*theirs)) {
-          AudioCodec negotiated(*ours);
-          negotiated.id = theirs->id;
-          audio_accept->AddCodec(negotiated);
-        }
-      }
-    }
-
-    audio_accept->SortCodecs();
-
-    if (secure() != SEC_DISABLED) {
-      CryptoParams crypto;
-
-      if (SelectCrypto(audio_offer, &crypto)) {
-        audio_accept->AddCrypto(crypto);
-      }
-    }
-
-    if (audio_accept->cryptos().empty() &&
-        (audio_offer->crypto_required() || secure() == SEC_REQUIRED)) {
-      return NULL;  // Fails the session setup.
-    }
-    accept->AddContent(audio_content->name, audio_content->type, audio_accept);
-  }
-
-  const ContentInfo* video_content = GetFirstVideoContent(offer);
-  if (video_content) {
-    const VideoContentDescription* video_offer =
-        static_cast<const VideoContentDescription*>(video_content->description);
-    VideoContentDescription* video_accept = new VideoContentDescription();
-    VideoCodecs video_codecs;
-    channel_manager_->GetSupportedVideoCodecs(&video_codecs);
-    for (VideoCodecs::const_iterator ours = video_codecs.begin();
-        ours != video_codecs.end(); ++ours) {
-      for (VideoCodecs::const_iterator theirs = video_offer->codecs().begin();
-          theirs != video_offer->codecs().end(); ++theirs) {
-        if (ours->Matches(*theirs)) {
-          VideoCodec negotiated(*ours);
-          negotiated.id = theirs->id;
-          video_accept->AddCodec(negotiated);
-        }
-      }
-    }
-
-    video_accept->set_bandwidth(options.video_bandwidth);
-    video_accept->SortCodecs();
-
-    if (secure() != SEC_DISABLED) {
-      CryptoParams crypto;
-
-      if (SelectCrypto(video_offer, &crypto)) {
-        video_accept->AddCrypto(crypto);
-      }
-    }
-
-    if (video_accept->cryptos().empty() &&
-        (video_offer->crypto_required() || secure() == SEC_REQUIRED)) {
-      return NULL;  // Fails the session setup.
-    }
-    accept->AddContent(video_content->name, video_content->type, video_accept);
-  }
-
-  return accept;
-}
-
 Call *MediaSessionClient::CreateCall() {
   Call *call = new Call(this);
   calls_[call->id()] = call;
@@ -484,7 +228,7 @@
 uint32 parse_ssrc(const std::string& ssrc) {
   // TODO: Return an error rather than defaulting to 0.
   uint32 default_ssrc = 0U;
-  return FromString(default_ssrc, ssrc);
+  return talk_base::FromString(default_ssrc, ssrc);
 }
 
 void ParseGingleSsrc(const buzz::XmlElement* parent_elem,
@@ -561,7 +305,7 @@
                     MediaContentDescription* media) {
   const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH);
   int bandwidth_kbps;
-  if (bw_elem && FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
+  if (bw_elem && talk_base::FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
     if (bandwidth_kbps >= 0) {
       media->set_bandwidth(bandwidth_kbps * 1000);
     }
@@ -1000,7 +744,10 @@
     elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
   }
 
-  // TODO: Figure out how to integrate SSRC into Jingle.
+  if (audio->rtcp_mux()) {
+    elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
+  }
+
   return elem;
 }
 
@@ -1022,12 +769,15 @@
     elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
   }
 
+  if (video->rtcp_mux()) {
+    elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
+  }
+
   if (video->bandwidth() != kAutoBandwidth) {
     elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
                                          video->bandwidth()));
   }
 
-  // TODO: Figure out how to integrate SSRC into Jingle.
   return elem;
 }
 
diff --git a/talk/session/phone/mediasessionclient.h b/talk/session/phone/mediasessionclient.h
index 07fc258..4616589 100644
--- a/talk/session/phone/mediasessionclient.h
+++ b/talk/session/phone/mediasessionclient.h
@@ -35,6 +35,7 @@
 #include "talk/session/phone/call.h"
 #include "talk/session/phone/channelmanager.h"
 #include "talk/session/phone/cryptoparams.h"
+#include "talk/session/phone/mediasession.h"
 #include "talk/base/sigslot.h"
 #include "talk/base/sigslotrepeater.h"
 #include "talk/base/messagequeue.h"
@@ -47,53 +48,24 @@
 namespace cricket {
 
 class Call;
-class SessionDescription;
-typedef std::vector<AudioCodec> AudioCodecs;
-typedef std::vector<VideoCodec> VideoCodecs;
 
-// SEC_ENABLED and SEC_REQUIRED should only be used if the session
-// was negotiated over TLS, to protect the inline crypto material
-// exchange.
-// SEC_DISABLED: No crypto in outgoing offer and answer. Fail any
-//               offer with crypto required.
-// SEC_ENABLED: Crypto in outgoing offer and answer. Fail any offer
-//              with unsupported required crypto. Crypto set but not
-//              required in outgoing offer.
-// SEC_REQUIRED: Crypto in outgoing offer and answer with
-//               required='true'. Fail any offer with no or
-//               unsupported crypto (implicit crypto required='true'
-//               in the offer.)
-enum SecureMediaPolicy {SEC_DISABLED, SEC_ENABLED, SEC_REQUIRED};
-
-const int kAutoBandwidth = -1;
-
-struct CallOptions {
-  CallOptions() :
-      is_video(false),
-      is_muc(false),
-      video_bandwidth(kAutoBandwidth) {
-  }
-
-  bool is_video;
-  bool is_muc;
-  // bps. -1 == auto.
-  int video_bandwidth;
-};
-
-class MediaSessionClient: public SessionClient, public sigslot::has_slots<> {
+class MediaSessionClient : public SessionClient, public sigslot::has_slots<> {
  public:
-
   MediaSessionClient(const buzz::Jid& jid, SessionManager *manager);
   // Alternative constructor, allowing injection of media_engine
   // and device_manager.
   MediaSessionClient(const buzz::Jid& jid, SessionManager *manager,
-      MediaEngine* media_engine, DeviceManager* device_manager);
+                     MediaEngineInterface* media_engine,
+                     DeviceManager* device_manager);
   ~MediaSessionClient();
 
   const buzz::Jid &jid() const { return jid_; }
   SessionManager* session_manager() const { return session_manager_; }
   ChannelManager* channel_manager() const { return channel_manager_; }
 
+  SecureMediaPolicy secure() const { return desc_factory_.secure(); }
+  void set_secure(SecureMediaPolicy s) { desc_factory_.set_secure(s); }
+
   int GetCapabilities() { return channel_manager_->GetCapabilities(); }
 
   Call *CreateCall();
@@ -125,18 +97,19 @@
     return channel_manager_->SetVideoOptions(cam_device);
   }
 
+  SessionDescription* CreateOffer(const CallOptions& options) {
+    return desc_factory_.CreateOffer(options);
+  }
+  SessionDescription* CreateAnswer(const SessionDescription* offer,
+                                   const CallOptions& options) {
+    return desc_factory_.CreateAnswer(offer, options);
+  }
+
   sigslot::signal2<Call *, Call *> SignalFocus;
   sigslot::signal1<Call *> SignalCallCreate;
   sigslot::signal1<Call *> SignalCallDestroy;
   sigslot::repeater0<> SignalDevicesChange;
 
-  SessionDescription* CreateOffer(const CallOptions& options);
-  SessionDescription* CreateAnswer(const SessionDescription* offer,
-                                   const CallOptions& options);
-
-  SecureMediaPolicy secure() const { return secure_; }
-  void set_secure(SecureMediaPolicy s) { secure_ = s; }
-
  private:
   void Construct();
   void OnSessionCreate(Session *session, bool received_initiate);
@@ -156,134 +129,12 @@
   SessionManager* session_manager_;
   Call *focus_call_;
   ChannelManager *channel_manager_;
+  MediaSessionDescriptionFactory desc_factory_;
   std::map<uint32, Call *> calls_;
   std::map<std::string, Call *> session_map_;
-  SecureMediaPolicy secure_;
   friend class Call;
 };
 
-enum MediaType {
-  MEDIA_TYPE_AUDIO,
-  MEDIA_TYPE_VIDEO
-};
-
-class MediaContentDescription : public ContentDescription {
- public:
-  MediaContentDescription()
-      : ssrc_(0),
-        ssrc_set_(false),
-        rtcp_mux_(false),
-        bandwidth_(kAutoBandwidth),
-        crypto_required_(false),
-        rtp_header_extensions_set_(false) {
-  }
-
-  virtual MediaType type() const = 0;
-
-  uint32 ssrc() const { return ssrc_; }
-  bool ssrc_set() const { return ssrc_set_; }
-  void set_ssrc(uint32 ssrc) {
-    ssrc_ = ssrc;
-    ssrc_set_ = true;
-  }
-
-  bool rtcp_mux() const { return rtcp_mux_; }
-  void set_rtcp_mux(bool mux) { rtcp_mux_ = mux; }
-
-  int bandwidth() const { return bandwidth_; }
-  void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; }
-
-  const std::vector<CryptoParams>& cryptos() const { return cryptos_; }
-  void AddCrypto(const CryptoParams& params) {
-    cryptos_.push_back(params);
-  }
-  bool crypto_required() const { return crypto_required_; }
-  void set_crypto_required(bool crypto) {
-    crypto_required_ = crypto;
-  }
-
-  const std::vector<RtpHeaderExtension>& rtp_header_extensions() const {
-    return rtp_header_extensions_;
-  }
-  void AddRtpHeaderExtension(const RtpHeaderExtension& ext) {
-    rtp_header_extensions_.push_back(ext);
-    rtp_header_extensions_set_ = true;
-  }
-  void ClearRtpHeaderExtensions() {
-    rtp_header_extensions_.clear();
-    rtp_header_extensions_set_ = true;
-  }
-  // We can't always tell if an empty list of header extensions is
-  // because the other side doesn't support them, or just isn't hooked up to
-  // signal them. For now we assume an empty list means no signaling, but
-  // provide the ClearRtpHeaderExtensions method to allow "no support" to be
-  // clearly indicated (i.e. when derived from other information).
-  bool rtp_header_extensions_set() const {
-    return rtp_header_extensions_set_;
-  }
-
- protected:
-  uint32 ssrc_;
-  bool ssrc_set_;
-  bool rtcp_mux_;
-  int bandwidth_;
-  std::vector<CryptoParams> cryptos_;
-  bool crypto_required_;
-  std::vector<RtpHeaderExtension> rtp_header_extensions_;
-  bool rtp_header_extensions_set_;
-};
-
-template <class C>
-class MediaContentDescriptionImpl : public MediaContentDescription {
- public:
-  struct PreferenceSort {
-    bool operator()(C a, C b) { return a.preference > b.preference; }
-  };
-
-  const std::vector<C>& codecs() const { return codecs_; }
-  void AddCodec(const C& codec) {
-    codecs_.push_back(codec);
-  }
-  void SortCodecs() {
-    std::sort(codecs_.begin(), codecs_.end(), PreferenceSort());
-  }
-
- private:
-  std::vector<C> codecs_;
-};
-
-class AudioContentDescription : public MediaContentDescriptionImpl<AudioCodec> {
- public:
-  AudioContentDescription() :
-      conference_mode_(false) {}
-
-  virtual MediaType type() const { return MEDIA_TYPE_AUDIO; }
-
-  bool conference_mode() const { return conference_mode_; }
-  void set_conference_mode(bool enable) {
-    conference_mode_ = enable;
-  }
-
-  const std::string &lang() const { return lang_; }
-  void set_lang(const std::string &lang) { lang_ = lang; }
-
-
- private:
-  bool conference_mode_;
-  std::string lang_;
-};
-
-class VideoContentDescription : public MediaContentDescriptionImpl<VideoCodec> {
- public:
-  virtual MediaType type() const { return MEDIA_TYPE_VIDEO; }
-};
-
-// Convenience functions.
-bool IsAudioContent(const ContentInfo* content);
-bool IsVideoContent(const ContentInfo* content);
-const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc);
-const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc);
-
 }  // namespace cricket
 
 #endif  // TALK_SESSION_PHONE_MEDIASESSIONCLIENT_H_
diff --git a/talk/session/phone/rtpdump.cc b/talk/session/phone/rtpdump.cc
index 6358399..65381fd 100644
--- a/talk/session/phone/rtpdump.cc
+++ b/talk/session/phone/rtpdump.cc
@@ -38,8 +38,7 @@
 
 namespace cricket {
 
-const std::string RtpDumpFileHeader::kFirstLine =
-    "#!rtpplay1.0 0.0.0.0/0\n";
+const char RtpDumpFileHeader::kFirstLine[] = "#!rtpplay1.0 0.0.0.0/0\n";
 
 RtpDumpFileHeader::RtpDumpFileHeader(uint32 start_ms, uint32 s, uint16 p)
     : start_sec(start_ms / 1000),
@@ -296,7 +295,12 @@
       packet_filter_(PF_ALL),
       file_header_written_(false),
       start_time_ms_(talk_base::Time()) {
-  }
+}
+
+void RtpDumpWriter::set_packet_filter(int filter) {
+  packet_filter_ = filter;
+  LOG(LS_INFO) << "RtpDumpWriter set_packet_filter to " << packet_filter_;
+}
 
 uint32 RtpDumpWriter::GetElapsedTime() const {
   return talk_base::TimeSince(start_time_ms_);
@@ -304,8 +308,8 @@
 
 talk_base::StreamResult RtpDumpWriter::WriteFileHeader() {
   talk_base::StreamResult res = stream_->WriteAll(
-      RtpDumpFileHeader::kFirstLine.c_str(),
-      RtpDumpFileHeader::kFirstLine.size(), NULL, NULL);
+      RtpDumpFileHeader::kFirstLine,
+      strlen(RtpDumpFileHeader::kFirstLine), NULL, NULL);
   if (res != talk_base::SR_SUCCESS) {
     return res;
   }
diff --git a/talk/session/phone/rtpdump.h b/talk/session/phone/rtpdump.h
index 6b9f5a0..57138b8 100644
--- a/talk/session/phone/rtpdump.h
+++ b/talk/session/phone/rtpdump.h
@@ -61,7 +61,7 @@
   RtpDumpFileHeader(uint32 start_ms, uint32 s, uint16 p);
   void WriteToByteBuffer(talk_base::ByteBuffer* buf);
 
-  static const std::string kFirstLine;
+  static const char kFirstLine[];
   static const size_t kHeaderLength = 16;
   uint32 start_sec;   // start of recording, the seconds part.
   uint32 start_usec;  // start of recording, the microseconds part.
@@ -181,10 +181,7 @@
   explicit RtpDumpWriter(talk_base::StreamInterface* stream);
 
   // Filter to control what packets we actually record.
-  void set_packet_filter(int filter) {
-    packet_filter_ = filter;
-  }
-
+  void set_packet_filter(int filter);
   // 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 3b46e57..b3578e1 100644
--- a/talk/session/phone/srtpfilter.cc
+++ b/talk/session/phone/srtpfilter.cc
@@ -76,9 +76,8 @@
 
 namespace cricket {
 
-const std::string& CS_DEFAULT = CS_AES_CM_128_HMAC_SHA1_80;
-const std::string CS_AES_CM_128_HMAC_SHA1_80 = "AES_CM_128_HMAC_SHA1_80";
-const std::string CS_AES_CM_128_HMAC_SHA1_32 = "AES_CM_128_HMAC_SHA1_32";
+const char CS_AES_CM_128_HMAC_SHA1_80[] = "AES_CM_128_HMAC_SHA1_80";
+const char CS_AES_CM_128_HMAC_SHA1_32[] = "AES_CM_128_HMAC_SHA1_32";
 const int SRTP_MASTER_KEY_BASE64_LEN = SRTP_MASTER_KEY_LEN * 4 / 3;
 
 #ifndef HAVE_SRTP
@@ -95,6 +94,7 @@
 #endif  // !HAVE_SRTP
 
 void EnableSrtpDebugging() {
+#ifdef HAVE_SRTP
 #ifdef _DEBUG
   debug_on(mod_srtp);
   debug_on(mod_auth);
@@ -105,6 +105,7 @@
   // debug_on(mod_aes_cbc);
   // debug_on(mod_hmac);
 #endif
+#endif  // HAVE_SRTP
 }
 
 SrtpFilter::SrtpFilter()
diff --git a/talk/session/phone/srtpfilter.h b/talk/session/phone/srtpfilter.h
index a03b50f..409a3a9 100644
--- a/talk/session/phone/srtpfilter.h
+++ b/talk/session/phone/srtpfilter.h
@@ -50,11 +50,10 @@
 // Cipher suite to use for SRTP. Typically a 80-bit HMAC will be used, except
 // in applications (voice) where the additional bandwidth may be significant.
 // A 80-bit HMAC is always used for SRTCP.
-extern const std::string& CS_DEFAULT;
 // 128-bit AES with 80-bit SHA-1 HMAC.
-extern const std::string CS_AES_CM_128_HMAC_SHA1_80;
+extern const char CS_AES_CM_128_HMAC_SHA1_80[];
 // 128-bit AES with 32-bit SHA-1 HMAC.
-extern const std::string CS_AES_CM_128_HMAC_SHA1_32;
+extern const char CS_AES_CM_128_HMAC_SHA1_32[];
 // Key is 128 bits and salt is 112 bits == 30 bytes. B64 bloat => 40 bytes.
 extern const int SRTP_MASTER_KEY_BASE64_LEN;
 
diff --git a/talk/session/phone/videocommon.h b/talk/session/phone/videocommon.h
index ff8e8de..27a2e90 100644
--- a/talk/session/phone/videocommon.h
+++ b/talk/session/phone/videocommon.h
@@ -89,6 +89,7 @@
   FOURCC_HDYC = FOURCC('H', 'D', 'Y', 'C'),  // Alias for UYVY
   FOURCC_2VUY = FOURCC('2', 'v', 'u', 'y'),  // Alias for UYVY
   FOURCC_JPEG = FOURCC('J', 'P', 'E', 'G'),  // Alias for MJPG
+  FOURCC_DMB1 = FOURCC('d', 'm', 'b', '1'),  // Alias for MJPG on Mac
   FOURCC_BA81 = FOURCC('B', 'A', '8', '1'),  // Alias for BGGR
   FOURCC_RGB3 = FOURCC('R', 'G', 'B', '3'),  // Alias for RAW
   FOURCC_BGR3 = FOURCC('B', 'G', 'R', '3'),  // Alias for 24BG
diff --git a/talk/session/phone/videoframe.h b/talk/session/phone/videoframe.h
new file mode 100644
index 0000000..3fa533e
--- /dev/null
+++ b/talk/session/phone/videoframe.h
@@ -0,0 +1,149 @@
+/*
+ * libjingle
+ * Copyright 2004, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_SESSION_PHONE_VIDEOFRAME_H_
+#define TALK_SESSION_PHONE_VIDEOFRAME_H_
+
+#include "talk/base/basictypes.h"
+
+namespace flute {
+class MagicCamVideoRenderer;
+}
+
+namespace cricket {
+
+// Represents a YUV420 (a.k.a. I420) video frame.
+class VideoFrame {
+  friend class flute::MagicCamVideoRenderer;
+
+ public:
+  VideoFrame() : rendered_(false) {}
+
+  virtual ~VideoFrame() {}
+
+  virtual size_t GetWidth() const = 0;
+  virtual size_t GetHeight() const = 0;
+  size_t GetChromaWidth() const { return (GetWidth() + 1) / 2; }
+  size_t GetChromaHeight() const { return (GetHeight() + 1) / 2; }
+  virtual const uint8 *GetYPlane() const = 0;
+  virtual const uint8 *GetUPlane() const = 0;
+  virtual const uint8 *GetVPlane() const = 0;
+  virtual uint8 *GetYPlane() = 0;
+  virtual uint8 *GetUPlane() = 0;
+  virtual uint8 *GetVPlane() = 0;
+  virtual int32 GetYPitch() const = 0;
+  virtual int32 GetUPitch() const = 0;
+  virtual int32 GetVPitch() const = 0;
+
+  // For retrieving the aspect ratio of each pixel. Usually this is 1x1, but
+  // the aspect_ratio_idc parameter of H.264 can specify non-square pixels.
+  virtual size_t GetPixelWidth() const = 0;
+  virtual size_t GetPixelHeight() const = 0;
+
+  // TODO: Add a fourcc format here and probably combine VideoFrame
+  // with CapturedFrame.
+  virtual int64 GetElapsedTime() const = 0;
+  virtual int64 GetTimeStamp() const = 0;
+  virtual void SetElapsedTime(int64 elapsed_time) = 0;
+  virtual void SetTimeStamp(int64 time_stamp) = 0;
+
+  // Make a shallow copy of the frame. The frame buffer itself is not copied.
+  // Both the current and new VideoFrame will share a single reference-counted
+  // frame buffer.
+  virtual VideoFrame *Copy() const = 0;
+
+  // Since VideoFrame supports shallow copy and the internal frame buffer might
+  // be shared, in case VideoFrame needs exclusive access of the frame buffer,
+  // user can call MakeExclusive() to make sure the frame buffer is exclusive
+  // accessable to the current object.  This might mean a deep copy of the frame
+  // buffer if it is currently shared by other objects.
+  virtual bool MakeExclusive() = 0;
+
+  // Writes the frame into the given frame buffer, provided that it is of
+  // sufficient size. Returns the frame's actual size, regardless of whether
+  // it was written or not (like snprintf). If there is insufficient space,
+  // 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 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.
+  virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, uint8 *buffer,
+                                    size_t size, size_t pitch_rgb) const = 0;
+
+  // Writes the frame into the given planes, stretched to the given width and
+  // height. The parameter "interpolate" controls whether to interpolate or just
+  // take the nearest-point. The parameter "crop" controls whether to crop this
+  // frame to the aspect ratio of the given dimensions before stretching.
+  virtual void StretchToPlanes(uint8 *y, uint8 *u, uint8 *v,
+                               int32 pitchY, int32 pitchU, int32 pitchV,
+                               size_t width, size_t height,
+                               bool interpolate, bool crop) const = 0;
+
+  // Writes the frame into the given frame buffer, stretched to the given width
+  // and height, provided that it is of sufficient size. Returns the frame's
+  // actual size, regardless of whether it was written or not (like snprintf).
+  // If there is insufficient space, nothing is written. The parameter
+  // "interpolate" controls whether to interpolate or just take the
+  // nearest-point. The parameter "crop" controls whether to crop this frame to
+  // the aspect ratio of the given dimensions before stretching.
+  virtual size_t StretchToBuffer(size_t w, size_t h, uint8 *buffer, size_t size,
+                                 bool interpolate, bool crop) const = 0;
+
+  // Writes the frame into the target VideoFrame, stretched to the size of that
+  // frame. The parameter "interpolate" controls whether to interpolate or just
+  // take the nearest-point. The parameter "crop" controls whether to crop this
+  // frame to the aspect ratio of the target frame before stretching.
+  virtual void StretchToFrame(VideoFrame *target, bool interpolate,
+                              bool crop) const = 0;
+
+  // Stretches the frame to the given size, creating a new VideoFrame object to
+  // hold it. The parameter "interpolate" controls whether to interpolate or
+  // just take the nearest-point. The parameter "crop" controls whether to crop
+  // this frame to the aspect ratio of the given dimensions before stretching.
+  virtual VideoFrame *Stretch(size_t w, size_t h, bool interpolate,
+                              bool crop) const = 0;
+
+  // Set the video frame to black.
+  bool SetToBlack();
+
+  // Size of an I420 image of given dimensions when stored as a frame buffer.
+  static size_t SizeOf(size_t w, size_t h) {
+    return w * h + ((w + 1) / 2) * ((h + 1) / 2) * 2;
+  }
+
+ protected:
+  // The frame needs to be rendered to magiccam only once.
+  // TODO: Remove this flag once magiccam rendering is fully replaced
+  // by client3d rendering.
+  mutable bool rendered_;
+};
+
+}  // namespace cricket
+
+#endif  // TALK_SESSION_PHONE_VIDEOFRAME_H_
diff --git a/talk/session/phone/videorenderer.h b/talk/session/phone/videorenderer.h
new file mode 100644
index 0000000..f1fe547
--- /dev/null
+++ b/talk/session/phone/videorenderer.h
@@ -0,0 +1,47 @@
+/*
+ * libjingle
+ * Copyright 2004, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_SESSION_PHONE_VIDEORENDERER_H_
+#define TALK_SESSION_PHONE_VIDEORENDERER_H_
+
+namespace cricket {
+
+class VideoFrame;
+
+// Abstract interface for rendering VideoFrames.
+class VideoRenderer {
+ public:
+  virtual ~VideoRenderer() {}
+  // Called when the video has changed size.
+  virtual bool SetSize(int width, int height, int reserved) = 0;
+  // Called when a new frame is available for display.
+  virtual bool RenderFrame(const VideoFrame *frame) = 0;
+};
+
+}  // namespace cricket
+
+#endif  // TALK_SESSION_PHONE_VIDEORENDERER_H_
diff --git a/talk/site_scons/site_tools/talk_linux.py b/talk/site_scons/site_tools/talk_linux.py
index 1e204bc..67214c0 100644
--- a/talk/site_scons/site_tools/talk_linux.py
+++ b/talk/site_scons/site_tools/talk_linux.py
@@ -221,15 +221,20 @@
                       (package, package))
   package_ccflags = _GetPackageFlags('--cflags', packages)
   package_libs = _GetPackageFlags('--libs', packages)
-  # Split package_libs into actual libs and non-lib linker flags.
+  # Split package_libs into libs, libdirs, and misc. linker flags. (In a perfect
+  # world we could just leave libdirs in link_flags, but some linkers are
+  # somehow confused by the different argument order.)
   libs = [flag[2:] for flag in package_libs if flag[0:2] == '-l']
-  link_flags = [flag for flag in package_libs if flag[0:2] != '-l']
+  libdirs = [flag[2:] for flag in package_libs if flag[0:2] == '-L']
+  link_flags = [flag for flag in package_libs if flag[0:2] not in ['-l', '-L']]
   return {
       'ccflags': package_ccflags,
       'libs': libs,
+      'libdirs': libdirs,
       'link_flags': link_flags,
       'dependent_target_settings' : {
           'libs': libs[:],
+          'libdirs': libdirs[:],
           'link_flags': link_flags[:],
       },
   }
diff --git a/talk/site_scons/talk.py b/talk/site_scons/talk.py
index e2a2166..e2ac143 100644
--- a/talk/site_scons/talk.py
+++ b/talk/site_scons/talk.py
@@ -7,16 +7,40 @@
 import os
 import SCons.Util
 
-# Keep a global dictionary of library target params for lookups in
-# ExtendComponent().
-_all_lib_targets = {}
-# Maintain a set of all prebuilt static libraries.
-_all_prebuilt_libraries = set()
-# Set of libraries not found in the above (used to detect out-of-order build
-# rules).
-_all_system_libraries = set()
+class LibraryInfo:
+  """Records information on the libraries defined in a build configuration.
 
-def _GetLibParams(lib):
+  Attributes:
+    lib_targets: Dictionary of library target params for lookups in
+        ExtendComponent().
+    prebuilt_libraries: Set of all prebuilt static libraries.
+    system_libraries: Set of libraries not found in the above (used to detect
+        out-of-order build rules).
+  """
+
+  # Dictionary of LibraryInfo objects keyed by BUILD_TYPE value.
+  __library_info = {}
+
+  @staticmethod
+  def get(env):
+    """Gets the LibraryInfo object for the current build type.
+
+    Args:
+      env: The environment object.
+
+    Returns:
+      The LibraryInfo object.
+    """
+    return LibraryInfo.__library_info.setdefault(env['BUILD_TYPE'],
+                                                 LibraryInfo())
+
+  def __init__(self):
+    self.lib_targets = {}
+    self.prebuilt_libraries = set()
+    self.system_libraries = set()
+
+
+def _GetLibParams(env, lib):
   """Gets the params for the given library if it is a library target.
 
   Returns the params that were specified when the given lib target name was
@@ -25,26 +49,29 @@
   dependencies for future targets.
 
   Args:
+    env: The environment object.
     lib: The library's name as a string.
 
   Returns:
     Its dictionary of params, or None.
   """
-  if lib in _all_lib_targets:
-    return _all_lib_targets[lib]
+  info = LibraryInfo.get(env)
+  if lib in info.lib_targets:
+    return info.lib_targets[lib]
   else:
-    if lib not in _all_prebuilt_libraries and lib not in _all_system_libraries:
-      _all_system_libraries.add(lib)
+    if lib not in info.prebuilt_libraries and lib not in info.system_libraries:
+      info.system_libraries.add(lib)
     return None
 
 
-def _RecordLibParams(lib, params):
+def _RecordLibParams(env, lib, params):
   """Record the params used for a library target.
 
   Record the params used for a library target while checking for several error
   conditions.
 
   Args:
+    env: The environment object.
     lib: The library target's name as a string.
     params: Its dictionary of params.
 
@@ -53,17 +80,18 @@
         previously declared to be prebuilt, or the lib target is being defined
         after a reverse library dependency.
   """
-  if lib in _all_lib_targets:
+  info = LibraryInfo.get(env)
+  if lib in info.lib_targets:
     raise Exception('Multiple definitions of ' + lib)
-  if lib in _all_prebuilt_libraries:
+  if lib in info.prebuilt_libraries:
     raise Exception(lib + ' already declared as a prebuilt library')
-  if lib in _all_system_libraries:
+  if lib in info.system_libraries:
     raise Exception(lib + ' cannot be defined after its reverse library '
                     'dependencies')
-  _all_lib_targets[lib] = params
+  info.lib_targets[lib] = params
 
 
-def _IsPrebuiltLibrary(lib):
+def _IsPrebuiltLibrary(env, lib):
   """Checks whether or not the given library is a prebuilt static library.
 
   Returns whether or not the given library name has been declared to be a
@@ -71,26 +99,29 @@
   negative result so as to detect out-of-order dependencies for future targets.
 
   Args:
+    env: The environment object.
     lib: The library's name as a string.
 
   Returns:
     True or False
   """
-  if lib in _all_prebuilt_libraries:
+  info = LibraryInfo.get(env)
+  if lib in info.prebuilt_libraries:
     return True
   else:
-    if lib not in _all_lib_targets and lib not in _all_system_libraries:
-      _all_system_libraries.add(lib)
+    if lib not in info.lib_targets and lib not in info.system_libraries:
+      info.system_libraries.add(lib)
     return False
 
 
-def _RecordPrebuiltLibrary(lib):
+def _RecordPrebuiltLibrary(env, lib):
   """Record that a library is a prebuilt static library.
 
   Record that the given library name refers to a prebuilt static library while
   checking for several error conditions.
 
   Args:
+    env: The environment object.
     lib: The library's name as a string.
 
   Raises:
@@ -98,14 +129,15 @@
         previously declared as a target, or the lib is being declared as
         prebuilt after a reverse library dependency.
   """
-  if lib in _all_prebuilt_libraries:
+  info = LibraryInfo.get(env)
+  if lib in info.prebuilt_libraries:
     raise Exception('Multiple prebuilt declarations of ' + lib)
-  if lib in _all_lib_targets:
+  if lib in info.lib_targets:
     raise Exception(lib + ' already defined as a target')
-  if lib in _all_system_libraries:
+  if lib in info.system_libraries:
     raise Exception(lib + ' cannot be declared as prebuilt after its reverse '
                     'library dependencies')
-  _all_prebuilt_libraries.add(lib)
+  info.prebuilt_libraries.add(lib)
 
 
 def _GenericLibrary(env, static, **kwargs):
@@ -123,20 +155,21 @@
   return ExtendComponent(env, 'ComponentLibrary', **params)
 
 
-def DeclarePrebuiltLibraries(libraries):
+def DeclarePrebuiltLibraries(env, libraries):
   """Informs the build engine about external static libraries.
 
   Informs the build engine that the given external library name(s) are prebuilt
   static libraries, as opposed to shared libraries.
 
   Args:
+    env: The environment object.
     libraries: The library or libraries that are being declared as prebuilt
         static libraries.
   """
   if not SCons.Util.is_List(libraries):
     libraries = [libraries]
   for library in libraries:
-    _RecordPrebuiltLibrary(library)
+    _RecordPrebuiltLibrary(env, library)
 
 
 def Library(env, **kwargs):
@@ -396,7 +429,7 @@
 def MergeSettingsFromLibraryDependencies(env, params):
   if 'libs' in params:
     for lib in params['libs']:
-      libparams = _GetLibParams(lib)
+      libparams = _GetLibParams(env, lib)
       if libparams:
         if 'dependent_target_settings' in libparams:
           params = CombineDicts(
@@ -440,7 +473,7 @@
 
   # save pristine params of lib targets for future reference
   if 'ComponentLibrary' == component:
-    _RecordLibParams(name, dict(params))
+    _RecordLibParams(env, name, dict(params))
 
   # add any dependent target settings from library dependencies
   params = MergeSettingsFromLibraryDependencies(env, params)
@@ -512,9 +545,9 @@
     # not track dependencies on system shared libraries anyway so we lose
     # nothing by removing them from LIBS.
     static_libs = [lib for lib in libs if
-                   _GetLibParams(lib) or _IsPrebuiltLibrary(lib)]
+                   _GetLibParams(env, lib) or _IsPrebuiltLibrary(env, lib)]
     shared_libs = ['-l' + lib for lib in libs if not
-                   (_GetLibParams(lib) or _IsPrebuiltLibrary(lib))]
+                   (_GetLibParams(env, lib) or _IsPrebuiltLibrary(env, lib))]
     env.Replace(LIBS=static_libs)
     env.Append(_LIBFLAGS=shared_libs)
 
@@ -537,7 +570,7 @@
       # link 64 bit versions of libraries
       libs = []
       for lib in env_64bit['LIBS']:
-        libparams = _GetLibParams(lib)
+        libparams = _GetLibParams(env, lib)
         if libparams and 'also64bit' in libparams:
           libs.append(lib + '64')
         else:
diff --git a/talk/xmllite/xmlelement.cc b/talk/xmllite/xmlelement.cc
index 3ec085c..bf436fb 100644
--- a/talk/xmllite/xmlelement.cc
+++ b/talk/xmllite/xmlelement.cc
@@ -26,7 +26,6 @@
  */
 
 #include <string>
-#include <iostream>
 #include <vector>
 #include <sstream>
 
diff --git a/talk/xmllite/xmlnsstack.cc b/talk/xmllite/xmlnsstack.cc
index 18e1607..b2bf370 100644
--- a/talk/xmllite/xmlnsstack.cc
+++ b/talk/xmllite/xmlnsstack.cc
@@ -2,31 +2,30 @@
  * libjingle
  * Copyright 2004--2005, Google Inc.
  *
- * Redistribution and use in source and binary forms, with or without 
+ * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
  *
- *  1. Redistributions of source code must retain the above copyright notice, 
+ *  1. Redistributions of source code must retain the above copyright notice,
  *     this list of conditions and the following disclaimer.
  *  2. Redistributions in binary form must reproduce the above copyright notice,
  *     this list of conditions and the following disclaimer in the documentation
  *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products 
+ *  3. The name of the author may not be used to endorse or promote products
  *     derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include <string>
-#include <iostream>
 #include <vector>
 #include <sstream>
 #include "talk/xmllite/xmlelement.h"
@@ -56,9 +55,6 @@
                         pxmlnsStack_->end());
   }
 }
-const std::pair<std::string, bool> NS_NOT_FOUND(STR_EMPTY, false);
-const std::pair<std::string, bool> EMPTY_NS_FOUND(STR_EMPTY, true);
-const std::pair<std::string, bool> XMLNS_DEFINITION_FOUND(NS_XMLNS, true);
 
 const std::string *
 XmlnsStack::NsForPrefix(const std::string & prefix) {
diff --git a/talk/xmllite/xmlparser.cc b/talk/xmllite/xmlparser.cc
index 5f6f7d5..b267414 100644
--- a/talk/xmllite/xmlparser.cc
+++ b/talk/xmllite/xmlparser.cc
@@ -29,7 +29,6 @@
 
 #include <string>
 #include <vector>
-#include <iostream>
 #include "talk/base/common.h"
 #include "talk/xmllite/xmlconstants.h"
 #include "talk/xmllite/xmlelement.h"
diff --git a/talk/xmllite/xmlprinter.cc b/talk/xmllite/xmlprinter.cc
index b05898f..db6704b 100644
--- a/talk/xmllite/xmlprinter.cc
+++ b/talk/xmllite/xmlprinter.cc
@@ -2,33 +2,33 @@
  * libjingle
  * Copyright 2004--2005, Google Inc.
  *
- * Redistribution and use in source and binary forms, with or without 
+ * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
  *
- *  1. Redistributions of source code must retain the above copyright notice, 
+ *  1. Redistributions of source code must retain the above copyright notice,
  *     this list of conditions and the following disclaimer.
  *  2. Redistributions in binary form must reproduce the above copyright notice,
  *     this list of conditions and the following disclaimer in the documentation
  *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products 
+ *  3. The name of the author may not be used to endorse or promote products
  *     derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <string>
-#include <iostream>
-#include <vector>
+#include <ostream>
 #include <sstream>
+#include <string>
+#include <vector>
 #include "talk/xmllite/xmlelement.h"
 #include "talk/xmllite/xmlprinter.h"
 #include "talk/xmllite/xmlnsstack.h"
diff --git a/talk/xmpp/constants.cc b/talk/xmpp/constants.cc
index 2d108ac..a1aaa63 100644
--- a/talk/xmpp/constants.cc
+++ b/talk/xmpp/constants.cc
@@ -477,6 +477,9 @@
 const QName QN_CALLPERF_REMOTEUSER(true, STR_EMPTY, "remoteUser");
 const QName QN_CALLPERF_STARTTIME(true, STR_EMPTY, "startTime");
 const QName QN_CALLPERF_CALL_LENGTH(true, STR_EMPTY, "callLength");
+const QName QN_CALLPERF_CALL_ACCEPTED(STR_EMPTY, "callAccepted");
+const QName QN_CALLPERF_CALL_ERROR_CODE(STR_EMPTY, "callErrorCode");
+const QName QN_CALLPERF_TERMINATE_CODE(STR_EMPTY, "terminateCode");
 const QName QN_CALLPERF_DATAPOINT(true, NS_GOOGLE_CALLPERF_STATS, "dataPoint");
 const QName QN_CALLPERF_DATAPOINT_TIME(true, STR_EMPTY, "timeStamp");
 const QName QN_CALLPERF_DATAPOINT_FRACTION_LOST(true, STR_EMPTY, "fraction_lost");
@@ -488,9 +491,41 @@
 const QName QN_CALLPERF_DATAPOINT_PACKETS_R(true, STR_EMPTY, "packetsReceived");
 const QName QN_CALLPERF_DATAPOINT_BYTES_S(true, STR_EMPTY, "bytesSent");
 const QName QN_CALLPERF_DATAPOINT_PACKETS_S(true, STR_EMPTY, "packetsSent");
+const QName QN_CALLPERF_DATAPOINT_PROCESS_CPU(STR_EMPTY, "processCpu");
+const QName QN_CALLPERF_DATAPOINT_SYSTEM_CPU(STR_EMPTY, "systemCpu");
+const QName QN_CALLPERF_DATAPOINT_CPUS(STR_EMPTY, "cpus");
 const QName QN_CALLPERF_CONNECTION(true, NS_GOOGLE_CALLPERF_STATS, "connection");
 const QName QN_CALLPERF_CONNECTION_LOCAL_ADDRESS(true, STR_EMPTY, "localAddress");
 const QName QN_CALLPERF_CONNECTION_REMOTE_ADDRESS(true, STR_EMPTY, "remoteAddress");
+const QName QN_CALLPERF_CONNECTION_FLAGS(STR_EMPTY, "flags");
+const QName QN_CALLPERF_CONNECTION_RTT(STR_EMPTY, "rtt");
+const QName QN_CALLPERF_CONNECTION_TOTAL_BYTES_S(
+    STR_EMPTY, "totalBytesSent");
+const QName QN_CALLPERF_CONNECTION_BYTES_SECOND_S(
+    STR_EMPTY, "bytesSecondSent");
+const QName QN_CALLPERF_CONNECTION_TOTAL_BYTES_R(
+    STR_EMPTY, "totalBytesRecv");
+const QName QN_CALLPERF_CONNECTION_BYTES_SECOND_R(
+    STR_EMPTY, "bytesSecondRecv");
+const QName QN_CALLPERF_CANDIDATE(NS_GOOGLE_CALLPERF_STATS, "candidate");
+const QName QN_CALLPERF_CANDIDATE_ENDPOINT(STR_EMPTY, "endpoint");
+const QName QN_CALLPERF_CANDIDATE_PROTOCOL(STR_EMPTY, "protocol");
+const QName QN_CALLPERF_CANDIDATE_ADDRESS(STR_EMPTY, "address");
+const QName QN_CALLPERF_MEDIA(NS_GOOGLE_CALLPERF_STATS, "media");
+const QName QN_CALLPERF_MEDIA_DIRECTION(STR_EMPTY, "direction");
+const QName QN_CALLPERF_MEDIA_SSRC(STR_EMPTY, "SSRC");
+const QName QN_CALLPERF_MEDIA_ENERGY(STR_EMPTY, "energy");
+const QName QN_CALLPERF_MEDIA_FIR(STR_EMPTY, "fir");
+const QName QN_CALLPERF_MEDIA_NACK(STR_EMPTY, "nack");
+const QName QN_CALLPERF_MEDIA_FPS(STR_EMPTY, "fps");
+const QName QN_CALLPERF_MEDIA_FPS_NETWORK(STR_EMPTY, "fpsNetwork");
+const QName QN_CALLPERF_MEDIA_FPS_DECODED(STR_EMPTY, "fpsDecoded");
+const QName QN_CALLPERF_MEDIA_JITTER_BUFFER_SIZE(
+    STR_EMPTY, "jitterBufferSize");
+const QName QN_CALLPERF_MEDIA_PREFERRED_JITTER_BUFFER_SIZE(
+    STR_EMPTY, "preferredJitterBufferSize");
+const QName QN_CALLPERF_MEDIA_TOTAL_PLAYOUT_DELAY(
+    STR_EMPTY, "totalPlayoutDelay");
 
 // Muc invites.
 const QName QN_MUC_USER_INVITE(true, NS_MUC_USER, "invite");
diff --git a/talk/xmpp/constants.h b/talk/xmpp/constants.h
index 07e8554..ecc8e00 100644
--- a/talk/xmpp/constants.h
+++ b/talk/xmpp/constants.h
@@ -428,6 +428,9 @@
 extern const QName QN_CALLPERF_REMOTEUSER;
 extern const QName QN_CALLPERF_STARTTIME;
 extern const QName QN_CALLPERF_CALL_LENGTH;
+extern const QName QN_CALLPERF_CALL_ACCEPTED;
+extern const QName QN_CALLPERF_CALL_ERROR_CODE;
+extern const QName QN_CALLPERF_TERMINATE_CODE;
 extern const QName QN_CALLPERF_DATAPOINT;
 extern const QName QN_CALLPERF_DATAPOINT_TIME;
 extern const QName QN_CALLPERF_DATAPOINT_FRACTION_LOST;
@@ -439,9 +442,34 @@
 extern const QName QN_CALLPERF_DATAPOINT_PACKETS_R;
 extern const QName QN_CALLPERF_DATAPOINT_BYTES_S;
 extern const QName QN_CALLPERF_DATAPOINT_PACKETS_S;
+extern const QName QN_CALLPERF_DATAPOINT_PROCESS_CPU;
+extern const QName QN_CALLPERF_DATAPOINT_SYSTEM_CPU;
+extern const QName QN_CALLPERF_DATAPOINT_CPUS;
 extern const QName QN_CALLPERF_CONNECTION;
 extern const QName QN_CALLPERF_CONNECTION_LOCAL_ADDRESS;
 extern const QName QN_CALLPERF_CONNECTION_REMOTE_ADDRESS;
+extern const QName QN_CALLPERF_CONNECTION_FLAGS;
+extern const QName QN_CALLPERF_CONNECTION_RTT;
+extern const QName QN_CALLPERF_CONNECTION_TOTAL_BYTES_S;
+extern const QName QN_CALLPERF_CONNECTION_BYTES_SECOND_S;
+extern const QName QN_CALLPERF_CONNECTION_TOTAL_BYTES_R;
+extern const QName QN_CALLPERF_CONNECTION_BYTES_SECOND_R;
+extern const QName QN_CALLPERF_CANDIDATE;
+extern const QName QN_CALLPERF_CANDIDATE_ENDPOINT;
+extern const QName QN_CALLPERF_CANDIDATE_PROTOCOL;
+extern const QName QN_CALLPERF_CANDIDATE_ADDRESS;
+extern const QName QN_CALLPERF_MEDIA;
+extern const QName QN_CALLPERF_MEDIA_DIRECTION;
+extern const QName QN_CALLPERF_MEDIA_SSRC;
+extern const QName QN_CALLPERF_MEDIA_ENERGY;
+extern const QName QN_CALLPERF_MEDIA_FIR;
+extern const QName QN_CALLPERF_MEDIA_NACK;
+extern const QName QN_CALLPERF_MEDIA_FPS;
+extern const QName QN_CALLPERF_MEDIA_FPS_NETWORK;
+extern const QName QN_CALLPERF_MEDIA_FPS_DECODED;
+extern const QName QN_CALLPERF_MEDIA_JITTER_BUFFER_SIZE;
+extern const QName QN_CALLPERF_MEDIA_PREFERRED_JITTER_BUFFER_SIZE;
+extern const QName QN_CALLPERF_MEDIA_TOTAL_PLAYOUT_DELAY;
 
 // Muc invites.
 extern const QName QN_MUC_USER_INVITE;
diff --git a/talk/xmpp/iqtask.cc b/talk/xmpp/iqtask.cc
index d73aada..f319990 100644
--- a/talk/xmpp/iqtask.cc
+++ b/talk/xmpp/iqtask.cc
@@ -34,8 +34,10 @@
 
 static const int kDefaultIqTimeoutSecs = 15;
 
-IqTask::IqTask(talk_base::Task* parent, const std::string& verb,
-               const buzz::Jid& to, buzz::XmlElement* el)
+IqTask::IqTask(XmppTaskParentInterface* parent,
+               const std::string& verb,
+               const buzz::Jid& to,
+               buzz::XmlElement* el)
     : buzz::XmppTask(parent, buzz::XmppEngine::HL_SINGLE),
       to_(to),
       stanza_(MakeIq(verb, to_, task_id())) {
diff --git a/talk/xmpp/iqtask.h b/talk/xmpp/iqtask.h
index 22e658d..7d9d621 100644
--- a/talk/xmpp/iqtask.h
+++ b/talk/xmpp/iqtask.h
@@ -37,7 +37,8 @@
 
 class IqTask : public buzz::XmppTask {
  public:
-  IqTask(talk_base::Task* parent, const std::string& verb, const buzz::Jid& to,
+  IqTask(XmppTaskParentInterface* parent,
+         const std::string& verb, const buzz::Jid& to,
          buzz::XmlElement* el);
   virtual ~IqTask() {}
 
diff --git a/talk/xmpp/mucroomlookuptask.cc b/talk/xmpp/mucroomlookuptask.cc
index 2cc5285..e6a204a 100644
--- a/talk/xmpp/mucroomlookuptask.cc
+++ b/talk/xmpp/mucroomlookuptask.cc
@@ -34,14 +34,14 @@
 
 namespace buzz {
 
-MucRoomLookupTask::MucRoomLookupTask(Task* parent,
+MucRoomLookupTask::MucRoomLookupTask(XmppTaskParentInterface* parent,
                                      const std::string& room_name,
                                      const std::string& organizer_domain)
     : IqTask(parent, STR_SET, Jid(STR_MUC_LOOKUP_DOMAIN),
              MakeRoomQuery(room_name, organizer_domain)) {
 }
 
-MucRoomLookupTask::MucRoomLookupTask(Task* parent,
+MucRoomLookupTask::MucRoomLookupTask(XmppTaskParentInterface* parent,
                                      const Jid& room_jid)
     : IqTask(parent, STR_SET, Jid(STR_MUC_LOOKUP_DOMAIN),
              MakeJidQuery(room_jid)) {
diff --git a/talk/xmpp/mucroomlookuptask.h b/talk/xmpp/mucroomlookuptask.h
index 9ade9fa..e8e6c76 100644
--- a/talk/xmpp/mucroomlookuptask.h
+++ b/talk/xmpp/mucroomlookuptask.h
@@ -41,9 +41,11 @@
 
 class MucRoomLookupTask : public IqTask {
  public:
-  MucRoomLookupTask(Task* parent, const std::string& room_name,
-      const std::string& organizer_domain);
-  MucRoomLookupTask(Task* parent, const Jid& room_jid);
+  MucRoomLookupTask(XmppTaskParentInterface* parent,
+                    const std::string& room_name,
+                    const std::string& organizer_domain);
+  MucRoomLookupTask(XmppTaskParentInterface* parent,
+                    const Jid& room_jid);
 
   sigslot::signal1<const MucRoomInfo&> SignalResult;
 
diff --git a/talk/xmpp/xmppclient.cc b/talk/xmpp/xmppclient.cc
index ba32908..578884a 100644
--- a/talk/xmpp/xmppclient.cc
+++ b/talk/xmpp/xmppclient.cc
@@ -36,13 +36,6 @@
 
 namespace buzz {
 
-talk_base::TaskParent* XmppClient::GetParent(int code) {
-  if (code == XMPP_CLIENT_TASK_CODE)
-    return this;
-  else
-    return talk_base::Task::GetParent(code);
-}
-
 class XmppClient::Private :
     public sigslot::has_slots<>,
     public XmppSessionHandler,
@@ -279,7 +272,7 @@
 }
 
 XmppClient::XmppClient(TaskParent * parent)
-    : Task(parent),
+    : XmppTaskParentInterface(parent),
       delivering_signal_(false),
       valid_(false) {
   d_.reset(new Private(this));
diff --git a/talk/xmpp/xmppclient.h b/talk/xmpp/xmppclient.h
index 9983794..b8f854e 100644
--- a/talk/xmpp/xmppclient.h
+++ b/talk/xmpp/xmppclient.h
@@ -31,14 +31,14 @@
 #include <string>
 #include "talk/base/basicdefs.h"
 #include "talk/base/sigslot.h"
-#include "talk/xmpp/xmppengine.h"
+#include "talk/base/task.h"
 #include "talk/xmpp/asyncsocket.h"
 #include "talk/xmpp/xmppclientsettings.h"
-#include "talk/base/task.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
 
 namespace buzz {
 
-class XmppTask;
 class PreXmppAuth;
 class CaptchaChallenge;
 
@@ -68,22 +68,22 @@
 //
 /////////////////////////////////////////////////////////////////////
 
-class XmppClient : public talk_base::Task, public sigslot::has_slots<>
+class XmppClient : public XmppTaskParentInterface,
+                   public XmppClientInterface,
+                   public sigslot::has_slots<>
 {
 public:
   explicit XmppClient(talk_base::TaskParent * parent);
-  ~XmppClient();
+  virtual ~XmppClient();
 
   XmppReturnStatus Connect(const XmppClientSettings & settings,
                            const std::string & lang,
                            AsyncSocket * socket,
                            PreXmppAuth * preauth);
 
-  virtual talk_base::TaskParent* GetParent(int code);
   virtual int ProcessStart();
   virtual int ProcessResponse();
   XmppReturnStatus Disconnect();
-  const Jid & jid();
 
   sigslot::signal1<XmppEngine::State> SignalStateChange;
   XmppEngine::State GetState();
@@ -101,30 +101,31 @@
   // (if we used GAIA authentication)
   std::string GetAuthCookie();
 
-  std::string NextId();
-  XmppReturnStatus SendStanza(const XmlElement *stanza);
   XmppReturnStatus SendRaw(const std::string & text);
-  XmppReturnStatus SendStanzaError(const XmlElement * pelOriginal,
-                       XmppStanzaError code,
-                       const std::string & text);
 
   XmppEngine* engine();
 
   sigslot::signal2<const char *, int> SignalLogInput;
   sigslot::signal2<const char *, int> SignalLogOutput;
 
-private:
+  // As XmppTaskParentIntreface
+  virtual XmppClientInterface* GetClient() { return this; }
+
+  // As XmppClientInterface
+  virtual const Jid& jid();
+  virtual std::string NextId();
+  virtual XmppReturnStatus SendStanza(const XmlElement *stanza);
+  virtual XmppReturnStatus SendStanzaError(const XmlElement * pelOriginal,
+                                           XmppStanzaError code,
+                                           const std::string & text);
+  virtual void AddXmppTask(XmppTask *, XmppEngine::HandlerLevel);
+  virtual void RemoveXmppTask(XmppTask *);
+
+ private:
   friend class XmppTask;
 
   void OnAuthDone();
 
-  // managed tasks and dispatching
-  void AddXmppTask(XmppTask *, XmppEngine::HandlerLevel);
-  void RemoveXmppTask(XmppTask *);
-
-  sigslot::signal0<> SignalDisconnected;
-
-private:
   // Internal state management
   enum {
     STATE_PRE_XMPP_LOGIN = STATE_NEXT,
diff --git a/talk/xmpp/xmpplogintask.cc b/talk/xmpp/xmpplogintask.cc
index 537818c..340d95d 100644
--- a/talk/xmpp/xmpplogintask.cc
+++ b/talk/xmpp/xmpplogintask.cc
@@ -25,7 +25,6 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <iostream>
 #include <string>
 #include <vector>
 #include "talk/base/base64.h"
diff --git a/talk/xmpp/xmpptask.cc b/talk/xmpp/xmpptask.cc
index 5c4c653..60841d4 100644
--- a/talk/xmpp/xmpptask.cc
+++ b/talk/xmpp/xmpptask.cc
@@ -35,18 +35,22 @@
 
 RateLimitManager task_rate_manager;
 
-XmppTask::XmppTask(TaskParent* parent, XmppEngine::HandlerLevel level)
-    : Task(parent), client_(NULL) {
+XmppClientInterface::XmppClientInterface() {
+}
+
+XmppClientInterface::~XmppClientInterface() {
+}
+
+XmppTask::XmppTask(XmppTaskParentInterface* parent,
+                   XmppEngine::HandlerLevel level)
+    : XmppTaskBase(parent), stopped_(false) {
 #ifdef _DEBUG
   debug_force_timeout_ = false;
 #endif
 
-  XmppClient* client =
-      static_cast<XmppClient*>(parent->GetParent(XMPP_CLIENT_TASK_CODE));
-  client_ = client;
-  id_ = client->NextId();
-  client->AddXmppTask(this, level);
-  client->SignalDisconnected.connect(this, &XmppTask::OnDisconnect);
+  id_ = GetClient()->NextId();
+  GetClient()->AddXmppTask(this, level);
+  GetClient()->SignalDisconnected.connect(this, &XmppTask::OnDisconnect);
 }
 
 XmppTask::~XmppTask() {
@@ -55,25 +59,25 @@
 
 void XmppTask::StopImpl() {
   while (NextStanza() != NULL) {}
-  if (client_) {
-    client_->RemoveXmppTask(this);
-    client_->SignalDisconnected.disconnect(this);
-    client_ = NULL;
+  if (!stopped_) {
+    GetClient()->RemoveXmppTask(this);
+    GetClient()->SignalDisconnected.disconnect(this);
+    stopped_ = true;
   }
 }
 
 XmppReturnStatus XmppTask::SendStanza(const XmlElement* stanza) {
-  if (client_ == NULL)
+  if (stopped_)
     return XMPP_RETURN_BADSTATE;
-  return client_->SendStanza(stanza);
+  return GetClient()->SendStanza(stanza);
 }
 
 XmppReturnStatus XmppTask::SendStanzaError(const XmlElement* element_original,
                                            XmppStanzaError code,
                                            const std::string& text) {
-  if (client_ == NULL)
+  if (stopped_)
     return XMPP_RETURN_BADSTATE;
-  return client_->SendStanzaError(element_original, code, text);
+  return GetClient()->SendStanzaError(element_original, code, text);
 }
 
 void XmppTask::Stop() {
@@ -152,7 +156,7 @@
 
   // It is legal for the server to identify itself with "domain" or
   // "myself@domain"
-  Jid me = client_->jid();
+  Jid me = GetClient()->jid();
   return (from == Jid(me.domain())) || (from == me.BareJid());
 }
 
diff --git a/talk/xmpp/xmpptask.h b/talk/xmpp/xmpptask.h
index 86fbe27..fd82c79 100644
--- a/talk/xmpp/xmpptask.h
+++ b/talk/xmpp/xmpptask.h
@@ -31,8 +31,9 @@
 #include <string>
 #include <deque>
 #include "talk/base/sigslot.h"
-#include "talk/xmpp/xmppengine.h"
 #include "talk/base/task.h"
+#include "talk/base/taskparent.h"
+#include "talk/xmpp/xmppengine.h"
 
 namespace buzz {
 
@@ -61,19 +62,76 @@
 //
 /////////////////////////////////////////////////////////////////////
 
-class XmppClient;
+class XmppTask;
 
-class XmppTask :
-  public talk_base::Task,
-  public XmppStanzaHandler,
-  public sigslot::has_slots<>
+// XmppClientInterface is an abstract interface for sending and
+// handling stanzas.  It can be implemented for unit tests or
+// different network environments.  It will usually be implemented by
+// XmppClient.
+class XmppClientInterface {
+ public:
+  XmppClientInterface();
+  virtual ~XmppClientInterface();
+
+  virtual const Jid& jid() = 0;
+  virtual std::string NextId() = 0;
+  virtual XmppReturnStatus SendStanza(const XmlElement* stanza) = 0;
+  virtual XmppReturnStatus SendStanzaError(const XmlElement* original_stanza,
+                                           XmppStanzaError error_code,
+                                           const std::string& message) = 0;
+  virtual void AddXmppTask(XmppTask* task, XmppEngine::HandlerLevel level) = 0;
+  virtual void RemoveXmppTask(XmppTask* task) = 0;
+  sigslot::signal0<> SignalDisconnected;
+
+  DISALLOW_EVIL_CONSTRUCTORS(XmppClientInterface);
+};
+
+// XmppTaskParentInterface is the interface require for any parent of
+// an XmppTask.  It needs, for example, a way to get an
+// XmppClientInterface.
+
+// We really ought to inherit from a TaskParentInterface, but we tried
+// that and it's way too complicated to change
+// Task/TaskParent/TaskRunner.  For now, this works.
+class XmppTaskParentInterface : public talk_base::Task {
+ public:
+  explicit XmppTaskParentInterface(talk_base::TaskParent* parent)
+      : Task(parent) {
+  }
+  virtual ~XmppTaskParentInterface() {}
+
+  virtual XmppClientInterface* GetClient() = 0;
+
+  DISALLOW_EVIL_CONSTRUCTORS(XmppTaskParentInterface);
+};
+
+class XmppTaskBase : public XmppTaskParentInterface {
+ public:
+  explicit XmppTaskBase(XmppTaskParentInterface* parent)
+      : XmppTaskParentInterface(parent),
+        parent_(parent) {
+  }
+  virtual ~XmppTaskBase() {}
+
+  virtual XmppClientInterface* GetClient() {
+    return parent_->GetClient();
+  }
+
+ protected:
+  XmppTaskParentInterface* parent_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(XmppTaskBase);
+};
+
+class XmppTask : public XmppTaskBase,
+                 public XmppStanzaHandler,
+                 public sigslot::has_slots<>
 {
  public:
-  XmppTask(talk_base::TaskParent* parent,
+  XmppTask(XmppTaskParentInterface* parent,
            XmppEngine::HandlerLevel level = XmppEngine::HL_NONE);
   virtual ~XmppTask();
 
-  virtual XmppClient* GetClient() const { return client_; }
   std::string task_id() const { return id_; }
   void set_task_id(std::string id) { id_ = id; }
 
@@ -81,9 +139,9 @@
   void set_debug_force_timeout(const bool f) { debug_force_timeout_ = f; }
 #endif
 
- protected:
-  friend class XmppClient;
+  virtual bool HandleStanza(const XmlElement* stanza) { return false; }
 
+ protected:
   XmppReturnStatus SendStanza(const XmlElement* stanza);
   XmppReturnStatus SetResult(const std::string& code);
   XmppReturnStatus SendStanzaError(const XmlElement* element_original,
@@ -91,7 +149,6 @@
                                    const std::string& text);
 
   virtual void Stop();
-  virtual bool HandleStanza(const XmlElement* stanza) { return false; }
   virtual void OnDisconnect();
 
   virtual void QueueStanza(const XmlElement* stanza);
@@ -116,7 +173,7 @@
 private:
   void StopImpl();
 
-  XmppClient* client_;
+  bool stopped_;
   std::deque<XmlElement*> stanza_queue_;
   talk_base::scoped_ptr<XmlElement> next_stanza_;
   std::string id_;