Update to 0.5.3.

git-svn-id: http://libjingle.googlecode.com/svn/trunk@60 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/CHANGELOG b/CHANGELOG
index e0c746e..73ffc71 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,11 @@
 Libjingle
 
+0.5.3 - May 10, 2011
+  - Stream notification and selection.
+  - Better XEP-0045 support.
+  - Easier to create composite media engines where one part is fake.
+  - Make GtkVideoRenderer thread-safe.
+
 0.5.2 - Jan 11, 2010
   - Fixed build on Windows 7 with VS 2010
   - Fixed build on Windows x64
diff --git a/talk/base/linux.cc b/talk/base/linux.cc
index b9f657e..056e0a0 100644
--- a/talk/base/linux.cc
+++ b/talk/base/linux.cc
@@ -129,6 +129,17 @@
 #endif
 }
 
+bool ProcCpuInfo::GetCpuFamily(int* id) {
+  int cpu_family = 0;
+
+  GetSectionIntValue(0, "cpu family", &cpu_family);
+
+  if (id) {
+    *id = cpu_family;
+  }
+  return true;
+}
+
 bool ProcCpuInfo::GetSectionStringValue(size_t section_num,
                                         const std::string& key,
                                         std::string* result) {
diff --git a/talk/base/linux.h b/talk/base/linux.h
index 32f2cb6..c6151a4 100644
--- a/talk/base/linux.h
+++ b/talk/base/linux.h
@@ -100,6 +100,9 @@
   // Obtains the number of physical CPU cores and places the value num.
   virtual bool GetNumPhysicalCpus(int* num);
 
+  // Obtains the CPU family id.
+  virtual bool GetCpuFamily(int* id);
+
   // Obtains the number of sections in /proc/cpuinfo, which may be greater
   // than the number of CPUs (e.g. on ARM)
   virtual bool GetSectionCount(size_t* count);
diff --git a/talk/base/nethelpers.cc b/talk/base/nethelpers.cc
index b877d78..b408749 100644
--- a/talk/base/nethelpers.cc
+++ b/talk/base/nethelpers.cc
@@ -57,22 +57,8 @@
   }
 }
 
-// The functions below are used to do gethostbyname, but with an allocated
-// instead of a static buffer.
-hostent* SafeGetHostByName(const char* hostname, int* herrno) {
-  if (NULL == hostname || NULL == herrno) {
-    return NULL;
-  }
-  hostent* result = NULL;
-#if defined(WIN32)
-  // On Windows we have to allocate a buffer, and manually copy the hostent,
-  // along with its embedded pointers.
-  hostent* ent = gethostbyname(hostname);
-  if (!ent) {
-    *herrno = WSAGetLastError();
-    return NULL;
-  }
-
+#if defined(WIN32) || defined(ANDROID)
+static hostent* DeepCopyHostent(const hostent* ent) {
   // Get the total number of bytes we need to copy, and allocate our buffer.
   int num_aliases = 0, num_addrs = 0;
   int total_len = sizeof(hostent);
@@ -88,7 +74,7 @@
   }
   total_len += sizeof(char*);
 
-  result = static_cast<hostent*>(malloc(total_len));
+  hostent* result = static_cast<hostent*>(malloc(total_len));
   if (NULL == result) {
     return NULL;
   }
@@ -119,7 +105,27 @@
     p += ent->h_length;
   }
   result->h_addr_list[num_addrs] = NULL;
+  
+  return result;
+}
+#endif
 
+// The functions below are used to do gethostbyname, but with an allocated
+// instead of a static buffer.
+hostent* SafeGetHostByName(const char* hostname, int* herrno) {
+  if (NULL == hostname || NULL == herrno) {
+    return NULL;
+  }
+  hostent* result = NULL;
+#if defined(WIN32)
+  // On Windows we have to allocate a buffer, and manually copy the hostent,
+  // along with its embedded pointers.
+  hostent* ent = gethostbyname(hostname);
+  if (!ent) {
+    *herrno = WSAGetLastError();
+    return NULL;
+  }
+  result = DeepCopyHostent(ent);
   *herrno = 0;
 #elif defined(LINUX) || defined(ANDROID)
   // gethostbyname() is not thread safe, so we need to call gethostbyname_r()
@@ -150,6 +156,14 @@
     free(buf);
     return NULL;
   }
+#if defined(ANDROID)
+  // Note that Android's version of gethostbyname_r has a bug such that the
+  // returned hostent contains pointers into thread-local storage.  (See bug
+  // 4383723.)  So we deep copy the result before returning.
+  hostent* deep_copy = DeepCopyHostent(result);
+  FreeHostEnt(result);
+  result = deep_copy;
+#endif
   *herrno = 0;
 #elif defined(OSX) || defined(IOS)
   // Mac OS returns an object with everything allocated.
diff --git a/talk/base/sigslot.h b/talk/base/sigslot.h
index e9b85c7..e4af30e 100644
--- a/talk/base/sigslot.h
+++ b/talk/base/sigslot.h
@@ -78,8 +78,9 @@
 #ifndef TALK_BASE_SIGSLOT_H__
 #define TALK_BASE_SIGSLOT_H__
 
-#include <set>
 #include <list>
+#include <set>
+#include <stdlib.h>
 
 // On our copy of sigslot.h, we force single threading
 #define SIGSLOT_PURE_ISO
diff --git a/talk/examples/call/callclient.cc b/talk/examples/call/callclient.cc
index 2bb51cd..0dd58a9 100644
--- a/talk/examples/call/callclient.cc
+++ b/talk/examples/call/callclient.cc
@@ -225,6 +225,7 @@
       pmuc_domain_("groupchat.google.com"),
       local_renderer_(NULL),
       remote_renderer_(NULL),
+      static_views_accumulated_count_(0),
       roster_(new RosterMap),
       portallocator_flags_(0),
       allow_local_ips_(false),
@@ -277,6 +278,7 @@
       delete local_renderer_;
       local_renderer_ = NULL;
     }
+    RemoveAllStaticRenderedViews();
     console_->SetPrompt(NULL);
     console_->Print("call destroyed");
     call_ = NULL;
@@ -371,6 +373,8 @@
 
 void CallClient::OnCallCreate(cricket::Call* call) {
   call->SignalSessionState.connect(this, &CallClient::OnSessionState);
+  call->SignalMediaSourcesUpdate.connect(
+      this, &CallClient::OnMediaSourcesUpdate);
 }
 
 void CallClient::OnSessionState(cricket::Call* call,
@@ -574,23 +578,13 @@
     call_ = media_client_->CreateCall();
     console_->SetPrompt(jid.Str().c_str());
     session_ = call_->InitiateSession(jid, options);
-    if (options.is_muc) {
-      // If people in this room are already in a call, must add all their
-      // streams.
-      buzz::Muc::MemberMap& members = mucs_[jid]->members();
-      for (buzz::Muc::MemberMap::iterator elem = members.begin();
-           elem != members.end();
-           ++elem) {
-        AddStream(elem->second.audio_src_id(), elem->second.video_src_id());
-      }
-    }
   }
   media_client_->SetFocus(call_);
   if (call_->video()) {
-    call_->SetLocalRenderer(local_renderer_);
-    // TODO: Call this once for every different remote SSRC
-    // once we get to testing multiway video.
-    call_->SetVideoRenderer(session_, 0, remote_renderer_);
+    if (!options.is_muc) {
+      call_->SetLocalRenderer(local_renderer_);
+      call_->SetVideoRenderer(session_, 0, remote_renderer_);
+    }
   }
 }
 
@@ -619,20 +613,6 @@
   console_->Printf("Unable to voicemail %s.\n", to.Str().c_str());
 }
 
-void CallClient::AddStream(uint32 audio_src_id, uint32 video_src_id) {
-  if (audio_src_id || video_src_id) {
-    console_->Printf("Adding stream (%u, %u)\n", audio_src_id, video_src_id);
-    call_->AddStream(session_, audio_src_id, video_src_id);
-  }
-}
-
-void CallClient::RemoveStream(uint32 audio_src_id, uint32 video_src_id) {
-  if (audio_src_id || video_src_id) {
-    console_->Printf("Removing stream (%u, %u)\n", audio_src_id, video_src_id);
-    call_->RemoveStream(session_, audio_src_id, video_src_id);
-  }
-}
-
 void CallClient::Accept(const cricket::CallOptions& options) {
   ASSERT(call_ && incoming_call_);
   ASSERT(call_->sessions().size() == 1);
@@ -744,53 +724,8 @@
   }
 
   if (!status.available()) {
-    // User is leaving the room.
-    buzz::Muc::MemberMap::iterator elem =
-      muc->members().find(status.jid().resource());
-
-    ASSERT(elem != muc->members().end());
-
-    // If user had src-ids, they have the left the room without explicitly
-    // hanging-up; must tear down the stream if in a call to this room.
-    if (call_ && session_->remote_name() == muc->jid().Str()) {
-      RemoveStream(elem->second.audio_src_id(), elem->second.video_src_id());
-    }
-
     // Remove them from the room.
-    muc->members().erase(elem);
-  } else {
-    // Either user has joined or something changed about them.
-    // Note: The [] operator here will create a new entry if it does not
-    // exist, which is what we want.
-    buzz::MucStatus& member_status(
-        muc->members()[status.jid().resource()]);
-    if (call_ && session_->remote_name() == muc->jid().Str()) {
-      // We are in a call to this muc. Must potentially update our streams.
-      // The following code will correctly update our streams regardless of
-      // whether the SSRCs have been removed, added, or changed and regardless
-      // of whether that has been done to both or just one. This relies on the
-      // fact that AddStream/RemoveStream do nothing for SSRC arguments that are
-      // zero.
-      uint32 remove_audio_src_id = 0;
-      uint32 remove_video_src_id = 0;
-      uint32 add_audio_src_id = 0;
-      uint32 add_video_src_id = 0;
-      if (member_status.audio_src_id() != status.audio_src_id()) {
-        remove_audio_src_id = member_status.audio_src_id();
-        add_audio_src_id = status.audio_src_id();
-      }
-      if (member_status.video_src_id() != status.video_src_id()) {
-        remove_video_src_id = member_status.video_src_id();
-        add_video_src_id = status.video_src_id();
-      }
-      // Remove the old SSRCs, if any.
-      RemoveStream(remove_audio_src_id, remove_video_src_id);
-      // Add the new SSRCs, if any.
-      AddStream(add_audio_src_id, add_video_src_id);
-    }
-    // Update the status. This will use the compiler-generated copy
-    // constructor, which is perfectly adequate for this class.
-    member_status = status;
+    muc->members().erase(status.jid().resource());
   }
 }
 
@@ -905,3 +840,66 @@
 void CallClient::SetVolume(const std::string& level) {
   media_client_->SetOutputVolume(strtol(level.c_str(), NULL, 10));
 }
