Update libjingle to 55618622.
Update libyuv to r826.

TEST=try bots
R=niklas.enbom@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5038 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/localaudiosource.cc b/talk/app/webrtc/localaudiosource.cc
index 3663aac..2cd472a 100644
--- a/talk/app/webrtc/localaudiosource.cc
+++ b/talk/app/webrtc/localaudiosource.cc
@@ -53,7 +53,9 @@
     "googHighpassFilter";
 const char MediaConstraintsInterface::kTypingNoiseDetection[] =
     "googTypingNoiseDetection";
-const char MediaConstraintsInterface::kInternalAecDump[] = "internalAecDump";
+const char MediaConstraintsInterface::kAudioMirroring[] = "googAudioMirroring";
+// TODO(perkj): Remove kInternalAecDump once its not used by Chrome.
+const char MediaConstraintsInterface::kInternalAecDump[] = "deprecatedAecDump";
 
 namespace {
 
@@ -90,10 +92,10 @@
       options->noise_suppression.Set(value);
     else if (iter->key == MediaConstraintsInterface::kHighpassFilter)
       options->highpass_filter.Set(value);
-    else if (iter->key == MediaConstraintsInterface::kInternalAecDump)
-      options->aec_dump.Set(value);
     else if (iter->key == MediaConstraintsInterface::kTypingNoiseDetection)
       options->typing_detection.Set(value);
+    else if (iter->key == MediaConstraintsInterface::kAudioMirroring)
+      options->stereo_swapping.Set(value);
     else
       success = false;
   }
@@ -103,14 +105,16 @@
 }  // namespace
 
 talk_base::scoped_refptr<LocalAudioSource> LocalAudioSource::Create(
+    const PeerConnectionFactoryInterface::Options& options,
     const MediaConstraintsInterface* constraints) {
   talk_base::scoped_refptr<LocalAudioSource> source(
       new talk_base::RefCountedObject<LocalAudioSource>());
-  source->Initialize(constraints);
+  source->Initialize(options, constraints);
   return source;
 }
 
 void LocalAudioSource::Initialize(
+    const PeerConnectionFactoryInterface::Options& options,
     const MediaConstraintsInterface* constraints) {
   if (!constraints)
     return;
@@ -119,12 +123,14 @@
   // constraints.
   FromConstraints(constraints->GetOptional(), &options_);
 
-  cricket::AudioOptions options;
-  if (!FromConstraints(constraints->GetMandatory(), &options)) {
+  cricket::AudioOptions audio_options;
+  if (!FromConstraints(constraints->GetMandatory(), &audio_options)) {
     source_state_ = kEnded;
     return;
   }
-  options_.SetAll(options);
+  options_.SetAll(audio_options);
+  if (options.enable_aec_dump)
+    options_.aec_dump.Set(true);
   source_state_ = kLive;
 }
 
diff --git a/talk/app/webrtc/localaudiosource.h b/talk/app/webrtc/localaudiosource.h
index e0fda03..fb769ed 100644
--- a/talk/app/webrtc/localaudiosource.h
+++ b/talk/app/webrtc/localaudiosource.h
@@ -30,6 +30,7 @@
 
 #include "talk/app/webrtc/mediastreaminterface.h"
 #include "talk/app/webrtc/notifier.h"
+#include "talk/app/webrtc/peerconnectioninterface.h"
 #include "talk/base/scoped_ptr.h"
 #include "talk/media/base/mediachannel.h"
 
@@ -44,6 +45,7 @@
  public:
   // Creates an instance of LocalAudioSource.
   static talk_base::scoped_refptr<LocalAudioSource> Create(
+      const PeerConnectionFactoryInterface::Options& options,
       const MediaConstraintsInterface* constraints);
 
   virtual SourceState state() const { return source_state_; }
@@ -58,7 +60,8 @@
   }
 
  private:
-  void Initialize(const MediaConstraintsInterface* constraints);
+  void Initialize(const PeerConnectionFactoryInterface::Options& options,
+                  const MediaConstraintsInterface* constraints);
 
   cricket::AudioOptions options_;
   SourceState source_state_;
diff --git a/talk/app/webrtc/localaudiosource_unittest.cc b/talk/app/webrtc/localaudiosource_unittest.cc
index ae07787..f8880e0 100644
--- a/talk/app/webrtc/localaudiosource_unittest.cc
+++ b/talk/app/webrtc/localaudiosource_unittest.cc
@@ -39,6 +39,7 @@
 using webrtc::LocalAudioSource;
 using webrtc::MediaConstraintsInterface;
 using webrtc::MediaSourceInterface;
+using webrtc::PeerConnectionFactoryInterface;
 
 TEST(LocalAudioSourceTest, SetValidOptions) {
   webrtc::FakeConstraints constraints;
@@ -52,7 +53,8 @@
   constraints.AddOptional(MediaConstraintsInterface::kHighpassFilter, true);
 
   talk_base::scoped_refptr<LocalAudioSource> source =
-      LocalAudioSource::Create(&constraints);
+      LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(),
+                               &constraints);
 
   bool value;
   EXPECT_TRUE(source->options().echo_cancellation.Get(&value));
@@ -72,7 +74,8 @@
 TEST(LocalAudioSourceTest, OptionNotSet) {
   webrtc::FakeConstraints constraints;
   talk_base::scoped_refptr<LocalAudioSource> source =
-      LocalAudioSource::Create(&constraints);
+      LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(),
+                               &constraints);
   bool value;
   EXPECT_FALSE(source->options().highpass_filter.Get(&value));
 }
@@ -83,7 +86,8 @@
   constraints.AddOptional(MediaConstraintsInterface::kEchoCancellation, true);
 
   talk_base::scoped_refptr<LocalAudioSource> source =
