Update to libjingle 0.6.11

git-svn-id: http://libjingle.googlecode.com/svn/trunk@112 dd674b97-3498-5ee5-1854-bdd07cd0ff33
diff --git a/talk/app/webrtc/peerconnection_unittest.cc b/talk/app/webrtc/peerconnection_unittest.cc
index 91e65d7..1906641 100644
--- a/talk/app/webrtc/peerconnection_unittest.cc
+++ b/talk/app/webrtc/peerconnection_unittest.cc
@@ -332,13 +332,8 @@
 
 // This test sets up a call between two parties. Both parties send static frames
 // to each other. Once the test is finished the number of sent frames is
-// compared to the number of recieved frames.
-// TODO - This unittest is failing when SRTP is enabled in 
-// PeerConnection. This is due to race between the media and signaling.
-// When media arrives before signal, VoiceEngine asserts when it fails
-// to decode encrypted RTP packets. Once VoiceEngine is fixed, enable 
-// this test case.
-TEST_F(P2PTestConductor, DISABLED_LocalP2PTest) {
+// compared to the number of received frames.
+TEST_F(P2PTestConductor, LocalP2PTest) {
   EXPECT_TRUE(StartSession());
   const int kMaxWaitForActivationMs = 5000;
   EXPECT_TRUE_WAIT(ActivationNotPending(), kMaxWaitForActivationMs);
diff --git a/talk/app/webrtc/peerconnectionimpl_unittest.cc b/talk/app/webrtc/peerconnectionimpl_unittest.cc
index e690b2c..007aedb 100644
--- a/talk/app/webrtc/peerconnectionimpl_unittest.cc
+++ b/talk/app/webrtc/peerconnectionimpl_unittest.cc
@@ -195,7 +195,7 @@
   EXPECT_EQ(kStreamLabel1, pc_->remote_streams()->at(0)->label());
 }
 
-TEST_F(PeerConnectionImplTest, DISABLED_UpdateStream) {
+TEST_F(PeerConnectionImplTest, UpdateStream) {
   AddStream(kStreamLabel1);
   WAIT(PeerConnectionInterface::kNegotiating == observer_.state_, kTimeout);
   pc_->ProcessSignalingMessage(CreateAnswerMessage(observer_.last_message_));
diff --git a/talk/app/webrtc/peerconnectionsignaling.cc b/talk/app/webrtc/peerconnectionsignaling.cc
index 9accf05..a3bfb63 100644
--- a/talk/app/webrtc/peerconnectionsignaling.cc
+++ b/talk/app/webrtc/peerconnectionsignaling.cc
@@ -76,13 +76,19 @@
 }
 
 // Fills a MediaSessionOptions struct with the MediaTracks we want to sent given
-// the local MediaStreams.
+// the local MediaStreams. |local_streams| can be null if there are no tracks to
+// send.
 static void InitMediaSessionOptions(
     cricket::MediaSessionOptions* options,
     StreamCollectionInterface* local_streams) {
+  if (!VERIFY(options != NULL))
+      return;
   // In order to be able to receive video,
   // the has_video should always be true even if there are no video tracks.
   options->has_video = true;
+  if (local_streams == NULL)
+    return;
+
   for (size_t i = 0; i < local_streams->count(); ++i) {
     MediaStreamInterface* stream = local_streams->at(i);
 
@@ -163,13 +169,6 @@
       queued_local_streams_.clear();
       queued_local_streams_.push_back(local_streams);
 
-      // If we are still Initializing we need to wait before we can handle
-      // the offer. Queue it and handle it when the state changes.
-      if (state_ == kInitializing) {
-        received_pre_offer_ = true;
-        break;
-      }
-
       if (state_ == kWaitingForAnswer) {
         // Message received out of order or Glare occurred and the decision was
         // to use the incoming offer.
@@ -177,7 +176,26 @@
         // Be nice and handle this offer instead of the pending offer.
         signaling_thread_->Clear(this, MSG_SEND_QUEUED_OFFER);
       }
-      // Post a task to handle the answer.
+
+      // Provide the remote session description and the remote candidates from
+      // the parsed ROAP message to the |provider_|.
+      // The session description ownership is transferred from |roap_session_|
+      // to |provider_|.
+      provider_->SetRemoteDescription(roap_session_.ReleaseRemoteDescription(),
+                                      cricket::CA_OFFER);
+      provider_->SetRemoteCandidates(roap_session_.RemoteCandidates());
+      // Update the known remote MediaStreams.
+      UpdateRemoteStreams(provider_->remote_description());
+
+      // If we are still Initializing we need to wait until we have our local
+      // candidates before we can handle the offer. Queue it and handle it when
+      // the state changes.
+      if (state_ == kInitializing) {
+        received_pre_offer_ = true;
+        break;
+      }
+
+      // Post a task to generate the answer.
       signaling_thread_->Post(this, MSG_GENERATE_ANSWER);
       ChangeState(kWaitingForOK);
       break;
@@ -193,27 +211,29 @@
         return;
       }
 
-      // Hand over the remote session description and the remote candidates from
-      // the parsed ROAP message to the provider_.
-      // The session description ownership is transferred from roap_session_ to
-      // the provider_;
-      const cricket::SessionDescription* remote_desc =
-          provider_->SetRemoteSessionDescription(
-              roap_session_.ReleaseRemoteDescription(),
-              roap_session_.RemoteCandidates());
-      // Let the provider now that the negotiation is done and both local and
-      // remote session description is now valid.
-      provider_->NegotiationDone();
+      talk_base::scoped_ptr<cricket::SessionDescription> remote_desc(
+          roap_session_.ReleaseRemoteDescription());
 
-      // Update the list of known remote MediaStreams.
-      UpdateRemoteStreams(remote_desc);
       // Pop the first item of queued StreamCollections containing local
       // MediaStreams that just have been negotiated.
       scoped_refptr<StreamCollectionInterface> streams(
           queued_local_streams_.front());
       queued_local_streams_.pop_front();
       // Update the state of the local MediaStreams.
-      UpdateSendingLocalStreams(remote_desc, streams);
+      UpdateSendingLocalStreams(remote_desc.get(), streams);
+
+      // Hand the ownership of the local session description to |provider_|.
+      provider_->SetLocalDescription(local_desc_.release(), cricket::CA_OFFER);
+
+      // Provide the remote session description and the remote candidates from
+      // the parsed ROAP message to the |provider_|.
+      // The session description ownership is transferred from |roap_session_|
+      // to |provider_|.
+      provider_->SetRemoteDescription(remote_desc.release(),
+                                      cricket::CA_ANSWER);
+      provider_->SetRemoteCandidates(roap_session_.RemoteCandidates());
+      // Update the list of known remote MediaStreams.
+      UpdateRemoteStreams(provider_->remote_description());
 
       // Let the remote peer know we have received the answer.
       SignalNewPeerConnectionMessage(roap_session_.CreateOk());
@@ -228,14 +248,15 @@
     }
     case RoapSession::kOk: {
       if (state_ == kWaitingForOK) {
-        // Let the provider know the negotiation is done.
-        provider_->NegotiationDone();
-
         scoped_refptr<StreamCollectionInterface> streams(
             queued_local_streams_.front());
         queued_local_streams_.pop_front();
         // Update the state of the local streams.
-        UpdateSendingLocalStreams(local_desc_, streams);
+        UpdateSendingLocalStreams(local_desc_.get(), streams);
+
+        // Hand over the ownership of the local description to the provider.
+        provider_->SetLocalDescription(local_desc_.release(),
+                                       cricket::CA_ANSWER);
         ChangeState(kIdle);
         // Check if we have an updated offer waiting in the queue.
         if (!queued_local_streams_.empty())
@@ -328,10 +349,8 @@
   cricket::MediaSessionOptions options;
   InitMediaSessionOptions(&options, local_streams);
 
-  const cricket::SessionDescription* local_desc =
-      provider_->ProvideOffer(options);
-
-  SignalNewPeerConnectionMessage(roap_session_.CreateOffer(local_desc,
+  local_desc_.reset(provider_->CreateOffer(options));
+  SignalNewPeerConnectionMessage(roap_session_.CreateOffer(local_desc_.get(),
                                                            candidates_));
 }
 
@@ -339,19 +358,22 @@
   ChangeState(kShutingDown);
   signaling_thread_->Clear(this);  // Don't send queued offers or answers.
   queued_local_streams_.clear();
-  provider_->SetRemoteSessionDescription(NULL, cricket::Candidates());
-  provider_->NegotiationDone();
+  cricket::MediaSessionOptions options;
+  InitMediaSessionOptions(&options, NULL);
+
+  // Create new empty session descriptions without StreamParams.
+  // By applying these descriptions we don't send or receive any streams.
+  const SessionDescription* local_desc = provider_->CreateOffer(options);
+  SessionDescription* remote_desc = provider_->CreateAnswer(local_desc,
+                                                            options);
+
+  provider_->SetRemoteDescription(remote_desc, cricket::CA_OFFER);
+  provider_->SetLocalDescription(local_desc, cricket::CA_ANSWER);
+
   UpdateRemoteStreams(NULL);
 }
 
 void PeerConnectionSignaling::CreateAnswer_s() {
-  // Let the provider know about the remote offer.
-  // The provider takes ownership and return a pointer for us to use.
-  const cricket::SessionDescription* remote_desc =
-      provider_->SetRemoteSessionDescription(
-          roap_session_.ReleaseRemoteDescription(),
-          roap_session_.RemoteCandidates());
-
   scoped_refptr<StreamCollectionInterface> streams(
       queued_local_streams_.back());
   // Clean up all queued collections of local streams except the last one.
@@ -364,16 +386,14 @@
   cricket::MediaSessionOptions options;
   InitMediaSessionOptions(&options, streams);
   // Create an local session description based on this.
-  local_desc_ = provider_->ProvideAnswer(options);
-
-  if (!VerifyAnswer(local_desc_)) {
+  local_desc_.reset(provider_->CreateAnswer(provider_->remote_description(),
+                                            options));
+  if (!VerifyAnswer(local_desc_.get())) {
     SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(kRefused));
     return;
   }
 
-  UpdateRemoteStreams(remote_desc);
-  ChangeState(kWaitingForOK);
-  SignalNewPeerConnectionMessage(roap_session_.CreateAnswer(local_desc_,
+  SignalNewPeerConnectionMessage(roap_session_.CreateAnswer(local_desc_.get(),
                                                             candidates_));
 }
 
diff --git a/talk/app/webrtc/peerconnectionsignaling.h b/talk/app/webrtc/peerconnectionsignaling.h
index 14188a9..9e195ee 100644
--- a/talk/app/webrtc/peerconnectionsignaling.h
+++ b/talk/app/webrtc/peerconnectionsignaling.h
@@ -239,9 +239,9 @@
   // Currently known remote MediaStreams.
   talk_base::scoped_refptr<StreamCollection> remote_streams_;
 
-  // Weak reference to the local session description of the local MediaStreams
-  // being negotiated.
-  const cricket::SessionDescription* local_desc_;
+  // The local session description of the local MediaStreams that is being
+  // negotiated.
+  talk_base::scoped_ptr<const cricket::SessionDescription> local_desc_;
 
   // Local MediaStreams being negotiated.
   talk_base::scoped_refptr<StreamCollection> local_streams_;
diff --git a/talk/app/webrtc/peerconnectionsignaling_unittest.cc b/talk/app/webrtc/peerconnectionsignaling_unittest.cc
index 7b4d3a0..8980128 100644
--- a/talk/app/webrtc/peerconnectionsignaling_unittest.cc
+++ b/talk/app/webrtc/peerconnectionsignaling_unittest.cc
@@ -136,40 +136,64 @@
       cricket::ChannelManager* channel_manager)
       : update_session_description_counter_(0),
         session_description_factory_(
-          new cricket::MediaSessionDescriptionFactory(channel_manager)) {
+            new cricket::MediaSessionDescriptionFactory(channel_manager)),
+            offer_set_(false) {
   }
-  virtual const cricket::SessionDescription* ProvideOffer(
+  virtual cricket::SessionDescription* CreateOffer(
       const cricket::MediaSessionOptions& options) {
-    local_desc_.reset(session_description_factory_->CreateOffer(
-        options, local_desc_.get()));
+    return session_description_factory_->CreateOffer(options,
+                                                     local_desc_.get());
+  }
+  virtual cricket::SessionDescription* CreateAnswer(
+      const cricket::SessionDescription*offer,
+      const cricket::MediaSessionOptions& options) {
+    return session_description_factory_->CreateAnswer(offer, options,
+                                                      local_desc_.get());
+  }
+  virtual void SetLocalDescription(const cricket::SessionDescription* desc,
+                                   cricket::ContentAction type) {
+    local_desc_.reset(desc);
+    UpdateNegotiationState(type);
+  }
+  virtual void SetRemoteDescription(
+      cricket::SessionDescription* remote_offer,
+      cricket::ContentAction type) {
+    remote_desc_.reset(remote_offer);
+    UpdateNegotiationState(type);
+  }
+  virtual const cricket::SessionDescription* local_description() const {
     return local_desc_.get();
   }
-
-  virtual const cricket::SessionDescription* SetRemoteSessionDescription(
-      const cricket::SessionDescription* remote_offer,
-      const cricket::Candidates& remote_candidates) {
-    remote_desc_.reset(remote_offer);
+  virtual const cricket::SessionDescription* remote_description() const {
     return remote_desc_.get();
   }
-
-  virtual const cricket::SessionDescription* ProvideAnswer(
-      const cricket::MediaSessionOptions& options) {
-    local_desc_.reset(session_description_factory_->CreateAnswer(
-        remote_desc_.get(), options, local_desc_.get()));
-    return local_desc_.get();
+  virtual void SetRemoteCandidates(
+      const std::vector<cricket::Candidate>& remote_candidates) {
   }
 
-  virtual void NegotiationDone() {
-    ++update_session_description_counter_;
-  }
-
+  // |update_session_description_counter_| is the number of successful
+  // negotiations / re-negotiations.
   size_t update_session_description_counter_;
 
  protected:
+  void UpdateNegotiationState(cricket::ContentAction type) {
+    if (type  == cricket::CA_ANSWER && offer_set_) {
+      // We have received and offer and now we receive an answer.
+      // Negotiation is done. Update the counter to indicate this.
+      ++update_session_description_counter_;
+      offer_set_ = false;
+    } else {
+      // Received an offer when expecting an answer.
+      EXPECT_FALSE(offer_set_);
+      offer_set_ = true;
+    }
+  }
+
   talk_base::scoped_ptr<cricket::MediaSessionDescriptionFactory>
       session_description_factory_;
   talk_base::scoped_ptr<const cricket::SessionDescription> local_desc_;
   talk_base::scoped_ptr<const cricket::SessionDescription> remote_desc_;
+  bool offer_set_;
 };
 
 // PeerConnectionSignalingTest create two PeerConnectionSignaling instances
diff --git a/talk/app/webrtc/roapmessages.h b/talk/app/webrtc/roapmessages.h
index fe99e1f..7b18010 100644
--- a/talk/app/webrtc/roapmessages.h
+++ b/talk/app/webrtc/roapmessages.h
@@ -103,7 +103,7 @@
   // Get remote SessionDescription if the session description has been parsed
   // and the ownership is transferred to the caller.
   // NULL otherwise.
-  const cricket::SessionDescription* ReleaseSessionDescription() {
+  cricket::SessionDescription* ReleaseSessionDescription() {
     return parsed_desc_.release();
   }
   const std::vector<cricket::Candidate>& candidates() const {
@@ -142,7 +142,7 @@
   // Get remote SessionDescription if the session description has been parsed
   // and the ownership is transferred to the caller.
   // NULL otherwise.
-  const cricket::SessionDescription* ReleaseSessionDescription() {
+  cricket::SessionDescription* ReleaseSessionDescription() {
     return parsed_desc_.release();
   }
   const std::vector<cricket::Candidate>&  candidates() { return candidates_; }
diff --git a/talk/app/webrtc/roapsession.cc b/talk/app/webrtc/roapsession.cc
index 6d76b0c..014539f 100644
--- a/talk/app/webrtc/roapsession.cc
+++ b/talk/app/webrtc/roapsession.cc
@@ -271,7 +271,7 @@
   return kError;
 }
 
-const SessionDescription* RoapSession::ReleaseRemoteDescription() {
+SessionDescription* RoapSession::ReleaseRemoteDescription() {
   return remote_desc_.release();
 }
 
diff --git a/talk/app/webrtc/roapsession.h b/talk/app/webrtc/roapsession.h
index c5712a8..7a0c27f 100644
--- a/talk/app/webrtc/roapsession.h
+++ b/talk/app/webrtc/roapsession.h
@@ -89,7 +89,7 @@
   ParseResult Parse(const std::string& msg);
   RoapErrorCode RemoteError();
   // Get remote SessionDescription. The ownership is transferred to the caller.
-  const SessionDescription* ReleaseRemoteDescription();
+  SessionDescription* ReleaseRemoteDescription();
   const std::vector<Candidate>& RemoteCandidates();
 
  private:
@@ -110,7 +110,7 @@
   std::string session_token_;
   std::string response_token_;
 
-  talk_base::scoped_ptr<const SessionDescription> remote_desc_;
+  talk_base::scoped_ptr<SessionDescription> remote_desc_;
   std::vector<Candidate> remote_candidates_;
 
   RoapErrorCode remote_error_;
diff --git a/talk/app/webrtc/sessiondescriptionprovider.h b/talk/app/webrtc/sessiondescriptionprovider.h
index f0a7645..7142cdd 100644
--- a/talk/app/webrtc/sessiondescriptionprovider.h
+++ b/talk/app/webrtc/sessiondescriptionprovider.h
@@ -36,18 +36,32 @@
 
 class SessionDescriptionProvider {
  public:
-  virtual const cricket::SessionDescription* ProvideOffer(
+  // Creates a new SessionDescription based on |options|.
+  // It does not affect the currently set local or remote SessionDescription.
+  // Caller is owner of the created SessionDescription.
+  virtual cricket::SessionDescription* CreateOffer(
       const cricket::MediaSessionOptions& options) = 0;
 
-  // Transfer ownership of remote_offer.
-  virtual const cricket::SessionDescription* SetRemoteSessionDescription(
-      const cricket::SessionDescription* remote_offer,
+  // Creates a new SessionDescription based on |offer| and |options|.
+  // It does not affect the currently set local or remote SessionDescription.
+  // Caller is owner of the created SessionDescription.
+  virtual cricket::SessionDescription* CreateAnswer(
+      const cricket::SessionDescription* offer,
+      const cricket::MediaSessionOptions& options) = 0;
+
+  // Transfers ownership of session description.
+  virtual void SetLocalDescription(const cricket::SessionDescription* desc,
+                                   cricket::ContentAction type) = 0;
+  virtual void SetRemoteDescription(cricket::SessionDescription* desc,
+                                    cricket::ContentAction type) = 0;
+  // Sets all the currently known remote candidates.
+  virtual void SetRemoteCandidates(
       const std::vector<cricket::Candidate>& remote_candidates) = 0;
 
-  virtual const cricket::SessionDescription* ProvideAnswer(
-      const cricket::MediaSessionOptions& options) = 0;
-
-  virtual void NegotiationDone() = 0;
+  // Current local session description.
+  virtual const cricket::SessionDescription* local_description() const = 0;
+  // Current remote session description.
+  virtual const cricket::SessionDescription* remote_description() const = 0;
 
  protected:
   virtual ~SessionDescriptionProvider() {}
diff --git a/talk/app/webrtc/webrtcsdp.cc b/talk/app/webrtc/webrtcsdp.cc
index 2fcb959..6986a8b 100644
--- a/talk/app/webrtc/webrtcsdp.cc
+++ b/talk/app/webrtc/webrtcsdp.cc
@@ -110,6 +110,13 @@
 static const char kMediaTypeVideo[] = "video";
 static const char kMediaTypeAudio[] = "audio";
 
+// Default Video resolution.
+// TODO: Implement negotiation of video resolution.
+static const int kDefaultVideoWidth = 640;
+static const int kDefaultVideoHeight = 480;
+static const int kDefaultVideoFrameRate = 30;
+static const int kDefaultVideoPreference = 0;
+
 static void BuildMediaDescription(const cricket::ContentInfo& content_info,
                                   const std::vector<Candidate>& candidates,
                                   const MediaType media_type,
@@ -689,8 +696,12 @@
       if (media_type == cricket::MEDIA_TYPE_VIDEO) {
         VideoContentDescription* video_desc =
             static_cast<VideoContentDescription*>(media_desc);
+        // TODO: We will send resolution in SDP. For now, use VGA.
         video_desc->AddCodec(cricket::VideoCodec(payload_type, encoding_name,
-                                                 0, 0, 0, 0));
+                                                 kDefaultVideoWidth,
+                                                 kDefaultVideoHeight,
+                                                 kDefaultVideoFrameRate,
+                                                 kDefaultVideoPreference));
       } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
         AudioContentDescription* audio_desc =
             static_cast<AudioContentDescription*>(media_desc);
diff --git a/talk/app/webrtc/webrtcsdp_unittest.cc b/talk/app/webrtc/webrtcsdp_unittest.cc
index 2fd1e6b..7e34d2c 100644
--- a/talk/app/webrtc/webrtcsdp_unittest.cc
+++ b/talk/app/webrtc/webrtcsdp_unittest.cc
@@ -154,7 +154,7 @@
     video->AddStream(video_stream3);
     video->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_80",
         "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", ""));
-    video->AddCodec(VideoCodec(120, "VP8", 352, 288, 30, 0));
+    video->AddCodec(VideoCodec(120, "VP8", 640, 480, 30, 0));
     desc_.AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP,
                      video.release());
 
@@ -224,6 +224,9 @@
       const VideoCodec c1 = vcd1->codecs().at(i);
       const VideoCodec c2 = vcd2->codecs().at(i);
       EXPECT_TRUE(c1.Matches(c2));
+      EXPECT_EQ(c1.width, c2.width);
+      EXPECT_EQ(c1.height, c2.height);
+      EXPECT_EQ(c1.framerate, c2.framerate);
     }
 
     // streams
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index 7e8a0a1..f84af03 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -57,6 +57,60 @@
 static const char kRtpVideoChannelStr[] = "video_rtp";
 static const char kRtcpVideoChannelStr[] = "video_rtcp";
 
+// Constants for setting the default encoder size.
+// TODO: Implement proper negotiation of video resolution.
+static const int kDefaultVideoCodecId = 100;
+static const int kDefaultVideoCodecFramerate = 30;
+static const char kDefaultVideoCodecName[] = "VP8";
+static const int kDefaultVideoCodecWidth = 640;
+static const int kDefaultVideoCodecHeight = 480;
+
+// MediaSessionDescriptionFactory always creates one StreamParams for
+// legacy reasons even if options don't contain any streams.
+// We need to remove it in that case since here options contains all streams
+// we want to send.
+// TODO: This is placed in the wrong file. Can we solve this problem by
+// adding a method MediaSessionDescriptionFactory::set_add_legacy_streams(false)
+// to prevent it from creating legacy streams.
+static void RemoveLegacyStreams(const cricket::MediaSessionOptions& options,
+                                cricket::SessionDescription* description) {
+  bool found_audio_stream = false;
+  bool found_video_stream = false;
+  for (cricket::MediaSessionOptions::Streams::const_iterator it =
+      options.streams.begin();
+       it != options.streams.end(); ++it) {
+    if (it->type == cricket::MEDIA_TYPE_AUDIO)
+      found_audio_stream = true;
+
+    if (it->type == cricket::MEDIA_TYPE_VIDEO)
+      found_video_stream = true;
+  }
+  if (found_audio_stream == false) {
+    const cricket::ContentInfo* audio_info =
+        cricket::GetFirstAudioContent(description);
+      if (audio_info) {
+        const cricket::MediaContentDescription* const_media_desc =
+            static_cast<const cricket::MediaContentDescription*>(
+            audio_info->description);
+        cricket::MediaContentDescription* media_desc =
+            const_cast<cricket::MediaContentDescription*> (const_media_desc);
+        media_desc->mutable_streams().clear();
+      }
+  }
+  if (found_video_stream == false) {
+    const cricket::ContentInfo* video_info =
+        cricket::GetFirstVideoContent(description);
+      if (video_info) {
+        const cricket::MediaContentDescription* const_media_desc =
+            static_cast<const cricket::MediaContentDescription*>(
+                video_info->description);
+        cricket::MediaContentDescription* media_desc =
+               const_cast<cricket::MediaContentDescription*> (const_media_desc);
+        media_desc->mutable_streams().clear();
+      }
+  }
+}
+
 WebRtcSession::WebRtcSession(cricket::ChannelManager* channel_manager,
                              talk_base::Thread* signaling_thread,
                              talk_base::Thread* worker_thread,
@@ -76,6 +130,13 @@
 bool WebRtcSession::Initialize() {
   // By default SRTP-SDES is enabled in WebRtc.
   set_secure_policy(cricket::SEC_REQUIRED);
+
+  const cricket::VideoCodec default_codec(kDefaultVideoCodecId,
+      kDefaultVideoCodecName, kDefaultVideoCodecWidth, kDefaultVideoCodecHeight,
+      kDefaultVideoCodecFramerate, 0);
+  channel_manager_->SetDefaultVideoEncoderConfig(
+      cricket::VideoEncoderConfig(default_codec));
+
   return CreateChannels();
 }
 
@@ -116,52 +177,13 @@
   return true;
 }
 
-void WebRtcSession::SetRemoteCandidates(
-    const cricket::Candidates& candidates) {
-  // First partition the candidates for the proxies. During creation of channels
-  // we created CN_AUDIO (audio) and CN_VIDEO (video) proxies.
-  cricket::Candidates audio_candidates;
-  cricket::Candidates video_candidates;
-  for (cricket::Candidates::const_iterator citer = candidates.begin();
-       citer != candidates.end(); ++citer) {
-    if (((*citer).name().compare(kRtpVideoChannelStr) == 0) ||
-        ((*citer).name().compare(kRtcpVideoChannelStr)) == 0) {
-      // Candidate names for video rtp and rtcp channel
-      video_candidates.push_back(*citer);
-    } else {
-      // Candidates for audio rtp and rtcp channel
-      // Channel name will be "rtp" and "rtcp"
-      audio_candidates.push_back(*citer);
-    }
-  }
+// Enabling voice and video channel.
+void WebRtcSession::EnableChannels() {
+  if (!voice_channel_->enabled())
+    voice_channel_->Enable(true);
 
-  if (!audio_candidates.empty()) {
-    cricket::TransportProxy* audio_proxy = GetTransportProxy(cricket::CN_AUDIO);
-    if (audio_proxy) {
-      // CompleteNegotiation will set actual impl's in Proxy.
-      if (!audio_proxy->negotiated())
-        audio_proxy->CompleteNegotiation();
-      // TODO - Add a interface to TransportProxy to accept
-      // remote candidate list.
-      audio_proxy->impl()->OnRemoteCandidates(audio_candidates);
-    } else {
-      LOG(LS_INFO) << "No audio TransportProxy exists";
-    }
-  }
-
-  if (!video_candidates.empty()) {
-    cricket::TransportProxy* video_proxy = GetTransportProxy(cricket::CN_VIDEO);
-    if (video_proxy) {
-      // CompleteNegotiation will set actual impl's in Proxy.
-      if (!video_proxy->negotiated())
-        video_proxy->CompleteNegotiation();
-      // TODO - Add a interface to TransportProxy to accept
-      // remote candidate list.
-      video_proxy->impl()->OnRemoteCandidates(video_candidates);
-    } else {
-      LOG(LS_INFO) << "No video TransportProxy exists";
-    }
-  }
+  if (!video_channel_->enabled())
+    video_channel_->Enable(true);
 }
 
 void WebRtcSession::OnTransportRequestSignaling(
@@ -269,19 +291,34 @@
                                      cricket::VideoRenderer* renderer) {
   ASSERT(signaling_thread()->IsCurrent());
   // TODO: Fix SetLocalRenderer.
-  //video_channel_->SetLocalRenderer(0, renderer);
+  // video_channel_->SetLocalRenderer(0, renderer);
 }
 
 void WebRtcSession::SetRemoteRenderer(const std::string& name,
                                       cricket::VideoRenderer* renderer) {
   ASSERT(signaling_thread()->IsCurrent());
 
-  // TODO: Only the ssrc = 0 is supported at the moment.
-  // Only one channel.
-  video_channel_->SetRenderer(0, renderer);
+  const cricket::ContentInfo* video_info =
+      cricket::GetFirstVideoContent(remote_description());
+  if (!video_info) {
+    LOG(LS_ERROR) << "Video not received in this call";
+  }
+
+  const cricket::MediaContentDescription* video_content =
+      static_cast<const cricket::MediaContentDescription*>(
+          video_info->description);
+  cricket::StreamParams stream;
+  if (cricket::GetStreamByNickAndName(video_content->streams(), "", name,
+                                      &stream)) {
+    video_channel_->SetRenderer(stream.first_ssrc(), renderer);
+  } else {
+    // Allow that |stream| does not exist if renderer is null but assert
+    // otherwise.
+    VERIFY(renderer == NULL);
+  }
 }
 
-const cricket::SessionDescription* WebRtcSession::ProvideOffer(
+cricket::SessionDescription* WebRtcSession::CreateOffer(
     const cricket::MediaSessionOptions& options) {
   if (!options.has_video) {
     LOG(LS_WARNING) << "To receive video, has_video flag must be set to true";
@@ -290,65 +327,119 @@
 
   cricket::SessionDescription* offer(
       session_desc_factory_.CreateOffer(options, local_description()));
-  set_local_description(offer);
+
+  // MediaSessionDescriptionFactory always creates one StreamParams for
+  // legacy reasons even if options don't contain any streams.
+  // We need to remove it in that case since here options contains all streams
+  // we want to send.
+  RemoveLegacyStreams(options, offer);
   return offer;
 }
 
-const cricket::SessionDescription* WebRtcSession::SetRemoteSessionDescription(
-    const cricket::SessionDescription* remote_offer,
-    const std::vector<cricket::Candidate>& remote_candidates) {
-  set_remote_description(
-      const_cast<cricket::SessionDescription*>(remote_offer));
-  SetRemoteCandidates(remote_candidates);
-  return remote_offer;
-}
-
-const cricket::SessionDescription* WebRtcSession::ProvideAnswer(
+cricket::SessionDescription* WebRtcSession::CreateAnswer(
+    const cricket::SessionDescription* offer,
     const cricket::MediaSessionOptions& options) {
   cricket::SessionDescription* answer(
-      session_desc_factory_.CreateAnswer(remote_description(), options,
+      session_desc_factory_.CreateAnswer(offer, options,
                                          local_description()));
-  set_local_description(answer);
+  // MediaSessionDescriptionFactory always creates one StreamParams for
+  // legacy reasons even if options don't contain any streams.
+  // We need to remove it in that case since here options contains all streams
+  // we want to send.
+  RemoveLegacyStreams(options, answer);
   return answer;
 }
 
-void WebRtcSession::NegotiationDone() {
-  // SetState of session is called after session receives both local and
-  // remote descriptions. State transition will happen only when session
-  // is in INIT state.
-  if (state() == STATE_INIT) {
+void WebRtcSession::SetLocalDescription(const cricket::SessionDescription* desc,
+                                        cricket::ContentAction type) {
+  if (!VERIFY((type == cricket::CA_ANSWER &&
+               state() == STATE_RECEIVEDINITIATE) ||
+              (type == cricket::CA_OFFER &&
+               (state() != STATE_RECEIVEDINITIATE &&
+                state() != STATE_SENTINITIATE)))) {
+    LOG(LS_ERROR) << "SetLocalDescription called with action in wrong state, "
+                  << "action: " << type << " state: " << state();
+    return;
+  }
+
+  set_local_description(desc);
+  if (type == cricket::CA_ANSWER) {
+    EnableChannels();
+    SetState(STATE_SENTACCEPT);
+  } else {
     SetState(STATE_SENTINITIATE);
+  }
+}
+
+void WebRtcSession::SetRemoteDescription(cricket::SessionDescription* desc,
+                                         cricket::ContentAction type) {
+  if (!VERIFY((type == cricket::CA_ANSWER &&
+               state() == STATE_SENTINITIATE) ||
+              (type == cricket::CA_OFFER &&
+               (state() != STATE_RECEIVEDINITIATE &&
+                state() != STATE_SENTINITIATE)))) {
+    LOG(LS_ERROR) << "SetRemoteDescription called with action in wrong state, "
+                  << "action: " << type << " state: " << state();
+    return;
+  }
+  set_remote_description(desc);
+
+  if (type  == cricket::CA_ANSWER) {
+    EnableChannels();
     SetState(STATE_RECEIVEDACCEPT);
+  } else {
+    SetState(STATE_RECEIVEDINITIATE);
+  }
+}
 
-    // Enabling voice and video channel.
-    voice_channel_->Enable(true);
-    video_channel_->Enable(true);
+void WebRtcSession::SetRemoteCandidates(
+    const cricket::Candidates& candidates) {
+  // First partition the candidates for the proxies. During creation of channels
+  // we created CN_AUDIO (audio) and CN_VIDEO (video) proxies.
+  cricket::Candidates audio_candidates;
+  cricket::Candidates video_candidates;
+  for (cricket::Candidates::const_iterator citer = candidates.begin();
+       citer != candidates.end(); ++citer) {
+    if (((*citer).name().compare(kRtpVideoChannelStr) == 0) ||
+        ((*citer).name().compare(kRtcpVideoChannelStr)) == 0) {
+      // Candidate names for video rtp and rtcp channel
+      video_candidates.push_back(*citer);
+    } else {
+      // Candidates for audio rtp and rtcp channel
+      // Channel name will be "rtp" and "rtcp"
+      audio_candidates.push_back(*citer);
+    }
   }
 
-  const cricket::ContentInfo* audio_info =
-      cricket::GetFirstAudioContent(local_description());
-  if (audio_info) {
-    const cricket::MediaContentDescription* audio_content =
-        static_cast<const cricket::MediaContentDescription*>(
-            audio_info->description);
-    // Since channels are currently not supporting multiple send streams,
-    // we can remove stream from a session by muting it.
-    // TODO - Change needed when multiple send streams support
-    // is available.
-    voice_channel_->Mute(audio_content->streams().size() == 0);
+  // TODO: Justins comment:This is bad encapsulation, suggest we add a
+  // helper to BaseSession to allow us to
+  // pass in candidates without touching the transport proxies.
+  if (!audio_candidates.empty()) {
+    cricket::TransportProxy* audio_proxy = GetTransportProxy(cricket::CN_AUDIO);
+    if (audio_proxy) {
+      // CompleteNegotiation will set actual impl's in Proxy.
+      if (!audio_proxy->negotiated())
+        audio_proxy->CompleteNegotiation();
+      // TODO - Add a interface to TransportProxy to accept
+      // remote candidate list.
+      audio_proxy->impl()->OnRemoteCandidates(audio_candidates);
+    } else {
+      LOG(LS_INFO) << "No audio TransportProxy exists";
+    }
   }
 
-  const cricket::ContentInfo* video_info =
-      cricket::GetFirstVideoContent(local_description());
-  if (video_info) {
-    const cricket::MediaContentDescription* video_content =
-        static_cast<const cricket::MediaContentDescription*>(
-            video_info->description);
-    // Since channels are currently not supporting multiple send streams,
-    // we can remove stream from a session by muting it.
-    // TODO - Change needed when multiple send streams support
-    // is available.
-    video_channel_->Mute(video_content->streams().size() == 0);
+  if (!video_candidates.empty()) {
+    cricket::TransportProxy* video_proxy = GetTransportProxy(cricket::CN_VIDEO);
+    if (video_proxy) {
+      // CompleteNegotiation will set actual impl's in Proxy.
+      if (!video_proxy->negotiated())
+        video_proxy->CompleteNegotiation();
+      // TODO - Add a interface to TransportProxy to accept
+      // remote candidate list.
+      video_proxy->impl()->OnRemoteCandidates(video_candidates);
+    } else {
+      LOG(LS_INFO) << "No video TransportProxy exists";
+    }
   }
 }
 
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index f18783b..3206194 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -83,16 +83,24 @@
   // TODO - It may be necessary to supply error code as well.
   sigslot::signal0<> SignalError;
 
- protected:
   // Implements SessionDescriptionProvider
-  virtual const cricket::SessionDescription* ProvideOffer(
+  virtual cricket::SessionDescription* CreateOffer(
       const cricket::MediaSessionOptions& options);
-  virtual const cricket::SessionDescription* SetRemoteSessionDescription(
-      const cricket::SessionDescription* remote_offer,
+  virtual cricket::SessionDescription* CreateAnswer(
+      const cricket::SessionDescription* offer,
+      const cricket::MediaSessionOptions& options);
+  virtual void SetLocalDescription(const cricket::SessionDescription* desc,
+                                   cricket::ContentAction type);
+  virtual void SetRemoteDescription(cricket::SessionDescription* desc,
+                                    cricket::ContentAction type);
+  virtual void SetRemoteCandidates(
       const std::vector<cricket::Candidate>& remote_candidates);
-  virtual const cricket::SessionDescription* ProvideAnswer(
-      const cricket::MediaSessionOptions& options);
-  virtual void NegotiationDone();
+  virtual const cricket::SessionDescription* local_description() const {
+    return cricket::BaseSession::local_description();
+  }
+  virtual const cricket::SessionDescription* remote_description() const {
+    return cricket::BaseSession::remote_description();
+  }
 
  private:
   // Implements MediaProviderInterface.
@@ -115,12 +123,12 @@
 
   // Creates channels for voice and video.
   bool CreateChannels();
+  void EnableChannels();
   virtual void OnMessage(talk_base::Message* msg);
   void InsertTransportCandidates(const cricket::Candidates& candidates);
   void Terminate();
   // Get candidate from the local candidates list by the name.
   bool CheckCandidate(const std::string& name);
-  void SetRemoteCandidates(const cricket::Candidates& candidates);
 
   talk_base::scoped_ptr<cricket::VoiceChannel> voice_channel_;
   talk_base::scoped_ptr<cricket::VideoChannel> video_channel_;
diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc
index e28df9f..313a35c 100644
--- a/talk/app/webrtc/webrtcsession_unittest.cc
+++ b/talk/app/webrtc/webrtcsession_unittest.cc
@@ -28,10 +28,21 @@
 #include "talk/app/webrtc/webrtcsession.h"
 #include "talk/base/thread.h"
 #include "talk/base/gunit.h"
+#include "talk/session/phone/fakedevicemanager.h"
+#include "talk/session/phone/fakemediaengine.h"
 #include "talk/p2p/client/fakeportallocator.h"
 #include "talk/session/phone/channelmanager.h"
 #include "talk/session/phone/mediasession.h"
 
+static const char kStream1[] = "stream1";
+static const char kVideoTrack1[] = "video1";
+static const char kAudioTrack1[] = "audio1";
+
+static const char kStream2[] = "stream2";
+static const char kVideoTrack2[] = "video2";
+static const char kAudioTrack2[] = "audio2";
+
+
 class MockWebRtcSessionObserver : public webrtc::WebRtcSessionObserver {
  public:
   virtual void OnCandidatesReady(
@@ -54,49 +65,40 @@
   }
   virtual ~WebRtcSessionForTest() {}
 
-  using webrtc::WebRtcSession::ProvideOffer;
-  using webrtc::WebRtcSession::SetRemoteSessionDescription;
-  using webrtc::WebRtcSession::ProvideAnswer;
-  using webrtc::WebRtcSession::NegotiationDone;
+  using webrtc::WebRtcSession::CreateOffer;
+  using webrtc::WebRtcSession::CreateAnswer;
+  using webrtc::WebRtcSession::SetLocalDescription;
+  using webrtc::WebRtcSession::SetRemoteDescription;
+  using webrtc::WebRtcSession::SetRemoteCandidates;
 };
 
 class WebRtcSessionTest : public testing::Test {
  protected:
   virtual void SetUp() {
-    channel_manager_.reset(
-        new cricket::ChannelManager(talk_base::Thread::Current()));
-    port_allocator_.reset(
-        new cricket::FakePortAllocator(talk_base::Thread::Current(), NULL));
+    cricket::FakeMediaEngine* media_engine(new cricket::FakeMediaEngine());
+    cricket::FakeDeviceManager* device_manager(
+        new cricket::FakeDeviceManager());
+
+    channel_manager_.reset(new cricket::ChannelManager(
+        media_engine, device_manager, talk_base::Thread::Current()));
+    port_allocator_.reset(new cricket::FakePortAllocator(
+        talk_base::Thread::Current(), NULL));
     desc_factory_.reset(
         new cricket::MediaSessionDescriptionFactory(channel_manager_.get()));
-  }
 
-  bool InitializeSession() {
-    return session_.get()->Initialize();
-  }
-
-  bool CheckChannels() {
-    return (session_->voice_channel() != NULL &&
-            session_->video_channel() != NULL);
-  }
-
-  void CheckTransportChannels() {
-    EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, "rtp") != NULL);
-    EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, "rtcp") != NULL);
-    EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, "video_rtp") != NULL);
-    EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, "video_rtcp") != NULL);
-  }
-
-  void Init() {
     ASSERT_TRUE(channel_manager_.get() != NULL);
     ASSERT_TRUE(session_.get() == NULL);
-    EXPECT_TRUE(channel_manager_.get()->Init());
+    ASSERT_TRUE(channel_manager_.get()->Init());
+
     session_.reset(new WebRtcSessionForTest(
         channel_manager_.get(), talk_base::Thread::Current(),
         talk_base::Thread::Current(), port_allocator_.get()));
     session_->RegisterObserver(&observer_);
-    desc_provider_ = session_.get();
-    EXPECT_TRUE(InitializeSession());
+
+    EXPECT_TRUE(session_->Initialize());
+
+    video_channel_ = media_engine->GetVideoChannel(0);
+    voice_channel_ = media_engine->GetVoiceChannel(0);
   }
 
   void PopulateFakeCandidates() {
@@ -114,11 +116,62 @@
     }
   }
 
-  void CreateOffer() {
+  // Create a session description based on options. Used for testing but don't
+  // test WebRtcSession.
+  cricket::SessionDescription* CreateTestOffer(
+      const cricket::MediaSessionOptions& options) {
+    desc_factory_->set_secure(cricket::SEC_REQUIRED);
+    return desc_factory_->CreateOffer(options, NULL);
+  }
+
+  // Create a session description based on options. Used for testing but don't
+  // test WebRtcSession.
+  cricket::SessionDescription* CreateTestAnswer(
+      const cricket::SessionDescription* offer,
+      const cricket::MediaSessionOptions& options) {
+    desc_factory_->set_secure(cricket::SEC_REQUIRED);
+    return desc_factory_->CreateAnswer(offer, options, NULL);
+  }
+
+  cricket::MediaSessionOptions OptionsWithStream1() {
+    cricket::MediaSessionOptions options;
+    options.AddStream(cricket::MEDIA_TYPE_VIDEO, kVideoTrack1, kStream1);
+    options.AddStream(cricket::MEDIA_TYPE_AUDIO, kAudioTrack1, kStream1);
+    return options;
+  }
+
+  cricket::MediaSessionOptions OptionsWithStream2() {
+    cricket::MediaSessionOptions options;
+    options.AddStream(cricket::MEDIA_TYPE_VIDEO, kVideoTrack2, kStream2);
+    options.AddStream(cricket::MEDIA_TYPE_AUDIO, kAudioTrack2, kStream2);
+    return options;
+  }
+
+  cricket::MediaSessionOptions OptionsWithStream1And2() {
+    cricket::MediaSessionOptions options;
+    options.AddStream(cricket::MEDIA_TYPE_VIDEO, kVideoTrack1, kStream1);
+    options.AddStream(cricket::MEDIA_TYPE_AUDIO, kAudioTrack1, kStream1);
+    options.AddStream(cricket::MEDIA_TYPE_VIDEO, kVideoTrack2, kStream2);
+    options.AddStream(cricket::MEDIA_TYPE_AUDIO, kAudioTrack2, kStream2);
+    return options;
+  }
+
+  cricket::MediaSessionOptions OptionsReceiveOnly() {
     cricket::MediaSessionOptions options;
     options.has_video = true;
-    session_->ProvideOffer(options);
-    ASSERT_TRUE(session_->local_description() != NULL);
+    return options;
+  }
+
+  bool ChannelsExist() {
+    return (session_->voice_channel() != NULL &&
+            session_->video_channel() != NULL);
+  }
+
+  void CheckTransportChannels() {
+    EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, "rtp") != NULL);
+    EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, "rtcp") != NULL);
+    EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, "video_rtp") != NULL);
+    EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, "video_rtcp") != NULL);
   }
 
   void VerifyCryptoParams(const cricket::SessionDescription* sdp,
@@ -186,12 +239,8 @@
         desc_factory_->CreateOffer(options, NULL);
     ASSERT_TRUE(offer != NULL);
     VerifyNoCryptoParams(offer);
-    // Change security parameter to SEC_REQUIRED.
-    desc_factory_->set_secure(cricket::SEC_REQUIRED);
-    PopulateFakeCandidates();
-    session_->SetRemoteSessionDescription(offer, candidates_);
     const cricket::SessionDescription* answer =
-        session_->ProvideAnswer(options);
+        session_->CreateAnswer(offer, options);
     // Answer should be NULL as no crypto params in offer.
     ASSERT_TRUE(answer == NULL);
   }
@@ -204,63 +253,130 @@
         desc_factory_->CreateOffer(options, NULL);
     ASSERT_TRUE(offer != NULL);
     VerifyCryptoParams(offer, true);
-    PopulateFakeCandidates();
-    session_->SetRemoteSessionDescription(offer, candidates_);
     const cricket::SessionDescription* answer =
-        session_->ProvideAnswer(options);
+        session_->CreateAnswer(offer, options);
     ASSERT_TRUE(answer != NULL);
     VerifyCryptoParams(answer, false);
   }
 
   talk_base::scoped_ptr<cricket::PortAllocator> port_allocator_;
-  webrtc::SessionDescriptionProvider* desc_provider_;
   talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
   talk_base::scoped_ptr<cricket::MediaSessionDescriptionFactory> desc_factory_;
   talk_base::scoped_ptr<WebRtcSessionForTest> session_;
   MockWebRtcSessionObserver observer_;
   std::vector<cricket::Candidate> candidates_;
+  cricket::FakeVideoMediaChannel* video_channel_;
+  cricket::FakeVoiceMediaChannel* voice_channel_;
 };
 
 TEST_F(WebRtcSessionTest, TestInitialize) {
-  WebRtcSessionTest::Init();
-  EXPECT_TRUE(CheckChannels());
+  EXPECT_TRUE(ChannelsExist());
   CheckTransportChannels();
   talk_base::Thread::Current()->ProcessMessages(1000);
   EXPECT_EQ(4u, observer_.candidates_.size());
 }
 
-// TODO - Adding test cases for session.
-TEST_F(WebRtcSessionTest, DISABLE_TestOfferAnswer) {
-  WebRtcSessionTest::Init();
-  EXPECT_TRUE(CheckChannels());
-  CheckTransportChannels();
-  talk_base::Thread::Current()->ProcessMessages(1);
+// Test creating offers and receive answers and make sure the
+// media engine creates the expected send and receive streams.
+TEST_F(WebRtcSessionTest, TestCreateOfferReceiveAnswer) {
+  cricket::MediaSessionOptions options = OptionsWithStream1();
+  cricket::SessionDescription* offer = session_->CreateOffer(options);
+
+  cricket::MediaSessionOptions options2 = OptionsWithStream2();
+  cricket::SessionDescription* answer =  CreateTestAnswer(offer, options2);
+
+  session_->SetLocalDescription(offer, cricket::CA_OFFER);
+  session_->SetRemoteDescription(answer, cricket::CA_ANSWER);
+
+  ASSERT_EQ(1u, video_channel_->recv_streams().size());
+  cricket::StreamParams recv_video_stream =
+      video_channel_->recv_streams()[0];
+  EXPECT_TRUE(kVideoTrack2 == recv_video_stream.name);
+
+  ASSERT_EQ(1u, voice_channel_->recv_streams().size());
+  cricket::StreamParams recv_audio_stream =
+      voice_channel_->recv_streams()[0];
+  EXPECT_TRUE(kAudioTrack2 == recv_audio_stream.name);
+
+  ASSERT_EQ(1u, video_channel_->send_streams().size());
+  EXPECT_TRUE(kVideoTrack1 == video_channel_->send_streams()[0].name);
+  ASSERT_EQ(1u, voice_channel_->send_streams().size());
+  EXPECT_TRUE(kAudioTrack1 == voice_channel_->send_streams()[0].name);
+
+  // Create new offer without send streams.
+  offer = session_->CreateOffer(OptionsReceiveOnly());
+  // Test with same answer.
+  session_->SetLocalDescription(offer, cricket::CA_OFFER);
+  session_->SetRemoteDescription(answer, cricket::CA_ANSWER);
+
+  EXPECT_EQ(0u, video_channel_->send_streams().size());
+  EXPECT_EQ(0u, voice_channel_->send_streams().size());
+
+  // Make sure the receive streams have not changed.
+  ASSERT_EQ(1u, video_channel_->recv_streams().size());
+  EXPECT_EQ(recv_video_stream, video_channel_->recv_streams()[0]);
+  ASSERT_EQ(1u, voice_channel_->recv_streams().size());
+  EXPECT_EQ(recv_audio_stream, voice_channel_->recv_streams()[0]);
+}
+
+// Test receiving offers and creating answers and make sure the
+// media engine creates the expected send and receive streams.
+TEST_F(WebRtcSessionTest, TestReceiveOfferCreateAnswer) {
+  cricket::SessionDescription* offer = CreateTestOffer(OptionsWithStream2());
+
+  cricket::MediaSessionOptions answer_options = OptionsWithStream1();
+  cricket::SessionDescription* answer =
+        session_->CreateAnswer(offer, answer_options);
+  session_->SetRemoteDescription(offer, cricket::CA_OFFER);
+  session_->SetLocalDescription(answer, cricket::CA_ANSWER);
+
+  ASSERT_EQ(1u, video_channel_->recv_streams().size());
+  EXPECT_TRUE(kVideoTrack2 == video_channel_->recv_streams()[0].name);
+
+  ASSERT_EQ(1u, voice_channel_->recv_streams().size());
+  EXPECT_TRUE(kAudioTrack2 == voice_channel_->recv_streams()[0].name);
+
+  ASSERT_EQ(1u, video_channel_->send_streams().size());
+  EXPECT_TRUE(kVideoTrack1 == video_channel_->send_streams()[0].name);
+  ASSERT_EQ(1u, voice_channel_->send_streams().size());
+  EXPECT_TRUE(kAudioTrack1 == voice_channel_->send_streams()[0].name);
+
+  offer = CreateTestOffer(OptionsWithStream1And2());
+
+  // Answer by turning off all send streams.
+  answer = session_->CreateAnswer(offer, OptionsReceiveOnly());
+  session_->SetRemoteDescription(offer, cricket::CA_OFFER);
+  session_->SetLocalDescription(answer, cricket::CA_ANSWER);
+
+  ASSERT_EQ(2u, video_channel_->recv_streams().size());
+  EXPECT_TRUE(kVideoTrack1 == video_channel_->recv_streams()[0].name);
+  EXPECT_TRUE(kVideoTrack2 == video_channel_->recv_streams()[1].name);
+  ASSERT_EQ(2u, voice_channel_->recv_streams().size());
+  EXPECT_TRUE(kAudioTrack1 == voice_channel_->recv_streams()[0].name);
+  EXPECT_TRUE(kAudioTrack2 == voice_channel_->recv_streams()[1].name);
+
+  // Make we have no send streams.
+  EXPECT_EQ(0u, video_channel_->send_streams().size());
+  EXPECT_EQ(0u, voice_channel_->send_streams().size());
 }
 
 TEST_F(WebRtcSessionTest, TestDefaultSetSecurePolicy) {
-  WebRtcSessionTest::Init();
   EXPECT_EQ(cricket::SEC_REQUIRED, session_->secure_policy());
 }
 
 TEST_F(WebRtcSessionTest, VerifyCryptoParamsInSDP) {
-  WebRtcSessionTest::Init();
-  CreateOffer();
-  VerifyCryptoParams(session_->local_description(), true);
+  VerifyCryptoParams(session_->CreateOffer(OptionsWithStream1()), true);
 }
 
 TEST_F(WebRtcSessionTest, VerifyNoCryptoParamsInSDP) {
-  WebRtcSessionTest::Init();
   session_->set_secure_policy(cricket::SEC_DISABLED);
-  CreateOffer();
-  VerifyNoCryptoParams(session_->local_description());
+  VerifyNoCryptoParams(session_->CreateOffer(OptionsWithStream1()));
 }
 
 TEST_F(WebRtcSessionTest, VerifyAnswerFromNonCryptoOffer) {
-  WebRtcSessionTest::Init();
   VerifyAnswerFromNonCryptoOffer();
 }
 
 TEST_F(WebRtcSessionTest, VerifyAnswerFromCryptoOffer) {
-  WebRtcSessionTest::Init();
   VerifyAnswerFromCryptoOffer();
 }
diff --git a/talk/session/phone/fakewebrtcvoiceengine.h b/talk/session/phone/fakewebrtcvoiceengine.h
index 29735f8..568d408 100644
--- a/talk/session/phone/fakewebrtcvoiceengine.h
+++ b/talk/session/phone/fakewebrtcvoiceengine.h
@@ -182,7 +182,7 @@
   void TriggerProcessPacket(MediaProcessorDirection direction) {
     webrtc::ProcessingTypes pt =
         (direction == cricket::MPD_TX) ?
-            webrtc::kRecordingPerChannel : webrtc::kPlaybackAllChannelsMixed;
+            webrtc::kRecordingPerChannel : webrtc::kPlaybackPerChannel;
     if (media_processor_ != NULL) {
       media_processor_->Process(0,
                                 pt,
diff --git a/talk/session/phone/webrtcvideoframe_unittest.cc b/talk/session/phone/webrtcvideoframe_unittest.cc
index ea47a72..903827e 100644
--- a/talk/session/phone/webrtcvideoframe_unittest.cc
+++ b/talk/session/phone/webrtcvideoframe_unittest.cc
@@ -86,12 +86,48 @@
 TEST_WEBRTCVIDEOFRAME(ConvertToRGB565Buffer)
 TEST_WEBRTCVIDEOFRAME(ConvertToRGB565BufferStride)
 TEST_WEBRTCVIDEOFRAME(ConvertToRGB565BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertToBayerBGGRBuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertToBayerBGGRBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertToBayerBGGRBufferInverted)
 TEST_WEBRTCVIDEOFRAME(ConvertToYUY2Buffer)
 TEST_WEBRTCVIDEOFRAME(ConvertToYUY2BufferStride)
 TEST_WEBRTCVIDEOFRAME(ConvertToYUY2BufferInverted)
 TEST_WEBRTCVIDEOFRAME(ConvertToUYVYBuffer)
 TEST_WEBRTCVIDEOFRAME(ConvertToUYVYBufferStride)
 TEST_WEBRTCVIDEOFRAME(ConvertToUYVYBufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromABGRBuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromABGRBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromABGRBufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromARGB1555Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromARGB1555BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromARGB1555BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromARGB4444Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromARGB4444BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromARGB4444BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromARGBBuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromARGBBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromARGBBufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromBGRABuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromBGRABufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromBGRABufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromRAWBuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromRAWBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromRAWBufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromRGB24Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromRGB24BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromRGB24BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromRGB565Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromRGB565BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromRGB565BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromBayerBGGRBuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromBayerBGGRBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromBayerBGGRBufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromYUY2Buffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromYUY2BufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromYUY2BufferInverted)
+TEST_WEBRTCVIDEOFRAME(ConvertFromUYVYBuffer)
+TEST_WEBRTCVIDEOFRAME(ConvertFromUYVYBufferStride)
+TEST_WEBRTCVIDEOFRAME(ConvertFromUYVYBufferInverted)
 //TEST_WEBRTCVIDEOFRAME(ConvertToI422Buffer)
 TEST_WEBRTCVIDEOFRAME(ConvertARGBToBayerGRBG)
 TEST_WEBRTCVIDEOFRAME(ConvertARGBToBayerGBRG)
diff --git a/talk/session/phone/webrtcvoiceengine.cc b/talk/session/phone/webrtcvoiceengine.cc
index 58706f9..9d44fce 100644
--- a/talk/session/phone/webrtcvoiceengine.cc
+++ b/talk/session/phone/webrtcvoiceengine.cc
@@ -940,13 +940,14 @@
       it != channels_.end(); ++it) {
     ASSERT(*it != NULL);
     uint32 local_ssrc;
-    if (voe()->rtp()->GetLocalSSRC((*it)->voe_channel(), local_ssrc) != -1) {
-      if (ssrc == local_ssrc) {
-        *channel_num = (*it)->voe_channel();
-      }
-    }
-    if (*channel_num == -1 && (direction & MPD_RX) != 0) {
+    if ((direction & MPD_RX) != 0) {
       *channel_num = (*it)->GetChannelNum(ssrc);
+    } else {
+      if (voe()->rtp()->GetLocalSSRC((*it)->voe_channel(), local_ssrc) != -1) {
+        if (ssrc == local_ssrc) {
+          *channel_num = (*it)->voe_channel();
+        }
+      }
     }
     if (*channel_num != -1) {
       return true;
@@ -1079,7 +1080,7 @@
   talk_base::CritScope cs(&signal_media_critical_);
   webrtc::ProcessingTypes processing_type;
   if (direction == MPD_RX) {
-    processing_type = webrtc::kPlaybackAllChannelsMixed;
+    processing_type = webrtc::kPlaybackPerChannel;
     if (SignalRxMediaFrame.is_empty()) {
       register_with_webrtc = true;
     }
@@ -1134,13 +1135,13 @@
     SignalRxMediaFrame.disconnect(voice_processor);
     if (SignalRxMediaFrame.is_empty()) {
       if (voe()->media()->DeRegisterExternalMediaProcessing(channel_id,
-          webrtc::kPlaybackAllChannelsMixed) != -1) {
+          webrtc::kPlaybackPerChannel) != -1) {
         LOG(LS_INFO) << "Media Processing DeRegistration Succeeded. channel:"
                      << channel_id;
       } else {
         LOG_RTCERR2(DeRegisterExternalMediaProcessing,
                     channel_id,
-                    webrtc::kPlaybackAllChannelsMixed);
+                    webrtc::kPlaybackPerChannel);
         success = false;
       }
     }
@@ -1176,7 +1177,7 @@
   if (FindChannelAndSsrc(channel, &media_channel, &ssrc)) {
     talk_base::CritScope cs(&signal_media_critical_);
     AudioFrame frame(audio10ms, length, sampling_freq, is_stereo);
-    if (type == webrtc::kPlaybackAllChannelsMixed) {
+    if (type == webrtc::kPlaybackPerChannel) {
       SignalRxMediaFrame(ssrc, &frame);
     } else if (type == webrtc::kRecordingPerChannel) {
       SignalTxMediaFrame(ssrc, &frame);
@@ -1192,6 +1193,7 @@
     : WebRtcMediaChannel<VoiceMediaChannel, WebRtcVoiceEngine>(
           engine,
           engine->voe()->base()->CreateChannel()),
+      recv_codecs_set_(false),
       channel_options_(0),
       agc_adjusted_(false),
       dtmf_allowed_(false),
@@ -1272,12 +1274,21 @@
         LOG_RTCERR2(SetRecPayloadType, voe_channel(), ToString(voe_codec));
         ret = false;
       }
+      // Set the receive codecs on all receiving channels.
+      for (ChannelMap::iterator it = mux_channels_.begin();
+           it != mux_channels_.end() && ret; ++it) {
+        if (engine()->voe()->codec()->SetRecPayloadType(
+            it->second, voe_codec) == -1) {
+          LOG_RTCERR2(SetRecPayloadType, it->second, ToString(voe_codec));
+          ret = false;
+        }
+      }
     } else {
       LOG(LS_WARNING) << "Unknown codec " << ToString(*it);
       ret = false;
     }
   }
-
+  recv_codecs_set_ = ret;
   return ret;
 }
 
@@ -1652,17 +1663,19 @@
 
   // Use the same recv payload types as our default channel.
   ResetRecvCodecs(channel);
-  int ncodecs = engine()->voe()->codec()->NumOfCodecs();
-  for (int i = 0; i < ncodecs; ++i) {
-    webrtc::CodecInst voe_codec;
-    if (engine()->voe()->codec()->GetCodec(i, voe_codec) != -1) {
-      voe_codec.rate = 0;  // Needed to make GetRecPayloadType work for ISAC
-      if (engine()->voe()->codec()->GetRecPayloadType(
-          voe_channel(), voe_codec) != -1) {
-        if (engine()->voe()->codec()->SetRecPayloadType(
-            channel, voe_codec) == -1) {
-          LOG_RTCERR2(SetRecPayloadType, channel, ToString(voe_codec));
-          return false;
+  if (recv_codecs_set_) {
+    int ncodecs = engine()->voe()->codec()->NumOfCodecs();
+    for (int i = 0; i < ncodecs; ++i) {
+      webrtc::CodecInst voe_codec;
+      if (engine()->voe()->codec()->GetCodec(i, voe_codec) != -1) {
+        voe_codec.rate = 0;  // Needed to make GetRecPayloadType work for ISAC
+        if (engine()->voe()->codec()->GetRecPayloadType(
+            voe_channel(), voe_codec) != -1) {
+          if (engine()->voe()->codec()->SetRecPayloadType(
+              channel, voe_codec) == -1) {
+            LOG_RTCERR2(SetRecPayloadType, channel, ToString(voe_codec));
+            return false;
+          }
         }
       }
     }
diff --git a/talk/session/phone/webrtcvoiceengine.h b/talk/session/phone/webrtcvoiceengine.h
index dfb9023..7cc148a 100644
--- a/talk/session/phone/webrtcvoiceengine.h
+++ b/talk/session/phone/webrtcvoiceengine.h
@@ -347,6 +347,7 @@
   typedef std::map<uint32, int> ChannelMap;
   talk_base::scoped_ptr<WebRtcSoundclipStream> ringback_tone_;
   std::set<int> ringback_channels_;  // channels playing ringback
+  bool recv_codecs_set_;
   talk_base::scoped_ptr<webrtc::CodecInst> send_codec_;
   int channel_options_;
   bool agc_adjusted_;
@@ -355,6 +356,7 @@
   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
diff --git a/talk/session/phone/webrtcvoiceengine_unittest.cc b/talk/session/phone/webrtcvoiceengine_unittest.cc
index 82838d1..9b4df86 100644
--- a/talk/session/phone/webrtcvoiceengine_unittest.cc
+++ b/talk/session/phone/webrtcvoiceengine_unittest.cc
@@ -277,6 +277,25 @@
   EXPECT_STREQ("telephone-event", gcodec.plname);
 }
 
+TEST_F(WebRtcVoiceEngineTestFake, SetRecvCodecsAfterAddingStreams) {
+  EXPECT_TRUE(SetupEngine());
+  std::vector<cricket::AudioCodec> codecs;
+  codecs.push_back(kIsacCodec);
+  codecs[0].id = 106;  // collide with existing telephone-event
+
+  EXPECT_TRUE(channel_->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrc1)));
+  EXPECT_TRUE(channel_->SetRecvCodecs(codecs));
+
+  int channel_num2 = voe_.GetLastChannel();
+  webrtc::CodecInst gcodec;
+  talk_base::strcpyn(gcodec.plname, ARRAY_SIZE(gcodec.plname), "ISAC");
+  gcodec.plfreq = 16000;
+  EXPECT_EQ(0, voe_.GetRecPayloadType(channel_num2, gcodec));
+  EXPECT_EQ(106, gcodec.pltype);
+  EXPECT_STREQ("ISAC", gcodec.plname);
+}
+
 // Test that we apply codecs properly.
 TEST_F(WebRtcVoiceEngineTestFake, SetSendCodecs) {
   EXPECT_TRUE(SetupEngine());
@@ -1086,6 +1105,8 @@
 // webrtcvoiceengine works as expected
 TEST_F(WebRtcVoiceEngineTestFake, RegisterVoiceProcessor) {
   EXPECT_TRUE(SetupEngine());
+  channel_->AddRecvStream(
+      cricket::StreamParams::CreateLegacy(kSsrc1));
   uint32 ssrc = 0;
   voe_.GetLocalSSRC(0, ssrc);
   cricket::FakeMediaProcessor vp_1;