Update talk to 61538839.

TBR=mallinath

Review URL: https://webrtc-codereview.appspot.com/8669005

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5548 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/mediastreamhandler.cc b/talk/app/webrtc/mediastreamhandler.cc
index a94eef3..ca8e105 100644
--- a/talk/app/webrtc/mediastreamhandler.cc
+++ b/talk/app/webrtc/mediastreamhandler.cc
@@ -106,6 +106,8 @@
 void LocalAudioTrackHandler::OnEnabledChanged() {
   cricket::AudioOptions options;
   if (audio_track_->enabled() && audio_track_->GetSource()) {
+    // TODO(xians): Remove this static_cast since we should be able to connect
+    // a remote audio track to peer connection.
     options = static_cast<LocalAudioSource*>(
         audio_track_->GetSource())->options();
   }
@@ -125,10 +127,12 @@
     : TrackHandler(track, ssrc),
       audio_track_(track),
       provider_(provider) {
+  track->GetSource()->RegisterAudioObserver(this);
   OnEnabledChanged();
 }
 
 RemoteAudioTrackHandler::~RemoteAudioTrackHandler() {
+  audio_track_->GetSource()->UnregisterAudioObserver(this);
 }
 
 void RemoteAudioTrackHandler::Stop() {
@@ -143,6 +147,14 @@
                              audio_track_->GetRenderer());
 }
 
+void RemoteAudioTrackHandler::OnSetVolume(double volume) {
+  // When the track is disabled, the volume of the source, which is the
+  // corresponding WebRtc Voice Engine channel will be 0. So we do not allow
+  // setting the volume to the source when the track is disabled.
+  if (audio_track_->enabled())
+    provider_->SetAudioPlayoutVolume(ssrc(), volume);
+}
+
 LocalVideoTrackHandler::LocalVideoTrackHandler(
     VideoTrackInterface* track,
     uint32 ssrc,
diff --git a/talk/app/webrtc/mediastreamhandler.h b/talk/app/webrtc/mediastreamhandler.h
index 625de85..53afd55 100644
--- a/talk/app/webrtc/mediastreamhandler.h
+++ b/talk/app/webrtc/mediastreamhandler.h
@@ -118,7 +118,8 @@
 // RemoteAudioTrackHandler listen to events on a remote AudioTrack instance
 // connected to a PeerConnection and orders the |provider| to executes the
 // requested change.
-class RemoteAudioTrackHandler : public TrackHandler {
+class RemoteAudioTrackHandler : public AudioSourceInterface::AudioObserver,
+                                public TrackHandler {
  public:
   RemoteAudioTrackHandler(AudioTrackInterface* track,
                           uint32 ssrc,
@@ -131,6 +132,9 @@
   virtual void OnEnabledChanged() OVERRIDE;
 
  private:
+  // AudioSourceInterface::AudioObserver implementation.
+  virtual void OnSetVolume(double volume) OVERRIDE;
+
   AudioTrackInterface* audio_track_;
   AudioProviderInterface* provider_;
 };
diff --git a/talk/app/webrtc/mediastreamhandler_unittest.cc b/talk/app/webrtc/mediastreamhandler_unittest.cc
index 475258e..6eedb7e 100644
--- a/talk/app/webrtc/mediastreamhandler_unittest.cc
+++ b/talk/app/webrtc/mediastreamhandler_unittest.cc
@@ -31,6 +31,7 @@
 
 #include "talk/app/webrtc/audiotrack.h"
 #include "talk/app/webrtc/mediastream.h"
+#include "talk/app/webrtc/remoteaudiosource.h"
 #include "talk/app/webrtc/streamcollection.h"
 #include "talk/app/webrtc/videosource.h"
 #include "talk/app/webrtc/videotrack.h"
@@ -59,6 +60,7 @@
   MOCK_METHOD4(SetAudioSend, void(uint32 ssrc, bool enable,
                                   const cricket::AudioOptions& options,
                                   cricket::AudioRenderer* renderer));
+  MOCK_METHOD2(SetAudioPlayoutVolume, void(uint32 ssrc, double volume));
 };
 
 // Helper class to test MediaStreamHandler.
@@ -110,12 +112,11 @@
         FakeVideoSource::Create());
     video_track_ = VideoTrack::Create(kVideoTrackId, source);
     EXPECT_TRUE(stream_->AddTrack(video_track_));
-    audio_track_ = AudioTrack::Create(kAudioTrackId,
-                                           NULL);
-    EXPECT_TRUE(stream_->AddTrack(audio_track_));
   }
 
   void AddLocalAudioTrack() {
+    audio_track_ = AudioTrack::Create(kAudioTrackId, NULL);
+    EXPECT_TRUE(stream_->AddTrack(audio_track_));
     EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _));
     handlers_.AddLocalAudioTrack(stream_, stream_->GetAudioTracks()[0],
                                  kAudioSsrc);
@@ -144,6 +145,9 @@
   }
 
   void AddRemoteAudioTrack() {
+    audio_track_ = AudioTrack::Create(kAudioTrackId,
+                                      RemoteAudioSource::Create().get());
+    EXPECT_TRUE(stream_->AddTrack(audio_track_));
     EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, true, _));
     handlers_.AddRemoteAudioTrack(stream_, stream_->GetAudioTracks()[0],
                                   kAudioSsrc);
@@ -292,4 +296,27 @@
   handlers_.TearDown();
 }
 
+TEST_F(MediaStreamHandlerTest, RemoteAudioTrackSetVolume) {
+  AddRemoteAudioTrack();
+
+  double volume = 0.5;
+  EXPECT_CALL(audio_provider_, SetAudioPlayoutVolume(kAudioSsrc, volume));
+  audio_track_->GetSource()->SetVolume(volume);
+
+  // Disable the audio track, this should prevent setting the volume.
+  EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, false, _));
+  audio_track_->set_enabled(false);
+  audio_track_->GetSource()->SetVolume(1.0);
+
+  EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, true, _));
+  audio_track_->set_enabled(true);
+
+  double new_volume = 0.8;
+  EXPECT_CALL(audio_provider_, SetAudioPlayoutVolume(kAudioSsrc, new_volume));
+  audio_track_->GetSource()->SetVolume(new_volume);
+
+  RemoveRemoteAudioTrack();
+  handlers_.TearDown();
+}
+
 }  // namespace webrtc
diff --git a/talk/app/webrtc/mediastreaminterface.h b/talk/app/webrtc/mediastreaminterface.h
index 96d0942..fa0572e 100644
--- a/talk/app/webrtc/mediastreaminterface.h
+++ b/talk/app/webrtc/mediastreaminterface.h
@@ -142,9 +142,24 @@
 
 // AudioSourceInterface is a reference counted source used for AudioTracks.
 // The same source can be used in multiple AudioTracks.
-// TODO(perkj): Extend this class with necessary methods to allow separate
-// sources for each audio track.
 class AudioSourceInterface : public MediaSourceInterface {
+ public:
+  class AudioObserver {
+   public:
+    virtual void OnSetVolume(double volume) = 0;
+
+   protected:
+    virtual ~AudioObserver() {}
+  };
+
+  // TODO(xians): Makes all the interface pure virtual after Chrome has their
+  // implementations.
+  // Sets the volume to the source. |volume| is in  the range of [0, 10].
+  virtual void SetVolume(double volume) {}
+
+  // Registers/unregisters observer to the audio source.
+  virtual void RegisterAudioObserver(AudioObserver* observer) {}
+  virtual void UnregisterAudioObserver(AudioObserver* observer) {}
 };
 
 // Interface for receiving audio data from a AudioTrack.
diff --git a/talk/app/webrtc/mediastreamprovider.h b/talk/app/webrtc/mediastreamprovider.h
index ae00b1d..5cf0e27 100644
--- a/talk/app/webrtc/mediastreamprovider.h
+++ b/talk/app/webrtc/mediastreamprovider.h
@@ -53,6 +53,10 @@
                             const cricket::AudioOptions& options,
                             cricket::AudioRenderer* renderer) = 0;
 
+  // Sets the audio playout volume of a remote audio track with |ssrc|.
+  // |volume| is in the range of [0, 10].
+  virtual void SetAudioPlayoutVolume(uint32 ssrc, double volume) = 0;
+
  protected:
   virtual ~AudioProviderInterface() {}
 };
diff --git a/talk/app/webrtc/mediastreamsignaling.cc b/talk/app/webrtc/mediastreamsignaling.cc
index 610b3f8..14648ee 100644
--- a/talk/app/webrtc/mediastreamsignaling.cc
+++ b/talk/app/webrtc/mediastreamsignaling.cc
@@ -33,6 +33,7 @@
 #include "talk/app/webrtc/mediastreamproxy.h"
 #include "talk/app/webrtc/mediaconstraintsinterface.h"
 #include "talk/app/webrtc/mediastreamtrackproxy.h"
+#include "talk/app/webrtc/remoteaudiosource.h"
 #include "talk/app/webrtc/remotevideocapturer.h"
 #include "talk/app/webrtc/sctputils.h"
 #include "talk/app/webrtc/videosource.h"
@@ -140,7 +141,7 @@
   AudioTrackInterface* AddAudioTrack(webrtc::MediaStreamInterface* stream,
                                      const std::string& track_id) {
     return AddTrack<AudioTrackInterface, AudioTrack, AudioTrackProxy>(
-        stream, track_id, static_cast<AudioSourceInterface*>(NULL));
+        stream, track_id, RemoteAudioSource::Create().get());
   }
 
   VideoTrackInterface* AddVideoTrack(webrtc::MediaStreamInterface* stream,
diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc
index 40640cf..b404ec4 100644
--- a/talk/app/webrtc/peerconnection.cc
+++ b/talk/app/webrtc/peerconnection.cc
@@ -459,13 +459,19 @@
 }
 
 bool PeerConnection::GetStats(StatsObserver* observer,
-                              MediaStreamTrackInterface* track) {
+                              webrtc::MediaStreamTrackInterface* track) {
+  return GetStats(observer, track, kStatsOutputLevelStandard);
+}
+
+bool PeerConnection::GetStats(StatsObserver* observer,
+                              MediaStreamTrackInterface* track,
+                              StatsOutputLevel level) {
   if (!VERIFY(observer != NULL)) {
     LOG(LS_ERROR) << "GetStats - observer is NULL.";
     return false;
   }
 
-  stats_.UpdateStats();
+  stats_.UpdateStats(level);
   talk_base::scoped_ptr<GetStatsMsg> msg(new GetStatsMsg(observer));
   if (!stats_.GetStats(track, &(msg->reports))) {
     return false;
@@ -542,7 +548,7 @@
   }
   // Update stats here so that we have the most recent stats for tracks and
   // streams that might be removed by updating the session description.
-  stats_.UpdateStats();
+  stats_.UpdateStats(kStatsOutputLevelStandard);
   std::string error;
   if (!session_->SetLocalDescription(desc, &error)) {
     PostSetSessionDescriptionFailure(observer, error);
@@ -565,7 +571,7 @@
   }
   // Update stats here so that we have the most recent stats for tracks and
   // streams that might be removed by updating the session description.
-  stats_.UpdateStats();
+  stats_.UpdateStats(kStatsOutputLevelStandard);
   std::string error;
   if (!session_->SetRemoteDescription(desc, &error)) {
     PostSetSessionDescriptionFailure(observer, error);
@@ -606,7 +612,7 @@
 void PeerConnection::Close() {
   // Update stats here so that we have the most recent stats for tracks and
   // streams before the channels are closed.
-  stats_.UpdateStats();
+  stats_.UpdateStats(kStatsOutputLevelStandard);
 
   session_->Terminate();
 }
diff --git a/talk/app/webrtc/peerconnection.h b/talk/app/webrtc/peerconnection.h
index 9cc9f38..70155d9 100644
--- a/talk/app/webrtc/peerconnection.h
+++ b/talk/app/webrtc/peerconnection.h
@@ -76,6 +76,9 @@
       const DataChannelInit* config);
   virtual bool GetStats(StatsObserver* observer,
                         webrtc::MediaStreamTrackInterface* track);
+  virtual bool GetStats(StatsObserver* observer,
+                        webrtc::MediaStreamTrackInterface* track,
+                        StatsOutputLevel level);
 
   virtual SignalingState signaling_state();
 
diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h
index 667774e..2f44885 100644
--- a/talk/app/webrtc/peerconnectioninterface.h
+++ b/talk/app/webrtc/peerconnectioninterface.h
@@ -166,6 +166,15 @@
   };
   typedef std::vector<IceServer> IceServers;
 