-      LocalAudioSource::Create(&constraints);
+      LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(),
+                               &constraints);
 
   bool value;
   EXPECT_TRUE(source->options().echo_cancellation.Get(&value));
@@ -96,7 +100,8 @@
   constraints.AddOptional("invalidKey", false);
 
   talk_base::scoped_refptr<LocalAudioSource> source =
-      LocalAudioSource::Create(&constraints);
+      LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(),
+                               &constraints);
 
   EXPECT_EQ(MediaSourceInterface::kLive, source->state());
   bool value;
@@ -110,7 +115,8 @@
   constraints.AddMandatory("invalidKey", false);
 
   talk_base::scoped_refptr<LocalAudioSource> source =
-      LocalAudioSource::Create(&constraints);
+      LocalAudioSource::Create(PeerConnectionFactoryInterface::Options(),
+                               &constraints);
 
   EXPECT_EQ(MediaSourceInterface::kEnded, source->state());
   bool value;
diff --git a/talk/app/webrtc/mediaconstraintsinterface.h b/talk/app/webrtc/mediaconstraintsinterface.h
index 48022dd..97d5cf6 100644
--- a/talk/app/webrtc/mediaconstraintsinterface.h
+++ b/talk/app/webrtc/mediaconstraintsinterface.h
@@ -81,6 +81,7 @@
   static const char kNoiseSuppression[];  // googNoiseSuppression
   static const char kHighpassFilter[];  // googHighpassFilter
   static const char kTypingNoiseDetection[];  // googTypingNoiseDetection
+  static const char kAudioMirroring[];  // googAudioMirroring
 
   // Google-specific constraint keys for a local video source
   static const char kNoiseReduction[];  // googNoiseReduction
@@ -106,6 +107,8 @@
   static const char kEnableDtlsSrtp[];  // Enable DTLS-SRTP
   // Temporary pseudo-constraints used to enable DataChannels
   static const char kEnableRtpDataChannels[];  // Enable RTP DataChannels
+  // TODO(perkj): Remove kEnableSctpDataChannels once Chrome use
+  // PeerConnectionFactory::SetOptions.
   static const char kEnableSctpDataChannels[];  // Enable SCTP DataChannels
 
   // The prefix of internal-only constraints whose JS set values should be
@@ -116,9 +119,8 @@
   // line flags. So they are prefixed with "internal" so JS values will be
   // removed.
   // Used by a local audio source.
+  // TODO(perkj): Remove once Chrome use PeerConnectionFactory::SetOptions.
   static const char kInternalAecDump[];  // internalAecDump
-  // Used for disabling security and use plain RTP.
-  static const char kInternalDisableEncryption[];  // internalDisableEncryption
 
  protected:
   // Dtor protected as objects shouldn't be deleted via this interface
diff --git a/talk/app/webrtc/mediastreamsignaling.cc b/talk/app/webrtc/mediastreamsignaling.cc
index 771a4e8..ef9f3e0 100644
--- a/talk/app/webrtc/mediastreamsignaling.cc
+++ b/talk/app/webrtc/mediastreamsignaling.cc
@@ -267,6 +267,10 @@
   }
   scoped_refptr<DataChannel> channel(
       data_channel_factory_->CreateDataChannel(label, &config));
+  if (!channel.get()) {
+    LOG(LS_ERROR) << "Failed to create DataChannel from the OPEN message.";
+    return false;
+  }
   data_channels_[label] = channel;
   stream_observer_->OnAddDataChannel(channel);
   // It's immediately ready to use.
diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc
index 7fafad7..de88f88 100644
--- a/talk/app/webrtc/peerconnection.cc
+++ b/talk/app/webrtc/peerconnection.cc
@@ -317,7 +317,8 @@
   stats_.set_session(session_.get());
 
   // Initialize the WebRtcSession. It creates transport channels etc.
-  if (!session_->Initialize(constraints, dtls_identity_service))
+  if (!session_->Initialize(factory_->options(), constraints,
+                            dtls_identity_service))
     return false;
 
   // Register PeerConnection as receiver of local ice candidates.
diff --git a/talk/app/webrtc/peerconnection_unittest.cc b/talk/app/webrtc/peerconnection_unittest.cc
index 9de6ec9..76d9cd7 100644
--- a/talk/app/webrtc/peerconnection_unittest.cc
+++ b/talk/app/webrtc/peerconnection_unittest.cc
@@ -157,8 +157,8 @@
       // Disable highpass filter so that we can get all the test audio frames.
       constraints.AddMandatory(
           MediaConstraintsInterface::kHighpassFilter, false);
-      talk_base::scoped_refptr<webrtc::LocalAudioSource> source =
-          webrtc::LocalAudioSource::Create(&constraints);
+      talk_base::scoped_refptr<webrtc::AudioSourceInterface> source =
+          peer_connection_factory_->CreateAudioSource(&constraints);
       // TODO(perkj): Test audio source when it is implemented. Currently audio
       // always use the default input.
       talk_base::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
diff --git a/talk/app/webrtc/peerconnectionfactory.cc b/talk/app/webrtc/peerconnectionfactory.cc
index 7d30fab..e8b8f63 100644
--- a/talk/app/webrtc/peerconnectionfactory.cc
+++ b/talk/app/webrtc/peerconnectionfactory.cc
@@ -261,7 +261,7 @@
 PeerConnectionFactory::CreateAudioSource_s(
     const MediaConstraintsInterface* constraints) {
   talk_base::scoped_refptr<LocalAudioSource> source(
-      LocalAudioSource::Create(constraints));
+      LocalAudioSource::Create(options_, constraints));
   return source;
 }
 
