Use the DtlsSrtpTransport in BaseChannel.

The DtlsSrtpTransport takes the reponsiblity of setting up DTLS-SRTP from
the BaseChannel.

The BaseChannel doesn't handle the signals from the P2P layer transport anymore.
The RtpTransport handles the signals from the PacketTransportInternal and the
DtlsSrtpTransport handles the DTLS-specific signals and determines when to extract
the keys and setting the parameters.

In channel_unittests.cc, call from DTLS to SDES is expected to fail since the
fallback from DTLS to SDES is not supported.

Bug: webrtc:7013
Change-Id: I0a54e017986f5a8ae9710e79643a4651bef3c38f
Reviewed-on: https://webrtc-review.googlesource.com/24702
Commit-Queue: Zhi Huang <zhihuang@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20941}
diff --git a/p2p/base/fakedtlstransport.h b/p2p/base/fakedtlstransport.h
index fef00fd..54b7018 100644
--- a/p2p/base/fakedtlstransport.h
+++ b/p2p/base/fakedtlstransport.h
@@ -91,6 +91,10 @@
       dest_ = dest;
       if (local_cert_ && dest_->local_cert_) {
         do_dtls_ = true;
+        RTC_LOG(LS_INFO) << "FakeDtlsTransport is doing DTLS";
+      } else {
+        do_dtls_ = false;
+        RTC_LOG(LS_INFO) << "FakeDtlsTransport is not doing DTLS";
       }
       SetWritable(true);
       if (!asymmetric) {
@@ -137,6 +141,7 @@
   }
   bool SetLocalCertificate(
       const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
+    do_dtls_ = true;
     local_cert_ = certificate;
     return true;
   }
diff --git a/pc/channel.cc b/pc/channel.cc
index b976cc0..ea4684f 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -32,8 +32,6 @@
 #include "p2p/base/packettransportinternal.h"
 #include "pc/channelmanager.h"
 #include "pc/rtpmediautils.h"
-#include "pc/rtptransport.h"
-#include "pc/srtptransport.h"
 
 namespace cricket {
 using rtc::Bind;
@@ -64,9 +62,6 @@
   MSG_FIRSTPACKETRECEIVED,
 };
 
-// Value specified in RFC 5764.
-static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp";
-
 static const int kAgcMinus10db = -10;
 
 static void SafeSetError(const std::string& message, std::string* error_desc) {
@@ -144,30 +139,13 @@
       signaling_thread_(signaling_thread),
       content_name_(content_name),
       rtcp_mux_required_(rtcp_mux_required),
+      unencrypted_rtp_transport_(
+          rtc::MakeUnique<webrtc::RtpTransport>(rtcp_mux_required)),
       srtp_required_(srtp_required),
       media_channel_(std::move(media_channel)) {
   RTC_DCHECK_RUN_ON(worker_thread_);
-  if (srtp_required) {
-    auto transport =
-        rtc::MakeUnique<webrtc::SrtpTransport>(rtcp_mux_required, content_name);
-    srtp_transport_ = transport.get();
-    rtp_transport_ = std::move(transport);
-#if defined(ENABLE_EXTERNAL_AUTH)
-    srtp_transport_->EnableExternalAuth();
-#endif
-  } else {
-    rtp_transport_ = rtc::MakeUnique<webrtc::RtpTransport>(rtcp_mux_required);
-    srtp_transport_ = nullptr;
-  }
-  rtp_transport_->SignalReadyToSend.connect(
-      this, &BaseChannel::OnTransportReadyToSend);
-  // TODO(zstein):  RtpTransport::SignalPacketReceived will probably be replaced
-  // with a callback interface later so that the demuxer can select which
-  // channel to signal.
-  rtp_transport_->SignalPacketReceived.connect(this,
-                                               &BaseChannel::OnPacketReceived);
-  rtp_transport_->SignalNetworkRouteChanged.connect(
-      this, &BaseChannel::OnNetworkRouteChanged);
+  rtp_transport_ = unencrypted_rtp_transport_.get();
+  ConnectToRtpTransport();
   RTC_LOG(LS_INFO) << "Created channel for " << content_name;
 }
 
@@ -186,29 +164,30 @@
   RTC_LOG(LS_INFO) << "Destroyed channel: " << content_name_;
 }
 
-void BaseChannel::DisconnectTransportChannels_n() {
-  // Send any outstanding RTCP packets.
-  FlushRtcpMessages_n();
+void BaseChannel::ConnectToRtpTransport() {
+  RTC_DCHECK(rtp_transport_);
+  rtp_transport_->SignalReadyToSend.connect(
+      this, &BaseChannel::OnTransportReadyToSend);
+  // TODO(zstein):  RtpTransport::SignalPacketReceived will probably be replaced
+  // with a callback interface later so that the demuxer can select which
+  // channel to signal.
+  rtp_transport_->SignalPacketReceived.connect(this,
+                                               &BaseChannel::OnPacketReceived);
+  rtp_transport_->SignalNetworkRouteChanged.connect(
+      this, &BaseChannel::OnNetworkRouteChanged);
+  rtp_transport_->SignalWritableState.connect(this,
+                                              &BaseChannel::OnWritableState);
+  rtp_transport_->SignalSentPacket.connect(this,
+                                           &BaseChannel::SignalSentPacket_n);
+}
 
-  // Stop signals from transport channels, but keep them alive because
-  // media_channel may use them from a different thread.
-  if (rtp_dtls_transport_) {
-    DisconnectFromDtlsTransport(rtp_dtls_transport_);
-  } else if (rtp_transport_->rtp_packet_transport()) {
-    DisconnectFromPacketTransport(rtp_transport_->rtp_packet_transport());
-  }
-  if (rtcp_dtls_transport_) {
-    DisconnectFromDtlsTransport(rtcp_dtls_transport_);
-  } else if (rtp_transport_->rtcp_packet_transport()) {
-    DisconnectFromPacketTransport(rtp_transport_->rtcp_packet_transport());
-  }
-
-  rtp_transport_->SetRtpPacketTransport(nullptr);
-  rtp_transport_->SetRtcpPacketTransport(nullptr);
-
-  // Clear pending read packets/messages.
-  network_thread_->Clear(&invoker_);
-  network_thread_->Clear(this);
+void BaseChannel::DisconnectFromRtpTransport() {
+  RTC_DCHECK(rtp_transport_);
+  rtp_transport_->SignalReadyToSend.disconnect(this);
+  rtp_transport_->SignalPacketReceived.disconnect(this);
+  rtp_transport_->SignalNetworkRouteChanged.disconnect(this);
+  rtp_transport_->SignalWritableState.disconnect(this);
+  rtp_transport_->SignalSentPacket.disconnect(this);
 }
 
 void BaseChannel::Init_w(DtlsTransportInternal* rtp_dtls_transport,
@@ -246,8 +225,19 @@
   // Packets arrive on the network thread, processing packets calls virtual
   // functions, so need to stop this process in Deinit that is called in
   // derived classes destructor.
-  network_thread_->Invoke<void>(
-      RTC_FROM_HERE, Bind(&BaseChannel::DisconnectTransportChannels_n, this));
+  network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
+    FlushRtcpMessages_n();
+
+    if (dtls_srtp_transport_) {
+      dtls_srtp_transport_->SetDtlsTransports(nullptr, nullptr);
+    } else {
+      rtp_transport_->SetRtpPacketTransport(nullptr);
+      rtp_transport_->SetRtcpPacketTransport(nullptr);
+    }
+    // Clear pending read packets/messages.
+    network_thread_->Clear(&invoker_);
+    network_thread_->Clear(this);
+  });
 }
 
 void BaseChannel::SetTransports(DtlsTransportInternal* rtp_dtls_transport,
@@ -290,6 +280,12 @@
     RTC_DCHECK(rtp_dtls_transport->transport_name() ==
                rtcp_dtls_transport->transport_name());
   }
