Revert of Separating SCTP code from BaseChannel/MediaChannel. (patchset #14 id:240001 of https://codereview.webrtc.org/2564333002/ )

Reason for revert:
Hitting DCHECK in chromium's WebrtcTransportTest.TerminateDataChannel and WebrtcTransportTest.DataStreamLate. Will investigate and reland.

Original issue's description:
> Separating SCTP code from BaseChannel/MediaChannel.
>
> The BaseChannel code is geared around RTP; the presence of media engines,
> send and receive streams, SRTP, SDP directional attribute negotiation, etc.
> It doesn't make sense to use it for SCTP as well. This separation should make
> future work both on BaseChannel and the SCTP code paths easier.
>
> SctpDataEngine now becomes SctpTransport, and is used by WebRtcSession
> directly. cricket::DataChannel is also renamed, to RtpDataChannel, so it
> doesn't get confused with webrtc::DataChannel any more.
>
> Beyond just moving code around, some consequences of this CL:
> - We'll now stop using the worker thread for SCTP. Packets will be
>   processed right on the network thread instead.
> - The SDP directional attribute is ignored, as it's supposed to be.
>
> BUG=None
>
> Review-Url: https://codereview.webrtc.org/2564333002
> Cr-Commit-Position: refs/heads/master@{#15906}
> Committed: https://chromium.googlesource.com/external/webrtc/+/67b3bbe639645ab719972682359acda303d94454

TBR=pthatcher@webrtc.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=None

Review-Url: https://codereview.webrtc.org/2614813003
Cr-Commit-Position: refs/heads/master@{#15908}
diff --git a/webrtc/api/datachannel.cc b/webrtc/api/datachannel.cc
index ad7cb57..9812e9b 100644
--- a/webrtc/api/datachannel.cc
+++ b/webrtc/api/datachannel.cc
@@ -16,7 +16,7 @@
 #include "webrtc/api/sctputils.h"
 #include "webrtc/base/logging.h"
 #include "webrtc/base/refcount.h"
-#include "webrtc/media/sctp/sctptransportinternal.h"
+#include "webrtc/media/sctp/sctpdataengine.h"
 
 namespace webrtc {
 
@@ -328,12 +328,12 @@
   }
 }
 
-void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params,
+void DataChannel::OnDataReceived(cricket::DataChannel* channel,
+                                 const cricket::ReceiveDataParams& params,
                                  const rtc::CopyOnWriteBuffer& payload) {
-  if (data_channel_type_ == cricket::DCT_RTP && params.ssrc != receive_ssrc_) {
-    return;
-  }
-  if (data_channel_type_ == cricket::DCT_SCTP && params.sid != config_.id) {
+  uint32_t expected_ssrc =
+      (data_channel_type_ == cricket::DCT_RTP) ? receive_ssrc_ : config_.id;
+  if (params.ssrc != expected_ssrc) {
     return;
   }
 
@@ -342,17 +342,17 @@
     if (handshake_state_ != kHandshakeWaitingForAck) {
       // Ignore it if we are not expecting an ACK message.
       LOG(LS_WARNING) << "DataChannel received unexpected CONTROL message, "
-                      << "sid = " << params.sid;
+                      << "sid = " << params.ssrc;
       return;
     }
     if (ParseDataChannelOpenAckMessage(payload)) {
       // We can send unordered as soon as we receive the ACK message.
       handshake_state_ = kHandshakeReady;
       LOG(LS_INFO) << "DataChannel received OPEN_ACK message, sid = "
-                   << params.sid;
+                   << params.ssrc;
     } else {
       LOG(LS_WARNING) << "DataChannel failed to parse OPEN_ACK message, sid = "
-                      << params.sid;
+                      << params.ssrc;
     }
     return;
   }
@@ -360,7 +360,7 @@
   ASSERT(params.type == cricket::DMT_BINARY ||
          params.type == cricket::DMT_TEXT);
 
-  LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.sid;
+  LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.ssrc;
   // We can send unordered as soon as we receive any DATA message since the
   // remote side must have received the OPEN (and old clients do not send
   // OPEN_ACK).
@@ -390,8 +390,9 @@
   }
 }
 
-void DataChannel::OnStreamClosedRemotely(int sid) {
-  if (data_channel_type_ == cricket::DCT_SCTP && sid == config_.id) {
+void DataChannel::OnStreamClosedRemotely(uint32_t sid) {
+  if (data_channel_type_ == cricket::DCT_SCTP &&
+      sid == static_cast<uint32_t>(config_.id)) {
     Close();
   }
 }
@@ -550,7 +551,7 @@
 
     send_params.max_rtx_count = config_.maxRetransmits;
     send_params.max_rtx_ms = config_.maxRetransmitTime;
-    send_params.sid = config_.id;
+    send_params.ssrc = config_.id;
   } else {
     send_params.ssrc = send_ssrc_;
   }
@@ -622,7 +623,7 @@
          (!is_open_message || !config_.negotiated));
 
   cricket::SendDataParams send_params;
-  send_params.sid = config_.id;
+  send_params.ssrc = config_.id;
   // Send data as ordered before we receive any message from the remote peer to
   // make sure the remote peer will not receive any data before it receives the
   // OPEN message.
diff --git a/webrtc/api/datachannel.h b/webrtc/api/datachannel.h
index 19f95df..9208ada 100644
--- a/webrtc/api/datachannel.h
+++ b/webrtc/api/datachannel.h
@@ -144,10 +144,11 @@
   // stream on an existing DataMediaChannel, and we've finished negotiation.
   void OnChannelReady(bool writable);
 
-  // Slots for provider to connect signals to.
-  void OnDataReceived(const cricket::ReceiveDataParams& params,
+  // Sigslots from cricket::DataChannel
+  void OnDataReceived(cricket::DataChannel* channel,
+                      const cricket::ReceiveDataParams& params,
                       const rtc::CopyOnWriteBuffer& payload);
-  void OnStreamClosedRemotely(int sid);
+  void OnStreamClosedRemotely(uint32_t sid);
 
   // The remote peer request that this channel should be closed.
   void RemotePeerRequestClose();
diff --git a/webrtc/api/datachannel_unittest.cc b/webrtc/api/datachannel_unittest.cc
index 4ce1be5..a8c8361 100644
--- a/webrtc/api/datachannel_unittest.cc
+++ b/webrtc/api/datachannel_unittest.cc
@@ -329,7 +329,7 @@
   params.type = cricket::DMT_CONTROL;
   rtc::CopyOnWriteBuffer payload;
   webrtc::WriteDataChannelOpenAckMessage(&payload);
-  dc->OnDataReceived(params, payload);
+  dc->OnDataReceived(NULL, params, payload);
 
   // Sends another message and verifies it's unordered.
   ASSERT_TRUE(dc->Send(buffer));
@@ -353,7 +353,7 @@
   params.ssrc = init.id;
   params.type = cricket::DMT_TEXT;
   webrtc::DataBuffer buffer("data");
-  dc->OnDataReceived(params, buffer.data);
+  dc->OnDataReceived(NULL, params, buffer.data);
 
   // Sends a message and verifies it's unordered.
   ASSERT_TRUE(dc->Send(buffer));
@@ -414,7 +414,7 @@
   cricket::ReceiveDataParams params;
   params.ssrc = 0;
   webrtc::DataBuffer buffer("abcd");
-  webrtc_data_channel_->OnDataReceived(params, buffer.data);
+  webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data);
 
   EXPECT_EQ(0U, observer_->messages_received());
 }
@@ -430,7 +430,7 @@
   params.ssrc = 1;
   webrtc::DataBuffer buffer("abcd");
 
-  webrtc_data_channel_->OnDataReceived(params, buffer.data);
+  webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data);
   EXPECT_EQ(1U, observer_->messages_received());
 }
 
@@ -472,9 +472,9 @@
   EXPECT_EQ(0U, webrtc_data_channel_->bytes_received());
 
   // Receive three buffers while data channel isn't open.
-  webrtc_data_channel_->OnDataReceived(params, buffers[0].data);
-  webrtc_data_channel_->OnDataReceived(params, buffers[1].data);
-  webrtc_data_channel_->OnDataReceived(params, buffers[2].data);
+  webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[0].data);
+  webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[1].data);
+  webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[2].data);
   EXPECT_EQ(0U, observer_->messages_received());
   EXPECT_EQ(0U, webrtc_data_channel_->messages_received());
   EXPECT_EQ(0U, webrtc_data_channel_->bytes_received());
@@ -488,9 +488,9 @@
   EXPECT_EQ(bytes_received, webrtc_data_channel_->bytes_received());
 
   // Receive three buffers while open.
-  webrtc_data_channel_->OnDataReceived(params, buffers[3].data);
-  webrtc_data_channel_->OnDataReceived(params, buffers[4].data);
-  webrtc_data_channel_->OnDataReceived(params, buffers[5].data);
+  webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[3].data);
+  webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[4].data);
+  webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[5].data);
   bytes_received += buffers[3].size() + buffers[4].size() + buffers[5].size();
   EXPECT_EQ(6U, observer_->messages_received());
   EXPECT_EQ(6U, webrtc_data_channel_->messages_received());
@@ -593,7 +593,7 @@
 
   // Receiving data without having an observer will overflow the buffer.
   for (size_t i = 0; i < 16 * 1024 + 1; ++i) {
-    webrtc_data_channel_->OnDataReceived(params, buffer);
+    webrtc_data_channel_->OnDataReceived(NULL, params, buffer);
   }
   EXPECT_EQ(webrtc::DataChannelInterface::kClosed,
             webrtc_data_channel_->state());
diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc
index f8bea1b..cdf5a97 100644
--- a/webrtc/api/peerconnection.cc
+++ b/webrtc/api/peerconnection.cc
@@ -38,7 +38,7 @@
 #include "webrtc/base/trace_event.h"
 #include "webrtc/call/call.h"
 #include "webrtc/logging/rtc_event_log/rtc_event_log.h"
-#include "webrtc/media/sctp/sctptransport.h"
+#include "webrtc/media/sctp/sctpdataengine.h"
 #include "webrtc/pc/channelmanager.h"
 #include "webrtc/system_wrappers/include/field_trial.h"
 
@@ -652,14 +652,7 @@
       std::unique_ptr<cricket::TransportController>(
           factory_->CreateTransportController(
               port_allocator_.get(),
-              configuration.redetermine_role_on_ice_restart)),
-#ifdef HAVE_SCTP
-      std::unique_ptr<cricket::SctpTransportInternalFactory>(
-          new cricket::SctpTransportFactory(factory_->network_thread()))
-#else
-      nullptr
-#endif
-          ));
+              configuration.redetermine_role_on_ice_restart))));
 
   stats_.reset(new StatsCollector(this));
   stats_collector_ = RTCStatsCollector::Create(this);
@@ -1126,7 +1119,7 @@
   // SCTP sids.
   rtc::SSLRole role;
   if (session_->data_channel_type() == cricket::DCT_SCTP &&
-      session_->GetSctpSslRole(&role)) {
+      session_->GetSslRole(session_->data_channel(), &role)) {
     AllocateSctpSids(role);
   }
 
@@ -1208,7 +1201,7 @@
   // SCTP sids.
   rtc::SSLRole role;
   if (session_->data_channel_type() == cricket::DCT_SCTP &&
-      session_->GetSctpSslRole(&role)) {
+      session_->GetSslRole(session_->data_channel(), &role)) {
     AllocateSctpSids(role);
   }
 
@@ -2150,7 +2143,7 @@
   if (session_->data_channel_type() == cricket::DCT_SCTP) {
     if (new_config.id < 0) {
       rtc::SSLRole role;
-      if ((session_->GetSctpSslRole(&role)) &&
+      if ((session_->GetSslRole(session_->data_channel(), &role)) &&
           !sid_allocator_.AllocateSid(role, &new_config.id)) {
         LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
         return nullptr;
diff --git a/webrtc/api/peerconnectioninterface_unittest.cc b/webrtc/api/peerconnectioninterface_unittest.cc
index d6da24e..c52df41 100644
--- a/webrtc/api/peerconnectioninterface_unittest.cc
+++ b/webrtc/api/peerconnectioninterface_unittest.cc
@@ -35,7 +35,7 @@
 #include "webrtc/base/stringutils.h"
 #include "webrtc/base/thread.h"
 #include "webrtc/media/base/fakevideocapturer.h"
-#include "webrtc/media/sctp/sctptransportinternal.h"
+#include "webrtc/media/sctp/sctpdataengine.h"
 #include "webrtc/p2p/base/fakeportallocator.h"
 #include "webrtc/p2p/base/faketransportcontroller.h"
 #include "webrtc/pc/mediasession.h"
diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc
index dc14970..3ab7ac9 100644
--- a/webrtc/api/rtcstatscollector.cc
+++ b/webrtc/api/rtcstatscollector.cc
@@ -463,16 +463,10 @@
           ChannelNamePair(pc_->session()->video_channel()->content_name(),
                           pc_->session()->video_channel()->transport_name()));
     }
-    if (pc_->session()->rtp_data_channel()) {
-      channel_name_pairs_->data =
-          rtc::Optional<ChannelNamePair>(ChannelNamePair(
-              pc_->session()->rtp_data_channel()->content_name(),
-              pc_->session()->rtp_data_channel()->transport_name()));
-    }
-    if (pc_->session()->sctp_content_name()) {
+    if (pc_->session()->data_channel()) {
       channel_name_pairs_->data = rtc::Optional<ChannelNamePair>(
-          ChannelNamePair(*pc_->session()->sctp_content_name(),
-                          *pc_->session()->sctp_transport_name()));
+          ChannelNamePair(pc_->session()->data_channel()->content_name(),
+                          pc_->session()->data_channel()->transport_name()));
     }
     media_info_.reset(PrepareMediaInfo_s().release());
 
diff --git a/webrtc/api/test/mock_webrtcsession.h b/webrtc/api/test/mock_webrtcsession.h
index ae75035..7fefad8 100644
--- a/webrtc/api/test/mock_webrtcsession.h
+++ b/webrtc/api/test/mock_webrtcsession.h
@@ -15,7 +15,6 @@
 #include <string>
 
 #include "webrtc/api/webrtcsession.h"
-#include "webrtc/media/sctp/sctptransportinternal.h"
 #include "webrtc/test/gmock.h"
 
 namespace webrtc {
@@ -36,8 +35,7 @@
             std::unique_ptr<cricket::TransportController>(
                 new cricket::TransportController(rtc::Thread::Current(),
                                                  rtc::Thread::Current(),
-                                                 nullptr)),
-            std::unique_ptr<cricket::SctpTransportInternalFactory>()) {}
+                                                 nullptr))) {}
   MOCK_METHOD0(voice_channel, cricket::VoiceChannel*());
   MOCK_METHOD0(video_channel, cricket::VideoChannel*());
   // Libjingle uses "local" for a outgoing track, and "remote" for a incoming
diff --git a/webrtc/api/webrtcsdp.cc b/webrtc/api/webrtcsdp.cc
index 2c1ba64..e5ee416 100644
--- a/webrtc/api/webrtcsdp.cc
+++ b/webrtc/api/webrtcsdp.cc
@@ -33,7 +33,7 @@
 #include "webrtc/media/base/cryptoparams.h"
 #include "webrtc/media/base/mediaconstants.h"
 #include "webrtc/media/base/rtputils.h"
-#include "webrtc/media/sctp/sctptransportinternal.h"
+#include "webrtc/media/sctp/sctpdataengine.h"
 #include "webrtc/p2p/base/candidate.h"
 #include "webrtc/p2p/base/p2pconstants.h"
 #include "webrtc/p2p/base/port.h"
diff --git a/webrtc/api/webrtcsession.cc b/webrtc/api/webrtcsession.cc
index dad485c..5d414ba 100644
--- a/webrtc/api/webrtcsession.cc
+++ b/webrtc/api/webrtcsession.cc
@@ -33,7 +33,6 @@
 #include "webrtc/call/call.h"
 #include "webrtc/media/base/mediaconstants.h"
 #include "webrtc/media/base/videocapturer.h"
-#include "webrtc/media/sctp/sctptransportinternal.h"
 #include "webrtc/p2p/base/portallocator.h"
 #include "webrtc/p2p/base/transportchannel.h"
 #include "webrtc/pc/channel.h"
@@ -75,9 +74,9 @@
     "Called with SDP without ice-ufrag and ice-pwd.";
 const char kSessionError[] = "Session error code: ";
 const char kSessionErrorDesc[] = "Session error description: ";
-const char kDtlsSrtpSetupFailureRtp[] =
+const char kDtlsSetupFailureRtp[] =
     "Couldn't set up DTLS-SRTP on RTP channel.";
-const char kDtlsSrtpSetupFailureRtcp[] =
+const char kDtlsSetupFailureRtcp[] =
     "Couldn't set up DTLS-SRTP on RTCP channel.";
 const char kEnableBundleFailed[] = "Failed to enable BUNDLE.";
 
@@ -292,31 +291,6 @@
   return false;
 }
 
-// Get the SCTP port out of a SessionDescription.
-// Return -1 if not found.
-static int GetSctpPort(const SessionDescription* session_description) {
-  const ContentInfo* content_info = GetFirstDataContent(session_description);
-  RTC_DCHECK(content_info);
-  if (!content_info) {
-    return -1;
-  }
-  const cricket::DataContentDescription* data =
-      static_cast<const cricket::DataContentDescription*>(
-          (content_info->description));
-  std::string value;
-  cricket::DataCodec match_pattern(cricket::kGoogleSctpDataCodecPlType,
-                                   cricket::kGoogleSctpDataCodecName);
-  for (const cricket::DataCodec& codec : data->codecs()) {
-    if (!codec.Matches(match_pattern)) {
-      continue;
-    }
-    if (codec.GetParam(cricket::kCodecParamPort, &value)) {
-      return rtc::FromString<int>(value);
-    }
-  }
-  return -1;
-}
-
 static bool BadSdp(const std::string& source,
                    const std::string& type,
                    const std::string& reason,
@@ -466,8 +440,7 @@
     rtc::Thread* worker_thread,
     rtc::Thread* signaling_thread,
     cricket::PortAllocator* port_allocator,
-    std::unique_ptr<cricket::TransportController> transport_controller,
-    std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory)
+    std::unique_ptr<cricket::TransportController> transport_controller)
     : network_thread_(network_thread),
       worker_thread_(worker_thread),
       signaling_thread_(signaling_thread),
@@ -476,7 +449,6 @@
       // Due to this constraint session id |sid_| is max limited to LLONG_MAX.
       sid_(rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX)),
       transport_controller_(std::move(transport_controller)),
-      sctp_factory_(std::move(sctp_factory)),
       media_controller_(media_controller),
       channel_manager_(media_controller_->channel_manager()),
       ice_observer_(NULL),
@@ -498,7 +470,7 @@
   transport_controller_->SignalCandidatesRemoved.connect(
       this, &WebRtcSession::OnTransportControllerCandidatesRemoved);
   transport_controller_->SignalDtlsHandshakeError.connect(
-      this, &WebRtcSession::OnTransportControllerDtlsHandshakeError);
+      this, &WebRtcSession::OnDtlsHandshakeError);
 }
 
 WebRtcSession::~WebRtcSession() {
@@ -513,14 +485,9 @@
     SignalVoiceChannelDestroyed();
     channel_manager_->DestroyVoiceChannel(voice_channel_.release());
   }
-  if (rtp_data_channel_) {
+  if (data_channel_) {
     SignalDataChannelDestroyed();
-    channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release());
-  }
-  if (sctp_transport_) {
-    SignalDataChannelDestroyed();
-    network_thread_->Invoke<void>(
-        RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
+    channel_manager_->DestroyDataChannel(data_channel_.release());
   }
 #ifdef HAVE_QUIC
   if (quic_data_transport_) {
@@ -630,10 +597,9 @@
 void WebRtcSession::Close() {
   SetState(STATE_CLOSED);
   RemoveUnusedChannels(nullptr);
-  RTC_DCHECK(!voice_channel_);
-  RTC_DCHECK(!video_channel_);
-  RTC_DCHECK(!rtp_data_channel_);
-  RTC_DCHECK(!sctp_transport_);
+  ASSERT(!voice_channel_);
+  ASSERT(!video_channel_);
+  ASSERT(!data_channel_);
   media_controller_->Close();
 }
 
@@ -645,9 +611,8 @@
   if (video_channel() && video_channel()->content_name() == content_name) {
     return video_channel();
   }
-  if (rtp_data_channel() &&
-      rtp_data_channel()->content_name() == content_name) {
-    return rtp_data_channel();
+  if (data_channel() && data_channel()->content_name() == content_name) {
+    return data_channel();
   }
   return nullptr;
 }
@@ -656,31 +621,20 @@
   return webrtc_session_desc_factory_->SdesPolicy();
 }
 
-bool WebRtcSession::GetSctpSslRole(rtc::SSLRole* role) {
-  if (!local_description() || !remote_description()) {
-    LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the "
-                 << "SSL Role of the SCTP transport.";
-    return false;
-  }
-  if (!sctp_transport_) {
-    LOG(LS_INFO) << "Non-rejected SCTP m= section is needed to get the "
-                 << "SSL Role of the SCTP transport.";
-    return false;
-  }
-
-  return transport_controller_->GetSslRole(*sctp_transport_name_, role);
-}
-
-bool WebRtcSession::GetSslRole(const std::string& content_name,
+bool WebRtcSession::GetSslRole(const std::string& transport_name,
                                rtc::SSLRole* role) {
   if (!local_description() || !remote_description()) {
-    LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the "
+    LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
                  << "SSL Role of the session.";
     return false;
   }
 
-  return transport_controller_->GetSslRole(GetTransportName(content_name),
-                                           role);
+  return transport_controller_->GetSslRole(transport_name, role);
+}
+
+bool WebRtcSession::GetSslRole(const cricket::BaseChannel* channel,
+                               rtc::SSLRole* role) {
+  return channel && GetSslRole(channel->transport_name(), role);
 }
 
 void WebRtcSession::CreateOffer(
@@ -964,27 +918,9 @@
     }
   };
 
-  bool ret = (set_content(voice_channel()) && set_content(video_channel()) &&
-              set_content(rtp_data_channel()));
-  // Need complete offer/answer before starting SCTP, according to
-  // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19
-  if (sctp_transport_ && local_description() && remote_description()) {
-    ret &= network_thread_->Invoke<bool>(
-        RTC_FROM_HERE,
-        rtc::Bind(&WebRtcSession::PushdownSctpParameters_n, this, source));
-  }
-  return ret;
-}
-
-bool WebRtcSession::PushdownSctpParameters_n(cricket::ContentSource source) {
-  RTC_DCHECK(network_thread_->IsCurrent());
-  RTC_DCHECK(local_description());
-  RTC_DCHECK(remote_description());
-  // Apply the SCTP port (which is hidden inside a DataCodec structure...)
-  // When we support "max-message-size", that would also be pushed down here.
-  return sctp_transport_->Start(
-      GetSctpPort(local_description()->description()),
-      GetSctpPort(remote_description()->description()));
+  return (set_content(voice_channel()) &&
+          set_content(video_channel()) &&
+          set_content(data_channel()));
 }
 
 bool WebRtcSession::PushdownTransportDescription(cricket::ContentSource source,
@@ -1056,6 +992,46 @@
   return true;
 }
 
+std::unique_ptr<SessionStats> WebRtcSession::GetStats_s() {
+  ASSERT(signaling_thread()->IsCurrent());
+  ChannelNamePairs channel_name_pairs;
+  if (voice_channel()) {
+    channel_name_pairs.voice = rtc::Optional<ChannelNamePair>(ChannelNamePair(
+        voice_channel()->content_name(), voice_channel()->transport_name()));
+  }
+  if (video_channel()) {
+    channel_name_pairs.video = rtc::Optional<ChannelNamePair>(ChannelNamePair(
+        video_channel()->content_name(), video_channel()->transport_name()));
+  }
+  if (data_channel()) {
+    channel_name_pairs.data = rtc::Optional<ChannelNamePair>(ChannelNamePair(
+        data_channel()->content_name(), data_channel()->transport_name()));
+  }
+  return GetStats(channel_name_pairs);
+}
+
+std::unique_ptr<SessionStats> WebRtcSession::GetStats(
+    const ChannelNamePairs& channel_name_pairs) {
+  if (network_thread()->IsCurrent()) {
+    return GetStats_n(channel_name_pairs);
+  }
+  return network_thread()->Invoke<std::unique_ptr<SessionStats>>(
+      RTC_FROM_HERE,
+      rtc::Bind(&WebRtcSession::GetStats_n, this, channel_name_pairs));
+}
+
+bool WebRtcSession::GetLocalCertificate(
+    const std::string& transport_name,
+    rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
+  return transport_controller_->GetLocalCertificate(transport_name,
+                                                    certificate);
+}
+
+std::unique_ptr<rtc::SSLCertificate> WebRtcSession::GetRemoteSSLCertificate(
+    const std::string& transport_name) {
+  return transport_controller_->GetRemoteSSLCertificate(transport_name);
+}
+
 bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
   const std::string* first_content_name = bundle.FirstContentName();
   if (!first_content_name) {
@@ -1063,6 +1039,7 @@
     return false;
   }
   const std::string& transport_name = *first_content_name;
+  cricket::BaseChannel* first_channel = GetChannel(transport_name);
 
 #ifdef HAVE_QUIC
   if (quic_data_transport_ &&
@@ -1073,8 +1050,8 @@
   }
 #endif
 
-  auto maybe_set_transport = [this, bundle,
-                              transport_name](cricket::BaseChannel* ch) {
+  auto maybe_set_transport = [this, bundle, transport_name,
+                              first_channel](cricket::BaseChannel* ch) {
     if (!ch || !bundle.HasContentName(ch->content_name())) {
       return true;
     }
@@ -1096,21 +1073,9 @@
 
   if (!maybe_set_transport(voice_channel()) ||
       !maybe_set_transport(video_channel()) ||
-      !maybe_set_transport(rtp_data_channel())) {
+      !maybe_set_transport(data_channel())) {
     return false;
   }
-  // For SCTP, transport creation/deletion happens here instead of in the
-  // object itself.
-  if (sctp_transport_) {
-    RTC_DCHECK(sctp_transport_name_);
-    RTC_DCHECK(sctp_content_name_);
-    if (transport_name != *sctp_transport_name_ &&
-        bundle.HasContentName(*sctp_content_name_)) {
-      network_thread_->Invoke<void>(
-          RTC_FROM_HERE, rtc::Bind(&WebRtcSession::ChangeSctpTransport_n, this,
-                                   transport_name));
-    }
-  }
 
   return true;
 }
@@ -1283,129 +1248,60 @@
 bool WebRtcSession::SendData(const cricket::SendDataParams& params,
                              const rtc::CopyOnWriteBuffer& payload,
                              cricket::SendDataResult* result) {
-  if (!rtp_data_channel_ && !sctp_transport_) {
-    LOG(LS_ERROR) << "SendData called when rtp_data_channel_ "
-                  << "and sctp_transport_ are NULL.";
+  if (!data_channel_) {
+    LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
     return false;
   }
-  return rtp_data_channel_
-             ? rtp_data_channel_->SendData(params, payload, result)
-             : network_thread_->Invoke<bool>(
-                   RTC_FROM_HERE,
-                   Bind(&cricket::SctpTransportInternal::SendData,
-                        sctp_transport_.get(), params, payload, result));
+  return data_channel_->SendData(params, payload, result);
 }
 
 bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
-  if (!rtp_data_channel_ && !sctp_transport_) {
+  if (!data_channel_) {
     // Don't log an error here, because DataChannels are expected to call
     // ConnectDataChannel in this state. It's the only way to initially tell
     // whether or not the underlying transport is ready.
     return false;
   }
-  if (rtp_data_channel_) {
-    rtp_data_channel_->SignalReadyToSendData.connect(
-        webrtc_data_channel, &DataChannel::OnChannelReady);
-    rtp_data_channel_->SignalDataReceived.connect(webrtc_data_channel,
-                                                  &DataChannel::OnDataReceived);
-  } else {
-    SignalSctpReadyToSendData.connect(webrtc_data_channel,
-                                      &DataChannel::OnChannelReady);
-    SignalSctpDataReceived.connect(webrtc_data_channel,
-                                   &DataChannel::OnDataReceived);
-    SignalSctpStreamClosedRemotely.connect(
-        webrtc_data_channel, &DataChannel::OnStreamClosedRemotely);
-  }
+  data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
+                                               &DataChannel::OnChannelReady);
+  data_channel_->SignalDataReceived.connect(webrtc_data_channel,
+                                            &DataChannel::OnDataReceived);
+  data_channel_->SignalStreamClosedRemotely.connect(
+      webrtc_data_channel, &DataChannel::OnStreamClosedRemotely);
   return true;
 }
 
 void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
-  if (!rtp_data_channel_ && !sctp_transport_) {
-    LOG(LS_ERROR) << "DisconnectDataChannel called when rtp_data_channel_ and "
-                     "sctp_transport_ are NULL.";
+  if (!data_channel_) {
+    LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL.";
     return;
   }
-  if (rtp_data_channel_) {
-    rtp_data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
-    rtp_data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
-  } else {
-    SignalSctpReadyToSendData.disconnect(webrtc_data_channel);
-    SignalSctpDataReceived.disconnect(webrtc_data_channel);
-    SignalSctpStreamClosedRemotely.disconnect(webrtc_data_channel);
-  }
+  data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
+  data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
+  data_channel_->SignalStreamClosedRemotely.disconnect(webrtc_data_channel);
 }
 
 void WebRtcSession::AddSctpDataStream(int sid) {
-  if (!sctp_transport_) {
-    LOG(LS_ERROR) << "AddSctpDataStream called when sctp_transport_ is NULL.";
+  if (!data_channel_) {
+    LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL.";
     return;
   }
-  network_thread_->Invoke<void>(
-      RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream,
-                               sctp_transport_.get(), sid));
+  data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid));
+  data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid));
 }
 
 void WebRtcSession::RemoveSctpDataStream(int sid) {
-  if (!sctp_transport_) {
-    LOG(LS_ERROR) << "RemoveSctpDataStream called when sctp_transport_ is "
+  if (!data_channel_) {
+    LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is "
                   << "NULL.";
     return;
   }
-  network_thread_->Invoke<void>(
-      RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream,
-                               sctp_transport_.get(), sid));
+  data_channel_->RemoveRecvStream(sid);
+  data_channel_->RemoveSendStream(sid);
 }
 
 bool WebRtcSession::ReadyToSendData() const {
-  return (rtp_data_channel_ && rtp_data_channel_->ready_to_send_data()) ||
-         sctp_ready_to_send_data_;
-}
-
-std::unique_ptr<SessionStats> WebRtcSession::GetStats_s() {
-  ASSERT(signaling_thread()->IsCurrent());
-  ChannelNamePairs channel_name_pairs;
-  if (voice_channel()) {
-    channel_name_pairs.voice = rtc::Optional<ChannelNamePair>(ChannelNamePair(
-        voice_channel()->content_name(), voice_channel()->transport_name()));
-  }
-  if (video_channel()) {
-    channel_name_pairs.video = rtc::Optional<ChannelNamePair>(ChannelNamePair(
-        video_channel()->content_name(), video_channel()->transport_name()));
-  }
-  if (rtp_data_channel()) {
-    channel_name_pairs.data = rtc::Optional<ChannelNamePair>(
-        ChannelNamePair(rtp_data_channel()->content_name(),
-                        rtp_data_channel()->transport_name()));
-  }
-  if (sctp_transport_) {
-    RTC_DCHECK(sctp_content_name_);
-    RTC_DCHECK(sctp_transport_name_);
-    channel_name_pairs.data = rtc::Optional<ChannelNamePair>(
-        ChannelNamePair(*sctp_content_name_, *sctp_transport_name_));
-  }
-  return GetStats(channel_name_pairs);
-}
-
-std::unique_ptr<SessionStats> WebRtcSession::GetStats(
-    const ChannelNamePairs& channel_name_pairs) {
-  if (network_thread()->IsCurrent()) {
-    return GetStats_n(channel_name_pairs);
-  }
-  return network_thread()->Invoke<std::unique_ptr<SessionStats>>(
-      RTC_FROM_HERE,
-      rtc::Bind(&WebRtcSession::GetStats_n, this, channel_name_pairs));
-}
-
-bool WebRtcSession::GetLocalCertificate(
-    const std::string& transport_name,
-    rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
-  return transport_controller_->GetLocalCertificate(transport_name,
-                                                    certificate);
-}
-
-std::unique_ptr<rtc::SSLCertificate> WebRtcSession::GetRemoteSSLCertificate(
-    const std::string& transport_name) {
-  return transport_controller_->GetRemoteSSLCertificate(transport_name);
+  return data_channel_ && data_channel_->ready_to_send_data();
 }
 
 cricket::DataChannelType WebRtcSession::data_channel_type() const {
@@ -1430,11 +1326,6 @@
   transport_controller_->SetLocalCertificate(certificate);
 }
 