diff --git a/talk/app/webrtc/peerconnectionfactory.h b/talk/app/webrtc/peerconnectionfactory.h
index 7faf609..dff885d 100644
--- a/talk/app/webrtc/peerconnectionfactory.h
+++ b/talk/app/webrtc/peerconnectionfactory.h
@@ -40,6 +40,10 @@
 class PeerConnectionFactory : public PeerConnectionFactoryInterface,
                               public talk_base::MessageHandler {
  public:
+  virtual void SetOptions(const Options& options) {
+    options_ = options;
+  }
+
   virtual talk_base::scoped_refptr<PeerConnectionInterface>
       CreatePeerConnection(
           const PeerConnectionInterface::IceServers& configuration,
@@ -77,6 +81,7 @@
   virtual cricket::ChannelManager* channel_manager();
   virtual talk_base::Thread* signaling_thread();
   virtual talk_base::Thread* worker_thread();
+  const Options& options() const { return options_; }
 
  protected:
   PeerConnectionFactory();
@@ -109,6 +114,7 @@
   bool owns_ptrs_;
   talk_base::Thread* signaling_thread_;
   talk_base::Thread* worker_thread_;
+  Options options_;
   talk_base::scoped_refptr<PortAllocatorFactoryInterface> allocator_factory_;
   // External Audio device used for audio playback.
   talk_base::scoped_refptr<AudioDeviceModule> default_adm_;
diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h
index bd86f3e..a127dad 100644
--- a/talk/app/webrtc/peerconnectioninterface.h
+++ b/talk/app/webrtc/peerconnectioninterface.h
@@ -390,6 +390,19 @@
 // argument.
 class PeerConnectionFactoryInterface : public talk_base::RefCountInterface {
  public:
+  class Options {
+   public:
+    Options() :
+      enable_aec_dump(false),
+      disable_encryption(false),
+      disable_sctp_data_channels(false) {
+    }
+    bool enable_aec_dump;
+    bool disable_encryption;
+    bool disable_sctp_data_channels;
+  };
+
+  virtual void SetOptions(const Options& options) = 0;
   virtual talk_base::scoped_refptr<PeerConnectionInterface>
      CreatePeerConnection(
          const PeerConnectionInterface::IceServers& configuration,
diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc
index 42ae662..db7cac4 100644
--- a/talk/app/webrtc/statscollector.cc
+++ b/talk/app/webrtc/statscollector.cc
@@ -82,6 +82,15 @@
     "googFrameRateDecoded";
 const char StatsReport::kStatsValueNameFrameRateOutput[] =
     "googFrameRateOutput";
+const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs";
+const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs";
+const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs";
+const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs";
+const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs";
+const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] =
+    "googMinPlayoutDelayMs";
+const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs";
+
 const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput";
 const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent";
 const char StatsReport::kStatsValueNameFrameWidthReceived[] =
@@ -119,6 +128,7 @@
 
 const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession";
 const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe";
+const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc";
 const char StatsReport::kStatsReportTypeSsrc[] = "ssrc";
 const char StatsReport::kStatsReportTypeTrack[] = "googTrack";
 const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate";
@@ -241,6 +251,21 @@
                    info.framerate_decoded);
   report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
                    info.framerate_output);
+
+  report->AddValue(StatsReport::kStatsValueNameDecodeMs,
+                   info.decode_ms);
+  report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs,
+                   info.max_decode_ms);
+  report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
+                   info.current_delay_ms);
+  report->AddValue(StatsReport::kStatsValueNameTargetDelayMs,
+                   info.target_delay_ms);
+  report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
+                   info.jitter_buffer_ms);
+  report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs,
+                   info.min_playout_delay_ms);
+  report->AddValue(StatsReport::kStatsValueNameRenderDelayMs,
+                   info.render_delay_ms);
 }
 
 void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
@@ -293,6 +318,18 @@
                    info.bucket_delay);
 }
 
+void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
+                        StatsReport* report) {
+  report->timestamp = info.remote_stats[0].timestamp;
+  // TODO(hta): Extract some stats here.
+}
+
+void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
+                        StatsReport* report) {
+  report->timestamp = info.remote_stats[0].timestamp;
+  // TODO(hta): Extract some stats here.
+}
+
 uint32 ExtractSsrc(const cricket::VoiceReceiverInfo& info) {
   return info.ssrc;
 }
@@ -319,11 +356,20 @@
   for (; it != data.end(); ++it) {
     std::string id;
     uint32 ssrc = ExtractSsrc(*it);
-    StatsReport* report = collector->PrepareReport(ssrc, transport_id);
+    // Each object can result in 2 objects, a local and a remote object.
+    // TODO(hta): Handle the case of multiple SSRCs per object.
+    StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id);
     if (!report) {
       continue;
     }
     ExtractStats(*it, report);
+    if (it->remote_stats.size() > 0) {
+      report = collector->PrepareRemoteReport(ssrc, transport_id);
+      if (!report) {
+        continue;
+      }
+      ExtractRemoteStats(*it, report);
+    }
   }
 };
 
@@ -406,8 +452,9 @@
   }
 }
 
-StatsReport* StatsCollector::PrepareReport(uint32 ssrc,
-                                           const std::string& transport_id) {
+StatsReport* StatsCollector::PrepareLocalReport(
+    uint32 ssrc,
+    const std::string& transport_id) {
   std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
   StatsMap::iterator it = reports_.find(StatsId(
       StatsReport::kStatsReportTypeSsrc, ssrc_id));
@@ -427,10 +474,8 @@
                            &track_id);
   }
 
-  StatsReport* report = &reports_[
-      StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id)];
-  report->id = StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id);
-  report->type = StatsReport::kStatsReportTypeSsrc;
+  StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc,
+                                          ssrc_id);
 
   // Clear out stats from previous GatherStats calls if any.
   if (report->timestamp != stats_gathering_started_) {
@@ -446,6 +491,44 @@
   return report;
 }
 
