* VideoCapture refactoring.
* Bug fixes.
Review URL: https://webrtc-codereview.appspot.com/350010

git-svn-id: http://libjingle.googlecode.com/svn/trunk@108 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/app/webrtcv1/webrtcsession.cc b/talk/app/webrtcv1/webrtcsession.cc
index 057cbda..dc2364d 100644
--- a/talk/app/webrtcv1/webrtcsession.cc
+++ b/talk/app/webrtcv1/webrtcsession.cc
@@ -122,7 +122,7 @@
   // Limit the amount of time that setting up a call may take.
   StartTransportTimeout(kCallSetupTimeout);
   // Set default secure option to SEC_REQUIRED.
-  desc_factory_.set_secure(cricket::SEC_REQUIRED);
+  // desc_factory_.set_secure(cricket::SEC_REQUIRED);
   return true;
 }
 
@@ -444,9 +444,7 @@
 
   transport_->ConnectChannels();
   EnableAllStreams();
-  if (!SetVideoCapture(true)) {
-    return false;
-  }
+  SetVideoCapture(true);
 
   set_local_description(answer.release());
 
diff --git a/talk/base/stream.cc b/talk/base/stream.cc
index dbe13bd..24a97dc 100644
--- a/talk/base/stream.cc
+++ b/talk/base/stream.cc
@@ -735,6 +735,12 @@
   // all events are done on the owner_ thread
 }
 
+FifoBuffer::FifoBuffer(size_t size, Thread *owner)
+    : state_(SS_OPEN), buffer_(new char[size]), buffer_length_(size),
+      data_length_(0), read_position_(0), owner_(owner) {
+  // all events are done on the owner_ thread
+}
+
 FifoBuffer::~FifoBuffer() {
 }
 
diff --git a/talk/base/stream.h b/talk/base/stream.h
index 15a706c..808156c 100644
--- a/talk/base/stream.h
+++ b/talk/base/stream.h
@@ -129,7 +129,7 @@
   // latter operation but not the former.
   //
 
-  // The following four methods are used to avoid coping data multiple times.
+  // The following four methods are used to avoid copying data multiple times.
 
   // GetReadData returns a pointer to a buffer which is owned by the stream.
   // The buffer contains data_len bytes.  NULL is returned if no data is
@@ -560,6 +560,8 @@
  public:
   // Creates a FIFO buffer with the specified capacity.
   explicit FifoBuffer(size_t length);
+  // Creates a FIFO buffer with the specified capacity and owner
+  FifoBuffer(size_t length, Thread* owner);
   virtual ~FifoBuffer();
   // Gets the amount of data currently readable from the buffer.
   bool GetBuffered(size_t* data_len) const;
diff --git a/talk/libjingle.scons b/talk/libjingle.scons
index a31009e..4cfed14 100644
--- a/talk/libjingle.scons
+++ b/talk/libjingle.scons
@@ -248,6 +248,7 @@
                "session/phone/soundclip.cc",
                "session/phone/srtpfilter.cc",
                "session/phone/ssrcmuxfilter.cc",
+               "session/phone/streamparams.cc",
                "session/phone/videoadapter.cc",
                "session/phone/videocapturer.cc",
                "session/phone/videocommon.cc",
diff --git a/talk/session/phone/call.cc b/talk/session/phone/call.cc
index 4b2e229..3c770af 100644
--- a/talk/session/phone/call.cc
+++ b/talk/session/phone/call.cc
@@ -181,30 +181,28 @@
 void Call::AddVoiceStream(Session *session, uint32 voice_ssrc) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
   if (voice_channel && voice_ssrc) {
-    voice_channel->AddStream(voice_ssrc);
+    voice_channel->AddRecvStream(StreamParams::CreateLegacy(voice_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?
-    // It doesn't seem to be used.
-    video_channel->AddStream(video_ssrc, 0U);
+    video_channel->AddRecvStream(StreamParams::CreateLegacy(video_ssrc));
   }
 }
 
 void Call::RemoveVoiceStream(Session *session, uint32 voice_ssrc) {
   VoiceChannel *voice_channel = GetVoiceChannel(session);
   if (voice_channel && voice_ssrc) {
-    voice_channel->RemoveStream(voice_ssrc);
+    voice_channel->RemoveRecvStream(voice_ssrc);
   }
 }
 
 void Call::RemoveVideoStream(Session *session, uint32 video_ssrc) {
   VideoChannel *video_channel = GetVideoChannel(session);
   if (video_channel && video_ssrc) {
-    video_channel->RemoveStream(video_ssrc);
+    video_channel->RemoveRecvStream(video_ssrc);
   }
 }
 
@@ -239,13 +237,6 @@
   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);
@@ -257,7 +248,6 @@
   // 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 {
@@ -271,7 +261,6 @@
     // 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 {
diff --git a/talk/session/phone/channel.cc b/talk/session/phone/channel.cc
index cbd670d..a58734f 100644
--- a/talk/session/phone/channel.cc
+++ b/talk/session/phone/channel.cc
@@ -61,6 +61,18 @@
   VideoMediaChannel::Error error;
 };
 
+struct SsrcMessageData : public talk_base::MessageData {
+  explicit SsrcMessageData(uint32 ssrc) : ssrc(ssrc), result(false) {}
+  uint32 ssrc;
+  bool result;
+};
+
+struct StreamMessageData : public talk_base::MessageData {
+  explicit StreamMessageData(const StreamParams& sp) : sp(sp), result(false) {}
+  StreamParams sp;
+  bool result;
+};
+
 static const char* PacketType(bool rtcp) {
   return (!rtcp) ? "RTP" : "RTCP";
 }
@@ -146,15 +158,15 @@
   return true;
 }
 