+
+void CallClient::OnMediaSourcesUpdate(cricket::Call* call,
+                                      cricket::Session* session,
+                                      const cricket::MediaSources& sources) {
+  for (cricket::NamedSources::const_iterator it = sources.video.begin();
+       it != sources.video.end(); ++it) {
+    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);
+    }
+  }
+
+  SendViewRequest(session);
+}
+
+// TODO: Would these methods to add and remove views make
+// more sense in call.cc?  Would other clients use them?
+void CallClient::AddStaticRenderedView(
+    cricket::Session* session,
+    uint32 ssrc, int width, int height, int framerate,
+    int x_offset, int y_offset) {
+  StaticRenderedView rendered_view(
+      cricket::StaticVideoView(ssrc, width, height, framerate),
+      cricket::VideoRendererFactory::CreateGuiVideoRenderer(
+          x_offset, y_offset));
+  rendered_view.renderer->SetSize(width, height, 0);
+  call_->SetVideoRenderer(session, ssrc, rendered_view.renderer);
+  static_rendered_views_.push_back(rendered_view);
+  ++static_views_accumulated_count_;
+}
+
+bool CallClient::RemoveStaticRenderedView(uint32 ssrc) {
+  for (StaticRenderedViews::iterator it = static_rendered_views_.begin();
+       it != static_rendered_views_.end(); ++it) {
+    if (it->view.ssrc == ssrc) {
+      delete it->renderer;
+      static_rendered_views_.erase(it);
+      return true;
+    }
+  }
+  return false;
+}
+
+void CallClient::RemoveAllStaticRenderedViews() {
+  for (StaticRenderedViews::iterator it = static_rendered_views_.begin();
+       it != static_rendered_views_.end(); ++it) {
+    delete it->renderer;
+  }
+  static_rendered_views_.clear();
+}
+
+void CallClient::SendViewRequest(cricket::Session* session) {
+  cricket::ViewRequest request;
+  for (StaticRenderedViews::iterator it = static_rendered_views_.begin();
+       it != static_rendered_views_.end(); ++it) {
+    request.static_video_views.push_back(it->view);
+  }
+  call_->SendViewRequest(session, request);
+}
diff --git a/talk/examples/call/callclient.h b/talk/examples/call/callclient.h
index 12456cd..4c2da17 100644
--- a/talk/examples/call/callclient.h
+++ b/talk/examples/call/callclient.h
@@ -74,6 +74,19 @@
   std::string status;
 };
 
+struct StaticRenderedView {
+  StaticRenderedView(const cricket::StaticVideoView& view,
+                     cricket::VideoRenderer* renderer) :
+      view(view),
+      renderer(renderer) {
+  }
+
+  cricket::StaticVideoView view;
+  cricket::VideoRenderer* renderer;
+};
+
+typedef std::vector<StaticRenderedView> StaticRenderedViews;
+
 class CallClient: public sigslot::has_slots<> {
  public:
   explicit CallClient(buzz::XmppClient* xmpp_client);
@@ -144,6 +157,18 @@
   void OnDevicesChange();
   void OnFoundVoicemailJid(const buzz::Jid& to, const buzz::Jid& voicemail);
   void OnVoicemailJidError(const buzz::Jid& to);
+  void OnMediaSourcesUpdate(cricket::Call* call,
+                            cricket::Session* session,
+                            const cricket::MediaSources& sources);
+
+  void AddStaticRenderedView(
+      cricket::Session* session,
+      uint32 ssrc, int width, int height, int framerate,
+      int x_offset, int y_offset);
+  bool RemoveStaticRenderedView(uint32 ssrc);
+  void RemoveAllStaticRenderedViews();
+  void SendViewRequest(cricket::Session* session);
+
 
   static const std::string strerror(buzz::XmppEngine::Error err);
 
@@ -180,6 +205,8 @@
   std::string pmuc_domain_;
   cricket::VideoRenderer* local_renderer_;
   cricket::VideoRenderer* remote_renderer_;
+  StaticRenderedViews static_rendered_views_;
+  uint32 static_views_accumulated_count_;
 
   buzz::Status my_status_;
   buzz::PresencePushTask* presence_push_;
diff --git a/talk/examples/call/presencepushtask.cc b/talk/examples/call/presencepushtask.cc
index f820030..ca7416d 100644
--- a/talk/examples/call/presencepushtask.cc
+++ b/talk/examples/call/presencepushtask.cc
@@ -213,42 +213,7 @@
 
 void PresencePushTask::FillMucStatus(const Jid& from, const XmlElement* stanza,
                                      MucStatus* s) {
-  // First get the normal user status info. Happily, this is in the same
-  // format as it is for user presence.
   FillStatus(from, stanza, s);
-
-  // Now look for src IDs, which will be present if this user is in a
-  // multiway call to this MUC.
-  const XmlElement* xstanza = stanza->FirstNamed(QN_MUC_USER_X);
-  if (xstanza) {
-    const XmlElement* media;
-    for (media = xstanza->FirstNamed(QN_GOOGLE_MUC_USER_MEDIA);
-        media; media = media->NextNamed(QN_GOOGLE_MUC_USER_MEDIA)) {
-
-      const XmlElement* type = media->FirstNamed(QN_GOOGLE_MUC_USER_TYPE);
-      if (!type) continue; // Shouldn't happen
-
-      const XmlElement* src_id = media->FirstNamed(QN_GOOGLE_MUC_USER_SRC_ID);
-      if (!src_id) continue; // Shouldn't happen
-
-      char *endptr;
-      uint32 src_id_num = strtoul(src_id->BodyText().c_str(), &endptr, 10);
-      if (src_id->BodyText().c_str()[0] == '\0' || endptr[0] != '\0') {
-        // String is not composed exclusively of leading whitespace plus a
-        // number (shouldn't happen). Ignore it.
-        continue;
-      }
-      // Else it's valid. Set it.
-
-      if (type->BodyText() == "audio") {
-        // This is the audio media element. Get the src-id.
-        s->set_audio_src_id(src_id_num);
-      } else if (type->BodyText() == "video") {
-        // This is the video media element. Get the src-id.
-        s->set_video_src_id(src_id_num);
-      }
-    }
-  }
 }
 
 }
diff --git a/talk/examples/call/status.h b/talk/examples/call/status.h
index be4c2bd..92fe88c 100644
--- a/talk/examples/call/status.h
+++ b/talk/examples/call/status.h
@@ -224,19 +224,6 @@
 };
 
 class MucStatus : public Status {
-public:
-  MucStatus() : audio_src_id_(0), video_src_id_(0) {}
-  uint32 audio_src_id() const { return audio_src_id_; }
-  uint32 video_src_id() const { return video_src_id_; }
-  void set_audio_src_id(uint32 audio_src_id) {
-    audio_src_id_ = audio_src_id;
-  }
-  void set_video_src_id(uint32 video_src_id) {
-    video_src_id_ = video_src_id;
-  }
-private:
-  uint32 audio_src_id_;
-  uint32 video_src_id_;
 };
 
 }
diff --git a/talk/libjingle.scons b/talk/libjingle.scons
index 5d77429..cc7947c 100644
--- a/talk/libjingle.scons
+++ b/talk/libjingle.scons
@@ -183,6 +183,7 @@
                "session/phone/devicemanager.cc",
                "session/phone/filemediaengine.cc",
                "session/phone/mediaengine.cc",
+               "session/phone/mediamessages.cc",
                "session/phone/mediamonitor.cc",
                "session/phone/mediasessionclient.cc",
                "session/phone/rtpdump.cc",
diff --git a/talk/p2p/base/constants.cc b/talk/p2p/base/constants.cc
index b069c74..e7025a0 100644
--- a/talk/p2p/base/constants.cc
+++ b/talk/p2p/base/constants.cc
@@ -34,6 +34,7 @@
 
 const std::string NS_EMPTY("");
 const std::string NS_JINGLE("urn:xmpp:jingle:1");
+const std::string NS_JINGLE_DRAFT("google:jingle");
 const std::string NS_GINGLE("http://www.google.com/session");
 
 // actions (aka <session> or <jingle>)
@@ -64,9 +65,7 @@
 const std::string GINGLE_ACTION_REJECT("reject");
 const std::string GINGLE_ACTION_TERMINATE("terminate");
 const std::string GINGLE_ACTION_CANDIDATES("candidates");
-const std::string GINGLE_ACTION_NOTIFY("notify");
 const std::string GINGLE_ACTION_UPDATE("update");
-const std::string GINGLE_ACTION_VIEW("view");
 
 const std::string LN_ERROR("error");
 const buzz::QName QN_GINGLE_REDIRECT(true, NS_GINGLE, "redirect");
@@ -194,33 +193,33 @@
 const std::string STR_TERMINATE_INTERNAL_SERVER_ERROR("internal-server-error");
 const std::string STR_TERMINATE_UNKNOWN_ERROR("unknown-error");
 