+StatsReport* StatsCollector::PrepareRemoteReport(
+    uint32 ssrc,
+    const std::string& transport_id) {
+  std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
+  StatsMap::iterator it = reports_.find(StatsId(
+      StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id));
+
+  std::string track_id;
+  if (it == reports_.end()) {
+    if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
+      LOG(LS_WARNING) << "The SSRC " << ssrc
+                      << " is not associated with a track";
+      return NULL;
+    }
+  } else {
+    // Keeps the old track id since we want to report the stats for inactive
+    // tracks.
+    ExtractValueFromReport(it->second,
+                           StatsReport::kStatsValueNameTrackId,
+                           &track_id);
+  }
+
+  StatsReport* report = GetOrCreateReport(
+      StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id);
+
+  // Clear out stats from previous GatherStats calls if any.
+  // The timestamp will be added later. Zero it for debugging.
+  report->values.clear();
+  report->timestamp = 0;
+
+  report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
+  report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
+  // Add the mapping of SSRC to transport.
+  report->AddValue(StatsReport::kStatsValueNameTransportId,
+                   transport_id);
+  return report;
+}
+
 std::string StatsCollector::AddOneCertificateReport(
     const talk_base::SSLCertificate* cert, const std::string& issuer_id) {
   // TODO(bemasc): Move this computation to a helper class that caches these
@@ -592,6 +675,9 @@
                           info.local_candidate.address().ToString());
           report.AddValue(StatsReport::kStatsValueNameRemoteAddress,
                           info.remote_candidate.address().ToString());
+          report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
+          report.AddValue(StatsReport::kStatsValueNameTransportType,
+                          info.local_candidate.protocol());
           reports_[report.id] = report;
         }
       }
@@ -668,4 +754,19 @@
   return true;
 }
 
+StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
+                                               const std::string& id) {
+  std::string statsid = StatsId(type, id);
+  StatsReport* report = NULL;
+  std::map<std::string, StatsReport>::iterator it = reports_.find(statsid);
+  if (it == reports_.end()) {
+    report = &reports_[statsid];  // Create new element.
+    report->id = statsid;
+    report->type = type;
+  } else {
+    report = &reports_[statsid];
+  }
+  return report;
+}
+
 }  // namespace webrtc
diff --git a/talk/app/webrtc/statscollector.h b/talk/app/webrtc/statscollector.h
index c34b5a0..01da059 100644
--- a/talk/app/webrtc/statscollector.h
+++ b/talk/app/webrtc/statscollector.h
@@ -65,9 +65,11 @@
   // |reports|.
   bool GetStats(MediaStreamTrackInterface* track, StatsReports* reports);
 
-  WebRtcSession* session() { return session_; }
-  // Prepare an SSRC report for the given ssrc. Used internally.
-  StatsReport* PrepareReport(uint32 ssrc, const std::string& transport);
+  // Prepare an SSRC report for the given ssrc. Used internally
+  // in the ExtractStatsFromList template.
+  StatsReport* PrepareLocalReport(uint32 ssrc, const std::string& transport);
+  // Prepare an SSRC report for the given remote ssrc. Used internally.
+  StatsReport* PrepareRemoteReport(uint32 ssrc, const std::string& transport);
   // Extracts the ID of a Transport belonging to an SSRC. Used internally.
   bool GetTransportIdFromProxy(const std::string& proxy,
                                std::string* transport_id);
@@ -88,9 +90,12 @@
   void ExtractVideoInfo();
   double GetTimeNow();
   void BuildSsrcToTransportId();
+  WebRtcSession* session() { return session_; }
+  webrtc::StatsReport* GetOrCreateReport(const std::string& type,
+                                         const std::string& id);
 
   // A map from the report id to the report.
-  std::map<std::string, webrtc::StatsReport> reports_;
+  std::map<std::string, StatsReport> reports_;
   // Raw pointer to the session the statistics are gathered from.
   WebRtcSession* session_;
   double stats_gathering_started_;
diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc
index d9cde5d..66a5ee0 100644
--- a/talk/app/webrtc/statscollector_unittest.cc
+++ b/talk/app/webrtc/statscollector_unittest.cc
@@ -58,6 +58,10 @@
 const char kNotFound[] = "NOT FOUND";
 const char kNoReports[] = "NO REPORTS";
 
+// Constant names for track identification.
+const char kTrackId[] = "somename";
+const uint32 kSsrcOfTrack = 1234;
+
 class MockWebRtcSession : public webrtc::WebRtcSession {
  public:
   explicit MockWebRtcSession(cricket::ChannelManager* channel_manager)
@@ -207,11 +211,37 @@
           new cricket::ChannelManager(media_engine_,
                                       new cricket::FakeDeviceManager(),
                                       talk_base::Thread::Current())),
-      session_(channel_manager_.get()) {
+      session_(channel_manager_.get()),
+      track_id_(kTrackId) {
     // By default, we ignore session GetStats calls.
     EXPECT_CALL(session_, GetStats(_)).WillRepeatedly(Return(false));
   }
 
+  // This creates a standard setup with a transport called "trspname"
+  // having one transport channel
+  // and the specified virtual connection name.
+  void InitSessionStats(const std::string vc_name) {
+    const std::string kTransportName("trspname");
+    cricket::TransportStats transport_stats;
+    cricket::TransportChannelStats channel_stats;
+    channel_stats.component = 1;
+    transport_stats.content_name = kTransportName;
+    transport_stats.channel_stats.push_back(channel_stats);
+
+    session_stats_.transport_stats[kTransportName] = transport_stats;
+    session_stats_.proxy_to_transport[vc_name] = kTransportName;
+  }
+
+  // Adds a track with a given SSRC into the stats.
+  void AddVideoTrackStats() {
+    stream_ = webrtc::MediaStream::Create("streamlabel");
+    track_= webrtc::VideoTrack::Create(kTrackId, NULL);
+    stream_->AddTrack(track_);
+    EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(track_id_),
+                            Return(true)));
+  }
+
   void TestCertificateReports(const talk_base::FakeSSLCertificate& local_cert,
                               const std::vector<std::string>& local_ders,
                               const talk_base::FakeSSLCertificate& remote_cert,
@@ -283,9 +313,14 @@
     EXPECT_NE(kNotFound, remote_certificate_id);
     CheckCertChainReports(reports, remote_ders, remote_certificate_id);
   }