-bool BaseChannel::RemoveStream(uint32 ssrc) {
-  StreamMessageData data(ssrc, 0);
-  Send(MSG_REMOVESTREAM, &data);
-  return true;
+bool BaseChannel::AddRecvStream(const StreamParams& sp) {
+  StreamMessageData data(sp);
+  Send(MSG_ADDRECVSTREAM, &data);
+  return data.result;
 }
 
-bool BaseChannel::SetRtcpCName(const std::string& cname) {
-  SetRtcpCNameData data(cname);
-  Send(MSG_SETRTCPCNAME, &data);
+bool BaseChannel::RemoveRecvStream(uint32 ssrc) {
+  SsrcMessageData data(ssrc);
+  Send(MSG_REMOVERECVSTREAM, &data);
   return data.result;
 }
 
@@ -518,10 +530,6 @@
   return media_channel()->SetSendBandwidth(true, max_bandwidth);
 }
 
-bool BaseChannel::SetRtcpCName_w(const std::string& cname) {
-  return media_channel()->SetRtcpCName(cname);
-}
-
 bool BaseChannel::SetSrtp_w(const std::vector<CryptoParams>& cryptos,
                             ContentAction action, ContentSource src) {
   bool ret;
@@ -558,14 +566,186 @@
   return ret;
 }
 
-bool BaseChannel::AddSsrcMuxStreams_w(
-    const std::vector<StreamParams>& streams) {
-  for (std::vector<StreamParams>::const_iterator it = streams.begin();
-       it != streams.end(); ++it)  {
-    if (!ssrc_filter_.AddStream(*it))
-      return false;
+bool BaseChannel::AddRecvStream_w(const StreamParams& sp) {
+  ASSERT(worker_thread() == talk_base::Thread::Current());
+  if (!media_channel()->AddRecvStream(sp))
+    return false;
+
+  return ssrc_filter_.AddStream(sp);
+}
+
+bool BaseChannel::RemoveRecvStream_w(uint32 ssrc) {
+  ASSERT(worker_thread() == talk_base::Thread::Current());
+  ssrc_filter_.RemoveStream(ssrc);
+  return media_channel()->RemoveRecvStream(ssrc);
+}
+
+bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
+                                       ContentAction action) {
+  if (!VERIFY(action == CA_OFFER || action == CA_ANSWER || action == CA_UPDATE))
+    return false;
+
+  // If this is an update, streams only contain streams that have changed.
+  if (action == CA_UPDATE) {
+    for (StreamParamsVec::const_iterator it = streams.begin();
+         it != streams.end(); ++it) {
+      StreamParams existing_stream;
+      bool stream_exist = GetStreamByNickAndName(local_streams_, it->nick,
+                                                 it->name, &existing_stream);
+      if (!stream_exist && it->has_ssrcs()) {
+        if (media_channel()->AddSendStream(*it)) {
+          local_streams_.push_back(*it);
+          LOG(LS_INFO) << "Add send stream ssrc: " << it->first_ssrc();
+        } else {
+          LOG(LS_INFO) << "Failed to add send stream ssrc: "
+                       << it->first_ssrc();
+          return false;
+        }
+      } else if (stream_exist) {
+        if (!media_channel()->RemoveSendStream(existing_stream.first_ssrc())) {
+            LOG(LS_ERROR) << "Failed to remove send stream with ssrc "
+                          << it->first_ssrc() << ".";
+            return false;
+        }
+        RemoveStreamBySsrc(&local_streams_, existing_stream.first_ssrc());
+      } else {
+        LOG(LS_ERROR) << "Unknown send stream update.";
+        return false;
+      }
+    }
+    return true;
   }
-  return true;
+  // Else streams are all the streams we want to send.
+
+  // Check for streams that have been removed.
+  bool ret = true;
+  for (StreamParamsVec::const_iterator it = local_streams_.begin();
+       it != local_streams_.end(); ++it) {
+    if (!GetStreamBySsrc(streams, it->first_ssrc(), NULL)) {
+      if (!media_channel()->RemoveSendStream(it->first_ssrc())) {
+        LOG(LS_ERROR) << "Failed to remove send stream with ssrc "
+                      << it->first_ssrc() << ".";
+        ret = false;
+      }
+    }
+  }
+  // Check for new streams.
+  for (StreamParamsVec::const_iterator it = streams.begin();
+       it != streams.end(); ++it) {
+    if (!GetStreamBySsrc(local_streams_, it->first_ssrc(), NULL)) {
+      if (media_channel()->AddSendStream(*it)) {
+        LOG(LS_INFO) << "Add send ssrc: " << it->ssrcs[0];
+      } else {
+        LOG(LS_INFO) << "Failed to add send stream ssrc: " << it->first_ssrc();
+        ret = false;
+      }
+    }
+  }
+  local_streams_ = streams;
+  return ret;
+}
+
+bool BaseChannel::UpdateRemoteStreams_w(
+    const std::vector<StreamParams>& streams,
+    ContentAction action) {
+  // If this is an update, streams only contain streams that have changed.
+  if (action == CA_UPDATE) {
+    for (StreamParamsVec::const_iterator it = streams.begin();
+         it != streams.end(); ++it) {
+      StreamParams existing_stream;
+      bool stream_exists = GetStreamByNickAndName(remote_streams_, it->nick,
+                                                  it->name, &existing_stream);
+      if (!stream_exists && it->has_ssrcs()) {
+        if (AddRecvStream_w(*it)) {
+          remote_streams_.push_back(*it);
+          LOG(LS_INFO) << "Add remote stream ssrc: " << it->first_ssrc();
+        } else {
+          LOG(LS_INFO) << "Failed to add remote stream ssrc: "
+                       << it->first_ssrc();
+          return false;
+        }
+      } else if (stream_exists) {
+        if (!RemoveRecvStream_w(existing_stream.first_ssrc())) {
+            LOG(LS_ERROR) << "Failed to remove remote stream with ssrc "
+                          << it->first_ssrc() << ".";
+            return false;
+        }
+        RemoveStreamBySsrc(&remote_streams_, existing_stream.first_ssrc());
+      } else {
+        LOG(LS_ERROR) << "Unknown remote stream update.";
+        return false;
+      }
+    }
+    return true;
+  }
+  // Else streams are all the streams we want to receive.
+
+  // Check for streams that have been removed.
+  bool ret = true;
+  for (StreamParamsVec::const_iterator it = remote_streams_.begin();
+       it != remote_streams_.end(); ++it) {
+    if (!GetStreamBySsrc(streams, it->first_ssrc(), NULL)) {
+      if (!RemoveRecvStream_w(it->first_ssrc())) {
+        LOG(LS_ERROR) << "Failed to remove remote stream with ssrc "
+                      << it->first_ssrc() << ".";
+        ret = false;
+      }
+    }
+  }
+  // Check for new streams.
+  for (StreamParamsVec::const_iterator it = streams.begin();
+      it != streams.end(); ++it) {
+    if (!GetStreamBySsrc(remote_streams_, it->first_ssrc(), NULL)) {
+      if (AddRecvStream_w(*it)) {
+        LOG(LS_INFO) << "Add remote ssrc: " << it->ssrcs[0];
+      } else {
+        LOG(LS_INFO) << "Failed to add remote stream ssrc: "
+                     << it->first_ssrc();
+        ret = false;
+      }
+    }
+  }
+  remote_streams_ = streams;
+  return ret;
+}
+
+bool BaseChannel::SetBaseLocalContent_w(const MediaContentDescription* content,
+                                        ContentAction action) {
+  bool ret = UpdateLocalStreams_w(content->streams(), action);
+  // Set local SRTP parameters (what we will encrypt with).
+  if (ret) {
+    ret = SetSrtp_w(content->cryptos(), action, CS_LOCAL);
+  }
+  // Set local RTCP mux parameters.
+  if (ret) {
+    ret = SetRtcpMux_w(content->rtcp_mux(), action, CS_LOCAL);
+  }
+  // Set local RTP header extensions.
+  if (ret && content->rtp_header_extensions_set()) {
+    ret = media_channel()->SetRecvRtpHeaderExtensions(
+        content->rtp_header_extensions());
+  }
+  return ret;
+}
+
+bool BaseChannel::SetBaseRemoteContent_w(const MediaContentDescription* content,
+                                         ContentAction action) {
+  bool ret = UpdateRemoteStreams_w(content->streams(), action);
+  // Set remote SRTP parameters (what the other side will encrypt with).
+  if (ret) {
+    ret = SetSrtp_w(content->cryptos(), action, CS_REMOTE);
+  }
+  // Set remote RTCP mux parameters.
+  if (ret) {
+    ret = SetRtcpMux_w(content->rtcp_mux(), action, CS_REMOTE);
+  }
+
+  // Set remote RTP header extensions.
+  if (ret && content->rtp_header_extensions_set()) {
+    ret = media_channel()->SetSendRtpHeaderExtensions(
+        content->rtp_header_extensions());
+  }
+  return ret;
 }
 
 void BaseChannel::OnMessage(talk_base::Message *pmsg) {
@@ -583,13 +763,6 @@
     case MSG_UNMUTE:
       UnmuteMedia_w();
       break;
-
-    case MSG_SETRTCPCNAME: {
-      SetRtcpCNameData* data = static_cast<SetRtcpCNameData*>(pmsg->pdata);
-      data->result = SetRtcpCName_w(data->cname);
-      break;
-    }
-
     case MSG_SETLOCALCONTENT: {
       SetContentData* data = static_cast<SetContentData*>(pmsg->pdata);
       data->result = SetLocalContent_w(data->content, data->action);
@@ -600,13 +773,16 @@
       data->result = SetRemoteContent_w(data->content, data->action);
       break;
     }
-
-    case MSG_REMOVESTREAM: {
+    case MSG_ADDRECVSTREAM: {
       StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
-      RemoveStream_w(data->ssrc1);
+      data->result = AddRecvStream_w(data->sp);
       break;
     }
-
+    case MSG_REMOVERECVSTREAM: {
+      SsrcMessageData* data = static_cast<SsrcMessageData*>(pmsg->pdata);
+      data->result = RemoveRecvStream_w(data->ssrc);
+      break;
+    }
     case MSG_SETMAXSENDBANDWIDTH: {
       SetBandwidthData* data = static_cast<SetBandwidthData*>(pmsg->pdata);
       data->result = SetMaxSendBandwidth_w(data->value);
@@ -686,12 +862,6 @@
   return true;
 }
 
-bool VoiceChannel::AddStream(uint32 ssrc) {
-  StreamMessageData data(ssrc, 0);
-  Send(MSG_ADDSTREAM, &data);
-  return true;
-}
-
 bool VoiceChannel::SetRingbackTone(const void* buf, int len) {
   SetRingbackToneMessageData data(buf, len);
   Send(MSG_SETRINGBACKTONE, &data);
@@ -795,7 +965,7 @@
   if (!media_channel()->SetPlayout(recv)) {
     SendLastMediaError();
   }
-    
+
   // Send outgoing data if we're the active call, we have the remote content,
   // and we have had some form of connectivity.
   bool send = enabled() && has_remote_content() && was_ever_writable();
@@ -826,27 +996,13 @@
       static_cast<const AudioContentDescription*>(content);
   ASSERT(audio != NULL);
 
-  bool ret;
-  if (audio->has_ssrcs()) {
-    // TODO: Handle multiple streams and ssrcs here.
-    media_channel()->SetSendSsrc(audio->first_ssrc());
-    LOG(LS_INFO) << "Set send ssrc for audio: " << audio->first_ssrc();
-  }
-  // Set local SRTP parameters (what we will encrypt with).
-  ret = SetSrtp_w(audio->cryptos(), action, CS_LOCAL);
-  // Set local RTCP mux parameters.
-  if (ret) {
-    ret = SetRtcpMux_w(audio->rtcp_mux(), action, CS_LOCAL);
-  }
+  bool ret = SetBaseLocalContent_w(content, action);
+
   // Set local audio codecs (what we want to receive).
   if (ret) {
     ret = media_channel()->SetRecvCodecs(audio->codecs());
   }
-  // Set local RTP header extensions.
-  if (ret && audio->rtp_header_extensions_set()) {
-    ret = media_channel()->SetRecvRtpHeaderExtensions(
-        audio->rtp_header_extensions());
-  }
+
   // If everything worked, see if we can start receiving.
   if (ret) {
     set_has_local_content(true);
@@ -866,28 +1022,11 @@
       static_cast<const AudioContentDescription*>(content);
   ASSERT(audio != NULL);
 
-  bool ret;
-  // Set remote SRTP parameters (what the other side will encrypt with).
-  ret = SetSrtp_w(audio->cryptos(), action, CS_REMOTE);
-  // Set remote RTCP mux parameters.
-  if (ret) {
-    ret = SetRtcpMux_w(audio->rtcp_mux(), action, CS_REMOTE);
-  }
-  // Set SSRC mux filter
-  if (ret) {
-    // TODO: Refactor adding / removing to the ssrc mux filter
-    // when updating receiving streams based on the streams in content.
-    ret = AddSsrcMuxStreams_w(audio->streams());
-  }
-
   // Set remote video codecs (what the other side wants to receive).
+  bool ret = media_channel()->SetSendCodecs(audio->codecs());
+
   if (ret) {
-    ret = media_channel()->SetSendCodecs(audio->codecs());
-  }
-  // Set remote RTP header extensions.
-  if (ret && audio->rtp_header_extensions_set()) {
-    ret = media_channel()->SetSendRtpHeaderExtensions(
-        audio->rtp_header_extensions());
+    ret =  SetBaseRemoteContent_w(content, action);
   }
 
   // Tweak our audio processing settings, if needed.
@@ -913,19 +1052,6 @@
   return ret;
 }
 
-void VoiceChannel::AddStream_w(uint32 ssrc) {
-  ASSERT(worker_thread() == talk_base::Thread::Current());
-  media_channel()->AddStream(ssrc);
-  // TODO: Work is ongoing to refactor AddStream_w to take
-  // StreamParams as input.
-  ssrc_filter()->AddStream(StreamParams::CreateLegacy(ssrc));
-}
-
-void VoiceChannel::RemoveStream_w(uint32 ssrc) {
-  media_channel()->RemoveStream(ssrc);
-  ssrc_filter()->RemoveStream(ssrc);
-}
-
 bool VoiceChannel::SetRingbackTone_w(const void* buf, int len) {
   ASSERT(worker_thread() == talk_base::Thread::Current());
   return media_channel()->SetRingbackTone(static_cast<const char*>(buf), len);
@@ -963,11 +1089,6 @@
 
 void VoiceChannel::OnMessage(talk_base::Message *pmsg) {
   switch (pmsg->message_id) {
-    case MSG_ADDSTREAM: {
-      StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
-      AddStream_w(data->ssrc1);
-      break;
-    }
     case MSG_SETRINGBACKTONE: {
       SetRingbackToneMessageData* data =
           static_cast<SetRingbackToneMessageData*>(pmsg->pdata);
@@ -1100,12 +1221,6 @@
   DisableMedia_w();
 }
 
-bool VideoChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) {
-  StreamMessageData data(ssrc, voice_ssrc);
-  Send(MSG_ADDSTREAM, &data);
-  return true;
-}
-
 bool VideoChannel::SetRenderer(uint32 ssrc, VideoRenderer* renderer) {
   RenderMessageData data(ssrc, renderer);
   Send(MSG_SETRENDERER, &data);
@@ -1191,28 +1306,13 @@
       static_cast<const VideoContentDescription*>(content);
   ASSERT(video != NULL);
 
-  bool ret;
-  if (video->has_ssrcs()) {
-    // TODO: Handle multiple streams and ssrcs here.
-    media_channel()->SetSendSsrc(video->first_ssrc());
-    LOG(LS_INFO) << "Set send ssrc for video: " << video->first_ssrc();
-  }
-  // Set local SRTP parameters (what we will encrypt with).
-  ret = SetSrtp_w(video->cryptos(), action, CS_LOCAL);
-  // Set local RTCP mux parameters.
-  if (ret) {
-    ret = SetRtcpMux_w(video->rtcp_mux(), action, CS_LOCAL);
-  }
+  bool ret = SetBaseLocalContent_w(content, action);
 
   // Set local video codecs (what we want to receive).
   if (ret) {
     ret = media_channel()->SetRecvCodecs(video->codecs());
   }
-  // Set local RTP header extensions.
-  if (ret && video->rtp_header_extensions_set()) {
-    ret = media_channel()->SetRecvRtpHeaderExtensions(
-        video->rtp_header_extensions());
-  }
+
   // If everything worked, see if we can start receiving.
   if (ret) {
     set_has_local_content(true);
@@ -1232,27 +1332,11 @@
       static_cast<const VideoContentDescription*>(content);
   ASSERT(video != NULL);
 
-  bool ret;
-  // Set remote SRTP parameters (what the other side will encrypt with).
-  ret = SetSrtp_w(video->cryptos(), action, CS_REMOTE);
-  // Set remote RTCP mux parameters.
-  if (ret) {
-    ret = SetRtcpMux_w(video->rtcp_mux(), action, CS_REMOTE);
-  }
-  // Set SSRC mux filter
-  if (ret) {
-    // TODO: Refactor adding / removing to the ssrc mux filter
-    // when updating receiving streams based on the streams in content.
-    ret = AddSsrcMuxStreams_w(video->streams());
-  }
   // Set remote video codecs (what the other side wants to receive).
+  bool ret = media_channel()->SetSendCodecs(video->codecs());
+
   if (ret) {
-    ret = media_channel()->SetSendCodecs(video->codecs());
-  }
-  // Set remote RTP header extensions.
-  if (ret && video->rtp_header_extensions_set()) {
-    ret = media_channel()->SetSendRtpHeaderExtensions(
-        video->rtp_header_extensions());
+    ret =  SetBaseRemoteContent_w(content, action);
   }
   // Tweak our video processing settings, if needed.
   int video_options = 0;
@@ -1279,18 +1363,6 @@
   return ret;
 }
 
-void VideoChannel::AddStream_w(uint32 ssrc, uint32 voice_ssrc) {
-  media_channel()->AddStream(ssrc, voice_ssrc);
-  // TODO: Work is ongoing to refactor AddStream_w to take
-  // StreamParams as input.
-  ssrc_filter()->AddStream(StreamParams::CreateLegacy(ssrc));
-}
-
-void VideoChannel::RemoveStream_w(uint32 ssrc) {
-  media_channel()->RemoveStream(ssrc);
-  ssrc_filter()->RemoveStream(ssrc);
-}
-
 void VideoChannel::SetRenderer_w(uint32 ssrc, VideoRenderer* renderer) {
   media_channel()->SetRenderer(ssrc, renderer);
 }
@@ -1311,11 +1383,6 @@
 
 void VideoChannel::OnMessage(talk_base::Message *pmsg) {
   switch (pmsg->message_id) {
-    case MSG_ADDSTREAM: {
-      StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
-      AddStream_w(data->ssrc1, data->ssrc2);
-      break;
-    }
     case MSG_SETRENDERER: {
       RenderMessageData* data = static_cast<RenderMessageData*>(pmsg->pdata);
       SetRenderer_w(data->ssrc, data->renderer);
diff --git a/talk/session/phone/channel.h b/talk/session/phone/channel.h
index 3272d4b..f262e00 100644
--- a/talk/session/phone/channel.h
+++ b/talk/session/phone/channel.h
@@ -45,6 +45,7 @@
 #include "talk/session/phone/rtcpmuxfilter.h"
 #include "talk/session/phone/screencastid.h"
 #include "talk/session/phone/ssrcmuxfilter.h"
+#include "talk/session/phone/streamparams.h"
 #include "talk/session/phone/srtpfilter.h"
 
 namespace cricket {
@@ -62,14 +63,14 @@
   MSG_EARLYMEDIATIMEOUT = 8,
   MSG_PRESSDTMF = 9,
   MSG_SETRENDERER = 10,
-  MSG_ADDSTREAM = 11,
-  MSG_REMOVESTREAM = 12,
+  MSG_ADDRECVSTREAM = 11,
+  MSG_REMOVERECVSTREAM = 12,
   MSG_SETRINGBACKTONE = 13,
   MSG_PLAYRINGBACKTONE = 14,
   MSG_SETMAXSENDBANDWIDTH = 15,
   MSG_ADDSCREENCAST = 16,
   MSG_REMOVESCREENCAST = 17,
-  MSG_SETRTCPCNAME = 18,
+  // Removed MSG_SETRTCPCNAME = 18. It is no longer used.
   MSG_SENDINTRAFRAME = 19,
   MSG_REQUESTINTRAFRAME = 20,
   MSG_SCREENCASTWINDOWEVENT = 21,
@@ -108,7 +109,6 @@
   bool secure() const { return srtp_filter_.IsActive(); }
 
   // Channel control
-  bool SetRtcpCName(const std::string& cname);
   bool SetLocalContent(const MediaContentDescription* content,
                        ContentAction action);
   bool SetRemoteContent(const MediaContentDescription* content,
@@ -119,7 +119,8 @@
   bool Mute(bool mute);
 
   // Multiplexing
-  bool RemoveStream(uint32 ssrc);
+  bool AddRecvStream(const StreamParams& sp);
+  bool RemoveRecvStream(uint32 ssrc);
 
   // Monitoring
   void StartConnectionMonitor(int cms);
@@ -215,24 +216,10 @@
   void UnmuteMedia_w();
   void ChannelWritable_w();
   void ChannelNotWritable_w();
-
-  struct StreamMessageData : public talk_base::MessageData {
-    StreamMessageData(uint32 s1, uint32 s2) : ssrc1(s1), ssrc2(s2) {}
-    uint32 ssrc1;
-    uint32 ssrc2;
-  };
-  virtual void RemoveStream_w(uint32 ssrc) = 0;
+  bool AddRecvStream_w(const StreamParams& sp);
+  bool RemoveRecvStream_w(uint32 ssrc);
 
   virtual void ChangeState() = 0;
-
-  struct SetRtcpCNameData : public talk_base::MessageData {
-    explicit SetRtcpCNameData(const std::string& cname)
-        : cname(cname), result(false) {}
-    std::string cname;
-    bool result;
-  };
-  bool SetRtcpCName_w(const std::string& cname);
-
   struct SetContentData : public talk_base::MessageData {
     SetContentData(const MediaContentDescription* content,
                    ContentAction action)
@@ -245,8 +232,16 @@
   // Gets the content appropriate to the channel (audio or video).
   virtual const MediaContentDescription* GetFirstContent(
       const SessionDescription* sdesc) = 0;
+  bool UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
+                            ContentAction action);
+  bool UpdateRemoteStreams_w(const std::vector<StreamParams>& streams,
+                             ContentAction action);
+  bool SetBaseLocalContent_w(const MediaContentDescription* content,
+                             ContentAction action);
   virtual bool SetLocalContent_w(const MediaContentDescription* content,
                                  ContentAction action) = 0;
+  bool SetBaseRemoteContent_w(const MediaContentDescription* content,
+                              ContentAction action);
   virtual bool SetRemoteContent_w(const MediaContentDescription* content,
                                   ContentAction action) = 0;
 
@@ -254,9 +249,6 @@
                  ContentSource src);
   bool SetRtcpMux_w(bool enable, ContentAction action, ContentSource src);
 
-  // SSRC mux handling methods.
-  bool AddSsrcMuxStreams_w(const std::vector<StreamParams>& streams);
-
   struct SetBandwidthData : public talk_base::MessageData {
     explicit SetBandwidthData(int value) : value(value), result(false) {}
     int value;
@@ -281,6 +273,8 @@
   MediaEngineInterface *media_engine_;
   BaseSession *session_;
   MediaChannel *media_channel_;
+  std::vector<StreamParams> local_streams_;
+  std::vector<StreamParams> remote_streams_;
 
   std::string content_name_;
   bool rtcp_;
@@ -313,9 +307,6 @@
     return static_cast<VoiceMediaChannel*>(BaseChannel::media_channel());
   }
 
-  // Add an incoming stream with the specified SSRC.
-  bool AddStream(uint32 ssrc);
-
   bool SetRingbackTone(const void* buf, int len);
   void SetEarlyMedia(bool enable);
   // This signal is emitted when we have gone a period of time without
@@ -412,9 +403,6 @@
   virtual bool SetRemoteContent_w(const MediaContentDescription* content,
                                   ContentAction action);
 
-  void AddStream_w(uint32 ssrc);
-  void RemoveStream_w(uint32 ssrc);
-
   bool SetRingbackTone_w(const void* buf, int len);
   bool PlayRingbackTone_w(uint32 ssrc, bool play, bool loop);
   void HandleEarlyMediaTimeout();
@@ -454,9 +442,6 @@
     return static_cast<VideoMediaChannel*>(BaseChannel::media_channel());
   }
 
-  // Add an incoming stream with the specified SSRC.
-  bool AddStream(uint32 ssrc, uint32 voice_ssrc);
-
   bool SetRenderer(uint32 ssrc, VideoRenderer* renderer);
 
   bool AddScreencast(uint32 ssrc, const ScreencastId& id);
@@ -487,9 +472,6 @@
   virtual bool SetRemoteContent_w(const MediaContentDescription* content,
                                   ContentAction action);
 
-  void AddStream_w(uint32 ssrc, uint32 voice_ssrc);
-  void RemoveStream_w(uint32 ssrc);
-
   void SendIntraFrame_w() {
     media_channel()->SendIntraFrame();
   }
diff --git a/talk/session/phone/channel_unittest.cc b/talk/session/phone/channel_unittest.cc
index 86d3b6d..b53cca0 100644
--- a/talk/session/phone/channel_unittest.cc
+++ b/talk/session/phone/channel_unittest.cc
@@ -40,6 +40,7 @@
 using cricket::CA_OFFER;
 using cricket::CA_ANSWER;
 using cricket::CA_UPDATE;
+using cricket::StreamParams;
 
 static const cricket::AudioCodec kPcmuCodec(0, "PCMU", 64000, 8000, 1, 0);
 static const cricket::AudioCodec kPcmaCodec(8, "PCMA", 64000, 8000, 1, 0);
@@ -48,6 +49,8 @@
 static const cricket::VideoCodec kH264SvcCodec(99, "H264-SVC", 320, 200, 15, 0);
 static const uint32 kSsrc1 = 0x1111;
 static const uint32 kSsrc2 = 0x2222;
+static const uint32 kSsrc3 = 0x3333;
+static const char kCName[] = "a@b.com";
 
 class VoiceTraits {
  public:
@@ -123,10 +126,23 @@
         this, &ChannelTest<T>::OnMediaChannelError);
     channel2_->SignalMediaError.connect(
         this, &ChannelTest<T>::OnMediaChannelError);
-    CreateContent(flags1, kPcmuCodec, kH264Codec, &media_content1_);
-    CreateContent(flags2, kPcmuCodec, kH264Codec, &media_content2_);
-    AddLegacyStreamInContent(kSsrc1, flags1, &media_content1_);
-    AddLegacyStreamInContent(kSsrc2, flags2, &media_content2_);
+    CreateContent(flags1, kPcmuCodec, kH264Codec, &local_media_content1_);
+    CreateContent(flags2, kPcmuCodec, kH264Codec, &local_media_content2_);
+    CopyContent(local_media_content1_, &remote_media_content1_);
+    CopyContent(local_media_content2_, &remote_media_content2_);
+    // Add stream information (SSRC) to the local content but not to the remote
+    // content. This means that we per default know the SSRC of what we send but
+    // not what we receive.
+    AddLegacyStreamInContent(kSsrc1, flags1, &local_media_content1_);
+    AddLegacyStreamInContent(kSsrc2, flags2, &local_media_content2_);
+
+    // If SSRC_MUX is used we also need to know the SSRC of the incoming stream.
+    if (flags1 & SSRC_MUX) {
+      AddLegacyStreamInContent(kSsrc1, flags1, &remote_media_content1_);
+    }
+    if (flags2 & SSRC_MUX) {
+      AddLegacyStreamInContent(kSsrc2, flags2, &remote_media_content2_);
+    }
   }
 
   void CreateChannels(
@@ -144,10 +160,21 @@
         this, &ChannelTest<T>::OnMediaMonitor);
     channel2_->SignalMediaError.connect(
         this, &ChannelTest<T>::OnMediaChannelError);
-    CreateContent(flags, kPcmuCodec, kH264Codec, &media_content1_);
-    CreateContent(flags, kPcmuCodec, kH264Codec, &media_content2_);
-    AddLegacyStreamInContent(kSsrc1, flags, &media_content1_);
-    AddLegacyStreamInContent(kSsrc2, flags, &media_content2_);
+    CreateContent(flags, kPcmuCodec, kH264Codec, &local_media_content1_);
+    CreateContent(flags, kPcmuCodec, kH264Codec, &local_media_content2_);
+    CopyContent(local_media_content1_, &remote_media_content1_);
+    CopyContent(local_media_content2_, &remote_media_content2_);
+    // Add stream information (SSRC) to the local content but not to the remote
+    // content. This means that we per default know the SSRC of what we send but
+    // not what we receive.
+    AddLegacyStreamInContent(kSsrc1, flags, &local_media_content1_);
+    AddLegacyStreamInContent(kSsrc2, flags, &local_media_content2_);
+
+    // If SSRC_MUX is used we also need to know the SSRC of the incoming stream.
+    if (flags & SSRC_MUX) {
+      AddLegacyStreamInContent(kSsrc1, flags, &remote_media_content1_);
+      AddLegacyStreamInContent(kSsrc2, flags, &remote_media_content2_);
+    }
   }
   typename T::Channel* CreateChannel(talk_base::Thread* thread,
                                      cricket::MediaEngineInterface* engine,
@@ -164,12 +191,12 @@
   }
 
   bool SendInitiate() {
-    bool result = channel1_->SetLocalContent(&media_content1_, CA_OFFER);
+    bool result = channel1_->SetLocalContent(&local_media_content1_, CA_OFFER);
     if (result) {
       channel1_->Enable(true);
-      result = channel2_->SetRemoteContent(&media_content1_, CA_OFFER);
+      result = channel2_->SetRemoteContent(&remote_media_content1_, CA_OFFER);
       if (result) {
-        result = channel2_->SetLocalContent(&media_content2_, CA_ANSWER);
+        result = channel2_->SetLocalContent(&local_media_content2_, CA_ANSWER);
         if (result) {
           session1_.Connect(&session2_);
         }
@@ -179,7 +206,7 @@
   }
   bool SendAccept() {
     channel2_->Enable(true);
-    return channel1_->SetRemoteContent(&media_content2_, CA_ANSWER);
+    return channel1_->SetRemoteContent(&remote_media_content2_, CA_ANSWER);
   }
   bool SendTerminate() {
     channel1_.reset();
@@ -188,10 +215,10 @@
   }
 
   bool AddStream1(int id) {
-    return channel1_->AddStream(id);
+    return channel1_->AddRecvStream(cricket::StreamParams::CreateLegacy(id));
   }
   bool RemoveStream1(int id) {
-    return channel1_->RemoveStream(id);
+    return channel1_->RemoveRecvStream(id);
   }
 
   cricket::FakeTransport* GetTransport1() {
@@ -293,6 +320,10 @@
                      typename T::Content* content) {
     // overridden in specialized classes
   }
+  void CopyContent(const typename T::Content& source,
+                   typename T::Content* content) {
+    // overridden in specialized classes
+  }
 
   class CallThread : public talk_base::SignalThread {
    public:
@@ -361,21 +392,11 @@
     EXPECT_FALSE(media_channel1_->sending());
     EXPECT_FALSE(media_channel1_->playout());
     EXPECT_TRUE(media_channel1_->codecs().empty());
-    EXPECT_TRUE(media_channel1_->streams().empty());
+    EXPECT_TRUE(media_channel1_->recv_streams().empty());
     EXPECT_TRUE(media_channel1_->rtp_packets().empty());
     EXPECT_TRUE(media_channel1_->rtcp_packets().empty());
   }
 
-  // Test that SetRtcpCName sets the RTCP CNAME successfully.
-  void TestSetRtcpCName() {
-    static const char* kTestCName = "a@b.com";
-    CreateChannels(0, 0);
-    EXPECT_TRUE(channel1_->SetRtcpCName(kTestCName));
-    EXPECT_EQ(kTestCName, media_channel1_->rtcp_cname());
-    EXPECT_TRUE(channel2_->SetRtcpCName(kTestCName));
-    EXPECT_EQ(kTestCName, media_channel2_->rtcp_cname());
-  }
-
   // Test that SetLocalContent and SetRemoteContent properly configure
   // the codecs.
   void TestSetContents() {
@@ -454,11 +475,193 @@
     CreateChannels(0, 0);
     EXPECT_TRUE(AddStream1(1));
     EXPECT_TRUE(AddStream1(2));
-    EXPECT_EQ(2U, media_channel1_->streams().size());
+    EXPECT_EQ(2U, media_channel1_->recv_streams().size());
     EXPECT_TRUE(RemoveStream1(2));
-    EXPECT_EQ(1U, media_channel1_->streams().size());
+    EXPECT_EQ(1U, media_channel1_->recv_streams().size());
     EXPECT_TRUE(RemoveStream1(1));
-    EXPECT_EQ(0U, media_channel1_->streams().size());
+    EXPECT_EQ(0U, media_channel1_->recv_streams().size());
+  }
+
+  // Test that SetLocalContent properly handles adding and removing StreamParams
+  // to the local content description.
+  // This test uses the CA_UPDATE action that don't require a full
+  // MediaContentDescription to do an update.
+  void TestUpdateStreamsInLocalContent() {
+    cricket::StreamParams stream1;
+    stream1.name = "Stream1";
+    stream1.nick = "1";
+    stream1.ssrcs.push_back(kSsrc1);
+    stream1.cname = "stream1_cname";
+
+    cricket::StreamParams stream2;
+    stream2.name = "Stream2";
+    stream2.nick = "2";
+    stream2.ssrcs.push_back(kSsrc2);
+    stream2.cname = "stream2_cname";
+
+    cricket::StreamParams stream3;
+    stream3.name = "Stream3";
+    stream3.nick = "3";
+    stream3.ssrcs.push_back(kSsrc3);
+    stream3.cname = "stream3_cname";
+
+    CreateChannels(0, 0);
+    typename T::Content content1;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content1);
+    content1.AddStream(stream1);
+    EXPECT_EQ(0u, media_channel1_->send_streams().size());
+    EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER));
+
+    ASSERT_EQ(1u, media_channel1_->send_streams().size());
+    EXPECT_EQ(stream1, media_channel1_->send_streams()[0]);
+
+    // Update the local streams by adding another sending stream.
+    // Use a partial updated session description.
+    typename T::Content content2;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content2);
+    content2.AddStream(stream2);
+    content2.AddStream(stream3);
+    EXPECT_TRUE(channel1_->SetLocalContent(&content2, CA_UPDATE));
+    ASSERT_EQ(3u, media_channel1_->send_streams().size());
+    EXPECT_EQ(stream1, media_channel1_->send_streams()[0]);
+    EXPECT_EQ(stream2, media_channel1_->send_streams()[1]);
+    EXPECT_EQ(stream3, media_channel1_->send_streams()[2]);
+
+    // Update the local streams by removing the first sending stream.
+    // This is done by removing all SSRCS for this particular stream.
+    typename T::Content content3;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content3);
+    stream1.ssrcs.clear();
+    content3.AddStream(stream1);
+
+    EXPECT_TRUE(channel1_->SetLocalContent(&content3, CA_UPDATE));
+    ASSERT_EQ(2u, media_channel1_->send_streams().size());
+    EXPECT_EQ(stream2, media_channel1_->send_streams()[0]);
+    EXPECT_EQ(stream3, media_channel1_->send_streams()[1]);
+  }
+
+  // Test that SetRemoteContent properly handles adding and removing
+  // StreamParams to the remote content description.
+  // This test uses the CA_UPDATE action that don't require a full
+  // MediaContentDescription to do an update.
+  void TestUpdateStreamsInRemoteContent() {
+    cricket::StreamParams stream1;
+    stream1.name = "Stream1";
+    stream1.nick = "1";
+    stream1.ssrcs.push_back(kSsrc1);
+    stream1.cname = "stream1_cname";
+
+    cricket::StreamParams stream2;
+    stream2.name = "Stream2";
+    stream2.nick = "2";
+    stream2.ssrcs.push_back(kSsrc2);
+    stream2.cname = "stream2_cname";
+
+    cricket::StreamParams stream3;
+    stream3.name = "Stream3";
+    stream3.nick = "3";
+    stream3.ssrcs.push_back(kSsrc3);
+    stream3.cname = "stream3_cname";
+
+    CreateChannels(0, 0);
+    typename T::Content content1;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content1);
+    content1.AddStream(stream1);
+    EXPECT_EQ(0u, media_channel1_->recv_streams().size());
+    EXPECT_TRUE(channel1_->SetRemoteContent(&content1, CA_OFFER));
+
+    ASSERT_EQ(1u, media_channel1_->recv_streams().size());
+    EXPECT_EQ(stream1, media_channel1_->recv_streams()[0]);
+
+    // Update the local streams by adding another sending stream.
+    // Use a partial updated session description.
+    typename T::Content content2;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content2);
+    content2.AddStream(stream2);
+    content2.AddStream(stream3);
+    EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_UPDATE));
+    ASSERT_EQ(3u, media_channel1_->recv_streams().size());
+    EXPECT_EQ(stream1, media_channel1_->recv_streams()[0]);
+    EXPECT_EQ(stream2, media_channel1_->recv_streams()[1]);
+    EXPECT_EQ(stream3, media_channel1_->recv_streams()[2]);
+
+    // Update the remote streams by removing the first stream.
+    // This is done by removing all SSRCS for this particular stream.
+    typename T::Content content3;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content3);
+    stream1.ssrcs.clear();
+    content3.AddStream(stream1);
+
+    EXPECT_TRUE(channel1_->SetRemoteContent(&content3, CA_UPDATE));
+    ASSERT_EQ(2u, media_channel1_->recv_streams().size());
+    EXPECT_EQ(stream2, media_channel1_->recv_streams()[0]);
+    EXPECT_EQ(stream3, media_channel1_->recv_streams()[1]);
+  }
+
+  // Test that SetLocalContent and SetRemoteContent properly
+  // handles adding and removing StreamParams when the action is a full
+  // CA_OFFER / CA_ANSWER.
+  void TestChangeStreamParamsInContent() {
+    cricket::StreamParams stream1;
+    stream1.name = "Stream1";
+    stream1.ssrcs.push_back(kSsrc1);
+    stream1.cname = "stream1_cname";
+
+    cricket::StreamParams stream2;
+    stream2.name = "Stream2";
+    stream2.ssrcs.push_back(kSsrc2);
+    stream2.cname = "stream2_cname";
+
+    // Setup a call where channel 1 send |stream1| to channel 2.
+    CreateChannels(0, 0);
+    typename T::Content content1;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content1);
+    content1.AddStream(stream1);
+    EXPECT_TRUE(channel1_->SetLocalContent(&content1, CA_OFFER));
+    EXPECT_TRUE(channel1_->Enable(true));
+    EXPECT_EQ(1u, media_channel1_->send_streams().size());
+
+    EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER));
+    EXPECT_EQ(1u, media_channel2_->recv_streams().size());
+    session1_.Connect(&session2_);
+
+    // Channel 2 do not send anything.
+    typename T::Content content2;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content2);
+    EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_ANSWER));
+    EXPECT_EQ(0u, media_channel1_->recv_streams().size());
+    EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_ANSWER));
+    EXPECT_TRUE(channel2_->Enable(true));
+    EXPECT_EQ(0u, media_channel2_->send_streams().size());
+
+    EXPECT_TRUE(SendCustomRtp1(kSsrc1));
+    EXPECT_TRUE(CheckCustomRtp2(kSsrc1));
+
+    // Let channel 2 update the content by sending |stream2| and enable SRTP.
+    typename T::Content content3;
+    CreateContent(SECURE, kPcmuCodec, kH264Codec, &content3);
+    content3.AddStream(stream2);
+    EXPECT_TRUE(channel2_->SetLocalContent(&content3, CA_OFFER));
+    ASSERT_EQ(1u, media_channel2_->send_streams().size());
+    EXPECT_EQ(stream2, media_channel2_->send_streams()[0]);
+
+    EXPECT_TRUE(channel1_->SetRemoteContent(&content3, CA_OFFER));
+    ASSERT_EQ(1u, media_channel1_->recv_streams().size());
+    EXPECT_EQ(stream2, media_channel1_->recv_streams()[0]);
+
+    // Channel 1 replies but stop sending stream1.
+    typename T::Content content4;
+    CreateContent(SECURE, kPcmuCodec, kH264Codec, &content4);
+    EXPECT_TRUE(channel1_->SetLocalContent(&content4, CA_ANSWER));
+    EXPECT_EQ(0u, media_channel1_->send_streams().size());
+
+    EXPECT_TRUE(channel2_->SetRemoteContent(&content4, CA_ANSWER));
+    EXPECT_EQ(0u, media_channel2_->recv_streams().size());
+
+    EXPECT_TRUE(channel1_->secure());
+    EXPECT_TRUE(channel2_->secure());
+    EXPECT_TRUE(SendCustomRtp2(kSsrc2));
+    EXPECT_TRUE(CheckCustomRtp1(kSsrc2));
   }
 
   // Test that we only start playout and sending at the right times.