-// Session notify messages
-const buzz::QName QN_GINGLE_NOTIFY(true, NS_GINGLE, "notify");
-const buzz::QName QN_GINGLE_NOTIFY_NICK(true, cricket::NS_EMPTY, "nick");
-const buzz::QName QN_GINGLE_NOTIFY_SOURCE(true, NS_GINGLE, "source");
-const buzz::QName QN_GINGLE_NOTIFY_SOURCE_MTYPE(
-    true, cricket::NS_EMPTY, "mtype");
-const buzz::QName QN_GINGLE_NOTIFY_SOURCE_SSRC(true, cricket::NS_EMPTY, "ssrc");
-const std::string GINGLE_NOTIFY_SOURCE_MTYPE_AUDIO("audio");
-const std::string GINGLE_NOTIFY_SOURCE_MTYPE_VIDEO("video");
-
-// Session view messages
-const buzz::QName QN_GINGLE_VIEW(true, cricket::NS_EMPTY, "view");
-const buzz::QName QN_GINGLE_VIEW_TYPE(true, cricket::NS_EMPTY, "type");
-const buzz::QName QN_GINGLE_VIEW_NICK(true, cricket::NS_EMPTY, "nick");
-const buzz::QName QN_GINGLE_VIEW_MEDIA_TYPE(true, cricket::NS_EMPTY, "mtype");
-const buzz::QName QN_GINGLE_VIEW_SSRC(true, cricket::NS_EMPTY, "ssrc");
-const std::string GINGLE_VIEW_TYPE_STATIC("static");
-const std::string GINGLE_VIEW_TYPE_DYNAMIC("dynamic");
-const std::string GINGLE_VIEW_MEDIA_TYPE_AUDIO("audio");
-const std::string GINGLE_VIEW_MEDIA_TYPE_VIDEO("video");
-const buzz::QName QN_GINGLE_VIEW_PARAMS(true, cricket::NS_EMPTY, "params");
-const buzz::QName QN_GINGLE_VIEW_PARAMS_WIDTH(true, cricket::NS_EMPTY, "width");
-const buzz::QName QN_GINGLE_VIEW_PARAMS_HEIGHT(
+// Draft view and notify messages.
+const buzz::QName QN_JINGLE_DRAFT_CONTENT_NAME(true, cricket::NS_EMPTY, "name");
+const std::string STR_JINGLE_DRAFT_CONTENT_NAME_VIDEO("video");
+const std::string STR_JINGLE_DRAFT_CONTENT_NAME_AUDIO("audio");
+const buzz::QName QN_JINGLE_DRAFT_NOTIFY(true, NS_JINGLE_DRAFT, "notify");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE(
+    true, NS_JINGLE_DRAFT, "source");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_NICK(true, cricket::NS_EMPTY, "nick");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_NAME(true, cricket::NS_EMPTY, "name");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_USAGE(true, cricket::NS_EMPTY, "usage");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_STATE(true, cricket::NS_EMPTY, "state");
+const std::string STR_JINGLE_DRAFT_SOURCE_STATE_REMOVED("removed");
+const buzz::QName QN_JINGLE_DRAFT_SOURCE_SSRC(true, NS_JINGLE_DRAFT, "ssrc");
+const buzz::QName QN_JINGLE_DRAFT_VIEW(true, NS_JINGLE_DRAFT, "view");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_TYPE(true, cricket::NS_EMPTY, "type");
+const std::string STR_JINGLE_DRAFT_VIEW_TYPE_NONE("none");
+const std::string STR_JINGLE_DRAFT_VIEW_TYPE_STATIC("static");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_SSRC(true, cricket::NS_EMPTY, "ssrc");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS(true, NS_JINGLE_DRAFT, "params");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_WIDTH(
+    true, cricket::NS_EMPTY, "width");
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_HEIGHT(
     true, cricket::NS_EMPTY, "height");
-const buzz::QName QN_GINGLE_VIEW_PARAMS_FRAMERATE(
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_FRAMERATE(
     true, cricket::NS_EMPTY, "framerate");
-
+const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_PREFERENCE(
+    true, cricket::NS_EMPTY, "preference");
 
 // old stuff
 #ifdef FEATURE_ENABLE_VOICEMAIL
diff --git a/talk/p2p/base/constants.h b/talk/p2p/base/constants.h
index 9dd4d24..5ffeca8 100644
--- a/talk/p2p/base/constants.h
+++ b/talk/p2p/base/constants.h
@@ -44,6 +44,7 @@
 
 extern const std::string NS_EMPTY;
 extern const std::string NS_JINGLE;
+extern const std::string NS_JINGLE_DRAFT;
 extern const std::string NS_GINGLE;
 
 enum SignalingProtocol {
@@ -80,9 +81,7 @@
 extern const std::string GINGLE_ACTION_REJECT;
 extern const std::string GINGLE_ACTION_TERMINATE;
 extern const std::string GINGLE_ACTION_CANDIDATES;
-extern const std::string GINGLE_ACTION_NOTIFY;
 extern const std::string GINGLE_ACTION_UPDATE;
-extern const std::string GINGLE_ACTION_VIEW;
 
 extern const std::string LN_ERROR;
 extern const buzz::QName QN_GINGLE_REDIRECT;
@@ -209,29 +208,29 @@
 extern const std::string STR_TERMINATE_INTERNAL_SERVER_ERROR;
 extern const std::string STR_TERMINATE_UNKNOWN_ERROR;
 
-// Session notify messages
-extern const buzz::QName QN_GINGLE_NOTIFY;
-extern const buzz::QName QN_GINGLE_NOTIFY_NICK;
-extern const buzz::QName QN_GINGLE_NOTIFY_SOURCE;
-extern const buzz::QName QN_GINGLE_NOTIFY_SOURCE_MTYPE;
-extern const buzz::QName QN_GINGLE_NOTIFY_SOURCE_SSRC;
-extern const std::string GINGLE_NOTIFY_SOURCE_MTYPE_AUDIO;
-extern const std::string GINGLE_NOTIFY_SOURCE_MTYPE_VIDEO;
-
-// Session view messages
-extern const buzz::QName QN_GINGLE_VIEW;
-extern const buzz::QName QN_GINGLE_VIEW_TYPE;
-extern const buzz::QName QN_GINGLE_VIEW_NICK;
-extern const buzz::QName QN_GINGLE_VIEW_MEDIA_TYPE;
-extern const buzz::QName QN_GINGLE_VIEW_SSRC;
-extern const std::string GINGLE_VIEW_TYPE_STATIC;
-extern const std::string GINGLE_VIEW_TYPE_DYNAMIC;
-extern const std::string GINGLE_VIEW_MEDIA_TYPE_AUDIO;
-extern const std::string GINGLE_VIEW_MEDIA_TYPE_VIDEO;
-extern const buzz::QName QN_GINGLE_VIEW_PARAMS;
-extern const buzz::QName QN_GINGLE_VIEW_PARAMS_WIDTH;
-extern const buzz::QName QN_GINGLE_VIEW_PARAMS_HEIGHT;
-extern const buzz::QName QN_GINGLE_VIEW_PARAMS_FRAMERATE;
+// Draft view and notify messages.
+extern const buzz::QName QN_JINGLE_DRAFT_CONTENT_NAME;
+extern const std::string STR_JINGLE_DRAFT_CONTENT_NAME_VIDEO;
+extern const std::string STR_JINGLE_DRAFT_CONTENT_NAME_AUDIO;
+extern const buzz::QName QN_JINGLE_DRAFT_NOTIFY;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_NICK;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_NAME;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_USAGE;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_STATE;
+extern const std::string STR_JINGLE_DRAFT_SOURCE_STATE_REMOVED;
+extern const buzz::QName QN_JINGLE_DRAFT_SOURCE_SSRC;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_NAME;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_TYPE;
+extern const std::string STR_JINGLE_DRAFT_VIEW_TYPE_NONE;
+extern const std::string STR_JINGLE_DRAFT_VIEW_TYPE_STATIC;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_SSRC;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_WIDTH;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_HEIGHT;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_FRAMERATE;
+extern const buzz::QName QN_JINGLE_DRAFT_VIEW_PARAMS_PREFERENCE;
 
 // old stuff
 #ifdef FEATURE_ENABLE_VOICEMAIL
diff --git a/talk/p2p/base/p2ptransportchannel.cc b/talk/p2p/base/p2ptransportchannel.cc
index c5d4a53..17f52a7 100644
--- a/talk/p2p/base/p2ptransportchannel.cc
+++ b/talk/p2p/base/p2ptransportchannel.cc
@@ -169,6 +169,7 @@
     transport_(transport),
     allocator_(allocator),
     worker_thread_(talk_base::Thread::Current()),
+    incoming_only_(false),
     waiting_for_signaling_(false),
     error_(0),
     best_connection_(NULL),
@@ -282,8 +283,9 @@
 
   std::vector<RemoteCandidate>::iterator iter;
   for (iter = remote_candidates_.begin(); iter != remote_candidates_.end();
-       ++iter)
+       ++iter) {
     CreateConnection(port, *iter, iter->origin_port(), false);
+  }
 
   SortConnections();
 }
@@ -419,6 +421,12 @@
     }
   } else {
     Port::CandidateOrigin origin = GetOrigin(port, origin_port);
+
+    // Don't create connection if this is a candidate we received in a
+    // message and we are not allowed to make outgoing connections.
+    if (origin == cricket::Port::ORIGIN_MESSAGE && incoming_only_)
+      return false;
+
     connection = port->CreateConnection(remote_candidate, origin);
     if (!connection)
       return false;
diff --git a/talk/p2p/base/p2ptransportchannel.h b/talk/p2p/base/p2ptransportchannel.h
index 805e159..0083d23 100644
--- a/talk/p2p/base/p2ptransportchannel.h
+++ b/talk/p2p/base/p2ptransportchannel.h
@@ -93,6 +93,8 @@
   const std::vector<Connection *>& connections() const { return connections_; }
   Connection* best_connection() const { return best_connection_; }
 
+  void set_incoming_only(bool value) { incoming_only_ = value; }
+
   // Handler for internal messages.
   virtual void OnMessage(talk_base::Message *pmsg);
 
@@ -141,6 +143,7 @@
   P2PTransport* transport_;
   PortAllocator *allocator_;
   talk_base::Thread *worker_thread_;
+  bool incoming_only_;
   bool waiting_for_signaling_;
   int error_;
   std::vector<PortAllocatorSession*> allocator_sessions_;
diff --git a/talk/p2p/base/parsing.h b/talk/p2p/base/parsing.h
index 4143c2c..7931431 100644
--- a/talk/p2p/base/parsing.h
+++ b/talk/p2p/base/parsing.h
@@ -36,6 +36,8 @@
 
 namespace cricket {
 
+typedef std::vector<buzz::XmlElement*> XmlElements;
+
 // We decided "bool Parse(in, out*, error*)" is generally the best
 // parse signature.  "out Parse(in)" doesn't allow for errors.
 // "error* Parse(in, out*)" doesn't allow flexible memory management.
diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc
index e6bab9c..800b9b9 100644
--- a/talk/p2p/base/port.cc
+++ b/talk/p2p/base/port.cc
@@ -213,13 +213,15 @@
   } else if (msg->type() == STUN_BINDING_REQUEST) {
     SignalUnknownAddress(this, addr, msg, remote_username);
   } else {
-    // NOTE(tschmelcher): This is benign. It occurs if we pruned a
-    // connection for this port while it had STUN requests in flight, because
-    // we then get back responses for them, which this code correctly does not
-    // handle.
-    LOG_J(LS_INFO, this) << "Received unexpected STUN message type ("
-                         << msg->type() << ") from unknown address ("
-                         << addr.ToString() << ")";
+    // NOTE(tschmelcher): STUN_BINDING_RESPONSE is benign. It occurs if we
+    // pruned a connection for this port while it had STUN requests in flight,
+    // because we then get back responses for them, which this code correctly
+    // does not handle.
+    if (msg->type() != STUN_BINDING_RESPONSE) {
+      LOG_J(LS_ERROR, this) << "Received unexpected STUN message type ("
+                            << msg->type() << ") from unknown address ("
+                            << addr.ToString() << ")";
+    }
     delete msg;
   }
 }
diff --git a/talk/p2p/base/session.cc b/talk/p2p/base/session.cc
index 1839118..eb34e0a 100644
--- a/talk/p2p/base/session.cc
+++ b/talk/p2p/base/session.cc
@@ -644,13 +644,8 @@
     case ACTION_TRANSPORT_ACCEPT:
       valid = OnTransportAcceptMessage(msg, &error);
       break;