+
   cricket::FakeMediaEngine* media_engine_;
   talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
   MockWebRtcSession session_;
+  cricket::SessionStats session_stats_;
+  talk_base::scoped_refptr<webrtc::MediaStream> stream_;
+  talk_base::scoped_refptr<webrtc::VideoTrack> track_;
+  std::string track_id_;
 };
 
 // This test verifies that 64-bit counters are passed successfully.
@@ -297,17 +332,13 @@
   webrtc::StatsReports reports;  // returned values.
   cricket::VideoSenderInfo video_sender_info;
   cricket::VideoMediaInfo stats_read;
-  const uint32 kSsrcOfTrack = 1234;
-  const std::string kNameOfTrack("somename");
   // The number of bytes must be larger than 0xFFFFFFFF for this test.
   const int64 kBytesSent = 12345678901234LL;
   const std::string kBytesSentString("12345678901234");
 
   stats.set_session(&session_);
-  talk_base::scoped_refptr<webrtc::MediaStream> stream(
-      webrtc::MediaStream::Create("streamlabel"));
-  stream->AddTrack(webrtc::VideoTrack::Create(kNameOfTrack, NULL));
-  stats.AddStream(stream);
+  AddVideoTrackStats();
+  stats.AddStream(stream_);
 
   // Construct a stats value to read.
   video_sender_info.ssrcs.push_back(1234);
@@ -319,9 +350,6 @@
   EXPECT_CALL(*media_channel, GetStats(_))
     .WillOnce(DoAll(SetArgPointee<0>(stats_read),
                     Return(true)));
-  EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
-    .WillOnce(DoAll(SetArgPointee<1>(kNameOfTrack),
-                    Return(true)));
   stats.UpdateStats();
   stats.GetStats(NULL, &reports);
   std::string result = ExtractSsrcStatsValue(reports, "bytesSent");
@@ -339,16 +367,12 @@
   cricket::VideoMediaInfo stats_read;
   // Set up an SSRC just to test that we get both kinds of stats back: SSRC and
   // BWE.
-  const uint32 kSsrcOfTrack = 1234;
-  const std::string kNameOfTrack("somename");
   const int64 kBytesSent = 12345678901234LL;
   const std::string kBytesSentString("12345678901234");
 
   stats.set_session(&session_);
-  talk_base::scoped_refptr<webrtc::MediaStream> stream(
-      webrtc::MediaStream::Create("streamlabel"));
-  stream->AddTrack(webrtc::VideoTrack::Create(kNameOfTrack, NULL));
-  stats.AddStream(stream);
+  AddVideoTrackStats();
+  stats.AddStream(stream_);
 
   // Construct a stats value to read.
   video_sender_info.ssrcs.push_back(1234);
@@ -365,9 +389,7 @@
   EXPECT_CALL(*media_channel, GetStats(_))
     .WillOnce(DoAll(SetArgPointee<0>(stats_read),
                     Return(true)));
-  EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
-    .WillOnce(DoAll(SetArgPointee<1>(kNameOfTrack),
-                    Return(true)));
+
   stats.UpdateStats();
   stats.GetStats(NULL, &reports);
   std::string result = ExtractSsrcStatsValue(reports, "bytesSent");
@@ -417,13 +439,8 @@
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, "", false, NULL);
-  const std::string kTrackId("somename");
-  talk_base::scoped_refptr<webrtc::MediaStream> stream(
-      webrtc::MediaStream::Create("streamlabel"));
-  talk_base::scoped_refptr<webrtc::VideoTrack> track =
-      webrtc::VideoTrack::Create(kTrackId, NULL);
-  stream->AddTrack(track);
-  stats.AddStream(stream);
+  AddVideoTrackStats();
+  stats.AddStream(stream_);
 
   stats.set_session(&session_);
 
@@ -449,13 +466,8 @@
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, "", false, NULL);
-  const std::string kTrackId("somename");
-  talk_base::scoped_refptr<webrtc::MediaStream> stream(
-      webrtc::MediaStream::Create("streamlabel"));
-  talk_base::scoped_refptr<webrtc::VideoTrack> track =
-      webrtc::VideoTrack::Create(kTrackId, NULL);
-  stream->AddTrack(track);
-  stats.AddStream(stream);
+  AddVideoTrackStats();
+  stats.AddStream(stream_);
 
   stats.set_session(&session_);
 
@@ -464,7 +476,6 @@
   // Constructs an ssrc stats update.
   cricket::VideoSenderInfo video_sender_info;
   cricket::VideoMediaInfo stats_read;
-  const uint32 kSsrcOfTrack = 1234;
   const int64 kBytesSent = 12345678901234LL;
 
   // Construct a stats value to read.
@@ -477,23 +488,20 @@
   EXPECT_CALL(*media_channel, GetStats(_))
     .WillOnce(DoAll(SetArgPointee<0>(stats_read),
                     Return(true)));
-  EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
-    .WillOnce(DoAll(SetArgPointee<1>(kTrackId),
-                    Return(true)));
 
   stats.UpdateStats();
   stats.GetStats(NULL, &reports);