+
+  if (rtp_packet_transport == rtp_transport_->rtp_packet_transport()) {
+    // Nothing to do if transport isn't changing.
+    return;
+  }
+
   std::string debug_name;
   if (rtp_dtls_transport) {
     transport_name_ = rtp_dtls_transport->transport_name();
@@ -297,36 +293,31 @@
   } else {
     debug_name = rtp_packet_transport->transport_name();
   }
-  if (rtp_packet_transport == rtp_transport_->rtp_packet_transport()) {
-    // Nothing to do if transport isn't changing.
-    return;
-  }
-
-  // When using DTLS-SRTP, we must reset the SrtpTransport every time the
-  // DtlsTransport changes and wait until the DTLS handshake is complete to set
-  // the newly negotiated parameters.
-  if (ShouldSetupDtlsSrtp_n()) {
-    // Set |writable_| to false such that UpdateWritableState_w can set up
-    // DTLS-SRTP when |writable_| becomes true again.
-    writable_ = false;
-    dtls_active_ = false;
-    if (srtp_transport_) {
-      srtp_transport_->ResetParams();
-    }
-  }
-
   // If this BaseChannel doesn't require RTCP mux and we haven't fully
   // negotiated RTCP mux, we need an RTCP transport.
   if (rtcp_packet_transport) {
     RTC_LOG(LS_INFO) << "Setting RTCP Transport for " << content_name()
                      << " on " << debug_name << " transport "
                      << rtcp_packet_transport;
-    SetTransport_n(true, rtcp_dtls_transport, rtcp_packet_transport);
+    SetTransport_n(/*rtcp=*/true, rtcp_dtls_transport, rtcp_packet_transport);
   }
 
   RTC_LOG(LS_INFO) << "Setting RTP Transport for " << content_name() << " on "
                    << debug_name << " transport " << rtp_packet_transport;
-  SetTransport_n(false, rtp_dtls_transport, rtp_packet_transport);
+  SetTransport_n(/*rtcp=*/false, rtp_dtls_transport, rtp_packet_transport);
+
+  // Set DtlsTransport/PacketTransport for RTP-level transport.
+  if ((rtp_dtls_transport_ || rtcp_dtls_transport_) && dtls_srtp_transport_) {
+    // When setting the transport with non-null |dtls_srtp_transport_|, we are
+    // using DTLS-SRTP. This could happen for bundling. If the
+    // |dtls_srtp_transport| is null, we cannot tell if it doing DTLS-SRTP or
+    // SDES until the description is set. So don't call |EnableDtlsSrtp_n| here.
+    dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport,
+                                            rtcp_dtls_transport);
+  } else {
+    rtp_transport_->SetRtpPacketTransport(rtp_packet_transport);
+    rtp_transport_->SetRtcpPacketTransport(rtcp_packet_transport);
+  }
 
   // Update aggregate writable/ready-to-send state between RTP and RTCP upon
   // setting new transport channels.
@@ -353,20 +344,10 @@
   }
 
   RTC_DCHECK(old_packet_transport != new_packet_transport);
-  if (old_dtls_transport) {
-    DisconnectFromDtlsTransport(old_dtls_transport);
-  } else if (old_packet_transport) {
-    DisconnectFromPacketTransport(old_packet_transport);
-  }
 
-  if (rtcp) {
-    rtp_transport_->SetRtcpPacketTransport(new_packet_transport);
-  } else {
-    rtp_transport_->SetRtpPacketTransport(new_packet_transport);
-  }
   old_dtls_transport = new_dtls_transport;
 
-  // If there's no new transport, we're done after disconnecting from old one.
+  // If there's no new transport, we're done.
   if (!new_packet_transport) {
     return;
   }
@@ -377,48 +358,12 @@
         << "should never happen.";
   }
 
-  if (new_dtls_transport) {
-    ConnectToDtlsTransport(new_dtls_transport);
-  } else {
-    ConnectToPacketTransport(new_packet_transport);
-  }
   auto& socket_options = rtcp ? rtcp_socket_options_ : socket_options_;
   for (const auto& pair : socket_options) {
     new_packet_transport->SetOption(pair.first, pair.second);
   }
 }
 