@@ -471,13 +674,13 @@
     EXPECT_TRUE(channel1_->Enable(true));
     EXPECT_FALSE(media_channel1_->playout());
     EXPECT_FALSE(media_channel1_->sending());
-    EXPECT_TRUE(channel1_->SetLocalContent(&media_content1_, CA_OFFER));
+    EXPECT_TRUE(channel1_->SetLocalContent(&local_media_content1_, CA_OFFER));
     EXPECT_TRUE(media_channel1_->playout());
     EXPECT_FALSE(media_channel1_->sending());
-    EXPECT_TRUE(channel2_->SetRemoteContent(&media_content1_, CA_OFFER));
+    EXPECT_TRUE(channel2_->SetRemoteContent(&local_media_content1_, CA_OFFER));
     EXPECT_FALSE(media_channel2_->playout());
     EXPECT_FALSE(media_channel2_->sending());
-    EXPECT_TRUE(channel2_->SetLocalContent(&media_content2_, CA_ANSWER));
+    EXPECT_TRUE(channel2_->SetLocalContent(&local_media_content2_, CA_ANSWER));
     EXPECT_FALSE(media_channel2_->playout());
     EXPECT_FALSE(media_channel2_->sending());
     session1_.Connect(&session2_);
@@ -488,7 +691,7 @@
     EXPECT_TRUE(channel2_->Enable(true));
     EXPECT_TRUE(media_channel2_->playout());
     EXPECT_TRUE(media_channel2_->sending());
-    EXPECT_TRUE(channel1_->SetRemoteContent(&media_content2_, CA_ANSWER));
+    EXPECT_TRUE(channel1_->SetRemoteContent(&local_media_content2_, CA_ANSWER));
     EXPECT_TRUE(media_channel1_->playout());
     EXPECT_TRUE(media_channel1_->sending());
   }
@@ -1083,8 +1286,10 @@
   typename T::MediaChannel* media_channel2_;
   talk_base::scoped_ptr<typename T::Channel> channel1_;
   talk_base::scoped_ptr<typename T::Channel> channel2_;
-  typename T::Content media_content1_;
-  typename T::Content media_content2_;
+  typename T::Content local_media_content1_;
+  typename T::Content local_media_content2_;
+  typename T::Content remote_media_content1_;
+  typename T::Content remote_media_content2_;
   // The RTP and RTCP packets to send in the tests.
   std::string rtp_packet_;
   std::string rtcp_packet_;
@@ -1111,6 +1316,13 @@
 }
 
 template<>
+void ChannelTest<VoiceTraits>::CopyContent(
+    const cricket::AudioContentDescription& source,
+    cricket::AudioContentDescription* audio) {
+  *audio = source;
+}
+
+template<>
 bool ChannelTest<VoiceTraits>::CodecMatches(const cricket::AudioCodec& c1,
                                             const cricket::AudioCodec& c2) {
   return c1.name == c2.name && c1.clockrate == c2.clockrate &&
@@ -1120,8 +1332,7 @@
 template<>
 void ChannelTest<VoiceTraits>::AddLegacyStreamInContent(
     uint32 ssrc, int flags, cricket::AudioContentDescription* audio) {
-  if (flags & SSRC_MUX)
-    audio->AddLegacyStream(ssrc);
+  audio->AddLegacyStream(ssrc);
 }
 
 class VoiceChannelTest
@@ -1152,7 +1363,7 @@
 // override to add 0 parameter
 template<>
 bool ChannelTest<VideoTraits>::AddStream1(int id) {
-  return channel1_->AddStream(id, 0);
+  return channel1_->AddRecvStream(cricket::StreamParams::CreateLegacy(id));
 }
 
 template<>
@@ -1171,6 +1382,13 @@
 }
 
 template<>
+void ChannelTest<VideoTraits>::CopyContent(
+    const cricket::VideoContentDescription& source,
+    cricket::VideoContentDescription* video) {
+  *video = source;
+}
+
+template<>
 bool ChannelTest<VideoTraits>::CodecMatches(const cricket::VideoCodec& c1,
                                             const cricket::VideoCodec& c2) {
   return c1.name == c2.name && c1.width == c2.width && c1.height == c2.height &&
@@ -1180,8 +1398,7 @@
 template<>
 void ChannelTest<VideoTraits>::AddLegacyStreamInContent(
     uint32 ssrc, int flags, cricket::VideoContentDescription* video) {
-  if (flags & SSRC_MUX)
-    video->AddLegacyStream(ssrc);
+  video->AddLegacyStream(ssrc);
 }
 
 class VideoChannelTest
@@ -1203,10 +1420,6 @@
   EXPECT_TRUE(media_channel1_->dtmf_queue().empty());
 }
 
-TEST_F(VoiceChannelTest, TestSetRtcpCName) {
-  Base::TestSetRtcpCName();
-}
-
 TEST_F(VoiceChannelTest, TestSetContents) {
   Base::TestSetContents();
 }
@@ -1227,6 +1440,18 @@
   Base::TestStreams();
 }
 
+TEST_F(VoiceChannelTest, TestUpdateStreamsInLocalContent) {
+  Base::TestUpdateStreamsInLocalContent();
+}
+
+TEST_F(VoiceChannelTest, TestUpdateRemoteStreamsInContent) {
+  Base::TestUpdateStreamsInRemoteContent();
+}
+
+TEST_F(VoiceChannelTest, TestChangeStreamParamsInContent) {
+  Base::TestChangeStreamParamsInContent();
+}
+
 TEST_F(VoiceChannelTest, TestPlayoutAndSendingStates) {
   Base::TestPlayoutAndSendingStates();
 }
@@ -1495,10 +1720,6 @@
   Base::TestInit();
 }
 
-TEST_F(VideoChannelTest, TestSetRtcpCName) {
-  Base::TestSetRtcpCName();
-}
-
 TEST_F(VideoChannelTest, TestSetContents) {
   Base::TestSetContents();
 }
@@ -1519,6 +1740,18 @@
   Base::TestStreams();
 }
 
+TEST_F(VideoChannelTest, TestUpdateStreamsInLocalContent) {
+  Base::TestUpdateStreamsInLocalContent();
+}
+
+TEST_F(VideoChannelTest, TestUpdateRemoteStreamsInContent) {
+  Base::TestUpdateStreamsInRemoteContent();
+}
+
+TEST_F(VideoChannelTest, TestChangeStreamParamsInContent) {
+  Base::TestChangeStreamParamsInContent();
+}
+
 TEST_F(VideoChannelTest, TestPlayoutAndSendingStates) {
   Base::TestPlayoutAndSendingStates();
 }
diff --git a/talk/session/phone/fakemediaengine.h b/talk/session/phone/fakemediaengine.h
index 1656b58..ee0f7e7 100644
--- a/talk/session/phone/fakemediaengine.h
+++ b/talk/session/phone/fakemediaengine.h
@@ -35,7 +35,9 @@
 #include <vector>
 
 #include "talk/base/buffer.h"
+#include "talk/p2p/base/sessiondescription.h"
 #include "talk/session/phone/mediaengine.h"