-    case ACTION_NOTIFY:
     case ACTION_UPDATE:
-      // TODO: Process these non-standard messages, but
-      // only once we figure out how in a jingle-specific way (or
-      // remove the need altogether).  For now, just don't send an
-      // error back, because it disrupts call establishment.
-      valid = true;
+      valid = OnUpdateMessage(msg, &error);
       break;
     default:
       valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST,
@@ -791,9 +786,8 @@
   return true;
 }
 
-// Only used by app/win32/fileshare.cc.
 bool Session::OnInfoMessage(const SessionMessage& msg) {
-  SignalInfoMessage(this, CopyOfXmlChildren(msg.action_elem));
+  SignalInfoMessage(this, msg.action_elem);
   return true;
 }
 
@@ -833,6 +827,13 @@
   return true;
 }
 
+bool Session::OnUpdateMessage(const SessionMessage& msg,
+                              MessageError* error) {
+  // TODO: Once someone needs it, parse the message
+  // into a data structure and signal out.
+  return true;
+}
+
 bool BareJidsEqual(const std::string& name1,
                    const std::string& name2) {
   buzz::Jid jid1(name1);
diff --git a/talk/p2p/base/session.h b/talk/p2p/base/session.h
index 95a201b..6a8750c 100644
--- a/talk/p2p/base/session.h
+++ b/talk/p2p/base/session.h
@@ -339,11 +339,12 @@
   virtual bool Reject(const std::string& reason);
   virtual bool TerminateWithReason(const std::string& reason);
 
-  // The two clients in the session may also send one another arbitrary XML
-  // messages, which are called "info" messages.  Both of these functions take
-  // ownership of the XmlElements and delete them when done.
+  // The two clients in the session may also send one another
+  // arbitrary XML messages, which are called "info" messages. Sending
+  // takes ownership of the given elements.  The signal does not; the
+  // parent element will be deleted after the signal.
   bool SendInfoMessage(const XmlElements& elems);
-  sigslot::signal2<Session*, const XmlElements&> SignalInfoMessage;
+  sigslot::signal2<Session*, const buzz::XmlElement*> SignalInfoMessage;
 
   // Maps passed to serialization functions.
   TransportParserMap GetTransportParsers();
@@ -512,6 +513,7 @@
   bool OnTerminateMessage(const SessionMessage& msg, MessageError* error);
   bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error);
   bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error);
+  bool OnUpdateMessage(const SessionMessage& msg, MessageError* error);
   bool OnRedirectError(const SessionRedirect& redirect, SessionError* error);
 
   // Verifies that we are in the appropriate state to receive this message.
diff --git a/talk/p2p/base/sessionmessages.cc b/talk/p2p/base/sessionmessages.cc
index 5c91460..d15c9e3 100644
--- a/talk/p2p/base/sessionmessages.cc
+++ b/talk/p2p/base/sessionmessages.cc
@@ -32,8 +32,6 @@
 #include "talk/base/logging.h"
 #include "talk/base/scoped_ptr.h"
 #include "talk/base/stringutils.h"
-#include "talk/xmllite/xmlconstants.h"
-#include "talk/xmpp/constants.h"
 #include "talk/p2p/base/constants.h"
 #include "talk/p2p/base/p2ptransport.h"
 #include "talk/p2p/base/parsing.h"
@@ -41,6 +39,7 @@
 #include "talk/p2p/base/sessiondescription.h"
 #include "talk/p2p/base/transport.h"
 #include "talk/xmllite/xmlconstants.h"
+#include "talk/xmpp/constants.h"
 
 namespace cricket {
 
@@ -73,8 +72,8 @@
     return ACTION_TRANSPORT_INFO;
   if (type == JINGLE_ACTION_TRANSPORT_ACCEPT)
     return ACTION_TRANSPORT_ACCEPT;
-  if (type == GINGLE_ACTION_NOTIFY)
-    return ACTION_NOTIFY;
+  if (type == JINGLE_ACTION_DESCRIPTION_INFO)
+    return ACTION_UPDATE;
   if (type == GINGLE_ACTION_UPDATE)
     return ACTION_UPDATE;
 
diff --git a/talk/p2p/base/sessionmessages.h b/talk/p2p/base/sessionmessages.h
index 82e63bd..ef7cc69 100644
--- a/talk/p2p/base/sessionmessages.h
+++ b/talk/p2p/base/sessionmessages.h
@@ -32,10 +32,11 @@
 #include <vector>
 #include <map>
 
-#include "talk/xmllite/xmlelement.h"
-#include "talk/p2p/base/constants.h"
-#include "talk/p2p/base/sessiondescription.h"  // Needed to delete contents.
 #include "talk/base/basictypes.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/sessiondescription.h"  // Needed to delete contents.
+#include "talk/xmllite/xmlelement.h"
 
 namespace cricket {
 
@@ -45,7 +46,6 @@
 class ContentParser;
 class TransportParser;
 
-typedef std::vector<buzz::XmlElement*> XmlElements;
 typedef std::vector<Candidate> Candidates;
 typedef std::map<std::string, ContentParser*> ContentParserMap;
 typedef std::map<std::string, TransportParser*> TransportParserMap;
@@ -62,10 +62,6 @@
   ACTION_TRANSPORT_INFO,
   ACTION_TRANSPORT_ACCEPT,
 
-  // TODO: Make better names for these when we think of a
-  // "jingley" way of signaling them.  Even better, remove them from
-  // being needed at all.
-  ACTION_NOTIFY,
   ACTION_UPDATE,
 };
 
@@ -160,6 +156,7 @@
   std::string target;
 };
 
+
 bool IsSessionMessage(const buzz::XmlElement* stanza);
 bool ParseSessionMessage(const buzz::XmlElement* stanza,
                          SessionMessage* msg,
diff --git a/talk/session/phone/call.cc b/talk/session/phone/call.cc
index f2c3960..a150688 100644
--- a/talk/session/phone/call.cc
+++ b/talk/session/phone/call.cc
@@ -132,6 +132,30 @@
     TerminateSession(*it);
 }
 
+bool Call::SendViewRequest(Session* session,
+                           const ViewRequest& view_request) {
+  StaticVideoViews::const_iterator it;
+  for (it = view_request.static_video_views.begin();
+       it != view_request.static_video_views.end(); ++it) {
+    const NamedSource* found_source =
+        media_sources.GetVideoSourceBySsrc(it->ssrc);
+    if (!found_source) {
+      LOG(LS_WARNING) <<
+          "Tried sending view request for bad ssrc: " << it->ssrc;
+      return false;
+    }
+  }
+
+  XmlElements elems;
+  WriteError error;
+  if (!WriteViewRequest(CN_VIDEO, view_request, &elems, &error)) {
+    LOG(LS_ERROR) << "Couldn't write out view request: " << error.text;
+    return false;
+  }
+
+  return session->SendInfoMessage(elems);
+}
+
 void Call::SetLocalRenderer(VideoRenderer* renderer) {
   local_renderer_ = renderer;
   if (session_client_->GetFocus() == this) {
@@ -147,25 +171,31 @@
   }
 }
 
-void Call::AddStream(BaseSession *session,
-                     uint32 voice_ssrc, uint32 video_ssrc) {
+void Call::AddVoiceStream(BaseSession *session, uint32 voice_ssrc) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
-  VideoChannel *video_channel = GetVideoChannel(session);
   if (voice_channel && voice_ssrc) {
     voice_channel->AddStream(voice_ssrc);
   }
+}
+
+void Call::AddVideoStream(BaseSession *session, uint32 video_ssrc) {
+  VideoChannel *video_channel = GetVideoChannel(session);
   if (video_channel && video_ssrc) {
-    video_channel->AddStream(video_ssrc, voice_ssrc);
+    // TODO: Do we need the audio_ssrc here?
+    // It doesn't seem to be used.
+    video_channel->AddStream(video_ssrc, 0U);
   }
 }
 
-void Call::RemoveStream(BaseSession *session,
-                        uint32 voice_ssrc, uint32 video_ssrc) {
+void Call::RemoveVoiceStream(BaseSession *session, uint32 voice_ssrc) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
-  VideoChannel *video_channel = GetVideoChannel(session);
   if (voice_channel && voice_ssrc) {
     voice_channel->RemoveStream(voice_ssrc);
   }
+}
+
+void Call::RemoveVideoStream(BaseSession *session, uint32 video_ssrc) {
+  VideoChannel *video_channel = GetVideoChannel(session);
   if (video_channel && video_ssrc) {
     video_channel->RemoveStream(video_ssrc);
   }
@@ -240,6 +270,7 @@
     sessions_.push_back(session);
     session->SignalState.connect(this, &Call::OnSessionState);
     session->SignalError.connect(this, &Call::OnSessionError);
+    session->SignalInfoMessage.connect(this, &Call::OnSessionInfo);
     session->SignalReceivedTerminateReason
       .connect(this, &Call::OnReceivedTerminateReason);
 
@@ -490,6 +521,78 @@
   SignalSessionError(this, session, error);
 }
 