+  // Used by GetStats to decide which stats to include in the stats reports.
+  // |kStatsOutputLevelStandard| includes the standard stats for Javascript API;
+  // |kStatsOutputLevelDebug| includes both the standard stats and additional
+  // stats for debugging purposes.
+  enum StatsOutputLevel {
+    kStatsOutputLevelStandard,
+    kStatsOutputLevelDebug,
+  };
+
   // Accessor methods to active local streams.
   virtual talk_base::scoped_refptr<StreamCollectionInterface>
       local_streams() = 0;
@@ -190,9 +199,14 @@
   virtual talk_base::scoped_refptr<DtmfSenderInterface> CreateDtmfSender(
       AudioTrackInterface* track) = 0;
 
+  // TODO(jiayl): remove the old API once all Chrome overrides are updated.
   virtual bool GetStats(StatsObserver* observer,
                         MediaStreamTrackInterface* track) = 0;
 
+  virtual bool GetStats(StatsObserver* observer,
+                        MediaStreamTrackInterface* track,
+                        StatsOutputLevel level) = 0;
+
   virtual talk_base::scoped_refptr<DataChannelInterface> CreateDataChannel(
       const std::string& label,
       const DataChannelInit* config) = 0;
diff --git a/talk/app/webrtc/peerconnectionproxy.h b/talk/app/webrtc/peerconnectionproxy.h
index f07416d..57bee51 100644
--- a/talk/app/webrtc/peerconnectionproxy.h
+++ b/talk/app/webrtc/peerconnectionproxy.h
@@ -45,6 +45,9 @@
   PROXY_METHOD1(talk_base::scoped_refptr<DtmfSenderInterface>,
                 CreateDtmfSender, AudioTrackInterface*)
   PROXY_METHOD2(bool, GetStats, StatsObserver*, MediaStreamTrackInterface*)
+  PROXY_METHOD3(bool, GetStats, StatsObserver*,
+                MediaStreamTrackInterface*,
+                StatsOutputLevel)
   PROXY_METHOD2(talk_base::scoped_refptr<DataChannelInterface>,
                 CreateDataChannel, const std::string&, const DataChannelInit*)
   PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, local_description)