+#include "talk/session/phone/streamparams.h"
 #include "talk/session/phone/rtputils.h"
 
 namespace cricket {
@@ -67,7 +69,6 @@
   const std::list<std::string>& rtp_packets() const { return rtp_packets_; }
   const std::list<std::string>& rtcp_packets() const { return rtcp_packets_; }
   int options() const { return options_; }
-  const uint32 send_ssrc() { return send_ssrc_; }
 
   bool SendRtp(const void* data, int len) {
     if (!sending_ || !Base::network_interface_) {
@@ -128,12 +129,57 @@
   void set_fail_set_recv_codecs(bool fail) {
     fail_set_recv_codecs_ = fail;
   }
-  virtual void SetSendSsrc(uint32 ssrc) {
-    send_ssrc_ = ssrc;
+  virtual bool AddSendStream(const StreamParams& sp) {
+    if (std::find(send_streams_.begin(), send_streams_.end(), sp) !=
+        send_streams_.end()) {
+        return false;
+    }
+    send_streams_.push_back(sp);
+    return true;
+  }
+  virtual bool RemoveSendStream(uint32 ssrc) {
+    return RemoveStreamBySsrc(&send_streams_, ssrc);
+  }
+  virtual bool AddRecvStream(const StreamParams& sp) {
+    if (std::find(receive_streams_.begin(), receive_streams_.end(), sp) !=
+        receive_streams_.end()) {
+        return false;
+    }
+    receive_streams_.push_back(sp);
+    return true;
+  }
+  virtual bool RemoveRecvStream(uint32 ssrc) {
+    return RemoveStreamBySsrc(&receive_streams_, ssrc);
+  }
+  const std::vector<StreamParams>& send_streams() { return send_streams_; }
+  const std::vector<StreamParams>& recv_streams() {
+    return receive_streams_;
+  }
+  bool HasRecvStream(uint32 ssrc) const {
+    return GetStreamBySsrc(receive_streams_, ssrc, NULL);
+  }
+
+  // TODO: This is to support legacy unit test that only check one
+  // sending stream.
+  const uint32 send_ssrc() {
+    if (send_streams_.empty())
+      return 0;
+    return send_streams_[0].first_ssrc();
+  }
+
+  // TODO: This is to support legacy unit test that only check one
+  // sending stream.
+  const std::string rtcp_cname() {
+    if (send_streams_.empty())
+      return "";
+    return send_streams_[0].cname;
   }
 
  protected:
-  void set_sending(bool send) { sending_ = send; }
+  bool set_sending(bool send) {
+    sending_ = send;
+    return true;
+  }
   void set_playout(bool playout) { playout_ = playout; }
   virtual void OnPacketReceived(talk_base::Buffer* packet) {
     rtp_packets_.push_back(std::string(packet->data(), packet->length()));
@@ -156,9 +202,12 @@
   std::vector<RtpHeaderExtension> send_extensions_;
   std::list<std::string> rtp_packets_;
   std::list<std::string> rtcp_packets_;
+  std::vector<StreamParams> send_streams_;
+  std::vector<StreamParams> receive_streams_;
   bool fail_set_send_codecs_;
   bool fail_set_recv_codecs_;
   uint32 send_ssrc_;
+  std::string rtcp_cname_;
 };
 
 class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> {
@@ -178,9 +227,7 @@
   const std::vector<AudioCodec>& send_codecs() const { return send_codecs_; }
   const std::vector<AudioCodec>& codecs() const { return send_codecs(); }
   bool muted() const { return muted_; }
-  const std::set<uint32>& streams() const { return streams_; }
   const std::vector<DtmfEvent>& dtmf_queue() const { return dtmf_queue_; }
-  const std::string& rtcp_cname() const { return rtcp_cname_; }
 
   uint32 ringback_tone_ssrc() const { return ringback_tone_ssrc_; }
   bool ringback_tone_play() const { return ringback_tone_play_; }
@@ -210,26 +257,22 @@
     if (fail_set_send_) {
       return false;
     }
-    set_sending(flag != SEND_NOTHING);
-    return true;
-  }
-  virtual bool SetRtcpCName(const std::string& cname) {
-    rtcp_cname_ = cname;
-    return true;
+    return set_sending(flag != SEND_NOTHING);
   }
   virtual bool SetSendBandwidth(bool autobw, int bps) { return true; }
   virtual bool Mute(bool on) {
     muted_ = on;
     return true;
   }
-
-  virtual bool AddStream(uint32 ssrc) {
-    streams_.insert(ssrc);
-    output_scalings_[ssrc] = OutputScaling();
+  virtual bool AddRecvStream(const StreamParams& sp) {
+    if (!RtpHelper<VoiceMediaChannel>::AddRecvStream(sp))
+      return false;
+    output_scalings_[sp.first_ssrc()] = OutputScaling();
     return true;
   }
-  virtual bool RemoveStream(uint32 ssrc) {
-    streams_.erase(ssrc);
+  virtual bool RemoveRecvStream(uint32 ssrc) {
+    if (!RtpHelper<VoiceMediaChannel>::RemoveRecvStream(ssrc))
+      return false;
     output_scalings_.erase(ssrc);
     return true;
   }
@@ -297,10 +340,8 @@
   std::vector<AudioCodec> recv_codecs_;
   std::vector<AudioCodec> send_codecs_;
   bool muted_;
-  std::set<uint32> streams_;
   std::map<uint32, OutputScaling> output_scalings_;
   std::vector<DtmfEvent> dtmf_queue_;
-  std::string rtcp_cname_;
   bool fail_set_send_;
   uint32 ringback_tone_ssrc_;
   bool ringback_tone_play_;
@@ -323,8 +364,7 @@
   const std::vector<VideoCodec>& codecs() const { return send_codecs(); }
   bool muted() const { return muted_; }
   bool rendering() const { return playout(); }
-  const std::map<uint32, VideoRenderer*>& streams() const { return streams_; }
-  const std::string rtcp_cname() const { return rtcp_cname_; }
+  const std::map<uint32, VideoRenderer*>& renderers() const { return streams_; }
 
   virtual bool SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
     if (fail_set_recv_codecs()) {
@@ -357,8 +397,7 @@
   }
 
   virtual bool SetSend(bool send) {
-    set_sending(send);
-    return true;
+    return set_sending(send);
   }
   virtual bool AddScreencast(uint32 ssrc, const ScreencastId& id) {
     screen_casting_ = true;
@@ -368,24 +407,20 @@
     screen_casting_ = false;
     return true;
   }
-  virtual bool SetRtcpCName(const std::string& cname) {
-    rtcp_cname_ = cname;
-    return true;
-  }
   virtual bool SetSendBandwidth(bool autobw, int bps) { return true; }
   virtual bool Mute(bool on) {
     muted_ = on;
     return true;
   }
-
-  virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc) {
-    if (streams_.find(ssrc) != streams_.end()) {
+  virtual bool AddRecvStream(const StreamParams& sp) {
+    if (!RtpHelper<VideoMediaChannel>::AddRecvStream(sp))
       return false;
-    }
-    streams_[ssrc] = NULL;
+    streams_[sp.first_ssrc()] = NULL;
     return true;
   }
-  virtual bool RemoveStream(uint32 ssrc) {
+  virtual bool RemoveRecvStream(uint32 ssrc) {
+    if (!RtpHelper<VideoMediaChannel>::RemoveRecvStream(ssrc))
+      return false;
     streams_.erase(ssrc);
     return true;
   }
@@ -415,7 +450,6 @@
   std::map<uint32, VideoRenderer*> streams_;
   bool muted_;
   bool screen_casting_;
-  std::string rtcp_cname_;
   bool sent_intra_frame_;
   bool requested_intra_frame_;
 };
diff --git a/talk/session/phone/filemediaengine.cc b/talk/session/phone/filemediaengine.cc
index 2a7b8d8..6f2c5e1 100644
--- a/talk/session/phone/filemediaengine.cc
+++ b/talk/session/phone/filemediaengine.cc
@@ -34,6 +34,7 @@
 #include "talk/base/stream.h"
 #include "talk/session/phone/rtpdump.h"
 #include "talk/session/phone/rtputils.h"
+#include "talk/session/phone/streamparams.h"
 
 namespace cricket {
 
@@ -258,7 +259,8 @@
 FileVoiceChannel::FileVoiceChannel(
     talk_base::StreamInterface* input_file_stream,
     talk_base::StreamInterface* output_file_stream)
-    : rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
+    : send_ssrc_(0),
+      rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
                                                  output_file_stream)) {}
 
 FileVoiceChannel::~FileVoiceChannel() {}
@@ -272,8 +274,22 @@
   return rtp_sender_receiver_->SetSend(flag != SEND_NOTHING);
 }
 
-void FileVoiceChannel::SetSendSsrc(uint32 ssrc) {
-  rtp_sender_receiver_->SetSendSsrc(ssrc);
+bool FileVoiceChannel::AddSendStream(const StreamParams& sp) {
+  if (send_ssrc_ != 0 || sp.ssrcs.size() != 1) {
+    LOG(LS_ERROR) << "FileVoiceChannel only supports one send stream.";
+    return false;
+  }
+  send_ssrc_ = sp.ssrcs[0];
+  rtp_sender_receiver_->SetSendSsrc(send_ssrc_);
+  return true;
+}
+
+bool FileVoiceChannel::RemoveSendStream(uint32 ssrc) {
+  if (ssrc != send_ssrc_)
+    return false;
+  send_ssrc_ = 0;
+  rtp_sender_receiver_->SetSendSsrc(send_ssrc_);
+  return true;
 }
 
 void FileVoiceChannel::OnPacketReceived(talk_base::Buffer* packet) {
@@ -286,7 +302,8 @@
 FileVideoChannel::FileVideoChannel(
     talk_base::StreamInterface* input_file_stream,
     talk_base::StreamInterface* output_file_stream)
-    : rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
+    : send_ssrc_(0),
+      rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
                                                  output_file_stream)) {}
 
 FileVideoChannel::~FileVideoChannel() {}
@@ -300,8 +317,22 @@
   return rtp_sender_receiver_->SetSend(send);
 }
 
-void FileVideoChannel::SetSendSsrc(uint32 ssrc) {
-  rtp_sender_receiver_->SetSendSsrc(ssrc);
+bool FileVideoChannel::AddSendStream(const StreamParams& sp) {
+  if (send_ssrc_ != 0 || sp.ssrcs.size() != 1) {
+    LOG(LS_ERROR) << "FileVideoChannel only support one send stream.";
+    return false;
+  }
+  send_ssrc_ = sp.ssrcs[0];
+  rtp_sender_receiver_->SetSendSsrc(send_ssrc_);
+  return true;
+}
+
+bool FileVideoChannel::RemoveSendStream(uint32 ssrc) {
+  if (ssrc != send_ssrc_)
+    return false;
+  send_ssrc_ = 0;
+  rtp_sender_receiver_->SetSendSsrc(send_ssrc_);
+  return true;
 }
 
 void FileVideoChannel::OnPacketReceived(talk_base::Buffer* packet) {
diff --git a/talk/session/phone/filemediaengine.h b/talk/session/phone/filemediaengine.h
index 8f61638..f181dc8 100644
--- a/talk/session/phone/filemediaengine.h
+++ b/talk/session/phone/filemediaengine.h
@@ -165,8 +165,6 @@
   }
   virtual bool SetPlayout(bool playout) { return true; }
   virtual bool SetSend(SendFlags flag);
-  virtual bool AddStream(uint32 ssrc) { return true; }
-  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) {
@@ -185,13 +183,16 @@
   // Implement pure virtual methods of MediaChannel.
   virtual void OnPacketReceived(talk_base::Buffer* packet);
   virtual void OnRtcpReceived(talk_base::Buffer* packet) {}
-  virtual void SetSendSsrc(uint32 ssrc);
-  virtual bool SetRtcpCName(const std::string& cname) { return true; }
+  virtual bool AddSendStream(const StreamParams& sp);
+  virtual bool RemoveSendStream(uint32 ssrc);
+  virtual bool AddRecvStream(const StreamParams& sp) { return true; }
+  virtual bool RemoveRecvStream(uint32 ssrc) { return true; }
   virtual bool Mute(bool on) { return false; }
   virtual bool SetSendBandwidth(bool autobw, int bps) { return true; }
   virtual bool SetOptions(int options) { return true; }
 
  private:
+  uint32 send_ssrc_;
   talk_base::scoped_ptr<RtpSenderReceiver> rtp_sender_receiver_;
   DISALLOW_COPY_AND_ASSIGN(FileVoiceChannel);
 };
@@ -217,8 +218,6 @@
   }
   virtual bool SetRender(bool render) { return true; }
   virtual bool SetSend(bool send);
-  virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc) { return true; }
-  virtual bool RemoveStream(uint32 ssrc) { return true; }
   virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer) {
     return true;
   }
@@ -233,13 +232,16 @@
   // Implement pure virtual methods of MediaChannel.
   virtual void OnPacketReceived(talk_base::Buffer* packet);
   virtual void OnRtcpReceived(talk_base::Buffer* packet) {}
-  virtual void SetSendSsrc(uint32 ssrc);
-  virtual bool SetRtcpCName(const std::string& cname) { return true; }
+  virtual bool AddSendStream(const StreamParams& sp);
+  virtual bool RemoveSendStream(uint32 ssrc);
+  virtual bool AddRecvStream(const StreamParams& sp) { return true; }
+  virtual bool RemoveRecvStream(uint32 ssrc) { return true; }
   virtual bool Mute(bool on) { return false; }
   virtual bool SetSendBandwidth(bool autobw, int bps) { return true; }
   virtual bool SetOptions(int options) { return true; }
 
  private:
+  uint32 send_ssrc_;
   talk_base::scoped_ptr<RtpSenderReceiver> rtp_sender_receiver_;
   DISALLOW_COPY_AND_ASSIGN(FileVideoChannel);
 };
diff --git a/talk/session/phone/filemediaengine_unittest.cc b/talk/session/phone/filemediaengine_unittest.cc
index cab7120..c915251 100644
--- a/talk/session/phone/filemediaengine_unittest.cc
+++ b/talk/session/phone/filemediaengine_unittest.cc
@@ -34,6 +34,7 @@
 #include "talk/base/stream.h"
 #include "talk/session/phone/filemediaengine.h"
 #include "talk/session/phone/rtpdump.h"
+#include "talk/session/phone/streamparams.h"
 #include "talk/session/phone/testutils.h"
 
 namespace cricket {
@@ -379,7 +380,7 @@
                                       voice_output_filename_, "", "", 1));
   EXPECT_TRUE(NULL != voice_channel_.get());
   const uint32 send_ssrc = RtpTestUtility::kDefaultSsrc + 1;
-  voice_channel_->SetSendSsrc(send_ssrc);
+  voice_channel_->AddSendStream(StreamParams::CreateLegacy(send_ssrc));
 
   talk_base::MemoryStream net_dump;
   FileNetworkInterface net_interface(&net_dump, voice_channel_.get());
diff --git a/talk/session/phone/mediachannel.h b/talk/session/phone/mediachannel.h
index b1022e8..83aa3e4 100644
--- a/talk/session/phone/mediachannel.h
+++ b/talk/session/phone/mediachannel.h
@@ -46,6 +46,7 @@
 namespace cricket {
 
 class ScreencastId;
+struct StreamParams;
 class VideoRenderer;
 
 const int kMinRtpHeaderExtensionId = 1;
@@ -116,10 +117,21 @@
   virtual void OnPacketReceived(talk_base::Buffer* packet) = 0;
   // Called when a RTCP packet is received.
   virtual void OnRtcpReceived(talk_base::Buffer* packet) = 0;
-  // Sets the SSRC to be used for outgoing data.
-  virtual void SetSendSsrc(uint32 id) = 0;
-  // Set the CNAME of RTCP
-  virtual bool SetRtcpCName(const std::string& cname) = 0;
+  // Creates a new outgoing media stream with SSRCs and CNAME as described
+  // by sp.
+  virtual bool AddSendStream(const StreamParams& sp) = 0;
+  // Removes an outgoing media stream.
+  // ssrc must be the first SSRC of the media stream if the stream uses
+  // multiple SSRCs.
+  virtual bool RemoveSendStream(uint32 ssrc) = 0;
+  // Creates a new incoming media stream with SSRCs and CNAME as described
+  // by sp.
+  virtual bool AddRecvStream(const StreamParams& sp) = 0;
+  // Removes an incoming media stream.
+  // ssrc must be the first SSRC of the media stream if the stream uses
+  // multiple SSRCs.
+  virtual bool RemoveRecvStream(uint32 ssrc) = 0;
+
   // Mutes the channel.
   virtual bool Mute(bool on) = 0;
 
@@ -348,10 +360,6 @@
   virtual bool SetPlayout(bool playout) = 0;
   // Starts or stops sending (and potentially capture) of local audio.
   virtual bool SetSend(SendFlags flag) = 0;
-  // Adds a new receive-only stream with the specified SSRC.
-  virtual bool AddStream(uint32 ssrc) = 0;
-  // Removes a stream added with AddStream.
-  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 of the stream sent to the speaker.
@@ -406,10 +414,6 @@
   virtual bool SetRender(bool render) = 0;
   // Starts or stops transmission (and potentially capture) of local video.
   virtual bool SetSend(bool send) = 0;
-  // Adds a new receive-only stream with the specified SSRC.
-  virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc) = 0;
-  // Removes a stream added with AddStream.
-  virtual bool RemoveStream(uint32 ssrc) = 0;
   // Sets the renderer object to be used for the specified stream.
   // If SSRC is 0, the renderer is used for the 'default' stream.
   virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer) = 0;
diff --git a/talk/session/phone/mediasession.cc b/talk/session/phone/mediasession.cc
index 4e66872..0784b56 100644
--- a/talk/session/phone/mediasession.cc
+++ b/talk/session/phone/mediasession.cc
@@ -105,17 +105,6 @@
   return false;
 }
 
-static const StreamParams* FindStreamParamsByName(
-    const StreamParamsVec& params_vec,
-    const std::string& name) {
-  for (StreamParamsVec::const_iterator it = params_vec.begin();
-       it != params_vec.end(); ++it) {
-    if (it->name == name)
-      return &*it;
-  }
-  return NULL;
-}
-
 static const StreamParams* FindFirstStreamParamsByCname(
     const StreamParamsVec& params_vec,
     const std::string& cname) {
@@ -127,21 +116,6 @@
   return NULL;
 }
 
-static const StreamParams* FindStreamParamsBySsrc(
-    const StreamParamsVec& params_vec,
-    uint32 ssrc) {
-  for (StreamParamsVec::const_iterator stream_it = params_vec.begin();
-       stream_it != params_vec.end(); ++stream_it) {
-    const std::vector<uint32>& ssrcs = stream_it->ssrcs;
-    for (std::vector<uint32>::const_iterator ssrc_it = ssrcs.begin();
-         ssrc_it !=  ssrcs.end(); ++ssrc_it) {
-      if (ssrc == *ssrc_it)
-        return &*stream_it;
-    }
-  }
-  return NULL;
-}
-
 // Generates a new CNAME or the CNAME of an already existing StreamParams
 // if a StreamParams exist for another Stream in streams with sync_label
 // sync_label.