+void Call::OnSessionInfo(Session *session,
+                         const buzz::XmlElement* action_elem) {
+  // We have a different list of "updates" because we only want to
+  // signal the sources that were added or removed.  We want to filter
+  // out un-changed sources.
+  cricket::MediaSources updates;
+
+  if (IsSourcesNotify(action_elem)) {
+    MediaSources sources;
+    ParseError error;
+    if (!ParseSourcesNotify(action_elem, session->remote_description(),
+                            &sources, &error)) {
+      // TODO: Is there a way we can signal an IQ error
+      // back to the sender?
+      LOG(LS_WARNING) << "Invalid sources notify message: " << error.text;
+      return;
+    }
+
+    NamedSources::iterator it;
+    for (it = sources.audio.begin(); it != sources.audio.end(); ++it) {
+      const NamedSource* found;
+      if (it->ssrc_set) {
+        found = media_sources.GetAudioSourceBySsrc(it->ssrc);
+      } else {
+        // For backwards compatibility, we remove by nick.
+        // TODO: Remove once all senders use explicit remove by ssrc.
+        found = media_sources.GetFirstAudioSourceByNick(it->nick);
+        it->SetSsrc(found->ssrc);
+        it->removed = true;
+      }
+      if (it->removed && found) {
+        LOG(LS_INFO) << "Remove voice stream:  " << found->ssrc;
+        RemoveVoiceStream(session, found->ssrc);
+        media_sources.RemoveAudioSourceBySsrc(it->ssrc);
+        updates.audio.push_back(*it);
+      } else if (!it->removed && !found) {
+        LOG(LS_INFO) << "Add voice stream:  " << it->ssrc;
+        AddVoiceStream(session, it->ssrc);
+        media_sources.AddAudioSource(*it);
+        updates.audio.push_back(*it);
+      }
+    }
+    for (it = sources.video.begin(); it != sources.video.end(); ++it) {
+      const NamedSource* found;
+      if (it->ssrc_set) {
+        found = media_sources.GetVideoSourceBySsrc(it->ssrc);
+      } else {
+        // For backwards compatibility, we remove by nick.
+        // TODO: Remove once all senders use explicit remove by ssrc.
+        found = media_sources.GetFirstVideoSourceByNick(it->nick);
+        it->SetSsrc(found->ssrc);
+        it->removed = true;
+      }
+      if (it->removed && found) {
+        LOG(LS_INFO) << "Remove video stream:  " << found->ssrc;
+        RemoveVideoStream(session, found->ssrc);
+        media_sources.RemoveVideoSourceBySsrc(it->ssrc);
+        updates.video.push_back(*it);
+      } else if (!it->removed && !found) {
+        LOG(LS_INFO) << "Add video stream:  " << it->ssrc;
+        AddVideoStream(session, it->ssrc);
+        media_sources.AddVideoSource(*it);
+        updates.video.push_back(*it);
+      }
+    }
+
+    if (!updates.audio.empty() || !updates.video.empty()) {
+      SignalMediaSourcesUpdate(this, session, updates);
+    }
+  }
+}
+
 void Call::OnReceivedTerminateReason(Session *session,
                                      const std::string &reason) {
   session_client_->session_manager()->signaling_thread()->Clear(this,
diff --git a/talk/session/phone/call.h b/talk/session/phone/call.h
index 8e9bcdc..06c3981 100644
--- a/talk/session/phone/call.h
+++ b/talk/session/phone/call.h
@@ -37,6 +37,7 @@
 #include "talk/p2p/client/socketmonitor.h"
 #include "talk/xmpp/jid.h"
 #include "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/mediamessages.h"
 #include "talk/session/phone/voicechannel.h"
 
 namespace cricket {
@@ -54,11 +55,11 @@
   void RejectSession(BaseSession *session);
   void TerminateSession(BaseSession *session);
   void Terminate();
+  bool SendViewRequest(Session* session,
+                       const ViewRequest& view_request);
   void SetLocalRenderer(VideoRenderer* renderer);
   void SetVideoRenderer(BaseSession *session, uint32 ssrc,
                         VideoRenderer* renderer);
-  void AddStream(BaseSession *session, uint32 voice_ssrc, uint32 video_ssrc);
-  void RemoveStream(BaseSession *session, uint32 voice_ssrc, uint32 video_ssrc);
   void StartConnectionMonitor(BaseSession *session, int cms);
   void StopConnectionMonitor(BaseSession *session);
   void StartAudioMonitor(BaseSession *session, int cms);
@@ -96,11 +97,15 @@
   sigslot::signal2<Call *, const std::vector<ConnectionInfo> &>
       SignalVideoConnectionMonitor;
   sigslot::signal2<Call *, const VideoMediaInfo&> SignalVideoMediaMonitor;
+  sigslot::signal3<Call *,
+                   Session *,
+                   const MediaSources&> SignalMediaSourcesUpdate;
 
  private:
   void OnMessage(talk_base::Message *message);
   void OnSessionState(BaseSession *session, BaseSession::State state);
   void OnSessionError(BaseSession *session, Session::Error error);
+  void OnSessionInfo(Session *session, const buzz::XmlElement* action_elem);
   void OnReceivedTerminateReason(Session *session, const std::string &reason);
   void IncomingSession(Session *session, const SessionDescription* offer);
   // Returns true on success.
@@ -117,11 +122,16 @@
   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);
   void ContinuePlayDTMF();
 
   uint32 id_;
   MediaSessionClient *session_client_;
   std::vector<Session *> sessions_;
+  MediaSources media_sources;
   std::map<std::string, VoiceChannel *> voice_channel_map_;
   std::map<std::string, VideoChannel *> video_channel_map_;
   VideoRenderer* local_renderer_;
diff --git a/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index 92010b5..d670bf1 100644
--- a/talk/session/phone/channel.cc
+++ b/talk/session/phone/channel.cc
@@ -1018,11 +1018,16 @@
   Send(MSG_SENDINTRAFRAME);
   return true;
 }
+
 bool VideoChannel::RequestIntraFrame() {
   Send(MSG_REQUESTINTRAFRAME);
   return true;
 }
 
+void VideoChannel::EnableCpuAdaptation(bool enable) {
+  Send(enable ? MSG_ENABLECPUADAPTATION : MSG_DISABLECPUADAPTATION);
+}
+
 void VideoChannel::ChangeState() {
   // render incoming data if we are the active call
   // we receive data on the default channel and multiplexed streams
@@ -1167,6 +1172,12 @@
     case MSG_REQUESTINTRAFRAME:
       RequestIntraFrame_w();
       break;
+    case MSG_ENABLECPUADAPTATION:
+      EnableCpuAdaptation_w(true);
+      break;
+    case MSG_DISABLECPUADAPTATION:
+      EnableCpuAdaptation_w(false);
+      break;
     case MSG_CHANNEL_ERROR: {
       const VideoChannelErrorMessageData* data =
           static_cast<VideoChannelErrorMessageData*>(pmsg->pdata);
diff --git a/talk/session/phone/channel.h b/talk/session/phone/channel.h
index e1172dd..8b33386 100644
--- a/talk/session/phone/channel.h
+++ b/talk/session/phone/channel.h
@@ -70,7 +70,9 @@
   MSG_REQUESTINTRAFRAME = 20,
   MSG_RTPPACKET = 22,
   MSG_RTCPPACKET = 23,
-  MSG_CHANNEL_ERROR = 24
+  MSG_CHANNEL_ERROR = 24,
+  MSG_ENABLECPUADAPTATION = 25,
+  MSG_DISABLECPUADAPTATION = 26
 };
 
 // BaseChannel contains logic common to voice and video, including
@@ -402,6 +404,7 @@
 
   bool SendIntraFrame();
   bool RequestIntraFrame();
+  void EnableCpuAdaptation(bool enable);
 
   sigslot::signal3<VideoChannel*, uint32, VideoMediaChannel::Error>
       SignalMediaError;
@@ -425,6 +428,12 @@
   void RequestIntraFrame_w() {
     media_channel()->RequestIntraFrame();
   }
+  void EnableCpuAdaptation_w(bool enable) {
+    // TODO: The following call will clear all other options, which is
+    // OK now since SetOptions is not used in video media channel. In the
+    // future, add GetOptions() method and change the options.
+    media_channel()->SetOptions(enable ? OPT_CPU_ADAPTATION : 0);
+  }
 
   struct RenderMessageData : public talk_base::MessageData {
     RenderMessageData(uint32 s, VideoRenderer* r) : ssrc(s), renderer(r) {}
diff --git a/talk/session/phone/channelmanager.h b/talk/session/phone/channelmanager.h
index 06ab92e..3b34e62 100644
--- a/talk/session/phone/channelmanager.h
+++ b/talk/session/phone/channelmanager.h
@@ -79,15 +79,6 @@
   void GetSupportedAudioCodecs(std::vector<AudioCodec>* codecs) const;
   void GetSupportedVideoCodecs(std::vector<VideoCodec>* codecs) const;
 
-  // Determines if a specific audio or video codec is supported.
-  // Can be called before starting the media engine.
-  bool FindAudioCodec(const AudioCodec& codec) const {
-    return media_engine_->FindAudioCodec(codec);
-  }
-  bool FindVideoCodec(const VideoCodec& video_codec) const {
-    return media_engine_->FindVideoCodec(video_codec);
-  }
-
   // Indicates whether the media engine is started.
   bool initialized() const { return initialized_; }
   // Starts up the media engine.
diff --git a/talk/session/phone/filemediaengine.cc b/talk/session/phone/filemediaengine.cc
index 6e86bd2..3e7e517 100644
--- a/talk/session/phone/filemediaengine.cc
+++ b/talk/session/phone/filemediaengine.cc
@@ -58,20 +58,61 @@
 }
 
 VoiceMediaChannel* FileMediaEngine::CreateChannel() {
-  if (!voice_input_filename_.empty() || !voice_output_filename_.empty()) {
-    return new FileVoiceChannel(voice_input_filename_, voice_output_filename_);
-  } else {
+  talk_base::FileStream* input_file_stream = NULL;
+  talk_base::FileStream* output_file_stream = NULL;
+
+  if (voice_input_filename_.empty() && voice_output_filename_.empty())
     return NULL;
+  if (!voice_input_filename_.empty()) {
+    input_file_stream = talk_base::Filesystem::OpenFile(
+        talk_base::Pathname(voice_input_filename_), "rb");
+    if (!input_file_stream) {
+      LOG(LS_ERROR) << "Not able to open the input audio stream file.";
+      return NULL;
+    }
   }
+
+  if (!voice_output_filename_.empty()) {
+    output_file_stream = talk_base::Filesystem::OpenFile(
+        talk_base::Pathname(voice_output_filename_), "wb");
+    if (!output_file_stream) {
+      delete input_file_stream;
+      LOG(LS_ERROR) << "Not able to open the output audio stream file.";
+      return NULL;
+    }
+  }
+
+  return new FileVoiceChannel(input_file_stream, output_file_stream);
 }
 
 VideoMediaChannel* FileMediaEngine::CreateVideoChannel(
     VoiceMediaChannel* voice_ch) {
-  if (!video_input_filename_.empty() || !video_output_filename_.empty()) {
-    return new FileVideoChannel(video_input_filename_, video_output_filename_);
-  } else {
-    return NULL;
+  talk_base::FileStream* input_file_stream = NULL;
+  talk_base::FileStream* output_file_stream = NULL;
+
+  if (video_input_filename_.empty() && video_output_filename_.empty())
+      return NULL;
+
+  if (!video_input_filename_.empty()) {
+    input_file_stream = talk_base::Filesystem::OpenFile(
+        talk_base::Pathname(video_input_filename_), "rb");
+    if (!input_file_stream) {
+      LOG(LS_ERROR) << "Not able to open the input video stream file.";
+      return NULL;
+    }
   }
+
+  if (!video_output_filename_.empty()) {
+    output_file_stream = talk_base::Filesystem::OpenFile(
+        talk_base::Pathname(video_output_filename_), "wb");
+    if (!output_file_stream) {
+      delete input_file_stream;
+      LOG(LS_ERROR) << "Not able to open the output video stream file.";
+      return NULL;
+    }
+  }
+
+  return new FileVideoChannel(input_file_stream, output_file_stream);
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -80,8 +121,9 @@
 class RtpSenderReceiver
     : public talk_base::Thread, public talk_base::MessageHandler {
  public:
-  RtpSenderReceiver(MediaChannel* channel, const std::string& in_file,
-                    const std::string& out_file);
+  RtpSenderReceiver(MediaChannel* channel,
+                    talk_base::StreamInterface* input_file_stream,
+                    talk_base::StreamInterface* output_file_stream);
 
   // Called by media channel. Context: media channel thread.
   bool SetSend(bool send);
@@ -117,14 +159,14 @@
 ///////////////////////////////////////////////////////////////////////////
 // Implementation of RtpSenderReceiver.
 ///////////////////////////////////////////////////////////////////////////
-RtpSenderReceiver::RtpSenderReceiver(MediaChannel* channel,
-                                     const std::string& in_file,
-                                     const std::string& out_file)
+RtpSenderReceiver::RtpSenderReceiver(
+    MediaChannel* channel,
+    talk_base::StreamInterface* input_file_stream,
+    talk_base::StreamInterface* output_file_stream)
     : media_channel_(channel),
       sending_(false),
       first_packet_(true) {
-  input_stream_.reset(talk_base::Filesystem::OpenFile(
-      talk_base::Pathname(in_file), "rb"));
+  input_stream_.reset(input_file_stream);
   if (input_stream_.get()) {
     rtp_dump_reader_.reset(new RtpDumpLoopReader(input_stream_.get()));
     // Start the sender thread, which reads rtp dump records, waits based on
@@ -133,8 +175,7 @@
   }
 
   // Create a rtp dump writer for the output RTP dump stream.
-  output_stream_.reset(talk_base::Filesystem::OpenFile(
-      talk_base::Pathname(out_file), "wb"));
+  output_stream_.reset(output_file_stream);
   if (output_stream_.get()) {
     rtp_dump_writer_.reset(new RtpDumpWriter(output_stream_.get()));
   }
@@ -207,10 +248,11 @@
 ///////////////////////////////////////////////////////////////////////////
 // Implementation of FileVoiceChannel.
 ///////////////////////////////////////////////////////////////////////////
-FileVoiceChannel::FileVoiceChannel(const std::string& in_file,
-                                   const std::string& out_file)
-    : rtp_sender_receiver_(new RtpSenderReceiver(this, in_file, out_file)) {
-}
+FileVoiceChannel::FileVoiceChannel(
+    talk_base::StreamInterface* input_file_stream,
+    talk_base::StreamInterface* output_file_stream)
+    : rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
+                                                 output_file_stream)) {}
 
 FileVoiceChannel::~FileVoiceChannel() {}
 
@@ -230,10 +272,11 @@
 ///////////////////////////////////////////////////////////////////////////
 // Implementation of FileVideoChannel.
 ///////////////////////////////////////////////////////////////////////////
-FileVideoChannel::FileVideoChannel(const std::string& in_file,
-                                   const std::string& out_file)
-    : rtp_sender_receiver_(new RtpSenderReceiver(this, in_file, out_file)) {
-}
+FileVideoChannel::FileVideoChannel(
+    talk_base::StreamInterface* input_file_stream,
+    talk_base::StreamInterface* output_file_stream)
+    : rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
+                                                 output_file_stream)) {}
 
 FileVideoChannel::~FileVideoChannel() {}
 
diff --git a/talk/session/phone/filemediaengine.h b/talk/session/phone/filemediaengine.h
index e63b415..62e28f9 100644
--- a/talk/session/phone/filemediaengine.h
+++ b/talk/session/phone/filemediaengine.h
@@ -30,6 +30,7 @@
 #include <vector>
 
 #include "talk/base/scoped_ptr.h"
+#include "talk/base/stream.h"
 #include "talk/session/phone/codec.h"
 #include "talk/session/phone/mediachannel.h"
 #include "talk/session/phone/mediaengine.h"
@@ -125,7 +126,8 @@
 
 class FileVoiceChannel : public VoiceMediaChannel {
  public:
-  FileVoiceChannel(const std::string& in_file, const std::string& out_file);
+  FileVoiceChannel(talk_base::StreamInterface* input_file_stream,
+      talk_base::StreamInterface* output_file_stream);
   virtual ~FileVoiceChannel();
 
   // Implement pure virtual methods of VoiceMediaChannel.
@@ -170,7 +172,8 @@
 
 class FileVideoChannel : public VideoMediaChannel {
  public:
-  FileVideoChannel(const std::string& in_file, const std::string& out_file);
+  FileVideoChannel(talk_base::StreamInterface* input_file_stream,
+      talk_base::StreamInterface* output_file_stream);
   virtual ~FileVideoChannel();
 
   // Implement pure virtual methods of VideoMediaChannel.
diff --git a/talk/session/phone/gtkvideorenderer.cc b/talk/session/phone/gtkvideorenderer.cc
index 4e356b3..059c688 100644
--- a/talk/session/phone/gtkvideorenderer.cc
+++ b/talk/session/phone/gtkvideorenderer.cc
@@ -33,24 +33,39 @@
 
 namespace cricket {
 
+GtkVideoRenderer::GtkVideoRenderer(int x, int y)
+    : window_(NULL),
+      draw_area_(NULL),
+      initial_x_(x),
+      initial_y_(y) {
+  g_thread_init(NULL);
+  gdk_threads_init();
+}
+
 GtkVideoRenderer::~GtkVideoRenderer() {
   if (window_) {
+    gdk_threads_enter();
     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();
+
   // For the first frame, initialize the GTK window
   if (!window_ && !Initialize(width, height)) {
+    gdk_threads_leave();
     return false;
   }
 
   image_.reset(new uint8[width * height * 4]);
   gtk_window_resize(GTK_WINDOW(window_), width, height);
+  gdk_threads_leave();
   return true;
 }
 
@@ -70,6 +85,7 @@
                             frame->GetWidth() * frame->GetHeight() * 4,
                             frame->GetWidth() * 4);
 
+  gdk_threads_enter();
   // draw the ABGR image
   gdk_draw_rgb_32_image(draw_area_->window,
                         draw_area_->style->fg_gc[GTK_STATE_NORMAL],
@@ -83,6 +99,7 @@
 
   // Run the Gtk main loop to refresh the window.
   Pump();
+  gdk_threads_leave();
   return true;
 }
 
diff --git a/talk/session/phone/gtkvideorenderer.h b/talk/session/phone/gtkvideorenderer.h
index e1eb8db..6bdc606 100644
--- a/talk/session/phone/gtkvideorenderer.h
+++ b/talk/session/phone/gtkvideorenderer.h
@@ -38,12 +38,7 @@
 
 class GtkVideoRenderer : public VideoRenderer {
  public:
-  GtkVideoRenderer(int x, int y)
-      : window_(NULL),
-        draw_area_(NULL),
-        initial_x_(x),
-        initial_y_(y) {
-  }
+  GtkVideoRenderer(int x, int y);
   virtual ~GtkVideoRenderer();
 
   // Implementation of pure virtual methods of VideoRenderer.
diff --git a/talk/session/phone/mediachannel.h b/talk/session/phone/mediachannel.h
index 924eb5d..7c22b70 100644
--- a/talk/session/phone/mediachannel.h
+++ b/talk/session/phone/mediachannel.h
@@ -64,8 +64,10 @@
 };
 
 enum VideoMediaChannelOptions {
-  OPT_INTERPOLATE = 0x10000   // Increase the output framerate by 2x by
-                              // interpolating frames
+  // Increase the output framerate by 2x by interpolating frames
+  OPT_INTERPOLATE = 0x10000,
+  // Enable video adaptation due to cpu load.
+  OPT_CPU_ADAPTATION = 0x20000
 };
 
 class MediaChannel : public sigslot::has_slots<> {
@@ -109,6 +111,7 @@
   virtual bool SetSendBandwidth(bool autobw, int bps) = 0;
   // Sets the media options to use.
   virtual bool SetOptions(int options) = 0;
+  // TODO: add virtual int GetOptions() = 0;
 
  protected:
   NetworkInterface *network_interface_;
@@ -140,6 +143,9 @@
   float fraction_lost;
   int ext_seqnum;
   int jitter_ms;
+  int jitter_buffer_ms;
+  int jitter_buffer_preferred_ms;
+  int delay_estimate_ms;
   int audio_level;
 };
 
diff --git a/talk/session/phone/mediaengine.h b/talk/session/phone/mediaengine.h
index f5dd04d..4383408 100644
--- a/talk/session/phone/mediaengine.h
+++ b/talk/session/phone/mediaengine.h
@@ -142,8 +142,6 @@
 
   virtual const std::vector<AudioCodec>& audio_codecs() = 0;
   virtual const std::vector<VideoCodec>& video_codecs() = 0;
-  virtual bool FindAudioCodec(const AudioCodec &codec) = 0;
-  virtual bool FindVideoCodec(const VideoCodec &codec) = 0;
 
   // Logging control
   virtual void SetVoiceLogging(int min_sev, const char* filter) = 0;
@@ -158,6 +156,7 @@
 class CompositeMediaEngine : public MediaEngine {
  public:
   CompositeMediaEngine() {}
+  virtual ~CompositeMediaEngine() {}
   virtual bool Init() {
     if (!voice_.Init())
       return false;
@@ -231,13 +230,6 @@
     return video_.codecs();
   }
 
-  virtual bool FindAudioCodec(const AudioCodec &codec) {
-    return voice_.FindCodec(codec);
-  }
-  virtual bool FindVideoCodec(const VideoCodec &codec) {
-    return video_.FindCodec(codec);
-  }
-
   virtual void SetVoiceLogging(int min_sev, const char* filter) {
     return voice_.SetLogging(min_sev, filter);
   }
@@ -245,7 +237,7 @@
     return video_.SetLogging(min_sev, filter);
   }
 
- private:
+ protected:
   VOICE voice_;
   VIDEO video_;
 };
@@ -273,7 +265,6 @@
   int GetInputLevel() { return 0; }
   bool SetLocalMonitor(bool enable) { return true; }
   const std::vector<AudioCodec>& codecs() { return codecs_; }
-  bool FindCodec(const AudioCodec&) { return false; }
   void SetLogging(int min_sev, const char* filter) {}
  private:
   std::vector<AudioCodec> codecs_;
@@ -287,7 +278,8 @@
   void Terminate() {}
   int GetCapabilities() { return 0; }
   // If you need this to return an actual channel, use FakeMediaEngine instead.
-  VideoMediaChannel* CreateChannel(VoiceMediaChannel* voice_media_channel) {
+  VideoMediaChannel* CreateChannel(
+      VoiceMediaChannel* voice_media_channel) {
     return NULL;
   }
   bool SetOptions(int opts) { return true; }
@@ -298,7 +290,6 @@
   bool SetLocalRenderer(VideoRenderer* renderer) { return true; }
   CaptureResult SetCapture(bool capture) { return CR_SUCCESS;  }
   const std::vector<VideoCodec>& codecs() { return codecs_; }
-  bool FindCodec(const VideoCodec&) { return false; }
   void SetLogging(int min_sev, const char* filter) {}
   sigslot::signal1<CaptureResult> SignalCaptureResult;
  private:
diff --git a/talk/session/phone/mediamessages.cc b/talk/session/phone/mediamessages.cc
new file mode 100644
index 0000000..b1e9b76
--- /dev/null
+++ b/talk/session/phone/mediamessages.cc
@@ -0,0 +1,242 @@
+/*
+ * libjingle
+ * Copyright 2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/phone/mediamessages.h"
+
+#include "talk/base/stringencode.h"
+#include "talk/p2p/base/constants.h"
+#include "talk/session/phone/mediasessionclient.h"
+#include "talk/xmllite/xmlelement.h"
+
+namespace cricket {
+
+const NamedSource* GetFirstSourceByNick(const NamedSources& sources,
+                                        const std::string& nick) {
+  for (NamedSources::const_iterator source = sources.begin();
+       source != sources.end(); ++source) {
+    if (source->nick == nick) {
+      return &*source;
+    }
+  }
+  return NULL;
+}
+
+const NamedSource* GetSourceBySsrc(const NamedSources& sources, uint32 ssrc) {
+  for (NamedSources::const_iterator source = sources.begin();
+       source != sources.end(); ++source) {
+    if (source->ssrc == ssrc) {
+      return &*source;
+    }
+  }
+  return NULL;
+}
+
+const NamedSource* MediaSources::GetFirstAudioSourceByNick(
+    const std::string& nick) {
+  return GetFirstSourceByNick(audio, nick);
+}
+
+const NamedSource* MediaSources::GetFirstVideoSourceByNick(
+    const std::string& nick) {
+  return GetFirstSourceByNick(video, nick);
+}
+
+const NamedSource* MediaSources::GetAudioSourceBySsrc(uint32 ssrc) {
+  return GetSourceBySsrc(audio, ssrc);
+}
+
+const NamedSource* MediaSources::GetVideoSourceBySsrc(uint32 ssrc) {
+  return GetSourceBySsrc(video, ssrc);
+}
+
+// NOTE: There is no check here for duplicate sources, so check before
+// adding.
+void AddSource(NamedSources* sources, const NamedSource& source) {
+  sources->push_back(source);
+}
+
+void MediaSources::AddAudioSource(const NamedSource& source) {
+  AddSource(&audio, source);
+}
+
+void MediaSources::AddVideoSource(const NamedSource& source) {
+  AddSource(&video, source);
+}
+
+void RemoveSourceBySsrc(NamedSources* sources, uint32 ssrc) {
+  for (NamedSources::iterator source = sources->begin();
+       source != sources->end(); ) {
+    if (source->ssrc == ssrc) {
+      source = sources->erase(source);
+    } else {
+      ++source;
+    }
+  }
+}
+
+void MediaSources::RemoveAudioSourceBySsrc(uint32 ssrc) {
+  RemoveSourceBySsrc(&audio, ssrc);
+}
+
+void MediaSources::RemoveVideoSourceBySsrc(uint32 ssrc) {
+  RemoveSourceBySsrc(&video, ssrc);
+}
+
+bool ParseSsrc(const std::string& string, uint32* ssrc) {
+  return talk_base::FromString(string, ssrc);
+}
+
+bool ParseSsrc(const buzz::XmlElement* element, uint32* ssrc) {
+  if (element == NULL) {
+    return false;
+  }
+  return ParseSsrc(element->BodyText(), ssrc);
+}
+
+bool ParseNamedSource(const buzz::XmlElement* source_elem,
+                      NamedSource* named_source,
+                      ParseError* error) {
+  named_source->nick = source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_NICK);
+  if (named_source->nick.empty()) {
+    return BadParse("Missing or invalid nick.", error);
+  }
+
+  named_source->name = source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_NAME);
+  named_source->usage = source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_USAGE);
+  named_source->removed =
+      (STR_JINGLE_DRAFT_SOURCE_STATE_REMOVED ==
+       source_elem->Attr(QN_JINGLE_DRAFT_SOURCE_STATE));
+
+  const buzz::XmlElement* ssrc_elem =
+      source_elem->FirstNamed(QN_JINGLE_DRAFT_SOURCE_SSRC);
+  if (ssrc_elem != NULL && !ssrc_elem->BodyText().empty()) {
+    uint32 ssrc;
+    if (!ParseSsrc(ssrc_elem->BodyText(), &ssrc)) {
+      return BadParse("Missing or invalid ssrc.", error);
+    }
+    named_source->SetSsrc(ssrc);
+  }
+
+  return true;
+}
+
+bool IsSourcesNotify(const buzz::XmlElement* action_elem) {
+  return action_elem->FirstNamed(QN_JINGLE_DRAFT_NOTIFY) != NULL;
+}
+
+bool ParseSourcesNotify(const buzz::XmlElement* action_elem,
+                        const SessionDescription* session_description,
+                        MediaSources* sources,
+                        ParseError* error) {
+  for (const buzz::XmlElement* notify_elem
+           = action_elem->FirstNamed(QN_JINGLE_DRAFT_NOTIFY);
+       notify_elem != NULL;
+       notify_elem = notify_elem->NextNamed(QN_JINGLE_DRAFT_NOTIFY)) {
+    std::string content_name = notify_elem->Attr(QN_JINGLE_DRAFT_CONTENT_NAME);
+    for (const buzz::XmlElement* source_elem
+             = notify_elem->FirstNamed(QN_JINGLE_DRAFT_SOURCE);
+         source_elem != NULL;
+         source_elem = source_elem->NextNamed(QN_JINGLE_DRAFT_SOURCE)) {
+      NamedSource named_source;
+      if (!ParseNamedSource(source_elem, &named_source, error)) {
+        return false;
+      }
+
+      if (session_description == NULL) {
+        return BadParse("unknown content name: " + content_name, error);
+      }
+      const ContentInfo* content =
+          FindContentInfoByName(session_description->contents(), content_name);
+      if (content == NULL) {
+        return BadParse("unknown content name: " + content_name, error);
+      }
+
+      if (IsAudioContent(content)) {
+        sources->audio.push_back(named_source);
+      } else if (IsVideoContent(content)) {
+        sources->video.push_back(named_source);
+      }
+    }
+  }
+
+  return true;
+}
+
+buzz::XmlElement* CreateViewElem(const std::string& name,
+                                 const std::string& type) {
+  buzz::XmlElement* view_elem =
+      new buzz::XmlElement(QN_JINGLE_DRAFT_VIEW, true);
+  view_elem->AddAttr(QN_JINGLE_DRAFT_CONTENT_NAME, name);
+  view_elem->SetAttr(QN_JINGLE_DRAFT_VIEW_TYPE, type);
+  return view_elem;
+}
+
+buzz::XmlElement* CreateVideoViewElem(const std::string& content_name,
+                                      const std::string& type) {
+  return CreateViewElem(content_name, type);
+}
+
+buzz::XmlElement* CreateNoneVideoViewElem(const std::string& content_name) {
+  return CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_NONE);
+}
+
+buzz::XmlElement* CreateStaticVideoViewElem(const std::string& content_name,
+                                            const StaticVideoView& view) {
+  buzz::XmlElement* view_elem =
+      CreateVideoViewElem(content_name, STR_JINGLE_DRAFT_VIEW_TYPE_STATIC);
+  AddXmlAttr(view_elem, QN_JINGLE_DRAFT_VIEW_SSRC, view.ssrc);
+
+  buzz::XmlElement* params_elem = new buzz::XmlElement(
+      QN_JINGLE_DRAFT_VIEW_PARAMS);
+  AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_WIDTH, view.width);
+  AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_HEIGHT, view.height);
+  AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_FRAMERATE,
+             view.framerate);
+  AddXmlAttr(params_elem, QN_JINGLE_DRAFT_VIEW_PARAMS_PREFERENCE,
+             view.preference);
+  view_elem->AddElement(params_elem);
+
+  return view_elem;
+}
+
+bool WriteViewRequest(const std::string& content_name,
+                      const ViewRequest& request,
+                      XmlElements* elems,
+                      WriteError* error) {
+  if (request.static_video_views.size() == 0) {
+    elems->push_back(CreateNoneVideoViewElem(content_name));
+  } else {
+    for (StaticVideoViews::const_iterator view =
+             request.static_video_views.begin();
+         view != request.static_video_views.end(); ++view) {
+      elems->push_back(CreateStaticVideoViewElem(content_name, *view));
+    }
+  }
+  return true;
+}
+
+}  // namespace cricket
diff --git a/talk/session/phone/mediamessages.h b/talk/session/phone/mediamessages.h
new file mode 100644
index 0000000..58e8793
--- /dev/null
+++ b/talk/session/phone/mediamessages.h
@@ -0,0 +1,106 @@
+/*
+ * libjingle
+ * Copyright 2010, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_SESSION_PHONE_MEDIAMESSAGES_H_
+#define TALK_SESSION_PHONE_MEDIAMESSAGES_H_
+
+#include <string>
+#include <vector>
+#include "talk/base/basictypes.h"
+#include "talk/p2p/base/parsing.h"
+#include "talk/p2p/base/sessiondescription.h"
+
+namespace cricket {
+
+struct NamedSource {
+  NamedSource() : ssrc(0), ssrc_set(false), removed(false) {}
+
+  void SetSsrc(uint32 ssrc) {
+    this->ssrc = ssrc;
+    this->ssrc_set = true;
+  }
+
+  std::string nick;
+  std::string name;
+  std::string usage;
+  uint32 ssrc;
+  bool ssrc_set;
+  bool removed;
+};
+
+typedef std::vector<NamedSource> NamedSources;
+
+class MediaSources {
+ public:
+  const NamedSource* GetAudioSourceBySsrc(uint32 ssrc);
+  const NamedSource* GetVideoSourceBySsrc(uint32 ssrc);
+  // TODO: Remove once all senders use excplict remove by ssrc.
+  const NamedSource* GetFirstAudioSourceByNick(const std::string& nick);
+  const NamedSource* GetFirstVideoSourceByNick(const std::string& nick);
+  void AddAudioSource(const NamedSource& source);
+  void AddVideoSource(const NamedSource& source);
+  void RemoveAudioSourceBySsrc(uint32 ssrc);
+  void RemoveVideoSourceBySsrc(uint32 ssrc);
+  NamedSources audio;
+  NamedSources video;
+};
+
+struct StaticVideoView {
+  StaticVideoView(uint32 ssrc, int width, int height, int framerate)
+      : ssrc(ssrc),
+        width(width),
+        height(height),
+        framerate(framerate),
+        preference(0) {}
+
+  uint32 ssrc;
+  int width;
+  int height;
+  int framerate;
+  int preference;
+};
+
+typedef std::vector<StaticVideoView> StaticVideoViews;
+
+struct ViewRequest {
+  StaticVideoViews static_video_views;
+};
+
+bool WriteViewRequest(const std::string& content_name,
+                      const ViewRequest& view,
+                      XmlElements* elems,
+                      WriteError* error);
+
+bool IsSourcesNotify(const buzz::XmlElement* action_elem);
+// The session_description is needed to map content_name => media type.
+bool ParseSourcesNotify(const buzz::XmlElement* action_elem,
+                        const SessionDescription* session_description,
+                        MediaSources* sources,
+                        ParseError* error);
+}  // namespace cricket
+
+#endif  // TALK_SESSION_PHONE_MEDIAMESSAGES_H_
diff --git a/talk/session/phone/mediasessionclient.cc b/talk/session/phone/mediasessionclient.cc
index 10ea1af..6112db8 100644
--- a/talk/session/phone/mediasessionclient.cc
+++ b/talk/session/phone/mediasessionclient.cc
@@ -206,6 +206,24 @@
   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)
@@ -214,12 +232,8 @@
   const ContentInfos& contents = sdesc->contents();
   for (ContentInfos::const_iterator content = contents.begin();
        content != contents.end(); content++) {
-    if (content->type == NS_JINGLE_RTP) {
-      const MediaContentDescription* media =
-          static_cast<const MediaContentDescription*>(content->description);
-      if (media->type() == media_type) {
-        return &*content;
-      }
+    if (IsMediaContent(&*content, media_type)) {
+      return &*content;
     }
   }
   return NULL;
diff --git a/talk/session/phone/mediasessionclient.h b/talk/session/phone/mediasessionclient.h
index 8041678..07fc258 100644
--- a/talk/session/phone/mediasessionclient.h
+++ b/talk/session/phone/mediasessionclient.h
@@ -279,10 +279,11 @@
 };
 
 // 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/videocommon.h b/talk/session/phone/videocommon.h
index b978200..ff8e8de 100644
--- a/talk/session/phone/videocommon.h
+++ b/talk/session/phone/videocommon.h
@@ -61,6 +61,7 @@
 enum FourCC {
   // Canonical fourcc codes used in our code.
   FOURCC_I420 = FOURCC('I', '4', '2', '0'),
+  FOURCC_YV12 = FOURCC('Y', 'V', '1', '2'),
   FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'),
   FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'),
   FOURCC_M420 = FOURCC('M', '4', '2', '0'),
@@ -89,6 +90,8 @@
   FOURCC_2VUY = FOURCC('2', 'v', 'u', 'y'),  // Alias for UYVY
   FOURCC_JPEG = FOURCC('J', 'P', 'E', 'G'),  // Alias for MJPG
   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
 
   // Match any fourcc.
   FOURCC_ANY  = 0xFFFFFFFF,
@@ -152,6 +155,19 @@
 
   int framerate() const { return IntervalToFps(interval); }
 
+  // Check if both width and height are 0.
+  bool IsSize0x0() const { return 0 == width && 0 == height; }
+
+  // Check if this format is less than another one by comparing the resolution
+  // and frame rate.
+  bool IsPixelRateLess(const VideoFormat& format) const {
+    return width * height * framerate() <
+        format.width * format.height * format.framerate();
+  }
+
+  // Get a string presentation in the form of "fourcc width x height x fps"
+  std::string ToString() const;
+
   int    width;     // in number of pixels
   int    height;    // in number of pixels
   int64  interval;  // in nanoseconds
diff --git a/talk/site_scons/site_tools/talk_libjingle.py b/talk/site_scons/site_tools/talk_libjingle.py
index 3086614..33c136c 100644
--- a/talk/site_scons/site_tools/talk_libjingle.py
+++ b/talk/site_scons/site_tools/talk_libjingle.py
@@ -6,6 +6,7 @@
 
 import subprocess
 
+
 # We need this in libjingle because main.scons depends on it and
 # libjingle depends on main.scons.
 def EnableFeatureWherePackagePresent(env, bit, cpp_flag, package):
@@ -23,9 +24,10 @@
     env.SetBits(bit)
     env.Append(CPPDEFINES = [cpp_flag])
   else:
-    print ("Warning: Package \"%s\" not found. Feature \"%s\" will not be "
-           "built. To build with this feature, install the package that "
-           "provides the \"%s.pc\" file.") % (package, bit, package)
+    print ('Warning: Package \"%s\" not found. Feature \"%s\" will not be '
+           'built. To build with this feature, install the package that '
+           'provides the \"%s.pc\" file.') % (package, bit, package)
+
 
 def _HavePackage(package):
   """Whether the given pkg-config package name is present on the build system.
@@ -33,13 +35,15 @@
   Args:
     package: The name of the package.
 
-  Return:
+  Returns:
     True if the package is present, else False
   """
-  return subprocess.call(["pkg-config", "--exists", package]) == 0
+  return subprocess.call(['pkg-config', '--exists', package]) == 0
 
-def generate(env):
+
+def generate(env):  # pylint: disable-msg=C6409
   env.AddMethod(EnableFeatureWherePackagePresent)
 
-def exists(env):
+
+def exists(env):  # pylint: disable-msg=C6409,W0613
   return 1
diff --git a/talk/xmpp/constants.cc b/talk/xmpp/constants.cc
index 772c97d..db9095f 100644
--- a/talk/xmpp/constants.cc
+++ b/talk/xmpp/constants.cc
@@ -137,8 +137,13 @@
 const std::string STR_BOTH("both");
 const std::string STR_REMOVE("remove");
 
-const std::string STR_UNAVAILABLE("unavailable");
-
+const std::string STR_TYPE("type");
+const std::string STR_NAME("name");
+const std::string STR_ID("id");
+const std::string STR_JID("jid");
+const std::string STR_SUBSCRIPTION("subscription");
+const std::string STR_ASK("ask");
+const std::string STR_X("x");
 const std::string STR_GOOGLE_COM("google.com");
 const std::string STR_GMAIL_COM("gmail.com");
 const std::string STR_GOOGLEMAIL_COM("googlemail.com");
@@ -146,13 +151,14 @@
 const std::string STR_TALK_GOOGLE_COM("talk.google.com");
 const std::string STR_TALKX_L_GOOGLE_COM("talkx.l.google.com");
 
-const std::string STR_X("x");
-
 #ifdef FEATURE_ENABLE_VOICEMAIL
 const std::string STR_VOICEMAIL("voicemail");
 const std::string STR_OUTGOINGVOICEMAIL("outgoingvoicemail");
 #endif
 
+const std::string STR_UNAVAILABLE("unavailable");
+
+
 const QName QN_STREAM_STREAM(true, NS_STREAM, STR_STREAM);
 const QName QN_STREAM_FEATURES(true, NS_STREAM, "features");
 const QName QN_STREAM_ERROR(true, NS_STREAM, "error");
@@ -296,13 +302,6 @@
 
 const QName QN_XML_LANG(true, NS_XML, "lang");
 
-const std::string STR_TYPE("type");
-const std::string STR_ID("id");
-const std::string STR_NAME("name");
-const std::string STR_JID("jid");
-const std::string STR_SUBSCRIPTION("subscription");
-const std::string STR_ASK("ask");
-
 const QName QN_ENCODING(true, STR_EMPTY, STR_ENCODING);
 const QName QN_VERSION(true, STR_EMPTY, STR_VERSION);
 const QName QN_TO(true, STR_EMPTY, "to");
@@ -328,7 +327,6 @@
 const QName QN_XMLNS_STREAM(true, NS_XMLNS, STR_STREAM);
 
 
-
 // Presence
 const std::string STR_SHOW_AWAY("away");
 const std::string STR_SHOW_CHAT("chat");
@@ -377,12 +375,26 @@
 const QName QN_DISCO_ITEMS_QUERY(true, NS_DISCO_ITEMS, "query");
 const QName QN_DISCO_ITEM(true, NS_DISCO_ITEMS, "item");
 
+
+// JEP 0045
+const std::string NS_MUC("http://jabber.org/protocol/muc");
+const QName QN_MUC_X(true, NS_MUC, "x");
+const QName QN_MUC_ITEM(true, NS_MUC, "item");
+const QName QN_MUC_AFFILIATION(true, NS_MUC, "affiliation");
+const QName QN_MUC_ROLE(true, NS_MUC, "role");
+const std::string STR_AFFILIATION_NONE("none");
+const std::string STR_ROLE_PARTICIPANT("participant");
+
+const std::string NS_MUC_OWNER("http://jabber.org/protocol/muc#owner");
+const QName QN_MUC_OWNER_QUERY(true, NS_MUC_OWNER, "query");
+
 const std::string NS_MUC_USER("http://jabber.org/protocol/muc#user");
 const QName QN_MUC_USER_CONTINUE(true, NS_MUC_USER, "continue");
 const QName QN_MUC_USER_X(true, NS_MUC_USER, "x");
 const QName QN_MUC_USER_ITEM(true, NS_MUC_USER, "item");
 const QName QN_MUC_USER_STATUS(true, NS_MUC_USER, "status");
 
+
 // JEP 0115
 const std::string NS_CAPS("http://jabber.org/protocol/caps");
 const QName QN_CAPS_C(true, NS_CAPS, "c");
diff --git a/talk/xmpp/constants.h b/talk/xmpp/constants.h
index c78f7f7..8df83b9 100644
--- a/talk/xmpp/constants.h
+++ b/talk/xmpp/constants.h
@@ -91,14 +91,6 @@
 extern const std::string STR_BOTH;
 extern const std::string STR_REMOVE;
 
-extern const std::string STR_MESSAGE;
-extern const std::string STR_BODY;
-extern const std::string STR_PRESENCE;
-extern const std::string STR_STATUS;
-extern const std::string STR_SHOW;
-extern const std::string STR_PRIOIRTY;
-extern const std::string STR_IQ;
-
 extern const std::string STR_TYPE;
 extern const std::string STR_NAME;
 extern const std::string STR_ID;
@@ -120,6 +112,7 @@
 
 extern const std::string STR_UNAVAILABLE;
 
+
 extern const QName QN_STREAM_STREAM;
 extern const QName QN_STREAM_FEATURES;
 extern const QName QN_STREAM_ERROR;
@@ -357,6 +350,10 @@
 extern const QName QN_MUC_ROLE;
 extern const std::string STR_AFFILIATION_NONE;
 extern const std::string STR_ROLE_PARTICIPANT;
+
+extern const std::string NS_MUC_OWNER;
+extern const QName QN_MUC_OWNER_QUERY;
+
 extern const std::string NS_MUC_USER;
 extern const QName QN_MUC_USER_CONTINUE;
 extern const QName QN_MUC_USER_X;