-void BaseChannel::ConnectToDtlsTransport(DtlsTransportInternal* transport) {
-  RTC_DCHECK(network_thread_->IsCurrent());
-
-  // TODO(zstein): de-dup with ConnectToPacketTransport
-  transport->SignalWritableState.connect(this, &BaseChannel::OnWritableState);
-  transport->SignalDtlsState.connect(this, &BaseChannel::OnDtlsState);
-  transport->SignalSentPacket.connect(this, &BaseChannel::SignalSentPacket_n);
-}
-
-void BaseChannel::DisconnectFromDtlsTransport(
-    DtlsTransportInternal* transport) {
-  RTC_DCHECK(network_thread_->IsCurrent());
-  transport->SignalWritableState.disconnect(this);
-  transport->SignalDtlsState.disconnect(this);
-  transport->SignalSentPacket.disconnect(this);
-}
-
-void BaseChannel::ConnectToPacketTransport(
-    rtc::PacketTransportInternal* transport) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  transport->SignalWritableState.connect(this, &BaseChannel::OnWritableState);
-  transport->SignalSentPacket.connect(this, &BaseChannel::SignalSentPacket_n);
-}
-
-void BaseChannel::DisconnectFromPacketTransport(
-    rtc::PacketTransportInternal* transport) {
-  RTC_DCHECK_RUN_ON(network_thread_);
-  transport->SignalWritableState.disconnect(this);
-  transport->SignalSentPacket.disconnect(this);
-}
-
 bool BaseChannel::Enable(bool enable) {
   worker_thread_->Invoke<void>(
       RTC_FROM_HERE,
@@ -558,29 +503,17 @@
   return transport ? transport->SetOption(opt, value) : -1;
 }
 
-void BaseChannel::OnWritableState(rtc::PacketTransportInternal* transport) {
-  RTC_DCHECK(transport == rtp_transport_->rtp_packet_transport() ||
-             transport == rtp_transport_->rtcp_packet_transport());
+void BaseChannel::OnWritableState(bool writable) {
   RTC_DCHECK(network_thread_->IsCurrent());
-  UpdateWritableState_n();
-}
-
-void BaseChannel::OnDtlsState(DtlsTransportInternal* transport,
-                              DtlsTransportState state) {
-  if (!ShouldSetupDtlsSrtp_n()) {
-    return;
-  }
-
-  // Reset the SrtpTransport if it's not the CONNECTED state. For the CONNECTED
-  // state, setting up DTLS-SRTP context is deferred to ChannelWritable_w to
-  // cover other scenarios like the whole transport is writable (not just this
-  // TransportChannel) or when TransportChannel is attached after DTLS is
-  // negotiated.
-  if (state != DTLS_TRANSPORT_CONNECTED) {
-    dtls_active_ = false;
-    if (srtp_transport_) {
-      srtp_transport_->ResetParams();
+  if (writable) {
+    // This is used to cover the scenario when the DTLS handshake is completed
+    // and DtlsTransport becomes writable before the remote description is set.
+    if (ShouldSetupDtlsSrtp_n()) {
+      EnableDtlsSrtp_n();
     }
+    ChannelWritable_n();
+  } else {
+    ChannelNotWritable_n();
   }
 }
 
@@ -658,16 +591,18 @@
       RTC_NOTREACHED();
       return false;
     }
-    // Bon voyage.
-    return rtcp
-               ? rtp_transport_->SendRtcpPacket(packet, options, PF_SRTP_BYPASS)
-               : rtp_transport_->SendRtpPacket(packet, options, PF_SRTP_BYPASS);
+
+    std::string packet_type = rtcp ? "RTCP" : "RTP";
+    RTC_LOG(LS_WARNING) << "Sending an " << packet_type
+                        << " packet without encryption.";
+  } else {
+    // Make sure we didn't accidentally send any packets without encryption.
+    RTC_DCHECK(rtp_transport_ == sdes_transport_.get() ||
+               rtp_transport_ == dtls_srtp_transport_.get());
   }
-  RTC_DCHECK(srtp_transport_);
-  RTC_DCHECK(srtp_transport_->IsActive());
   // Bon voyage.
-  return rtcp ? srtp_transport_->SendRtcpPacket(packet, options, PF_SRTP_BYPASS)
-              : srtp_transport_->SendRtpPacket(packet, options, PF_SRTP_BYPASS);
+  return rtcp ? rtp_transport_->SendRtcpPacket(packet, options, PF_SRTP_BYPASS)
+              : rtp_transport_->SendRtpPacket(packet, options, PF_SRTP_BYPASS);
 }
 
 bool BaseChannel::HandlesPayloadType(int packet_type) const {
@@ -763,164 +698,15 @@
                    << (was_ever_writable_ ? "" : " for the first time");
 
   was_ever_writable_ = true;
-  MaybeSetupDtlsSrtp_n();
   writable_ = true;
   UpdateMediaSendRecvState();
 }
 
-void BaseChannel::SignalDtlsSrtpSetupFailure_n(bool rtcp) {
-  RTC_DCHECK(network_thread_->IsCurrent());
-  invoker_.AsyncInvoke<void>(
-      RTC_FROM_HERE, signaling_thread(),
-      Bind(&BaseChannel::SignalDtlsSrtpSetupFailure_s, this, rtcp));
-}
-
-void BaseChannel::SignalDtlsSrtpSetupFailure_s(bool rtcp) {
-  RTC_DCHECK(signaling_thread() == rtc::Thread::Current());
-  SignalDtlsSrtpSetupFailure(this, rtcp);
-}
-
 bool BaseChannel::ShouldSetupDtlsSrtp_n() const {
   // Since DTLS is applied to all transports, checking RTP should be enough.
   return rtp_dtls_transport_ && rtp_dtls_transport_->IsDtlsActive();
 }
 
-// This function returns true if either DTLS-SRTP is not in use
-// *or* DTLS-SRTP is successfully set up.
-bool BaseChannel::SetupDtlsSrtp_n(bool rtcp) {
-  RTC_DCHECK(network_thread_->IsCurrent());
-  bool ret = false;
-
-  DtlsTransportInternal* transport =
-      rtcp ? rtcp_dtls_transport_ : rtp_dtls_transport_;
-  RTC_DCHECK(transport);
-  RTC_DCHECK(transport->IsDtlsActive());
-
-  int selected_crypto_suite;
-
-  if (!transport->GetSrtpCryptoSuite(&selected_crypto_suite)) {
-    RTC_LOG(LS_ERROR) << "No DTLS-SRTP selected crypto suite";
-    return false;
-  }
-
-  RTC_LOG(LS_INFO) << "Installing keys from DTLS-SRTP on " << content_name()
-                   << " " << RtpRtcpStringLiteral(rtcp);
-
-  int key_len;
-  int salt_len;
-  if (!rtc::GetSrtpKeyAndSaltLengths(selected_crypto_suite, &key_len,
-      &salt_len)) {
-    RTC_LOG(LS_ERROR) << "Unknown DTLS-SRTP crypto suite"
-                      << selected_crypto_suite;
-    return false;
-  }
-
-  // OK, we're now doing DTLS (RFC 5764)
-  std::vector<unsigned char> dtls_buffer(key_len * 2 + salt_len * 2);
-
-  // RFC 5705 exporter using the RFC 5764 parameters
-  if (!transport->ExportKeyingMaterial(kDtlsSrtpExporterLabel, NULL, 0, false,
-                                       &dtls_buffer[0], dtls_buffer.size())) {
-    RTC_LOG(LS_WARNING) << "DTLS-SRTP key export failed";
-    RTC_NOTREACHED();  // This should never happen
-    return false;
-  }
-
-  // Sync up the keys with the DTLS-SRTP interface
-  std::vector<unsigned char> client_write_key(key_len + salt_len);
-  std::vector<unsigned char> server_write_key(key_len + salt_len);
-  size_t offset = 0;
-  memcpy(&client_write_key[0], &dtls_buffer[offset], key_len);
-  offset += key_len;
-  memcpy(&server_write_key[0], &dtls_buffer[offset], key_len);
-  offset += key_len;
-  memcpy(&client_write_key[key_len], &dtls_buffer[offset], salt_len);
-  offset += salt_len;
-  memcpy(&server_write_key[key_len], &dtls_buffer[offset], salt_len);
-
-  std::vector<unsigned char> *send_key, *recv_key;
-  rtc::SSLRole role;
-  if (!transport->GetSslRole(&role)) {
-    RTC_LOG(LS_WARNING) << "GetSslRole failed";
-    return false;
-  }
-
-  if (role == rtc::SSL_SERVER) {
-    send_key = &server_write_key;
-    recv_key = &client_write_key;
-  } else {
-    send_key = &client_write_key;
-    recv_key = &server_write_key;
-  }
-
-  // Use an empty encrypted header extension ID vector if not set. This could
-  // happen when the DTLS handshake is completed before processing the
-  // Offer/Answer which contains the encrypted header extension IDs.
-  std::vector<int> send_extension_ids;
-  std::vector<int> recv_extension_ids;
-  if (catched_send_extension_ids_) {
-    send_extension_ids = *catched_send_extension_ids_;
-  }
-  if (catched_recv_extension_ids_) {
-    recv_extension_ids = *catched_recv_extension_ids_;
-  }
-
-  if (rtcp) {
-    if (!dtls_active()) {
-      RTC_DCHECK(srtp_transport_);
-      ret = srtp_transport_->SetRtcpParams(
-          selected_crypto_suite, &(*send_key)[0],
-          static_cast<int>(send_key->size()), send_extension_ids,
-          selected_crypto_suite, &(*recv_key)[0],
-          static_cast<int>(recv_key->size()), recv_extension_ids);
-    } else {
-      // RTCP doesn't need to call SetRtpParam because it is only used
-      // to make the updated encrypted RTP header extension IDs take effect.
-      ret = true;
-    }
-  } else {
-    RTC_DCHECK(srtp_transport_);
-    ret = srtp_transport_->SetRtpParams(
-        selected_crypto_suite, &(*send_key)[0],
-        static_cast<int>(send_key->size()), send_extension_ids,
-        selected_crypto_suite, &(*recv_key)[0],
-        static_cast<int>(recv_key->size()), recv_extension_ids);
-    dtls_active_ = ret;
-  }
-
-  if (!ret) {
-    RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation failed";
-  }
-
-  return ret;
-}
-
-void BaseChannel::MaybeSetupDtlsSrtp_n() {
-  if (dtls_active()) {
-    return;
-  }
-
-  if (!ShouldSetupDtlsSrtp_n()) {
-    return;
-  }
-
-  if (!srtp_transport_) {
-    EnableSrtpTransport_n();
-  }
-
-  if (!SetupDtlsSrtp_n(false)) {
-    SignalDtlsSrtpSetupFailure_n(false);
-    return;
-  }
-
-  if (rtcp_dtls_transport_) {
-    if (!SetupDtlsSrtp_n(true)) {
-      SignalDtlsSrtpSetupFailure_n(true);
-      return;
-    }
-  }
-}
-
 void BaseChannel::ChannelNotWritable_n() {
   RTC_DCHECK(network_thread_->IsCurrent());
   if (!writable_)
@@ -986,25 +772,57 @@
   return true;
 }
 
-void BaseChannel::EnableSrtpTransport_n() {
-  if (srtp_transport_ == nullptr) {
-    rtp_transport_->SignalReadyToSend.disconnect(this);
-    rtp_transport_->SignalPacketReceived.disconnect(this);
-    rtp_transport_->SignalNetworkRouteChanged.disconnect(this);
-
-    auto transport = rtc::MakeUnique<webrtc::SrtpTransport>(
-        std::move(rtp_transport_), content_name_);
-    srtp_transport_ = transport.get();
-    rtp_transport_ = std::move(transport);
-
-    rtp_transport_->SignalReadyToSend.connect(
-        this, &BaseChannel::OnTransportReadyToSend);
-    rtp_transport_->SignalPacketReceived.connect(
-        this, &BaseChannel::OnPacketReceived);
-    rtp_transport_->SignalNetworkRouteChanged.connect(
-        this, &BaseChannel::OnNetworkRouteChanged);
-    RTC_LOG(LS_INFO) << "Wrapping RtpTransport in SrtpTransport.";
+void BaseChannel::EnableSdes_n() {
+  if (sdes_transport_) {
+    return;
   }
+  // DtlsSrtpTransport and SrtpTransport shouldn't be enabled at the same
+  // time.
+  RTC_DCHECK(!dtls_srtp_transport_);
+  RTC_DCHECK(unencrypted_rtp_transport_);
+  DisconnectFromRtpTransport();
+  sdes_transport_ = rtc::MakeUnique<webrtc::SrtpTransport>(
+      std::move(unencrypted_rtp_transport_), content_name_);
+  rtp_transport_ = sdes_transport_.get();
+  ConnectToRtpTransport();
+  RTC_LOG(LS_INFO) << "Wrapping RtpTransport in SrtpTransport.";
+}
+
+void BaseChannel::EnableDtlsSrtp_n() {
+  if (dtls_srtp_transport_) {
+    return;
+  }
+  // DtlsSrtpTransport and SrtpTransport shouldn't be enabled at the same
+  // time.
+  RTC_DCHECK(!sdes_transport_);
+  RTC_DCHECK(unencrypted_rtp_transport_);
+  DisconnectFromRtpTransport();
+
+  auto srtp_transport = rtc::MakeUnique<webrtc::SrtpTransport>(
+      std::move(unencrypted_rtp_transport_), content_name_);
+#if defined(ENABLE_EXTERNAL_AUTH)
+  srtp_transport->EnableExternalAuth();
+#endif
+  dtls_srtp_transport_ =
+      rtc::MakeUnique<webrtc::DtlsSrtpTransport>(std::move(srtp_transport));
+
+  rtp_transport_ = dtls_srtp_transport_.get();
+  ConnectToRtpTransport();
+  if (cached_send_extension_ids_) {
+    dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds(
+        *cached_send_extension_ids_);
+  }
+  if (cached_recv_extension_ids_) {
+    dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds(
+        *cached_recv_extension_ids_);
+  }
+  // Set the DtlsTransport and the |dtls_srtp_transport_| will handle the DTLS
+  // relate signal internally.
+  RTC_DCHECK(rtp_dtls_transport_);
+  dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport_,
+                                          rtcp_dtls_transport_);
+
+  RTC_LOG(LS_INFO) << "Wrapping SrtpTransport in DtlsSrtpTransport.";
 }
 
 bool BaseChannel::SetSrtp_n(const std::vector<CryptoParams>& cryptos,
@@ -1022,75 +840,59 @@
 
   // If SRTP was not required, but we're setting a description that uses SDES,
   // we need to upgrade to an SrtpTransport.
-  if (!srtp_transport_ && !dtls && !cryptos.empty()) {
-    EnableSrtpTransport_n();
+  if (!sdes_transport_ && !dtls && !cryptos.empty()) {
+    EnableSdes_n();
   }
 
-  bool encrypted_header_extensions_id_changed =
-      EncryptedHeaderExtensionIdsChanged(src, encrypted_extension_ids);
-  CacheEncryptedHeaderExtensionIds(src, encrypted_extension_ids);
+  if ((action == CA_ANSWER || action == CA_PRANSWER) && dtls) {
+    EnableDtlsSrtp_n();
+  }
 
-  switch (action) {
-    case CA_OFFER:
-      // If DTLS is already active on the channel, we could be renegotiating
-      // here. We don't update the srtp filter.
-      if (!dtls) {
+  UpdateEncryptedHeaderExtensionIds(src, encrypted_extension_ids);
+
+  if (!dtls) {
+    switch (action) {
+      case CA_OFFER:
         ret = sdes_negotiator_.SetOffer(cryptos, src);
-      }
-      break;
-    case CA_PRANSWER:
-      // If we're doing DTLS-SRTP, we don't want to update the filter
-      // with an answer, because we already have SRTP parameters.
-      if (!dtls) {
+        break;
+      case CA_PRANSWER:
         ret = sdes_negotiator_.SetProvisionalAnswer(cryptos, src);
-      }
-      break;
-    case CA_ANSWER:
-      // If we're doing DTLS-SRTP, we don't want to update the filter
-      // with an answer, because we already have SRTP parameters.
-      if (!dtls) {
+        break;
+      case CA_ANSWER:
         ret = sdes_negotiator_.SetAnswer(cryptos, src);
-      }
-      break;
-    default:
-      break;
-  }
+        break;
+      default:
+        break;
+    }
 
-  // If setting an SDES answer succeeded, apply the negotiated parameters
-  // to the SRTP transport.
-  if ((action == CA_PRANSWER || action == CA_ANSWER) && !dtls && ret) {
-    if (sdes_negotiator_.send_cipher_suite() &&
-        sdes_negotiator_.recv_cipher_suite()) {
-      RTC_DCHECK(catched_send_extension_ids_);
-      RTC_DCHECK(catched_recv_extension_ids_);
-      ret = srtp_transport_->SetRtpParams(
-          *(sdes_negotiator_.send_cipher_suite()),
-          sdes_negotiator_.send_key().data(),
-          static_cast<int>(sdes_negotiator_.send_key().size()),
-          *(catched_send_extension_ids_),
-          *(sdes_negotiator_.recv_cipher_suite()),
-          sdes_negotiator_.recv_key().data(),
-          static_cast<int>(sdes_negotiator_.recv_key().size()),
-          *(catched_recv_extension_ids_));
-    } else {
-      RTC_LOG(LS_INFO) << "No crypto keys are provided for SDES.";
-      if (action == CA_ANSWER && srtp_transport_) {
-        // Explicitly reset the |srtp_transport_| if no crypto param is
-        // provided in the answer. No need to call |ResetParams()| for
-        // |sdes_negotiator_| because it resets the params inside |SetAnswer|.
-        srtp_transport_->ResetParams();
+    // If setting an SDES answer succeeded, apply the negotiated parameters
+    // to the SRTP transport.
+    if ((action == CA_PRANSWER || action == CA_ANSWER) && ret) {
+      if (sdes_negotiator_.send_cipher_suite() &&
+          sdes_negotiator_.recv_cipher_suite()) {
+        RTC_DCHECK(cached_send_extension_ids_);
+        RTC_DCHECK(cached_recv_extension_ids_);
+        ret = sdes_transport_->SetRtpParams(
+            *(sdes_negotiator_.send_cipher_suite()),
+            sdes_negotiator_.send_key().data(),
+            static_cast<int>(sdes_negotiator_.send_key().size()),
+            *(cached_send_extension_ids_),
+            *(sdes_negotiator_.recv_cipher_suite()),
+            sdes_negotiator_.recv_key().data(),
+            static_cast<int>(sdes_negotiator_.recv_key().size()),
+            *(cached_recv_extension_ids_));
+      } else {
+        RTC_LOG(LS_INFO) << "No crypto keys are provided for SDES.";
+        if (action == CA_ANSWER && sdes_transport_) {
+          // Explicitly reset the |sdes_transport_| if no crypto param is
+          // provided in the answer. No need to call |ResetParams()| for
+          // |sdes_negotiator_| because it resets the params inside |SetAnswer|.
+          sdes_transport_->ResetParams();
+        }
       }
     }
   }
 
-  // Only update SRTP transport if using DTLS. SDES is handled internally
-  // by the SRTP filter.
-  if (ret && dtls_active() && rtp_dtls_transport_ &&
-      rtp_dtls_transport_->dtls_state() == DTLS_TRANSPORT_CONNECTED &&
-      encrypted_header_extensions_id_changed) {
-    ret = SetupDtlsSrtp_n(/*rtcp=*/false);
-  }
-
   if (!ret) {
     SafeSetError("Failed to setup SRTP.", error_desc);
     return false;
@@ -1124,20 +926,7 @@
     case CA_ANSWER:
       ret = rtcp_mux_filter_.SetAnswer(enable, src);
       if (ret && rtcp_mux_filter_.IsActive()) {
-        // We permanently activated RTCP muxing; signal that we no longer need
-        // the RTCP transport.
-        std::string debug_name =
-            transport_name_.empty()
-                ? rtp_transport_->rtp_packet_transport()->transport_name()
-                : transport_name_;
-        RTC_LOG(LS_INFO) << "Enabling rtcp-mux for " << content_name()
-                         << "; no longer need RTCP transport for "
-                         << debug_name;
-        if (rtp_transport_->rtcp_packet_transport()) {
-          SetTransport_n(true, nullptr, nullptr);
-          SignalRtcpMuxFullyActive(transport_name_);
-        }
-        UpdateWritableState_n();
+        ActivateRtcpMux();
       }
       break;
     default:
@@ -1285,8 +1074,8 @@
 
 void BaseChannel::CacheRtpAbsSendTimeHeaderExtension_n(
     int rtp_abs_sendtime_extn_id) {
-  if (srtp_transport_) {
-    srtp_transport_->CacheRtpAbsSendTimeHeaderExtension(
+  if (sdes_transport_) {
+    sdes_transport_->CacheRtpAbsSendTimeHeaderExtension(
         rtp_abs_sendtime_extn_id);
   } else {
     RTC_LOG(LS_WARNING)
@@ -1331,9 +1120,7 @@
   }
 }
 
-void BaseChannel::SignalSentPacket_n(
-    rtc::PacketTransportInternal* /* transport */,
-    const rtc::SentPacket& sent_packet) {
+void BaseChannel::SignalSentPacket_n(const rtc::SentPacket& sent_packet) {
   RTC_DCHECK(network_thread_->IsCurrent());
   invoker_.AsyncInvoke<void>(
       RTC_FROM_HERE, worker_thread_,
@@ -1345,24 +1132,45 @@
   SignalSentPacket(sent_packet);
 }
 
-void BaseChannel::CacheEncryptedHeaderExtensionIds(
+void BaseChannel::UpdateEncryptedHeaderExtensionIds(
     cricket::ContentSource source,
     const std::vector<int>& extension_ids) {
-  source == ContentSource::CS_LOCAL
-      ? catched_recv_extension_ids_.emplace(extension_ids)
-      : catched_send_extension_ids_.emplace(extension_ids);
+  if (source == ContentSource::CS_LOCAL) {
+    cached_recv_extension_ids_ = std::move(extension_ids);
+    if (dtls_srtp_transport_) {
+      dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds(
+          extension_ids);
+    }
+  } else {
+    cached_send_extension_ids_ = std::move(extension_ids);
+    if (dtls_srtp_transport_) {
+      dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds(
+          extension_ids);
+    }
+  }
 }
 
-bool BaseChannel::EncryptedHeaderExtensionIdsChanged(
-    cricket::ContentSource source,
-    const std::vector<int>& new_extension_ids) {
-  if (source == ContentSource::CS_LOCAL) {
-    return !catched_recv_extension_ids_ ||
-           (*catched_recv_extension_ids_) != new_extension_ids;
-  } else {
-    return !catched_send_extension_ids_ ||
-           (*catched_send_extension_ids_) != new_extension_ids;
+void BaseChannel::ActivateRtcpMux() {
+  // We permanently activated RTCP muxing; signal that we no longer need
+  // the RTCP transport.
+  std::string debug_name =
+      transport_name_.empty()
+          ? rtp_transport_->rtp_packet_transport()->transport_name()
+          : transport_name_;
+  RTC_LOG(LS_INFO) << "Enabling rtcp-mux for " << content_name()
+                   << "; no longer need RTCP transport for " << debug_name;
+  if (rtp_transport_->rtcp_packet_transport()) {
+    SetTransport_n(/*rtcp=*/true, nullptr, nullptr);
+    if (dtls_srtp_transport_) {
+      RTC_DCHECK(rtp_dtls_transport_);
+      dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport_,
+                                              /*rtcp_dtls_transport_=*/nullptr);
+    } else {
+      rtp_transport_->SetRtcpPacketTransport(nullptr);
+    }
+    SignalRtcpMuxFullyActive(transport_name_);
   }
+  UpdateWritableState_n();
 }
 
 VoiceChannel::VoiceChannel(rtc::Thread* worker_thread,
diff --git a/pc/channel.h b/pc/channel.h
index dbc6367..1c4e50a 100644
--- a/pc/channel.h
+++ b/pc/channel.h
@@ -29,10 +29,13 @@
 #include "p2p/base/packettransportinternal.h"
 #include "p2p/client/socketmonitor.h"
 #include "pc/audiomonitor.h"
+#include "pc/dtlssrtptransport.h"
 #include "pc/mediamonitor.h"
 #include "pc/mediasession.h"
 #include "pc/rtcpmuxfilter.h"
+#include "pc/rtptransport.h"
 #include "pc/srtpfilter.h"
+#include "pc/srtptransport.h"
 #include "pc/transportcontroller.h"
 #include "rtc_base/asyncinvoker.h"
 #include "rtc_base/asyncudpsocket.h"
@@ -43,8 +46,6 @@
 
 namespace webrtc {
 class AudioSinkInterface;
-class RtpTransportInternal;
-class SrtpTransport;
 }  // namespace webrtc
 
 namespace cricket {
@@ -101,9 +102,13 @@
   bool enabled() const { return enabled_; }
 
   // This function returns true if we are using SDES.
-  bool sdes_active() const { return sdes_negotiator_.IsActive(); }
+  bool sdes_active() const {
+    return sdes_transport_ && sdes_negotiator_.IsActive();
+  }
   // The following function returns true if we are using DTLS-based keying.
-  bool dtls_active() const { return dtls_active_; }
+  bool dtls_active() const {
+    return dtls_srtp_transport_ && dtls_srtp_transport_->IsActive();
+  }
   // This function returns true if using SRTP (DTLS-based keying or SDES).
   bool srtp_active() const { return sdes_active() || dtls_active(); }
 
@@ -225,11 +230,6 @@
   bool IsReadyToSendMedia_w() const;
   rtc::Thread* signaling_thread() { return signaling_thread_; }
 
-  void ConnectToDtlsTransport(DtlsTransportInternal* transport);
-  void DisconnectFromDtlsTransport(DtlsTransportInternal* transport);
-  void ConnectToPacketTransport(rtc::PacketTransportInternal* transport);
-  void DisconnectFromPacketTransport(rtc::PacketTransportInternal* transport);
-
   void FlushRtcpMessages_n();
 
   // NetworkInterface implementation, called by MediaEngine
@@ -238,10 +238,8 @@
   bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
                 const rtc::PacketOptions& options) override;
 
-  // From TransportChannel
-  void OnWritableState(rtc::PacketTransportInternal* transport);
-
-  void OnDtlsState(DtlsTransportInternal* transport, DtlsTransportState state);
+  // From RtpTransportInternal
+  void OnWritableState(bool writable);
 
   void OnNetworkRouteChanged(rtc::Optional<rtc::NetworkRoute> network_route);
 
@@ -349,30 +347,34 @@
   void AddHandledPayloadType(int payload_type);
 
  private:
+  void ConnectToRtpTransport();
+  void DisconnectFromRtpTransport();
   void InitNetwork_n(DtlsTransportInternal* rtp_dtls_transport,
                      DtlsTransportInternal* rtcp_dtls_transport,
                      rtc::PacketTransportInternal* rtp_packet_transport,
                      rtc::PacketTransportInternal* rtcp_packet_transport);
-  void DisconnectTransportChannels_n();
-  void SignalSentPacket_n(rtc::PacketTransportInternal* transport,
-                          const rtc::SentPacket& sent_packet);
+  void SignalSentPacket_n(const rtc::SentPacket& sent_packet);
   void SignalSentPacket_w(const rtc::SentPacket& sent_packet);
   bool IsReadyToSendMedia_n() const;
   void CacheRtpAbsSendTimeHeaderExtension_n(int rtp_abs_sendtime_extn_id);
   // Wraps the existing RtpTransport in an SrtpTransport.
-  void EnableSrtpTransport_n();
+  void EnableSdes_n();
 
-  // Cache the encrypted header extension IDs when setting the local/remote
+  // Wraps the existing RtpTransport in a new SrtpTransport and wraps that in a
+  // new DtlsSrtpTransport.
+  void EnableDtlsSrtp_n();
+
+  // Update the encrypted header extension IDs when setting the local/remote
   // description and use them later together with other crypto parameters from
-  // DtlsTransport.
-  void CacheEncryptedHeaderExtensionIds(cricket::ContentSource source,
-                                        const std::vector<int>& extension_ids);
+  // DtlsTransport. If DTLS-SRTP is enabled, it also update the encrypted header
+  // extension IDs for DtlsSrtpTransport.
+  void UpdateEncryptedHeaderExtensionIds(cricket::ContentSource source,
+                                         const std::vector<int>& extension_ids);
 
-  // Return true if the new header extension IDs are different from the existing
-  // ones.
-  bool EncryptedHeaderExtensionIdsChanged(
-      cricket::ContentSource source,
-      const std::vector<int>& new_extension_ids);
+  // Permanently enable RTCP muxing. Set null RTCP PacketTransport for
+  // BaseChannel and RtpTransport. If using DTLS-SRTP, set null DtlsTransport
+  // for DtlsSrtpTransport.
+  void ActivateRtcpMux();
 
   rtc::Thread* const worker_thread_;
   rtc::Thread* const network_thread_;
@@ -392,8 +394,14 @@
   // If non-null, "X_dtls_transport_" will always equal "X_packet_transport_".
   DtlsTransportInternal* rtp_dtls_transport_ = nullptr;
   DtlsTransportInternal* rtcp_dtls_transport_ = nullptr;
-  std::unique_ptr<webrtc::RtpTransportInternal> rtp_transport_;
-  webrtc::SrtpTransport* srtp_transport_ = nullptr;
+
+  webrtc::RtpTransportInternal* rtp_transport_ = nullptr;
+  // Only one of these transports is non-null at a time. One for DTLS-SRTP, one
+  // for SDES and one for unencrypted RTP.
+  std::unique_ptr<webrtc::SrtpTransport> sdes_transport_;
+  std::unique_ptr<webrtc::DtlsSrtpTransport> dtls_srtp_transport_;
+  std::unique_ptr<webrtc::RtpTransport> unencrypted_rtp_transport_;
+
   std::vector<std::pair<rtc::Socket::Option, int> > socket_options_;
   std::vector<std::pair<rtc::Socket::Option, int> > rtcp_socket_options_;
   SrtpFilter sdes_negotiator_;
@@ -401,7 +409,6 @@
   bool writable_ = false;
   bool was_ever_writable_ = false;
   bool has_received_packet_ = false;
-  bool dtls_active_ = false;
   const bool srtp_required_ = true;
 
   // MediaChannel related members that should be accessed from the worker
@@ -419,8 +426,8 @@
       webrtc::RtpTransceiverDirection::kInactive;
 
   // The cached encrypted header extension IDs.
-  rtc::Optional<std::vector<int>> catched_send_extension_ids_;
-  rtc::Optional<std::vector<int>> catched_recv_extension_ids_;
+  rtc::Optional<std::vector<int>> cached_send_extension_ids_;
+  rtc::Optional<std::vector<int>> cached_recv_extension_ids_;
 };
 
 // VoiceChannel is a specialization that adds support for early media, DTMF,
diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc
index d1ceac5..7cc5dec 100644
--- a/pc/channel_unittest.cc
+++ b/pc/channel_unittest.cc
@@ -1398,6 +1398,17 @@
     EXPECT_TRUE(CheckNoRtcp2());
   }
 
+  // Test that the DTLS to SDES fallback is not supported and the negotiation
+  // between DTLS to SDES end points will fail.
+  void SendDtlsToSdesNotSupported() {
+    int flags1 = SECURE | DTLS;
+    int flags2 = SECURE;
+    CreateChannels(flags1, flags2);
+    EXPECT_FALSE(channel1_->srtp_active());
+    EXPECT_FALSE(channel2_->srtp_active());
+    EXPECT_FALSE(SendInitiate());
+  }
+
   // Test that we properly handling SRTP negotiating down to RTP.
   void SendSrtpToRtp() {
     CreateChannels(SECURE, 0);
@@ -1982,7 +1993,7 @@
     cricket::AudioContentDescription* audio) {
   audio->AddCodec(audio_codec);
   audio->set_rtcp_mux((flags & RTCP_MUX) != 0);
-  if (flags & SECURE) {
+  if ((flags & SECURE) && !(flags & DTLS)) {
     audio->AddCrypto(cricket::CryptoParams(
         1, rtc::CS_AES_CM_128_HMAC_SHA1_32,
         "inline:" + rtc::CreateRandomString(40), std::string()));
@@ -2295,7 +2306,7 @@
 }
 
 TEST_F(VoiceChannelSingleThreadTest, SendDtlsSrtpToSrtp) {
-  Base::SendSrtpToSrtp(DTLS, 0);
+  Base::SendDtlsToSdesNotSupported();
 }
 
 TEST_F(VoiceChannelSingleThreadTest, SendDtlsSrtpToDtlsSrtp) {
@@ -2646,7 +2657,7 @@
 }
 
 TEST_F(VoiceChannelDoubleThreadTest, SendDtlsSrtpToSrtp) {
-  Base::SendSrtpToSrtp(DTLS, 0);
+  Base::SendDtlsToSdesNotSupported();
 }
 
 TEST_F(VoiceChannelDoubleThreadTest, SendDtlsSrtpToDtlsSrtp) {
@@ -2947,7 +2958,7 @@
 }
 
 TEST_F(VideoChannelSingleThreadTest, SendDtlsSrtpToSrtp) {
-  Base::SendSrtpToSrtp(DTLS, 0);
+  Base::SendDtlsToSdesNotSupported();
 }
 
 TEST_F(VideoChannelSingleThreadTest, SendDtlsSrtpToDtlsSrtp) {
@@ -3170,7 +3181,7 @@
 }
 
 TEST_F(VideoChannelDoubleThreadTest, SendDtlsSrtpToSrtp) {
-  Base::SendSrtpToSrtp(DTLS, 0);
+  Base::SendDtlsToSdesNotSupported();
 }
 
 TEST_F(VideoChannelDoubleThreadTest, SendDtlsSrtpToDtlsSrtp) {
diff --git a/pc/dtlssrtptransport.cc b/pc/dtlssrtptransport.cc
index 1c2d21f..bc5d3aa 100644
--- a/pc/dtlssrtptransport.cc
+++ b/pc/dtlssrtptransport.cc
@@ -33,6 +33,10 @@
       this, &DtlsSrtpTransport::OnPacketReceived);
   srtp_transport_->SignalReadyToSend.connect(this,
                                              &DtlsSrtpTransport::OnReadyToSend);
+  srtp_transport_->SignalWritableState.connect(
+      this, &DtlsSrtpTransport::OnWritableState);
+  srtp_transport_->SignalSentPacket.connect(this,
+                                            &DtlsSrtpTransport::OnSentPacket);
 }
 
 void DtlsSrtpTransport::SetDtlsTransports(
@@ -51,23 +55,22 @@
     srtp_transport_->ResetParams();
   }
 
-  if (rtcp_dtls_transport) {
-    // This would only be possible if using BUNDLE but not rtcp-mux, which isn't
-    // allowed according to the BUNDLE spec.
-    RTC_CHECK(!(IsActive()))
-        << "Setting RTCP for DTLS/SRTP after the DTLS is active "
-        << "should never happen.";
+  const std::string transport_name =
+      rtp_dtls_transport ? rtp_dtls_transport->transport_name() : "null";
 
-    RTC_LOG(LS_INFO) << "Setting RTCP Transport on "
-                     << rtcp_dtls_transport->transport_name() << " transport "
-                     << rtcp_dtls_transport;
-    SetRtcpDtlsTransport(rtcp_dtls_transport);
-    SetRtcpPacketTransport(rtcp_dtls_transport);
-  }
+  // This would only be possible if using BUNDLE but not rtcp-mux, which isn't
+  // allowed according to the BUNDLE spec.
+  RTC_CHECK(!(IsActive()))
+      << "Setting RTCP for DTLS/SRTP after the DTLS is active "
+      << "should never happen.";
 
-  RTC_LOG(LS_INFO) << "Setting RTP Transport on "
-                   << rtp_dtls_transport->transport_name() << " transport "
-                   << rtp_dtls_transport;
+  RTC_LOG(LS_INFO) << "Setting RTCP Transport on " << transport_name
+                   << " transport " << rtcp_dtls_transport;
+  SetRtcpDtlsTransport(rtcp_dtls_transport);
+  SetRtcpPacketTransport(rtcp_dtls_transport);
+
+  RTC_LOG(LS_INFO) << "Setting RTP Transport on " << transport_name
+                   << " transport " << rtp_dtls_transport;
   SetRtpDtlsTransport(rtp_dtls_transport);
   SetRtpPacketTransport(rtp_dtls_transport);
 
@@ -81,18 +84,28 @@
   }
 }
 
-void DtlsSrtpTransport::SetSendEncryptedHeaderExtensionIds(
+void DtlsSrtpTransport::UpdateSendEncryptedHeaderExtensionIds(
     const std::vector<int>& send_extension_ids) {
+  if (send_extension_ids_ == send_extension_ids) {
+    return;
+  }
   send_extension_ids_.emplace(send_extension_ids);
-  // Reset the crypto parameters to update the send_extension IDs.
-  SetupRtpDtlsSrtp();
+  if (DtlsHandshakeCompleted()) {
+    // Reset the crypto parameters to update the send extension IDs.
+    SetupRtpDtlsSrtp();
+  }
 }
 
-void DtlsSrtpTransport::SetRecvEncryptedHeaderExtensionIds(
+void DtlsSrtpTransport::UpdateRecvEncryptedHeaderExtensionIds(
     const std::vector<int>& recv_extension_ids) {
+  if (recv_extension_ids_ == recv_extension_ids) {
+    return;
+  }
   recv_extension_ids_.emplace(recv_extension_ids);
-  // Reset the crypto parameters to update the send_extension IDs.
-  SetupRtpDtlsSrtp();
+  if (DtlsHandshakeCompleted()) {
+    // Reset the crypto parameters to update the receive extension IDs.
+    SetupRtpDtlsSrtp();
+  }
 }
 
 bool DtlsSrtpTransport::IsDtlsActive() {
@@ -267,9 +280,12 @@
 void DtlsSrtpTransport::SetDtlsTransport(
     cricket::DtlsTransportInternal* new_dtls_transport,
     cricket::DtlsTransportInternal** old_dtls_transport) {
+  if (*old_dtls_transport == new_dtls_transport) {
+    return;
+  }
+
   if (*old_dtls_transport) {
     (*old_dtls_transport)->SignalDtlsState.disconnect(this);
-    (*old_dtls_transport)->SignalWritableState.disconnect(this);
   }
 
   *old_dtls_transport = new_dtls_transport;
@@ -277,8 +293,6 @@
   if (new_dtls_transport) {
     new_dtls_transport->SignalDtlsState.connect(
         this, &DtlsSrtpTransport::OnDtlsState);
-    new_dtls_transport->SignalWritableState.connect(
-        this, &DtlsSrtpTransport::OnWritableState);
   }
 }
 
@@ -321,11 +335,15 @@
   MaybeSetupDtlsSrtp();
 }
 
-void DtlsSrtpTransport::OnWritableState(
-    rtc::PacketTransportInternal* transport) {
-  RTC_DCHECK(transport == srtp_transport_->rtp_packet_transport() ||
-             transport == srtp_transport_->rtcp_packet_transport());
-  UpdateWritableStateAndMaybeSetupDtlsSrtp();
+void DtlsSrtpTransport::OnWritableState(bool writable) {
+  SetWritable(writable);
+  if (writable) {
+    MaybeSetupDtlsSrtp();
+  }
+}
+
+void DtlsSrtpTransport::OnSentPacket(const rtc::SentPacket& sent_packet) {
+  SignalSentPacket(sent_packet);
 }
 
 void DtlsSrtpTransport::OnPacketReceived(bool rtcp,
diff --git a/pc/dtlssrtptransport.h b/pc/dtlssrtptransport.h
index 43f072a..e033425 100644
--- a/pc/dtlssrtptransport.h
+++ b/pc/dtlssrtptransport.h
@@ -38,10 +38,10 @@
   void SetRtcpMuxEnabled(bool enable) override;
 
   // Set the header extension ids that should be encrypted.
-  void SetSendEncryptedHeaderExtensionIds(
+  void UpdateSendEncryptedHeaderExtensionIds(
       const std::vector<int>& send_extension_ids);
 
-  void SetRecvEncryptedHeaderExtensionIds(
+  void UpdateRecvEncryptedHeaderExtensionIds(
       const std::vector<int>& recv_extension_ids);
 
   bool IsActive() { return srtp_transport_->IsActive(); }
@@ -75,7 +75,8 @@
 
   void OnDtlsState(cricket::DtlsTransportInternal* dtls_transport,
                    cricket::DtlsTransportState state);
-  void OnWritableState(rtc::PacketTransportInternal* transport);
+  void OnWritableState(bool writable);
+  void OnSentPacket(const rtc::SentPacket& sent_packet);
   void OnPacketReceived(bool rtcp,
                         rtc::CopyOnWriteBuffer* packet,
                         const rtc::PacketTime& packet_time);
diff --git a/pc/dtlssrtptransport_unittest.cc b/pc/dtlssrtptransport_unittest.cc
index ac1af1c..77c5902 100644
--- a/pc/dtlssrtptransport_unittest.cc
+++ b/pc/dtlssrtptransport_unittest.cc
@@ -70,8 +70,6 @@
       bool rtcp_mux_enabled) {
     auto rtp_transport = rtc::MakeUnique<RtpTransport>(rtcp_mux_enabled);
 
-    rtp_transport->SetRtpPacketTransport(rtp_dtls);
-    rtp_transport->SetRtcpPacketTransport(rtcp_dtls);
     rtp_transport->AddHandledPayloadType(0x00);
     rtp_transport->AddHandledPayloadType(0xc9);
 
@@ -437,11 +435,14 @@
   encrypted_headers.push_back(kHeaderExtensionIDs[0]);
   encrypted_headers.push_back(kHeaderExtensionIDs[1]);
 
-  dtls_srtp_transport1_->SetSendEncryptedHeaderExtensionIds(encrypted_headers);
-  dtls_srtp_transport1_->SetRecvEncryptedHeaderExtensionIds(encrypted_headers);
-  dtls_srtp_transport2_->SetSendEncryptedHeaderExtensionIds(encrypted_headers);
-  dtls_srtp_transport2_->SetRecvEncryptedHeaderExtensionIds(encrypted_headers);
-  SendRecvRtpPacketsWithHeaderExtension(encrypted_headers);
+  dtls_srtp_transport1_->UpdateSendEncryptedHeaderExtensionIds(
+      encrypted_headers);
+  dtls_srtp_transport1_->UpdateRecvEncryptedHeaderExtensionIds(
+      encrypted_headers);
+  dtls_srtp_transport2_->UpdateSendEncryptedHeaderExtensionIds(
+      encrypted_headers);
+  dtls_srtp_transport2_->UpdateRecvEncryptedHeaderExtensionIds(
+      encrypted_headers);
 }
 
 // Tests if RTCP muxing is enabled. DtlsSrtpTransport is ready to send once the
diff --git a/pc/rtptransport.cc b/pc/rtptransport.cc
index d2604f9..26f7e3e 100644
--- a/pc/rtptransport.cc
+++ b/pc/rtptransport.cc
@@ -33,6 +33,8 @@
     rtp_packet_transport_->SignalReadyToSend.disconnect(this);
     rtp_packet_transport_->SignalReadPacket.disconnect(this);
     rtp_packet_transport_->SignalNetworkRouteChanged.disconnect(this);
+    rtp_packet_transport_->SignalWritableState.disconnect(this);
+    rtp_packet_transport_->SignalSentPacket.disconnect(this);
     // Reset the network route of the old transport.
     SignalNetworkRouteChanged(rtc::Optional<rtc::NetworkRoute>());
   }
@@ -43,6 +45,10 @@
                                                    &RtpTransport::OnReadPacket);
     new_packet_transport->SignalNetworkRouteChanged.connect(
         this, &RtpTransport::OnNetworkRouteChange);
+    new_packet_transport->SignalWritableState.connect(
+        this, &RtpTransport::OnWritableState);
+    new_packet_transport->SignalSentPacket.connect(this,
+                                                   &RtpTransport::OnSentPacket);
     // Set the network route for the new transport.
     SignalNetworkRouteChanged(new_packet_transport->network_route());
   }
@@ -63,6 +69,8 @@
     rtcp_packet_transport_->SignalReadyToSend.disconnect(this);
     rtcp_packet_transport_->SignalReadPacket.disconnect(this);
     rtcp_packet_transport_->SignalNetworkRouteChanged.disconnect(this);
+    rtcp_packet_transport_->SignalWritableState.disconnect(this);
+    rtcp_packet_transport_->SignalSentPacket.disconnect(this);
     // Reset the network route of the old transport.
     SignalNetworkRouteChanged(rtc::Optional<rtc::NetworkRoute>());
   }
@@ -73,6 +81,10 @@
                                                    &RtpTransport::OnReadPacket);
     new_packet_transport->SignalNetworkRouteChanged.connect(
         this, &RtpTransport::OnNetworkRouteChange);
+    new_packet_transport->SignalWritableState.connect(
+        this, &RtpTransport::OnWritableState);
+    new_packet_transport->SignalSentPacket.connect(this,
+                                                   &RtpTransport::OnSentPacket);
     // Set the network route for the new transport.
     SignalNetworkRouteChanged(new_packet_transport->network_route());
   }
@@ -172,6 +184,13 @@
   return nullptr;
 }
 
+bool RtpTransport::IsRtpTransportWritable() {
+  auto rtcp_packet_transport =
+      rtcp_mux_enabled_ ? nullptr : rtcp_packet_transport_;
+  return rtp_packet_transport_ && rtp_packet_transport_->writable() &&
+         (!rtcp_packet_transport || rtcp_packet_transport->writable());
+}
+
 void RtpTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) {
   SetReadyToSend(transport == rtcp_packet_transport_, true);
 }
@@ -181,6 +200,20 @@
   SignalNetworkRouteChanged(network_route);
 }
 
+void RtpTransport::OnWritableState(
+    rtc::PacketTransportInternal* packet_transport) {
+  RTC_DCHECK(packet_transport == rtp_packet_transport_ ||
+             packet_transport == rtcp_packet_transport_);
+  SignalWritableState(IsRtpTransportWritable());
+}
+
+void RtpTransport::OnSentPacket(rtc::PacketTransportInternal* packet_transport,
+                                const rtc::SentPacket& sent_packet) {
+  RTC_DCHECK(packet_transport == rtp_packet_transport_ ||
+             packet_transport == rtcp_packet_transport_);
+  SignalSentPacket(sent_packet);
+}
+
 void RtpTransport::SetReadyToSend(bool rtcp, bool ready) {
   if (rtcp) {
     rtcp_ready_to_send_ = ready;
@@ -226,7 +259,6 @@
   if (!WantsPacket(rtcp, &packet)) {
     return;
   }
-
   // This mutates |packet| if it is protected.
   SignalPacketReceived(rtcp, &packet, packet_time);
 }
diff --git a/pc/rtptransport.h b/pc/rtptransport.h
index a42493a..497a748 100644
--- a/pc/rtptransport.h
+++ b/pc/rtptransport.h
@@ -75,10 +75,14 @@
   RtpTransportAdapter* GetInternal() override;
 
  private:
+  bool IsRtpTransportWritable();
   bool HandlesPacket(const uint8_t* data, size_t len);
 
   void OnReadyToSend(rtc::PacketTransportInternal* transport);
   void OnNetworkRouteChange(rtc::Optional<rtc::NetworkRoute> network_route);
+  void OnWritableState(rtc::PacketTransportInternal* packet_transport);
+  void OnSentPacket(rtc::PacketTransportInternal* packet_transport,
+                    const rtc::SentPacket& sent_packet);
 
   // Updates "ready to send" for an individual channel and fires
   // SignalReadyToSend.
diff --git a/pc/rtptransportinternal.h b/pc/rtptransportinternal.h
index afede1e..2ad66ab 100644
--- a/pc/rtptransportinternal.h
+++ b/pc/rtptransportinternal.h
@@ -57,13 +57,15 @@
   sigslot::signal3<bool, rtc::CopyOnWriteBuffer*, const rtc::PacketTime&>
       SignalPacketReceived;
 
+  // Called whenever the network route of the P2P layer transport changes.
+  // The argument is an optional network route.
+  sigslot::signal1<rtc::Optional<rtc::NetworkRoute>> SignalNetworkRouteChanged;
+
   // Called whenever a transport's writable state might change. The argument is
   // true if the transport is writable, otherwise it is false.
   sigslot::signal1<bool> SignalWritableState;
 
-  // Called whenever the network route of the P2P layer transport changes.
-  // The argument is an optional network route.
-  sigslot::signal1<rtc::Optional<rtc::NetworkRoute>> SignalNetworkRouteChanged;
+  sigslot::signal1<const rtc::SentPacket&> SignalSentPacket;
 
   virtual bool IsWritable(bool rtcp) const = 0;
 
diff --git a/pc/srtptransport.cc b/pc/srtptransport.cc
index 98a3bea..32e5a00 100644
--- a/pc/srtptransport.cc
+++ b/pc/srtptransport.cc
@@ -51,6 +51,9 @@
                                             &SrtpTransport::OnReadyToSend);
   rtp_transport_->SignalNetworkRouteChanged.connect(
       this, &SrtpTransport::OnNetworkRouteChanged);
+  rtp_transport_->SignalWritableState.connect(this,
+                                              &SrtpTransport::OnWritableState);
+  rtp_transport_->SignalSentPacket.connect(this, &SrtpTransport::OnSentPacket);
 }
 
 bool SrtpTransport::SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
diff --git a/pc/srtptransport.h b/pc/srtptransport.h
index 8704351..23e2a3c 100644
--- a/pc/srtptransport.h
+++ b/pc/srtptransport.h
@@ -100,9 +100,8 @@
   }
 
  private:
-  void CreateSrtpSessions();
-
   void ConnectToRtpTransport();
+  void CreateSrtpSessions();
 
   bool SendPacket(bool rtcp,
                   rtc::CopyOnWriteBuffer* packet,
@@ -115,6 +114,12 @@
   void OnReadyToSend(bool ready) { SignalReadyToSend(ready); }
   void OnNetworkRouteChanged(rtc::Optional<rtc::NetworkRoute> network_route);
 
+  void OnWritableState(bool writable) { SignalWritableState(writable); }
+
+  void OnSentPacket(const rtc::SentPacket& sent_packet) {
+    SignalSentPacket(sent_packet);
+  }
+
   bool ProtectRtp(void* data, int in_len, int max_len, int* out_len);
 
   // Overloaded version, outputs packet index.