@@ -158,10 +132,13 @@
        stream_it != streams.end() ; ++stream_it) {
     if (synch_label != stream_it->sync_label)
       continue;
-    const StreamParams* param = FindStreamParamsByName(params_vec,
-                                                       stream_it->name);
-    if (param) {
-      *cname = param->cname;
+
+    StreamParams param;
+    // nick is empty for StreamParams generated using
+    // MediaSessionDescriptionFactory.
+    if (GetStreamByNickAndName(params_vec, "", stream_it->name,
+                               &param)) {
+      *cname = param.cname;
       return true;
     }
   }
@@ -183,7 +160,7 @@
   uint32 ssrc = 0;
   do {
     ssrc = talk_base::CreateRandomNonZeroId();
-  } while (FindStreamParamsBySsrc(params_vec, ssrc));
+  } while (GetStreamBySsrc(params_vec, ssrc, NULL));
   return ssrc;
 }
 
@@ -221,9 +198,11 @@
        stream_it != streams.end(); ++stream_it) {
     if (stream_it->type != media_type)
       continue;  // Wrong media type.
-    const StreamParams* params = FindStreamParamsByName(*current_params,
-                                                        stream_it->name);
-    if (!params) {
+
+    StreamParams param;
+    // nick is empty for StreamParams generated using
+    // MediaSessionDescriptionFactory.
+    if (!GetStreamByNickAndName(*current_params, "", stream_it->name, &param)) {
       // This is a new stream.
       // Get a CNAME. Either new or same as one of the other synched streams.
       std::string cname;
@@ -245,7 +224,7 @@
       // This is necessary so that we can use the CNAME for other media types.
       current_params->push_back(stream_param);
     } else {
-      content_description->AddStream(*params);
+      content_description->AddStream(param);
     }
   }
   return true;
diff --git a/talk/session/phone/mediasession.h b/talk/session/phone/mediasession.h
index 4a10836..c35a994 100644
--- a/talk/session/phone/mediasession.h
+++ b/talk/session/phone/mediasession.h
@@ -46,7 +46,6 @@
 typedef std::vector<AudioCodec> AudioCodecs;
 typedef std::vector<VideoCodec> VideoCodecs;
 typedef std::vector<CryptoParams> CryptoParamsVec;
-typedef std::vector<StreamParams> StreamParamsVec;
 
 // SEC_ENABLED and SEC_REQUIRED should only be used if the session
 // was negotiated over TLS, to protect the inline crypto material
@@ -179,7 +178,15 @@
   void AddLegacyStream(uint32 ssrc) {
     streams_.push_back(StreamParams::CreateLegacy(ssrc));
   }
-
+  // Sets the CNAME of all StreamParams if it have not been set.
+  // This can be used to set the CNAME of legacy streams.
+  void SetCnameIfEmpty(const std::string& cname) {
+    for (cricket::StreamParamsVec::iterator it = streams_.begin();
+         it != streams_.end(); ++it) {
+      if (it->cname.empty())
+        it->cname = cname;
+    }
+  }
   uint32 first_ssrc() const {
     if (streams_.empty()) {
       return 0;
diff --git a/talk/session/phone/mediasessionclient_unittest.cc b/talk/session/phone/mediasessionclient_unittest.cc
index 574b052..0b99106 100644
--- a/talk/session/phone/mediasessionclient_unittest.cc
+++ b/talk/session/phone/mediasessionclient_unittest.cc
@@ -2133,14 +2133,11 @@
 
     cricket::FakeVoiceMediaChannel* voice_channel = fme_->GetVoiceChannel(0);
     ASSERT_TRUE(voice_channel != NULL);
-    ASSERT_TRUE(
-        voice_channel->streams().find(1234U) != voice_channel->streams().end());
-    ASSERT_TRUE(
-        voice_channel->streams().find(2468U) != voice_channel->streams().end());
+    ASSERT_TRUE(voice_channel->HasRecvStream(1234U));
+    ASSERT_TRUE(voice_channel->HasRecvStream(2468U));
     cricket::FakeVideoMediaChannel* video_channel = fme_->GetVideoChannel(0);
     ASSERT_TRUE(video_channel != NULL);
-    ASSERT_TRUE(
-        video_channel->streams().find(5678U) != video_channel->streams().end());
+    ASSERT_TRUE(video_channel->HasRecvStream(5678U));
     ClearStanzas();
 
     cricket::ViewRequest viewRequest;
@@ -2194,14 +2191,11 @@
 
     voice_channel = fme_->GetVoiceChannel(0);
     ASSERT_TRUE(voice_channel != NULL);
-    ASSERT_FALSE(
-        voice_channel->streams().find(1234U) != voice_channel->streams().end());
-    ASSERT_TRUE(
-        voice_channel->streams().find(2468U) != voice_channel->streams().end());
+    ASSERT_FALSE(voice_channel->HasRecvStream(1234U));
+    ASSERT_TRUE(voice_channel->HasRecvStream(2468U));
     video_channel = fme_->GetVideoChannel(0);
     ASSERT_TRUE(video_channel != NULL);
-    ASSERT_FALSE(
-        video_channel->streams().find(5678U) != video_channel->streams().end());
+    ASSERT_FALSE(video_channel->HasRecvStream(5678U));
 
     // Fails because ssrc is now invalid.
     ASSERT_FALSE(
diff --git a/talk/session/phone/srtpfilter.cc b/talk/session/phone/srtpfilter.cc
index dbff814..af02a7c 100644
--- a/talk/session/phone/srtpfilter.cc
+++ b/talk/session/phone/srtpfilter.cc
@@ -34,6 +34,7 @@
 
 #include "talk/base/base64.h"
 #include "talk/base/logging.h"
+#include "talk/base/stringencode.h"
 #include "talk/base/timeutils.h"
 #include "talk/session/phone/rtputils.h"
 
@@ -65,6 +66,8 @@
 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;
+const int SRTP_MASTER_KEY_KEY_LEN = 16;
+const int SRTP_MASTER_KEY_SALT_LEN = 14;
 
 #ifndef HAVE_SRTP
 
@@ -143,6 +146,68 @@
   return ret;
 }
 
+bool SrtpFilter::SetRtpParams(const std::string& send_cs,
+                              const uint8* send_key, int send_key_len,
+                              const std::string& recv_cs,
+                              const uint8* recv_key, int recv_key_len) {
+  if (state_ == ST_ACTIVE) {
+    LOG(LS_ERROR) << "Tried to set SRTP Params when filter already active";
+    return false;
+  }
+  CreateSrtpSessions();
+  if (!send_session_->SetSend(send_cs, send_key, send_key_len))
+    return false;
+
+  if (!recv_session_->SetRecv(recv_cs, recv_key, recv_key_len))
+    return false;
+
+  state_ = ST_ACTIVE;
+
+  LOG(LS_INFO) << "SRTP activated with negotiated parameters:"
+               << " send cipher_suite " << send_cs
+               << " recv cipher_suite " << recv_cs;
+
+  return true;
+}
+
+// This function is provided separately because DTLS-SRTP behaves
+// differently in RTP/RTCP mux and non-mux modes.
+//
+// - In the non-muxed case, RTP and RTCP are keyed with different
+//   keys (from different DTLS handshakes), and so we need a new
+//   SrtpSession.
+// - In the muxed case, they are keyed with the same keys, so
+//   this function is not needed
+bool SrtpFilter::SetRtcpParams(const std::string& send_cs,
+                               const uint8* send_key, int send_key_len,
+                               const std::string& recv_cs,
+                               const uint8* recv_key, int recv_key_len) {
+  // This can only be called once, but can be safely called after
+  // SetRtpParams
+  if (send_rtcp_session_.get() || send_rtcp_session_.get()) {
+    LOG(LS_ERROR) << "Tried to set SRTCP Params when filter already active";
+    return false;
+  }
+
+  send_rtcp_session_.reset(new SrtpSession());
+  SignalSrtpError.repeat(send_rtcp_session_->SignalSrtpError);
+  send_rtcp_session_->set_signal_silent_time(signal_silent_time_in_ms_);
+  if (!send_rtcp_session_->SetRecv(send_cs, send_key, send_key_len))
+    return false;
+
+  recv_rtcp_session_.reset(new SrtpSession());
+  SignalSrtpError.repeat(recv_rtcp_session_->SignalSrtpError);
+  recv_rtcp_session_->set_signal_silent_time(signal_silent_time_in_ms_);
+  if (!recv_rtcp_session_->SetRecv(recv_cs, recv_key, recv_key_len))
+    return false;
+
+  LOG(LS_INFO) << "SRTCP activated with negotiated parameters:"
+               << " send cipher_suite " << send_cs
+               << " recv cipher_suite " << recv_cs;
+
+  return true;
+}
+
 bool SrtpFilter::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
   if (!IsActive()) {
     LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active";
@@ -156,7 +221,11 @@
     LOG(LS_WARNING) << "Failed to ProtectRtcp: SRTP not active";
     return false;
   }
-  return send_session_->ProtectRtcp(p, in_len, max_len, out_len);
+  if (send_rtcp_session_.get()) {
+    return send_rtcp_session_->ProtectRtcp(p, in_len, max_len, out_len);
+  } else {
+    return send_session_->ProtectRtcp(p, in_len, max_len, out_len);
+  }
 }
 
 bool SrtpFilter::UnprotectRtp(void* p, int in_len, int* out_len) {
@@ -172,7 +241,11 @@
     LOG(LS_WARNING) << "Failed to UnprotectRtcp: SRTP not active";
     return false;
   }
-  return recv_session_->UnprotectRtcp(p, in_len, out_len);
+  if (recv_rtcp_session_.get()) {
+    return recv_rtcp_session_->UnprotectRtcp(p, in_len, out_len);
+  } else {
+    return recv_session_->UnprotectRtcp(p, in_len, out_len);
+  }
 }
 
 void SrtpFilter::set_signal_silent_time(uint32 signal_silent_time_in_ms) {
@@ -180,6 +253,10 @@
   if (state_ == ST_ACTIVE) {
     send_session_->set_signal_silent_time(signal_silent_time_in_ms);
     recv_session_->set_signal_silent_time(signal_silent_time_in_ms);
+    if (send_rtcp_session_.get())
+      send_rtcp_session_->set_signal_silent_time(signal_silent_time_in_ms);
+    if (recv_rtcp_session_.get())
+      recv_rtcp_session_->set_signal_silent_time(signal_silent_time_in_ms);
   }
 }
 
diff --git a/talk/session/phone/srtpfilter.h b/talk/session/phone/srtpfilter.h
index aad461a..da7a236 100644
--- a/talk/session/phone/srtpfilter.h
+++ b/talk/session/phone/srtpfilter.h
@@ -57,6 +57,10 @@
 // Key is 128 bits and salt is 112 bits == 30 bytes. B64 bloat => 40 bytes.
 extern const int SRTP_MASTER_KEY_BASE64_LEN;
 
+// Needed for DTLS-SRTP
+extern const int SRTP_MASTER_KEY_KEY_LEN;
+extern const int SRTP_MASTER_KEY_SALT_LEN;
+
 class SrtpSession;
 class SrtpStat;
 
@@ -99,6 +103,17 @@
   bool SetAnswer(const std::vector<CryptoParams>& answer_params,
                  ContentSource source);
 
+  // Just set up both sets of keys directly.
+  // Used with DTLS-SRTP.
+  bool SetRtpParams(const std::string& send_cs,
+                    const uint8* send_key, int send_key_len,
+                    const std::string& recv_cs,
+                    const uint8* recv_key, int recv_key_len);
+  bool SetRtcpParams(const std::string& send_cs,
+                     const uint8* send_key, int send_key_len,
+                     const std::string& recv_cs,
+                     const uint8* recv_key, int recv_key_len);
+
   // Encrypts/signs an individual RTP/RTCP packet, in-place.
   // If an HMAC is used, this will increase the packet size.
   bool ProtectRtp(void* data, int in_len, int max_len, int* out_len);
@@ -142,6 +157,8 @@
   std::vector<CryptoParams> offer_params_;
   talk_base::scoped_ptr<SrtpSession> send_session_;
   talk_base::scoped_ptr<SrtpSession> recv_session_;
+  talk_base::scoped_ptr<SrtpSession> send_rtcp_session_;
+  talk_base::scoped_ptr<SrtpSession> recv_rtcp_session_;
 };
 
 // Class that wraps a libSRTP session.
diff --git a/talk/session/phone/srtpfilter_unittest.cc b/talk/session/phone/srtpfilter_unittest.cc
index fe93908..276c341 100644
--- a/talk/session/phone/srtpfilter_unittest.cc
+++ b/talk/session/phone/srtpfilter_unittest.cc
@@ -365,6 +365,64 @@
   EXPECT_FALSE(f2_.IsActive());
 }
 