-void WebRtcSession::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) {
-  SetError(ERROR_TRANSPORT,
-           rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp);
-}
-
 bool WebRtcSession::waiting_for_certificate_for_testing() const {
   return webrtc_session_desc_factory_->waiting_for_certificate_for_testing();
 }
@@ -1564,16 +1455,7 @@
   }
 }
 
-void WebRtcSession::OnTransportControllerDtlsHandshakeError(
-    rtc::SSLHandshakeError error) {
-  if (metrics_observer_) {
-    metrics_observer_->IncrementEnumCounter(
-        webrtc::kEnumCounterDtlsHandshakeError, static_cast<int>(error),
-        static_cast<int>(rtc::SSLHandshakeError::MAX_VALUE));
-  }
-}
-
-// Enabling voice and video (and RTP data) channel.
+// Enabling voice and video channel.
 void WebRtcSession::EnableChannels() {
   if (voice_channel_ && !voice_channel_->enabled())
     voice_channel_->Enable(true);
@@ -1581,8 +1463,8 @@
   if (video_channel_ && !video_channel_->enabled())
     video_channel_->Enable(true);
 
-  if (rtp_data_channel_ && !rtp_data_channel_->enabled())
-    rtp_data_channel_->Enable(true);
+  if (data_channel_ && !data_channel_->enabled())
+    data_channel_->Enable(true);
 }
 
 // Returns the media index for a local ice candidate given the content name.
@@ -1692,15 +1574,9 @@
   const cricket::ContentInfo* data_info =
       cricket::GetFirstDataContent(desc);
   if (!data_info || data_info->rejected) {
-    if (rtp_data_channel_) {
+    if (data_channel_) {
       SignalDataChannelDestroyed();
-      channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release());
-    }
-    if (sctp_transport_) {
-      SignalDataChannelDestroyed();
-      network_thread_->Invoke<void>(
-          RTC_FROM_HERE,
-          rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
+      channel_manager_->DestroyDataChannel(data_channel_.release());
     }
 #ifdef HAVE_QUIC
     // Clean up the existing QuicDataTransport and its QuicTransportChannels.
@@ -1761,8 +1637,8 @@
   }
 
   const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
-  if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected &&
-      !rtp_data_channel_ && !sctp_transport_) {
+  if (data_channel_type_ != cricket::DCT_NONE &&
+      data && !data->rejected && !data_channel_) {
     if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) {
       LOG(LS_ERROR) << "Failed to create data channel.";
       return false;
@@ -1788,8 +1664,8 @@
     voice_channel_->ActivateRtcpMux();
   }
 
-  voice_channel_->SignalDtlsSrtpSetupFailure.connect(
-      this, &WebRtcSession::OnDtlsSrtpSetupFailure);
+  voice_channel_->SignalDtlsSetupFailure.connect(
+      this, &WebRtcSession::OnDtlsSetupFailure);
 
   SignalVoiceChannelCreated();
   voice_channel_->SignalSentPacket.connect(this,
@@ -1812,8 +1688,8 @@
   if (require_rtcp_mux) {
     video_channel_->ActivateRtcpMux();
   }
-  video_channel_->SignalDtlsSrtpSetupFailure.connect(
-      this, &WebRtcSession::OnDtlsSrtpSetupFailure);
+  video_channel_->SignalDtlsSetupFailure.connect(
+      this, &WebRtcSession::OnDtlsSetupFailure);
 
   SignalVideoChannelCreated();
   video_channel_->SignalSentPacket.connect(this,
@@ -1823,48 +1699,40 @@
 
 bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content,
                                       const std::string* bundle_transport) {
-  const std::string transport_name =
-      bundle_transport ? *bundle_transport : content->name;
 #ifdef HAVE_QUIC
   if (data_channel_type_ == cricket::DCT_QUIC) {
     RTC_DCHECK(transport_controller_->quic());
+    const std::string transport_name =
+        bundle_transport ? *bundle_transport : content->name;
     quic_data_transport_->SetTransport(transport_name);
     return true;
   }
 #endif  // HAVE_QUIC
   bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
-  if (sctp) {
-    if (!sctp_factory_) {
-      LOG(LS_ERROR)
-          << "Trying to create SCTP transport, but didn't compile with "
-             "SCTP support (HAVE_SCTP)";
-      return false;
-    }
-    if (!network_thread_->Invoke<bool>(
-            RTC_FROM_HERE, rtc::Bind(&WebRtcSession::CreateSctpTransport_n,
-                                     this, content->name, transport_name))) {
-      return false;
-    };
-  } else {
-    bool require_rtcp_mux =
-        rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
-    bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux;
-    rtp_data_channel_.reset(channel_manager_->CreateRtpDataChannel(
-        media_controller_, transport_controller_.get(), content->name,
-        bundle_transport, create_rtcp_transport_channel, SrtpRequired()));
-    if (!rtp_data_channel_) {
-      return false;
-    }
-    if (require_rtcp_mux) {
-      rtp_data_channel_->ActivateRtcpMux();
-    }
-    rtp_data_channel_->SignalDtlsSrtpSetupFailure.connect(
-        this, &WebRtcSession::OnDtlsSrtpSetupFailure);
-    rtp_data_channel_->SignalSentPacket.connect(this,
-                                                &WebRtcSession::OnSentPacket_w);
+  bool require_rtcp_mux =
+      rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
+  bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux;
+  data_channel_.reset(channel_manager_->CreateDataChannel(
+      media_controller_, transport_controller_.get(), content->name,
+      bundle_transport, create_rtcp_transport_channel, SrtpRequired(),
+      data_channel_type_));
+  if (!data_channel_) {
+    return false;
+  }
+  if (require_rtcp_mux) {
+    data_channel_->ActivateRtcpMux();
   }
 
+  if (sctp) {
+    data_channel_->SignalDataReceived.connect(
+        this, &WebRtcSession::OnDataChannelMessageReceived);
+  }
+
+  data_channel_->SignalDtlsSetupFailure.connect(
+      this, &WebRtcSession::OnDtlsSetupFailure);
+
   SignalDataChannelCreated();
+  data_channel_->SignalSentPacket.connect(this, &WebRtcSession::OnSentPacket_w);
   return true;
 }
 
@@ -1890,79 +1758,16 @@
   return session_stats;
 }
 
-bool WebRtcSession::CreateSctpTransport_n(const std::string& content_name,
-                                          const std::string& transport_name) {
-  RTC_DCHECK(network_thread_->IsCurrent());
-  RTC_DCHECK(sctp_factory_);
-  cricket::TransportChannel* tc =
-      transport_controller_->CreateTransportChannel_n(
-          transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
-  sctp_transport_ = sctp_factory_->CreateSctpTransport(tc);
-  RTC_DCHECK(sctp_transport_);
-  sctp_invoker_.reset(new rtc::AsyncInvoker());
-  sctp_transport_->SignalReadyToSendData.connect(
-      this, &WebRtcSession::OnSctpTransportReadyToSendData_n);
-  sctp_transport_->SignalDataReceived.connect(
-      this, &WebRtcSession::OnSctpTransportDataReceived_n);
-  sctp_transport_->SignalStreamClosedRemotely.connect(
-      this, &WebRtcSession::OnSctpStreamClosedRemotely_n);
-  sctp_transport_name_ = rtc::Optional<std::string>(transport_name);
-  sctp_content_name_ = rtc::Optional<std::string>(content_name);
-  return true;
+void WebRtcSession::OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp) {
+  SetError(ERROR_TRANSPORT,
+           rtcp ? kDtlsSetupFailureRtcp : kDtlsSetupFailureRtp);
 }
 
-void WebRtcSession::ChangeSctpTransport_n(const std::string& transport_name) {
-  RTC_DCHECK(network_thread_->IsCurrent());
-  RTC_DCHECK(sctp_transport_);
-  RTC_DCHECK(sctp_transport_name_);
-  std::string old_sctp_transport_name = *sctp_transport_name_;
-  sctp_transport_name_ = rtc::Optional<std::string>(transport_name);
-  cricket::TransportChannel* tc =
-      transport_controller_->CreateTransportChannel_n(
-          transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
-  sctp_transport_->SetTransportChannel(tc);
-  transport_controller_->DestroyTransportChannel_n(
-      old_sctp_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
-}
-
-void WebRtcSession::DestroySctpTransport_n() {
-  RTC_DCHECK(network_thread_->IsCurrent());
-  sctp_transport_.reset(nullptr);
-  sctp_content_name_.reset();
-  sctp_transport_name_.reset();
-  sctp_invoker_.reset(nullptr);
-  sctp_ready_to_send_data_ = false;
-}
-
-void WebRtcSession::OnSctpTransportReadyToSendData_n() {
-  RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP);
-  RTC_DCHECK(network_thread_->IsCurrent());
-  sctp_invoker_->AsyncInvoke<void>(
-      RTC_FROM_HERE, signaling_thread_,
-      rtc::Bind(&WebRtcSession::OnSctpTransportReadyToSendData_s, this, true));
-}
-
-void WebRtcSession::OnSctpTransportReadyToSendData_s(bool ready) {
-  RTC_DCHECK(signaling_thread_->IsCurrent());
-  sctp_ready_to_send_data_ = ready;
-  SignalSctpReadyToSendData(ready);
-}
-
-void WebRtcSession::OnSctpTransportDataReceived_n(
+void WebRtcSession::OnDataChannelMessageReceived(
+    cricket::DataChannel* channel,
     const cricket::ReceiveDataParams& params,
     const rtc::CopyOnWriteBuffer& payload) {
   RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP);
-  RTC_DCHECK(network_thread_->IsCurrent());
-  sctp_invoker_->AsyncInvoke<void>(
-      RTC_FROM_HERE, signaling_thread_,
-      rtc::Bind(&WebRtcSession::OnSctpTransportDataReceived_s, this, params,
-                payload));
-}
-
-void WebRtcSession::OnSctpTransportDataReceived_s(
-    const cricket::ReceiveDataParams& params,
-    const rtc::CopyOnWriteBuffer& payload) {
-  RTC_DCHECK(signaling_thread_->IsCurrent());
   if (params.type == cricket::DMT_CONTROL && IsOpenMessage(payload)) {
     // Received OPEN message; parse and signal that a new data channel should
     // be created.
@@ -1976,19 +1781,8 @@
     }
     config.open_handshake_role = InternalDataChannelInit::kAcker;
     SignalDataChannelOpenMessage(label, config);
-  } else {
-    // Otherwise just forward the signal.
-    SignalSctpDataReceived(params, payload);
   }
-}
-
-void WebRtcSession::OnSctpStreamClosedRemotely_n(int sid) {
-  RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP);
-  RTC_DCHECK(network_thread_->IsCurrent());
-  sctp_invoker_->AsyncInvoke<void>(
-      RTC_FROM_HERE, signaling_thread_,
-      rtc::Bind(&sigslot::signal1<int>::operator(),
-                &SignalSctpStreamClosedRemotely, sid));
+  // Otherwise ignore the message.
 }
 
 // Returns false if bundle is enabled and rtcp_mux is disabled.
@@ -2182,11 +1976,8 @@
   if (video_channel()) {
     transport_names.insert(video_channel()->transport_name());
   }
-  if (rtp_data_channel()) {
-    transport_names.insert(rtp_data_channel()->transport_name());
-  }
-  if (sctp_transport_name_) {
-    transport_names.insert(*sctp_transport_name_);
+  if (data_channel()) {
+    transport_names.insert(data_channel()->transport_name());
   }
   for (const auto& name : transport_names) {
     cricket::TransportStats stats;
@@ -2303,17 +2094,17 @@
       return quic_data_transport_->transport_name();
     }
 #endif
-    if (sctp_transport_) {
-      RTC_DCHECK(sctp_content_name_);
-      RTC_DCHECK(sctp_transport_name_);
-      if (content_name == *sctp_content_name_) {
-        return *sctp_transport_name_;
-      }
-    }
     // Return an empty string if failed to retrieve the transport name.
     return "";
   }
   return channel->transport_name();
 }
 
+void WebRtcSession::OnDtlsHandshakeError(rtc::SSLHandshakeError error) {
+  if (metrics_observer_) {
+    metrics_observer_->IncrementEnumCounter(
+        webrtc::kEnumCounterDtlsHandshakeError, static_cast<int>(error),
+        static_cast<int>(rtc::SSLHandshakeError::MAX_VALUE));
+  }
+}
 }  // namespace webrtc
diff --git a/webrtc/api/webrtcsession.h b/webrtc/api/webrtcsession.h
index e346112..ef31560 100644
--- a/webrtc/api/webrtcsession.h
+++ b/webrtc/api/webrtcsession.h
@@ -22,7 +22,6 @@
 #include "webrtc/api/peerconnectioninterface.h"
 #include "webrtc/api/statstypes.h"
 #include "webrtc/base/constructormagic.h"
-#include "webrtc/base/optional.h"
 #include "webrtc/base/sigslot.h"
 #include "webrtc/base/sslidentity.h"
 #include "webrtc/base/thread.h"
@@ -38,9 +37,7 @@
 namespace cricket {
 
 class ChannelManager;
-class RtpDataChannel;
-class SctpTransportInternal;
-class SctpTransportInternalFactory;
+class DataChannel;
 class StatsReport;
 class VideoChannel;
 class VoiceChannel;
@@ -70,8 +67,8 @@
 extern const char kSdpWithoutSdesAndDtlsDisabled[];
 extern const char kSessionError[];
 extern const char kSessionErrorDesc[];
-extern const char kDtlsSrtpSetupFailureRtp[];
-extern const char kDtlsSrtpSetupFailureRtcp[];
+extern const char kDtlsSetupFailureRtp[];
+extern const char kDtlsSetupFailureRtcp[];
 extern const char kEnableBundleFailed[];
 
 // Maximum number of received video streams that will be processed by webrtc
@@ -161,15 +158,13 @@
     ERROR_TRANSPORT = 2,  // transport error of some kind
   };
 
-  // |sctp_factory| may be null, in which case SCTP is treated as unsupported.
   WebRtcSession(
       webrtc::MediaControllerInterface* media_controller,
       rtc::Thread* network_thread,
       rtc::Thread* worker_thread,
       rtc::Thread* signaling_thread,
       cricket::PortAllocator* port_allocator,
-      std::unique_ptr<cricket::TransportController> transport_controller,
-      std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory);
+      std::unique_ptr<cricket::TransportController> transport_controller);
   virtual ~WebRtcSession();
 
   // These are const to allow them to be called from const methods.
@@ -204,34 +199,26 @@
     ice_observer_ = observer;
   }
 
-  // Exposed for stats collecting.
   virtual cricket::VoiceChannel* voice_channel() {
     return voice_channel_.get();
   }
   virtual cricket::VideoChannel* video_channel() {
     return video_channel_.get();
   }
-  // Only valid when using deprecated RTP data channels.
-  virtual cricket::RtpDataChannel* rtp_data_channel() {
-    return rtp_data_channel_.get();
-  }
-  virtual rtc::Optional<std::string> sctp_content_name() const {
-    return sctp_content_name_;
-  }
-  virtual rtc::Optional<std::string> sctp_transport_name() const {
-    return sctp_transport_name_;
+  virtual cricket::DataChannel* data_channel() {
+    return data_channel_.get();
   }
 
   cricket::BaseChannel* GetChannel(const std::string& content_name);
 
   cricket::SecurePolicy SdesPolicy() const;
 
-  // Get current SSL role used by SCTP's underlying transport.
-  bool GetSctpSslRole(rtc::SSLRole* role);
-  // Get SSL role for an arbitrary m= section (handles bundling correctly).
-  // TODO(deadbeef): This is only used internally by the session description
-  // factory, it shouldn't really be public).
-  bool GetSslRole(const std::string& content_name, rtc::SSLRole* role);
+  // Get current ssl role from transport.
+  bool GetSslRole(const std::string& transport_name, rtc::SSLRole* role);
+
+  // Get current SSL role for this channel's transport.
+  // If |transport| is null, returns false.
+  bool GetSslRole(const cricket::BaseChannel* channel, rtc::SSLRole* role);
 
   void CreateOffer(
       CreateSessionDescriptionObserver* observer,
@@ -245,7 +232,6 @@
   // The ownership of |desc| will be transferred after this call.
   bool SetRemoteDescription(SessionDescriptionInterface* desc,
                             std::string* err_desc);
-
   bool ProcessIceMessage(const IceCandidateInterface* ice_candidate);
 
   bool RemoveRemoteIceCandidates(
@@ -340,7 +326,7 @@
   // WebRTCSessionDescriptionFactory. Should happen before setLocalDescription.
   void OnCertificateReady(
       const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
-  void OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp);
+  void OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp);
 
   // For unit test.
   bool waiting_for_certificate_for_testing() const;
@@ -352,9 +338,8 @@
     transport_controller_->SetMetricsObserver(metrics_observer);
   }
 
-  // Called when voice_channel_, video_channel_ and
-  // rtp_data_channel_/sctp_transport_ are created and destroyed. As a result
-  // of, for example, setting a new description.
+  // Called when voice_channel_, video_channel_ and data_channel_ are created
+  // and destroyed. As a result of, for example, setting a new description.
   sigslot::signal0<> SignalVoiceChannelCreated;
   sigslot::signal0<> SignalVoiceChannelDestroyed;
   sigslot::signal0<> SignalVideoChannelCreated;
@@ -412,7 +397,6 @@
   bool PushdownMediaDescription(cricket::ContentAction action,
                                 cricket::ContentSource source,
                                 std::string* error_desc);
-  bool PushdownSctpParameters_n(cricket::ContentSource source);
 
   bool PushdownTransportDescription(cricket::ContentSource source,
                                     cricket::ContentAction action,
@@ -477,24 +461,11 @@
   std::unique_ptr<SessionStats> GetStats_n(
       const ChannelNamePairs& channel_name_pairs);
 
-  bool CreateSctpTransport_n(const std::string& content_name,
-                             const std::string& transport_name);
-  // For bundling.
-  void ChangeSctpTransport_n(const std::string& transport_name);
-  void DestroySctpTransport_n();
-  // SctpTransport signal handlers. Needed to marshal signals from the network
-  // to signaling thread.
-  void OnSctpTransportReadyToSendData_n();
-  // This may be called with "false" if the direction of the m= section causes
-  // us to tear down the SCTP connection.
-  void OnSctpTransportReadyToSendData_s(bool ready);
-  void OnSctpTransportDataReceived_n(const cricket::ReceiveDataParams& params,
-                                     const rtc::CopyOnWriteBuffer& payload);
-  // Beyond just firing the signal to the signaling thread, listens to SCTP
-  // CONTROL messages on unused SIDs and processes them as OPEN messages.
-  void OnSctpTransportDataReceived_s(const cricket::ReceiveDataParams& params,
-                                     const rtc::CopyOnWriteBuffer& payload);
-  void OnSctpStreamClosedRemotely_n(int sid);
+  // Listens to SCTP CONTROL messages on unused SIDs and process them as OPEN
+  // messages.
+  void OnDataChannelMessageReceived(cricket::DataChannel* channel,
+                                    const cricket::ReceiveDataParams& params,
+                                    const rtc::CopyOnWriteBuffer& payload);
 
   std::string BadStateErrMsg(State state);
   void SetIceConnectionState(PeerConnectionInterface::IceConnectionState state);
@@ -527,7 +498,6 @@
   // this session.
   bool SrtpRequired() const;
 
-  // TransportController signal handlers.
   void OnTransportControllerConnectionState(cricket::IceConnectionState state);
   void OnTransportControllerReceiving(bool receiving);
   void OnTransportControllerGatheringState(cricket::IceGatheringState state);
@@ -536,7 +506,6 @@
       const std::vector<cricket::Candidate>& candidates);
   void OnTransportControllerCandidatesRemoved(
       const std::vector<cricket::Candidate>& candidates);
-  void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error);
 
   std::string GetSessionErrorMsg();
 
@@ -553,6 +522,8 @@
 
   const std::string GetTransportName(const std::string& content_name);
 
+  void OnDtlsHandshakeError(rtc::SSLHandshakeError error);
+
   rtc::Thread* const network_thread_;
   rtc::Thread* const worker_thread_;
   rtc::Thread* const signaling_thread_;
@@ -565,39 +536,10 @@
   bool initial_offerer_ = false;
 
   const std::unique_ptr<cricket::TransportController> transport_controller_;
-  const std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory_;
   MediaControllerInterface* media_controller_;
   std::unique_ptr<cricket::VoiceChannel> voice_channel_;
   std::unique_ptr<cricket::VideoChannel> video_channel_;
-  // |rtp_data_channel_| is used if in RTP data channel mode, |sctp_transport_|
-  // when using SCTP.
-  std::unique_ptr<cricket::RtpDataChannel> rtp_data_channel_;
-
-  std::unique_ptr<cricket::SctpTransportInternal> sctp_transport_;
-  // |sctp_transport_name_| keeps track of what DTLS transport the SCTP
-  // transport is using (which can change due to bundling).
-  rtc::Optional<std::string> sctp_transport_name_;
-  // |sctp_content_name_| is the content name (MID) in SDP.
-  rtc::Optional<std::string> sctp_content_name_;
-  // Value cached on signaling thread. Only updated when SctpReadyToSendData
-  // fires on the signaling thread.
-  bool sctp_ready_to_send_data_ = false;
-  // Same as signals provided by SctpTransport, but these are guaranteed to
-  // fire on the signaling thread, whereas SctpTransport fires on the networking
-  // thread.
-  // |sctp_invoker_| is used so that any signals queued on the signaling thread
-  // from the network thread are immediately discarded if the SctpTransport is
-  // destroyed (due to m= section being rejected).
-  // TODO(deadbeef): Use a proxy object to ensure that method calls/signals
-  // are marshalled to the right thread. Could almost use proxy.h for this,
-  // but it doesn't have a mechanism for marshalling sigslot::signals
-  std::unique_ptr<rtc::AsyncInvoker> sctp_invoker_;
-  sigslot::signal1<bool> SignalSctpReadyToSendData;
-  sigslot::signal2<const cricket::ReceiveDataParams&,
-                   const rtc::CopyOnWriteBuffer&>
-      SignalSctpDataReceived;
-  sigslot::signal1<int> SignalSctpStreamClosedRemotely;
-
+  std::unique_ptr<cricket::DataChannel> data_channel_;
   cricket::ChannelManager* channel_manager_;
   IceObserver* ice_observer_;
   PeerConnectionInterface::IceConnectionState ice_connection_state_;
diff --git a/webrtc/api/webrtcsession_unittest.cc b/webrtc/api/webrtcsession_unittest.cc
index 7c26d1d..116c8d5 100644
--- a/webrtc/api/webrtcsession_unittest.cc
+++ b/webrtc/api/webrtcsession_unittest.cc
@@ -40,7 +40,6 @@
 #include "webrtc/media/base/fakevideorenderer.h"
 #include "webrtc/media/base/mediachannel.h"
 #include "webrtc/media/engine/fakewebrtccall.h"
-#include "webrtc/media/sctp/sctptransportinternal.h"
 #include "webrtc/p2p/base/packettransportinterface.h"
 #include "webrtc/p2p/base/stunserver.h"
 #include "webrtc/p2p/base/teststunserver.h"
@@ -110,7 +109,6 @@
 static const int kMediaContentIndex1 = 1;
 static const char kMediaContentName1[] = "video";
 
-static const int kDefaultTimeout = 10000;  // 10 seconds.
 static const int kIceCandidatesTimeout = 10000;
 // STUN timeout with all retransmissions is a total of 9500ms.
 static const int kStunTimeout = 9500;
@@ -213,52 +211,6 @@
   size_t num_candidates_removed_ = 0;
 };
 
-// Used for tests in this file to verify that WebRtcSession responds to signals
-// from the SctpTransport correctly, and calls Start with the correct
-// local/remote ports.
-class FakeSctpTransport : public cricket::SctpTransportInternal {
- public:
-  void SetTransportChannel(cricket::TransportChannel* channel) override {}
-  bool Start(int local_port, int remote_port) override {
-    local_port_ = local_port;
-    remote_port_ = remote_port;
-    return true;
-  }
-  bool OpenStream(int sid) override { return true; }
-  bool ResetStream(int sid) override { return true; }
-  bool SendData(const cricket::SendDataParams& params,
-                const rtc::CopyOnWriteBuffer& payload,
-                cricket::SendDataResult* result = nullptr) override {
-    return true;
-  }
-  bool ReadyToSendData() override { return true; }
-  void set_debug_name_for_testing(const char* debug_name) override {}
-
-  int local_port() const { return local_port_; }
-  int remote_port() const { return remote_port_; }
-
- private:
-  int local_port_ = -1;
-  int remote_port_ = -1;
-};
-
-class FakeSctpTransportFactory : public cricket::SctpTransportInternalFactory {
- public:
-  std::unique_ptr<cricket::SctpTransportInternal> CreateSctpTransport(
-      cricket::TransportChannel*) override {
-    last_fake_sctp_transport_ = new FakeSctpTransport();
-    return std::unique_ptr<cricket::SctpTransportInternal>(
-        last_fake_sctp_transport_);
-  }
-
-  FakeSctpTransport* last_fake_sctp_transport() {
-    return last_fake_sctp_transport_;
-  }
-
- private:
-  FakeSctpTransport* last_fake_sctp_transport_ = nullptr;
-};
-
 class WebRtcSessionForTest : public webrtc::WebRtcSession {
  public:
   WebRtcSessionForTest(
@@ -268,15 +220,13 @@
       rtc::Thread* signaling_thread,
       cricket::PortAllocator* port_allocator,
       webrtc::IceObserver* ice_observer,
-      std::unique_ptr<cricket::TransportController> transport_controller,
-      std::unique_ptr<FakeSctpTransportFactory> sctp_factory)
+      std::unique_ptr<cricket::TransportController> transport_controller)
       : WebRtcSession(media_controller,
                       network_thread,
                       worker_thread,
                       signaling_thread,
                       port_allocator,
-                      std::move(transport_controller),
-                      std::move(sctp_factory)) {
+                      std::move(transport_controller)) {
     RegisterIceObserver(ice_observer);
   }
   virtual ~WebRtcSessionForTest() {}
@@ -299,6 +249,14 @@
     return rtcp_transport_channel(video_channel());
   }
 
+  rtc::PacketTransportInterface* data_rtp_transport_channel() {
+    return rtp_transport_channel(data_channel());
+  }
+
+  rtc::PacketTransportInterface* data_rtcp_transport_channel() {
+    return rtcp_transport_channel(data_channel());
+  }
+
  private:
   rtc::PacketTransportInterface* rtp_transport_channel(
       cricket::BaseChannel* ch) {
@@ -428,16 +386,13 @@
       std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
       PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy) {
     ASSERT_TRUE(session_.get() == NULL);
-    fake_sctp_transport_factory_ = new FakeSctpTransportFactory();
     session_.reset(new WebRtcSessionForTest(
         media_controller_.get(), rtc::Thread::Current(), rtc::Thread::Current(),
         rtc::Thread::Current(), allocator_.get(), &observer_,
         std::unique_ptr<cricket::TransportController>(
             new cricket::TransportController(rtc::Thread::Current(),
                                              rtc::Thread::Current(),
-                                             allocator_.get())),
-        std::unique_ptr<FakeSctpTransportFactory>(
-            fake_sctp_transport_factory_)));
+                                             allocator_.get()))));
     session_->SignalDataChannelOpenMessage.connect(
         this, &WebRtcSessionTest::OnDataChannelOpenMessage);
     session_->GetOnDestroyedSignal()->connect(
@@ -1541,8 +1496,6 @@
   webrtc::RtcEventLogNullImpl event_log_;
   cricket::FakeMediaEngine* media_engine_;
   cricket::FakeDataEngine* data_engine_;
-  // Actually owned by session_.
-  FakeSctpTransportFactory* fake_sctp_transport_factory_ = nullptr;
   std::unique_ptr<cricket::ChannelManager> channel_manager_;
   cricket::FakeCall fake_call_;
   std::unique_ptr<webrtc::MediaControllerInterface> media_controller_;
@@ -3922,7 +3875,7 @@
   Init();
   SetLocalDescriptionWithDataChannel();
   ASSERT_TRUE(data_engine_);
-  EXPECT_NE(nullptr, data_engine_->GetChannel(0));
+  EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type());
 }
 
 TEST_P(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) {
@@ -3934,43 +3887,7 @@
   InitWithDtls(GetParam());
 
   SetLocalDescriptionWithDataChannel();
-  EXPECT_NE(nullptr, data_engine_->GetChannel(0));
-}
-
-// Test that sctp_content_name/sctp_transport_name (used for stats) are correct
-// before and after BUNDLE is negotiated.
-TEST_P(WebRtcSessionTest, SctpContentAndTransportName) {
-  MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
-  SetFactoryDtlsSrtp();
-  InitWithDtls(GetParam());
-
-  // Initially these fields should be empty.
-  EXPECT_FALSE(session_->sctp_content_name());
-  EXPECT_FALSE(session_->sctp_transport_name());
-
-  // Create offer with audio/video/data.
-  // Default bundle policy is "balanced", so data should be using its own
-  // transport.
-  SendAudioVideoStream1();
-  CreateDataChannel();
-  InitiateCall();
-  ASSERT_TRUE(session_->sctp_content_name());
-  ASSERT_TRUE(session_->sctp_transport_name());
-  EXPECT_EQ("data", *session_->sctp_content_name());
-  EXPECT_EQ("data", *session_->sctp_transport_name());
-
-  // Create answer that finishes BUNDLE negotiation, which means everything
-  // should be bundled on the first transport (audio).
-  cricket::MediaSessionOptions answer_options;
-  answer_options.recv_video = true;
-  answer_options.bundle_enabled = true;
-  answer_options.data_channel_type = cricket::DCT_SCTP;
-  SetRemoteDescriptionWithoutError(CreateRemoteAnswer(
-      session_->local_description(), answer_options, cricket::SEC_DISABLED));
-  ASSERT_TRUE(session_->sctp_content_name());
-  ASSERT_TRUE(session_->sctp_transport_name());
-  EXPECT_EQ("data", *session_->sctp_content_name());
-  EXPECT_EQ("audio", *session_->sctp_transport_name());
+  EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type());
 }
 
 TEST_P(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) {
@@ -4002,39 +3919,30 @@
   EXPECT_TRUE(answer->description()->GetTransportInfoByName("data") != NULL);
 }
 
-// Test that if DTLS is disabled, we don't end up with an SctpTransport
-// created (or an RtpDataChannel).
 TEST_P(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) {
   configuration_.enable_dtls_srtp = rtc::Optional<bool>(false);
   InitWithDtls(GetParam());
 
   SetLocalDescriptionWithDataChannel();
-  EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
-  EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
+  EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type());
 }
 
-// Test that if DTLS is enabled, we end up with an SctpTransport created
-// (and not an RtpDataChannel).
 TEST_P(WebRtcSessionTest, TestSctpDataChannelWithDtls) {
   MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
 
   InitWithDtls(GetParam());
 
   SetLocalDescriptionWithDataChannel();
-  EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
-  EXPECT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
+  EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
 }
 
-// Test that if SCTP is disabled, we don't end up with an SctpTransport
-// created (or an RtpDataChannel).
 TEST_P(WebRtcSessionTest, TestDisableSctpDataChannels) {
   MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
   options_.disable_sctp_data_channels = true;
   InitWithDtls(GetParam());
 
   SetLocalDescriptionWithDataChannel();
-  EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
-  EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
+  EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type());
 }
 
 TEST_P(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) {
@@ -4065,19 +3973,31 @@
 
   // TEST PLAN: Set the port number to something new, set it in the SDP,
   // and pass it all the way down.
-  EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
+  EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
   CreateDataChannel();
-  ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
-  EXPECT_EQ(
-      new_recv_port,
-      fake_sctp_transport_factory_->last_fake_sctp_transport()->local_port());
-  EXPECT_EQ(
-      new_send_port,
-      fake_sctp_transport_factory_->last_fake_sctp_transport()->remote_port());
+
+  cricket::FakeDataMediaChannel* ch = data_engine_->GetChannel(0);
+  int portnum = -1;
+  ASSERT_TRUE(ch != NULL);
+  ASSERT_EQ(1UL, ch->send_codecs().size());
+  EXPECT_EQ(cricket::kGoogleSctpDataCodecPlType, ch->send_codecs()[0].id);
+  EXPECT_EQ(0, strcmp(cricket::kGoogleSctpDataCodecName,
+                      ch->send_codecs()[0].name.c_str()));
+  EXPECT_TRUE(ch->send_codecs()[0].GetParam(cricket::kCodecParamPort,
+                                            &portnum));
+  EXPECT_EQ(new_send_port, portnum);
+
+  ASSERT_EQ(1UL, ch->recv_codecs().size());
+  EXPECT_EQ(cricket::kGoogleSctpDataCodecPlType, ch->recv_codecs()[0].id);
+  EXPECT_EQ(0, strcmp(cricket::kGoogleSctpDataCodecName,
+                      ch->recv_codecs()[0].name.c_str()));
+  EXPECT_TRUE(ch->recv_codecs()[0].GetParam(cricket::kCodecParamPort,
+                                            &portnum));
+  EXPECT_EQ(new_recv_port, portnum);
 }
 