-  // |reports| should contain one session report, one track report, and one ssrc
-  // report.
-  EXPECT_EQ((size_t)3, reports.size());
+  // |reports| should contain at least one session report, one track report,
+  // and one ssrc report.
+  EXPECT_LE((size_t)3, reports.size());
   const webrtc::StatsReport* track_report = FindNthReportByType(
       reports, webrtc::StatsReport::kStatsReportTypeTrack, 1);
   EXPECT_FALSE(track_report == NULL);
 
-  stats.GetStats(track, &reports);
-  // |reports| should contain one session report, one track report, and one ssrc
-  // report.
-  EXPECT_EQ((size_t)3, reports.size());
+  stats.GetStats(track_, &reports);
+  // |reports| should contain at least one session report, one track report,
+  // and one ssrc report.
+  EXPECT_LE((size_t)3, reports.size());
   track_report = FindNthReportByType(
       reports, webrtc::StatsReport::kStatsReportTypeTrack, 1);
   EXPECT_FALSE(track_report == NULL);
@@ -516,13 +524,8 @@
   const std::string kVcName("vcname");
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, kVcName, false, NULL);
-  const std::string kTrackId("somename");
-  talk_base::scoped_refptr<webrtc::MediaStream> stream(
-      webrtc::MediaStream::Create("streamlabel"));
-  talk_base::scoped_refptr<webrtc::VideoTrack> track =
-      webrtc::VideoTrack::Create(kTrackId, NULL);
-  stream->AddTrack(track);
-  stats.AddStream(stream);
+  AddVideoTrackStats();
+  stats.AddStream(stream_);
 
   stats.set_session(&session_);
 
@@ -531,7 +534,6 @@
   // Constructs an ssrc stats update.
   cricket::VideoSenderInfo video_sender_info;
   cricket::VideoMediaInfo stats_read;
-  const uint32 kSsrcOfTrack = 1234;
   const int64 kBytesSent = 12345678901234LL;
 
   // Construct a stats value to read.
@@ -544,23 +546,10 @@
   EXPECT_CALL(*media_channel, GetStats(_))
     .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read),
                           Return(true)));
-  EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
-    .WillOnce(DoAll(SetArgPointee<1>(kTrackId),
-                    Return(true)));
 
-  // Instruct the session to return stats containing the transport channel.
-  const std::string kTransportName("trspname");
-  cricket::SessionStats session_stats;
-  cricket::TransportStats transport_stats;
-  cricket::TransportChannelStats channel_stats;
-  channel_stats.component = 1;
-  transport_stats.content_name = kTransportName;
-  transport_stats.channel_stats.push_back(channel_stats);
-
-  session_stats.transport_stats[kTransportName] = transport_stats;
-  session_stats.proxy_to_transport[kVcName] = kTransportName;
+  InitSessionStats(kVcName);
   EXPECT_CALL(session_, GetStats(_))
-    .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats),
+    .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
                           Return(true)));
 
   stats.UpdateStats();
@@ -575,6 +564,78 @@
   ASSERT_FALSE(transport_report == NULL);
 }
 
+// This test verifies that a remote stats object will not be created for
+// an outgoing SSRC where remote stats are not returned.
+TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) {
+  webrtc::StatsCollector stats;  // Implementation under test.
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  // The content_name known by the video channel.
+  const std::string kVcName("vcname");
+  cricket::VideoChannel video_channel(talk_base::Thread::Current(),
+      media_engine_, media_channel, &session_, kVcName, false, NULL);
+  AddVideoTrackStats();
+  stats.AddStream(stream_);
+
+  stats.set_session(&session_);
+
+  EXPECT_CALL(session_, video_channel())
+    .WillRepeatedly(ReturnNull());
+
+  stats.UpdateStats();
+  webrtc::StatsReports reports;
+  stats.GetStats(NULL, &reports);
+  const webrtc::StatsReport* remote_report = FindNthReportByType(reports,
+      webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1);
+  EXPECT_TRUE(remote_report == NULL);
+}
+
+// This test verifies that a remote stats object will be created for
+// an outgoing SSRC where stats are returned.
+TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) {
+  webrtc::StatsCollector stats;  // Implementation under test.
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  // The content_name known by the video channel.
+  const std::string kVcName("vcname");
+  cricket::VideoChannel video_channel(talk_base::Thread::Current(),
+      media_engine_, media_channel, &session_, kVcName, false, NULL);
+  AddVideoTrackStats();
+  stats.AddStream(stream_);
+
+  stats.set_session(&session_);
+
+  webrtc::StatsReports reports;
+
+  // Instruct the session to return stats containing the transport channel.
+  InitSessionStats(kVcName);
+  EXPECT_CALL(session_, GetStats(_))
+    .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
+                          Return(true)));
+
+  // Constructs an ssrc stats update.
+  cricket::VideoMediaInfo stats_read;
+
+  cricket::SsrcReceiverInfo remote_ssrc_stats;
+  remote_ssrc_stats.timestamp = 12345.678;
+  remote_ssrc_stats.ssrc = kSsrcOfTrack;
+  cricket::VideoSenderInfo video_sender_info;
+  video_sender_info.ssrcs.push_back(kSsrcOfTrack);
+  video_sender_info.remote_stats.push_back(remote_ssrc_stats);
+  stats_read.senders.push_back(video_sender_info);
+
+  EXPECT_CALL(session_, video_channel())
+    .WillRepeatedly(Return(&video_channel));
+  EXPECT_CALL(*media_channel, GetStats(_))
+    .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read),
+                          Return(true)));
+
+  stats.UpdateStats();
+  stats.GetStats(NULL, &reports);
+  const webrtc::StatsReport* remote_report = FindNthReportByType(reports,
+      webrtc::StatsReport::kStatsReportTypeRemoteSsrc, 1);
+  EXPECT_FALSE(remote_report == NULL);
+  EXPECT_NE(0, remote_report->timestamp);
+}
+
 // This test verifies that all chained certificates are correctly
 // reported
 TEST_F(StatsCollectorTest, ChainedCertificateReportsCreated) {
@@ -638,6 +699,7 @@
   EXPECT_CALL(session_, GetStats(_))
     .WillOnce(DoAll(SetArgPointee<0>(session_stats),
                     Return(true)));
+
   EXPECT_CALL(session_, video_channel())
     .WillRepeatedly(ReturnNull());
 
@@ -712,5 +774,4 @@
   ASSERT_EQ(kNotFound, remote_certificate_id);
 }
 