diff --git a/talk/app/webrtc/remoteaudiosource.cc b/talk/app/webrtc/remoteaudiosource.cc
new file mode 100644
index 0000000..1c275c7
--- /dev/null
+++ b/talk/app/webrtc/remoteaudiosource.cc
@@ -0,0 +1,72 @@
+/*
+ * libjingle
+ * Copyright 2014, 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/app/webrtc/remoteaudiosource.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "talk/base/logging.h"
+
+namespace webrtc {
+
+talk_base::scoped_refptr<RemoteAudioSource> RemoteAudioSource::Create() {
+  return new talk_base::RefCountedObject<RemoteAudioSource>();
+}
+
+RemoteAudioSource::RemoteAudioSource() {
+}
+
+RemoteAudioSource::~RemoteAudioSource() {
+  ASSERT(audio_observers_.empty());
+}
+
+MediaSourceInterface::SourceState RemoteAudioSource::state() const {
+  return MediaSourceInterface::kLive;
+}
+
+void RemoteAudioSource::SetVolume(double volume) {
+  ASSERT(volume >= 0 && volume <= 10);
+  for (AudioObserverList::iterator it = audio_observers_.begin();
+       it != audio_observers_.end(); ++it) {
+    (*it)->OnSetVolume(volume);
+  }
+}
+
+void RemoteAudioSource::RegisterAudioObserver(AudioObserver* observer) {
+  ASSERT(observer != NULL);
+  ASSERT(std::find(audio_observers_.begin(), audio_observers_.end(),
+                   observer) == audio_observers_.end());
+  audio_observers_.push_back(observer);
+}
+
+void RemoteAudioSource::UnregisterAudioObserver(AudioObserver* observer) {
+  ASSERT(observer != NULL);
+  audio_observers_.remove(observer);
+}
+
+}  // namespace webrtc
diff --git a/talk/app/webrtc/remoteaudiosource.h b/talk/app/webrtc/remoteaudiosource.h
new file mode 100644
index 0000000..ed24214
--- /dev/null
+++ b/talk/app/webrtc/remoteaudiosource.h
@@ -0,0 +1,66 @@
+/*
+ * libjingle
+ * Copyright 2014, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_APP_WEBRTC_REMOTEAUDIOSOURCE_H_
+#define TALK_APP_WEBRTC_REMOTEAUDIOSOURCE_H_
+
+#include <list>
+
+#include "talk/app/webrtc/mediastreaminterface.h"
+#include "talk/app/webrtc/notifier.h"
+
+namespace webrtc {
+
+using webrtc::AudioSourceInterface;
+
+// This class implements the audio source used by the remote audio track.
+class RemoteAudioSource : public Notifier<AudioSourceInterface> {
+ public:
+  // Creates an instance of RemoteAudioSource.
+  static talk_base::scoped_refptr<RemoteAudioSource> Create();
+
+ protected:
+  RemoteAudioSource();
+  virtual ~RemoteAudioSource();
+
+ private:
+  typedef std::list<AudioObserver*> AudioObserverList;
+
+  // MediaSourceInterface implementation.
+  virtual MediaSourceInterface::SourceState state() const OVERRIDE;
+
+  // AudioSourceInterface implementation.
+  virtual void SetVolume(double volume) OVERRIDE;
+  virtual void RegisterAudioObserver(AudioObserver* observer) OVERRIDE;
+  virtual void UnregisterAudioObserver(AudioObserver* observer) OVERRIDE;
+
+  AudioObserverList audio_observers_;
+};
+
+}  // namespace webrtc
+
+#endif  // TALK_APP_WEBRTC_REMOTEAUDIOSOURCE_H_
diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc
index 2efc11b..a900bba 100644
--- a/talk/app/webrtc/statscollector.cc
+++ b/talk/app/webrtc/statscollector.cc
@@ -78,6 +78,7 @@
 
 const char StatsReport::kStatsValueNameEncodeUsagePercent[] =
     "googEncodeUsagePercent";
+const char StatsReport::kStatsValueNameExpandRate[] = "googExpandRate";
 const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint";
 const char StatsReport::kStatsValueNameFingerprintAlgorithm[] =
     "googFingerprintAlgorithm";
@@ -121,12 +122,17 @@
     "googLocalCertificateId";
 const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
 const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent";
-const char StatsReport::kStatsValueNameNetEqExpandRate[] =
-    "googNetEqExpandRate";
 const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived";
 const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent";
 const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
 const char StatsReport::kStatsValueNameReadable[] = "googReadable";
+const char StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug[] =
+    "googReceivedPacketGroupArrivalTimeDebug";
+const char StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug[] =
+    "googReceivedPacketGroupPropagationDeltaDebug";
+const char
+StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[] =
+    "googReceivedPacketGroupPropagationDeltaSumDebug";
 const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
 const char StatsReport::kStatsValueNameRemoteCandidateType[] =
     "googRemoteCandidateType";
@@ -175,6 +181,20 @@
   AddValue(name, talk_base::ToString<int64>(value));
 }
 
+template <typename T>
+void StatsReport::AddValue(const std::string& name,
+                           const std::vector<T>& value) {
+  std::ostringstream oss;
+  oss << "[";
+  for (size_t i = 0; i < value.size(); ++i) {
+    oss << talk_base::ToString<T>(value[i]);
+    if (i != value.size() - 1)
+      oss << ", ";
+  }
+  oss << "]";
+  AddValue(name, oss.str());
+}
+
 void StatsReport::AddBoolean(const std::string& name, bool value) {
   AddValue(name, value ? "true" : "false");
 }
@@ -221,7 +241,7 @@
                    info.bytes_rcvd);
   report->AddValue(StatsReport::kStatsValueNameJitterReceived,
                    info.jitter_ms);
-  report->AddValue(StatsReport::kStatsValueNameNetEqExpandRate,
+  report->AddValue(StatsReport::kStatsValueNameExpandRate,
                    talk_base::ToString<float>(info.expand_rate));
   report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
                    info.packets_rcvd);
@@ -334,6 +354,7 @@
 
 void ExtractStats(const cricket::BandwidthEstimationInfo& info,
                   double stats_gathering_started,
+                  PeerConnectionInterface::StatsOutputLevel level,
                   StatsReport* report) {
   report->id = StatsReport::kStatsReportVideoBweId;
   report->type = StatsReport::kStatsReportTypeBwe;
@@ -358,6 +379,19 @@
                    info.transmit_bitrate);
   report->AddValue(StatsReport::kStatsValueNameBucketDelay,
                    info.bucket_delay);
+  if (level >= PeerConnectionInterface::kStatsOutputLevelDebug) {
+    report->AddValue(
+        StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug,
+        info.total_received_propagation_delta_ms);
+    if (info.recent_received_propagation_delta_ms.size() > 0) {
+      report->AddValue(
+          StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug,
+          info.recent_received_propagation_delta_ms);
+      report->AddValue(
+          StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug,
+          info.recent_received_packet_group_arrival_time_ms);
+    }
+  }
 }
 
 void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
@@ -399,7 +433,7 @@
       ExtractRemoteStats(*it, report);
     }
   }
-};
+}
 
 }  // namespace
 
@@ -463,7 +497,8 @@
   return true;
 }
 
-void StatsCollector::UpdateStats() {
+void
+StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
   double time_now = GetTimeNow();
   // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
   // ms apart will be ignored.
@@ -476,7 +511,7 @@
   if (session_) {
     ExtractSessionInfo();
     ExtractVoiceInfo();
-    ExtractVideoInfo();
+    ExtractVideoInfo(level);
   }
 }
 
@@ -569,6 +604,14 @@
 
   talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint(
       talk_base::SSLFingerprint::Create(digest_algorithm, cert));
+
+  // SSLFingerprint::Create can fail if the algorithm returned by
+  // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the
+  // implementation of SSLCertificate::ComputeDigest.  This currently happens
+  // with MD5- and SHA-224-signed certificates when linked to libNSS.
+  if (!ssl_fingerprint)
+    return std::string();
+
   std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
 
   talk_base::Buffer der_buffer;
@@ -737,12 +780,17 @@
   ExtractStatsFromList(voice_info.senders, transport_id, this);
 }
 
-void StatsCollector::ExtractVideoInfo() {
+void StatsCollector::ExtractVideoInfo(
+    PeerConnectionInterface::StatsOutputLevel level) {
   if (!session_->video_channel()) {
     return;
   }
+  cricket::StatsOptions options;
+  options.include_received_propagation_stats =
+      (level >= PeerConnectionInterface::kStatsOutputLevelDebug) ?
+          true : false;
   cricket::VideoMediaInfo video_info;
-  if (!session_->video_channel()->GetStats(&video_info)) {
+  if (!session_->video_channel()->GetStats(options, &video_info)) {
     LOG(LS_ERROR) << "Failed to get video channel stats.";
     return;
   }
@@ -760,7 +808,7 @@
   } else {
     StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId];
     ExtractStats(
-        video_info.bw_estimations[0], stats_gathering_started_, report);
+        video_info.bw_estimations[0], stats_gathering_started_, level, report);
   }
 }
 
diff --git a/talk/app/webrtc/statscollector.h b/talk/app/webrtc/statscollector.h
index 01da059..6256d77 100644
--- a/talk/app/webrtc/statscollector.h
+++ b/talk/app/webrtc/statscollector.h
@@ -35,6 +35,7 @@
 #include <map>
 
 #include "talk/app/webrtc/mediastreaminterface.h"
+#include "talk/app/webrtc/peerconnectioninterface.h"
 #include "talk/app/webrtc/statstypes.h"
 #include "talk/app/webrtc/webrtcsession.h"
 
@@ -57,13 +58,14 @@
   void AddStream(MediaStreamInterface* stream);
 
   // Gather statistics from the session and store them for future use.
-  void UpdateStats();
+  void UpdateStats(PeerConnectionInterface::StatsOutputLevel level);
 
   // Gets a StatsReports of the last collected stats. Note that UpdateStats must
   // be called before this function to get the most recent stats. |selector| is
   // a track label or empty string. The most recent reports are stored in
   // |reports|.
-  bool GetStats(MediaStreamTrackInterface* track, StatsReports* reports);
+  bool GetStats(MediaStreamTrackInterface* track,
+                StatsReports* reports);
 
   // Prepare an SSRC report for the given ssrc. Used internally
   // in the ExtractStatsFromList template.
@@ -87,7 +89,7 @@
 
   void ExtractSessionInfo();
   void ExtractVoiceInfo();
-  void ExtractVideoInfo();
+  void ExtractVideoInfo(PeerConnectionInterface::StatsOutputLevel level);
   double GetTimeNow();
   void BuildSsrcToTransportId();
   WebRtcSession* session() { return session_; }
diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc
index 1adcb0e..a7cda16 100644
--- a/talk/app/webrtc/statscollector_unittest.cc
+++ b/talk/app/webrtc/statscollector_unittest.cc
@@ -39,11 +39,14 @@
 #include "talk/session/media/channelmanager.h"
 #include "testing/base/public/gmock.h"
 
+using cricket::StatsOptions;
 using testing::_;
 using testing::DoAll;
+using testing::Field;
 using testing::Return;
 using testing::ReturnNull;
 using testing::SetArgPointee;
+using webrtc::PeerConnectionInterface;
 
 namespace cricket {
 
@@ -80,7 +83,7 @@
     : cricket::FakeVideoMediaChannel(NULL) {
   }
   // MOCK_METHOD0(transport_channel, cricket::TransportChannel*());
-  MOCK_METHOD1(GetStats, bool(cricket::VideoMediaInfo*));
+  MOCK_METHOD2(GetStats, bool(const StatsOptions&, cricket::VideoMediaInfo*));
 };
 
 bool GetValue(const webrtc::StatsReport* report,
@@ -289,7 +292,7 @@
     EXPECT_CALL(session_, video_channel())
       .WillRepeatedly(ReturnNull());
 
-    stats.UpdateStats();
+    stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
 
     stats.GetStats(NULL, &reports);
 
@@ -302,16 +305,24 @@
         webrtc::StatsReport::kStatsReportTypeComponent,
         reports,
         webrtc::StatsReport::kStatsValueNameLocalCertificateId);
-    EXPECT_NE(kNotFound, local_certificate_id);
-    CheckCertChainReports(reports, local_ders, local_certificate_id);
+    if (local_ders.size() > 0) {
+      EXPECT_NE(kNotFound, local_certificate_id);
+      CheckCertChainReports(reports, local_ders, local_certificate_id);
+    } else {
+      EXPECT_EQ(kNotFound, local_certificate_id);
+    }
 
     // Check remote certificate chain.
     std::string remote_certificate_id = ExtractStatsValue(
         webrtc::StatsReport::kStatsReportTypeComponent,
         reports,
         webrtc::StatsReport::kStatsValueNameRemoteCertificateId);
-    EXPECT_NE(kNotFound, remote_certificate_id);
-    CheckCertChainReports(reports, remote_ders, remote_certificate_id);
+    if (remote_ders.size() > 0) {
+      EXPECT_NE(kNotFound, remote_certificate_id);
+      CheckCertChainReports(reports, remote_ders, remote_certificate_id);
+    } else {
+      EXPECT_EQ(kNotFound, remote_certificate_id);
+    }
   }
 
   cricket::FakeMediaEngine* media_engine_;
@@ -347,10 +358,10 @@
 
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(Return(&video_channel));
-  EXPECT_CALL(*media_channel, GetStats(_))
-    .WillOnce(DoAll(SetArgPointee<0>(stats_read),
+  EXPECT_CALL(*media_channel, GetStats(_, _))
+    .WillOnce(DoAll(SetArgPointee<1>(stats_read),
                     Return(true)));
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
   std::string result = ExtractSsrcStatsValue(reports, "bytesSent");
   EXPECT_EQ(kBytesSentString, result);
@@ -386,11 +397,11 @@
 
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(Return(&video_channel));
-  EXPECT_CALL(*media_channel, GetStats(_))
-    .WillOnce(DoAll(SetArgPointee<0>(stats_read),
+  EXPECT_CALL(*media_channel, GetStats(_, _))
+    .WillOnce(DoAll(SetArgPointee<1>(stats_read),
                     Return(true)));
 
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
   std::string result = ExtractSsrcStatsValue(reports, "bytesSent");
   EXPECT_EQ(kBytesSentString, result);
@@ -406,7 +417,7 @@
   stats.set_session(&session_);
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(ReturnNull());
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
   const webrtc::StatsReport* session_report = FindNthReportByType(
       reports, webrtc::StatsReport::kStatsReportTypeSession, 1);
@@ -421,8 +432,8 @@
   stats.set_session(&session_);
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(ReturnNull());
-  stats.UpdateStats();
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
   const webrtc::StatsReport* session_report = FindNthReportByType(
       reports, webrtc::StatsReport::kStatsReportTypeSession, 1);
@@ -485,11 +496,11 @@
 
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(Return(&video_channel));
-  EXPECT_CALL(*media_channel, GetStats(_))
-    .WillOnce(DoAll(SetArgPointee<0>(stats_read),
+  EXPECT_CALL(*media_channel, GetStats(_, _))
+    .WillOnce(DoAll(SetArgPointee<1>(stats_read),
                     Return(true)));
 
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
   // |reports| should contain at least one session report, one track report,
   // and one ssrc report.
@@ -543,8 +554,8 @@
 
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(Return(&video_channel));
-  EXPECT_CALL(*media_channel, GetStats(_))
-    .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read),
+  EXPECT_CALL(*media_channel, GetStats(_, _))
+    .WillRepeatedly(DoAll(SetArgPointee<1>(stats_read),
                           Return(true)));
 
   InitSessionStats(kVcName);
@@ -552,7 +563,7 @@
     .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
                           Return(true)));
 
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
   std::string transport_id = ExtractStatsValue(
       webrtc::StatsReport::kStatsReportTypeSsrc,
@@ -581,7 +592,7 @@
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(ReturnNull());
 
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   webrtc::StatsReports reports;
   stats.GetStats(NULL, &reports);
   const webrtc::StatsReport* remote_report = FindNthReportByType(reports,
@@ -624,11 +635,11 @@
 
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(Return(&video_channel));
-  EXPECT_CALL(*media_channel, GetStats(_))
-    .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read),
+  EXPECT_CALL(*media_channel, GetStats(_, _))
+    .WillRepeatedly(DoAll(SetArgPointee<1>(stats_read),
                           Return(true)));
 
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
   const webrtc::StatsReport* remote_report = FindNthReportByType(reports,
       webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1);
@@ -703,7 +714,7 @@
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(ReturnNull());
 
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
 
   // Check that the local certificate is absent.
@@ -756,7 +767,7 @@
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(ReturnNull());
 
-  stats.UpdateStats();
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
 
   // Check that the local certificate is absent.
@@ -774,4 +785,63 @@
   ASSERT_EQ(kNotFound, remote_certificate_id);
 }
 
+// This test verifies that a remote certificate with an unsupported digest
+// algorithm is correctly ignored.
+TEST_F(StatsCollectorTest, UnsupportedDigestIgnored) {
+  // Build a local certificate.
+  std::string local_der = "This is the local der.";
+  talk_base::FakeSSLCertificate local_cert(DerToPem(local_der));
+
+  // Build a remote certificate with an unsupported digest algorithm.
+  std::string remote_der = "This is somebody else's der.";
+  talk_base::FakeSSLCertificate remote_cert(DerToPem(remote_der));
+  remote_cert.set_digest_algorithm("foobar");
+
+  TestCertificateReports(local_cert, std::vector<std::string>(1, local_der),
+                         remote_cert, std::vector<std::string>());
+}
+
+// Verifies the correct optons are passed to the VideoMediaChannel when using
+// verbose output level.
+TEST_F(StatsCollectorTest, StatsOutputLevelVerbose) {
+  webrtc::StatsCollector stats;  // Implementation under test.
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  cricket::VideoChannel video_channel(talk_base::Thread::Current(),
+      media_engine_, media_channel, &session_, "", false, NULL);
+  stats.set_session(&session_);
+
+  webrtc::StatsReports reports;  // returned values.
+  cricket::VideoMediaInfo stats_read;
+  cricket::BandwidthEstimationInfo bwe;
+  bwe.total_received_propagation_delta_ms = 10;
+  bwe.recent_received_propagation_delta_ms.push_back(100);
+  bwe.recent_received_propagation_delta_ms.push_back(200);
+  bwe.recent_received_packet_group_arrival_time_ms.push_back(1000);
+  bwe.recent_received_packet_group_arrival_time_ms.push_back(2000);
+  stats_read.bw_estimations.push_back(bwe);
+
+  EXPECT_CALL(session_, video_channel())
+    .WillRepeatedly(Return(&video_channel));
+
+  StatsOptions options;
+  options.include_received_propagation_stats = true;
+  EXPECT_CALL(*media_channel, GetStats(
+      Field(&StatsOptions::include_received_propagation_stats, true),
+      _))
+    .WillOnce(DoAll(SetArgPointee<1>(stats_read),
+                    Return(true)));
+
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelDebug);
+  stats.GetStats(NULL, &reports);
+  std::string result = ExtractBweStatsValue(
+      reports, "googReceivedPacketGroupPropagationDeltaSumDebug");
+  EXPECT_EQ("10", result);
+  result = ExtractBweStatsValue(
+      reports, "googReceivedPacketGroupPropagationDeltaDebug");
+  EXPECT_EQ("[100, 200]", result);
+  result = ExtractBweStatsValue(
+      reports, "googReceivedPacketGroupArrivalTimeDebug");
+  EXPECT_EQ("[1000, 2000]", result);
+}
+
 }  // namespace
diff --git a/talk/app/webrtc/statstypes.h b/talk/app/webrtc/statstypes.h
index 9110da3..39441e2 100644
--- a/talk/app/webrtc/statstypes.h
+++ b/talk/app/webrtc/statstypes.h
@@ -53,6 +53,8 @@
 
   void AddValue(const std::string& name, const std::string& value);
   void AddValue(const std::string& name, int64 value);
+  template <typename T>
+  void AddValue(const std::string& name, const std::vector<T>& value);
   void AddBoolean(const std::string& name, bool value);
 
   double timestamp;  // Time since 1970-01-01T00:00:00Z in milliseconds.
@@ -141,6 +143,7 @@
   static const char kStatsValueNameEchoDelayStdDev[];
   static const char kStatsValueNameEchoReturnLoss[];
   static const char kStatsValueNameEchoReturnLossEnhancement[];
+  static const char kStatsValueNameExpandRate[];
   static const char kStatsValueNameFirsReceived[];
   static const char kStatsValueNameFirsSent[];
   static const char kStatsValueNameFrameHeightInput[];
@@ -164,7 +167,6 @@
   static const char kStatsValueNameJitterReceived[];
   static const char kStatsValueNameNacksReceived[];
   static const char kStatsValueNameNacksSent[];
-  static const char kStatsValueNameNetEqExpandRate[];
   static const char kStatsValueNameRtt[];
   static const char kStatsValueNameAvailableSendBandwidth[];
   static const char kStatsValueNameAvailableReceiveBandwidth[];
@@ -189,6 +191,9 @@
   static const char kStatsValueNameRemoteCertificateId[];
   static const char kStatsValueNameLocalCandidateType[];
   static const char kStatsValueNameRemoteCandidateType[];
+  static const char kStatsValueNameRecvPacketGroupArrivalTimeDebug[];
+  static const char kStatsValueNameRecvPacketGroupPropagationDeltaDebug[];
+  static const char kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[];
 };
 
 typedef std::vector<StatsReport> StatsReports;
diff --git a/talk/app/webrtc/webrtc.scons b/talk/app/webrtc/webrtc.scons
index 9b1af3c..dd4bea0 100644
--- a/talk/app/webrtc/webrtc.scons
+++ b/talk/app/webrtc/webrtc.scons
@@ -31,6 +31,7 @@
       'peerconnectionfactory.cc',
       'peerconnection.cc',
       'portallocatorfactory.cc',
+      'remoteaudiosource.cc',
       'roapmessages.cc',
       'roapsession.cc',
       'roapsignaling.cc',
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index 59d7270..ef6af49 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -866,6 +866,18 @@
     voice_channel_->SetChannelOptions(options);
 }
 
+void WebRtcSession::SetAudioPlayoutVolume(uint32 ssrc, double volume) {
+  ASSERT(signaling_thread()->IsCurrent());
+  ASSERT(volume >= 0 && volume <= 10);
+  if (!voice_channel_) {
+    LOG(LS_ERROR) << "SetAudioPlayoutVolume: No audio channel exists.";
+    return;
+  }
+
+  if (!voice_channel_->SetOutputScaling(ssrc, volume, volume))
+    ASSERT(false);
+}
+
 bool WebRtcSession::SetCaptureDevice(uint32 ssrc,
                                      cricket::VideoCapturer* camera) {
   ASSERT(signaling_thread()->IsCurrent());
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index 384ac47..628aa1e 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -165,6 +165,7 @@
   virtual void SetAudioSend(uint32 ssrc, bool enable,
                             const cricket::AudioOptions& options,
                             cricket::AudioRenderer* renderer) OVERRIDE;
+  virtual void SetAudioPlayoutVolume(uint32 ssrc, double volume) OVERRIDE;
 
   // Implements VideoMediaProviderInterface.
   virtual bool SetCaptureDevice(uint32 ssrc,
diff --git a/talk/base/asyncpacketsocket.h b/talk/base/asyncpacketsocket.h
index 29ab55f..d9e1bff 100644
--- a/talk/base/asyncpacketsocket.h
+++ b/talk/base/asyncpacketsocket.h
@@ -28,6 +28,7 @@
 #ifndef TALK_BASE_ASYNCPACKETSOCKET_H_
 #define TALK_BASE_ASYNCPACKETSOCKET_H_
 
+#include "talk/base/buffer.h"
 #include "talk/base/dscp.h"
 #include "talk/base/sigslot.h"
 #include "talk/base/socket.h"
@@ -35,6 +36,29 @@
 
 namespace talk_base {
 
+// This structure holds the info needed to update the packet send time header
+// extension, including the information needed to update the authentication tag
+// after changing the value.
+struct PacketTimeUpdateParams {
+  PacketTimeUpdateParams()
+      : rtp_sendtime_extension_id(-1), srtp_auth_tag_len(-1),
+        srtp_packet_index(-1) {
+  }
+
+  int rtp_sendtime_extension_id;  // extension header id present in packet.
+  Buffer srtp_auth_key;           // Authentication key.
+  int srtp_auth_tag_len;          // Authentication tag length.
+  int64 srtp_packet_index;        // Required for Rtp Packet authentication.
+};
+
+// This structure holds meta information for the packet which is about to send
+// over network.
+struct PacketOptions {
+  PacketOptions() : dscp(DSCP_NO_CHANGE) {}
+  DiffServCodePoint dscp;
+  PacketTimeUpdateParams packet_time_params;
+};
+
 // This structure will have the information about when packet is actually
 // received by socket.
 struct PacketTime {
diff --git a/talk/base/fakenetwork.h b/talk/base/fakenetwork.h
index 3bdc97f..497ff20 100644
--- a/talk/base/fakenetwork.h
+++ b/talk/base/fakenetwork.h
@@ -109,10 +109,12 @@
         prefix_length = kFakeIPv6NetworkPrefixLength;
       }
       IPAddress prefix = TruncateIP(it->ipaddr(), prefix_length);
+      std::string key = MakeNetworkKey(it->hostname(), prefix, prefix_length);
       scoped_ptr<Network> net(new Network(it->hostname(),
                                           it->hostname(),
                                           prefix,
-                                          prefix_length));
+                                          prefix_length,
+                                          key));
       net->AddIP(it->ipaddr());
       networks.push_back(net.release());
     }
diff --git a/talk/base/fakesslidentity.h b/talk/base/fakesslidentity.h
index 203bb83..ee0e0a2 100644
--- a/talk/base/fakesslidentity.h
+++ b/talk/base/fakesslidentity.h
@@ -38,9 +38,12 @@
 
 class FakeSSLCertificate : public talk_base::SSLCertificate {
  public:
-  explicit FakeSSLCertificate(const std::string& data) : data_(data) {}
+  // SHA-1 is the default digest algorithm because it is available in all build
+  // configurations used for unit testing.
+  explicit FakeSSLCertificate(const std::string& data)
+      : data_(data), digest_algorithm_(DIGEST_SHA_1) {}
   explicit FakeSSLCertificate(const std::vector<std::string>& certs)
-      : data_(certs.front()) {
+      : data_(certs.front()), digest_algorithm_(DIGEST_SHA_1) {
     std::vector<std::string>::const_iterator it;
     // Skip certs[0].
     for (it = certs.begin() + 1; it != certs.end(); ++it) {
@@ -58,10 +61,11 @@
     VERIFY(SSLIdentity::PemToDer(kPemTypeCertificate, data_, &der_string));
     der_buffer->SetData(der_string.c_str(), der_string.size());
   }
+  void set_digest_algorithm(const std::string& algorithm) {
+    digest_algorithm_ = algorithm;
+  }
   virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const {
-    // SHA-1 is chosen because it is available in all build configurations
-    // used for unit testing.
-    *algorithm = DIGEST_SHA_1;
+    *algorithm = digest_algorithm_;
     return true;
   }
   virtual bool ComputeDigest(const std::string &algorithm,
@@ -86,6 +90,7 @@
   }
   std::string data_;
   std::vector<FakeSSLCertificate> certs_;
+  std::string digest_algorithm_;
 };
 
 class FakeSSLIdentity : public talk_base::SSLIdentity {
diff --git a/talk/base/maccocoasocketserver.h b/talk/base/maccocoasocketserver.h
index f4aeb33..51dc749 100644
--- a/talk/base/maccocoasocketserver.h
+++ b/talk/base/maccocoasocketserver.h
@@ -54,6 +54,8 @@
  private:
   MacCocoaSocketServerHelper* helper_;
   NSTimer* timer_;  // Weak.
+  // The count of how many times we're inside the NSApplication main loop.
+  int run_count_;
 
   DISALLOW_EVIL_CONSTRUCTORS(MacCocoaSocketServer);
 };
diff --git a/talk/base/maccocoasocketserver.mm b/talk/base/maccocoasocketserver.mm
index bf308e6..8257e38 100644
--- a/talk/base/maccocoasocketserver.mm
+++ b/talk/base/maccocoasocketserver.mm
@@ -53,6 +53,25 @@
 - (void)timerFired:(NSTimer*)timer {
   socketServer_->WakeUp();
 }
+
+- (void)breakMainloop {
+  [NSApp stop:self];
+  // NSApp stop only exits after finishing processing of the
+  // current event.  Since we're potentially in a timer callback
+  // and not an NSEvent handler, we need to trigger a dummy one
+  // and turn the loop over.  We may be able to skip this if we're
+  // on the ss' thread and not inside the app loop already.
+  NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined
+                                      location:NSMakePoint(0,0)
+                                 modifierFlags:0
+                                     timestamp:0
+                                  windowNumber:0
+                                       context:nil
+                                       subtype:0
+                                         data1:0
+                                         data2:0];
+  [NSApp postEvent:event atStart:NO];
+}
 @end
 
 namespace talk_base {
@@ -60,6 +79,7 @@
 MacCocoaSocketServer::MacCocoaSocketServer() {
   helper_ = [[MacCocoaSocketServerHelper alloc] initWithSocketServer:this];
   timer_ = nil;
+  run_count_ = 0;
 
   // Initialize the shared NSApplication
   [NSApplication sharedApplication];
@@ -71,12 +91,19 @@
   [helper_ release];
 }
 
+// ::Wait is reentrant, for example when blocking on another thread while
+// responding to I/O. Calls to [NSApp] MUST be made from the main thread
+// only!
 bool MacCocoaSocketServer::Wait(int cms, bool process_io) {
   talk_base::ScopedAutoreleasePool pool;
   if (!process_io && cms == 0) {
     // No op.
     return true;
   }
+  if ([NSApp isRunning]) {
+    // Only allow reentrant waiting if we're in a blocking send.
+    ASSERT(!process_io && cms == kForever);
+  }
 
   if (!process_io) {
     // No way to listen to common modes and not get socket events, unless
@@ -96,7 +123,9 @@
   }
 
   // Run until WakeUp is called, which will call stop and exit this loop.
+  run_count_++;
   [NSApp run];
+  run_count_--;
 
   if (!process_io) {
     // Reenable them.  Hopefully this won't cause spurious callbacks or
@@ -107,28 +136,22 @@
   return true;
 }
 
+// Can be called from any thread.  Post a message back to the main thread to
+// break out of the NSApp loop.
 void MacCocoaSocketServer::WakeUp() {
-  // Timer has either fired or shortcutted.
-  [timer_ invalidate];
-  [timer_ release];
-  timer_ = nil;
-  [NSApp stop:nil];
+  if (timer_ != nil) {
+    [timer_ invalidate];
+    [timer_ release];
+    timer_ = nil;
+  }
 
-  // NSApp stop only exits after finishing processing of the
-  // current event.  Since we're potentially in a timer callback
-  //  and not an NSEvent handler, we need to trigger a dummy one
-  // and turn the loop over.  We may be able to skip this if we're
-  // on the ss' thread and not inside the app loop already.
-  NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
-                                      location:NSMakePoint(0,0)
-                                 modifierFlags:0
-                                     timestamp:0
-                                  windowNumber:0
-                                       context:nil
-                                       subtype:1
-                                         data1:1
-                                         data2:1];
-  [NSApp postEvent:event atStart:YES];
+  // [NSApp isRunning] returns unexpected results when called from another
+  // thread.  Maintain our own count of how many times to break the main loop.
+  if (run_count_ > 0) {
+    [helper_ performSelectorOnMainThread:@selector(breakMainloop)
+                              withObject:nil
+                           waitUntilDone:false];
+  }
 }
 
 }  // namespace talk_base
diff --git a/talk/base/network.cc b/talk/base/network.cc
index 00b04c9..95a2e4d 100644
--- a/talk/base/network.cc
+++ b/talk/base/network.cc
@@ -79,16 +79,7 @@
 // Fetch list of networks every two seconds.
 const int kNetworksUpdateIntervalMs = 2000;
 
-
-// Makes a string key for this network. Used in the network manager's maps.
-// Network objects are keyed on interface name, network prefix and the
-// length of that prefix.
-std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix,
-                           int prefix_length) {
-  std::ostringstream ost;
-  ost << name << "%" << prefix.ToString() << "/" << prefix_length;
-  return ost.str();
-}
+const int kHighestNetworkPreference = 127;
 
 bool CompareNetworks(const Network* a, const Network* b) {
   if (a->prefix_length() == b->prefix_length()) {
@@ -99,9 +90,36 @@
   return a->name() < b->name();
 }
 
+bool SortNetworks(const Network* a, const Network* b) {
+  // Network types will be preferred above everything else while sorting
+  // Networks.
+
+  // Networks are sorted first by type.
+  if (a->type() != b->type()) {
+    return a->type() < b->type();
+  }
+
+  // After type, networks are sorted by IP address precedence values
+  // from RFC 3484-bis
+  if (IPAddressPrecedence(a->ip()) != IPAddressPrecedence(b->ip())) {
+    return IPAddressPrecedence(a->ip()) > IPAddressPrecedence(b->ip());
+  }
+
+  // TODO(mallinath) - Add VPN and Link speed conditions while sorting.
+
+  // Networks are sorted last by key.
+  return a->key() > b->key();
+}
 
 }  // namespace
 
+std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix,
+                           int prefix_length) {
+  std::ostringstream ost;
+  ost << name << "%" << prefix.ToString() << "/" << prefix_length;
+  return ost.str();
+}
+
 NetworkManager::NetworkManager() {
 }
 
@@ -180,6 +198,29 @@
     }
   }
   networks_ = merged_list;
+
+  // If the network lists changes, we resort it.
+  if (changed) {
+    std::sort(networks_.begin(), networks_.end(), SortNetworks);
+    // Now network interfaces are sorted, we should set the preference value
+    // for each of the interfaces we are planning to use.
+    // Preference order of network interfaces might have changed from previous
+    // sorting due to addition of higher preference network interface.
+    // Since we have already sorted the network interfaces based on our
+    // requirements, we will just assign a preference value starting with 127,
+    // in decreasing order.
+    int pref = kHighestNetworkPreference;
+    for (NetworkList::const_iterator iter = networks_.begin();
+         iter != networks_.end(); ++iter) {
+      (*iter)->set_preference(pref);
+      if (pref > 0) {
+        --pref;
+      } else {
+        LOG(LS_ERROR) << "Too many network interfaces to handle!";
+        break;
+      }
+    }
+  }
 }
 
 BasicNetworkManager::BasicNetworkManager()
@@ -240,6 +281,7 @@
         continue;
       }
     }
+
     int prefix_length = CountIPMaskBits(mask);
     prefix = TruncateIP(ip, prefix_length);
     std::string key = MakeNetworkKey(std::string(cursor->ifa_name),
@@ -249,7 +291,8 @@
       scoped_ptr<Network> network(new Network(cursor->ifa_name,
                                               cursor->ifa_name,
                                               prefix,
-                                              prefix_length));
+                                              prefix_length,
+                                              key));
       network->set_scope_id(scope_id);
       network->AddIP(ip);
       bool ignored = ((cursor->ifa_flags & IFF_LOOPBACK) ||
@@ -386,6 +429,7 @@
             continue;
           }
         }
+
         IPAddress prefix;
         int prefix_length = GetPrefix(prefixlist, ip, &prefix);
         std::string key = MakeNetworkKey(name, prefix, prefix_length);
@@ -394,7 +438,8 @@
           scoped_ptr<Network> network(new Network(name,
                                                   description,
                                                   prefix,
-                                                  prefix_length));
+                                                  prefix_length,
+                                                  key));
           network->set_scope_id(scope_id);
           network->AddIP(ip);
           bool ignore = ((adapter_addrs->IfType == IF_TYPE_SOFTWARE_LOOPBACK) ||
@@ -562,11 +607,20 @@
 }
 
 Network::Network(const std::string& name, const std::string& desc,
+                 const IPAddress& prefix, int prefix_length,
+                 const std::string& key)
+    : name_(name), description_(desc), prefix_(prefix),
+      prefix_length_(prefix_length), key_(key), scope_id_(0), ignored_(false),
+      uniform_numerator_(0), uniform_denominator_(0), exponential_numerator_(0),
+      exponential_denominator_(0), type_(ADAPTER_TYPE_UNKNOWN), preference_(0) {
+}
+
+Network::Network(const std::string& name, const std::string& desc,
                  const IPAddress& prefix, int prefix_length)
     : name_(name), description_(desc), prefix_(prefix),
       prefix_length_(prefix_length), scope_id_(0), ignored_(false),
       uniform_numerator_(0), uniform_denominator_(0), exponential_numerator_(0),
-      exponential_denominator_(0) {
+      exponential_denominator_(0), type_(ADAPTER_TYPE_UNKNOWN), preference_(0) {
 }
 
 std::string Network::ToString() const {
@@ -600,4 +654,5 @@
   ips_ = ips;
   return changed;
 }
+
 }  // namespace talk_base
diff --git a/talk/base/network.h b/talk/base/network.h
index 63f3e73..75a443b 100644
--- a/talk/base/network.h
+++ b/talk/base/network.h
@@ -45,9 +45,23 @@
 namespace talk_base {
 
 class Network;
-class NetworkSession;
 class Thread;
 
+enum AdapterType {
+  // This enum resembles the one in Chromium net::ConnectionType.
+  ADAPTER_TYPE_UNKNOWN = 0,
+  ADAPTER_TYPE_ETHERNET = 1,
+  ADAPTER_TYPE_WIFI = 2,
+  ADAPTER_TYPE_CELLULAR = 3,
+  ADAPTER_TYPE_VPN = 4
+};
+
+// Makes a string key for this network. Used in the network manager's maps.
+// Network objects are keyed on interface name, network prefix and the
+// length of that prefix.
+std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix,
+                           int prefix_length);
+
 // Generic network manager interface. It provides list of local
 // networks.
 class NetworkManager {
@@ -168,7 +182,12 @@
 // Represents a Unix-type network interface, with a name and single address.
 class Network {
  public:
-  Network() : prefix_(INADDR_ANY), scope_id_(0) {}
+  Network() : prefix_(INADDR_ANY), scope_id_(0),
+              type_(ADAPTER_TYPE_UNKNOWN) {}
+  Network(const std::string& name, const std::string& description,
+          const IPAddress& prefix, int prefix_length,
+          const std::string& key);
+
   Network(const std::string& name, const std::string& description,
           const IPAddress& prefix, int prefix_length);
 
@@ -184,6 +203,10 @@
   // Returns the length, in bits, of this network's prefix.
   int prefix_length() const { return prefix_length_; }
 
+  // |key_| has unique value per network interface. Used in sorting network
+  // interfaces. Key is derived from interface name and it's prefix.
+  std::string key() const { return key_; }
+
   // Returns the Network's current idea of the 'best' IP it has.
   // 'Best' currently means the first one added.
   // TODO: We should be preferring temporary addresses.
@@ -215,27 +238,32 @@
   bool ignored() const { return ignored_; }
   void set_ignored(bool ignored) { ignored_ = ignored; }
 
+  AdapterType type() const { return type_; }
+  int preference() const { return preference_; }
+  void set_preference(int preference) { preference_ = preference; }
+
   // Debugging description of this network
   std::string ToString() const;
 
  private:
-  typedef std::vector<NetworkSession*> SessionList;
-
   std::string name_;
   std::string description_;
   IPAddress prefix_;
   int prefix_length_;
+  std::string key_;
   std::vector<IPAddress> ips_;
   int scope_id_;
   bool ignored_;
-  SessionList sessions_;
   double uniform_numerator_;
   double uniform_denominator_;
   double exponential_numerator_;
   double exponential_denominator_;
+  AdapterType type_;
+  int preference_;
 
   friend class NetworkManager;
 };
+
 }  // namespace talk_base
 
 #endif  // TALK_BASE_NETWORK_H_
diff --git a/talk/base/network_unittest.cc b/talk/base/network_unittest.cc
index e11e78d..85aa2f8 100644
--- a/talk/base/network_unittest.cc
+++ b/talk/base/network_unittest.cc
@@ -527,6 +527,33 @@
   }
 }
 
+TEST_F(NetworkTest, TestNetworkListSorting) {
+  BasicNetworkManager manager;
+  Network ipv4_network1("test_eth0", "Test Network Adapter 1",
+                        IPAddress(0x12345600U), 24);
+  ipv4_network1.AddIP(IPAddress(0x12345600U));
+
+  IPAddress ip;
+  IPAddress prefix;
+  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:abcd:efab:cdef", &ip));
+  prefix = TruncateIP(ip, 64);
+  Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 2",
+                                       prefix, 64);
+  ipv6_eth1_publicnetwork1_ip1.AddIP(ip);
+
+  NetworkManager::NetworkList list;
+  list.push_back(new Network(ipv4_network1));
+  list.push_back(new Network(ipv6_eth1_publicnetwork1_ip1));
+  Network* net1 = list[0];
+  Network* net2 = list[1];
+
+  bool changed = false;
+  MergeNetworkList(manager, list, &changed);
+  ASSERT_TRUE(changed);
+  // After sorting IPv6 network should be higher order than IPv4 networks.
+  EXPECT_TRUE(net1->preference() < net2->preference());
+}
+
 #if defined(POSIX)
 // Verify that we correctly handle interfaces with no address.
 TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) {
diff --git a/talk/base/openssl.h b/talk/base/openssl.h
new file mode 100644
index 0000000..e2cfd2b
--- /dev/null
+++ b/talk/base/openssl.h
@@ -0,0 +1,37 @@
+/*
+ * libjingle
+ * Copyright 2013, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_BASE_OPENSSL_H_
+#define TALK_BASE_OPENSSL_H_
+
+#include <openssl/ssl.h>
+
+#if (OPENSSL_VERSION_NUMBER < 0x10001000L)
+#error OpenSSL is older than 1.0.1, which is the minimum supported version.
+#endif
+
+#endif  // TALK_BASE_OPENSSL_H_
diff --git a/talk/base/openssladapter.cc b/talk/base/openssladapter.cc
index 95d5a1a..9e6fe72 100644
--- a/talk/base/openssladapter.cc
+++ b/talk/base/openssladapter.cc
@@ -41,7 +41,6 @@
 #include <openssl/err.h>
 #include <openssl/opensslv.h>
 #include <openssl/rand.h>
-#include <openssl/ssl.h>
 #include <openssl/x509v3.h>
 
 #if HAVE_CONFIG_H
@@ -50,6 +49,7 @@
 
 #include "talk/base/common.h"
 #include "talk/base/logging.h"
+#include "talk/base/openssl.h"
 #include "talk/base/sslroots.h"
 #include "talk/base/stringutils.h"
 
@@ -688,11 +688,7 @@
     int extension_nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
 
     if (extension_nid == NID_subject_alt_name) {
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
       const X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension);
-#else
-      X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension);
-#endif
       if (!meth)
         break;
 
@@ -703,12 +699,8 @@
       // See http://readlist.com/lists/openssl.org/openssl-users/0/4761.html.
       unsigned char* ext_value_data = extension->value->data;
 
-#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
       const unsigned char **ext_value_data_ptr =
           (const_cast<const unsigned char **>(&ext_value_data));
-#else
-      unsigned char **ext_value_data_ptr = &ext_value_data;
-#endif
 
       if (meth->it) {
         ext_str = ASN1_item_d2i(NULL, ext_value_data_ptr,
diff --git a/talk/base/openssldigest.cc b/talk/base/openssldigest.cc
index 3d9276d..3d0d227 100644
--- a/talk/base/openssldigest.cc
+++ b/talk/base/openssldigest.cc
@@ -30,6 +30,7 @@
 #include "talk/base/openssldigest.h"
 
 #include "talk/base/common.h"
+#include "talk/base/openssl.h"
 
 namespace talk_base {
 
@@ -78,7 +79,6 @@
     md = EVP_md5();
   } else if (algorithm == DIGEST_SHA_1) {
     md = EVP_sha1();
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
   } else if (algorithm == DIGEST_SHA_224) {
     md = EVP_sha224();
   } else if (algorithm == DIGEST_SHA_256) {
@@ -87,7 +87,6 @@
     md = EVP_sha384();
   } else if (algorithm == DIGEST_SHA_512) {
     md = EVP_sha512();
-#endif
   } else {
     return false;
   }
@@ -108,7 +107,6 @@
     *algorithm = DIGEST_MD5;
   } else if (md_type == NID_sha1) {
     *algorithm = DIGEST_SHA_1;
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
   } else if (md_type == NID_sha224) {
     *algorithm = DIGEST_SHA_224;
   } else if (md_type == NID_sha256) {
@@ -117,7 +115,6 @@
     *algorithm = DIGEST_SHA_384;
   } else if (md_type == NID_sha512) {
     *algorithm = DIGEST_SHA_512;
-#endif
   } else {
     algorithm->clear();
     return false;
diff --git a/talk/base/opensslidentity.cc b/talk/base/opensslidentity.cc
index 33b02dd..bd361d1 100644
--- a/talk/base/opensslidentity.cc
+++ b/talk/base/opensslidentity.cc
@@ -32,7 +32,6 @@
 // Must be included first before openssl headers.
 #include "talk/base/win32.h"  // NOLINT
 
-#include <openssl/ssl.h>
 #include <openssl/bio.h>
 #include <openssl/err.h>
 #include <openssl/pem.h>
@@ -43,6 +42,7 @@
 #include "talk/base/checks.h"
 #include "talk/base/helpers.h"
 #include "talk/base/logging.h"
+#include "talk/base/openssl.h"
 #include "talk/base/openssldigest.h"
 
 namespace talk_base {
@@ -66,15 +66,6 @@
 static EVP_PKEY* MakeKey() {
   LOG(LS_INFO) << "Making key pair";
   EVP_PKEY* pkey = EVP_PKEY_new();
-#if OPENSSL_VERSION_NUMBER < 0x00908000l
-  // Only RSA_generate_key is available. Use that.
-  RSA* rsa = RSA_generate_key(KEY_LENGTH, 0x10001, NULL, NULL);
-  if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
-    EVP_PKEY_free(pkey);
-    RSA_free(rsa);
-    return NULL;
-  }
-#else
   // RSA_generate_key is deprecated. Use _ex version.
   BIGNUM* exponent = BN_new();
   RSA* rsa = RSA_new();
@@ -89,7 +80,6 @@
   }
   // ownership of rsa struct was assigned, don't free it.
   BN_free(exponent);
-#endif
   LOG(LS_INFO) << "Returning key pair";
   return pkey;
 }
diff --git a/talk/base/opensslstreamadapter.cc b/talk/base/opensslstreamadapter.cc
index 576b4245..cafef92 100644
--- a/talk/base/opensslstreamadapter.cc
+++ b/talk/base/opensslstreamadapter.cc
@@ -37,7 +37,6 @@
 #include <openssl/crypto.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
-#include <openssl/ssl.h>
 #include <openssl/x509v3.h>
 
 #include <vector>
@@ -45,6 +44,7 @@
 #include "talk/base/common.h"
 #include "talk/base/logging.h"
 #include "talk/base/stream.h"
+#include "talk/base/openssl.h"
 #include "talk/base/openssladapter.h"
 #include "talk/base/openssldigest.h"
 #include "talk/base/opensslidentity.h"
@@ -53,15 +53,6 @@
 
 namespace talk_base {
 
-#if (OPENSSL_VERSION_NUMBER >= 0x10001000L)
-#define HAVE_DTLS_SRTP
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
-#define HAVE_DTLS
-#endif
-
-#ifdef HAVE_DTLS_SRTP
 // SRTP cipher suite table
 struct SrtpCipherMapEntry {
   const char* external_name;
@@ -74,7 +65,6 @@
   {"AES_CM_128_HMAC_SHA1_32", "SRTP_AES128_CM_SHA1_32"},
   {NULL, NULL}
 };
-#endif
 
 //////////////////////////////////////////////////////////////////////
 // StreamBIO
@@ -248,7 +238,6 @@
                                                 bool use_context,
                                                 uint8* result,
                                                 size_t result_len) {
-#ifdef HAVE_DTLS_SRTP
   int i;
 
   i = SSL_export_keying_material(ssl_, result, result_len,
@@ -260,9 +249,6 @@
     return false;
 
   return true;
-#else
-  return false;
-#endif
 }
 
 bool OpenSSLStreamAdapter::SetDtlsSrtpCiphers(
@@ -272,7 +258,6 @@
   if (state_ != SSL_NONE)
     return false;
 
-#ifdef HAVE_DTLS_SRTP
   for (std::vector<std::string>::const_iterator cipher = ciphers.begin();
        cipher != ciphers.end(); ++cipher) {
     bool found = false;
@@ -298,13 +283,9 @@
 
   srtp_ciphers_ = internal_ciphers;
   return true;
-#else
-  return false;
-#endif
 }
 
 bool OpenSSLStreamAdapter::GetDtlsSrtpCipher(std::string* cipher) {
-#ifdef HAVE_DTLS_SRTP
   ASSERT(state_ == SSL_CONNECTED);
   if (state_ != SSL_CONNECTED)
     return false;
@@ -326,9 +307,6 @@
   ASSERT(false);  // This should never happen
 
   return false;
-#else
-  return false;
-#endif
 }
 
 int OpenSSLStreamAdapter::StartSSLWithServer(const char* server_name) {
@@ -665,14 +643,12 @@
 
     case SSL_ERROR_WANT_READ: {
         LOG(LS_VERBOSE) << " -- error want read";
-#ifdef HAVE_DTLS
         struct timeval timeout;
         if (DTLSv1_get_timeout(ssl_, &timeout)) {
           int delay = timeout.tv_sec * 1000 + timeout.tv_usec/1000;
 
           Thread::Current()->PostDelayed(delay, this, MSG_TIMEOUT, 0);
         }
-#endif
       }
       break;
 
@@ -727,9 +703,7 @@
   // Process our own messages and then pass others to the superclass
   if (MSG_TIMEOUT == msg->message_id) {
     LOG(LS_INFO) << "DTLS timeout expired";
-#ifdef HAVE_DTLS
     DTLSv1_handle_timeout(ssl_);
-#endif
     ContinueSSL();
   } else {
     StreamInterface::OnMessage(msg);
@@ -740,19 +714,11 @@
   SSL_CTX *ctx = NULL;
 
   if (role_ == SSL_CLIENT) {
-#ifdef HAVE_DTLS
     ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ?
         DTLSv1_client_method() : TLSv1_client_method());
-#else
-    ctx = SSL_CTX_new(TLSv1_client_method());
-#endif
   } else {
-#ifdef HAVE_DTLS
     ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ?
         DTLSv1_server_method() : TLSv1_server_method());
-#else
-    ctx = SSL_CTX_new(TLSv1_server_method());
-#endif
   }
   if (ctx == NULL)
     return NULL;
@@ -771,14 +737,12 @@
   SSL_CTX_set_verify_depth(ctx, 4);
   SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
 
-#ifdef HAVE_DTLS_SRTP
   if (!srtp_ciphers_.empty()) {
     if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_ciphers_.c_str())) {
       SSL_CTX_free(ctx);
       return NULL;
     }
   }
-#endif
 
   return ctx;
 }
@@ -852,27 +816,15 @@
 }
 
 bool OpenSSLStreamAdapter::HaveDtls() {
-#ifdef HAVE_DTLS
   return true;
-#else
-  return false;
-#endif
 }
 
 bool OpenSSLStreamAdapter::HaveDtlsSrtp() {
-#ifdef HAVE_DTLS_SRTP
   return true;
-#else
-  return false;
-#endif
 }
 
 bool OpenSSLStreamAdapter::HaveExporter() {
-#ifdef HAVE_DTLS_SRTP
   return true;
-#else
-  return false;
-#endif
 }
 
 }  // namespace talk_base
diff --git a/talk/base/physicalsocketserver.cc b/talk/base/physicalsocketserver.cc
index d4a4b1a..07a9d4b 100644
--- a/talk/base/physicalsocketserver.cc
+++ b/talk/base/physicalsocketserver.cc
@@ -541,6 +541,8 @@
       case OPT_DSCP:
         LOG(LS_WARNING) << "Socket::OPT_DSCP not supported.";
         return -1;
+      case OPT_RTP_SENDTIME_EXTN_ID:
+        return -1;  // No logging is necessary as this not a OS socket option.
       default:
         ASSERT(false);
         return -1;
diff --git a/talk/base/socket.h b/talk/base/socket.h
index 47f5522..590645f 100644
--- a/talk/base/socket.h
+++ b/talk/base/socket.h
@@ -185,7 +185,10 @@
     OPT_SNDBUF,      // send buffer size
     OPT_NODELAY,     // whether Nagle algorithm is enabled
     OPT_IPV6_V6ONLY, // Whether the socket is IPv6 only.
-    OPT_DSCP         // DSCP code
+    OPT_DSCP,        // DSCP code
+    OPT_RTP_SENDTIME_EXTN_ID,  // This is a non-traditional socket option param.
+                               // This is specific to libjingle and will be used
+                               // if SendTime option is needed at socket level.
   };
   virtual int GetOption(Option opt, int* value) = 0;
   virtual int SetOption(Option opt, int value) = 0;
diff --git a/talk/base/thread_unittest.cc b/talk/base/thread_unittest.cc
index 3a9103f..728e321 100644
--- a/talk/base/thread_unittest.cc
+++ b/talk/base/thread_unittest.cc
@@ -339,7 +339,7 @@
   Thread* expected_thread_;
 };
 
-TEST_F(AsyncInvokeTest, FireAndForget) {
+TEST_F(AsyncInvokeTest, DISABLED_FireAndForget) {
   AsyncInvoker invoker;
   // Create and start the thread.
   Thread thread;
@@ -350,7 +350,7 @@
   EXPECT_TRUE_WAIT(called, kWaitTimeout);
 }
 
-TEST_F(AsyncInvokeTest, WithCallback) {
+TEST_F(AsyncInvokeTest, DISABLED_WithCallback) {
   AsyncInvoker invoker;
   // Create and start the thread.
   Thread thread;
@@ -379,7 +379,7 @@
   EXPECT_EQ(0, int_value_);
 }
 
-TEST_F(AsyncInvokeTest, CancelCallingThread) {
+TEST_F(AsyncInvokeTest, DISABLED_CancelCallingThread) {
   AsyncInvoker invoker;
   { // Create and start the thread.
     Thread thread;
@@ -396,7 +396,7 @@
   EXPECT_EQ(0, int_value_);
 }
 
-TEST_F(AsyncInvokeTest, KillInvokerBeforeExecute) {
+TEST_F(AsyncInvokeTest, DISABLED_KillInvokerBeforeExecute) {
   Thread thread;
   thread.Start();
   {
@@ -413,7 +413,7 @@
   EXPECT_EQ(0, int_value_);
 }
 
-TEST_F(AsyncInvokeTest, Flush) {
+TEST_F(AsyncInvokeTest, DISABLED_Flush) {
   AsyncInvoker invoker;
   bool flag1 = false;
   bool flag2 = false;
@@ -431,7 +431,7 @@
   EXPECT_TRUE(flag2);
 }
 
-TEST_F(AsyncInvokeTest, FlushWithIds) {
+TEST_F(AsyncInvokeTest, DISABLED_FlushWithIds) {
   AsyncInvoker invoker;
   bool flag1 = false;
   bool flag2 = false;
diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp
index 0ed5c64..73c8a04 100755
--- a/talk/libjingle.gyp
+++ b/talk/libjingle.gyp
@@ -715,6 +715,7 @@
           'conditions': [
             ['OS!="ios"', {
               'sources': [
+                'base/openssl.h',
                 'base/openssladapter.cc',
                 'base/openssladapter.h',
                 'base/openssldigest.cc',
@@ -1175,6 +1176,8 @@
         'app/webrtc/portallocatorfactory.cc',
         'app/webrtc/portallocatorfactory.h',
         'app/webrtc/proxy.h',
+        'app/webrtc/remoteaudiosource.cc',
+        'app/webrtc/remoteaudiosource.h',
         'app/webrtc/remotevideocapturer.cc',
         'app/webrtc/remotevideocapturer.h',
         'app/webrtc/sctputils.cc',
diff --git a/talk/media/base/fakemediaengine.h b/talk/media/base/fakemediaengine.h
index 28facca..86a0dbf 100644
--- a/talk/media/base/fakemediaengine.h
+++ b/talk/media/base/fakemediaengine.h
@@ -560,7 +560,8 @@
     return true;
   }
 
-  virtual bool GetStats(VideoMediaInfo* info) { return false; }
+  virtual bool GetStats(const StatsOptions& options,
+                        VideoMediaInfo* info) { return false; }
   virtual bool SendIntraFrame() {
     sent_intra_frame_ = true;
     return true;
diff --git a/talk/media/base/filemediaengine.h b/talk/media/base/filemediaengine.h
index be196ae..6656cdf 100644
--- a/talk/media/base/filemediaengine.h
+++ b/talk/media/base/filemediaengine.h
@@ -297,7 +297,9 @@
   virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer) {
     return false;
   }
-  virtual bool GetStats(VideoMediaInfo* info) { return true; }
+  virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info) {
+    return true;
+  }
   virtual bool SendIntraFrame() { return false; }
   virtual bool RequestIntraFrame() { return false; }
 
diff --git a/talk/media/base/hybridvideoengine.cc b/talk/media/base/hybridvideoengine.cc
index f855417..8e992f0 100644
--- a/talk/media/base/hybridvideoengine.cc
+++ b/talk/media/base/hybridvideoengine.cc
@@ -273,10 +273,11 @@
       active_channel_->RequestIntraFrame();
 }
 
-bool HybridVideoMediaChannel::GetStats(VideoMediaInfo* info) {
+bool HybridVideoMediaChannel::GetStats(
+    const StatsOptions& options, VideoMediaInfo* info) {
   // TODO(juberti): Ensure that returning no stats until SetSendCodecs is OK.
   return active_channel_ &&
-      active_channel_->GetStats(info);
+      active_channel_->GetStats(options, info);
 }
 
 void HybridVideoMediaChannel::OnPacketReceived(
diff --git a/talk/media/base/hybridvideoengine.h b/talk/media/base/hybridvideoengine.h
index 9f91920..8cfb884 100644
--- a/talk/media/base/hybridvideoengine.h
+++ b/talk/media/base/hybridvideoengine.h
@@ -86,7 +86,7 @@
   virtual bool SendIntraFrame();
   virtual bool RequestIntraFrame();
 
-  virtual bool GetStats(VideoMediaInfo* info);
+  virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info);
 
   virtual void OnPacketReceived(talk_base::Buffer* packet,
                                 const talk_base::PacketTime& packet_time);
diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h
index ef87c9e..239798c 100644
--- a/talk/media/base/mediachannel.h
+++ b/talk/media/base/mediachannel.h
@@ -881,7 +881,8 @@
         actual_enc_bitrate(0),
         retransmit_bitrate(0),
         transmit_bitrate(0),
-        bucket_delay(0) {
+        bucket_delay(0),
+        total_received_propagation_delta_ms(0) {
   }
 
   int available_send_bandwidth;
@@ -891,6 +892,11 @@
   int retransmit_bitrate;
   int transmit_bitrate;
   int bucket_delay;
+  // The following stats are only valid when
+  // StatsOptions::include_received_propagation_stats is true.
+  int total_received_propagation_delta_ms;
+  std::vector<int> recent_received_propagation_delta_ms;
+  std::vector<int64> recent_received_packet_group_arrival_time_ms;
 };
 
 struct VoiceMediaInfo {
@@ -922,6 +928,12 @@
   std::vector<DataReceiverInfo> receivers;
 };
 
+struct StatsOptions {
+  StatsOptions() : include_received_propagation_stats(false) {}
+
+  bool include_received_propagation_stats;
+};
+
 class VoiceMediaChannel : public MediaChannel {
  public:
   enum Error {
@@ -1040,7 +1052,12 @@
   // |capturer|. If |ssrc| is non zero create a new stream with |ssrc| as SSRC.
   virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer) = 0;
   // Gets quality stats for the channel.
-  virtual bool GetStats(VideoMediaInfo* info) = 0;
+  virtual bool GetStats(const StatsOptions& options, VideoMediaInfo* info) = 0;
+  // This is needed for MediaMonitor to use the same template for voice, video
+  // and data MediaChannels.
+  bool GetStats(VideoMediaInfo* info) {
+    return GetStats(StatsOptions(), info);
+  }
 
   // Send an intra frame to the receivers.
   virtual bool SendIntraFrame() = 0;
diff --git a/talk/media/base/videoadapter.cc b/talk/media/base/videoadapter.cc
index 29be805..5b53d07 100644
--- a/talk/media/base/videoadapter.cc
+++ b/talk/media/base/videoadapter.cc
@@ -183,7 +183,8 @@
   output_format_.interval = talk_base::_max(
       output_format_.interval, input_format_.interval);
   if (old_input_interval != input_format_.interval) {
-    LOG(LS_INFO) << "VAdapt Input Interval: " << input_format_.interval;
+    LOG(LS_INFO) << "VAdapt input interval changed from "
+      << old_input_interval << " to " << input_format_.interval;
   }
 }
 
@@ -218,7 +219,8 @@
   output_format_.interval = talk_base::_max(
       output_format_.interval, input_format_.interval);
   if (old_output_interval != output_format_.interval) {
-    LOG(LS_INFO) << "VAdapt Output Interval: " << output_format_.interval;
+    LOG(LS_INFO) << "VAdapt output interval changed from "
+      << old_output_interval << " to " << output_format_.interval;
   }
 }
 
@@ -283,16 +285,12 @@
   }
   if (should_drop) {
     // Show VAdapt log every 90 frames dropped. (3 seconds)
-    // TODO(fbarchard): Consider GetLogSeverity() to change interval to less
-    // for LS_VERBOSE and more for LS_INFO.
-    bool show = (frames_in_ - frames_out_) % 90 == 0;
-
-    if (show) {
+    if ((frames_in_ - frames_out_) % 90 == 0) {
       // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
       // in default calls.
-      LOG(LS_INFO) << "VAdapt Drop Frame: " << frames_scaled_
-                   << " / " << frames_out_
-                   << " / " << frames_in_
+      LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_
+                   << " / out " << frames_out_
+                   << " / in " << frames_in_
                    << " Changes: " << adaption_changes_
                    << " Input: " << in_frame->GetWidth()
                    << "x" << in_frame->GetHeight()
@@ -344,9 +342,9 @@
   if (show) {
     // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
     // in default calls.
-    LOG(LS_INFO) << "VAdapt Frame: " << frames_scaled_
-                 << " / " << frames_out_
-                 << " / " << frames_in_
+    LOG(LS_INFO) << "VAdapt Frame: scaled " << frames_scaled_
+                 << " / out " << frames_out_
+                 << " / in " << frames_in_
                  << " Changes: " << adaption_changes_
                  << " Input: " << in_frame->GetWidth()
                  << "x" << in_frame->GetHeight()
diff --git a/talk/media/base/videoengine_unittest.h b/talk/media/base/videoengine_unittest.h
index e1c4a7d..6c782bd 100644
--- a/talk/media/base/videoengine_unittest.h
+++ b/talk/media/base/videoengine_unittest.h
@@ -781,7 +781,7 @@
   void GetStats() {
     SendAndReceive(DefaultCodec());
     cricket::VideoMediaInfo info;
-    EXPECT_TRUE(channel_->GetStats(&info));
+    EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info));
 
     ASSERT_EQ(1U, info.senders.size());
     // TODO(whyuan): bytes_sent and bytes_rcvd are different. Are both payload?
@@ -839,7 +839,7 @@
     EXPECT_FRAME_ON_RENDERER_WAIT(
         renderer2, 1, DefaultCodec().width, DefaultCodec().height, kTimeout);
     cricket::VideoMediaInfo info;
-    EXPECT_TRUE(channel_->GetStats(&info));
+    EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info));
 
     ASSERT_EQ(1U, info.senders.size());
     // TODO(whyuan): bytes_sent and bytes_rcvd are different. Are both payload?
@@ -912,7 +912,7 @@
 
     // Get stats, and make sure they are correct for two senders.
     cricket::VideoMediaInfo info;
-    EXPECT_TRUE(channel_->GetStats(&info));
+    EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info));
     ASSERT_EQ(2U, info.senders.size());
     EXPECT_EQ(NumRtpPackets(),
         info.senders[0].packets_sent + info.senders[1].packets_sent);
diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h
index 0b07925..315c69c 100644
--- a/talk/media/webrtc/fakewebrtcvideoengine.h
+++ b/talk/media/webrtc/fakewebrtcvideoengine.h
@@ -447,6 +447,10 @@
     WEBRTC_ASSERT_CHANNEL(channel);
     return channels_.find(channel)->second->send;
   }
+  bool GetReceive(int channel) const {
+    WEBRTC_ASSERT_CHANNEL(channel);
+    return channels_.find(channel)->second->receive_;
+  }
   int GetCaptureChannelId(int capture_id) const {
     WEBRTC_ASSERT_CAPTURER(capture_id);
     return capturers_.find(capture_id)->second->channel_id();
diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc
index d2e73ea..9a94b88 100644
--- a/talk/media/webrtc/webrtcvideoengine.cc
+++ b/talk/media/webrtc/webrtcvideoengine.cc
@@ -60,6 +60,7 @@
 #include "talk/media/webrtc/webrtcvie.h"
 #include "talk/media/webrtc/webrtcvoe.h"
 #include "talk/media/webrtc/webrtcvoiceengine.h"
+#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
 
 #if !defined(LIBPEERCONNECTION_LIB)
 #ifndef HAVE_WEBRTC_VIDEO
@@ -2253,7 +2254,8 @@
   return true;
 }
 
-bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) {
+bool WebRtcVideoMediaChannel::GetStats(const StatsOptions& options,
+                                       VideoMediaInfo* info) {
   // Get sender statistics and build VideoSenderInfo.
   unsigned int total_bitrate_sent = 0;
   unsigned int video_bitrate_sent = 0;
@@ -2453,11 +2455,29 @@
       LOG_RTCERR1(GetEstimatedReceiveBandwidth, channel->channel_id());
     }
   }
-
   // Build BandwidthEstimationInfo.
   // TODO(zhurunz): Add real unittest for this.
   BandwidthEstimationInfo bwe;
 
+  // TODO(jiayl): remove the condition when the necessary changes are available
+  // outside the dev branch.
+#ifdef USE_WEBRTC_DEV_BRANCH
+  if (options.include_received_propagation_stats) {
+    webrtc::ReceiveBandwidthEstimatorStats additional_stats;
+    // Only call for the default channel because the returned stats are
+    // collected for all the channels using the same estimator.
+    if (engine_->vie()->rtp()->GetReceiveBandwidthEstimatorStats(
+        recv_channels_[0]->channel_id(), &additional_stats)) {
+      bwe.total_received_propagation_delta_ms =
+          additional_stats.total_propagation_time_delta_ms;
+      bwe.recent_received_propagation_delta_ms.swap(
+          additional_stats.recent_propagation_time_delta_ms);
+      bwe.recent_received_packet_group_arrival_time_ms.swap(
+          additional_stats.recent_arrival_time_ms);
+    }
+  }
+#endif
+
   // Calculations done above per send/receive stream.
   bwe.actual_enc_bitrate = video_bitrate_sent;
   bwe.transmit_bitrate = total_bitrate_sent;
@@ -2632,6 +2652,15 @@
       return false;
     }
   }
+
+  if (send_time_extension) {
+    // For video RTP packets, we would like to update AbsoluteSendTimeHeader
+    // Extension closer to the network, @ socket level before sending.
+    // Pushing the extension id to socket layer.
+    MediaChannel::SetOption(NetworkInterface::ST_RTP,
+                            talk_base::Socket::OPT_RTP_SENDTIME_EXTN_ID,
+                            send_time_extension->id);
+  }
   return true;
 }
 
@@ -3083,6 +3112,13 @@
     }
   }
 
+  // Start receiving for both receive and send channels so that we get incoming
+  // RTP (if receiving) as well as RTCP feedback (if sending).
+  if (engine()->vie()->base()->StartReceive(channel_id) != 0) {
+    LOG_RTCERR1(StartReceive, channel_id);
+    return false;
+  }
+
   return true;
 }
 
@@ -3532,14 +3568,6 @@
       }
     }
   }
-
-  // Start receiving packets if at least one receive codec has been set.
-  if (!receive_codecs_.empty()) {
-    if (engine()->vie()->base()->StartReceive(channel_id) != 0) {
-      LOG_RTCERR1(StartReceive, channel_id);
-      return false;
-    }
-  }
   return true;
 }
 
diff --git a/talk/media/webrtc/webrtcvideoengine.h b/talk/media/webrtc/webrtcvideoengine.h
index fa1b248..cb19142 100644
--- a/talk/media/webrtc/webrtcvideoengine.h
+++ b/talk/media/webrtc/webrtcvideoengine.h
@@ -258,7 +258,7 @@
   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 GetStats(const StatsOptions& options, VideoMediaInfo* info);
   virtual bool SetCapturer(uint32 ssrc, VideoCapturer* capturer);
   virtual bool SendIntraFrame();
   virtual bool RequestIntraFrame();
diff --git a/talk/media/webrtc/webrtcvideoengine_unittest.cc b/talk/media/webrtc/webrtcvideoengine_unittest.cc
index 386ec0c..f2af10a 100644
--- a/talk/media/webrtc/webrtcvideoengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine_unittest.cc
@@ -1077,6 +1077,8 @@
 TEST_F(WebRtcVideoEngineTestFake, SetSend) {
   EXPECT_TRUE(SetupEngine());
   int channel_num = vie_.GetLastChannel();
+  // Verify receiving is also started.
+  EXPECT_TRUE(vie_.GetReceive(channel_num));
 
   // Set send codecs on the channel.
   std::vector<cricket::VideoCodec> codecs;
@@ -1298,12 +1300,20 @@
   ASSERT_NE(-1, channel1);
   ASSERT_NE(channel0, channel1);
 
+  // Both channels should have started receiving after created.
+  EXPECT_TRUE(vie_.GetReceive(channel0));
+  EXPECT_TRUE(vie_.GetReceive(channel1));
+
   // Set send codec.
   std::vector<cricket::VideoCodec> codecs;
   cricket::VideoCodec send_codec(100, "VP8", 640, 480, 30, 0);
   codecs.push_back(send_codec);
   EXPECT_TRUE(channel_->SetSendCodecs(codecs));
 
+  EXPECT_TRUE(channel_->SetSend(true));
+  EXPECT_TRUE(vie_.GetSend(channel0));
+  EXPECT_TRUE(vie_.GetSend(channel1));
+
   EXPECT_TRUE(capturer.CaptureFrame());
   EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0));
   EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel1));
@@ -1347,7 +1357,7 @@
   EXPECT_NE(first_receive_channel, second_receive_channel);
 
   cricket::VideoMediaInfo info;
-  EXPECT_TRUE(channel_->GetStats(&info));
+  EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info));
   ASSERT_EQ(1U, info.bw_estimations.size());
   ASSERT_EQ(0, info.bw_estimations[0].actual_enc_bitrate);
   ASSERT_EQ(0, info.bw_estimations[0].transmit_bitrate);
@@ -1374,7 +1384,7 @@
                                    first_channel_receive_bandwidth);
 
   info.Clear();
-  EXPECT_TRUE(channel_->GetStats(&info));
+  EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info));
   ASSERT_EQ(1U, info.bw_estimations.size());
   ASSERT_EQ(send_video_bitrate, info.bw_estimations[0].actual_enc_bitrate);
   ASSERT_EQ(send_total_bitrate, info.bw_estimations[0].transmit_bitrate);
@@ -1391,7 +1401,7 @@
                                    second_channel_receive_bandwidth);
 
   info.Clear();
-  EXPECT_TRUE(channel_->GetStats(&info));
+  EXPECT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info));
   ASSERT_EQ(1U, info.bw_estimations.size());
   ASSERT_EQ(send_video_bitrate, info.bw_estimations[0].actual_enc_bitrate);
   ASSERT_EQ(send_total_bitrate, info.bw_estimations[0].transmit_bitrate);
diff --git a/talk/p2p/base/candidate.h b/talk/p2p/base/candidate.h
index 19eed8c..0fa9f0e 100644
--- a/talk/p2p/base/candidate.h
+++ b/talk/p2p/base/candidate.h
@@ -33,6 +33,7 @@
 #include <string>
 #include <sstream>
 #include <iomanip>
+
 #include "talk/base/basictypes.h"
 #include "talk/base/socketaddress.h"
 #include "talk/p2p/base/constants.h"
@@ -163,13 +164,30 @@
     return ToStringInternal(true);
   }
 
-  uint32 GetPriority(uint32 type_preference) const {
+  uint32 GetPriority(uint32 type_preference,
+                     int network_adapter_preference) const {
     // RFC 5245 - 4.1.2.1.
     // priority = (2^24)*(type preference) +
     //            (2^8)*(local preference) +
     //            (2^0)*(256 - component ID)
+
+    // |local_preference| length is 2 bytes, 0-65535 inclusive.
+    // In our implemenation we will partion local_preference into
+    //              0                 1
+    //       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+    //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    //      |  NIC Pref     |    Addr Pref  |
+    //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    // NIC Type - Type of the network adapter e.g. 3G/Wifi/Wired.
+    // Addr Pref - Address preference value as per RFC 3484.
+    // local preference is calculated as - NIC Type << 8 | Addr_Pref.
+
     int addr_pref = IPAddressPrecedence(address_.ipaddr());
-    return (type_preference << 24) | (addr_pref << 8) | (256 - component_);
+    int local_preference = (network_adapter_preference << 8) | addr_pref;
+
+    return (type_preference << 24) |
+           (local_preference << 8) |
+           (256 - component_);
   }
 
  private:
@@ -177,9 +195,9 @@
     std::ostringstream ost;
     std::string address = sensitive ? address_.ToSensitiveString() :
                                       address_.ToString();
-    ost << "Cand[" << id_ << ":" << component_ << ":"
-        << type_ << ":" << protocol_ << ":"
-        << network_name_ << ":" << address << ":"
+    ost << "Cand[" << foundation_ << ":" << component_ << ":"
+        << protocol_ << ":" << priority_ << ":"
+        << address << ":" << type_ << ":" << related_address_ << ":"
         << username_ << ":" << password_ << "]";
     return ost.str();
   }
diff --git a/talk/p2p/base/p2ptransportchannel.cc b/talk/p2p/base/p2ptransportchannel.cc
index 104b5e6..1f53874 100644
--- a/talk/p2p/base/p2ptransportchannel.cc
+++ b/talk/p2p/base/p2ptransportchannel.cc
@@ -493,7 +493,8 @@
         port->Network()->name(), 0U,
         talk_base::ToString<uint32>(talk_base::ComputeCrc32(id)));
     new_remote_candidate.set_priority(
-        new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX));
+        new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX,
+                                         port->Network()->preference()));
   }
 
   if (port->IceProtocol() == ICEPROTO_RFC5245) {
diff --git a/talk/p2p/base/p2ptransportchannel_unittest.cc b/talk/p2p/base/p2ptransportchannel_unittest.cc
index 7fff3da..53a39c2 100644
--- a/talk/p2p/base/p2ptransportchannel_unittest.cc
+++ b/talk/p2p/base/p2ptransportchannel_unittest.cc
@@ -1559,8 +1559,11 @@
 // Test that we can quickly switch links if an interface goes down.
 TEST_F(P2PTransportChannelMultihomedTest, TestFailover) {
   AddAddress(0, kPublicAddrs[0]);
-  AddAddress(1, kPublicAddrs[1]);
+  // Adding alternate address will make sure |kPublicAddrs| has the higher
+  // priority than others. This is due to FakeNetwork::AddInterface method.
   AddAddress(1, kAlternateAddrs[1]);
+  AddAddress(1, kPublicAddrs[1]);
+
   // Use only local ports for simplicity.
   SetAllocatorFlags(0, kOnlyLocalPorts);
   SetAllocatorFlags(1, kOnlyLocalPorts);
diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc
index b6421ad..38031cb 100644
--- a/talk/p2p/base/port.cc
+++ b/talk/p2p/base/port.cc
@@ -258,7 +258,7 @@
   c.set_type(type);
   c.set_protocol(protocol);
   c.set_address(address);
-  c.set_priority(c.GetPriority(type_preference));
+  c.set_priority(c.GetPriority(type_preference, network_->preference()));
   c.set_username(username_fragment());
   c.set_password(password_);
   c.set_network_name(network_->name());
diff --git a/talk/p2p/client/portallocator_unittest.cc b/talk/p2p/client/portallocator_unittest.cc
index 1417707..0ea8fb5 100644
--- a/talk/p2p/client/portallocator_unittest.cc
+++ b/talk/p2p/client/portallocator_unittest.cc
@@ -53,8 +53,8 @@
 static const SocketAddress kClientAddr("11.11.11.11", 0);
 static const SocketAddress kClientIPv6Addr(
     "2401:fa00:4:1000:be30:5bff:fee5:c3", 0);
+static const SocketAddress kClientAddr2("22.22.22.22", 0);
 static const SocketAddress kNatAddr("77.77.77.77", talk_base::NAT_SERVER_PORT);
-static const SocketAddress kRemoteClientAddr("22.22.22.22", 0);
 static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT);
 static const SocketAddress kRelayUdpIntAddr("99.99.99.2", 5000);
 static const SocketAddress kRelayUdpExtAddr("99.99.99.3", 5001);
@@ -492,6 +492,23 @@
   EXPECT_TRUE_WAIT(candidate_allocation_done_, 9000);
 }
 
+TEST_F(PortAllocatorTest, TestCandidatePriorityOfMultipleInterfaces) {
+  AddInterface(kClientAddr);
+  AddInterface(kClientAddr2);
+  // Allocating only host UDP ports. This is done purely for testing
+  // convenience.
+  allocator().set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
+                        cricket::PORTALLOCATOR_DISABLE_STUN |
+                        cricket::PORTALLOCATOR_DISABLE_RELAY);
+  EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+  session_->StartGettingPorts();
+  EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout);
+  ASSERT_EQ(2U, candidates_.size());
+  EXPECT_EQ(2U, ports_.size());
+  // Candidates priorities should be different.
+  EXPECT_NE(candidates_[0].priority(), candidates_[1].priority());
+}
+
 // Test to verify ICE restart process.
 TEST_F(PortAllocatorTest, TestGetAllPortsRestarts) {
   AddInterface(kClientAddr);
diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc
index bc6bd09..b177590 100644
--- a/talk/session/media/channel.cc
+++ b/talk/session/media/channel.cc
@@ -1744,9 +1744,10 @@
   LOG(LS_INFO) << "Changing video state, recv=" << recv << " send=" << send;
 }
 
-bool VideoChannel::GetStats(VideoMediaInfo* stats) {
+bool VideoChannel::GetStats(
+    const StatsOptions& options, VideoMediaInfo* stats) {
   return InvokeOnWorker(Bind(&VideoMediaChannel::GetStats,
-                             media_channel(), stats));
+                             media_channel(), options, stats));
 }
 
 void VideoChannel::StartMediaMonitor(int cms) {
diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h
index f3793cb..5a69fed 100644
--- a/talk/session/media/channel.h
+++ b/talk/session/media/channel.h
@@ -524,7 +524,7 @@
   int GetScreencastFps(uint32 ssrc);
   int GetScreencastMaxPixels(uint32 ssrc);
   // Get statistics about the current media session.
-  bool GetStats(VideoMediaInfo* stats);
+  bool GetStats(const StatsOptions& options, VideoMediaInfo* stats);
 
   sigslot::signal2<VideoChannel*, const std::vector<ConnectionInfo>&>
       SignalConnectionMonitor;