-// Verifies that when a session's SctpTransport receives an OPEN message,
-// WebRtcSession signals the SctpTransport creation request with the expected
+// Verifies that when a session's DataChannel receives an OPEN message,
+// WebRtcSession signals the DataChannel creation request with the expected
 // config.
 TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) {
   MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
@@ -4085,10 +4005,8 @@
   InitWithDtls(GetParam());
 
   SetLocalDescriptionWithDataChannel();
-  EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
-  ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
+  EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
 
-  // Make the fake SCTP transport pretend it received an OPEN message.
   webrtc::DataChannelInit config;
   config.id = 1;
   rtc::CopyOnWriteBuffer payload;
@@ -4096,10 +4014,11 @@
   cricket::ReceiveDataParams params;
   params.ssrc = config.id;
   params.type = cricket::DMT_CONTROL;
-  fake_sctp_transport_factory_->last_fake_sctp_transport()->SignalDataReceived(
-      params, payload);
 
-  EXPECT_EQ_WAIT("a", last_data_channel_label_, kDefaultTimeout);
+  cricket::DataChannel* data_channel = session_->data_channel();
+  data_channel->SignalDataReceived(data_channel, params, payload);
+
+  EXPECT_EQ("a", last_data_channel_label_);
   EXPECT_EQ(config.id, last_data_channel_config_.id);
   EXPECT_FALSE(last_data_channel_config_.negotiated);
   EXPECT_EQ(webrtc::InternalDataChannelInit::kAcker,
diff --git a/webrtc/api/webrtcsessiondescriptionfactory.cc b/webrtc/api/webrtcsessiondescriptionfactory.cc
index 0ab458b..1f811f5 100644
--- a/webrtc/api/webrtcsessiondescriptionfactory.cc
+++ b/webrtc/api/webrtcsessiondescriptionfactory.cc
@@ -400,7 +400,7 @@
       // We should pass the current SSL role to the transport description
       // factory, if there is already an existing ongoing session.
       rtc::SSLRole ssl_role;
-      if (session_->GetSslRole(content.name, &ssl_role)) {
+      if (session_->GetSslRole(session_->GetChannel(content.name), &ssl_role)) {
         request.options.transport_options[content.name].prefer_passive_role =
             (rtc::SSL_SERVER == ssl_role);
       }
diff --git a/webrtc/media/BUILD.gn b/webrtc/media/BUILD.gn
index 3aee5b7..04936de 100644
--- a/webrtc/media/BUILD.gn
+++ b/webrtc/media/BUILD.gn
@@ -54,6 +54,7 @@
     "base/codec.h",
     "base/cryptoparams.h",
     "base/device.h",
+    "base/hybriddataengine.h",
     "base/mediachannel.h",
     "base/mediaconstants.cc",
     "base/mediaconstants.h",
@@ -142,13 +143,12 @@
     "engine/webrtcvoe.h",
     "engine/webrtcvoiceengine.cc",
     "engine/webrtcvoiceengine.h",
-    "sctp/sctptransportinternal.h",
   ]
 
   if (rtc_enable_sctp) {
     sources += [
-      "sctp/sctptransport.cc",
-      "sctp/sctptransport.h",
+      "sctp/sctpdataengine.cc",
+      "sctp/sctpdataengine.h",
     ]
   }
 
@@ -346,7 +346,7 @@
     ]
 
     if (rtc_enable_sctp) {
-      sources += [ "sctp/sctptransport_unittest.cc" ]
+      sources += [ "sctp/sctpdataengine_unittest.cc" ]
     }
 
     configs += [ ":rtc_media_unittests_config" ]
diff --git a/webrtc/media/base/fakemediaengine.h b/webrtc/media/base/fakemediaengine.h
index cbd4e6e..932427b 100644
--- a/webrtc/media/base/fakemediaengine.h
+++ b/webrtc/media/base/fakemediaengine.h
@@ -942,9 +942,11 @@
 
 class FakeDataEngine : public DataEngineInterface {
  public:
-  FakeDataEngine(){};
+  FakeDataEngine() : last_channel_type_(DCT_NONE) {}
 
-  virtual DataMediaChannel* CreateChannel(const MediaConfig& config) {
+  virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
+                                          const MediaConfig& config) {
+    last_channel_type_ = data_channel_type;
     FakeDataMediaChannel* ch = new FakeDataMediaChannel(this, DataOptions());
     channels_.push_back(ch);
     return ch;
@@ -964,9 +966,12 @@
 
   virtual const std::vector<DataCodec>& data_codecs() { return data_codecs_; }
 
+  DataChannelType last_channel_type() const { return last_channel_type_; }
+
  private:
   std::vector<FakeDataMediaChannel*> channels_;
   std::vector<DataCodec> data_codecs_;
+  DataChannelType last_channel_type_;
 };
 
 }  // namespace cricket
diff --git a/webrtc/media/base/hybriddataengine.h b/webrtc/media/base/hybriddataengine.h
new file mode 100644
index 0000000..341f054
--- /dev/null
+++ b/webrtc/media/base/hybriddataengine.h
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_
+#define WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "webrtc/media/base/codec.h"
+#include "webrtc/media/base/mediachannel.h"
+#include "webrtc/media/base/mediaengine.h"
+
+namespace cricket {
+
+class HybridDataEngine : public DataEngineInterface {
+ public:
+  // Takes ownership.
+  HybridDataEngine(DataEngineInterface* first,
+                   DataEngineInterface* second)
+      : first_(first),
+        second_(second) {
+    codecs_ = first_->data_codecs();
+    codecs_.insert(
+        codecs_.end(),
+        second_->data_codecs().begin(),
+        second_->data_codecs().end());
+  }
+
+  virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
+                                          const MediaConfig& config) {
+    DataMediaChannel* channel = NULL;
+    if (first_) {
+      channel = first_->CreateChannel(data_channel_type, config);
+    }
+    if (!channel && second_) {
+      channel = second_->CreateChannel(data_channel_type, config);
+    }
+    return channel;
+  }
+
+  virtual const std::vector<DataCodec>& data_codecs() { return codecs_; }
+
+ private:
+  std::unique_ptr<DataEngineInterface> first_;
+  std::unique_ptr<DataEngineInterface> second_;
+  std::vector<DataCodec> codecs_;
+};
+
+}  // namespace cricket
+
+#endif  // WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_
diff --git a/webrtc/media/base/mediachannel.h b/webrtc/media/base/mediachannel.h
index 4c6b024..d664240 100644
--- a/webrtc/media/base/mediachannel.h
+++ b/webrtc/media/base/mediachannel.h
@@ -1077,11 +1077,8 @@
 // signal fires, on up the chain.
 struct ReceiveDataParams {
   // The in-packet stream indentifier.
-  // RTP data channels use SSRCs, SCTP data channels use SIDs.
-  union {
-    uint32_t ssrc;
-    int sid;
-  };
+  // For SCTP, this is really SID, not SSRC.
+  uint32_t ssrc;
   // The type of message (binary, text, or control).
   DataMessageType type;
   // A per-stream value incremented per packet in the stream.
@@ -1089,16 +1086,18 @@
   // A per-stream value monotonically increasing with time.
   int timestamp;
 
-  ReceiveDataParams() : sid(0), type(DMT_TEXT), seq_num(0), timestamp(0) {}
+  ReceiveDataParams() :
+      ssrc(0),
+      type(DMT_TEXT),
+      seq_num(0),
+      timestamp(0) {
+  }
 };
 
 struct SendDataParams {
   // The in-packet stream indentifier.
-  // RTP data channels use SSRCs, SCTP data channels use SIDs.
-  union {
-    uint32_t ssrc;
-    int sid;
-  };
+  // For SCTP, this is really SID, not SSRC.
+  uint32_t ssrc;
   // The type of message (binary, text, or control).
   DataMessageType type;
 
@@ -1117,14 +1116,15 @@
   // is supported, not both at the same time.
   int max_rtx_ms;
 
-  SendDataParams()
-      : sid(0),
-        type(DMT_TEXT),
-        // TODO(pthatcher): Make these true by default?
-        ordered(false),
-        reliable(false),
-        max_rtx_count(0),
-        max_rtx_ms(0) {}
+  SendDataParams() :
+      ssrc(0),
+      type(DMT_TEXT),
+      // TODO(pthatcher): Make these true by default?
+      ordered(false),
+      reliable(false),
+      max_rtx_count(0),
+      max_rtx_ms(0) {
+  }
 };
 
 enum SendDataResult { SDR_SUCCESS, SDR_ERROR, SDR_BLOCK };
@@ -1183,6 +1183,8 @@
   // Signal when the media channel is ready to send the stream. Arguments are:
   //     writable(bool)
   sigslot::signal1<bool> SignalReadyToSend;
+  // Signal for notifying that the remote side has closed the DataChannel.
+  sigslot::signal1<uint32_t> SignalStreamClosedRemotely;
 };
 
 }  // namespace cricket
diff --git a/webrtc/media/base/mediaconstants.h b/webrtc/media/base/mediaconstants.h
index 44d8c7e..547839a 100644
--- a/webrtc/media/base/mediaconstants.h
+++ b/webrtc/media/base/mediaconstants.h
@@ -106,9 +106,9 @@
 extern const char kCodecParamMaxQuantization[];
 extern const char kCodecParamPort[];
 
-// We put the data codec names here so callers of DataEngine::CreateChannel
-// don't have to import rtpdataengine.h to get the codec names they want to
-// pass in.
+// We put the data codec names here so callers of
+// DataEngine::CreateChannel don't have to import rtpdataengine.h or
+// sctpdataengine.h to get the codec names they want to pass in.
 extern const int kGoogleRtpDataCodecPlType;
 extern const char kGoogleRtpDataCodecName[];
 
diff --git a/webrtc/media/base/mediaengine.h b/webrtc/media/base/mediaengine.h
index 0dbac55..debc171 100644
--- a/webrtc/media/base/mediaengine.h
+++ b/webrtc/media/base/mediaengine.h
@@ -171,7 +171,8 @@
 class DataEngineInterface {
  public:
   virtual ~DataEngineInterface() {}
-  virtual DataMediaChannel* CreateChannel(const MediaConfig& config) = 0;
+  virtual DataMediaChannel* CreateChannel(DataChannelType type,
+                                          const MediaConfig& config) = 0;
   virtual const std::vector<DataCodec>& data_codecs() = 0;
 };
 
diff --git a/webrtc/media/base/rtpdataengine.cc b/webrtc/media/base/rtpdataengine.cc
index 96f5a3b..12a37fa 100644
--- a/webrtc/media/base/rtpdataengine.cc
+++ b/webrtc/media/base/rtpdataengine.cc
@@ -40,7 +40,11 @@
 }
 
 DataMediaChannel* RtpDataEngine::CreateChannel(
+    DataChannelType data_channel_type,
     const MediaConfig& config) {
+  if (data_channel_type != DCT_RTP) {
+    return NULL;
+  }
   return new RtpDataMediaChannel(config);
 }
 
diff --git a/webrtc/media/base/rtpdataengine.h b/webrtc/media/base/rtpdataengine.h
index ca43494..adf0bef 100644
--- a/webrtc/media/base/rtpdataengine.h
+++ b/webrtc/media/base/rtpdataengine.h
@@ -27,7 +27,8 @@
  public:
   RtpDataEngine();
 