-
 }  // namespace
diff --git a/talk/app/webrtc/statstypes.h b/talk/app/webrtc/statstypes.h
index fb8c15b..e76aa86 100644
--- a/talk/app/webrtc/statstypes.h
+++ b/talk/app/webrtc/statstypes.h
@@ -88,6 +88,10 @@
   // The |id| field is the SSRC in decimal form of the rtp stream.
   static const char kStatsReportTypeSsrc[];
 
+  // StatsReport of |type| = "remoteSsrc" is statistics for a specific
+  // rtp stream, generated by the remote end of the connection.
+  static const char kStatsReportTypeRemoteSsrc[];
+
   // StatsReport of |type| = "googTrack" is statistics for a specific media
   // track. The |id| field is the track id.
   static const char kStatsReportTypeTrack[];
@@ -137,6 +141,13 @@
   static const char kStatsValueNameFrameRateReceived[];
   static const char kStatsValueNameFrameRateDecoded[];
   static const char kStatsValueNameFrameRateOutput[];
+  static const char kStatsValueNameDecodeMs[];
+  static const char kStatsValueNameMaxDecodeMs[];
+  static const char kStatsValueNameCurrentDelayMs[];
+  static const char kStatsValueNameTargetDelayMs[];
+  static const char kStatsValueNameJitterBufferMs[];
+  static const char kStatsValueNameMinPlayoutDelayMs[];
+  static const char kStatsValueNameRenderDelayMs[];
   static const char kStatsValueNameFrameRateInput[];
   static const char kStatsValueNameFrameRateSent[];
   static const char kStatsValueNameFrameWidthReceived[];
diff --git a/talk/app/webrtc/test/fakeconstraints.h b/talk/app/webrtc/test/fakeconstraints.h
index 927432d..b23007e 100644
--- a/talk/app/webrtc/test/fakeconstraints.h
+++ b/talk/app/webrtc/test/fakeconstraints.h
@@ -119,7 +119,6 @@
   }
 
   void SetAllowDtlsSctpDataChannels() {
-    SetMandatory(MediaConstraintsInterface::kEnableSctpDataChannels, true);
     SetMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true);
   }
 
diff --git a/talk/app/webrtc/webrtcsdp_unittest.cc b/talk/app/webrtc/webrtcsdp_unittest.cc
index 6f2e388..777a707 100644
--- a/talk/app/webrtc/webrtcsdp_unittest.cc
+++ b/talk/app/webrtc/webrtcsdp_unittest.cc
@@ -282,7 +282,7 @@
     "a=ice-ufrag:ufrag_data\r\n"
     "a=ice-pwd:pwd_data\r\n"
     "a=mid:data_content_name\r\n"
-    "a=sctpmap:5000 webrtc-datachannel 65536\r\n";
+    "a=sctpmap:5000 webrtc-datachannel 1024\r\n";
 
 static const char kSdpSctpDataChannelWithCandidatesString[] =
     "m=application 2345 DTLS/SCTP 5000\r\n"
@@ -297,7 +297,7 @@
     "a=ice-ufrag:ufrag_data\r\n"
     "a=ice-pwd:pwd_data\r\n"
     "a=mid:data_content_name\r\n"
-    "a=sctpmap:5000 webrtc-datachannel 65536\r\n";
+    "a=sctpmap:5000 webrtc-datachannel 1024\r\n";
 
 
 // One candidate reference string as per W3c spec.
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index 74d1488..69a47c4 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -68,10 +68,7 @@
 // line flag. So it is prefixed with kInternalConstraintPrefix so JS values
 // will be removed.
 const char MediaConstraintsInterface::kEnableSctpDataChannels[] =
-    "internalSctpDataChannels";
-
-const char MediaConstraintsInterface::kInternalDisableEncryption[] =
-    "internalDisableEncryption";
+    "deprecatedSctpDataChannels";
 
 // Error messages
 const char kSetLocalSdpFailed[] = "SetLocalDescription failed: ";
@@ -458,6 +455,7 @@
 }
 
 bool WebRtcSession::Initialize(
+    const PeerConnectionFactoryInterface::Options& options,
     const MediaConstraintsInterface* constraints,
     DTLSIdentityServiceInterface* dtls_identity_service) {
   // TODO(perkj): Take |constraints| into consideration. Return false if not all
@@ -476,19 +474,16 @@
   }
 
   // Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