+// Test directly setting the params with AES_CM_128_HMAC_SHA1_80
+TEST_F(SrtpFilterTest, TestProtect_SetParamsDirect_AES_CM_128_HMAC_SHA1_80) {
+  EXPECT_TRUE(f1_.SetRtpParams(CS_AES_CM_128_HMAC_SHA1_80,
+                               kTestKey1, kTestKeyLen,
+                               CS_AES_CM_128_HMAC_SHA1_80,
+                               kTestKey2, kTestKeyLen));
+  EXPECT_TRUE(f2_.SetRtpParams(CS_AES_CM_128_HMAC_SHA1_80,
+                               kTestKey2, kTestKeyLen,
+                               CS_AES_CM_128_HMAC_SHA1_80,
+                               kTestKey1, kTestKeyLen));
+  EXPECT_TRUE(f1_.SetRtcpParams(CS_AES_CM_128_HMAC_SHA1_80,
+                                kTestKey1, kTestKeyLen,
+                                CS_AES_CM_128_HMAC_SHA1_80,
+                                kTestKey2, kTestKeyLen));
+  EXPECT_TRUE(f2_.SetRtcpParams(CS_AES_CM_128_HMAC_SHA1_80,
+                                kTestKey2, kTestKeyLen,
+                                CS_AES_CM_128_HMAC_SHA1_80,
+                                kTestKey1, kTestKeyLen));
+  EXPECT_TRUE(f1_.IsActive());
+  EXPECT_TRUE(f2_.IsActive());
+  TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_80, CS_AES_CM_128_HMAC_SHA1_80);
+}
+
+// Test directly setting the params with AES_CM_128_HMAC_SHA1_32
+TEST_F(SrtpFilterTest, TestProtect_SetParamsDirect_AES_CM_128_HMAC_SHA1_32) {
+  EXPECT_TRUE(f1_.SetRtpParams(CS_AES_CM_128_HMAC_SHA1_32,
+                               kTestKey1, kTestKeyLen,
+                               CS_AES_CM_128_HMAC_SHA1_32,
+                               kTestKey2, kTestKeyLen));
+  EXPECT_TRUE(f2_.SetRtpParams(CS_AES_CM_128_HMAC_SHA1_32,
+                               kTestKey2, kTestKeyLen,
+                               CS_AES_CM_128_HMAC_SHA1_32,
+                               kTestKey1, kTestKeyLen));
+  EXPECT_TRUE(f1_.SetRtcpParams(CS_AES_CM_128_HMAC_SHA1_32,
+                                kTestKey1, kTestKeyLen,
+                                CS_AES_CM_128_HMAC_SHA1_32,
+                                kTestKey2, kTestKeyLen));
+  EXPECT_TRUE(f2_.SetRtcpParams(CS_AES_CM_128_HMAC_SHA1_32,
+                                kTestKey2, kTestKeyLen,
+                                CS_AES_CM_128_HMAC_SHA1_32,
+                                kTestKey1, kTestKeyLen));
+  EXPECT_TRUE(f1_.IsActive());
+  EXPECT_TRUE(f2_.IsActive());
+  TestProtectUnprotect(CS_AES_CM_128_HMAC_SHA1_32, CS_AES_CM_128_HMAC_SHA1_32);
+}
+
+// Test directly setting the params with bogus keys
+TEST_F(SrtpFilterTest, TestSetParamsKeyTooShort) {
+  EXPECT_FALSE(f1_.SetRtpParams(CS_AES_CM_128_HMAC_SHA1_80,
+                                kTestKey1, kTestKeyLen - 1,
+                                CS_AES_CM_128_HMAC_SHA1_80,
+                                kTestKey1, kTestKeyLen - 1));
+  EXPECT_FALSE(f1_.SetRtcpParams(CS_AES_CM_128_HMAC_SHA1_80,
+                                 kTestKey1, kTestKeyLen - 1,
+                                 CS_AES_CM_128_HMAC_SHA1_80,
+                                 kTestKey1, kTestKeyLen - 1));
+}
+
 class SrtpSessionTest : public testing::Test {
  protected:
   virtual void SetUp() {
diff --git a/talk/session/phone/ssrcmuxfilter.cc b/talk/session/phone/ssrcmuxfilter.cc
index c0da3ee..d81feab 100644
--- a/talk/session/phone/ssrcmuxfilter.cc
+++ b/talk/session/phone/ssrcmuxfilter.cc
@@ -32,44 +32,6 @@
 #include "talk/base/logging.h"
 #include "talk/session/phone/rtputils.h"
 
-namespace {
-
-using cricket::StreamParams;
-
-// TODO: Remove this function once cl 25538785 is landed.
-bool GetStreamBySsrc(const std::vector<StreamParams>& streams, uint32 ssrc,
-                     StreamParams* stream_out) {
-  for (std::vector<StreamParams>::const_iterator stream = streams.begin();
-       stream != streams.end(); ++stream) {
-    if (std::find(stream->ssrcs.begin(), stream->ssrcs.end(),
-                  ssrc) != stream->ssrcs.end()) {
-      if (stream_out != NULL) {
-        *stream_out = *stream;
-      }
-      return true;
-    }
-  }
-  return false;
-}
-
-// TODO: Remove this function once cl 25538785 is landed.
-bool RemoveStreamBySsrc(std::vector<StreamParams>* streams, uint32 ssrc) {
-  bool ret = false;
-  for (std::vector<StreamParams>::iterator stream = streams->begin();
-      stream != streams->end(); ) {
-    if (std::find(stream->ssrcs.begin(), stream->ssrcs.end(),
-                  ssrc) != stream->ssrcs.end()) {
-      stream = streams->erase(stream);
-      ret = true;
-    } else {
-      ++stream;
-    }
-  }
-  return ret;
-}
-
-}  // namespace
-
 namespace cricket {
 
 static const uint32 kSsrc01 = 0x01;
diff --git a/talk/session/phone/streamparams.cc b/talk/session/phone/streamparams.cc
new file mode 100644
index 0000000..415c4c7
--- /dev/null
+++ b/talk/session/phone/streamparams.cc
@@ -0,0 +1,90 @@
+/*
+ * libjingle
+ * Copyright 2011, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/phone/streamparams.h"
+
+namespace cricket {
+
+bool GetStreamBySsrc(const StreamParamsVec& streams, uint32 ssrc,
+                     StreamParams* stream_out) {
+  for (StreamParamsVec::const_iterator stream = streams.begin();
+       stream != streams.end(); ++stream) {
+    if (stream->has_ssrc(ssrc)) {
+      if (stream_out != NULL)
+        *stream_out = *stream;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool GetStreamByNickAndName(const StreamParamsVec& streams,
+                            const std::string& nick,
+                            const std::string& name,
+                            StreamParams* stream_out) {
+  for (StreamParamsVec::const_iterator stream = streams.begin();
+       stream != streams.end(); ++stream) {
+    if (stream->nick == nick && stream->name == name) {
+      if (stream_out != NULL)
+        *stream_out = *stream;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool RemoveStreamBySsrc(StreamParamsVec* streams, uint32 ssrc) {
+  bool ret = false;
+  for (StreamParamsVec::iterator stream = streams->begin();
+       stream != streams->end(); ) {
+    if (stream->has_ssrc(ssrc)) {
+      stream = streams->erase(stream);
+      ret = true;
+    } else {
+      ++stream;
+    }
+  }
+  return ret;
+}
+
+bool RemoveStreamByNickAndName(StreamParamsVec* streams,
+                               const std::string& nick,
+                               const std::string& name) {
+  bool ret = false;
+  for (StreamParamsVec::iterator stream = streams->begin();
+       stream != streams->end(); ) {
+    if (stream->nick == nick && stream->name == name) {
+      stream = streams->erase(stream);
+      ret = true;
+    } else {
+      ++stream;
+    }
+  }
+  return ret;
+}
+
+}  // namespace cricket
diff --git a/talk/session/phone/streamparams.h b/talk/session/phone/streamparams.h
index ec4b380..3e2bd94 100644
--- a/talk/session/phone/streamparams.h
+++ b/talk/session/phone/streamparams.h
@@ -43,9 +43,12 @@
 #ifndef TALK_SESSION_PHONE_STREAMPARAMS_H_
 #define TALK_SESSION_PHONE_STREAMPARAMS_H_
 
+#include <algorithm>
 #include <string>
 #include <vector>
 
+#include "talk/base/basictypes.h"
+
 namespace cricket {
 
 struct SsrcGroup {
@@ -94,6 +97,9 @@
   bool has_ssrcs() const {
     return !ssrcs.empty();
   }
+  bool has_ssrc(uint32 ssrc) const {
+    return std::find(ssrcs.begin(), ssrcs.end(), ssrc) != ssrcs.end();
+  }
 
   // Resource of the MUC jid of the participant of with this stream.
   // For 1:1 calls, should be left empty (which means remote streams
@@ -111,6 +117,29 @@
   std::string sync_label;  // Friendly name of cname.
 };
 
+typedef std::vector<StreamParams> StreamParamsVec;
+
+// Finds the stream in streams with the specified ssrc.
+// If you are only interested in the stream exist it is ok to call this function
+// stream_out = NULL.
+bool GetStreamBySsrc(const StreamParamsVec& streams, uint32 ssrc,
+                     StreamParams* stream_out);
+
+// Finds the stream in streams with the specified nick and name.
+// If you are only interested in the stream exist it is ok to call this function
+// stream_out = NULL.
+bool GetStreamByNickAndName(const StreamParamsVec& streams,
+                            const std::string& nick,
+                            const std::string& name,
+                            StreamParams* stream_out);
+
+// Removes the stream with ssrc from streams. Returns true if a stream is
+// removed, false otherwise.
+bool RemoveStreamBySsrc(StreamParamsVec* streams, uint32 ssrc);
+bool RemoveStreamByNickAndName(StreamParamsVec* streams,
+                               const std::string& nick,
+                               const std::string& name);
+
 }  // namespace cricket
 
 #endif  // TALK_SESSION_PHONE_STREAMPARAMS_H_
diff --git a/talk/session/phone/testutils.cc b/talk/session/phone/testutils.cc
index 679506a..f427f96 100644
--- a/talk/session/phone/testutils.cc
+++ b/talk/session/phone/testutils.cc
@@ -1,6 +1,6 @@
 /*
  * libjingle
- * Copyright 2004--2011 Google Inc.
+ * 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:
@@ -29,6 +29,9 @@
 
 #include <math.h>
 
+#ifdef HAVE_YUV
+#include "libyuv/compare.h"
+#endif
 #include "talk/base/bytebuffer.h"
 #include "talk/base/fileutils.h"
 #include "talk/base/gunit.h"
@@ -277,14 +280,17 @@
   return 10.0 * log10(65025.0 * size / sse);
 }
 
-double ComputeSumSquareError(const uint8 *org, const uint8 *rec,
-                             int size) {
+double ComputeSumSquareError(const uint8 *org, const uint8 *rec, int size) {
+#ifdef HAVE_YUV
+  return static_cast<double>(libyuv::ComputeSumSquareError(org, rec, size));
+#else
   double sse = 0.;
   for (int j = 0; j < size; ++j) {
     const int diff = static_cast<int>(org[j]) - static_cast<int>(rec[j]);
     sse += static_cast<double>(diff * diff);
   }
   return sse;
+#endif
 }
 
 // Loads the image with the specified prefix and size into |out|.
diff --git a/talk/session/phone/testutils.h b/talk/session/phone/testutils.h
index b4ce454..f5556e7 100644
--- a/talk/session/phone/testutils.h
+++ b/talk/session/phone/testutils.h
@@ -1,6 +1,6 @@
 /*
  * libjingle
- * Copyright 2004--2011 Google Inc.
+ * 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:
diff --git a/talk/session/phone/videocommon.h b/talk/session/phone/videocommon.h
index 6662bfc..fa695f0 100644
--- a/talk/session/phone/videocommon.h
+++ b/talk/session/phone/videocommon.h
@@ -36,34 +36,26 @@
 namespace cricket {
 
 //////////////////////////////////////////////////////////////////////////////
-// Definition of fourcc.
+// Definition of FourCC codes
 //////////////////////////////////////////////////////////////////////////////
-// Convert four characters to a fourcc code.
+// Convert four characters to a FourCC code.
 // Needs to be a macro otherwise the OS X compiler complains when the kFormat*
 // constants are used in a switch.
-#define FOURCC(a, b, c, d) (\
+#define FOURCC(a, b, c, d) ( \
     (static_cast<uint32>(a)) | (static_cast<uint32>(b) << 8) | \
     (static_cast<uint32>(c) << 16) | (static_cast<uint32>(d) << 24))
 
-// Get the name, that is, string with four characters, of a fourcc code.
-inline std::string GetFourccName(uint32 fourcc) {
-  std::string name;
-  name.push_back(static_cast<char>(fourcc & 0xFF));
-  name.push_back(static_cast<char>((fourcc >> 8) & 0xFF));
-  name.push_back(static_cast<char>((fourcc >> 16) & 0xFF));
-  name.push_back(static_cast<char>((fourcc >> 24) & 0xFF));
-  return name;
-}
-
-// FourCC codes used in Google Talk.
-// Some good pages discussing FourCC codes:
-//   http://developer.apple.com/quicktime/icefloe/dispatch020.html
+// Some pages discussing FourCC codes:
 //   http://www.fourcc.org/yuv.php
+//   http://v4l2spec.bytesex.org/spec/book1.htm
+//   http://developer.apple.com/quicktime/icefloe/dispatch020.html
+
 enum FourCC {
   // Canonical fourcc codes used in our code.
   FOURCC_I420 = FOURCC('I', '4', '2', '0'),
   FOURCC_I422 = FOURCC('I', '4', '2', '2'),
   FOURCC_I444 = FOURCC('I', '4', '4', '4'),
+  FOURCC_I400 = FOURCC('I', '4', '0', '0'),
   FOURCC_YV12 = FOURCC('Y', 'V', '1', '2'),
   FOURCC_YV16 = FOURCC('Y', 'V', '1', '6'),
   FOURCC_YV24 = FOURCC('Y', 'V', '2', '4'),
@@ -75,6 +67,9 @@
   FOURCC_ABGR = FOURCC('A', 'B', 'G', 'R'),
   FOURCC_BGRA = FOURCC('B', 'G', 'R', 'A'),
   FOURCC_ARGB = FOURCC('A', 'R', 'G', 'B'),
+  FOURCC_RGBP = FOURCC('R', 'G', 'B', 'P'), // bgr565
+  FOURCC_RGBO = FOURCC('R', 'G', 'B', 'O'), // abgr1555
+  FOURCC_R444 = FOURCC('R', '4', '4', '4'), // argb4444
   FOURCC_MJPG = FOURCC('M', 'J', 'P', 'G'),
   FOURCC_RAW  = FOURCC('r', 'a', 'w', ' '),
   FOURCC_NV21 = FOURCC('N', 'V', '2', '1'),
@@ -109,6 +104,16 @@
 // Converts fourcc aliases into canonical ones.
 uint32 CanonicalFourCC(uint32 fourcc);
 
+// Get FourCC code as a string
+inline std::string GetFourccName(uint32 fourcc) {
+  std::string name;
+  name.push_back(static_cast<char>(fourcc & 0xFF));
+  name.push_back(static_cast<char>((fourcc >> 8) & 0xFF));
+  name.push_back(static_cast<char>((fourcc >> 16) & 0xFF));
+  name.push_back(static_cast<char>((fourcc >> 24) & 0xFF));
+  return name;
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // Definition of VideoFormat.
 //////////////////////////////////////////////////////////////////////////////
diff --git a/talk/session/phone/webrtcvideoengine.cc b/talk/session/phone/webrtcvideoengine.cc
index 2fae254..9667538 100644
--- a/talk/session/phone/webrtcvideoengine.cc
+++ b/talk/session/phone/webrtcvideoengine.cc
@@ -40,6 +40,7 @@
 #include "talk/base/logging.h"
 #include "talk/base/stringutils.h"
 #include "talk/session/phone/rtputils.h"
+#include "talk/session/phone/streamparams.h"
 #include "talk/session/phone/videorenderer.h"
 #include "talk/session/phone/webrtcpassthroughrender.h"
 #include "talk/session/phone/webrtcvoiceengine.h"
@@ -58,6 +59,7 @@
 static const int kMinVideoBitrate = 100;
 static const int kStartVideoBitrate = 300;
 static const int kMaxVideoBitrate = 2000;
+static const int kConferenceModeMaxVideoBitrate = 500;
 
 static const int kVideoMtu = 1200;
 
@@ -535,6 +537,11 @@
     return false;
   }
   LOG(LS_INFO) << "Camera created with VCM";
+  CaptureResult ret = SetCapture(true);
+  if (ret != cricket::CR_SUCCESS && ret != cricket::CR_PENDING) {
+    LOG(LS_ERROR) << "Failed to start camera.";
+    return false;
+  }
   return true;
 }
 
@@ -764,21 +771,21 @@
 }
 
 void WebRtcVideoEngine::ConvertToCricketVideoCodec(
-    const webrtc::VideoCodec& in_codec, VideoCodec& out_codec) {
-  out_codec.id = in_codec.plType;
-  out_codec.name = in_codec.plName;
-  out_codec.width = in_codec.width;
-  out_codec.height = in_codec.height;
-  out_codec.framerate = in_codec.maxFramerate;
+    const webrtc::VideoCodec& in_codec, VideoCodec* out_codec) {
+  out_codec->id = in_codec.plType;
+  out_codec->name = in_codec.plName;
+  out_codec->width = in_codec.width;
+  out_codec->height = in_codec.height;
+  out_codec->framerate = in_codec.maxFramerate;
 }
 
 bool WebRtcVideoEngine::ConvertFromCricketVideoCodec(
-    const VideoCodec& in_codec, webrtc::VideoCodec& out_codec) {
+    const VideoCodec& in_codec, webrtc::VideoCodec* out_codec) {
   bool found = false;
   int ncodecs = vie_wrapper_->codec()->NumberOfCodecs();
   for (int i = 0; i < ncodecs; ++i) {
-    if (vie_wrapper_->codec()->GetCodec(i, out_codec) == 0 &&
-      in_codec.name == out_codec.plName) {
+    if (vie_wrapper_->codec()->GetCodec(i, *out_codec) == 0 &&
+      in_codec.name == out_codec->plName) {
       found = true;
       break;
     }
@@ -790,21 +797,21 @@
   }
 
   if (in_codec.id != 0)
-    out_codec.plType = in_codec.id;
+    out_codec->plType = in_codec.id;
 
   if (in_codec.width != 0)
-    out_codec.width = in_codec.width;
+    out_codec->width = in_codec.width;
 
   if (in_codec.height != 0)
-    out_codec.height = in_codec.height;
+    out_codec->height = in_codec.height;
 
   if (in_codec.framerate != 0)
-    out_codec.maxFramerate = in_codec.framerate;
+    out_codec->maxFramerate = in_codec.framerate;
 
   // Init the codec with the default bandwidth options.
-  out_codec.minBitrate = kMinVideoBitrate;
-  out_codec.startBitrate = kStartVideoBitrate;
-  out_codec.maxBitrate = kMaxVideoBitrate;
+  out_codec->minBitrate = kMinVideoBitrate;
+  out_codec->startBitrate = kStartVideoBitrate;
+  out_codec->maxBitrate = kMaxVideoBitrate;
 
   return true;
 }
@@ -822,7 +829,7 @@
 
 bool WebRtcVideoEngine::SetVoiceEngine(WebRtcVoiceEngine* voice_engine) {
   if (initialized_) {
-    LOG(LS_WARNING) << "SetVoiceEngine can not be called after Init.";
+    LOG(LS_WARNING) << "SetVoiceEngine can not be called after Init";
     return false;
   }
   voice_engine_ = voice_engine;
@@ -831,7 +838,7 @@
 
 bool WebRtcVideoEngine::EnableTimedRender() {
   if (initialized_) {
-    LOG(LS_WARNING) << "EnableTimedRender can not be called after Init.";
+    LOG(LS_WARNING) << "EnableTimedRender can not be called after Init";
     return false;
   }
   render_module_.reset(webrtc::VideoRender::CreateVideoRender(0, NULL,
@@ -880,7 +887,7 @@
   if (capturer == NULL) {
     // Stop capturing before clearing the capturer.
     if (SetCapture(false) != CR_SUCCESS) {
-      LOG(LS_WARNING) << "Camera failed to stop.";
+      LOG(LS_WARNING) << "Camera failed to stop";
       return false;
     }
     ClearCapturer();
@@ -980,6 +987,7 @@
       sending_(false),
       render_started_(false),
       muted_(false),
+      local_ssrc_(0),
       send_min_bitrate_(kMinVideoBitrate),
       send_start_bitrate_(kStartVideoBitrate),
       send_max_bitrate_(kMaxVideoBitrate),
@@ -1059,7 +1067,7 @@
 
     // Remove all receive streams and the default channel.
     while (!mux_channels_.empty()) {
-      RemoveStream(mux_channels_.begin()->first);
+      RemoveRecvStream(mux_channels_.begin()->first);
     }
   }
 
@@ -1074,7 +1082,7 @@
       iter != codecs.end(); ++iter) {
     if (engine()->FindCodec(*iter)) {
       webrtc::VideoCodec wcodec;
-      if (engine()->ConvertFromCricketVideoCodec(*iter, wcodec)) {
+      if (engine()->ConvertFromCricketVideoCodec(*iter, &wcodec)) {
         receive_codecs_.push_back(wcodec);
       }
     } else {
@@ -1099,7 +1107,7 @@
   VideoCodec checked_codec;
   VideoCodec current;  // defaults to 0x0
   if (sending_) {
-    engine()->ConvertToCricketVideoCodec(*send_codec_, current);
+    engine()->ConvertToCricketVideoCodec(*send_codec_, &current);
   }
   for (std::vector<VideoCodec>::const_iterator iter = codecs.begin();
       iter != codecs.end(); ++iter) {
@@ -1109,7 +1117,7 @@
       fec_type = iter->id;
     } else if (engine()->CanSendCodec(*iter, current, &checked_codec)) {
       webrtc::VideoCodec wcodec;
-      if (engine()->ConvertFromCricketVideoCodec(checked_codec, wcodec)) {
+      if (engine()->ConvertFromCricketVideoCodec(checked_codec, &wcodec)) {
         send_codecs.push_back(wcodec);
       }
     } else {
@@ -1185,8 +1193,12 @@
 }
 
 bool WebRtcVideoMediaChannel::SetSend(bool send) {
+  if (local_ssrc_ == 0 && send) {
+    LOG(LS_ERROR) << "No stream added";
+    return false;
+  }
   if (send == sending()) {
-    return true;  // no action required
+    return true;  // No action required.
   }
 
   if (send) {
@@ -1195,20 +1207,18 @@
     if (!send_codec_.get()) {
       return false;
     }
-
-    if (engine()->vie()->base()->StartSend(vie_channel_) != 0) {
-      LOG_RTCERR1(StartSend, vie_channel_);
+    // Start send now.
+    if (!StartSend()) {
       return false;
     }
   } else {
     // We've been asked to stop sending.
-    if (engine()->vie()->base()->StopSend(vie_channel_) != 0) {
-      LOG_RTCERR1(StopSend, vie_channel_);
+    if (!StopSend()) {
       return false;
     }
   }
-
   sending_ = send;
+
   return true;
 }
 
@@ -1217,8 +1227,58 @@
   return (it != mux_channels_.end()) ? it->second->channel_id() : -1;
 }
 
-bool WebRtcVideoMediaChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) {
-  if (mux_channels_.find(ssrc) != mux_channels_.end()) {
+bool WebRtcVideoMediaChannel::AddSendStream(const StreamParams& sp) {
+  // TODO: Implement send media from multiple streams.
+  if (local_ssrc_ != 0) {
+    LOG(LS_ERROR) << "WebRtcVideoMediaChannel supports one sending channel";
+    return false;
+  }
+  if (sp.ssrcs.size() != 1) {
+    LOG(LS_ERROR) << "WebRtcVideoMediaChannel supports one sending SSRC per"
+                  << " stream";
+    return false;
+  }
+  if (engine()->vie()->rtp()->SetLocalSSRC(vie_channel_,
+                                           sp.first_ssrc()) != 0) {
+    LOG_RTCERR1(SetLocalSSRC, vie_channel_);
+    return false;
+  }
+  if (engine()->vie()->rtp()->SetRTCPCName(vie_channel_,
+                                           sp.cname.c_str()) != 0) {
+    LOG_RTCERR2(SetRTCPCName, vie_channel_, sp.cname.c_str());
+    return false;
+  }
+
+  local_ssrc_ = sp.first_ssrc();
+
+  if (sending_) {
+    return StartSend();
+  }
+  return true;
+}
+
+bool WebRtcVideoMediaChannel::RemoveSendStream(uint32 ssrc) {
+  // TODO: Implement send media from multiple streams.
+  if (ssrc != local_ssrc_) {
+    return false;
+  }
+  if (sending_) {
+    StopSend();
+  }
+  local_ssrc_ = 0;
+  return true;
+}
+
+bool WebRtcVideoMediaChannel::AddRecvStream(const StreamParams& sp) {
+  if (mux_channels_.find(sp.first_ssrc()) != mux_channels_.end()) {
+    LOG(LS_ERROR) << "Stream already exists";
+    return false;
+  }
+
+  // TODO: Implement recv media from multiple SSRCs per stream.
+  if (sp.ssrcs.size() != 1) {
+    LOG(LS_ERROR) << "WebRtcVideoMediaChannel supports one receiving SSRC per"
+                  << " stream";
     return false;
   }
 
@@ -1231,19 +1291,20 @@
     LOG_RTCERR1(CreateChannel, channel_id);
     return false;
   }
-  if (!ConfigureChannel(channel_id) || !ConfigureReceiving(channel_id, ssrc)) {
+  if (!ConfigureChannel(channel_id) || !ConfigureReceiving(channel_id,
+                                                           sp.first_ssrc())) {
     engine_->vie()->base()->DeleteChannel(channel_id);
     return false;
   }
 
-  LOG(LS_INFO) << "New video stream " << ssrc
+  LOG(LS_INFO) << "New video stream " << sp.first_ssrc()
                << " registered to VideoEngine channel #"
-               << channel_id << ".";
+               << channel_id;
 
   return true;
 }
 
-bool WebRtcVideoMediaChannel::RemoveStream(uint32 ssrc) {
+bool WebRtcVideoMediaChannel::RemoveRecvStream(uint32 ssrc) {
   ChannelMap::iterator it = mux_channels_.find(ssrc);
 
   if (it == mux_channels_.end()) {
@@ -1266,7 +1327,7 @@
 
   LOG(LS_INFO) << "Removing video stream " << ssrc
                << " with VideoEngine channel #"
-               << channel_id << ".";
+               << channel_id;
   if (engine()->vie()->base()->DeleteChannel(channel_id) == -1) {
     LOG_RTCERR1(DeleteChannel, channel_id);
     // Leak the WebRtcVideoChannelInfo owned by |it| but remove the channel from
@@ -1280,6 +1341,22 @@
   return true;
 }
 
+bool WebRtcVideoMediaChannel::StartSend() {
+  if (engine()->vie()->base()->StartSend(vie_channel_) != 0) {
+    LOG_RTCERR1(StartSend, vie_channel_);
+    return false;
+  }
+  return true;
+}
+
+bool WebRtcVideoMediaChannel::StopSend() {
+  if (engine()->vie()->base()->StopSend(vie_channel_) != 0) {
+    LOG_RTCERR1(StopSend, vie_channel_);
+    return false;
+  }
+  return true;
+}
+
 bool WebRtcVideoMediaChannel::SetRenderer(uint32 ssrc,
                                           VideoRenderer* renderer) {
   if (mux_channels_.find(ssrc) == mux_channels_.end())
@@ -1317,7 +1394,7 @@
     sinfo.framerate_input = local_stream_info_->framerate();
     sinfo.framerate_sent = encoder_observer_->framerate();
     sinfo.nominal_bitrate = encoder_observer_->bitrate();
-    sinfo.preferred_bitrate = kMaxVideoBitrate;
+    sinfo.preferred_bitrate = send_max_bitrate_;
 
     // Get received RTCP statistics for the sender, if available.
     // It's not a fatal error if we can't, since RTCP may not have arrived yet.
@@ -1452,12 +1529,12 @@
 
   uint32 ssrc = 0;
   if (!GetRtcpSsrc(packet->data(), packet->length(), &ssrc)) {
-    LOG(LS_WARNING) << "Failed to parse SSRC from received RTCP packet.";
+    LOG(LS_WARNING) << "Failed to parse SSRC from received RTCP packet";
     return;
   }
   int type = 0;
   if (!GetRtcpType(packet->data(), packet->length(), & type)) {
-    LOG(LS_WARNING) << "Failed to parse type from received RTCP packet.";
+    LOG(LS_WARNING) << "Failed to parse type from received RTCP packet";
     return;
   }
 
@@ -1476,25 +1553,6 @@
                                                 packet->length());
 }
 
-void WebRtcVideoMediaChannel::SetSendSsrc(uint32 id) {
-  if (!sending_) {
-    if (engine()->vie()->rtp()->SetLocalSSRC(vie_channel_, id) != 0) {
-      LOG_RTCERR1(SetLocalSSRC, vie_channel_);
-    }
-  } else {
-    LOG(LS_ERROR) << "Channel already in send state";
-  }
-}
-
-bool WebRtcVideoMediaChannel::SetRtcpCName(const std::string& cname) {
-  if (engine()->vie()->rtp()->SetRTCPCName(vie_channel_,
-                                           cname.c_str()) != 0) {
-    LOG_RTCERR2(SetRTCPCName, vie_channel_, cname.c_str());
-    return false;
-  }
-  return true;
-}
-
 bool WebRtcVideoMediaChannel::Mute(bool on) {
   muted_ = on;
   return true;
@@ -1504,7 +1562,7 @@
   LOG(LS_INFO) << "RtcVideoMediaChanne::SetSendBandwidth";
 
   if (!send_codec_.get()) {
-    LOG(LS_INFO) << "The send codec has not been set up yet.";
+    LOG(LS_INFO) << "The send codec has not been set up yet";
     return true;
   }
 
@@ -1515,7 +1573,7 @@
     // Use the default values for min bitrate.
     min_bitrate = kMinVideoBitrate;
     // Use the default value or the bps for the max
-    max_bitrate = (bps <= 0) ? kMaxVideoBitrate : (bps / 1000);
+    max_bitrate = (bps <= 0) ? send_max_bitrate_ : (bps / 1000);
     // Maximum start bitrate can be kStartVideoBitrate.
     start_bitrate = talk_base::_min(kStartVideoBitrate, max_bitrate);
   } else {
@@ -1547,6 +1605,20 @@
   // Save the options, to be interpreted where appropriate.
   channel_options_ = options;
 
+  // Adjust send codec bitrate if needed.
+  int expected_bitrate = (0 != (channel_options_ & OPT_CONFERENCE)) ?
+      kConferenceModeMaxVideoBitrate : kMaxVideoBitrate;
+  if (NULL != send_codec_.get() && send_max_bitrate_ != expected_bitrate) {
+    // On success, SetSendCodec() will reset send_max_bitrate_ to
+    // expected_bitrate.
+    if (!SetSendCodec(*send_codec_,
+                      send_min_bitrate_,
+                      send_start_bitrate_,
+                      expected_bitrate)) {
+      return false;
+    }
+  }
+
   return true;
 }
 
diff --git a/talk/session/phone/webrtcvideoengine.h b/talk/session/phone/webrtcvideoengine.h
index c0e49f3..c25c4da 100644
--- a/talk/session/phone/webrtcvideoengine.h
+++ b/talk/session/phone/webrtcvideoengine.h
@@ -129,9 +129,9 @@
   void RegisterChannel(WebRtcVideoMediaChannel* channel);
   void UnregisterChannel(WebRtcVideoMediaChannel* channel);
   void ConvertToCricketVideoCodec(const webrtc::VideoCodec& in_codec,
-                                  VideoCodec& out_codec);
+                                  VideoCodec*  out_codec);
   bool ConvertFromCricketVideoCodec(const VideoCodec& in_codec,
-                                    webrtc::VideoCodec& out_codec);
+                                    webrtc::VideoCodec* out_codec);
   // Check whether the supplied trace should be ignored.
   bool ShouldIgnoreTrace(const std::string& trace);
   int GetNumOfChannels();
@@ -212,8 +212,11 @@
   virtual bool SetSendCodecs(const std::vector<VideoCodec> &codecs);
   virtual bool SetRender(bool render);
   virtual bool SetSend(bool send);
-  virtual bool AddStream(uint32 ssrc, uint32 voice_ssrc);
-  virtual bool RemoveStream(uint32 ssrc);
+
+  virtual bool AddSendStream(const StreamParams& sp);
+  virtual bool RemoveSendStream(uint32 ssrc);
+  virtual bool AddRecvStream(const StreamParams& sp);
+  virtual bool RemoveRecvStream(uint32 ssrc);
   virtual bool SetRenderer(uint32 ssrc, VideoRenderer* renderer);
   virtual bool GetStats(VideoMediaInfo* info);
   virtual bool AddScreencast(uint32 ssrc, const ScreencastId& id) {
@@ -227,8 +230,6 @@
 
   virtual void OnPacketReceived(talk_base::Buffer* packet);
   virtual void OnRtcpReceived(talk_base::Buffer* packet);
-  virtual void SetSendSsrc(uint32 id);
-  virtual bool SetRtcpCName(const std::string& cname);
   virtual bool Mute(bool on);
   virtual bool SetRecvRtpHeaderExtensions(
       const std::vector<RtpHeaderExtension>& extensions) {
@@ -279,6 +280,12 @@
   // |reset| is set to whether resetting has happened on vie or not.
   // Returns false on error.
   bool MaybeResetVieSendCodec(int new_width, int new_height, bool* reset);
+  // Call Webrtc function to start sending media on |vie_channel_|.
+  // Does not affect |sending_|.
+  bool StartSend();
+  // Call Webrtc function to stop sending media on |vie_channel_|.
+  // Does not affect |sending_|.
+  bool StopSend();
 
   WebRtcVideoEngine* engine_;
   VoiceMediaChannel* voice_channel_;
@@ -288,6 +295,8 @@
   bool sending_;
   bool render_started_;
   bool muted_;  // Flag to tell if we need to mute video.
+  // Our local SSRC. Currently only one send stream is supported.
+  uint32 local_ssrc_;
   int send_min_bitrate_;
   int send_start_bitrate_;
   int send_max_bitrate_;
diff --git a/talk/session/phone/webrtcvideoengine_unittest.cc b/talk/session/phone/webrtcvideoengine_unittest.cc
index 1db450e..88d1505 100644
--- a/talk/session/phone/webrtcvideoengine_unittest.cc
+++ b/talk/session/phone/webrtcvideoengine_unittest.cc
@@ -51,6 +51,7 @@
 static const unsigned int kMinBandwidthKbps = 100;
 static const unsigned int kStartBandwidthKbps = 300;
 static const unsigned int kMaxBandwidthKbps = 2000;
+static const unsigned int kConferenceModeMaxBandwidthKbps = 500;
 
 class FakeViEWrapper : public cricket::ViEWrapper {
  public:
@@ -275,6 +276,8 @@
   std::vector<cricket::VideoCodec> codec_list;
   codec_list.push_back(kVP8Codec);
   EXPECT_TRUE(channel_->SetSendCodecs(codec_list));
+  EXPECT_TRUE(channel_->AddSendStream(
+      cricket::StreamParams::CreateLegacy(123)));
   EXPECT_TRUE(channel_->SetSend(true));
 
   // Capture a smaller frame and verify vie send codec has been reset to
@@ -303,7 +306,7 @@
   EXPECT_TRUE(channel_->SetRecvCodecs(codecs));
 
   webrtc::VideoCodec wcodec;
-  EXPECT_TRUE(engine_.ConvertFromCricketVideoCodec(kVP8Codec, wcodec));
+  EXPECT_TRUE(engine_.ConvertFromCricketVideoCodec(kVP8Codec, &wcodec));
   EXPECT_TRUE(vie_.ReceiveCodecRegistered(channel_num, wcodec));
 }
 
@@ -385,6 +388,8 @@
   std::vector<cricket::VideoCodec> codecs;
   codecs.push_back(kVP8Codec);
   EXPECT_TRUE(channel_->SetSendCodecs(codecs));
+  EXPECT_TRUE(channel_->AddSendStream(
+      cricket::StreamParams::CreateLegacy(123)));
 
   // Verify we can start/stop/start/stop sending.
   EXPECT_TRUE(channel_->SetSend(true));
@@ -445,32 +450,60 @@
 }
 
 // Test SetSendSsrc.
-TEST_F(WebRtcVideoEngineTestFake, SetSendSsrc) {
+TEST_F(WebRtcVideoEngineTestFake, SetSendSsrcAndCname) {
   EXPECT_TRUE(SetupEngine());
   int channel_num = vie_.GetLastChannel();
 
-  // Verify ssrc is set correctly.
-  channel_->SetSendSsrc(1234);
+  cricket::StreamParams stream;
+  stream.ssrcs.push_back(1234);
+  stream.cname = "cname";
+  channel_->AddSendStream(stream);
+
   unsigned int ssrc = 0;
   EXPECT_EQ(0, vie_.GetLocalSSRC(channel_num, ssrc));
   EXPECT_EQ(1234U, ssrc);
-}
 
-// Test SetRtcpCName.
-TEST_F(WebRtcVideoEngineTestFake, SetRtcpCName) {
-  EXPECT_TRUE(SetupEngine());
-  int channel_num = vie_.GetLastChannel();
-
-  // Verify rtcp cname is set correctly.
-  EXPECT_TRUE(channel_->SetRtcpCName("cname"));
   char rtcp_cname[256];
   EXPECT_EQ(0, vie_.GetRTCPCName(channel_num, rtcp_cname));
   EXPECT_STREQ("cname", rtcp_cname);
 }
 
-// Test SetOptions.
-// TODO: Test whatever need to be turned on for multiway WebRTC call.
-TEST_F(WebRtcVideoEngineTestFake, SetOptions) {
+// Test SetOptions with OPT_CONFERENCE flag.
+TEST_F(WebRtcVideoEngineTestFake, SetOptionsWithConferenceMode) {
+  EXPECT_TRUE(SetupEngine());
+  int channel_num = vie_.GetLastChannel();
+  EXPECT_TRUE(channel_->SetSendCodecs(engine_.codecs()));
+
+  // Verify default send codec and bitrate.
+  webrtc::VideoCodec gcodec;
+  EXPECT_EQ(0, vie_.GetSendCodec(channel_num, gcodec));
+  EXPECT_EQ(kVP8Codec.id, gcodec.plType);
+  EXPECT_STREQ(kVP8Codec.name.c_str(), gcodec.plName);
+  EXPECT_EQ(kMinBandwidthKbps, gcodec.minBitrate);
+  EXPECT_EQ(kStartBandwidthKbps, gcodec.startBitrate);
+  EXPECT_EQ(kMaxBandwidthKbps, gcodec.maxBitrate);
+
+  // Set options with OPT_CONFERENCE flag.
+  EXPECT_TRUE(channel_->SetOptions(cricket::OPT_CONFERENCE));
+
+  // Verify channel now has new max bitrate.
+  EXPECT_EQ(0, vie_.GetSendCodec(channel_num, gcodec));
+  EXPECT_EQ(kVP8Codec.id, gcodec.plType);
+  EXPECT_STREQ(kVP8Codec.name.c_str(), gcodec.plName);
+  EXPECT_EQ(kMinBandwidthKbps, gcodec.minBitrate);
+  EXPECT_EQ(kStartBandwidthKbps, gcodec.startBitrate);
+  EXPECT_EQ(kConferenceModeMaxBandwidthKbps, gcodec.maxBitrate);
+
+  // Set options back to zero.
+  EXPECT_TRUE(channel_->SetOptions(0));
+
+  // Verify channel now has default max bitrate again.
+  EXPECT_EQ(0, vie_.GetSendCodec(channel_num, gcodec));
+  EXPECT_EQ(kVP8Codec.id, gcodec.plType);
+  EXPECT_STREQ(kVP8Codec.name.c_str(), gcodec.plName);
+  EXPECT_EQ(kMinBandwidthKbps, gcodec.minBitrate);
+  EXPECT_EQ(kStartBandwidthKbps, gcodec.startBitrate);
+  EXPECT_EQ(kMaxBandwidthKbps, gcodec.maxBitrate);
 }
 
 /////////////////////////
@@ -569,9 +602,8 @@
   // Technically we should call vcm->AddRef since we are using the vcm below,
   // however the FakeWebRtcVideoCaptureModule didn't implemented the refcount.
   // So for testing, this should be fine.
+  // The SetCaptureModule call always starts the capture.
   EXPECT_TRUE(engine_.SetCaptureModule(vcm));
-  EXPECT_FALSE(engine_.IsCapturing());
-  EXPECT_EQ(cricket::CR_PENDING, engine_.SetCapture(true));
   EXPECT_TRUE(engine_.IsCapturing());
   EXPECT_EQ(engine_.default_codec_format().width, vcm->cap().width);
   EXPECT_EQ(engine_.default_codec_format().height, vcm->cap().height);
@@ -685,6 +717,10 @@
   Base::AddRemoveRecvStreams();
 }
 
+TEST_F(WebRtcVideoMediaChannelTest, AddRemoveSendStreams) {
+  Base::AddRemoveSendStreams();
+}
+
 TEST_F(WebRtcVideoMediaChannelTest, SimulateConference) {
   Base::SimulateConference();
 }
diff --git a/talk/session/phone/webrtcvideoframe_unittest.cc b/talk/session/phone/webrtcvideoframe_unittest.cc
index 5793ab8..b115625 100644
--- a/talk/session/phone/webrtcvideoframe_unittest.cc
+++ b/talk/session/phone/webrtcvideoframe_unittest.cc
@@ -67,6 +67,11 @@
 TEST_WEBRTCVIDEOFRAME(ConvertToABGRBuffer)
 #endif
 TEST_WEBRTCVIDEOFRAME(ConvertToARGBBuffer)
+//TEST_LMIVIDEOFRAME(ConvertToRGB24Buffer)
+//TEST_LMIVIDEOFRAME(ConvertToRAWBuffer)
+//TEST_LMIVIDEOFRAME(ConvertToRGB565Buffer)
+//TEST_LMIVIDEOFRAME(ConvertToARGB1555Buffer)
+//TEST_LMIVIDEOFRAME(ConvertToARGB4444Buffer)
 //TEST_WEBRTCVIDEOFRAME(ConvertToYUY2Buffer)
 //TEST_WEBRTCVIDEOFRAME(ConvertToI422Buffer)
 TEST_WEBRTCVIDEOFRAME(ConvertARGBToBayerGRBG)
diff --git a/talk/session/phone/webrtcvoiceengine.cc b/talk/session/phone/webrtcvoiceengine.cc
index 561c13c..da9829a 100644
--- a/talk/session/phone/webrtcvoiceengine.cc
+++ b/talk/session/phone/webrtcvoiceengine.cc
@@ -45,6 +45,7 @@
 #include "talk/base/logging.h"
 #include "talk/base/stringencode.h"
 #include "talk/base/stringutils.h"
+#include "talk/session/phone/streamparams.h"
 #include "talk/session/phone/voiceprocessor.h"
 #include "talk/session/phone/webrtcvoe.h"
 
@@ -494,7 +495,7 @@
   }
 
   // No typing detection support on iOS or Android.
-#endif // !IOS && !ANDROID
+#endif  // !IOS && !ANDROID
 
   return true;
 }
@@ -1191,7 +1192,8 @@
       desired_playout_(false),
       playout_(false),
       desired_send_(SEND_NOTHING),
-      send_(SEND_NOTHING) {
+      send_(SEND_NOTHING),
+      local_ssrc_(0) {
   engine->RegisterChannel(this);
   LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel "
                   << voe_channel();
@@ -1205,9 +1207,6 @@
   // Enable RTCP (for quality stats and feedback messages)
   EnableRtcp(voe_channel());
 
-  // Create a random but nonzero send SSRC
-  SetSendSsrc(talk_base::CreateRandomNonZeroId());
-
   // Reset all recv codecs; they will be enabled via SetRecvCodecs.
   ResetRecvCodecs(voe_channel());
 }
@@ -1226,7 +1225,7 @@
   engine()->UnregisterChannel(this);
   // Remove any remaining streams.
   while (!mux_channels_.empty()) {
-    RemoveStream(mux_channels_.begin()->first);
+    RemoveRecvStream(mux_channels_.begin()->first);
   }
 
   // Delete the primary channel.
@@ -1462,7 +1461,9 @@
 
 bool WebRtcVoiceMediaChannel::SetSend(SendFlags send) {
   desired_send_ = send;
-  return ChangeSend(desired_send_);
+  if (local_ssrc_ != 0)
+    return ChangeSend(desired_send_);
+  return true;
 }
 
 bool WebRtcVoiceMediaChannel::PauseSend() {
@@ -1573,9 +1574,46 @@
   return true;
 }
 
-bool WebRtcVoiceMediaChannel::AddStream(uint32 ssrc) {
+bool WebRtcVoiceMediaChannel::AddSendStream(const StreamParams& sp) {
+  if (local_ssrc_ != 0) {
+    LOG(LS_ERROR) << "WebRtcVoiceMediaChannel supports one sending channel.";
+    return false;
+  }
+
+  if (engine()->voe()->rtp()->SetLocalSSRC(voe_channel(), sp.first_ssrc())
+        == -1) {
+    LOG_RTCERR2(SetSendSSRC, voe_channel(), sp.first_ssrc());
+    return false;
+  }
+
+  if (engine()->voe()->rtp()->SetRTCP_CNAME(voe_channel(),
+                                            sp.cname.c_str()) == -1) {
+     LOG_RTCERR2(SetRTCP_CNAME, voe_channel(), sp.cname);
+     return false;
+  }
+
+  local_ssrc_ = sp.first_ssrc();
+  if (desired_send_ != send_)
+    return ChangeSend(desired_send_);
+  return true;
+}
+
+bool WebRtcVoiceMediaChannel::RemoveSendStream(uint32 ssrc) {
+  if (ssrc != local_ssrc_) {
+    return false;
+  }
+  local_ssrc_ = 0;
+  ChangeSend(SEND_NOTHING);
+  return true;
+}
+
+bool WebRtcVoiceMediaChannel::AddRecvStream(const StreamParams& sp) {
   talk_base::CritScope lock(&mux_channels_cs_);
 
+  if (!VERIFY(sp.ssrcs.size() == 1))
+    return false;
+  uint32 ssrc = sp.first_ssrc();
+
   if (mux_channels_.find(ssrc) != mux_channels_.end()) {
     return false;
   }
@@ -1644,7 +1682,7 @@
   return SetPlayout(channel, playout_);
 }
 
-bool WebRtcVoiceMediaChannel::RemoveStream(uint32 ssrc) {
+bool WebRtcVoiceMediaChannel::RemoveRecvStream(uint32 ssrc) {
   talk_base::CritScope lock(&mux_channels_cs_);
   ChannelMap::iterator it = mux_channels_.find(ssrc);
 
@@ -1888,22 +1926,6 @@
                                                     packet->length());
 }
 
-void WebRtcVoiceMediaChannel::SetSendSsrc(uint32 ssrc) {
-  if (engine()->voe()->rtp()->SetLocalSSRC(voe_channel(), ssrc)
-      == -1) {
-     LOG_RTCERR2(SetSendSSRC, voe_channel(), ssrc);
-  }
-}
-
-bool WebRtcVoiceMediaChannel::SetRtcpCName(const std::string& cname) {
-  if (engine()->voe()->rtp()->SetRTCP_CNAME(voe_channel(),
-                                                    cname.c_str()) == -1) {
-     LOG_RTCERR2(SetRTCP_CNAME, voe_channel(), cname);
-     return false;
-  }
-  return true;
-}
-
 bool WebRtcVoiceMediaChannel::Mute(bool muted) {
   if (engine()->voe()->volume()->SetInputMute(voe_channel(),
       muted) == -1) {
@@ -1978,7 +2000,6 @@
   sinfo.echo_delay_std_ms = -1;
   if (engine()->voe()->processing()->GetEcMetricsStatus(echo_metrics_on) !=
       -1 && echo_metrics_on) {
-
     // TODO: we may want to use VoECallReport::GetEchoMetricsSummary
     // here, but it appears to be unsuitable currently. Revisit after this is
     // investigated: http://b/issue?id=5666755
diff --git a/talk/session/phone/webrtcvoiceengine.h b/talk/session/phone/webrtcvoiceengine.h
index ef677d8..dfb9023 100644
--- a/talk/session/phone/webrtcvoiceengine.h
+++ b/talk/session/phone/webrtcvoiceengine.h
@@ -296,8 +296,10 @@
   virtual bool SetSend(SendFlags send);
   bool PauseSend();
   bool ResumeSend();
-  virtual bool AddStream(uint32 ssrc);
-  virtual bool RemoveStream(uint32 ssrc);
+  virtual bool AddSendStream(const StreamParams& sp);
+  virtual bool RemoveSendStream(uint32 ssrc);
+  virtual bool AddRecvStream(const StreamParams& sp);
+  virtual bool RemoveRecvStream(uint32 ssrc);
   virtual bool GetActiveStreams(AudioInfo::StreamList* actives);
   virtual int GetOutputLevel();
   virtual bool SetOutputScaling(uint32 ssrc, double left, double right);
@@ -309,8 +311,6 @@
 
   virtual void OnPacketReceived(talk_base::Buffer* packet);
   virtual void OnRtcpReceived(talk_base::Buffer* packet);
-  virtual void SetSendSsrc(uint32 id);
-  virtual bool SetRtcpCName(const std::string& cname);
   virtual bool Mute(bool mute);
   virtual bool SetSendBandwidth(bool autobw, int bps) { return false; }
   virtual bool GetStats(VoiceMediaInfo* info);
@@ -355,12 +355,14 @@
   bool playout_;
   SendFlags desired_send_;
   SendFlags send_;
+  uint32 local_ssrc_;
   ChannelMap mux_channels_;  // for multiple sources
   // mux_channels_ can be read from WebRtc callback thread.  Accesses off the
   // WebRtc thread must be synchronized with edits on the worker thread.  Reads
   // on the worker thread are ok.
   mutable talk_base::CriticalSection mux_channels_cs_;
 };
-}
+
+}  // namespace cricket
 
 #endif  // TALK_SESSION_PHONE_WEBRTCVOICEENGINE_H_
diff --git a/talk/session/phone/webrtcvoiceengine_unittest.cc b/talk/session/phone/webrtcvoiceengine_unittest.cc
index 6bcc04e..82838d1 100644
--- a/talk/session/phone/webrtcvoiceengine_unittest.cc
+++ b/talk/session/phone/webrtcvoiceengine_unittest.cc
@@ -26,6 +26,7 @@
     &kTelephoneEventCodec,
 };
 const char kRingbackTone[] = "RIFF____WAVE____ABCD1234";
+static uint32 kSsrc1 = 0x99;
 
 class FakeVoEWrapper : public cricket::VoEWrapper {
  public:
@@ -103,6 +104,10 @@
       channel_ = engine_.CreateChannel();
       result = (channel_ != NULL);
     }
+    if (result) {
+      result = channel_->AddSendStream(
+          cricket::StreamParams::CreateLegacy(kSsrc1));
+    }
     return result;
   }
   void DeliverPacket(const void* data, int len) {
@@ -255,7 +260,8 @@
   codecs[0].id = 106;  // collide with existing telephone-event
   codecs[2].id = 126;
   EXPECT_TRUE(channel_->SetRecvCodecs(codecs));
-  EXPECT_TRUE(channel_->AddStream(2));
+  EXPECT_TRUE(channel_->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrc1)));
   int channel_num2 = voe_.GetLastChannel();
   webrtc::CodecInst gcodec;
   talk_base::strcpyn(gcodec.plname, ARRAY_SIZE(gcodec.plname), "ISAC");
@@ -622,7 +628,7 @@
   EXPECT_TRUE(voe_.GetPlayout(channel_num1));
 
   // Adding another stream should disable playout on the default channel.
-  EXPECT_TRUE(channel_->AddStream(2));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
   int channel_num2 = voe_.GetLastChannel();
   std::vector<cricket::AudioCodec> codecs;
   codecs.push_back(kPcmuCodec);
@@ -636,7 +642,7 @@
   EXPECT_TRUE(voe_.GetPlayout(channel_num2));
 
   // Adding yet another stream should have stream 2 and 3 enabled for playout.
-  EXPECT_TRUE(channel_->AddStream(3));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(3)));
   int channel_num3 = voe_.GetLastChannel();
   EXPECT_FALSE(voe_.GetPlayout(channel_num1));
   EXPECT_TRUE(voe_.GetPlayout(channel_num2));
@@ -663,8 +669,8 @@
 
   // Now remove the new streams and verify that the default channel is
   // played out again.
-  EXPECT_TRUE(channel_->RemoveStream(3));
-  EXPECT_TRUE(channel_->RemoveStream(2));
+  EXPECT_TRUE(channel_->RemoveRecvStream(3));
+  EXPECT_TRUE(channel_->RemoveRecvStream(2));
 
   EXPECT_TRUE(voe_.GetPlayout(channel_num1));
 }
@@ -832,29 +838,30 @@
 }
 
 // Test that we can set the outgoing SSRC properly.
+// SSRC is set in SetupEngine by calling AddSendStream.
 TEST_F(WebRtcVoiceEngineTestFake, SetSendSsrc) {
   EXPECT_TRUE(SetupEngine());
   int channel_num = voe_.GetLastChannel();
   unsigned int send_ssrc;
   EXPECT_EQ(0, voe_.GetLocalSSRC(channel_num, send_ssrc));
   EXPECT_NE(0U, send_ssrc);
-  channel_->SetSendSsrc(0x99);
   EXPECT_EQ(0, voe_.GetLocalSSRC(channel_num, send_ssrc));
-  EXPECT_EQ(0x99U, send_ssrc);
+  EXPECT_EQ(kSsrc1, send_ssrc);
 }
 
 // Test that we can set the outgoing SSRC properly with multiple streams.
+// SSRC is set in SetupEngine by calling AddSendStream.
 TEST_F(WebRtcVoiceEngineTestFake, SetSendSsrcWithMultipleStreams) {
   EXPECT_TRUE(SetupEngine());
   int channel_num1 = voe_.GetLastChannel();
   unsigned int send_ssrc;
-  channel_->SetSendSsrc(0x99);
   EXPECT_EQ(0, voe_.GetLocalSSRC(channel_num1, send_ssrc));
-  EXPECT_EQ(0x99U, send_ssrc);
-  EXPECT_TRUE(channel_->AddStream(2));
+  EXPECT_EQ(kSsrc1, send_ssrc);
+
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
   int channel_num2 = voe_.GetLastChannel();
   EXPECT_EQ(0, voe_.GetLocalSSRC(channel_num2, send_ssrc));
-  EXPECT_EQ(0x99U, send_ssrc);
+  EXPECT_EQ(kSsrc1, send_ssrc);
 }
 
 // Test that we can properly receive packets.
@@ -869,11 +876,11 @@
 // Test that we can properly receive packets on multiple streams.
 TEST_F(WebRtcVoiceEngineTestFake, RecvWithMultipleStreams) {
   EXPECT_TRUE(SetupEngine());
-  EXPECT_TRUE(channel_->AddStream(1));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
   int channel_num1 = voe_.GetLastChannel();
-  EXPECT_TRUE(channel_->AddStream(2));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
   int channel_num2 = voe_.GetLastChannel();
-  EXPECT_TRUE(channel_->AddStream(3));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(3)));
   int channel_num3 = voe_.GetLastChannel();
   // Create packets with the right SSRCs.
   char packets[4][sizeof(kPcmuFrame)];
@@ -903,24 +910,24 @@
   EXPECT_TRUE(voe_.CheckNoPacket(channel_num2));
   EXPECT_TRUE(voe_.CheckPacket(channel_num3, packets[3],
                                sizeof(packets[3])));
-  EXPECT_TRUE(channel_->RemoveStream(3));
-  EXPECT_TRUE(channel_->RemoveStream(2));
-  EXPECT_TRUE(channel_->RemoveStream(1));
+  EXPECT_TRUE(channel_->RemoveRecvStream(3));
+  EXPECT_TRUE(channel_->RemoveRecvStream(2));
+  EXPECT_TRUE(channel_->RemoveRecvStream(1));
 }
 
 // Test that we properly handle failures to add a stream.
 TEST_F(WebRtcVoiceEngineTestFake, AddStreamFail) {
   EXPECT_TRUE(SetupEngine());
   voe_.set_fail_create_channel(true);
-  EXPECT_FALSE(channel_->AddStream(2));
+  EXPECT_FALSE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
 }
 
 // Test that we properly clean up any streams that were added, even if
 // not explicitly removed.
 TEST_F(WebRtcVoiceEngineTestFake, StreamCleanup) {
   EXPECT_TRUE(SetupEngine());
-  EXPECT_TRUE(channel_->AddStream(1));
-  EXPECT_TRUE(channel_->AddStream(2));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
   EXPECT_EQ(3, voe_.GetNumChannels());  // default channel + 2 added
   delete channel_;
   channel_ = NULL;
@@ -966,8 +973,8 @@
 // Test that we can play a ringback tone properly in a multi-stream call.
 TEST_F(WebRtcVoiceEngineTestFake, PlayRingbackWithMultipleStreams) {
   EXPECT_TRUE(SetupEngine());
-  EXPECT_TRUE(channel_->AddStream(1));
-  EXPECT_TRUE(channel_->AddStream(2));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1)));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
   int channel_num = voe_.GetLastChannel();
   EXPECT_EQ(0, voe_.IsPlayingFileLocally(channel_num));
   // Check we fail if no ringback tone specified.
@@ -1045,7 +1052,8 @@
 
   // Add another stream and test on that.
   ++ssrc;
-  EXPECT_TRUE(channel_->AddStream(ssrc));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(
+      ssrc)));
   listener->Reset();
   voe_.TriggerCallbackOnError(voe_.GetLastChannel(),
                               VE_SATURATION_WARNING);
@@ -1066,8 +1074,8 @@
   codecs.push_back(kPcmuCodec);
   EXPECT_TRUE(channel_->SetSendCodecs(codecs));
   EXPECT_TRUE(channel_->SetSend(cricket::SEND_MICROPHONE));
-  EXPECT_TRUE(channel_->AddStream(2));
-  EXPECT_TRUE(channel_->AddStream(3));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(2)));
+  EXPECT_TRUE(channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(3)));
   EXPECT_TRUE(channel_->SetPlayout(true));
   voe_.set_playout_fail_channel(voe_.GetLastChannel() - 1);
   EXPECT_TRUE(channel_->SetPlayout(false));
@@ -1079,7 +1087,7 @@
 TEST_F(WebRtcVoiceEngineTestFake, RegisterVoiceProcessor) {
   EXPECT_TRUE(SetupEngine());
   uint32 ssrc = 0;
-  voe_.GetLocalSSRC(0,ssrc);
+  voe_.GetLocalSSRC(0, ssrc);
   cricket::FakeMediaProcessor vp_1;
   cricket::FakeMediaProcessor vp_2;
 
@@ -1126,7 +1134,7 @@
   EXPECT_FALSE(engine_.RegisterProcessor(0,
                                          &vp_1,
                                          cricket::MPD_RX));
-  channel_->AddStream(1);
+  channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(1));
   EXPECT_TRUE(engine_.RegisterProcessor(1,
                                         &vp_1,
                                         cricket::MPD_RX));
@@ -1136,7 +1144,7 @@
   EXPECT_FALSE(engine_.RegisterProcessor(1,
                                          &vp_1,
                                          cricket::MPD_TX));
-  channel_->RemoveStream(1);
+  channel_->RemoveRecvStream(1);
 }
 
 // Tests for the actual WebRtc VoE library.
diff --git a/talk/sound/pulseaudiosoundsystem.cc b/talk/sound/pulseaudiosoundsystem.cc
index 14aa119..4ccedc1 100644
--- a/talk/sound/pulseaudiosoundsystem.cc
+++ b/talk/sound/pulseaudiosoundsystem.cc
@@ -49,13 +49,6 @@
 // First PulseAudio protocol version that supports PA_STREAM_ADJUST_LATENCY.
 static const uint32_t kAdjustLatencyProtocolVersion = 13;
 
-// We define this flag if it's missing from our headers, because we want to be
-// able to compile against old headers but still use PA_STREAM_ADJUST_LATENCY
-// if run against a recent version of the library.
-#ifndef PA_STREAM_ADJUST_LATENCY
-#define PA_STREAM_ADJUST_LATENCY 0x2000U
-#endif
-
 // Lookup table from the cricket format enum in soundsysteminterface.h to
 // Pulse's enums.
 static const pa_sample_format_t kCricketFormatToPulseFormatTable[] = {