-  virtual DataMediaChannel* CreateChannel(const MediaConfig& config);
+  virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
+                                          const MediaConfig& config);
 
   virtual const std::vector<DataCodec>& data_codecs() {
     return data_codecs_;
diff --git a/webrtc/media/base/rtpdataengine_unittest.cc b/webrtc/media/base/rtpdataengine_unittest.cc
index dca509d..9d1e485 100644
--- a/webrtc/media/base/rtpdataengine_unittest.cc
+++ b/webrtc/media/base/rtpdataengine_unittest.cc
@@ -72,7 +72,8 @@
   cricket::RtpDataMediaChannel* CreateChannel(cricket::RtpDataEngine* dme) {
     cricket::MediaConfig config;
     cricket::RtpDataMediaChannel* channel =
-        static_cast<cricket::RtpDataMediaChannel*>(dme->CreateChannel(config));
+        static_cast<cricket::RtpDataMediaChannel*>(
+            dme->CreateChannel(cricket::DCT_RTP, config));
     channel->SetInterface(iface_.get());
     channel->SignalDataReceived.connect(
         receiver_.get(), &FakeDataReceiver::OnDataReceived);
diff --git a/webrtc/media/sctp/sctpdataengine.cc b/webrtc/media/sctp/sctpdataengine.cc
new file mode 100644
index 0000000..103aebd
--- /dev/null
+++ b/webrtc/media/sctp/sctpdataengine.cc
@@ -0,0 +1,1066 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/media/sctp/sctpdataengine.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <memory>
+#include <sstream>
+#include <vector>
+
+#include "usrsctplib/usrsctp.h"
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/copyonwritebuffer.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/safe_conversions.h"
+#include "webrtc/media/base/codec.h"
+#include "webrtc/media/base/mediaconstants.h"
+#include "webrtc/media/base/streamparams.h"
+
+namespace cricket {
+// The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280,
+// take off 80 bytes for DTLS/TURN/TCP/IP overhead.
+static constexpr size_t kSctpMtu = 1200;
+
+// The size of the SCTP association send buffer.  256kB, the usrsctp default.
+static constexpr int kSendBufferSize = 262144;
+
+struct SctpInboundPacket {
+  rtc::CopyOnWriteBuffer buffer;
+  ReceiveDataParams params;
+  // The |flags| parameter is used by SCTP to distinguish notification packets
+  // from other types of packets.
+  int flags;
+};
+
+namespace {
+// Set the initial value of the static SCTP Data Engines reference count.
+int g_usrsctp_usage_count = 0;
+rtc::GlobalLockPod g_usrsctp_lock_;
+
+typedef SctpDataMediaChannel::StreamSet StreamSet;
+
+// Returns a comma-separated, human-readable list of the stream IDs in 's'
+std::string ListStreams(const StreamSet& s) {
+  std::stringstream result;
+  bool first = true;
+  for (StreamSet::const_iterator it = s.begin(); it != s.end(); ++it) {
+    if (!first) {
+      result << ", " << *it;
+    } else {
+      result << *it;
+      first = false;
+    }
+  }
+  return result.str();
+}
+
+// Returns a pipe-separated, human-readable list of the SCTP_STREAM_RESET
+// flags in 'flags'
+std::string ListFlags(int flags) {
+  std::stringstream result;
+  bool first = true;
+  // Skip past the first 12 chars (strlen("SCTP_STREAM_"))
+#define MAKEFLAG(X) { X, #X + 12}
+  struct flaginfo_t {
+    int value;
+    const char* name;
+  } flaginfo[] = {
+    MAKEFLAG(SCTP_STREAM_RESET_INCOMING_SSN),
+    MAKEFLAG(SCTP_STREAM_RESET_OUTGOING_SSN),
+    MAKEFLAG(SCTP_STREAM_RESET_DENIED),
+    MAKEFLAG(SCTP_STREAM_RESET_FAILED),
+    MAKEFLAG(SCTP_STREAM_CHANGE_DENIED)
+  };
+#undef MAKEFLAG
+  for (uint32_t i = 0; i < arraysize(flaginfo); ++i) {
+    if (flags & flaginfo[i].value) {
+      if (!first) result << " | ";
+      result << flaginfo[i].name;
+      first = false;
+    }
+  }
+  return result.str();
+}
+
+// Returns a comma-separated, human-readable list of the integers in 'array'.
+// All 'num_elems' of them.
+std::string ListArray(const uint16_t* array, int num_elems) {
+  std::stringstream result;
+  for (int i = 0; i < num_elems; ++i) {
+    if (i) {
+      result << ", " << array[i];
+    } else {
+      result << array[i];
+    }
+  }
+  return result.str();
+}
+
+typedef rtc::ScopedMessageData<SctpInboundPacket> InboundPacketMessage;
+typedef rtc::ScopedMessageData<rtc::CopyOnWriteBuffer> OutboundPacketMessage;
+
+enum {
+  MSG_SCTPINBOUNDPACKET = 1,   // MessageData is SctpInboundPacket
+  MSG_SCTPOUTBOUNDPACKET = 2,  // MessageData is rtc:Buffer
+};
+
+// Helper for logging SCTP messages.
+void DebugSctpPrintf(const char* format, ...) {
+#if RTC_DCHECK_IS_ON
+  char s[255];
+  va_list ap;
+  va_start(ap, format);
+  vsnprintf(s, sizeof(s), format, ap);
+  LOG(LS_INFO) << "SCTP: " << s;
+  va_end(ap);
+#endif
+}
+
+// Get the PPID to use for the terminating fragment of this type.
+SctpDataMediaChannel::PayloadProtocolIdentifier GetPpid(DataMessageType type) {
+  switch (type) {
+  default:
+  case DMT_NONE:
+    return SctpDataMediaChannel::PPID_NONE;
+  case DMT_CONTROL:
+    return SctpDataMediaChannel::PPID_CONTROL;
+  case DMT_BINARY:
+    return SctpDataMediaChannel::PPID_BINARY_LAST;
+  case DMT_TEXT:
+    return SctpDataMediaChannel::PPID_TEXT_LAST;
+  };
+}
+
+bool GetDataMediaType(SctpDataMediaChannel::PayloadProtocolIdentifier ppid,
+                      DataMessageType* dest) {
+  ASSERT(dest != NULL);
+  switch (ppid) {
+    case SctpDataMediaChannel::PPID_BINARY_PARTIAL:
+    case SctpDataMediaChannel::PPID_BINARY_LAST:
+      *dest = DMT_BINARY;
+      return true;
+
+    case SctpDataMediaChannel::PPID_TEXT_PARTIAL:
+    case SctpDataMediaChannel::PPID_TEXT_LAST:
+      *dest = DMT_TEXT;
+      return true;
+
+    case SctpDataMediaChannel::PPID_CONTROL:
+      *dest = DMT_CONTROL;
+      return true;
+
+    case SctpDataMediaChannel::PPID_NONE:
+      *dest = DMT_NONE;
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+// Log the packet in text2pcap format, if log level is at LS_VERBOSE.
+void VerboseLogPacket(const void* data, size_t length, int direction) {
+  if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) {
+    char *dump_buf;
+    // Some downstream project uses an older version of usrsctp that expects
+    // a non-const "void*" as first parameter when dumping the packet, so we
+    // need to cast the const away here to avoid a compiler error.
+    if ((dump_buf = usrsctp_dumppacket(
+        const_cast<void*>(data), length, direction)) != NULL) {
+      LOG(LS_VERBOSE) << dump_buf;
+      usrsctp_freedumpbuffer(dump_buf);
+    }
+  }
+}
+
+// This is the callback usrsctp uses when there's data to send on the network
+// that has been wrapped appropriatly for the SCTP protocol.
+int OnSctpOutboundPacket(void* addr,
+                         void* data,
+                         size_t length,
+                         uint8_t tos,
+                         uint8_t set_df) {
+  SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr);
+  LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
+                  << "addr: " << addr << "; length: " << length
+                  << "; tos: " << std::hex << static_cast<int>(tos)
+                  << "; set_df: " << std::hex << static_cast<int>(set_df);
+
+  VerboseLogPacket(data, length, SCTP_DUMP_OUTBOUND);
+  // Note: We have to copy the data; the caller will delete it.
+  auto* msg = new OutboundPacketMessage(
+      new rtc::CopyOnWriteBuffer(reinterpret_cast<uint8_t*>(data), length));
+  channel->worker_thread()->Post(RTC_FROM_HERE, channel, MSG_SCTPOUTBOUNDPACKET,
+                                 msg);
+  return 0;
+}
+
+// This is the callback called from usrsctp when data has been received, after
+// a packet has been interpreted and parsed by usrsctp and found to contain
+// payload data. It is called by a usrsctp thread. It is assumed this function
+// will free the memory used by 'data'.
+int OnSctpInboundPacket(struct socket* sock,
+                        union sctp_sockstore addr,
+                        void* data,
+                        size_t length,
+                        struct sctp_rcvinfo rcv,
+                        int flags,
+                        void* ulp_info) {
+  SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
+  // Post data to the channel's receiver thread (copying it).
+  // TODO(ldixon): Unclear if copy is needed as this method is responsible for
+  // memory cleanup. But this does simplify code.
+  const SctpDataMediaChannel::PayloadProtocolIdentifier ppid =
+      static_cast<SctpDataMediaChannel::PayloadProtocolIdentifier>(
+          rtc::HostToNetwork32(rcv.rcv_ppid));
+  DataMessageType type = DMT_NONE;
+  if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) {
+    // It's neither a notification nor a recognized data packet.  Drop it.
+    LOG(LS_ERROR) << "Received an unknown PPID " << ppid
+                  << " on an SCTP packet.  Dropping.";
+  } else {
+    SctpInboundPacket* packet = new SctpInboundPacket;
+    packet->buffer.SetData(reinterpret_cast<uint8_t*>(data), length);
+    packet->params.ssrc = rcv.rcv_sid;
+    packet->params.seq_num = rcv.rcv_ssn;
+    packet->params.timestamp = rcv.rcv_tsn;
+    packet->params.type = type;
+    packet->flags = flags;
+    // The ownership of |packet| transfers to |msg|.
+    InboundPacketMessage* msg = new InboundPacketMessage(packet);
+    channel->worker_thread()->Post(RTC_FROM_HERE, channel,
+                                   MSG_SCTPINBOUNDPACKET, msg);
+  }
+  free(data);
+  return 1;
+}
+
+void InitializeUsrSctp() {
+  LOG(LS_INFO) << __FUNCTION__;
+  // First argument is udp_encapsulation_port, which is not releveant for our
+  // AF_CONN use of sctp.
+  usrsctp_init(0, &OnSctpOutboundPacket, &DebugSctpPrintf);
+
+  // To turn on/off detailed SCTP debugging. You will also need to have the
+  // SCTP_DEBUG cpp defines flag.
+  // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
+
+  // TODO(ldixon): Consider turning this on/off.
+  usrsctp_sysctl_set_sctp_ecn_enable(0);
+
+  // This is harmless, but we should find out when the library default
+  // changes.
+  int send_size = usrsctp_sysctl_get_sctp_sendspace();
+  if (send_size != kSendBufferSize) {
+    LOG(LS_ERROR) << "Got different send size than expected: " << send_size;
+  }
+
+  // TODO(ldixon): Consider turning this on/off.
+  // This is not needed right now (we don't do dynamic address changes):
+  // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
+  // when a new address is added or removed. This feature is enabled by
+  // default.
+  // usrsctp_sysctl_set_sctp_auto_asconf(0);
+
+  // TODO(ldixon): Consider turning this on/off.
+  // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
+  // being sent in response to INITs, setting it to 2 results
+  // in no ABORTs being sent for received OOTB packets.
+  // This is similar to the TCP sysctl.
+  //
+  // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
+  // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
+  // usrsctp_sysctl_set_sctp_blackhole(2);
+
+  // Set the number of default outgoing streams. This is the number we'll
+  // send in the SCTP INIT message.
+  usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(kMaxSctpStreams);
+}
+
+void UninitializeUsrSctp() {
+  LOG(LS_INFO) << __FUNCTION__;
+  // usrsctp_finish() may fail if it's called too soon after the channels are
+  // closed. Wait and try again until it succeeds for up to 3 seconds.
+  for (size_t i = 0; i < 300; ++i) {
+    if (usrsctp_finish() == 0) {
+      return;
+    }
+
+    rtc::Thread::SleepMs(10);
+  }
+  LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
+}
+
+void IncrementUsrSctpUsageCount() {
+  rtc::GlobalLockScope lock(&g_usrsctp_lock_);
+  if (!g_usrsctp_usage_count) {
+    InitializeUsrSctp();
+  }
+  ++g_usrsctp_usage_count;
+}
+
+void DecrementUsrSctpUsageCount() {
+  rtc::GlobalLockScope lock(&g_usrsctp_lock_);
+  --g_usrsctp_usage_count;
+  if (!g_usrsctp_usage_count) {
+    UninitializeUsrSctp();
+  }
+}
+
+DataCodec GetSctpDataCodec() {
+  DataCodec codec(kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName);
+  codec.SetParam(kCodecParamPort, kSctpDefaultPort);
+  return codec;
+}
+
+}  // namespace
+
+SctpDataEngine::SctpDataEngine() : codecs_(1, GetSctpDataCodec()) {}
+
+SctpDataEngine::~SctpDataEngine() {}
+
+// Called on the worker thread.
+DataMediaChannel* SctpDataEngine::CreateChannel(
+    DataChannelType data_channel_type,
+    const MediaConfig& config) {
+  if (data_channel_type != DCT_SCTP) {
+    return NULL;
+  }
+  return new SctpDataMediaChannel(rtc::Thread::Current(), config);
+}
+
+// static
+SctpDataMediaChannel* SctpDataMediaChannel::GetChannelFromSocket(
+    struct socket* sock) {
+  struct sockaddr* addrs = nullptr;
+  int naddrs = usrsctp_getladdrs(sock, 0, &addrs);
+  if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) {
+    return nullptr;
+  }
+  // usrsctp_getladdrs() returns the addresses bound to this socket, which
+  // contains the SctpDataMediaChannel* as sconn_addr.  Read the pointer,
+  // then free the list of addresses once we have the pointer.  We only open
+  // AF_CONN sockets, and they should all have the sconn_addr set to the
+  // pointer that created them, so [0] is as good as any other.
+  struct sockaddr_conn* sconn =
+      reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
+  SctpDataMediaChannel* channel =
+      reinterpret_cast<SctpDataMediaChannel*>(sconn->sconn_addr);
+  usrsctp_freeladdrs(addrs);
+
+  return channel;
+}
+
+// static
+int SctpDataMediaChannel::SendThresholdCallback(struct socket* sock,
+                                                uint32_t sb_free) {
+  // Fired on our I/O thread.  SctpDataMediaChannel::OnPacketReceived() gets
+  // a packet containing acknowledgments, which goes into usrsctp_conninput,
+  // and then back here.
+  SctpDataMediaChannel* channel = GetChannelFromSocket(sock);
+  if (!channel) {
+    LOG(LS_ERROR) << "SendThresholdCallback: Failed to get channel for socket "
+                  << sock;
+    return 0;
+  }
+  channel->OnSendThresholdCallback();
+  return 0;
+}
+
+SctpDataMediaChannel::SctpDataMediaChannel(rtc::Thread* thread,
+                                           const MediaConfig& config)
+    : DataMediaChannel(config),
+      worker_thread_(thread),
+      local_port_(kSctpDefaultPort),
+      remote_port_(kSctpDefaultPort),
+      sock_(NULL),
+      sending_(false),
+      receiving_(false),
+      debug_name_("SctpDataMediaChannel") {}
+
+SctpDataMediaChannel::~SctpDataMediaChannel() {
+  CloseSctpSocket();
+}
+
+void SctpDataMediaChannel::OnSendThresholdCallback() {
+  RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
+  SignalReadyToSend(true);
+}
+
+sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) {
+  sockaddr_conn sconn = {0};
+  sconn.sconn_family = AF_CONN;
+#ifdef HAVE_SCONN_LEN
+  sconn.sconn_len = sizeof(sockaddr_conn);
+#endif
+  // Note: conversion from int to uint16_t happens here.
+  sconn.sconn_port = rtc::HostToNetwork16(port);
+  sconn.sconn_addr = this;
+  return sconn;
+}
+
+bool SctpDataMediaChannel::OpenSctpSocket() {
+  if (sock_) {
+    LOG(LS_VERBOSE) << debug_name_
+                    << "->Ignoring attempt to re-create existing socket.";
+    return false;
+  }
+
+  IncrementUsrSctpUsageCount();
+
+  // If kSendBufferSize isn't reflective of reality, we log an error, but we
+  // still have to do something reasonable here.  Look up what the buffer's
+  // real size is and set our threshold to something reasonable.
+  const static int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 2;
+
+  sock_ = usrsctp_socket(
+      AF_CONN, SOCK_STREAM, IPPROTO_SCTP, OnSctpInboundPacket,
+      &SctpDataMediaChannel::SendThresholdCallback, kSendThreshold, this);
+  if (!sock_) {
+    LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
+    DecrementUsrSctpUsageCount();
+    return false;
+  }
+
+  // Make the socket non-blocking. Connect, close, shutdown etc will not block
+  // the thread waiting for the socket operation to complete.
+  if (usrsctp_set_non_blocking(sock_, 1) < 0) {
+    LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking.";
+    return false;
+  }
+
+  // This ensures that the usrsctp close call deletes the association. This
+  // prevents usrsctp from calling OnSctpOutboundPacket with references to
+  // this class as the address.
+  linger linger_opt;
+  linger_opt.l_onoff = 1;
+  linger_opt.l_linger = 0;
+  if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
+                         sizeof(linger_opt))) {
+    LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
+    return false;
+  }
+
+  // Enable stream ID resets.
+  struct sctp_assoc_value stream_rst;
+  stream_rst.assoc_id = SCTP_ALL_ASSOC;
+  stream_rst.assoc_value = 1;
+  if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET,
+                         &stream_rst, sizeof(stream_rst))) {
+    LOG_ERRNO(LS_ERROR) << debug_name_
+                        << "Failed to set SCTP_ENABLE_STREAM_RESET.";
+    return false;
+  }
+
+  // Nagle.
+  uint32_t nodelay = 1;
+  if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
+                         sizeof(nodelay))) {
+    LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY.";
+    return false;
+  }
+
+  // Subscribe to SCTP event notifications.
+  int event_types[] = {SCTP_ASSOC_CHANGE,
+                       SCTP_PEER_ADDR_CHANGE,
+                       SCTP_SEND_FAILED_EVENT,
+                       SCTP_SENDER_DRY_EVENT,
+                       SCTP_STREAM_RESET_EVENT};
+  struct sctp_event event = {0};
+  event.se_assoc_id = SCTP_ALL_ASSOC;
+  event.se_on = 1;
+  for (size_t i = 0; i < arraysize(event_types); i++) {
+    event.se_type = event_types[i];
+    if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
+                           sizeof(event)) < 0) {
+      LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
+                          << event.se_type;
+      return false;
+    }
+  }
+
+  // Register this class as an address for usrsctp. This is used by SCTP to
+  // direct the packets received (by the created socket) to this class.
+  usrsctp_register_address(this);
+  sending_ = true;
+  return true;
+}
+
+void SctpDataMediaChannel::CloseSctpSocket() {
+  sending_ = false;
+  if (sock_) {
+    // We assume that SO_LINGER option is set to close the association when
+    // close is called. This means that any pending packets in usrsctp will be
+    // discarded instead of being sent.
+    usrsctp_close(sock_);
+    sock_ = NULL;
+    usrsctp_deregister_address(this);
+
+    DecrementUsrSctpUsageCount();
+  }
+}
+
+bool SctpDataMediaChannel::Connect() {
+  LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
+
+  // If we already have a socket connection, just return.
+  if (sock_) {
+    LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
+                                      "is already established.";
+    return true;
+  }
+
+  // If no socket (it was closed) try to start it again. This can happen when
+  // the socket we are connecting to closes, does an sctp shutdown handshake,
+  // or behaves unexpectedly causing us to perform a CloseSctpSocket.
+  if (!sock_ && !OpenSctpSocket()) {
+    return false;
+  }
+
+  // Note: conversion from int to uint16_t happens on assignment.
+  sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
+  if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
+                   sizeof(local_sconn)) < 0) {
+    LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
+                        << ("Failed usrsctp_bind");
+    CloseSctpSocket();
+    return false;
+  }
+
+  // Note: conversion from int to uint16_t happens on assignment.
+  sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
+  int connect_result = usrsctp_connect(
+      sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
+  if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
+    LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
+                        << errno << ", but wanted " << SCTP_EINPROGRESS;
+    CloseSctpSocket();
+    return false;
+  }
+  // Set the MTU and disable MTU discovery.
+  // We can only do this after usrsctp_connect or it has no effect.
+  sctp_paddrparams params = {{0}};
+  memcpy(&params.spp_address, &remote_sconn, sizeof(remote_sconn));
+  params.spp_flags = SPP_PMTUD_DISABLE;
+  params.spp_pathmtu = kSctpMtu;
+  if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &params,
+                         sizeof(params))) {
+    LOG_ERRNO(LS_ERROR) << debug_name_
+                        << "Failed to set SCTP_PEER_ADDR_PARAMS.";
+  }
+  return true;
+}
+
+void SctpDataMediaChannel::Disconnect() {
+  // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
+  // shutdown handshake and remove the association.
+  CloseSctpSocket();
+}
+
+bool SctpDataMediaChannel::SetSend(bool send) {
+  if (!sending_ && send) {
+    return Connect();
+  }
+  if (sending_ && !send) {
+    Disconnect();
+  }
+  return true;
+}
+
+bool SctpDataMediaChannel::SetReceive(bool receive) {
+  receiving_ = receive;
+  return true;
+}
+
+bool SctpDataMediaChannel::SetSendParameters(const DataSendParameters& params) {
+  return SetSendCodecs(params.codecs);
+}
+
+bool SctpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) {
+  return SetRecvCodecs(params.codecs);
+}
+
+bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
+  return AddStream(stream);
+}
+
+bool SctpDataMediaChannel::RemoveSendStream(uint32_t ssrc) {
+  return ResetStream(ssrc);
+}
+
+bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
+  // SCTP DataChannels are always bi-directional and calling AddSendStream will
+  // enable both sending and receiving on the stream. So AddRecvStream is a
+  // no-op.
+  return true;
+}
+
+bool SctpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) {
+  // SCTP DataChannels are always bi-directional and calling RemoveSendStream
+  // will disable both sending and receiving on the stream. So RemoveRecvStream
+  // is a no-op.
+  return true;
+}
+
+bool SctpDataMediaChannel::SendData(
+    const SendDataParams& params,
+    const rtc::CopyOnWriteBuffer& payload,
+    SendDataResult* result) {
+  if (result) {
+    // Preset |result| to assume an error.  If SendData succeeds, we'll
+    // overwrite |*result| once more at the end.
+    *result = SDR_ERROR;
+  }
+
+  if (!sending_) {
+    LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
+                    << "Not sending packet with ssrc=" << params.ssrc
+                    << " len=" << payload.size() << " before SetSend(true).";
+    return false;
+  }
+
+  if (params.type != DMT_CONTROL &&
+      open_streams_.find(params.ssrc) == open_streams_.end()) {
+    LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
+                    << "Not sending data because ssrc is unknown: "
+                    << params.ssrc;
+    return false;
+  }
+
+  //
+  // Send data using SCTP.
+  ssize_t send_res = 0;  // result from usrsctp_sendv.
+  struct sctp_sendv_spa spa = {0};
+  spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
+  spa.sendv_sndinfo.snd_sid = params.ssrc;
+  spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(
+      GetPpid(params.type));
+
+  // Ordered implies reliable.
+  if (!params.ordered) {
+    spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
+    if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) {
+      spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
+      spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
+      spa.sendv_prinfo.pr_value = params.max_rtx_count;
+    } else {
+      spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
+      spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
+      spa.sendv_prinfo.pr_value = params.max_rtx_ms;
+    }
+  }
+
+  // We don't fragment.
+  send_res = usrsctp_sendv(
+      sock_, payload.data(), static_cast<size_t>(payload.size()), NULL, 0, &spa,
+      rtc::checked_cast<socklen_t>(sizeof(spa)), SCTP_SENDV_SPA, 0);
+  if (send_res < 0) {
+    if (errno == SCTP_EWOULDBLOCK) {
+      *result = SDR_BLOCK;
+      LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
+    } else {
+      LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
+                          << "->SendData(...): "
+                          << " usrsctp_sendv: ";
+    }
+    return false;
+  }
+  if (result) {
+    // Only way out now is success.
+    *result = SDR_SUCCESS;
+  }
+  return true;
+}
+
+// Called by network interface when a packet has been received.
+void SctpDataMediaChannel::OnPacketReceived(
+    rtc::CopyOnWriteBuffer* packet, const rtc::PacketTime& packet_time) {
+  RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
+  LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): "
+                  << " length=" << packet->size() << ", sending: " << sending_;
+  // Only give receiving packets to usrsctp after if connected. This enables two
+  // peers to each make a connect call, but for them not to receive an INIT
+  // packet before they have called connect; least the last receiver of the INIT
+  // packet will have called connect, and a connection will be established.
+  if (sending_) {
+    // Pass received packet to SCTP stack. Once processed by usrsctp, the data
+    // will be will be given to the global OnSctpInboundData, and then,
+    // marshalled by a Post and handled with OnMessage.
+    VerboseLogPacket(packet->cdata(), packet->size(), SCTP_DUMP_INBOUND);
+    usrsctp_conninput(this, packet->cdata(), packet->size(), 0);
+  } else {
+    // TODO(ldixon): Consider caching the packet for very slightly better
+    // reliability.
+  }
+}
+
+void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
+    SctpInboundPacket* packet) {
+  LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
+                  << "Received SCTP data:"
+                  << " ssrc=" << packet->params.ssrc
+                  << " notification: " << (packet->flags & MSG_NOTIFICATION)
+                  << " length=" << packet->buffer.size();
+  // Sending a packet with data == NULL (no data) is SCTPs "close the
+  // connection" message. This sets sock_ = NULL;
+  if (!packet->buffer.size() || !packet->buffer.data()) {
+    LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
+                                   "No data, closing.";
+    return;
+  }
+  if (packet->flags & MSG_NOTIFICATION) {
+    OnNotificationFromSctp(packet->buffer);
+  } else {
+    OnDataFromSctpToChannel(packet->params, packet->buffer);
+  }
+}
+
+void SctpDataMediaChannel::OnDataFromSctpToChannel(
+    const ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& buffer) {
+  if (receiving_) {
+    LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
+                    << "Posting with length: " << buffer.size()
+                    << " on stream " << params.ssrc;
+    // Reports all received messages to upper layers, no matter whether the sid
+    // is known.
+    SignalDataReceived(params, buffer.data<char>(), buffer.size());
+  } else {
+    LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
+                    << "Not receiving packet with sid=" << params.ssrc
+                    << " len=" << buffer.size() << " before SetReceive(true).";
+  }
+}
+
+bool SctpDataMediaChannel::AddStream(const StreamParams& stream) {
+  if (!stream.has_ssrcs()) {
+    return false;
+  }
+
+  const uint32_t ssrc = stream.first_ssrc();
+  if (ssrc > kMaxSctpSid) {
+    LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
+                    << "Not adding data stream '" << stream.id
+                    << "' with sid=" << ssrc << " because sid is too high.";
+    return false;
+  } else if (open_streams_.find(ssrc) != open_streams_.end()) {
+    LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
+                    << "Not adding data stream '" << stream.id
+                    << "' with sid=" << ssrc
+                    << " because stream is already open.";
+    return false;
+  } else if (queued_reset_streams_.find(ssrc) != queued_reset_streams_.end()
+             || sent_reset_streams_.find(ssrc) != sent_reset_streams_.end()) {
+    LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
+                    << "Not adding data stream '" << stream.id
+                    << "' with sid=" << ssrc
+                    << " because stream is still closing.";
+    return false;
+  }
+
+  open_streams_.insert(ssrc);
+  return true;
+}
+
+bool SctpDataMediaChannel::ResetStream(uint32_t ssrc) {
+  // We typically get this called twice for the same stream, once each for
+  // Send and Recv.
+  StreamSet::iterator found = open_streams_.find(ssrc);
+
+  if (found == open_streams_.end()) {
+    LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
+                    << "stream not found.";
+    return false;
+  } else {
+    LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
+                    << "Removing and queuing RE-CONFIG chunk.";
+    open_streams_.erase(found);
+  }
+
+  // SCTP won't let you have more than one stream reset pending at a time, but
+  // you can close multiple streams in a single reset.  So, we keep an internal
+  // queue of streams-to-reset, and send them as one reset message in
+  // SendQueuedStreamResets().
+  queued_reset_streams_.insert(ssrc);
+
+  // Signal our stream-reset logic that it should try to send now, if it can.
+  SendQueuedStreamResets();
+
+  // The stream will actually get removed when we get the acknowledgment.
+  return true;
+}
+
+void SctpDataMediaChannel::OnNotificationFromSctp(
+    const rtc::CopyOnWriteBuffer& buffer) {
+  const sctp_notification& notification =
+      reinterpret_cast<const sctp_notification&>(*buffer.data());
+  ASSERT(notification.sn_header.sn_length == buffer.size());
+
+  // TODO(ldixon): handle notifications appropriately.
+  switch (notification.sn_header.sn_type) {
+    case SCTP_ASSOC_CHANGE:
+      LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
+      OnNotificationAssocChange(notification.sn_assoc_change);
+      break;
+    case SCTP_REMOTE_ERROR:
+      LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
+      break;
+    case SCTP_SHUTDOWN_EVENT:
+      LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
+      break;
+    case SCTP_ADAPTATION_INDICATION:
+      LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION";
+      break;
+    case SCTP_PARTIAL_DELIVERY_EVENT:
+      LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
+      break;
+    case SCTP_AUTHENTICATION_EVENT:
+      LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
+      break;
+    case SCTP_SENDER_DRY_EVENT:
+      LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT";
+      SignalReadyToSend(true);
+      break;
+    // TODO(ldixon): Unblock after congestion.
+    case SCTP_NOTIFICATIONS_STOPPED_EVENT:
+      LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
+      break;
+    case SCTP_SEND_FAILED_EVENT:
+      LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
+      break;
+    case SCTP_STREAM_RESET_EVENT:
+      OnStreamResetEvent(&notification.sn_strreset_event);
+      break;
+    case SCTP_ASSOC_RESET_EVENT:
+      LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
+      break;
+    case SCTP_STREAM_CHANGE_EVENT:
+      LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
+      // An acknowledgment we get after our stream resets have gone through,
+      // if they've failed.  We log the message, but don't react -- we don't
+      // keep around the last-transmitted set of SSIDs we wanted to close for
+      // error recovery.  It doesn't seem likely to occur, and if so, likely
+      // harmless within the lifetime of a single SCTP association.
+      break;
+    default:
+      LOG(LS_WARNING) << "Unknown SCTP event: "
+                      << notification.sn_header.sn_type;
+      break;
+  }
+}
+
+void SctpDataMediaChannel::OnNotificationAssocChange(
+    const sctp_assoc_change& change) {
+  switch (change.sac_state) {
+    case SCTP_COMM_UP:
+      LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
+      break;
+    case SCTP_COMM_LOST:
+      LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
+      break;
+    case SCTP_RESTART:
+      LOG(LS_INFO) << "Association change SCTP_RESTART";
+      break;
+    case SCTP_SHUTDOWN_COMP:
+      LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
+      break;
+    case SCTP_CANT_STR_ASSOC:
+      LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
+      break;
+    default:
+      LOG(LS_INFO) << "Association change UNKNOWN";
+      break;
+  }
+}
+
+void SctpDataMediaChannel::OnStreamResetEvent(
+    const struct sctp_stream_reset_event* evt) {
+  // A stream reset always involves two RE-CONFIG chunks for us -- we always
+  // simultaneously reset a sid's sequence number in both directions.  The
+  // requesting side transmits a RE-CONFIG chunk and waits for the peer to send
+  // one back.  Both sides get this SCTP_STREAM_RESET_EVENT when they receive
+  // RE-CONFIGs.
+  const int num_ssrcs = (evt->strreset_length - sizeof(*evt)) /
+      sizeof(evt->strreset_stream_list[0]);
+  LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
+                  << "): Flags = 0x"
+                  << std::hex << evt->strreset_flags << " ("
+                  << ListFlags(evt->strreset_flags) << ")";
+  LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = ["
+                  << ListArray(evt->strreset_stream_list, num_ssrcs)
+                  << "], Open: ["
+                  << ListStreams(open_streams_) << "], Q'd: ["
+                  << ListStreams(queued_reset_streams_) << "], Sent: ["
+                  << ListStreams(sent_reset_streams_) << "]";
+
+  // If both sides try to reset some streams at the same time (even if they're
+  // disjoint sets), we can get reset failures.
+  if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) {
+    // OK, just try again.  The stream IDs sent over when the RESET_FAILED flag
+    // is set seem to be garbage values.  Ignore them.
+    queued_reset_streams_.insert(
+        sent_reset_streams_.begin(),
+        sent_reset_streams_.end());
+    sent_reset_streams_.clear();
+
+  } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
+    // Each side gets an event for each direction of a stream.  That is,
+    // closing sid k will make each side receive INCOMING and OUTGOING reset
+    // events for k.  As per RFC6525, Section 5, paragraph 2, each side will
+    // get an INCOMING event first.
+    for (int i = 0; i < num_ssrcs; i++) {
+      const int stream_id = evt->strreset_stream_list[i];
+
+      // See if this stream ID was closed by our peer or ourselves.
+      StreamSet::iterator it = sent_reset_streams_.find(stream_id);
+
+      // The reset was requested locally.
+      if (it != sent_reset_streams_.end()) {
+        LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
+                        << "): local sid " << stream_id << " acknowledged.";
+        sent_reset_streams_.erase(it);
+
+      } else if ((it = open_streams_.find(stream_id))
+                 != open_streams_.end()) {
+        // The peer requested the reset.
+        LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
+                        << "): closing sid " << stream_id;
+        open_streams_.erase(it);
+        SignalStreamClosedRemotely(stream_id);
+
+      } else if ((it = queued_reset_streams_.find(stream_id))
+                 != queued_reset_streams_.end()) {
+        // The peer requested the reset, but there was a local reset
+        // queued.
+        LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
+                        << "): double-sided close for sid " << stream_id;
+        // Both sides want the stream closed, and the peer got to send the
+        // RE-CONFIG first.  Treat it like the local Remove(Send|Recv)Stream
+        // finished quickly.
+        queued_reset_streams_.erase(it);
+
+      } else {
+        // This stream is unknown.  Sometimes this can be from an
+        // RESET_FAILED-related retransmit.
+        LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
+                        << "): Unknown sid " << stream_id;
+      }
+    }
+  }
+
+  // Always try to send the queued RESET because this call indicates that the
+  // last local RESET or remote RESET has made some progress.
+  SendQueuedStreamResets();
+}
+
+// Puts the specified |param| from the codec identified by |id| into |dest|
+// and returns true.  Or returns false if it wasn't there, leaving |dest|
+// untouched.
+static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
+                                 int id, const std::string& name,
+                                 const std::string& param, int* dest) {
+  std::string value;
+  DataCodec match_pattern(id, name);
+  for (size_t i = 0; i < codecs.size(); ++i) {
+    if (codecs[i].Matches(match_pattern)) {
+      if (codecs[i].GetParam(param, &value)) {
+        *dest = rtc::FromString<int>(value);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
+  return GetCodecIntParameter(
+      codecs, kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName,
+      kCodecParamPort, &remote_port_);
+}
+
+bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
+  return GetCodecIntParameter(
+      codecs, kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName,
+      kCodecParamPort, &local_port_);
+}
+
+void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
+    rtc::CopyOnWriteBuffer* buffer) {
+  if (buffer->size() > (kSctpMtu)) {
+    LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
+                  << "SCTP seems to have made a packet that is bigger "
+                  << "than its official MTU: " << buffer->size()
+                  << " vs max of " << kSctpMtu;
+  }
+  MediaChannel::SendPacket(buffer, rtc::PacketOptions());
+}
+
+bool SctpDataMediaChannel::SendQueuedStreamResets() {
+  if (!sent_reset_streams_.empty() || queued_reset_streams_.empty()) {
+    return true;
+  }
+
+  LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending ["
+                  << ListStreams(queued_reset_streams_) << "], Open: ["
+                  << ListStreams(open_streams_) << "], Sent: ["
+                  << ListStreams(sent_reset_streams_) << "]";
+
+  const size_t num_streams = queued_reset_streams_.size();
+  const size_t num_bytes =
+      sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t));
+
+  std::vector<uint8_t> reset_stream_buf(num_bytes, 0);
+  struct sctp_reset_streams* resetp = reinterpret_cast<sctp_reset_streams*>(
+      &reset_stream_buf[0]);
+  resetp->srs_assoc_id = SCTP_ALL_ASSOC;
+  resetp->srs_flags = SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING;
+  resetp->srs_number_streams = rtc::checked_cast<uint16_t>(num_streams);
+  int result_idx = 0;
+  for (StreamSet::iterator it = queued_reset_streams_.begin();
+       it != queued_reset_streams_.end(); ++it) {
+    resetp->srs_stream_list[result_idx++] = *it;
+  }
+
+  int ret = usrsctp_setsockopt(
+      sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp,
+      rtc::checked_cast<socklen_t>(reset_stream_buf.size()));
+  if (ret < 0) {
+    LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to send a stream reset for "
+                        << num_streams << " streams";
+    return false;
+  }
+
+  // sent_reset_streams_ is empty, and all the queued_reset_streams_ go into
+  // it now.
+  queued_reset_streams_.swap(sent_reset_streams_);
+  return true;
+}
+
+void SctpDataMediaChannel::OnMessage(rtc::Message* msg) {
+  switch (msg->message_id) {
+    case MSG_SCTPINBOUNDPACKET: {
+      std::unique_ptr<InboundPacketMessage> pdata(
+          static_cast<InboundPacketMessage*>(msg->pdata));
+      OnInboundPacketFromSctpToChannel(pdata->data().get());
+      break;
+    }
+    case MSG_SCTPOUTBOUNDPACKET: {
+      std::unique_ptr<OutboundPacketMessage> pdata(
+          static_cast<OutboundPacketMessage*>(msg->pdata));
+      OnPacketFromSctpToNetwork(pdata->data().get());
+      break;
+    }
+  }
+}
+}  // namespace cricket
diff --git a/webrtc/media/sctp/sctpdataengine.h b/webrtc/media/sctp/sctpdataengine.h
new file mode 100644
index 0000000..8b6f37d
--- /dev/null
+++ b/webrtc/media/sctp/sctpdataengine.h
@@ -0,0 +1,250 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MEDIA_SCTP_SCTPDATAENGINE_H_
+#define WEBRTC_MEDIA_SCTP_SCTPDATAENGINE_H_
+
+#include <errno.h>
+#include <string>
+#include <vector>
+
+namespace cricket {
+// Some ERRNO values get re-#defined to WSA* equivalents in some talk/
+// headers.  We save the original ones in an enum.
+enum PreservedErrno {
+  SCTP_EINPROGRESS = EINPROGRESS,
+  SCTP_EWOULDBLOCK = EWOULDBLOCK
+};
+}  // namespace cricket
+
+#include "webrtc/base/copyonwritebuffer.h"
+#include "webrtc/base/gtest_prod_util.h"
+#include "webrtc/media/base/codec.h"
+#include "webrtc/media/base/mediachannel.h"
+#include "webrtc/media/base/mediaengine.h"
+
+// Defined by "usrsctplib/usrsctp.h"
+struct sockaddr_conn;
+struct sctp_assoc_change;
+struct sctp_stream_reset_event;
+// Defined by <sys/socket.h>
+struct socket;
+namespace cricket {
+// The number of outgoing streams that we'll negotiate. Since stream IDs (SIDs)
+// are 0-based, the highest usable SID is 1023.
+//
+// It's recommended to use the maximum of 65535 in:
+// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.2
+// However, we use 1024 in order to save memory. usrsctp allocates 104 bytes
+// for each pair of incoming/outgoing streams (on a 64-bit system), so 65535
+// streams would waste ~6MB.
+//
+// Note: "max" and "min" here are inclusive.
+constexpr uint16_t kMaxSctpStreams = 1024;
+constexpr uint16_t kMaxSctpSid = kMaxSctpStreams - 1;
+constexpr uint16_t kMinSctpSid = 0;
+
+// This is the default SCTP port to use. It is passed along the wire and the
+// connectee and connector must be using the same port. It is not related to the
+// ports at the IP level. (Corresponds to: sockaddr_conn.sconn_port in
+// usrsctp.h)
+const int kSctpDefaultPort = 5000;
+
+class SctpDataMediaChannel;
+
+// A DataEngine that interacts with usrsctp.
+//
+// From channel calls, data flows like this:
+// [worker thread (although it can in princple be another thread)]
+//  1.  SctpDataMediaChannel::SendData(data)
+//  2.  usrsctp_sendv(data)
+// [worker thread returns; sctp thread then calls the following]
+//  3.  OnSctpOutboundPacket(wrapped_data)
+// [sctp thread returns having posted a message for the worker thread]
+//  4.  SctpDataMediaChannel::OnMessage(wrapped_data)
+//  5.  SctpDataMediaChannel::OnPacketFromSctpToNetwork(wrapped_data)
+//  6.  NetworkInterface::SendPacket(wrapped_data)
+//  7.  ... across network ... a packet is sent back ...
+//  8.  SctpDataMediaChannel::OnPacketReceived(wrapped_data)
+//  9.  usrsctp_conninput(wrapped_data)
+// [worker thread returns; sctp thread then calls the following]
+//  10.  OnSctpInboundData(data)
+// [sctp thread returns having posted a message fot the worker thread]
+//  11. SctpDataMediaChannel::OnMessage(inboundpacket)
+//  12. SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(inboundpacket)
+//  13. SctpDataMediaChannel::OnDataFromSctpToChannel(data)
+//  14. SctpDataMediaChannel::SignalDataReceived(data)
+// [from the same thread, methods registered/connected to
+//  SctpDataMediaChannel are called with the recieved data]
+class SctpDataEngine : public DataEngineInterface, public sigslot::has_slots<> {
+ public:
+  SctpDataEngine();
+  ~SctpDataEngine() override;
+
+  DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
+                                  const MediaConfig& config) override;
+  const std::vector<DataCodec>& data_codecs() override { return codecs_; }
+
+ private:
+  const std::vector<DataCodec> codecs_;
+};
+
+// TODO(ldixon): Make into a special type of TypedMessageData.
+// Holds data to be passed on to a channel.
+struct SctpInboundPacket;
+
+class SctpDataMediaChannel : public DataMediaChannel,
+                             public rtc::MessageHandler {
+ public:
+  // DataMessageType is used for the SCTP "Payload Protocol Identifier", as
+  // defined in http://tools.ietf.org/html/rfc4960#section-14.4
+  //
+  // For the list of IANA approved values see:
+  // http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xml
+  // The value is not used by SCTP itself. It indicates the protocol running
+  // on top of SCTP.
+  enum PayloadProtocolIdentifier {
+    PPID_NONE = 0,  // No protocol is specified.
+    // Matches the PPIDs in mozilla source and
+    // https://datatracker.ietf.org/doc/draft-ietf-rtcweb-data-protocol Sec. 9
+    // They're not yet assigned by IANA.
+    PPID_CONTROL = 50,
+    PPID_BINARY_PARTIAL = 52,
+    PPID_BINARY_LAST = 53,
+    PPID_TEXT_PARTIAL = 54,
+    PPID_TEXT_LAST = 51
+  };
+
+  typedef std::set<uint32_t> StreamSet;
+
+  // Given a thread which will be used to post messages (received data) to this
+  // SctpDataMediaChannel instance.
+  explicit SctpDataMediaChannel(rtc::Thread* thread, const MediaConfig& config);
+  virtual ~SctpDataMediaChannel();
+
+  // When SetSend is set to true, connects. When set to false, disconnects.
+  // Calling: "SetSend(true); SetSend(false); SetSend(true);" will connect,
+  // disconnect, and reconnect.
+  virtual bool SetSend(bool send);
+  // Unless SetReceive(true) is called, received packets will be discarded.
+  virtual bool SetReceive(bool receive);
+
+  virtual bool SetSendParameters(const DataSendParameters& params);
+  virtual bool SetRecvParameters(const DataRecvParameters& params);
+  virtual bool AddSendStream(const StreamParams& sp);
+  virtual bool RemoveSendStream(uint32_t ssrc);
+  virtual bool AddRecvStream(const StreamParams& sp);
+  virtual bool RemoveRecvStream(uint32_t ssrc);
+
+  // Called when Sctp gets data. The data may be a notification or data for
+  // OnSctpInboundData. Called from the worker thread.
+  virtual void OnMessage(rtc::Message* msg);
+  // Send data down this channel (will be wrapped as SCTP packets then given to
+  // sctp that will then post the network interface by OnMessage).
+  // Returns true iff successful data somewhere on the send-queue/network.
+  virtual bool SendData(const SendDataParams& params,
+                        const rtc::CopyOnWriteBuffer& payload,
+                        SendDataResult* result = NULL);
+  // A packet is received from the network interface. Posted to OnMessage.
+  virtual void OnPacketReceived(rtc::CopyOnWriteBuffer* packet,
+                                const rtc::PacketTime& packet_time);
+
+  // Exposed to allow Post call from c-callbacks.
+  rtc::Thread* worker_thread() const { return worker_thread_; }
+
+  // Many of these things are unused by SCTP, but are needed to fulfill
+  // the MediaChannel interface.
+  virtual void OnRtcpReceived(rtc::CopyOnWriteBuffer* packet,
+                              const rtc::PacketTime& packet_time) {}
+  virtual void OnReadyToSend(bool ready) {}
+  virtual void OnTransportOverheadChanged(int transport_overhead_per_packet) {}
+
+  void OnSendThresholdCallback();
+  // Helper for debugging.
+  void set_debug_name_for_testing(const char* debug_name) {
+    debug_name_ = debug_name;
+  }
+  const struct socket* socket() const { return sock_; }
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(SctpDataMediaChannelTest, EngineSignalsRightChannel);
+  static int SendThresholdCallback(struct socket* sock, uint32_t sb_free);
+  static SctpDataMediaChannel* GetChannelFromSocket(struct socket* sock);
+
+ private:
+  sockaddr_conn GetSctpSockAddr(int port);
+
+  bool SetSendCodecs(const std::vector<DataCodec>& codecs);
+  bool SetRecvCodecs(const std::vector<DataCodec>& codecs);
+
+  // Creates the socket and connects. Sets sending_ to true.
+  bool Connect();
+  // Closes the socket. Sets sending_ to false.
+  void Disconnect();
+
+  // Returns false when openning the socket failed; when successfull sets
+  // sending_ to true
+  bool OpenSctpSocket();
+  // Sets sending_ to false and sock_ to NULL.
+  void CloseSctpSocket();
+
+  // Sends a SCTP_RESET_STREAM for all streams in closing_ssids_.
+  bool SendQueuedStreamResets();
+
+  // Adds a stream.
+  bool AddStream(const StreamParams &sp);
+  // Queues a stream for reset.
+  bool ResetStream(uint32_t ssrc);
+
+  // Called by OnMessage to send packet on the network.
+  void OnPacketFromSctpToNetwork(rtc::CopyOnWriteBuffer* buffer);
+  // Called by OnMessage to decide what to do with the packet.
+  void OnInboundPacketFromSctpToChannel(SctpInboundPacket* packet);
+  void OnDataFromSctpToChannel(const ReceiveDataParams& params,
+                               const rtc::CopyOnWriteBuffer& buffer);
+  void OnNotificationFromSctp(const rtc::CopyOnWriteBuffer& buffer);
+  void OnNotificationAssocChange(const sctp_assoc_change& change);
+
+  void OnStreamResetEvent(const struct sctp_stream_reset_event* evt);
+
+  // Responsible for marshalling incoming data to the channels listeners, and
+  // outgoing data to the network interface.
+  rtc::Thread* worker_thread_;
+  // The local and remote SCTP port to use. These are passed along the wire
+  // and the listener and connector must be using the same port. It is not
+  // related to the ports at the IP level.  If set to -1, we default to
+  // kSctpDefaultPort.
+  int local_port_;
+  int remote_port_;
+  struct socket* sock_;  // The socket created by usrsctp_socket(...).
+
+  // sending_ is true iff there is a connected socket.
+  bool sending_;
+  // receiving_ controls whether inbound packets are thrown away.
+  bool receiving_;
+
+  // When a data channel opens a stream, it goes into open_streams_.  When we
+  // want to close it, the stream's ID goes into queued_reset_streams_.  When
+  // we actually transmit a RE-CONFIG chunk with that stream ID, the ID goes
+  // into sent_reset_streams_.  When we get a response RE-CONFIG chunk back
+  // acknowledging the reset, we remove the stream ID from
+  // sent_reset_streams_.  We use sent_reset_streams_ to differentiate
+  // between acknowledgment RE-CONFIG and peer-initiated RE-CONFIGs.
+  StreamSet open_streams_;
+  StreamSet queued_reset_streams_;
+  StreamSet sent_reset_streams_;
+
+  // A static human-readable name for debugging messages.
+  const char* debug_name_;
+};
+
+}  // namespace cricket
+
+#endif  // WEBRTC_MEDIA_SCTP_SCTPDATAENGINE_H_
diff --git a/webrtc/media/sctp/sctpdataengine_unittest.cc b/webrtc/media/sctp/sctpdataengine_unittest.cc
new file mode 100644
index 0000000..060b2d9
--- /dev/null
+++ b/webrtc/media/sctp/sctpdataengine_unittest.cc
@@ -0,0 +1,523 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "webrtc/base/bind.h"
+#include "webrtc/base/copyonwritebuffer.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/messagehandler.h"
+#include "webrtc/base/messagequeue.h"
+#include "webrtc/base/ssladapter.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/media/base/mediachannel.h"
+#include "webrtc/media/base/mediaconstants.h"
+#include "webrtc/media/sctp/sctpdataengine.h"
+
+namespace cricket {
+enum {
+  MSG_PACKET = 1,
+};
+
+// Fake NetworkInterface that sends/receives sctp packets.  The one in
+// webrtc/media/base/fakenetworkinterface.h only works with rtp/rtcp.
+class SctpFakeNetworkInterface : public MediaChannel::NetworkInterface,
+                                 public rtc::MessageHandler {
+ public:
+  explicit SctpFakeNetworkInterface(rtc::Thread* thread)
+    : thread_(thread),
+      dest_(NULL) {
+  }
+
+  void SetDestination(DataMediaChannel* dest) { dest_ = dest; }
+
+ protected:
+  // Called to send raw packet down the wire (e.g. SCTP an packet).
+  virtual bool SendPacket(rtc::CopyOnWriteBuffer* packet,
+                          const rtc::PacketOptions& options) {
+    LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::SendPacket";
+
+    rtc::CopyOnWriteBuffer* buffer = new rtc::CopyOnWriteBuffer(*packet);
+    thread_->Post(RTC_FROM_HERE, this, MSG_PACKET,
+                  rtc::WrapMessageData(buffer));
+    LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::SendPacket, Posted message.";
+    return true;
+  }
+
+  // Called when a raw packet has been recieved. This passes the data to the
+  // code that will interpret the packet. e.g. to get the content payload from
+  // an SCTP packet.
+  virtual void OnMessage(rtc::Message* msg) {
+    LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::OnMessage";
+    std::unique_ptr<rtc::CopyOnWriteBuffer> buffer(
+        static_cast<rtc::TypedMessageData<rtc::CopyOnWriteBuffer*>*>(
+            msg->pdata)->data());
+    if (dest_) {
+      dest_->OnPacketReceived(buffer.get(), rtc::PacketTime());
+    }
+    delete msg->pdata;
+  }
+
+  // Unsupported functions required to exist by NetworkInterface.
+  // TODO(ldixon): Refactor parent NetworkInterface class so these are not
+  // required. They are RTC specific and should be in an appropriate subclass.
+  virtual bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
+                        const rtc::PacketOptions& options) {
+    LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SendRtcp.";
+    return false;
+  }
+  virtual int SetOption(SocketType type, rtc::Socket::Option opt,
+                        int option) {
+    LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SetOption.";
+    return 0;
+  }
+  virtual void SetDefaultDSCPCode(rtc::DiffServCodePoint dscp) {
+    LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SetOption.";
+  }
+
+ private:
+  // Not owned by this class.
+  rtc::Thread* thread_;
+  DataMediaChannel* dest_;
+};
+
+// This is essentially a buffer to hold recieved data. It stores only the last
+// received data. Calling OnDataReceived twice overwrites old data with the
+// newer one.
+// TODO(ldixon): Implement constraints, and allow new data to be added to old
+// instead of replacing it.
+class SctpFakeDataReceiver : public sigslot::has_slots<> {
+ public:
+  SctpFakeDataReceiver() : received_(false) {}
+
+  void Clear() {
+    received_ = false;
+    last_data_ = "";
+    last_params_ = ReceiveDataParams();
+  }
+
+  virtual void OnDataReceived(const ReceiveDataParams& params,
+                              const char* data,
+                              size_t length) {
+    received_ = true;
+    last_data_ = std::string(data, length);
+    last_params_ = params;
+  }
+
+  bool received() const { return received_; }
+  std::string last_data() const { return last_data_; }
+  ReceiveDataParams last_params() const { return last_params_; }
+
+ private:
+  bool received_;
+  std::string last_data_;
+  ReceiveDataParams last_params_;
+};
+
+class SignalReadyToSendObserver : public sigslot::has_slots<> {
+ public:
+  SignalReadyToSendObserver() : signaled_(false), writable_(false) {}
+
+  void OnSignaled(bool writable) {
+    signaled_ = true;
+    writable_ = writable;
+  }
+
+  bool IsSignaled(bool writable) {
+    return signaled_ && (writable_ == writable);
+  }
+
+ private:
+  bool signaled_;
+  bool writable_;
+};
+
+class SignalChannelClosedObserver : public sigslot::has_slots<> {
+ public:
+  SignalChannelClosedObserver() {}
+  void BindSelf(SctpDataMediaChannel* channel) {
+    channel->SignalStreamClosedRemotely.connect(
+        this, &SignalChannelClosedObserver::OnStreamClosed);
+  }
+  void OnStreamClosed(uint32_t stream) { streams_.push_back(stream); }
+
+  int StreamCloseCount(uint32_t stream) {
+    return std::count(streams_.begin(), streams_.end(), stream);
+  }
+
+  bool WasStreamClosed(uint32_t stream) {
+    return std::find(streams_.begin(), streams_.end(), stream)
+        != streams_.end();
+  }
+
+ private:
+  std::vector<uint32_t> streams_;
+};
+
+class SignalChannelClosedReopener : public sigslot::has_slots<> {
+ public:
+  SignalChannelClosedReopener(SctpDataMediaChannel* channel,
+                              SctpDataMediaChannel* peer)
+      : channel_(channel), peer_(peer) {}
+
+  void OnStreamClosed(int stream) {
+    StreamParams p(StreamParams::CreateLegacy(stream));
+    channel_->AddSendStream(p);
+    channel_->AddRecvStream(p);
+    peer_->AddSendStream(p);
+    peer_->AddRecvStream(p);
+    streams_.push_back(stream);
+  }
+
+  int StreamCloseCount(int stream) {
+    return std::count(streams_.begin(), streams_.end(), stream);
+  }
+
+ private:
+  SctpDataMediaChannel* channel_;
+  SctpDataMediaChannel* peer_;
+  std::vector<int> streams_;
+};
+
+// SCTP Data Engine testing framework.
+class SctpDataMediaChannelTest : public testing::Test,
+                                 public sigslot::has_slots<> {
+ protected:
+  // usrsctp uses the NSS random number generator on non-Android platforms,
+  // so we need to initialize SSL.
+  static void SetUpTestCase() {
+  }
+
+  virtual void SetUp() { engine_.reset(new SctpDataEngine()); }
+
+  void SetupConnectedChannels() {
+    net1_.reset(new SctpFakeNetworkInterface(rtc::Thread::Current()));
+    net2_.reset(new SctpFakeNetworkInterface(rtc::Thread::Current()));
+    recv1_.reset(new SctpFakeDataReceiver());
+    recv2_.reset(new SctpFakeDataReceiver());
+    chan1_ready_to_send_count_ = 0;
+    chan2_ready_to_send_count_ = 0;
+    chan1_.reset(CreateChannel(net1_.get(), recv1_.get()));
+    chan1_->set_debug_name_for_testing("chan1/connector");
+    chan1_->SignalReadyToSend.connect(
+        this, &SctpDataMediaChannelTest::OnChan1ReadyToSend);
+    chan2_.reset(CreateChannel(net2_.get(), recv2_.get()));
+    chan2_->set_debug_name_for_testing("chan2/listener");
+    chan2_->SignalReadyToSend.connect(
+        this, &SctpDataMediaChannelTest::OnChan2ReadyToSend);
+    // Setup two connected channels ready to send and receive.
+    net1_->SetDestination(chan2_.get());
+    net2_->SetDestination(chan1_.get());
+
+    LOG(LS_VERBOSE) << "Channel setup ----------------------------- ";
+    AddStream(1);
+    AddStream(2);
+
+    LOG(LS_VERBOSE) << "Connect the channels -----------------------------";
+    // chan1 wants to setup a data connection.
+    chan1_->SetReceive(true);
+    // chan1 will have sent chan2 a request to setup a data connection. After
+    // chan2 accepts the offer, chan2 connects to chan1 with the following.
+    chan2_->SetReceive(true);
+    chan2_->SetSend(true);
+    // Makes sure that network packets are delivered and simulates a
+    // deterministic and realistic small timing delay between the SetSend calls.
+    ProcessMessagesUntilIdle();
+
+    // chan1 and chan2 are now connected so chan1 enables sending to complete
+    // the creation of the connection.
+    chan1_->SetSend(true);
+  }
+
+  virtual void TearDown() {
+    channel1()->SetSend(false);
+    channel2()->SetSend(false);
+
+    // Process messages until idle to prevent a sent packet from being dropped
+    // and causing memory leaks (not being deleted by the receiver).
+    ProcessMessagesUntilIdle();
+  }
+
+  bool AddStream(int ssrc) {
+    bool ret = true;
+    StreamParams p(StreamParams::CreateLegacy(ssrc));
+    ret = ret && chan1_->AddSendStream(p);
+    ret = ret && chan1_->AddRecvStream(p);
+    ret = ret && chan2_->AddSendStream(p);
+    ret = ret && chan2_->AddRecvStream(p);
+    return ret;
+  }
+
+  SctpDataMediaChannel* CreateChannel(SctpFakeNetworkInterface* net,
+                                      SctpFakeDataReceiver* recv) {
+    cricket::MediaConfig config;
+    SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(
+        engine_->CreateChannel(DCT_SCTP, config));
+    channel->SetInterface(net);
+    // When data is received, pass it to the SctpFakeDataReceiver.
+    channel->SignalDataReceived.connect(
+        recv, &SctpFakeDataReceiver::OnDataReceived);
+    return channel;
+  }
+
+  bool SendData(SctpDataMediaChannel* chan,
+                uint32_t ssrc,
+                const std::string& msg,
+                SendDataResult* result) {
+    SendDataParams params;
+    params.ssrc = ssrc;
+
+    return chan->SendData(params, rtc::CopyOnWriteBuffer(
+        &msg[0], msg.length()), result);
+  }
+
+  bool ReceivedData(const SctpFakeDataReceiver* recv,
+                    uint32_t ssrc,
+                    const std::string& msg) {
+    return (recv->received() &&
+            recv->last_params().ssrc == ssrc &&
+            recv->last_data() == msg);
+  }
+
+  bool ProcessMessagesUntilIdle() {
+    rtc::Thread* thread = rtc::Thread::Current();
+    while (!thread->empty()) {
+      rtc::Message msg;
+      if (thread->Get(&msg, rtc::Thread::kForever)) {
+        thread->Dispatch(&msg);
+      }
+    }
+    return !thread->IsQuitting();
+  }
+
+  SctpDataMediaChannel* channel1() { return chan1_.get(); }
+  SctpDataMediaChannel* channel2() { return chan2_.get(); }
+  SctpFakeDataReceiver* receiver1() { return recv1_.get(); }
+  SctpFakeDataReceiver* receiver2() { return recv2_.get(); }
+
+  int channel1_ready_to_send_count() { return chan1_ready_to_send_count_; }
+  int channel2_ready_to_send_count() { return chan2_ready_to_send_count_; }
+ private:
+  std::unique_ptr<SctpDataEngine> engine_;
+  std::unique_ptr<SctpFakeNetworkInterface> net1_;
+  std::unique_ptr<SctpFakeNetworkInterface> net2_;
+  std::unique_ptr<SctpFakeDataReceiver> recv1_;
+  std::unique_ptr<SctpFakeDataReceiver> recv2_;
+  std::unique_ptr<SctpDataMediaChannel> chan1_;
+  std::unique_ptr<SctpDataMediaChannel> chan2_;
+
+  int chan1_ready_to_send_count_;
+  int chan2_ready_to_send_count_;
+
+  void OnChan1ReadyToSend(bool send) {
+    if (send)
+      ++chan1_ready_to_send_count_;
+  }
+  void OnChan2ReadyToSend(bool send) {
+    if (send)
+      ++chan2_ready_to_send_count_;
+  }
+};
+
+// Verifies that SignalReadyToSend is fired.
+TEST_F(SctpDataMediaChannelTest, SignalReadyToSend) {
+  SetupConnectedChannels();
+
+  SignalReadyToSendObserver signal_observer_1;
+  SignalReadyToSendObserver signal_observer_2;
+
+  channel1()->SignalReadyToSend.connect(&signal_observer_1,
+                                        &SignalReadyToSendObserver::OnSignaled);
+  channel2()->SignalReadyToSend.connect(&signal_observer_2,
+                                        &SignalReadyToSendObserver::OnSignaled);
+
+  SendDataResult result;
+  ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
+  ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
+
+  EXPECT_TRUE_WAIT(signal_observer_1.IsSignaled(true), 1000);
+  EXPECT_TRUE_WAIT(signal_observer_2.IsSignaled(true), 1000);
+}
+
+TEST_F(SctpDataMediaChannelTest, SendData) {
+  SetupConnectedChannels();
+
+  SendDataResult result;
+  LOG(LS_VERBOSE) << "chan1 sending: 'hello?' -----------------------------";
+  ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
+  LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received()
+                  << ", recv2.last_params.ssrc="
+                  << receiver2()->last_params().ssrc
+                  << ", recv2.last_params.timestamp="
+                  << receiver2()->last_params().ssrc
+                  << ", recv2.last_params.seq_num="
+                  << receiver2()->last_params().seq_num
+                  << ", recv2.last_data=" << receiver2()->last_data();
+
+  LOG(LS_VERBOSE) << "chan2 sending: 'hi chan1' -----------------------------";
+  ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
+  LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received()
+                  << ", recv1.last_params.ssrc="
+                  << receiver1()->last_params().ssrc
+                  << ", recv1.last_params.timestamp="
+                  << receiver1()->last_params().ssrc
+                  << ", recv1.last_params.seq_num="
+                  << receiver1()->last_params().seq_num
+                  << ", recv1.last_data=" << receiver1()->last_data();
+}
+
+// Sends a lot of large messages at once and verifies SDR_BLOCK is returned.
+TEST_F(SctpDataMediaChannelTest, SendDataBlocked) {
+  SetupConnectedChannels();
+
+  SendDataResult result;
+  SendDataParams params;
+  params.ssrc = 1;
+
+  std::vector<char> buffer(1024 * 64, 0);
+
+  for (size_t i = 0; i < 100; ++i) {
+    channel1()->SendData(
+        params, rtc::CopyOnWriteBuffer(&buffer[0], buffer.size()), &result);
+    if (result == SDR_BLOCK)
+      break;
+  }
+
+  EXPECT_EQ(SDR_BLOCK, result);
+}
+
+TEST_F(SctpDataMediaChannelTest, ClosesRemoteStream) {
+  SetupConnectedChannels();
+  SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver;
+  chan_1_sig_receiver.BindSelf(channel1());
+  chan_2_sig_receiver.BindSelf(channel2());
+
+  SendDataResult result;
+  ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
+  ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
+
+  // Close channel 1.  Channel 2 should notify us.
+  channel1()->RemoveSendStream(1);
+  EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000);
+}
+
+TEST_F(SctpDataMediaChannelTest, ClosesTwoRemoteStreams) {
+  SetupConnectedChannels();
+  AddStream(3);
+  SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver;
+  chan_1_sig_receiver.BindSelf(channel1());
+  chan_2_sig_receiver.BindSelf(channel2());
+
+  SendDataResult result;
+  ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
+  ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
+
+  // Close two streams on one side.
+  channel2()->RemoveSendStream(2);
+  channel2()->RemoveSendStream(3);
+  EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000);
+  EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000);
+}
+
+TEST_F(SctpDataMediaChannelTest, ClosesStreamsOnBothSides) {
+  SetupConnectedChannels();
+  AddStream(3);
+  AddStream(4);
+  SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver;
+  chan_1_sig_receiver.BindSelf(channel1());
+  chan_2_sig_receiver.BindSelf(channel2());
+
+  SendDataResult result;
+  ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
+  ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
+
+  // Close one stream on channel1(), while closing three streams on
+  // channel2().  They will conflict (only one side can close anything at a
+  // time, apparently).  Test the resolution of the conflict.
+  channel1()->RemoveSendStream(1);
+
+  channel2()->RemoveSendStream(2);
+  channel2()->RemoveSendStream(3);
+  channel2()->RemoveSendStream(4);
+  EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000);
+  EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000);
+  EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000);
+  EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(4), 1000);
+}
+
+TEST_F(SctpDataMediaChannelTest, EngineSignalsRightChannel) {
+  SetupConnectedChannels();
+  EXPECT_TRUE_WAIT(channel1()->socket() != NULL, 1000);
+  struct socket *sock = const_cast<struct socket*>(channel1()->socket());
+  int prior_count = channel1_ready_to_send_count();
+  SctpDataMediaChannel::SendThresholdCallback(sock, 0);
+  EXPECT_GT(channel1_ready_to_send_count(), prior_count);
+}
+
+TEST_F(SctpDataMediaChannelTest, RefusesHighNumberedChannels) {
+  SetupConnectedChannels();
+  EXPECT_TRUE(AddStream(kMaxSctpSid));
+  EXPECT_FALSE(AddStream(kMaxSctpSid + 1));
+}
+
+// Flaky, see webrtc:4453.
+TEST_F(SctpDataMediaChannelTest, DISABLED_ReusesAStream) {
+  // Shut down channel 1, then open it up again for reuse.
+  SetupConnectedChannels();
+  SendDataResult result;
+  SignalChannelClosedObserver chan_2_sig_receiver;
+  chan_2_sig_receiver.BindSelf(channel2());
+
+  ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
+
+  channel1()->RemoveSendStream(1);
+  EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000);
+  // Channel 1 is gone now.
+
+  // Create a new channel 1.
+  AddStream(1);
+  ASSERT_TRUE(SendData(channel1(), 1, "hi?", &result));
+  EXPECT_EQ(SDR_SUCCESS, result);
+  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), 1000);
+  channel1()->RemoveSendStream(1);
+  EXPECT_TRUE_WAIT(chan_2_sig_receiver.StreamCloseCount(1) == 2, 1000);
+}
+
+}  // namespace cricket
diff --git a/webrtc/media/sctp/sctptransport.cc b/webrtc/media/sctp/sctptransport.cc
deleted file mode 100644
index b95cf8a..0000000
--- a/webrtc/media/sctp/sctptransport.cc
+++ /dev/null
@@ -1,1090 +0,0 @@
-/*
- *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#include <errno.h>
-namespace {
-// Some ERRNO values get re-#defined to WSA* equivalents in some talk/
-// headers. We save the original ones in an enum.
-enum PreservedErrno {
-  SCTP_EINPROGRESS = EINPROGRESS,
-  SCTP_EWOULDBLOCK = EWOULDBLOCK
-};
-}
-
-#include "webrtc/media/sctp/sctptransport.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-
-#include <memory>
-#include <sstream>
-
-#include "usrsctplib/usrsctp.h"
-#include "webrtc/base/arraysize.h"
-#include "webrtc/base/copyonwritebuffer.h"
-#include "webrtc/base/criticalsection.h"
-#include "webrtc/base/helpers.h"
-#include "webrtc/base/logging.h"
-#include "webrtc/base/safe_conversions.h"
-#include "webrtc/base/thread_checker.h"
-#include "webrtc/base/trace_event.h"
-#include "webrtc/media/base/codec.h"
-#include "webrtc/media/base/mediaconstants.h"
-#include "webrtc/media/base/rtputils.h"  // For IsRtpPacket
-#include "webrtc/media/base/streamparams.h"
-
-namespace {
-
-// The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280,
-// take off 80 bytes for DTLS/TURN/TCP/IP overhead.
-static constexpr size_t kSctpMtu = 1200;
-
-// The size of the SCTP association send buffer.  256kB, the usrsctp default.
-static constexpr int kSendBufferSize = 262144;
-
-// Set the initial value of the static SCTP Data Engines reference count.
-int g_usrsctp_usage_count = 0;
-rtc::GlobalLockPod g_usrsctp_lock_;
-
-// DataMessageType is used for the SCTP "Payload Protocol Identifier", as
-// defined in http://tools.ietf.org/html/rfc4960#section-14.4
-//
-// For the list of IANA approved values see:
-// http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xml
-// The value is not used by SCTP itself. It indicates the protocol running
-// on top of SCTP.
-enum PayloadProtocolIdentifier {
-  PPID_NONE = 0,  // No protocol is specified.
-  // Matches the PPIDs in mozilla source and
-  // https://datatracker.ietf.org/doc/draft-ietf-rtcweb-data-protocol Sec. 9
-  // They're not yet assigned by IANA.
-  PPID_CONTROL = 50,
-  PPID_BINARY_PARTIAL = 52,
-  PPID_BINARY_LAST = 53,
-  PPID_TEXT_PARTIAL = 54,
-  PPID_TEXT_LAST = 51
-};
-
-typedef std::set<uint32_t> StreamSet;
-
-// Returns a comma-separated, human-readable list of the stream IDs in 's'
-std::string ListStreams(const StreamSet& s) {
-  std::stringstream result;
-  bool first = true;
-  for (StreamSet::const_iterator it = s.begin(); it != s.end(); ++it) {
-    if (!first) {
-      result << ", " << *it;
-    } else {
-      result << *it;
-      first = false;
-    }
-  }
-  return result.str();
-}
-
-// Returns a pipe-separated, human-readable list of the SCTP_STREAM_RESET
-// flags in 'flags'
-std::string ListFlags(int flags) {
-  std::stringstream result;
-  bool first = true;
-// Skip past the first 12 chars (strlen("SCTP_STREAM_"))
-#define MAKEFLAG(X) \
-  { X, #X + 12 }
-  struct flaginfo_t {
-    int value;
-    const char* name;
-  } flaginfo[] = {MAKEFLAG(SCTP_STREAM_RESET_INCOMING_SSN),
-                  MAKEFLAG(SCTP_STREAM_RESET_OUTGOING_SSN),
-                  MAKEFLAG(SCTP_STREAM_RESET_DENIED),
-                  MAKEFLAG(SCTP_STREAM_RESET_FAILED),
-                  MAKEFLAG(SCTP_STREAM_CHANGE_DENIED)};
-#undef MAKEFLAG
-  for (uint32_t i = 0; i < arraysize(flaginfo); ++i) {
-    if (flags & flaginfo[i].value) {
-      if (!first)
-        result << " | ";
-      result << flaginfo[i].name;
-      first = false;
-    }
-  }
-  return result.str();
-}
-
-// Returns a comma-separated, human-readable list of the integers in 'array'.
-// All 'num_elems' of them.
-std::string ListArray(const uint16_t* array, int num_elems) {
-  std::stringstream result;
-  for (int i = 0; i < num_elems; ++i) {
-    if (i) {
-      result << ", " << array[i];
-    } else {
-      result << array[i];
-    }
-  }
-  return result.str();
-}
-
-// Helper for logging SCTP messages.
-void DebugSctpPrintf(const char* format, ...) {
-#if RTC_DCHECK_IS_ON
-  char s[255];
-  va_list ap;
-  va_start(ap, format);
-  vsnprintf(s, sizeof(s), format, ap);
-  LOG(LS_INFO) << "SCTP: " << s;
-  va_end(ap);
-#endif
-}
-
-// Get the PPID to use for the terminating fragment of this type.
-PayloadProtocolIdentifier GetPpid(cricket::DataMessageType type) {
-  switch (type) {
-    default:
-    case cricket::DMT_NONE:
-      return PPID_NONE;
-    case cricket::DMT_CONTROL:
-      return PPID_CONTROL;
-    case cricket::DMT_BINARY:
-      return PPID_BINARY_LAST;
-    case cricket::DMT_TEXT:
-      return PPID_TEXT_LAST;
-  }
-}
-
-bool GetDataMediaType(PayloadProtocolIdentifier ppid,
-                      cricket::DataMessageType* dest) {
-  RTC_DCHECK(dest != NULL);
-  switch (ppid) {
-    case PPID_BINARY_PARTIAL:
-    case PPID_BINARY_LAST:
-      *dest = cricket::DMT_BINARY;
-      return true;
-
-    case PPID_TEXT_PARTIAL:
-    case PPID_TEXT_LAST:
-      *dest = cricket::DMT_TEXT;
-      return true;
-
-    case PPID_CONTROL:
-      *dest = cricket::DMT_CONTROL;
-      return true;
-
-    case PPID_NONE:
-      *dest = cricket::DMT_NONE;
-      return true;
-
-    default:
-      return false;
-  }
-}
-
-// Log the packet in text2pcap format, if log level is at LS_VERBOSE.
-void VerboseLogPacket(const void* data, size_t length, int direction) {
-  if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) {
-    char* dump_buf;
-    // Some downstream project uses an older version of usrsctp that expects
-    // a non-const "void*" as first parameter when dumping the packet, so we
-    // need to cast the const away here to avoid a compiler error.
-    if ((dump_buf = usrsctp_dumppacket(const_cast<void*>(data), length,
-                                       direction)) != NULL) {
-      LOG(LS_VERBOSE) << dump_buf;
-      usrsctp_freedumpbuffer(dump_buf);
-    }
-  }
-}
-
-}  // namespace
-
-namespace cricket {
-
-// Handles global init/deinit, and mapping from usrsctp callbacks to
-// SctpTransport calls.
-class SctpTransport::UsrSctpWrapper {
- public:
-  static void InitializeUsrSctp() {
-    LOG(LS_INFO) << __FUNCTION__;
-    // First argument is udp_encapsulation_port, which is not releveant for our
-    // AF_CONN use of sctp.
-    usrsctp_init(0, &UsrSctpWrapper::OnSctpOutboundPacket, &DebugSctpPrintf);
-
-    // To turn on/off detailed SCTP debugging. You will also need to have the
-    // SCTP_DEBUG cpp defines flag.
-    // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
-
-    // TODO(ldixon): Consider turning this on/off.
-    usrsctp_sysctl_set_sctp_ecn_enable(0);
-
-    // This is harmless, but we should find out when the library default
-    // changes.
-    int send_size = usrsctp_sysctl_get_sctp_sendspace();
-    if (send_size != kSendBufferSize) {
-      LOG(LS_ERROR) << "Got different send size than expected: " << send_size;
-    }
-
-    // TODO(ldixon): Consider turning this on/off.
-    // This is not needed right now (we don't do dynamic address changes):
-    // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
-    // when a new address is added or removed. This feature is enabled by
-    // default.
-    // usrsctp_sysctl_set_sctp_auto_asconf(0);
-
-    // TODO(ldixon): Consider turning this on/off.
-    // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
-    // being sent in response to INITs, setting it to 2 results
-    // in no ABORTs being sent for received OOTB packets.
-    // This is similar to the TCP sysctl.
-    //
-    // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
-    // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
-    // usrsctp_sysctl_set_sctp_blackhole(2);
-
-    // Set the number of default outgoing streams. This is the number we'll
-    // send in the SCTP INIT message.
-    usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(kMaxSctpStreams);
-  }
-
-  static void UninitializeUsrSctp() {
-    LOG(LS_INFO) << __FUNCTION__;
-    // usrsctp_finish() may fail if it's called too soon after the transports
-    // are
-    // closed. Wait and try again until it succeeds for up to 3 seconds.
-    for (size_t i = 0; i < 300; ++i) {
-      if (usrsctp_finish() == 0) {
-        return;
-      }
-
-      rtc::Thread::SleepMs(10);
-    }
-    LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
-  }
-
-  static void IncrementUsrSctpUsageCount() {
-    rtc::GlobalLockScope lock(&g_usrsctp_lock_);
-    if (!g_usrsctp_usage_count) {
-      InitializeUsrSctp();
-    }
-    ++g_usrsctp_usage_count;
-  }
-
-  static void DecrementUsrSctpUsageCount() {
-    rtc::GlobalLockScope lock(&g_usrsctp_lock_);
-    --g_usrsctp_usage_count;
-    if (!g_usrsctp_usage_count) {
-      UninitializeUsrSctp();
-    }
-  }
-
-  // This is the callback usrsctp uses when there's data to send on the network
-  // that has been wrapped appropriatly for the SCTP protocol.
-  static int OnSctpOutboundPacket(void* addr,
-                                  void* data,
-                                  size_t length,
-                                  uint8_t tos,
-                                  uint8_t set_df) {
-    SctpTransport* transport = static_cast<SctpTransport*>(addr);
-    LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
-                    << "addr: " << addr << "; length: " << length
-                    << "; tos: " << std::hex << static_cast<int>(tos)
-                    << "; set_df: " << std::hex << static_cast<int>(set_df);
-
-    VerboseLogPacket(data, length, SCTP_DUMP_OUTBOUND);
-    // Note: We have to copy the data; the caller will delete it.
-    rtc::CopyOnWriteBuffer buf(reinterpret_cast<uint8_t*>(data), length);
-    // TODO(deadbeef): Why do we need an AsyncInvoke here? We're already on the
-    // right thread and don't need to unwind the stack.
-    transport->invoker_.AsyncInvoke<void>(
-        RTC_FROM_HERE, transport->network_thread_,
-        rtc::Bind(&SctpTransport::OnPacketFromSctpToNetwork, transport, buf));
-    return 0;
-  }
-
-  // This is the callback called from usrsctp when data has been received, after
-  // a packet has been interpreted and parsed by usrsctp and found to contain
-  // payload data. It is called by a usrsctp thread. It is assumed this function
-  // will free the memory used by 'data'.
-  static int OnSctpInboundPacket(struct socket* sock,
-                                 union sctp_sockstore addr,
-                                 void* data,
-                                 size_t length,
-                                 struct sctp_rcvinfo rcv,
-                                 int flags,
-                                 void* ulp_info) {
-    SctpTransport* transport = static_cast<SctpTransport*>(ulp_info);
-    // Post data to the transport's receiver thread (copying it).
-    // TODO(ldixon): Unclear if copy is needed as this method is responsible for
-    // memory cleanup. But this does simplify code.
-    const PayloadProtocolIdentifier ppid =
-        static_cast<PayloadProtocolIdentifier>(
-            rtc::HostToNetwork32(rcv.rcv_ppid));
-    DataMessageType type = DMT_NONE;
-    if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) {
-      // It's neither a notification nor a recognized data packet.  Drop it.
-      LOG(LS_ERROR) << "Received an unknown PPID " << ppid
-                    << " on an SCTP packet.  Dropping.";
-    } else {
-      rtc::CopyOnWriteBuffer buffer;
-      ReceiveDataParams params;
-      buffer.SetData(reinterpret_cast<uint8_t*>(data), length);
-      params.sid = rcv.rcv_sid;
-      params.seq_num = rcv.rcv_ssn;
-      params.timestamp = rcv.rcv_tsn;
-      params.type = type;
-      // The ownership of the packet transfers to |invoker_|. Using
-      // CopyOnWriteBuffer is the most convenient way to do this.
-      transport->invoker_.AsyncInvoke<void>(
-          RTC_FROM_HERE, transport->network_thread_,
-          rtc::Bind(&SctpTransport::OnInboundPacketFromSctpToChannel, transport,
-                    buffer, params, flags));
-    }
-    free(data);
-    return 1;
-  }
-
-  static SctpTransport* GetTransportFromSocket(struct socket* sock) {
-    struct sockaddr* addrs = nullptr;
-    int naddrs = usrsctp_getladdrs(sock, 0, &addrs);
-    if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) {
-      return nullptr;
-    }
-    // usrsctp_getladdrs() returns the addresses bound to this socket, which
-    // contains the SctpTransport* as sconn_addr.  Read the pointer,
-    // then free the list of addresses once we have the pointer.  We only open
-    // AF_CONN sockets, and they should all have the sconn_addr set to the
-    // pointer that created them, so [0] is as good as any other.
-    struct sockaddr_conn* sconn =
-        reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
-    SctpTransport* transport =
-        reinterpret_cast<SctpTransport*>(sconn->sconn_addr);
-    usrsctp_freeladdrs(addrs);
-
-    return transport;
-  }
-
-  static int SendThresholdCallback(struct socket* sock, uint32_t sb_free) {
-    // Fired on our I/O thread. SctpTransport::OnPacketReceived() gets
-    // a packet containing acknowledgments, which goes into usrsctp_conninput,
-    // and then back here.
-    SctpTransport* transport = GetTransportFromSocket(sock);
-    if (!transport) {
-      LOG(LS_ERROR)
-          << "SendThresholdCallback: Failed to get transport for socket "
-          << sock;
-      return 0;
-    }
-    transport->OnSendThresholdCallback();
-    return 0;
-  }
-};
-
-SctpTransport::SctpTransport(rtc::Thread* network_thread,
-                             TransportChannel* channel)
-    : network_thread_(network_thread),
-      transport_channel_(channel),
-      was_ever_writable_(channel->writable()) {
-  RTC_DCHECK(network_thread_);
-  RTC_DCHECK(transport_channel_);
-  RTC_DCHECK_RUN_ON(network_thread_);
-  ConnectTransportChannelSignals();
-}
-
-SctpTransport::~SctpTransport() {
-  // Close abruptly; no reset procedure.
-  CloseSctpSocket();
-}
-
-void SctpTransport::SetTransportChannel(cricket::TransportChannel* channel) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  RTC_DCHECK(channel);
-  DisconnectTransportChannelSignals();
-  transport_channel_ = channel;
-  ConnectTransportChannelSignals();
-  if (!was_ever_writable_ && channel->writable()) {
-    was_ever_writable_ = true;
-    // New channel is writable, now we can start the SCTP connection if Start
-    // was called already.
-    if (started_) {
-      RTC_DCHECK(!sock_);
-      Connect();
-    }
-  }
-}
-
-bool SctpTransport::Start(int local_sctp_port, int remote_sctp_port) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  if (local_sctp_port == -1) {
-    local_sctp_port = kSctpDefaultPort;
-  }
-  if (remote_sctp_port == -1) {
-    remote_sctp_port = kSctpDefaultPort;
-  }
-  if (started_) {
-    if (local_sctp_port != local_port_ || remote_sctp_port != remote_port_) {
-      LOG(LS_ERROR) << "Can't change SCTP port after SCTP association formed.";
-      return false;
-    }
-    return true;
-  }
-  local_port_ = local_sctp_port;
-  remote_port_ = remote_sctp_port;
-  started_ = true;
-  RTC_DCHECK(!sock_);
-  // Only try to connect if the DTLS channel has been writable before
-  // (indicating that the DTLS handshake is complete).
-  if (was_ever_writable_) {
-    return Connect();
-  }
-  return true;
-}
-
-bool SctpTransport::OpenStream(int sid) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  if (sid > kMaxSctpSid) {
-    LOG(LS_WARNING) << debug_name_ << "->OpenStream(...): "
-                    << "Not adding data stream "
-                    << "with sid=" << sid << " because sid is too high.";
-    return false;
-  } else if (open_streams_.find(sid) != open_streams_.end()) {
-    LOG(LS_WARNING) << debug_name_ << "->OpenStream(...): "
-                    << "Not adding data stream "
-                    << "with sid=" << sid << " because stream is already open.";
-    return false;
-  } else if (queued_reset_streams_.find(sid) != queued_reset_streams_.end() ||
-             sent_reset_streams_.find(sid) != sent_reset_streams_.end()) {
-    LOG(LS_WARNING) << debug_name_ << "->OpenStream(...): "
-                    << "Not adding data stream "
-                    << " with sid=" << sid
-                    << " because stream is still closing.";
-    return false;
-  }
-
-  open_streams_.insert(sid);
-  return true;
-}
-
-bool SctpTransport::ResetStream(int sid) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  StreamSet::iterator found = open_streams_.find(sid);
-  if (found == open_streams_.end()) {
-    LOG(LS_WARNING) << debug_name_ << "->ResetStream(" << sid << "): "
-                    << "stream not found.";
-    return false;
-  } else {
-    LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << sid << "): "
-                    << "Removing and queuing RE-CONFIG chunk.";
-    open_streams_.erase(found);
-  }
-
-  // SCTP won't let you have more than one stream reset pending at a time, but
-  // you can close multiple streams in a single reset.  So, we keep an internal
-  // queue of streams-to-reset, and send them as one reset message in
-  // SendQueuedStreamResets().
-  queued_reset_streams_.insert(sid);
-
-  // Signal our stream-reset logic that it should try to send now, if it can.
-  SendQueuedStreamResets();
-
-  // The stream will actually get removed when we get the acknowledgment.
-  return true;
-}
-
-bool SctpTransport::SendData(const SendDataParams& params,
-                             const rtc::CopyOnWriteBuffer& payload,
-                             SendDataResult* result) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  if (result) {
-    // Preset |result| to assume an error.  If SendData succeeds, we'll
-    // overwrite |*result| once more at the end.
-    *result = SDR_ERROR;
-  }
-
-  if (!sock_) {
-    LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
-                    << "Not sending packet with sid=" << params.sid
-                    << " len=" << payload.size() << " before Start().";
-    return false;
-  }
-
-  if (params.type != DMT_CONTROL &&
-      open_streams_.find(params.sid) == open_streams_.end()) {
-    LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
-                    << "Not sending data because sid is unknown: "
-                    << params.sid;
-    return false;
-  }
-
-  // Send data using SCTP.
-  ssize_t send_res = 0;  // result from usrsctp_sendv.
-  struct sctp_sendv_spa spa = {0};
-  spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
-  spa.sendv_sndinfo.snd_sid = params.sid;
-  spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(GetPpid(params.type));
-
-  // Ordered implies reliable.
-  if (!params.ordered) {
-    spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
-    if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) {
-      spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
-      spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
-      spa.sendv_prinfo.pr_value = params.max_rtx_count;
-    } else {
-      spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
-      spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
-      spa.sendv_prinfo.pr_value = params.max_rtx_ms;
-    }
-  }
-
-  // We don't fragment.
-  send_res = usrsctp_sendv(
-      sock_, payload.data(), static_cast<size_t>(payload.size()), NULL, 0, &spa,
-      rtc::checked_cast<socklen_t>(sizeof(spa)), SCTP_SENDV_SPA, 0);
-  if (send_res < 0) {
-    if (errno == SCTP_EWOULDBLOCK) {
-      *result = SDR_BLOCK;
-      ready_to_send_data_ = false;
-      LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
-    } else {
-      LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_ << "->SendData(...): "
-                          << " usrsctp_sendv: ";
-    }
-    return false;
-  }
-  if (result) {
-    // Only way out now is success.
-    *result = SDR_SUCCESS;
-  }
-  return true;
-}
-
-bool SctpTransport::ReadyToSendData() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  return ready_to_send_data_;
-}
-
-void SctpTransport::ConnectTransportChannelSignals() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  transport_channel_->SignalWritableState.connect(
-      this, &SctpTransport::OnWritableState);
-  transport_channel_->SignalReadPacket.connect(this,
-                                               &SctpTransport::OnPacketRead);
-}
-
-void SctpTransport::DisconnectTransportChannelSignals() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  transport_channel_->SignalWritableState.disconnect(this);
-  transport_channel_->SignalReadPacket.disconnect(this);
-}
-
-bool SctpTransport::Connect() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
-
-  // If we already have a socket connection (which shouldn't ever happen), just
-  // return.
-  RTC_DCHECK(!sock_);
-  if (sock_) {
-    LOG(LS_ERROR) << debug_name_ << "->Connect(): Ignored as socket "
-                                    "is already established.";
-    return true;
-  }
-
-  // If no socket (it was closed) try to start it again. This can happen when
-  // the socket we are connecting to closes, does an sctp shutdown handshake,
-  // or behaves unexpectedly causing us to perform a CloseSctpSocket.
-  if (!OpenSctpSocket()) {
-    return false;
-  }
-
-  // Note: conversion from int to uint16_t happens on assignment.
-  sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
-  if (usrsctp_bind(sock_, reinterpret_cast<sockaddr*>(&local_sconn),
-                   sizeof(local_sconn)) < 0) {
-    LOG_ERRNO(LS_ERROR) << debug_name_
-                        << "->Connect(): " << ("Failed usrsctp_bind");
-    CloseSctpSocket();
-    return false;
-  }
-
-  // Note: conversion from int to uint16_t happens on assignment.
-  sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
-  int connect_result = usrsctp_connect(
-      sock_, reinterpret_cast<sockaddr*>(&remote_sconn), sizeof(remote_sconn));
-  if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
-    LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
-                        << "Failed usrsctp_connect. got errno=" << errno
-                        << ", but wanted " << SCTP_EINPROGRESS;
-    CloseSctpSocket();
-    return false;
-  }
-  // Set the MTU and disable MTU discovery.
-  // We can only do this after usrsctp_connect or it has no effect.
-  sctp_paddrparams params = {{0}};
-  memcpy(&params.spp_address, &remote_sconn, sizeof(remote_sconn));
-  params.spp_flags = SPP_PMTUD_DISABLE;
-  params.spp_pathmtu = kSctpMtu;
-  if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &params,
-                         sizeof(params))) {
-    LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
-                        << "Failed to set SCTP_PEER_ADDR_PARAMS.";
-  }
-  // Since this is a fresh SCTP association, we'll always start out with empty
-  // queues, so "ReadyToSendData" should be true.
-  SetReadyToSendData();
-  return true;
-}
-
-bool SctpTransport::OpenSctpSocket() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  if (sock_) {
-    LOG(LS_WARNING) << debug_name_ << "->OpenSctpSocket(): "
-                    << "Ignoring attempt to re-create existing socket.";
-    return false;
-  }
-
-  UsrSctpWrapper::IncrementUsrSctpUsageCount();
-
-  // If kSendBufferSize isn't reflective of reality, we log an error, but we
-  // still have to do something reasonable here.  Look up what the buffer's
-  // real size is and set our threshold to something reasonable.
-  static const int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 2;
-
-  sock_ = usrsctp_socket(
-      AF_CONN, SOCK_STREAM, IPPROTO_SCTP, &UsrSctpWrapper::OnSctpInboundPacket,
-      &UsrSctpWrapper::SendThresholdCallback, kSendThreshold, this);
-  if (!sock_) {
-    LOG_ERRNO(LS_ERROR) << debug_name_ << "->OpenSctpSocket(): "
-                        << "Failed to create SCTP socket.";
-    UsrSctpWrapper::DecrementUsrSctpUsageCount();
-    return false;
-  }
-
-  if (!ConfigureSctpSocket()) {
-    usrsctp_close(sock_);
-    sock_ = nullptr;
-    UsrSctpWrapper::DecrementUsrSctpUsageCount();
-    return false;
-  }
-  // Register this class as an address for usrsctp. This is used by SCTP to
-  // direct the packets received (by the created socket) to this class.
-  usrsctp_register_address(this);
-  return true;
-}
-
-bool SctpTransport::ConfigureSctpSocket() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  RTC_DCHECK(sock_);
-  // Make the socket non-blocking. Connect, close, shutdown etc will not block
-  // the thread waiting for the socket operation to complete.
-  if (usrsctp_set_non_blocking(sock_, 1) < 0) {
-    LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): "
-                        << "Failed to set SCTP to non blocking.";
-    return false;
-  }
-
-  // This ensures that the usrsctp close call deletes the association. This
-  // prevents usrsctp from calling OnSctpOutboundPacket with references to
-  // this class as the address.
-  linger linger_opt;
-  linger_opt.l_onoff = 1;
-  linger_opt.l_linger = 0;
-  if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
-                         sizeof(linger_opt))) {
-    LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): "
-                        << "Failed to set SO_LINGER.";
-    return false;
-  }
-
-  // Enable stream ID resets.
-  struct sctp_assoc_value stream_rst;
-  stream_rst.assoc_id = SCTP_ALL_ASSOC;
-  stream_rst.assoc_value = 1;
-  if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET,
-                         &stream_rst, sizeof(stream_rst))) {
-    LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): "
-
-                        << "Failed to set SCTP_ENABLE_STREAM_RESET.";
-    return false;
-  }
-
-  // Nagle.
-  uint32_t nodelay = 1;
-  if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
-                         sizeof(nodelay))) {
-    LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): "
-                        << "Failed to set SCTP_NODELAY.";
-    return false;
-  }
-
-  // Subscribe to SCTP event notifications.
-  int event_types[] = {SCTP_ASSOC_CHANGE, SCTP_PEER_ADDR_CHANGE,
-                       SCTP_SEND_FAILED_EVENT, SCTP_SENDER_DRY_EVENT,
-                       SCTP_STREAM_RESET_EVENT};
-  struct sctp_event event = {0};
-  event.se_assoc_id = SCTP_ALL_ASSOC;
-  event.se_on = 1;
-  for (size_t i = 0; i < arraysize(event_types); i++) {
-    event.se_type = event_types[i];
-    if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
-                           sizeof(event)) < 0) {
-      LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): "
-
-                          << "Failed to set SCTP_EVENT type: " << event.se_type;
-      return false;
-    }
-  }
-  return true;
-}
-
-void SctpTransport::CloseSctpSocket() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  if (sock_) {
-    // We assume that SO_LINGER option is set to close the association when
-    // close is called. This means that any pending packets in usrsctp will be
-    // discarded instead of being sent.
-    usrsctp_close(sock_);
-    sock_ = nullptr;
-    usrsctp_deregister_address(this);
-    UsrSctpWrapper::DecrementUsrSctpUsageCount();
-    ready_to_send_data_ = false;
-  }
-}
-
-bool SctpTransport::SendQueuedStreamResets() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  if (!sent_reset_streams_.empty() || queued_reset_streams_.empty()) {
-    return true;
-  }
-
-  LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending ["
-                  << ListStreams(queued_reset_streams_) << "], Open: ["
-                  << ListStreams(open_streams_) << "], Sent: ["
-                  << ListStreams(sent_reset_streams_) << "]";
-
-  const size_t num_streams = queued_reset_streams_.size();
-  const size_t num_bytes =
-      sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t));
-
-  std::vector<uint8_t> reset_stream_buf(num_bytes, 0);
-  struct sctp_reset_streams* resetp =
-      reinterpret_cast<sctp_reset_streams*>(&reset_stream_buf[0]);
-  resetp->srs_assoc_id = SCTP_ALL_ASSOC;
-  resetp->srs_flags = SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING;
-  resetp->srs_number_streams = rtc::checked_cast<uint16_t>(num_streams);
-  int result_idx = 0;
-  for (StreamSet::iterator it = queued_reset_streams_.begin();
-       it != queued_reset_streams_.end(); ++it) {
-    resetp->srs_stream_list[result_idx++] = *it;
-  }
-
-  int ret =
-      usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp,
-                         rtc::checked_cast<socklen_t>(reset_stream_buf.size()));
-  if (ret < 0) {
-    LOG_ERRNO(LS_ERROR) << debug_name_ << "->SendQueuedStreamResets(): "
-                                          "Failed to send a stream reset for "
-                        << num_streams << " streams";
-    return false;
-  }
-
-  // sent_reset_streams_ is empty, and all the queued_reset_streams_ go into
-  // it now.
-  queued_reset_streams_.swap(sent_reset_streams_);
-  return true;
-}
-
-void SctpTransport::SetReadyToSendData() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  if (!ready_to_send_data_) {
-    ready_to_send_data_ = true;
-    SignalReadyToSendData();
-  }
-}
-
-void SctpTransport::OnWritableState(rtc::PacketTransportInterface* transport) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  RTC_DCHECK_EQ(transport_channel_, transport);
-  if (!was_ever_writable_ && transport->writable()) {
-    was_ever_writable_ = true;
-    if (started_) {
-      Connect();
-    }
-  }
-}
-
-// Called by network interface when a packet has been received.
-void SctpTransport::OnPacketRead(rtc::PacketTransportInterface* transport,
-                                 const char* data,
-                                 size_t len,
-                                 const rtc::PacketTime& packet_time,
-                                 int flags) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  RTC_DCHECK_EQ(transport_channel_, transport);
-  TRACE_EVENT0("webrtc", "SctpTransport::OnPacketRead");
-
-  // TODO(pthatcher): Do this in a more robust way by checking for
-  // SCTP or DTLS.
-  if (IsRtpPacket(data, len)) {
-    return;
-  }
-
-  LOG(LS_VERBOSE) << debug_name_ << "->OnPacketRead(...): "
-                  << " length=" << len << ", started: " << started_;
-  // Only give receiving packets to usrsctp after if connected. This enables two
-  // peers to each make a connect call, but for them not to receive an INIT
-  // packet before they have called connect; least the last receiver of the INIT
-  // packet will have called connect, and a connection will be established.
-  if (sock_) {
-    // Pass received packet to SCTP stack. Once processed by usrsctp, the data
-    // will be will be given to the global OnSctpInboundData, and then,
-    // marshalled by the AsyncInvoker.
-    VerboseLogPacket(data, len, SCTP_DUMP_INBOUND);
-    usrsctp_conninput(this, data, len, 0);
-  } else {
-    // TODO(ldixon): Consider caching the packet for very slightly better
-    // reliability.
-  }
-}
-
-void SctpTransport::OnSendThresholdCallback() {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  SetReadyToSendData();
-}
-
-sockaddr_conn SctpTransport::GetSctpSockAddr(int port) {
-  sockaddr_conn sconn = {0};
-  sconn.sconn_family = AF_CONN;
-#ifdef HAVE_SCONN_LEN
-  sconn.sconn_len = sizeof(sockaddr_conn);
-#endif
-  // Note: conversion from int to uint16_t happens here.
-  sconn.sconn_port = rtc::HostToNetwork16(port);
-  sconn.sconn_addr = this;
-  return sconn;
-}
-
-void SctpTransport::OnPacketFromSctpToNetwork(
-    const rtc::CopyOnWriteBuffer& buffer) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  if (buffer.size() > (kSctpMtu)) {
-    LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
-                  << "SCTP seems to have made a packet that is bigger "
-                  << "than its official MTU: " << buffer.size() << " vs max of "
-                  << kSctpMtu;
-  }
-  TRACE_EVENT0("webrtc", "SctpTransport::OnPacketFromSctpToNetwork");
-
-  // Don't create noise by trying to send a packet when the DTLS channel isn't
-  // even writable.
-  if (!transport_channel_->writable()) {
-    return;
-  }
-
-  // Bon voyage.
-  transport_channel_->SendPacket(buffer.data<char>(), buffer.size(),
-                                 rtc::PacketOptions(), PF_NORMAL);
-}
-
-void SctpTransport::OnInboundPacketFromSctpToChannel(
-    const rtc::CopyOnWriteBuffer& buffer,
-    ReceiveDataParams params,
-    int flags) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
-                  << "Received SCTP data:"
-                  << " sid=" << params.sid
-                  << " notification: " << (flags & MSG_NOTIFICATION)
-                  << " length=" << buffer.size();
-  // Sending a packet with data == NULL (no data) is SCTPs "close the
-  // connection" message. This sets sock_ = NULL;
-  if (!buffer.size() || !buffer.data()) {
-    LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
-                                   "No data, closing.";
-    return;
-  }
-  if (flags & MSG_NOTIFICATION) {
-    OnNotificationFromSctp(buffer);
-  } else {
-    OnDataFromSctpToChannel(params, buffer);
-  }
-}
-
-void SctpTransport::OnDataFromSctpToChannel(
-    const ReceiveDataParams& params,
-    const rtc::CopyOnWriteBuffer& buffer) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
-                  << "Posting with length: " << buffer.size() << " on stream "
-                  << params.sid;
-  // Reports all received messages to upper layers, no matter whether the sid
-  // is known.
-  SignalDataReceived(params, buffer);
-}
-
-void SctpTransport::OnNotificationFromSctp(
-    const rtc::CopyOnWriteBuffer& buffer) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  const sctp_notification& notification =
-      reinterpret_cast<const sctp_notification&>(*buffer.data());
-  RTC_DCHECK(notification.sn_header.sn_length == buffer.size());
-
-  // TODO(ldixon): handle notifications appropriately.
-  switch (notification.sn_header.sn_type) {
-    case SCTP_ASSOC_CHANGE:
-      LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
-      OnNotificationAssocChange(notification.sn_assoc_change);
-      break;
-    case SCTP_REMOTE_ERROR:
-      LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
-      break;
-    case SCTP_SHUTDOWN_EVENT:
-      LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
-      break;
-    case SCTP_ADAPTATION_INDICATION:
-      LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION";
-      break;
-    case SCTP_PARTIAL_DELIVERY_EVENT:
-      LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
-      break;
-    case SCTP_AUTHENTICATION_EVENT:
-      LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
-      break;
-    case SCTP_SENDER_DRY_EVENT:
-      LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT";
-      SetReadyToSendData();
-      break;
-    // TODO(ldixon): Unblock after congestion.
-    case SCTP_NOTIFICATIONS_STOPPED_EVENT:
-      LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
-      break;
-    case SCTP_SEND_FAILED_EVENT:
-      LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
-      break;
-    case SCTP_STREAM_RESET_EVENT:
-      OnStreamResetEvent(&notification.sn_strreset_event);
-      break;
-    case SCTP_ASSOC_RESET_EVENT:
-      LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
-      break;
-    case SCTP_STREAM_CHANGE_EVENT:
-      LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
-      // An acknowledgment we get after our stream resets have gone through,
-      // if they've failed.  We log the message, but don't react -- we don't
-      // keep around the last-transmitted set of SSIDs we wanted to close for
-      // error recovery.  It doesn't seem likely to occur, and if so, likely
-      // harmless within the lifetime of a single SCTP association.
-      break;
-    default:
-      LOG(LS_WARNING) << "Unknown SCTP event: "
-                      << notification.sn_header.sn_type;
-      break;
-  }
-}
-
-void SctpTransport::OnNotificationAssocChange(const sctp_assoc_change& change) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  switch (change.sac_state) {
-    case SCTP_COMM_UP:
-      LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
-      break;
-    case SCTP_COMM_LOST:
-      LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
-      break;
-    case SCTP_RESTART:
-      LOG(LS_INFO) << "Association change SCTP_RESTART";
-      break;
-    case SCTP_SHUTDOWN_COMP:
-      LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
-      break;
-    case SCTP_CANT_STR_ASSOC:
-      LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
-      break;
-    default:
-      LOG(LS_INFO) << "Association change UNKNOWN";
-      break;
-  }
-}
-
-void SctpTransport::OnStreamResetEvent(
-    const struct sctp_stream_reset_event* evt) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  // A stream reset always involves two RE-CONFIG chunks for us -- we always
-  // simultaneously reset a sid's sequence number in both directions.  The
-  // requesting side transmits a RE-CONFIG chunk and waits for the peer to send
-  // one back.  Both sides get this SCTP_STREAM_RESET_EVENT when they receive
-  // RE-CONFIGs.
-  const int num_sids = (evt->strreset_length - sizeof(*evt)) /
-                       sizeof(evt->strreset_stream_list[0]);
-  LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
-                  << "): Flags = 0x" << std::hex << evt->strreset_flags << " ("
-                  << ListFlags(evt->strreset_flags) << ")";
-  LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = ["
-                  << ListArray(evt->strreset_stream_list, num_sids)
-                  << "], Open: [" << ListStreams(open_streams_) << "], Q'd: ["
-                  << ListStreams(queued_reset_streams_) << "], Sent: ["
-                  << ListStreams(sent_reset_streams_) << "]";
-
-  // If both sides try to reset some streams at the same time (even if they're
-  // disjoint sets), we can get reset failures.
-  if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) {
-    // OK, just try again.  The stream IDs sent over when the RESET_FAILED flag
-    // is set seem to be garbage values.  Ignore them.
-    queued_reset_streams_.insert(sent_reset_streams_.begin(),
-                                 sent_reset_streams_.end());
-    sent_reset_streams_.clear();
-
-  } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
-    // Each side gets an event for each direction of a stream.  That is,
-    // closing sid k will make each side receive INCOMING and OUTGOING reset
-    // events for k.  As per RFC6525, Section 5, paragraph 2, each side will
-    // get an INCOMING event first.
-    for (int i = 0; i < num_sids; i++) {
-      const int stream_id = evt->strreset_stream_list[i];
-
-      // See if this stream ID was closed by our peer or ourselves.
-      StreamSet::iterator it = sent_reset_streams_.find(stream_id);
-
-      // The reset was requested locally.
-      if (it != sent_reset_streams_.end()) {
-        LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
-                        << "): local sid " << stream_id << " acknowledged.";
-        sent_reset_streams_.erase(it);
-
-      } else if ((it = open_streams_.find(stream_id)) != open_streams_.end()) {
-        // The peer requested the reset.
-        LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
-                        << "): closing sid " << stream_id;
-        open_streams_.erase(it);
-        SignalStreamClosedRemotely(stream_id);
-
-      } else if ((it = queued_reset_streams_.find(stream_id)) !=
-                 queued_reset_streams_.end()) {
-        // The peer requested the reset, but there was a local reset
-        // queued.
-        LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
-                        << "): double-sided close for sid " << stream_id;
-        // Both sides want the stream closed, and the peer got to send the
-        // RE-CONFIG first.  Treat it like the local Remove(Send|Recv)Stream
-        // finished quickly.
-        queued_reset_streams_.erase(it);
-
-      } else {
-        // This stream is unknown.  Sometimes this can be from an
-        // RESET_FAILED-related retransmit.
-        LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
-                        << "): Unknown sid " << stream_id;
-      }
-    }
-  }
-
-  // Always try to send the queued RESET because this call indicates that the
-  // last local RESET or remote RESET has made some progress.
-  SendQueuedStreamResets();
-}
-
-}  // namespace cricket
diff --git a/webrtc/media/sctp/sctptransport.h b/webrtc/media/sctp/sctptransport.h
deleted file mode 100644
index 6d3a41a..0000000
--- a/webrtc/media/sctp/sctptransport.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef WEBRTC_MEDIA_SCTP_SCTPTRANSPORT_H_
-#define WEBRTC_MEDIA_SCTP_SCTPTRANSPORT_H_
-
-#include <errno.h>
-
-#include <memory>  // for unique_ptr.
-#include <set>
-#include <string>
-#include <vector>
-
-#include "webrtc/base/asyncinvoker.h"
-#include "webrtc/base/constructormagic.h"
-#include "webrtc/base/copyonwritebuffer.h"
-#include "webrtc/base/sigslot.h"
-#include "webrtc/base/thread.h"
-// For SendDataParams/ReceiveDataParams.
-#include "webrtc/media/base/mediachannel.h"
-#include "webrtc/media/sctp/sctptransportinternal.h"
-#include "webrtc/p2p/base/transportchannel.h"
-
-// Defined by "usrsctplib/usrsctp.h"
-struct sockaddr_conn;
-struct sctp_assoc_change;
-struct sctp_stream_reset_event;
-// Defined by <sys/socket.h>
-struct socket;
-namespace cricket {
-
-// Holds data to be passed on to a channel.
-struct SctpInboundPacket;
-
-// From channel calls, data flows like this:
-// [network thread (although it can in princple be another thread)]
-//  1.  SctpTransport::SendData(data)
-//  2.  usrsctp_sendv(data)
-// [network thread returns; sctp thread then calls the following]
-//  3.  OnSctpOutboundPacket(wrapped_data)
-// [sctp thread returns having async invoked on the network thread]
-//  4.  SctpTransport::OnPacketFromSctpToNetwork(wrapped_data)
-//  5.  TransportChannel::SendPacket(wrapped_data)
-//  6.  ... across network ... a packet is sent back ...
-//  7.  SctpTransport::OnPacketReceived(wrapped_data)
-//  8.  usrsctp_conninput(wrapped_data)
-// [network thread returns; sctp thread then calls the following]
-//  9.  OnSctpInboundData(data)
-// [sctp thread returns having async invoked on the network thread]
-//  10. SctpTransport::OnInboundPacketFromSctpToChannel(inboundpacket)
-//  11. SctpTransport::OnDataFromSctpToChannel(data)
-//  12. SctpTransport::SignalDataReceived(data)
-// [from the same thread, methods registered/connected to
-//  SctpTransport are called with the recieved data]
-class SctpTransport : public SctpTransportInternal,
-                      public sigslot::has_slots<> {
- public:
-  // |network_thread| is where packets will be processed and callbacks from
-  // this transport will be posted, and is the only thread on which public
-  // methods can be called.
-  // |channel| is required (must not be null).
-  SctpTransport(rtc::Thread* network_thread,
-                cricket::TransportChannel* channel);
-  ~SctpTransport() override;
-
-  // SctpTransportInternal overrides (see sctptransportinternal.h for comments).
-  void SetTransportChannel(cricket::TransportChannel* channel) override;
-  bool Start(int local_port, int remote_port) override;
-  bool OpenStream(int sid) override;
-  bool ResetStream(int sid) override;
-  bool SendData(const SendDataParams& params,
-                const rtc::CopyOnWriteBuffer& payload,
-                SendDataResult* result = nullptr) override;
-  bool ReadyToSendData() override;
-  void set_debug_name_for_testing(const char* debug_name) override {
-    debug_name_ = debug_name;
-  }
-
-  // Exposed to allow Post call from c-callbacks.
-  // TODO(deadbeef): Remove this or at least make it return a const pointer.
-  rtc::Thread* network_thread() const { return network_thread_; }
-
- private:
-  void ConnectTransportChannelSignals();
-  void DisconnectTransportChannelSignals();
-
-  // Creates the socket and connects.
-  bool Connect();
-
-  // Returns false when opening the socket failed.
-  bool OpenSctpSocket();
-  // Helpet method to set socket options.
-  bool ConfigureSctpSocket();
-  // Sets |sock_ |to nullptr.
-  void CloseSctpSocket();
-
-  // Sends a SCTP_RESET_STREAM for all streams in closing_ssids_.
-  bool SendQueuedStreamResets();
-
-  // Sets the "ready to send" flag and fires signal if needed.
-  void SetReadyToSendData();
-
-  // Callbacks from DTLS channel.
-  void OnWritableState(rtc::PacketTransportInterface* transport);
-  virtual void OnPacketRead(rtc::PacketTransportInterface* transport,
-                            const char* data,
-                            size_t len,
-                            const rtc::PacketTime& packet_time,
-                            int flags);
-
-  // Methods related to usrsctp callbacks.
-  void OnSendThresholdCallback();
-  sockaddr_conn GetSctpSockAddr(int port);
-
-  // Called using |invoker_| to send packet on the network.
-  void OnPacketFromSctpToNetwork(const rtc::CopyOnWriteBuffer& buffer);
-  // Called using |invoker_| to decide what to do with the packet.
-  // The |flags| parameter is used by SCTP to distinguish notification packets
-  // from other types of packets.
-  void OnInboundPacketFromSctpToChannel(const rtc::CopyOnWriteBuffer& buffer,
-                                        ReceiveDataParams params,
-                                        int flags);
-  void OnDataFromSctpToChannel(const ReceiveDataParams& params,
-                               const rtc::CopyOnWriteBuffer& buffer);
-  void OnNotificationFromSctp(const rtc::CopyOnWriteBuffer& buffer);
-  void OnNotificationAssocChange(const sctp_assoc_change& change);
-
-  void OnStreamResetEvent(const struct sctp_stream_reset_event* evt);
-
-  // Responsible for marshalling incoming data to the channels listeners, and
-  // outgoing data to the network interface.
-  rtc::Thread* network_thread_;
-  // Helps pass inbound/outbound packets asynchronously to the network thread.
-  rtc::AsyncInvoker invoker_;
-  // Underlying DTLS channel.
-  TransportChannel* transport_channel_;
-  bool was_ever_writable_ = false;
-  int local_port_ = kSctpDefaultPort;
-  int remote_port_ = kSctpDefaultPort;
-  struct socket* sock_ = nullptr;  // The socket created by usrsctp_socket(...).
-
-  // Has Start been called? Don't create SCTP socket until it has.
-  bool started_ = false;
-  // Are we ready to queue data (SCTP socket created, and not blocked due to
-  // congestion control)? Different than |transport_channel_|'s "ready to
-  // send".
-  bool ready_to_send_data_ = false;
-
-  typedef std::set<uint32_t> StreamSet;
-  // When a data channel opens a stream, it goes into open_streams_.  When we
-  // want to close it, the stream's ID goes into queued_reset_streams_.  When
-  // we actually transmit a RE-CONFIG chunk with that stream ID, the ID goes
-  // into sent_reset_streams_.  When we get a response RE-CONFIG chunk back
-  // acknowledging the reset, we remove the stream ID from
-  // sent_reset_streams_.  We use sent_reset_streams_ to differentiate
-  // between acknowledgment RE-CONFIG and peer-initiated RE-CONFIGs.
-  StreamSet open_streams_;
-  StreamSet queued_reset_streams_;
-  StreamSet sent_reset_streams_;
-
-  // A static human-readable name for debugging messages.
-  const char* debug_name_ = "SctpTransport";
-  // Hides usrsctp interactions from this header file.
-  class UsrSctpWrapper;
-
-  RTC_DISALLOW_COPY_AND_ASSIGN(SctpTransport);
-};
-
-class SctpTransportFactory : public SctpTransportInternalFactory {
- public:
-  explicit SctpTransportFactory(rtc::Thread* network_thread)
-      : network_thread_(network_thread) {}
-
-  std::unique_ptr<SctpTransportInternal> CreateSctpTransport(
-      TransportChannel* channel) override {
-    return std::unique_ptr<SctpTransportInternal>(
-        new SctpTransport(network_thread_, channel));
-  }
-
- private:
-  rtc::Thread* network_thread_;
-};
-
-}  // namespace cricket
-
-#endif  // WEBRTC_MEDIA_SCTP_SCTPTRANSPORT_H_
diff --git a/webrtc/media/sctp/sctptransport_unittest.cc b/webrtc/media/sctp/sctptransport_unittest.cc
deleted file mode 100644
index 42e4dc6..0000000
--- a/webrtc/media/sctp/sctptransport_unittest.cc
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "webrtc/base/bind.h"
-#include "webrtc/base/copyonwritebuffer.h"
-#include "webrtc/base/criticalsection.h"
-#include "webrtc/base/gunit.h"
-#include "webrtc/base/helpers.h"
-#include "webrtc/base/ssladapter.h"
-#include "webrtc/base/thread.h"
-#include "webrtc/media/sctp/sctptransport.h"
-#include "webrtc/p2p/base/faketransportcontroller.h"
-
-namespace {
-static const int kDefaultTimeout = 10000;  // 10 seconds.
-// Use ports other than the default 5000 for testing.
-static const int kTransport1Port = 5001;
-static const int kTransport2Port = 5002;
-}
-
-namespace cricket {
-
-// This is essentially a buffer to hold recieved data. It stores only the last
-// received data. Calling OnDataReceived twice overwrites old data with the
-// newer one.
-// TODO(ldixon): Implement constraints, and allow new data to be added to old
-// instead of replacing it.
-class SctpFakeDataReceiver : public sigslot::has_slots<> {
- public:
-  SctpFakeDataReceiver() : received_(false) {}
-
-  void Clear() {
-    received_ = false;
-    last_data_ = "";
-    last_params_ = ReceiveDataParams();
-  }
-
-  virtual void OnDataReceived(const ReceiveDataParams& params,
-                              const rtc::CopyOnWriteBuffer& data) {
-    received_ = true;
-    last_data_ = std::string(data.data<char>(), data.size());
-    last_params_ = params;
-  }
-
-  bool received() const { return received_; }
-  std::string last_data() const { return last_data_; }
-  ReceiveDataParams last_params() const { return last_params_; }
-
- private:
-  bool received_;
-  std::string last_data_;
-  ReceiveDataParams last_params_;
-};
-
-class SignalReadyToSendObserver : public sigslot::has_slots<> {
- public:
-  SignalReadyToSendObserver() : signaled_(false) {}
-
-  void OnSignaled() { signaled_ = true; }
-
-  bool IsSignaled() { return signaled_; }
-
- private:
-  bool signaled_;
-};
-
-class SignalTransportClosedObserver : public sigslot::has_slots<> {
- public:
-  SignalTransportClosedObserver() {}
-  void BindSelf(SctpTransport* transport) {
-    transport->SignalStreamClosedRemotely.connect(
-        this, &SignalTransportClosedObserver::OnStreamClosed);
-  }
-  void OnStreamClosed(int stream) { streams_.push_back(stream); }
-
-  int StreamCloseCount(int stream) {
-    return std::count(streams_.begin(), streams_.end(), stream);
-  }
-
-  bool WasStreamClosed(int stream) {
-    return std::find(streams_.begin(), streams_.end(), stream) !=
-           streams_.end();
-  }
-
- private:
-  std::vector<int> streams_;
-};
-
-class SignalTransportClosedReopener : public sigslot::has_slots<> {
- public:
-  SignalTransportClosedReopener(SctpTransport* transport, SctpTransport* peer)
-      : transport_(transport), peer_(peer) {}
-
-  void OnStreamClosed(int stream) {
-    transport_->OpenStream(stream);
-    peer_->OpenStream(stream);
-    streams_.push_back(stream);
-  }
-
-  int StreamCloseCount(int stream) {
-    return std::count(streams_.begin(), streams_.end(), stream);
-  }
-
- private:
-  SctpTransport* transport_;
-  SctpTransport* peer_;
-  std::vector<int> streams_;
-};
-
-// SCTP Data Engine testing framework.
-class SctpTransportTest : public testing::Test, public sigslot::has_slots<> {
- protected:
-  // usrsctp uses the NSS random number generator on non-Android platforms,
-  // so we need to initialize SSL.
-  static void SetUpTestCase() {}
-
-  void SetupConnectedTransportsWithTwoStreams() {
-    fake_dtls1_.reset(new FakeTransportChannel("fake dtls 1", 0));
-    fake_dtls2_.reset(new FakeTransportChannel("fake dtls 2", 0));
-    recv1_.reset(new SctpFakeDataReceiver());
-    recv2_.reset(new SctpFakeDataReceiver());
-    transport1_.reset(CreateTransport(fake_dtls1_.get(), recv1_.get()));
-    transport1_->set_debug_name_for_testing("transport1");
-    transport1_->SignalReadyToSendData.connect(
-        this, &SctpTransportTest::OnChan1ReadyToSend);
-    transport2_.reset(CreateTransport(fake_dtls2_.get(), recv2_.get()));
-    transport2_->set_debug_name_for_testing("transport2");
-    transport2_->SignalReadyToSendData.connect(
-        this, &SctpTransportTest::OnChan2ReadyToSend);
-    // Setup two connected transports ready to send and receive.
-    bool asymmetric = false;
-    fake_dtls1_->SetDestination(fake_dtls2_.get(), asymmetric);
-
-    LOG(LS_VERBOSE) << "Transport setup ----------------------------- ";
-    AddStream(1);
-    AddStream(2);
-
-    LOG(LS_VERBOSE) << "Connect the transports -----------------------------";
-    // Both transports need to have started (with matching ports) for an
-    // association to be formed.
-    transport1_->Start(kTransport1Port, kTransport2Port);
-    transport2_->Start(kTransport2Port, kTransport1Port);
-  }
-
-  bool AddStream(int sid) {
-    bool ret = true;
-    ret = ret && transport1_->OpenStream(sid);
-    ret = ret && transport2_->OpenStream(sid);
-    return ret;
-  }
-
-  SctpTransport* CreateTransport(FakeTransportChannel* fake_dtls,
-                                 SctpFakeDataReceiver* recv) {
-    SctpTransport* transport =
-        new SctpTransport(rtc::Thread::Current(), fake_dtls);
-    // When data is received, pass it to the SctpFakeDataReceiver.
-    transport->SignalDataReceived.connect(
-        recv, &SctpFakeDataReceiver::OnDataReceived);
-    return transport;
-  }
-
-  bool SendData(SctpTransport* chan,
-                int sid,
-                const std::string& msg,
-                SendDataResult* result) {
-    SendDataParams params;
-    params.sid = sid;
-
-    return chan->SendData(params, rtc::CopyOnWriteBuffer(&msg[0], msg.length()),
-                          result);
-  }
-
-  bool ReceivedData(const SctpFakeDataReceiver* recv,
-                    int sid,
-                    const std::string& msg) {
-    return (recv->received() && recv->last_params().sid == sid &&
-            recv->last_data() == msg);
-  }
-
-  bool ProcessMessagesUntilIdle() {
-    rtc::Thread* thread = rtc::Thread::Current();
-    while (!thread->empty()) {
-      rtc::Message msg;
-      if (thread->Get(&msg, rtc::Thread::kForever)) {
-        thread->Dispatch(&msg);
-      }
-    }
-    return !thread->IsQuitting();
-  }
-
-  SctpTransport* transport1() { return transport1_.get(); }
-  SctpTransport* transport2() { return transport2_.get(); }
-  SctpFakeDataReceiver* receiver1() { return recv1_.get(); }
-  SctpFakeDataReceiver* receiver2() { return recv2_.get(); }
-  FakeTransportChannel* fake_dtls1() { return fake_dtls1_.get(); }
-  FakeTransportChannel* fake_dtls2() { return fake_dtls2_.get(); }
-
-  int transport1_ready_to_send_count() {
-    return transport1_ready_to_send_count_;
-  }
-  int transport2_ready_to_send_count() {
-    return transport2_ready_to_send_count_;
-  }
-
- private:
-  std::unique_ptr<FakeTransportChannel> fake_dtls1_;
-  std::unique_ptr<FakeTransportChannel> fake_dtls2_;
-  std::unique_ptr<SctpFakeDataReceiver> recv1_;
-  std::unique_ptr<SctpFakeDataReceiver> recv2_;
-  std::unique_ptr<SctpTransport> transport1_;
-  std::unique_ptr<SctpTransport> transport2_;
-
-  int transport1_ready_to_send_count_ = 0;
-  int transport2_ready_to_send_count_ = 0;
-
-  void OnChan1ReadyToSend() { ++transport1_ready_to_send_count_; }
-  void OnChan2ReadyToSend() { ++transport2_ready_to_send_count_; }
-};
-
-// Test that data can be sent end-to-end when an SCTP transport starts with one
-// transport channel (which is unwritable), and then switches to another
-// channel. A common scenario due to how BUNDLE works.
-TEST_F(SctpTransportTest, SwitchTransportChannel) {
-  FakeTransportChannel black_hole("black hole", 0);
-  FakeTransportChannel fake_dtls1("fake dtls 1", 0);
-  FakeTransportChannel fake_dtls2("fake dtls 2", 0);
-  SctpFakeDataReceiver recv1;
-  SctpFakeDataReceiver recv2;
-
-  // Construct transport1 with the "black hole" channel.
-  std::unique_ptr<SctpTransport> transport1(
-      CreateTransport(&black_hole, &recv1));
-  std::unique_ptr<SctpTransport> transport2(
-      CreateTransport(&fake_dtls2, &recv2));
-
-  // Add a stream.
-  transport1->OpenStream(1);
-  transport2->OpenStream(1);
-
-  // Tell them both to start (though transport1_ is connected to black_hole).
-  transport1->Start(kTransport1Port, kTransport2Port);
-  transport2->Start(kTransport2Port, kTransport1Port);
-
-  // Switch transport1_ to the normal fake_dtls1_ channel.
-  transport1->SetTransportChannel(&fake_dtls1);
-
-  // Connect the two fake DTLS channels.
-  bool asymmetric = false;
-  fake_dtls1.SetDestination(&fake_dtls2, asymmetric);
-
-  // Make sure we end up able to send data.
-  SendDataResult result;
-  ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result));
-  ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result));
-  EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout);
-  EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout);
-}
-
-// Calling Start twice shouldn't do anything bad, if with the same parameters.
-TEST_F(SctpTransportTest, DuplicateStartCallsIgnored) {
-  SetupConnectedTransportsWithTwoStreams();
-  EXPECT_TRUE(transport1()->Start(kTransport1Port, kTransport2Port));
-
-  // Make sure we can still send/recv data.
-  SendDataResult result;
-  ASSERT_TRUE(SendData(transport1(), 1, "foo", &result));
-  ASSERT_TRUE(SendData(transport2(), 1, "bar", &result));
-  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "foo"), kDefaultTimeout);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 1, "bar"), kDefaultTimeout);
-}
-
-// Calling Start a second time with a different port should fail.
-TEST_F(SctpTransportTest, CallingStartWithDifferentPortFails) {
-  SetupConnectedTransportsWithTwoStreams();
-  EXPECT_FALSE(transport1()->Start(kTransport1Port, 1234));
-  EXPECT_FALSE(transport1()->Start(1234, kTransport2Port));
-}
-
-// A value of -1 for the local/remote port should be treated as the default
-// (5000).
-TEST_F(SctpTransportTest, NegativeOnePortTreatedAsDefault) {
-  FakeTransportChannel fake_dtls1("fake dtls 1", 0);
-  FakeTransportChannel fake_dtls2("fake dtls 2", 0);
-  SctpFakeDataReceiver recv1;
-  SctpFakeDataReceiver recv2;
-  std::unique_ptr<SctpTransport> transport1(
-      CreateTransport(&fake_dtls1, &recv1));
-  std::unique_ptr<SctpTransport> transport2(
-      CreateTransport(&fake_dtls2, &recv2));
-
-  // Add a stream.
-  transport1->OpenStream(1);
-  transport2->OpenStream(1);
-
-  // Tell them both to start, giving one transport the default port and the
-  // other transport -1.
-  transport1->Start(kSctpDefaultPort, kSctpDefaultPort);
-  transport2->Start(-1, -1);
-
-  // Connect the two fake DTLS channels.
-  bool asymmetric = false;
-  fake_dtls1.SetDestination(&fake_dtls2, asymmetric);
-
-  // Make sure we end up able to send data.
-  SendDataResult result;
-  ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result));
-  ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result));
-  EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout);
-  EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, OpenStreamWithAlreadyOpenedStreamFails) {
-  FakeTransportChannel fake_dtls("fake dtls", 0);
-  SctpFakeDataReceiver recv;
-  std::unique_ptr<SctpTransport> transport(CreateTransport(&fake_dtls, &recv));
-  EXPECT_TRUE(transport->OpenStream(1));
-  EXPECT_FALSE(transport->OpenStream(1));
-}
-
-TEST_F(SctpTransportTest, ResetStreamWithAlreadyResetStreamFails) {
-  FakeTransportChannel fake_dtls("fake dtls", 0);
-  SctpFakeDataReceiver recv;
-  std::unique_ptr<SctpTransport> transport(CreateTransport(&fake_dtls, &recv));
-  EXPECT_TRUE(transport->OpenStream(1));
-  EXPECT_TRUE(transport->ResetStream(1));
-  EXPECT_FALSE(transport->ResetStream(1));
-}
-
-// Test that SignalReadyToSendData is fired after Start has been called and the
-// DTLS channel is writable.
-TEST_F(SctpTransportTest, SignalReadyToSendDataAfterDtlsWritable) {
-  FakeTransportChannel fake_dtls("fake dtls", 0);
-  SctpFakeDataReceiver recv;
-  std::unique_ptr<SctpTransport> transport(CreateTransport(&fake_dtls, &recv));
-
-  SignalReadyToSendObserver signal_observer;
-  transport->SignalReadyToSendData.connect(
-      &signal_observer, &SignalReadyToSendObserver::OnSignaled);
-
-  transport->Start(kSctpDefaultPort, kSctpDefaultPort);
-  fake_dtls.SetWritable(true);
-  EXPECT_TRUE_WAIT(signal_observer.IsSignaled(), kDefaultTimeout);
-}
-
-// Test that after an SCTP socket's buffer is filled, SignalReadyToSendData
-// is fired after it begins to be drained.
-TEST_F(SctpTransportTest, SignalReadyToSendDataAfterBlocked) {
-  SetupConnectedTransportsWithTwoStreams();
-  // Wait for initial SCTP association to be formed.
-  EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout);
-  // Make the fake transport unwritable so that messages pile up for the SCTP
-  // socket.
-  fake_dtls1()->SetWritable(false);
-  // Send messages until we get EWOULDBLOCK.
-  static const int kMaxMessages = 1024;
-  SendDataParams params;
-  params.sid = 1;
-  rtc::CopyOnWriteBuffer buf(1024);
-  memset(buf.data<uint8_t>(), 0, 1024);
-  SendDataResult result;
-  int message_count;
-  for (message_count = 0; message_count < kMaxMessages; ++message_count) {
-    if (!transport1()->SendData(params, buf, &result) && result == SDR_BLOCK) {
-      break;
-    }
-  }
-  ASSERT_NE(kMaxMessages, message_count)
-      << "Sent max number of messages without getting SDR_BLOCK?";
-  // Make sure the ready-to-send count hasn't changed.
-  EXPECT_EQ(1, transport1_ready_to_send_count());
-  // Make the transport writable again and expect a "SignalReadyToSendData" at
-  // some point.
-  fake_dtls1()->SetWritable(true);
-  EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, SendData) {
-  SetupConnectedTransportsWithTwoStreams();
-
-  SendDataResult result;
-  LOG(LS_VERBOSE)
-      << "transport1 sending: 'hello?' -----------------------------";
-  ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
-  LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received()
-                  << ", recv2.last_params.sid="
-                  << receiver2()->last_params().sid
-                  << ", recv2.last_params.timestamp="
-                  << receiver2()->last_params().timestamp
-                  << ", recv2.last_params.seq_num="
-                  << receiver2()->last_params().seq_num
-                  << ", recv2.last_data=" << receiver2()->last_data();
-
-  LOG(LS_VERBOSE)
-      << "transport2 sending: 'hi transport1' -----------------------------";
-  ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
-                   kDefaultTimeout);
-  LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received()
-                  << ", recv1.last_params.sid="
-                  << receiver1()->last_params().sid
-                  << ", recv1.last_params.timestamp="
-                  << receiver1()->last_params().timestamp
-                  << ", recv1.last_params.seq_num="
-                  << receiver1()->last_params().seq_num
-                  << ", recv1.last_data=" << receiver1()->last_data();
-}
-
-// Sends a lot of large messages at once and verifies SDR_BLOCK is returned.
-TEST_F(SctpTransportTest, SendDataBlocked) {
-  SetupConnectedTransportsWithTwoStreams();
-
-  SendDataResult result;
-  SendDataParams params;
-  params.sid = 1;
-
-  std::vector<char> buffer(1024 * 64, 0);
-
-  for (size_t i = 0; i < 100; ++i) {
-    transport1()->SendData(
-        params, rtc::CopyOnWriteBuffer(&buffer[0], buffer.size()), &result);
-    if (result == SDR_BLOCK)
-      break;
-  }
-
-  EXPECT_EQ(SDR_BLOCK, result);
-}
-
-// Trying to send data for a nonexistent stream should fail.
-TEST_F(SctpTransportTest, SendDataWithNonexistentStreamFails) {
-  SetupConnectedTransportsWithTwoStreams();
-  SendDataResult result;
-  EXPECT_FALSE(SendData(transport2(), 123, "some data", &result));
-  EXPECT_EQ(SDR_ERROR, result);
-}
-
-TEST_F(SctpTransportTest, ClosesRemoteStream) {
-  SetupConnectedTransportsWithTwoStreams();
-  SignalTransportClosedObserver transport1_sig_receiver,
-      transport2_sig_receiver;
-  transport1_sig_receiver.BindSelf(transport1());
-  transport2_sig_receiver.BindSelf(transport2());
-
-  SendDataResult result;
-  ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
-  ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
-                   kDefaultTimeout);
-
-  // Close transport 1.  Transport 2 should notify us.
-  transport1()->ResetStream(1);
-  EXPECT_TRUE_WAIT(transport2_sig_receiver.WasStreamClosed(1), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, ClosesTwoRemoteStreams) {
-  SetupConnectedTransportsWithTwoStreams();
-  AddStream(3);
-  SignalTransportClosedObserver transport1_sig_receiver,
-      transport2_sig_receiver;
-  transport1_sig_receiver.BindSelf(transport1());
-  transport2_sig_receiver.BindSelf(transport2());
-
-  SendDataResult result;
-  ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
-  ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
-                   kDefaultTimeout);
-
-  // Close two streams on one side.
-  transport2()->ResetStream(2);
-  transport2()->ResetStream(3);
-  EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(2), kDefaultTimeout);
-  EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(3), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, ClosesStreamsOnBothSides) {
-  SetupConnectedTransportsWithTwoStreams();
-  AddStream(3);
-  AddStream(4);
-  SignalTransportClosedObserver transport1_sig_receiver,
-      transport2_sig_receiver;
-  transport1_sig_receiver.BindSelf(transport1());
-  transport2_sig_receiver.BindSelf(transport2());
-
-  SendDataResult result;
-  ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
-  ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
-                   kDefaultTimeout);
-
-  // Close one stream on transport1(), while closing three streams on
-  // transport2().  They will conflict (only one side can close anything at a
-  // time, apparently).  Test the resolution of the conflict.
-  transport1()->ResetStream(1);
-
-  transport2()->ResetStream(2);
-  transport2()->ResetStream(3);
-  transport2()->ResetStream(4);
-  EXPECT_TRUE_WAIT(transport2_sig_receiver.WasStreamClosed(1), kDefaultTimeout);
-  EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(2), kDefaultTimeout);
-  EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(3), kDefaultTimeout);
-  EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(4), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, RefusesHighNumberedTransports) {
-  SetupConnectedTransportsWithTwoStreams();
-  EXPECT_TRUE(AddStream(kMaxSctpSid));
-  EXPECT_FALSE(AddStream(kMaxSctpSid + 1));
-}
-
-// Flaky, see webrtc:4453.
-TEST_F(SctpTransportTest, DISABLED_ReusesAStream) {
-  // Shut down transport 1, then open it up again for reuse.
-  SetupConnectedTransportsWithTwoStreams();
-  SendDataResult result;
-  SignalTransportClosedObserver transport2_sig_receiver;
-  transport2_sig_receiver.BindSelf(transport2());
-
-  ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
-
-  transport1()->ResetStream(1);
-  EXPECT_TRUE_WAIT(transport2_sig_receiver.WasStreamClosed(1), kDefaultTimeout);
-  // Transport 1 is gone now.
-
-  // Create a new transport 1.
-  AddStream(1);
-  ASSERT_TRUE(SendData(transport1(), 1, "hi?", &result));
-  EXPECT_EQ(SDR_SUCCESS, result);
-  EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), kDefaultTimeout);
-  transport1()->ResetStream(1);
-  EXPECT_TRUE_WAIT(transport2_sig_receiver.StreamCloseCount(1) == 2,
-                   kDefaultTimeout);
-}
-
-}  // namespace cricket
diff --git a/webrtc/media/sctp/sctptransportinternal.h b/webrtc/media/sctp/sctptransportinternal.h
deleted file mode 100644
index 7dd6bc7..0000000
--- a/webrtc/media/sctp/sctptransportinternal.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef WEBRTC_MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_
-#define WEBRTC_MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_
-
-// TODO(deadbeef): Move SCTP code out of media/, and make it not depend on
-// anything in media/.
-
-#include <memory>  // for unique_ptr
-#include <string>
-#include <vector>
-
-#include "webrtc/base/copyonwritebuffer.h"
-#include "webrtc/base/thread.h"
-// For SendDataParams/ReceiveDataParams.
-// TODO(deadbeef): Use something else for SCTP. It's confusing that we use an
-// SSRC field for SID.
-#include "webrtc/media/base/mediachannel.h"
-#include "webrtc/p2p/base/transportchannel.h"
-
-namespace cricket {
-
-// The number of outgoing streams that we'll negotiate. Since stream IDs (SIDs)
-// are 0-based, the highest usable SID is 1023.
-//
-// It's recommended to use the maximum of 65535 in:
-// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.2
-// However, we use 1024 in order to save memory. usrsctp allocates 104 bytes
-// for each pair of incoming/outgoing streams (on a 64-bit system), so 65535
-// streams would waste ~6MB.
-//
-// Note: "max" and "min" here are inclusive.
-constexpr uint16_t kMaxSctpStreams = 1024;
-constexpr uint16_t kMaxSctpSid = kMaxSctpStreams - 1;
-constexpr uint16_t kMinSctpSid = 0;
-
-// This is the default SCTP port to use. It is passed along the wire and the
-// connectee and connector must be using the same port. It is not related to the
-// ports at the IP level. (Corresponds to: sockaddr_conn.sconn_port in
-// usrsctp.h)
-const int kSctpDefaultPort = 5000;
-
-// Abstract SctpTransport interface for use internally (by
-// PeerConnection/WebRtcSession/etc.). Exists to allow mock/fake SctpTransports
-// to be created.
-class SctpTransportInternal {
- public:
-  virtual ~SctpTransportInternal() {}
-
-  // Changes what underlying DTLS channel is uses. Used when switching which
-  // bundled transport the SctpTransport uses.
-  // Assumes |channel| is non-null.
-  virtual void SetTransportChannel(TransportChannel* channel) = 0;
-
-  // When Start is called, connects as soon as possible; this can be called
-  // before DTLS completes, in which case the connection will begin when DTLS
-  // completes. This method can be called multiple times, though not if either
-  // of the ports are changed.
-  //
-  // |local_sctp_port| and |remote_sctp_port| are passed along the wire and the
-  // listener and connector must be using the same port. They are not related
-  // to the ports at the IP level. If set to -1, we default to
-  // kSctpDefaultPort.
-  //
-  // TODO(deadbeef): Add remote max message size as parameter to Start, once we
-  // start supporting it.
-  // TODO(deadbeef): Support calling Start with different local/remote ports
-  // and create a new association? Not clear if this is something we need to
-  // support though. See: https://github.com/w3c/webrtc-pc/issues/979
-  virtual bool Start(int local_sctp_port, int remote_sctp_port) = 0;
-
-  // NOTE: Initially there was a "Stop" method here, but it was never used, so
-  // it was removed.
-
-  // Informs SctpTransport that |sid| will start being used. Returns false if
-  // it is impossible to use |sid|, or if it's already in use.
-  // Until calling this, can't send data using |sid|.
-  // TODO(deadbeef): Actually implement the "returns false if |sid| can't be
-  // used" part. See:
-  // https://bugs.chromium.org/p/chromium/issues/detail?id=619849
-  virtual bool OpenStream(int sid) = 0;
-  // The inverse of OpenStream. When this method returns, the reset process may
-  // have not finished but it will have begun.
-  // TODO(deadbeef): We need a way to tell when it's done. See:
-  // https://bugs.chromium.org/p/webrtc/issues/detail?id=4453
-  virtual bool ResetStream(int sid) = 0;
-  // Send data down this channel (will be wrapped as SCTP packets then given to
-  // usrsctp that will then post the network interface).
-  // Returns true iff successful data somewhere on the send-queue/network.
-  // Uses |params.ssrc| as the SCTP sid.
-  virtual bool SendData(const SendDataParams& params,
-                        const rtc::CopyOnWriteBuffer& payload,
-                        SendDataResult* result = nullptr) = 0;
-
-  // Indicates when the SCTP socket is created and not blocked by congestion
-  // control. This changes to false when SDR_BLOCK is returned from SendData,
-  // and
-  // changes to true when SignalReadyToSendData is fired. The underlying DTLS/
-  // ICE channels may be unwritable while ReadyToSendData is true, because data
-  // can still be queued in usrsctp.
-  virtual bool ReadyToSendData() = 0;
-
-  sigslot::signal0<> SignalReadyToSendData;
-  // ReceiveDataParams includes SID, seq num, timestamp, etc. CopyOnWriteBuffer
-  // contains message payload.
-  sigslot::signal2<const ReceiveDataParams&, const rtc::CopyOnWriteBuffer&>
-      SignalDataReceived;
-  // Parameter is SID of closed stream.
-  sigslot::signal1<int> SignalStreamClosedRemotely;
-
-  // Helper for debugging.
-  virtual void set_debug_name_for_testing(const char* debug_name) = 0;
-};
-
-// Factory class which can be used to allow fake SctpTransports to be injected
-// for testing. Or, theoretically, SctpTransportInternal implementations that
-// use something other than usrsctp.
-class SctpTransportInternalFactory {
- public:
-  virtual ~SctpTransportInternalFactory() {}
-
-  // Create an SCTP transport using |channel| for the underlying transport.
-  virtual std::unique_ptr<SctpTransportInternal> CreateSctpTransport(
-      TransportChannel* channel) = 0;
-};
-
-}  // namespace cricket
-
-#endif  // WEBRTC_MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_
diff --git a/webrtc/pc/channel.cc b/webrtc/pc/channel.cc
index 56335e2..1f29261 100644
--- a/webrtc/pc/channel.cc
+++ b/webrtc/pc/channel.cc
@@ -69,6 +69,7 @@
   MSG_READYTOSENDDATA,
   MSG_DATARECEIVED,
   MSG_FIRSTPACKETRECEIVED,
+  MSG_STREAMCLOSEDREMOTELY,
 };
 
 // Value specified in RFC 5764.
@@ -930,16 +931,16 @@
   UpdateMediaSendRecvState();
 }
 
-void BaseChannel::SignalDtlsSrtpSetupFailure_n(bool rtcp) {
+void BaseChannel::SignalDtlsSetupFailure_n(bool rtcp) {
   RTC_DCHECK(network_thread_->IsCurrent());
   invoker_.AsyncInvoke<void>(
       RTC_FROM_HERE, signaling_thread(),
-      Bind(&BaseChannel::SignalDtlsSrtpSetupFailure_s, this, rtcp));
+      Bind(&BaseChannel::SignalDtlsSetupFailure_s, this, rtcp));
 }
 
-void BaseChannel::SignalDtlsSrtpSetupFailure_s(bool rtcp) {
+void BaseChannel::SignalDtlsSetupFailure_s(bool rtcp) {
   RTC_DCHECK(signaling_thread() == rtc::Thread::Current());
-  SignalDtlsSrtpSetupFailure(this, rtcp);
+  SignalDtlsSetupFailure(this, rtcp);
 }
 
 bool BaseChannel::SetDtlsSrtpCryptoSuites_n(TransportChannel* tc, bool rtcp) {
@@ -1060,13 +1061,13 @@
   }
 
   if (!SetupDtlsSrtp_n(false)) {
-    SignalDtlsSrtpSetupFailure_n(false);
+    SignalDtlsSetupFailure_n(false);
     return;
   }
 
   if (rtcp_transport_channel_) {
     if (!SetupDtlsSrtp_n(true)) {
-      SignalDtlsSrtpSetupFailure_n(true);
+      SignalDtlsSetupFailure_n(true);
       return;
     }
   }
@@ -2146,23 +2147,25 @@
   GetSupportedVideoCryptoSuites(crypto_options(), crypto_suites);
 }
 
-RtpDataChannel::RtpDataChannel(rtc::Thread* worker_thread,
-                               rtc::Thread* network_thread,
-                               DataMediaChannel* media_channel,
-                               TransportController* transport_controller,
-                               const std::string& content_name,
-                               bool rtcp,
-                               bool srtp_required)
+DataChannel::DataChannel(rtc::Thread* worker_thread,
+                         rtc::Thread* network_thread,
+                         DataMediaChannel* media_channel,
+                         TransportController* transport_controller,
+                         const std::string& content_name,
+                         bool rtcp,
+                         bool srtp_required)
     : BaseChannel(worker_thread,
                   network_thread,
                   media_channel,
                   transport_controller,
                   content_name,
                   rtcp,
-                  srtp_required) {}
+                  srtp_required),
+      data_channel_type_(cricket::DCT_NONE),
+      ready_to_send_data_(false) {}
 
-RtpDataChannel::~RtpDataChannel() {
-  TRACE_EVENT0("webrtc", "RtpDataChannel::~RtpDataChannel");
+DataChannel::~DataChannel() {
+  TRACE_EVENT0("webrtc", "DataChannel::~DataChannel");
   StopMediaMonitor();
   // this can't be done in the base class, since it calls a virtual
   DisableMedia_w();
@@ -2170,48 +2173,78 @@
   Deinit();
 }
 
-bool RtpDataChannel::Init_w(const std::string* bundle_transport_name) {
+bool DataChannel::Init_w(const std::string* bundle_transport_name) {
   if (!BaseChannel::Init_w(bundle_transport_name)) {
     return false;
   }
-  media_channel()->SignalDataReceived.connect(this,
-                                              &RtpDataChannel::OnDataReceived);
+  media_channel()->SignalDataReceived.connect(
+      this, &DataChannel::OnDataReceived);
   media_channel()->SignalReadyToSend.connect(
-      this, &RtpDataChannel::OnDataChannelReadyToSend);
+      this, &DataChannel::OnDataChannelReadyToSend);
+  media_channel()->SignalStreamClosedRemotely.connect(
+      this, &DataChannel::OnStreamClosedRemotely);
   return true;
 }
 
-bool RtpDataChannel::SendData(const SendDataParams& params,
-                              const rtc::CopyOnWriteBuffer& payload,
-                              SendDataResult* result) {
+bool DataChannel::SendData(const SendDataParams& params,
+                           const rtc::CopyOnWriteBuffer& payload,
+                           SendDataResult* result) {
   return InvokeOnWorker(
       RTC_FROM_HERE, Bind(&DataMediaChannel::SendData, media_channel(), params,
                           payload, result));
 }
 
-const ContentInfo* RtpDataChannel::GetFirstContent(
+const ContentInfo* DataChannel::GetFirstContent(
     const SessionDescription* sdesc) {
   return GetFirstDataContent(sdesc);
 }
 
-bool RtpDataChannel::CheckDataChannelTypeFromContent(
+bool DataChannel::WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet) {
+  if (data_channel_type_ == DCT_SCTP) {
+    // TODO(pthatcher): Do this in a more robust way by checking for
+    // SCTP or DTLS.
+    return !IsRtpPacket(packet->data(), packet->size());
+  } else if (data_channel_type_ == DCT_RTP) {
+    return BaseChannel::WantsPacket(rtcp, packet);
+  }
+  return false;
+}
+
+bool DataChannel::SetDataChannelType(DataChannelType new_data_channel_type,
+                                     std::string* error_desc) {
+  // It hasn't been set before, so set it now.
+  if (data_channel_type_ == DCT_NONE) {
+    data_channel_type_ = new_data_channel_type;
+    return true;
+  }
+
+  // It's been set before, but doesn't match.  That's bad.
+  if (data_channel_type_ != new_data_channel_type) {
+    std::ostringstream desc;
+    desc << "Data channel type mismatch."
+         << " Expected " << data_channel_type_
+         << " Got " << new_data_channel_type;
+    SafeSetError(desc.str(), error_desc);
+    return false;
+  }
+
+  // It's hasn't changed.  Nothing to do.
+  return true;
+}
+
+bool DataChannel::SetDataChannelTypeFromContent(
     const DataContentDescription* content,
     std::string* error_desc) {
   bool is_sctp = ((content->protocol() == kMediaProtocolSctp) ||
                   (content->protocol() == kMediaProtocolDtlsSctp));
-  // It's been set before, but doesn't match.  That's bad.
-  if (is_sctp) {
-    SafeSetError("Data channel type mismatch. Expected RTP, got SCTP.",
-                 error_desc);
-    return false;
-  }
-  return true;
+  DataChannelType data_channel_type = is_sctp ? DCT_SCTP : DCT_RTP;
+  return SetDataChannelType(data_channel_type, error_desc);
 }
 
-bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content,
-                                       ContentAction action,
-                                       std::string* error_desc) {
-  TRACE_EVENT0("webrtc", "RtpDataChannel::SetLocalContent_w");
+bool DataChannel::SetLocalContent_w(const MediaContentDescription* content,
+                                    ContentAction action,
+                                    std::string* error_desc) {
+  TRACE_EVENT0("webrtc", "DataChannel::SetLocalContent_w");
   RTC_DCHECK(worker_thread() == rtc::Thread::Current());
   LOG(LS_INFO) << "Setting local data description";
 
@@ -2223,14 +2256,19 @@
     return false;
   }
 
-  if (!CheckDataChannelTypeFromContent(data, error_desc)) {
+  if (!SetDataChannelTypeFromContent(data, error_desc)) {
     return false;
   }
 
-  if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) {
-    return false;
+  if (data_channel_type_ == DCT_RTP) {
+    if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) {
+      return false;
+    }
   }
 
+  // FYI: We send the SCTP port number (not to be confused with the
+  // underlying UDP port number) as a codec parameter.  So even SCTP
+  // data channels need codecs.
   DataRecvParameters recv_params = last_recv_params_;
   RtpParametersFromMediaDescription(data, &recv_params);
   if (!media_channel()->SetRecvParameters(recv_params)) {
@@ -2238,8 +2276,10 @@
                  error_desc);
     return false;
   }
-  for (const DataCodec& codec : data->codecs()) {
-    bundle_filter()->AddPayloadType(codec.id);
+  if (data_channel_type_ == DCT_RTP) {
+    for (const DataCodec& codec : data->codecs()) {
+      bundle_filter()->AddPayloadType(codec.id);
+    }
   }
   last_recv_params_ = recv_params;
 
@@ -2257,10 +2297,10 @@
   return true;
 }
 
-bool RtpDataChannel::SetRemoteContent_w(const MediaContentDescription* content,
-                                        ContentAction action,
-                                        std::string* error_desc) {
-  TRACE_EVENT0("webrtc", "RtpDataChannel::SetRemoteContent_w");
+bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content,
+                                     ContentAction action,
+                                     std::string* error_desc) {
+  TRACE_EVENT0("webrtc", "DataChannel::SetRemoteContent_w");
   RTC_DCHECK(worker_thread() == rtc::Thread::Current());
 
   const DataContentDescription* data =
@@ -2277,15 +2317,17 @@
     return true;
   }
 
-  if (!CheckDataChannelTypeFromContent(data, error_desc)) {
+  if (!SetDataChannelTypeFromContent(data, error_desc)) {
     return false;
   }
 
   LOG(LS_INFO) << "Setting remote data description";
-  if (!SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) {
+  if (data_channel_type_ == DCT_RTP &&
+      !SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) {
     return false;
   }
 
+
   DataSendParameters send_params = last_send_params_;
   RtpSendParametersFromMediaDescription<DataCodec>(data, &send_params);
   if (!media_channel()->SetSendParameters(send_params)) {
@@ -2310,7 +2352,7 @@
   return true;
 }
 
-void RtpDataChannel::UpdateMediaSendRecvState_w() {
+void DataChannel::UpdateMediaSendRecvState_w() {
   // Render incoming data if we're the active call, and we have the local
   // content. We receive data on the default channel and multiplexed streams.
   bool recv = IsReadyToReceiveMedia_w();
@@ -2331,7 +2373,7 @@
   LOG(LS_INFO) << "Changing data state, recv=" << recv << " send=" << send;
 }
 
-void RtpDataChannel::OnMessage(rtc::Message* pmsg) {
+void DataChannel::OnMessage(rtc::Message *pmsg) {
   switch (pmsg->message_id) {
     case MSG_READYTOSENDDATA: {
       DataChannelReadyToSendMessageData* data =
@@ -2344,7 +2386,7 @@
     case MSG_DATARECEIVED: {
       DataReceivedMessageData* data =
           static_cast<DataReceivedMessageData*>(pmsg->pdata);
-      SignalDataReceived(data->params, data->payload);
+      SignalDataReceived(this, data->params, data->payload);
       delete data;
       break;
     }
@@ -2354,27 +2396,33 @@
       delete data;
       break;
     }
+    case MSG_STREAMCLOSEDREMOTELY: {
+      rtc::TypedMessageData<uint32_t>* data =
+          static_cast<rtc::TypedMessageData<uint32_t>*>(pmsg->pdata);
+      SignalStreamClosedRemotely(data->data());
+      delete data;
+      break;
+    }
     default:
       BaseChannel::OnMessage(pmsg);
       break;
   }
 }
 
-void RtpDataChannel::OnConnectionMonitorUpdate(
-    ConnectionMonitor* monitor,
-    const std::vector<ConnectionInfo>& infos) {
+void DataChannel::OnConnectionMonitorUpdate(
+    ConnectionMonitor* monitor, const std::vector<ConnectionInfo>& infos) {
   SignalConnectionMonitor(this, infos);
 }
 
-void RtpDataChannel::StartMediaMonitor(int cms) {
+void DataChannel::StartMediaMonitor(int cms) {
   media_monitor_.reset(new DataMediaMonitor(media_channel(), worker_thread(),
       rtc::Thread::Current()));
-  media_monitor_->SignalUpdate.connect(this,
-                                       &RtpDataChannel::OnMediaMonitorUpdate);
+  media_monitor_->SignalUpdate.connect(
+      this, &DataChannel::OnMediaMonitorUpdate);
   media_monitor_->Start(cms);
 }
 
-void RtpDataChannel::StopMediaMonitor() {
+void DataChannel::StopMediaMonitor() {
   if (media_monitor_) {
     media_monitor_->Stop();
     media_monitor_->SignalUpdate.disconnect(this);
@@ -2382,28 +2430,27 @@
   }
 }
 
-void RtpDataChannel::OnMediaMonitorUpdate(DataMediaChannel* media_channel,
-                                          const DataMediaInfo& info) {
+void DataChannel::OnMediaMonitorUpdate(
+    DataMediaChannel* media_channel, const DataMediaInfo& info) {
   RTC_DCHECK(media_channel == this->media_channel());
   SignalMediaMonitor(this, info);
 }
 
-void RtpDataChannel::OnDataReceived(const ReceiveDataParams& params,
-                                    const char* data,
-                                    size_t len) {
+void DataChannel::OnDataReceived(
+    const ReceiveDataParams& params, const char* data, size_t len) {
   DataReceivedMessageData* msg = new DataReceivedMessageData(
       params, data, len);
   signaling_thread()->Post(RTC_FROM_HERE, this, MSG_DATARECEIVED, msg);
 }
 
-void RtpDataChannel::OnDataChannelError(uint32_t ssrc,
-                                        DataMediaChannel::Error err) {
+void DataChannel::OnDataChannelError(uint32_t ssrc,
+                                     DataMediaChannel::Error err) {
   DataChannelErrorMessageData* data = new DataChannelErrorMessageData(
       ssrc, err);
   signaling_thread()->Post(RTC_FROM_HERE, this, MSG_CHANNEL_ERROR, data);
 }
 
-void RtpDataChannel::OnDataChannelReadyToSend(bool writable) {
+void DataChannel::OnDataChannelReadyToSend(bool writable) {
   // This is usded for congestion control to indicate that the stream is ready
   // to send by the MediaChannel, as opposed to OnReadyToSend, which indicates
   // that the transport channel is ready.
@@ -2411,9 +2458,19 @@
                            new DataChannelReadyToSendMessageData(writable));
 }
 
-void RtpDataChannel::GetSrtpCryptoSuites_n(
-    std::vector<int>* crypto_suites) const {
+void DataChannel::GetSrtpCryptoSuites_n(std::vector<int>* crypto_suites) const {
   GetSupportedDataCryptoSuites(crypto_options(), crypto_suites);
 }
 
+bool DataChannel::ShouldSetupDtlsSrtp_n() const {
+  return data_channel_type_ == DCT_RTP && BaseChannel::ShouldSetupDtlsSrtp_n();
+}
+
+void DataChannel::OnStreamClosedRemotely(uint32_t sid) {
+  rtc::TypedMessageData<uint32_t>* message =
+      new rtc::TypedMessageData<uint32_t>(sid);
+  signaling_thread()->Post(RTC_FROM_HERE, this, MSG_STREAMCLOSEDREMOTELY,
+                           message);
+}
+
 }  // namespace cricket
diff --git a/webrtc/pc/channel.h b/webrtc/pc/channel.h
index ff98a2f..64c5f82 100644
--- a/webrtc/pc/channel.h
+++ b/webrtc/pc/channel.h
@@ -149,9 +149,9 @@
     return remote_streams_;
   }
 
-  sigslot::signal2<BaseChannel*, bool> SignalDtlsSrtpSetupFailure;
-  void SignalDtlsSrtpSetupFailure_n(bool rtcp);
-  void SignalDtlsSrtpSetupFailure_s(bool rtcp);
+  sigslot::signal2<BaseChannel*, bool> SignalDtlsSetupFailure;
+  void SignalDtlsSetupFailure_n(bool rtcp);
+  void SignalDtlsSetupFailure_s(bool rtcp);
 
   // Used for latency measurements.
   sigslot::signal1<BaseChannel*> SignalFirstPacketReceived;
@@ -261,7 +261,7 @@
                   rtc::CopyOnWriteBuffer* packet,
                   const rtc::PacketOptions& options);
 
-  bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet);
+  virtual bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet);
   void HandlePacket(bool rtcp, rtc::CopyOnWriteBuffer* packet,
                     const rtc::PacketTime& packet_time);
   void OnPacketReceived(bool rtcp,
@@ -282,7 +282,7 @@
   bool RemoveRecvStream_w(uint32_t ssrc);
   bool AddSendStream_w(const StreamParams& sp);
   bool RemoveSendStream_w(uint32_t ssrc);
-  bool ShouldSetupDtlsSrtp_n() const;
+  virtual bool ShouldSetupDtlsSrtp_n() const;
   // Do the DTLS key expansion and impose it on the SRTP/SRTCP filters.
   // |rtcp_channel| indicates whether to set up the RTP or RTCP filter.
   bool SetupDtlsSrtp_n(bool rtcp_channel);
@@ -615,17 +615,17 @@
   VideoRecvParameters last_recv_params_;
 };
 
-// RtpDataChannel is a specialization for data.
-class RtpDataChannel : public BaseChannel {
+// DataChannel is a specialization for data.
+class DataChannel : public BaseChannel {
  public:
-  RtpDataChannel(rtc::Thread* worker_thread,
-                 rtc::Thread* network_thread,
-                 DataMediaChannel* media_channel,
-                 TransportController* transport_controller,
-                 const std::string& content_name,
-                 bool rtcp,
-                 bool srtp_required);
-  ~RtpDataChannel();
+  DataChannel(rtc::Thread* worker_thread,
+              rtc::Thread* network_thread,
+              DataMediaChannel* media_channel,
+              TransportController* transport_controller,
+              const std::string& content_name,
+              bool rtcp,
+              bool srtp_required);
+  ~DataChannel();
   bool Init_w(const std::string* bundle_transport_name);
 
   virtual bool SendData(const SendDataParams& params,
@@ -640,16 +640,17 @@
     return ready_to_send_data_;
   }
 
-  sigslot::signal2<RtpDataChannel*, const DataMediaInfo&> SignalMediaMonitor;
-  sigslot::signal2<RtpDataChannel*, const std::vector<ConnectionInfo>&>
+  sigslot::signal2<DataChannel*, const DataMediaInfo&> SignalMediaMonitor;
+  sigslot::signal2<DataChannel*, const std::vector<ConnectionInfo>&>
       SignalConnectionMonitor;
-
-  sigslot::signal2<const ReceiveDataParams&, const rtc::CopyOnWriteBuffer&>
-      SignalDataReceived;
+  sigslot::signal3<DataChannel*, const ReceiveDataParams&,
+      const rtc::CopyOnWriteBuffer&> SignalDataReceived;
   // Signal for notifying when the channel becomes ready to send data.
   // That occurs when the channel is enabled, the transport is writable,
   // both local and remote descriptions are set, and the channel is unblocked.
   sigslot::signal1<bool> SignalReadyToSendData;
+  // Signal for notifying that the remote side has closed the DataChannel.
+  sigslot::signal1<uint32_t> SignalStreamClosedRemotely;
   cricket::MediaType media_type() override { return cricket::MEDIA_TYPE_DATA; }
 
  protected:
@@ -692,9 +693,15 @@
 
   // overrides from BaseChannel
   const ContentInfo* GetFirstContent(const SessionDescription* sdesc) override;
-  // Checks that data channel type is RTP.
-  bool CheckDataChannelTypeFromContent(const DataContentDescription* content,
-                                       std::string* error_desc);
+  // If data_channel_type_ is DCT_NONE, set it.  Otherwise, check that
+  // it's the same as what was set previously.  Returns false if it's
+  // set to one type one type and changed to another type later.
+  bool SetDataChannelType(DataChannelType new_data_channel_type,
+                          std::string* error_desc);
+  // Same as SetDataChannelType, but extracts the type from the
+  // DataContentDescription.
+  bool SetDataChannelTypeFromContent(const DataContentDescription* content,
+                                     std::string* error_desc);
   bool SetLocalContent_w(const MediaContentDescription* content,
                          ContentAction action,
                          std::string* error_desc) override;
@@ -702,6 +709,7 @@
                           ContentAction action,
                           std::string* error_desc) override;
   void UpdateMediaSendRecvState_w() override;
+  bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet) override;
 
   void OnMessage(rtc::Message* pmsg) override;
   void GetSrtpCryptoSuites_n(std::vector<int>* crypto_suites) const override;
@@ -710,13 +718,18 @@
       const std::vector<ConnectionInfo>& infos) override;
   void OnMediaMonitorUpdate(DataMediaChannel* media_channel,
                             const DataMediaInfo& info);
+  bool ShouldSetupDtlsSrtp_n() const override;
   void OnDataReceived(
       const ReceiveDataParams& params, const char* data, size_t len);
   void OnDataChannelError(uint32_t ssrc, DataMediaChannel::Error error);
   void OnDataChannelReadyToSend(bool writable);
+  void OnStreamClosedRemotely(uint32_t sid);
 
   std::unique_ptr<DataMediaMonitor> media_monitor_;
-  bool ready_to_send_data_ = false;
+  // TODO(pthatcher): Make a separate SctpDataChannel and
+  // RtpDataChannel instead of using this.
+  DataChannelType data_channel_type_;
+  bool ready_to_send_data_;
 
   // Last DataSendParameters sent down to the media_channel() via
   // SetSendParameters.
diff --git a/webrtc/pc/channel_unittest.cc b/webrtc/pc/channel_unittest.cc
index 589ebd5..5de3c94 100644
--- a/webrtc/pc/channel_unittest.cc
+++ b/webrtc/pc/channel_unittest.cc
@@ -84,14 +84,14 @@
                                   cricket::VideoMediaInfo,
                                   cricket::VideoOptions> {};
 
-class DataTraits : public Traits<cricket::RtpDataChannel,
+class DataTraits : public Traits<cricket::DataChannel,
                                  cricket::FakeDataMediaChannel,
                                  cricket::DataContentDescription,
                                  cricket::DataCodec,
                                  cricket::DataMediaInfo,
                                  cricket::DataOptions> {};
 
-// Base class for Voice/Video/RtpDataChannel tests
+// Base class for Voice/Video/DataChannel tests
 template<class T>
 class ChannelTest : public testing::Test, public sigslot::has_slots<> {
  public:
@@ -3288,32 +3288,32 @@
   Base::CanChangeMaxBitrate();
 }
 
-// RtpDataChannelSingleThreadTest
-class RtpDataChannelSingleThreadTest : public ChannelTest<DataTraits> {
+// DataChannelSingleThreadTest
+class DataChannelSingleThreadTest : public ChannelTest<DataTraits> {
  public:
   typedef ChannelTest<DataTraits> Base;
-  RtpDataChannelSingleThreadTest()
+  DataChannelSingleThreadTest()
       : Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::Yes) {}
 };
 
-// RtpDataChannelDoubleThreadTest
-class RtpDataChannelDoubleThreadTest : public ChannelTest<DataTraits> {
+// DataChannelDoubleThreadTest
+class DataChannelDoubleThreadTest : public ChannelTest<DataTraits> {
  public:
   typedef ChannelTest<DataTraits> Base;
-  RtpDataChannelDoubleThreadTest()
+  DataChannelDoubleThreadTest()
       : Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::No) {}
 };
 
 // Override to avoid engine channel parameter.
 template <>
-cricket::RtpDataChannel* ChannelTest<DataTraits>::CreateChannel(
+cricket::DataChannel* ChannelTest<DataTraits>::CreateChannel(
     rtc::Thread* worker_thread,
     rtc::Thread* network_thread,
     cricket::MediaEngineInterface* engine,
     cricket::FakeDataMediaChannel* ch,
     cricket::TransportController* transport_controller,
     int flags) {
-  cricket::RtpDataChannel* channel = new cricket::RtpDataChannel(
+  cricket::DataChannel* channel = new cricket::DataChannel(
       worker_thread, network_thread, ch, transport_controller, cricket::CN_DATA,
       (flags & RTCP) != 0, (flags & SECURE) != 0);
   rtc::CryptoOptions crypto_options;
@@ -3362,136 +3362,136 @@
   data->AddLegacyStream(ssrc);
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestInit) {
+TEST_F(DataChannelSingleThreadTest, TestInit) {
   Base::TestInit();
   EXPECT_FALSE(media_channel1_->IsStreamMuted(0));
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestDeinit) {
+TEST_F(DataChannelSingleThreadTest, TestDeinit) {
   Base::TestDeinit();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestSetContents) {
+TEST_F(DataChannelSingleThreadTest, TestSetContents) {
   Base::TestSetContents();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestSetContentsNullOffer) {
+TEST_F(DataChannelSingleThreadTest, TestSetContentsNullOffer) {
   Base::TestSetContentsNullOffer();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestSetContentsRtcpMux) {
+TEST_F(DataChannelSingleThreadTest, TestSetContentsRtcpMux) {
   Base::TestSetContentsRtcpMux();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestSetRemoteContentUpdate) {
+TEST_F(DataChannelSingleThreadTest, TestSetRemoteContentUpdate) {
   Base::TestSetRemoteContentUpdate();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestStreams) {
+TEST_F(DataChannelSingleThreadTest, TestStreams) {
   Base::TestStreams();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestUpdateStreamsInLocalContent) {
+TEST_F(DataChannelSingleThreadTest, TestUpdateStreamsInLocalContent) {
   Base::TestUpdateStreamsInLocalContent();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestUpdateRemoteStreamsInContent) {
+TEST_F(DataChannelSingleThreadTest, TestUpdateRemoteStreamsInContent) {
   Base::TestUpdateStreamsInRemoteContent();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestChangeStreamParamsInContent) {
+TEST_F(DataChannelSingleThreadTest, TestChangeStreamParamsInContent) {
   Base::TestChangeStreamParamsInContent();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestPlayoutAndSendingStates) {
+TEST_F(DataChannelSingleThreadTest, TestPlayoutAndSendingStates) {
   Base::TestPlayoutAndSendingStates();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestMediaContentDirection) {
+TEST_F(DataChannelSingleThreadTest, TestMediaContentDirection) {
   Base::TestMediaContentDirection();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestCallSetup) {
+TEST_F(DataChannelSingleThreadTest, TestCallSetup) {
   Base::TestCallSetup();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestCallTeardownRtcpMux) {
+TEST_F(DataChannelSingleThreadTest, TestCallTeardownRtcpMux) {
   Base::TestCallTeardownRtcpMux();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestOnReadyToSend) {
+TEST_F(DataChannelSingleThreadTest, TestOnReadyToSend) {
   Base::TestOnReadyToSend();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestOnReadyToSendWithRtcpMux) {
+TEST_F(DataChannelSingleThreadTest, TestOnReadyToSendWithRtcpMux) {
   Base::TestOnReadyToSendWithRtcpMux();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendRtpToRtp) {
+TEST_F(DataChannelSingleThreadTest, SendRtpToRtp) {
   Base::SendRtpToRtp();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendNoRtcpToNoRtcp) {
+TEST_F(DataChannelSingleThreadTest, SendNoRtcpToNoRtcp) {
   Base::SendNoRtcpToNoRtcp();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendNoRtcpToRtcp) {
+TEST_F(DataChannelSingleThreadTest, SendNoRtcpToRtcp) {
   Base::SendNoRtcpToRtcp();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendRtcpToNoRtcp) {
+TEST_F(DataChannelSingleThreadTest, SendRtcpToNoRtcp) {
   Base::SendRtcpToNoRtcp();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendRtcpToRtcp) {
+TEST_F(DataChannelSingleThreadTest, SendRtcpToRtcp) {
   Base::SendRtcpToRtcp();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendRtcpMuxToRtcp) {
+TEST_F(DataChannelSingleThreadTest, SendRtcpMuxToRtcp) {
   Base::SendRtcpMuxToRtcp();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendRtcpMuxToRtcpMux) {
+TEST_F(DataChannelSingleThreadTest, SendRtcpMuxToRtcpMux) {
   Base::SendRtcpMuxToRtcpMux();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcp) {
+TEST_F(DataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcp) {
   Base::SendEarlyRtcpMuxToRtcp();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcpMux) {
+TEST_F(DataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcpMux) {
   Base::SendEarlyRtcpMuxToRtcpMux();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToSrtp) {
+TEST_F(DataChannelSingleThreadTest, SendSrtpToSrtp) {
   Base::SendSrtpToSrtp();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToRtp) {
+TEST_F(DataChannelSingleThreadTest, SendSrtpToRtp) {
   Base::SendSrtpToSrtp();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendSrtcpMux) {
+TEST_F(DataChannelSingleThreadTest, SendSrtcpMux) {
   Base::SendSrtpToSrtp(RTCP_MUX, RTCP_MUX);
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendRtpToRtpOnThread) {
+TEST_F(DataChannelSingleThreadTest, SendRtpToRtpOnThread) {
   Base::SendRtpToRtpOnThread();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToSrtpOnThread) {
+TEST_F(DataChannelSingleThreadTest, SendSrtpToSrtpOnThread) {
   Base::SendSrtpToSrtpOnThread();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, SendWithWritabilityLoss) {
+TEST_F(DataChannelSingleThreadTest, SendWithWritabilityLoss) {
   Base::SendWithWritabilityLoss();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestMediaMonitor) {
+TEST_F(DataChannelSingleThreadTest, TestMediaMonitor) {
   Base::TestMediaMonitor();
 }
 
-TEST_F(RtpDataChannelSingleThreadTest, TestSendData) {
+TEST_F(DataChannelSingleThreadTest, TestSendData) {
   CreateChannels(0, 0);
   EXPECT_TRUE(SendInitiate());
   EXPECT_TRUE(SendAccept());
@@ -3506,136 +3506,136 @@
   EXPECT_EQ("foo", media_channel1_->last_sent_data());
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestInit) {
+TEST_F(DataChannelDoubleThreadTest, TestInit) {
   Base::TestInit();
   EXPECT_FALSE(media_channel1_->IsStreamMuted(0));
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestDeinit) {
+TEST_F(DataChannelDoubleThreadTest, TestDeinit) {
   Base::TestDeinit();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestSetContents) {
+TEST_F(DataChannelDoubleThreadTest, TestSetContents) {
   Base::TestSetContents();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestSetContentsNullOffer) {
+TEST_F(DataChannelDoubleThreadTest, TestSetContentsNullOffer) {
   Base::TestSetContentsNullOffer();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestSetContentsRtcpMux) {
+TEST_F(DataChannelDoubleThreadTest, TestSetContentsRtcpMux) {
   Base::TestSetContentsRtcpMux();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestSetRemoteContentUpdate) {
+TEST_F(DataChannelDoubleThreadTest, TestSetRemoteContentUpdate) {
   Base::TestSetRemoteContentUpdate();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestStreams) {
+TEST_F(DataChannelDoubleThreadTest, TestStreams) {
   Base::TestStreams();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestUpdateStreamsInLocalContent) {
+TEST_F(DataChannelDoubleThreadTest, TestUpdateStreamsInLocalContent) {
   Base::TestUpdateStreamsInLocalContent();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestUpdateRemoteStreamsInContent) {
+TEST_F(DataChannelDoubleThreadTest, TestUpdateRemoteStreamsInContent) {
   Base::TestUpdateStreamsInRemoteContent();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestChangeStreamParamsInContent) {
+TEST_F(DataChannelDoubleThreadTest, TestChangeStreamParamsInContent) {
   Base::TestChangeStreamParamsInContent();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestPlayoutAndSendingStates) {
+TEST_F(DataChannelDoubleThreadTest, TestPlayoutAndSendingStates) {
   Base::TestPlayoutAndSendingStates();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestMediaContentDirection) {
+TEST_F(DataChannelDoubleThreadTest, TestMediaContentDirection) {
   Base::TestMediaContentDirection();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestCallSetup) {
+TEST_F(DataChannelDoubleThreadTest, TestCallSetup) {
   Base::TestCallSetup();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestCallTeardownRtcpMux) {
+TEST_F(DataChannelDoubleThreadTest, TestCallTeardownRtcpMux) {
   Base::TestCallTeardownRtcpMux();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestOnReadyToSend) {
+TEST_F(DataChannelDoubleThreadTest, TestOnReadyToSend) {
   Base::TestOnReadyToSend();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestOnReadyToSendWithRtcpMux) {
+TEST_F(DataChannelDoubleThreadTest, TestOnReadyToSendWithRtcpMux) {
   Base::TestOnReadyToSendWithRtcpMux();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendRtpToRtp) {
+TEST_F(DataChannelDoubleThreadTest, SendRtpToRtp) {
   Base::SendRtpToRtp();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendNoRtcpToNoRtcp) {
+TEST_F(DataChannelDoubleThreadTest, SendNoRtcpToNoRtcp) {
   Base::SendNoRtcpToNoRtcp();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendNoRtcpToRtcp) {
+TEST_F(DataChannelDoubleThreadTest, SendNoRtcpToRtcp) {
   Base::SendNoRtcpToRtcp();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpToNoRtcp) {
+TEST_F(DataChannelDoubleThreadTest, SendRtcpToNoRtcp) {
   Base::SendRtcpToNoRtcp();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpToRtcp) {
+TEST_F(DataChannelDoubleThreadTest, SendRtcpToRtcp) {
   Base::SendRtcpToRtcp();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpMuxToRtcp) {
+TEST_F(DataChannelDoubleThreadTest, SendRtcpMuxToRtcp) {
   Base::SendRtcpMuxToRtcp();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpMuxToRtcpMux) {
+TEST_F(DataChannelDoubleThreadTest, SendRtcpMuxToRtcpMux) {
   Base::SendRtcpMuxToRtcpMux();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcp) {
+TEST_F(DataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcp) {
   Base::SendEarlyRtcpMuxToRtcp();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcpMux) {
+TEST_F(DataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcpMux) {
   Base::SendEarlyRtcpMuxToRtcpMux();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToSrtp) {
+TEST_F(DataChannelDoubleThreadTest, SendSrtpToSrtp) {
   Base::SendSrtpToSrtp();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToRtp) {
+TEST_F(DataChannelDoubleThreadTest, SendSrtpToRtp) {
   Base::SendSrtpToSrtp();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendSrtcpMux) {
+TEST_F(DataChannelDoubleThreadTest, SendSrtcpMux) {
   Base::SendSrtpToSrtp(RTCP_MUX, RTCP_MUX);
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendRtpToRtpOnThread) {
+TEST_F(DataChannelDoubleThreadTest, SendRtpToRtpOnThread) {
   Base::SendRtpToRtpOnThread();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToSrtpOnThread) {
+TEST_F(DataChannelDoubleThreadTest, SendSrtpToSrtpOnThread) {
   Base::SendSrtpToSrtpOnThread();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, SendWithWritabilityLoss) {
+TEST_F(DataChannelDoubleThreadTest, SendWithWritabilityLoss) {
   Base::SendWithWritabilityLoss();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestMediaMonitor) {
+TEST_F(DataChannelDoubleThreadTest, TestMediaMonitor) {
   Base::TestMediaMonitor();
 }
 
-TEST_F(RtpDataChannelDoubleThreadTest, TestSendData) {
+TEST_F(DataChannelDoubleThreadTest, TestSendData) {
   CreateChannels(0, 0);
   EXPECT_TRUE(SendInitiate());
   EXPECT_TRUE(SendAccept());
diff --git a/webrtc/pc/channelmanager.cc b/webrtc/pc/channelmanager.cc
index cdfed68..a0b3609 100644
--- a/webrtc/pc/channelmanager.cc
+++ b/webrtc/pc/channelmanager.cc
@@ -20,7 +20,11 @@
 #include "webrtc/base/stringutils.h"
 #include "webrtc/base/trace_event.h"
 #include "webrtc/media/base/device.h"
+#include "webrtc/media/base/hybriddataengine.h"
 #include "webrtc/media/base/rtpdataengine.h"
+#ifdef HAVE_SCTP
+#include "webrtc/media/sctp/sctpdataengine.h"
+#endif
 #include "webrtc/pc/srtpfilter.h"
 
 namespace cricket {
@@ -29,7 +33,11 @@
 using rtc::Bind;
 
 static DataEngineInterface* ConstructDataEngine() {
+#ifdef HAVE_SCTP
+  return new HybridDataEngine(new RtpDataEngine(), new SctpDataEngine());
+#else
   return new RtpDataEngine();
+#endif
 }
 
 ChannelManager::ChannelManager(MediaEngineInterface* me,
@@ -336,66 +344,73 @@
   delete video_channel;
 }
 
-RtpDataChannel* ChannelManager::CreateRtpDataChannel(
+DataChannel* ChannelManager::CreateDataChannel(
     webrtc::MediaControllerInterface* media_controller,
     TransportController* transport_controller,
     const std::string& content_name,
     const std::string* bundle_transport_name,
     bool rtcp,
-    bool srtp_required) {
-  return worker_thread_->Invoke<RtpDataChannel*>(
-      RTC_FROM_HERE, Bind(&ChannelManager::CreateRtpDataChannel_w, this,
-                          media_controller, transport_controller, content_name,
-                          bundle_transport_name, rtcp, srtp_required));
+    bool srtp_required,
+    DataChannelType channel_type) {
+  return worker_thread_->Invoke<DataChannel*>(
+      RTC_FROM_HERE,
+      Bind(&ChannelManager::CreateDataChannel_w, this, media_controller,
+           transport_controller, content_name, bundle_transport_name, rtcp,
+           srtp_required, channel_type));
 }
 
-RtpDataChannel* ChannelManager::CreateRtpDataChannel_w(
+DataChannel* ChannelManager::CreateDataChannel_w(
     webrtc::MediaControllerInterface* media_controller,
     TransportController* transport_controller,
     const std::string& content_name,
     const std::string* bundle_transport_name,
     bool rtcp,
-    bool srtp_required) {
+    bool srtp_required,
+    DataChannelType data_channel_type) {
   // This is ok to alloc from a thread other than the worker thread.
   ASSERT(initialized_);
   MediaConfig config;
   if (media_controller) {
     config = media_controller->config();
   }
-  DataMediaChannel* media_channel = data_media_engine_->CreateChannel(config);
+  DataMediaChannel* media_channel =
+      data_media_engine_->CreateChannel(data_channel_type, config);
   if (!media_channel) {
-    LOG(LS_WARNING) << "Failed to create RTP data channel.";
-    return nullptr;
+    LOG(LS_WARNING) << "Failed to create data channel of type "
+                    << data_channel_type;
+    return NULL;
   }
 
-  RtpDataChannel* data_channel = new RtpDataChannel(
-      worker_thread_, network_thread_, media_channel, transport_controller,
-      content_name, rtcp, srtp_required);
+  // Only RTP data channels need SRTP.
+  srtp_required = srtp_required && data_channel_type == DCT_RTP;
+  DataChannel* data_channel =
+      new DataChannel(worker_thread_, network_thread_, media_channel,
+                      transport_controller, content_name, rtcp, srtp_required);
   data_channel->SetCryptoOptions(crypto_options_);
   if (!data_channel->Init_w(bundle_transport_name)) {
     LOG(LS_WARNING) << "Failed to init data channel.";
     delete data_channel;
-    return nullptr;
+    return NULL;
   }
   data_channels_.push_back(data_channel);
   return data_channel;
 }
 
-void ChannelManager::DestroyRtpDataChannel(RtpDataChannel* data_channel) {
-  TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel");
+void ChannelManager::DestroyDataChannel(DataChannel* data_channel) {
+  TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel");
   if (data_channel) {
     worker_thread_->Invoke<void>(
         RTC_FROM_HERE,
-        Bind(&ChannelManager::DestroyRtpDataChannel_w, this, data_channel));
+        Bind(&ChannelManager::DestroyDataChannel_w, this, data_channel));
   }
 }
 
-void ChannelManager::DestroyRtpDataChannel_w(RtpDataChannel* data_channel) {
-  TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel_w");
+void ChannelManager::DestroyDataChannel_w(DataChannel* data_channel) {
+  TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel_w");
   // Destroy data channel.
   ASSERT(initialized_);
-  RtpDataChannels::iterator it =
-      std::find(data_channels_.begin(), data_channels_.end(), data_channel);
+  DataChannels::iterator it = std::find(data_channels_.begin(),
+      data_channels_.end(), data_channel);
   ASSERT(it != data_channels_.end());
   if (it == data_channels_.end())
     return;
diff --git a/webrtc/pc/channelmanager.h b/webrtc/pc/channelmanager.h
index 3022eca..7df49bb 100644
--- a/webrtc/pc/channelmanager.h
+++ b/webrtc/pc/channelmanager.h
@@ -110,15 +110,16 @@
       const VideoOptions& options);
   // Destroys a video channel created with the Create API.
   void DestroyVideoChannel(VideoChannel* video_channel);
-  RtpDataChannel* CreateRtpDataChannel(
+  DataChannel* CreateDataChannel(
       webrtc::MediaControllerInterface* media_controller,
       TransportController* transport_controller,
       const std::string& content_name,
       const std::string* bundle_transport_name,
       bool rtcp,
-      bool srtp_required);
+      bool srtp_required,
+      DataChannelType data_channel_type);
   // Destroys a data channel created with the Create API.
-  void DestroyRtpDataChannel(RtpDataChannel* data_channel);
+  void DestroyDataChannel(DataChannel* data_channel);
 
   // Indicates whether any channels exist.
   bool has_channels() const {
@@ -149,7 +150,7 @@
  private:
   typedef std::vector<VoiceChannel*> VoiceChannels;
   typedef std::vector<VideoChannel*> VideoChannels;
-  typedef std::vector<RtpDataChannel*> RtpDataChannels;
+  typedef std::vector<DataChannel*> DataChannels;
 
   void Construct(MediaEngineInterface* me,
                  DataEngineInterface* dme,
@@ -177,14 +178,15 @@
       bool srtp_required,
       const VideoOptions& options);
   void DestroyVideoChannel_w(VideoChannel* video_channel);
-  RtpDataChannel* CreateRtpDataChannel_w(
+  DataChannel* CreateDataChannel_w(
       webrtc::MediaControllerInterface* media_controller,
       TransportController* transport_controller,
       const std::string& content_name,
       const std::string* bundle_transport_name,
       bool rtcp,
-      bool srtp_required);
-  void DestroyRtpDataChannel_w(RtpDataChannel* data_channel);
+      bool srtp_required,
+      DataChannelType data_channel_type);
+  void DestroyDataChannel_w(DataChannel* data_channel);
 
   std::unique_ptr<MediaEngineInterface> media_engine_;
   std::unique_ptr<DataEngineInterface> data_media_engine_;
@@ -195,7 +197,7 @@
 
   VoiceChannels voice_channels_;
   VideoChannels video_channels_;
-  RtpDataChannels data_channels_;
+  DataChannels data_channels_;
 
   bool enable_rtx_;
   rtc::CryptoOptions crypto_options_;
diff --git a/webrtc/pc/channelmanager_unittest.cc b/webrtc/pc/channelmanager_unittest.cc
index 30d2fe5..df3e9e5 100644
--- a/webrtc/pc/channelmanager_unittest.cc
+++ b/webrtc/pc/channelmanager_unittest.cc
@@ -110,13 +110,13 @@
       &fake_mc_, transport_controller_, cricket::CN_VIDEO, nullptr,
       kDefaultRtcpEnabled, kDefaultSrtpRequired, VideoOptions());
   EXPECT_TRUE(video_channel != nullptr);
-  cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
+  cricket::DataChannel* data_channel = cm_->CreateDataChannel(
       &fake_mc_, transport_controller_, cricket::CN_DATA, nullptr,
-      kDefaultRtcpEnabled, kDefaultSrtpRequired);
-  EXPECT_TRUE(rtp_data_channel != nullptr);
+      kDefaultRtcpEnabled, kDefaultSrtpRequired, cricket::DCT_RTP);
+  EXPECT_TRUE(data_channel != nullptr);
   cm_->DestroyVideoChannel(video_channel);
   cm_->DestroyVoiceChannel(voice_channel);
-  cm_->DestroyRtpDataChannel(rtp_data_channel);
+  cm_->DestroyDataChannel(data_channel);
   cm_->Terminate();
 }
 
@@ -138,13 +138,13 @@
       &fake_mc_, transport_controller_, cricket::CN_VIDEO, nullptr,
       kDefaultRtcpEnabled, kDefaultSrtpRequired, VideoOptions());
   EXPECT_TRUE(video_channel != nullptr);
-  cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
+  cricket::DataChannel* data_channel = cm_->CreateDataChannel(
       &fake_mc_, transport_controller_, cricket::CN_DATA, nullptr,
-      kDefaultRtcpEnabled, kDefaultSrtpRequired);
-  EXPECT_TRUE(rtp_data_channel != nullptr);
+      kDefaultRtcpEnabled, kDefaultSrtpRequired, cricket::DCT_RTP);
+  EXPECT_TRUE(data_channel != nullptr);
   cm_->DestroyVideoChannel(video_channel);
   cm_->DestroyVoiceChannel(voice_channel);
-  cm_->DestroyRtpDataChannel(rtp_data_channel);
+  cm_->DestroyDataChannel(data_channel);
   cm_->Terminate();
 }