-  // It takes precendence over the kEnableSctpDataChannels constraint.
+  // It takes precendence over the disable_sctp_data_channels
+  // PeerConnectionFactoryInterface::Options.
   if (FindConstraint(
       constraints, MediaConstraintsInterface::kEnableRtpDataChannels,
       &value, NULL) && value) {
     LOG(LS_INFO) << "Allowing RTP data engine.";
     data_channel_type_ = cricket::DCT_RTP;
   } else {
-    bool sctp_enabled = FindConstraint(
-        constraints,
-        MediaConstraintsInterface::kEnableSctpDataChannels,
-        &value, NULL) && value;
     // DTLS has to be enabled to use SCTP.
-    if (sctp_enabled && dtls_enabled_) {
+    if (!options.disable_sctp_data_channels && dtls_enabled_) {
       LOG(LS_INFO) << "Allowing SCTP data engine.";
       data_channel_type_ = cricket::DCT_SCTP;
     }
@@ -520,11 +515,7 @@
   webrtc_session_desc_factory_->SignalIdentityReady.connect(
       this, &WebRtcSession::OnIdentityReady);
 
-  // Disable encryption if kDisableEncryption is set.
-  if (FindConstraint(
-         constraints,
-         MediaConstraintsInterface::kInternalDisableEncryption,
-         &value, NULL) && value) {
+  if (options.disable_encryption) {
     webrtc_session_desc_factory_->set_secure(cricket::SEC_DISABLED);
   }
 
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index 12e2291..dde33ca 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -110,7 +110,8 @@
                 MediaStreamSignaling* mediastream_signaling);
   virtual ~WebRtcSession();
 
-  bool Initialize(const MediaConstraintsInterface* constraints,
+  bool Initialize(const PeerConnectionFactoryInterface::Options& options,
+                  const MediaConstraintsInterface* constraints,
                   DTLSIdentityServiceInterface* dtls_identity_service);
   // Deletes the voice, video and data channel and changes the session state
   // to STATE_RECEIVEDTERMINATE.
diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc
index 493294f..ecbba18 100644
--- a/talk/app/webrtc/webrtcsession_unittest.cc
+++ b/talk/app/webrtc/webrtcsession_unittest.cc
@@ -81,6 +81,7 @@
 using webrtc::IceCandidateCollection;
 using webrtc::JsepIceCandidate;
 using webrtc::JsepSessionDescription;
+using webrtc::PeerConnectionFactoryInterface;
 using webrtc::PeerConnectionInterface;
 using webrtc::SessionDescriptionInterface;
 using webrtc::StreamCollection;
@@ -317,7 +318,8 @@
     EXPECT_EQ(PeerConnectionInterface::kIceGatheringNew,
         observer_.ice_gathering_state_);
 
-    EXPECT_TRUE(session_->Initialize(constraints_.get(), identity_service));
+    EXPECT_TRUE(session_->Initialize(options_, constraints_.get(),
+                                     identity_service));
   }
 
   void InitWithDtmfCodec() {
@@ -919,6 +921,7 @@
   cricket::TestStunServer stun_server_;
   talk_base::FakeNetworkManager network_manager_;
   cricket::BasicPortAllocator allocator_;
+  PeerConnectionFactoryInterface::Options options_;
   talk_base::scoped_ptr<FakeConstraints> constraints_;
   FakeMediaStreamSignaling mediastream_signaling_;
   talk_base::scoped_ptr<WebRtcSessionForTest> session_;
@@ -1932,9 +1935,7 @@
 }
 
 TEST_F(WebRtcSessionTest, VerifyNoCryptoParamsInSDP) {
-  constraints_.reset(new FakeConstraints());
-  constraints_->AddOptional(
-      webrtc::MediaConstraintsInterface::kInternalDisableEncryption, true);
+  options_.disable_encryption = true;
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   scoped_ptr<SessionDescriptionInterface> offer(
@@ -2424,9 +2425,7 @@
 
 // This test verifies the crypto parameter when security is disabled.
 TEST_F(WebRtcSessionTest, TestCryptoAfterSetLocalDescriptionWithDisabled) {
-  constraints_.reset(new FakeConstraints());
-  constraints_->AddOptional(
-      webrtc::MediaConstraintsInterface::kInternalDisableEncryption, true);
+  options_.disable_encryption = true;
   Init(NULL);
   mediastream_signaling_.SendAudioVideoStream1();
   talk_base::scoped_ptr<SessionDescriptionInterface> offer(
@@ -2567,8 +2566,8 @@
   constraints_.reset(new FakeConstraints());
   constraints_->AddOptional(
       webrtc::MediaConstraintsInterface::kEnableRtpDataChannels, true);
-  constraints_->AddOptional(
-    webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true);
+  options_.disable_sctp_data_channels = false;
+
   InitWithDtls(false);
 
   SetLocalDescriptionWithDataChannel();
@@ -2578,9 +2577,6 @@
 TEST_F(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
 
-  constraints_.reset(new FakeConstraints());
-  constraints_->AddOptional(
-      webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true);
   InitWithDtls(false);
 
   talk_base::scoped_ptr<SessionDescriptionInterface> offer(CreateOffer(NULL));
@@ -2591,9 +2587,6 @@
 TEST_F(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
   SetFactoryDtlsSrtp();
-  constraints_.reset(new FakeConstraints());
-  constraints_->AddOptional(
-      webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true);
   InitWithDtls(false);
 
   // Create remote offer with SCTP.
@@ -2613,8 +2606,6 @@
 TEST_F(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) {
   constraints_.reset(new FakeConstraints());
   constraints_->AddOptional(
-      webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true);
-  constraints_->AddOptional(
       webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, false);
   InitWithDtls(false);
 
@@ -2625,24 +2616,26 @@
 TEST_F(WebRtcSessionTest, TestSctpDataChannelWithDtls) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
 
-  constraints_.reset(new FakeConstraints());
-  constraints_->AddOptional(
-      webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true);
   InitWithDtls(false);
 
   SetLocalDescriptionWithDataChannel();
   EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
 }
 
+TEST_F(WebRtcSessionTest, TestDisableSctpDataChannels) {
+  MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
+  options_.disable_sctp_data_channels = true;
+  InitWithDtls(false);
+
+  SetLocalDescriptionWithDataChannel();
+  EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type());
+}
+
 TEST_F(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) {
   MAYBE_SKIP_TEST(talk_base::SSLStreamAdapter::HaveDtlsSrtp);
   const int new_send_port = 9998;
   const int new_recv_port = 7775;
 
-  constraints_.reset(new FakeConstraints());
-  constraints_->AddOptional(
-      webrtc::MediaConstraintsInterface::kEnableSctpDataChannels, true);
-
   InitWithDtls(false);
   SetFactoryDtlsSrtp();