Created the DtlsSrtpTransport.

The DtlsSrtpTransport is designed to take DTLS responsibilities from BaseChannel.
DtlsSrtpTransport is responsible for exporting keys from DtlsTransport
and setting up the wrapped SrtpTransport.

The DtlsSrtpTransport is not hooked up to BaseChannel yet in this CL.

Bug: webrtc:7013
Change-Id: I318c00dadf9b1e033ec842de6e1536e9227ab713
Reviewed-on: https://webrtc-review.googlesource.com/6700
Commit-Queue: Zhi Huang <zhihuang@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20804}
diff --git a/media/base/fakertp.h b/media/base/fakertp.h
index 735da01..6786bab 100644
--- a/media/base/fakertp.h
+++ b/media/base/fakertp.h
@@ -42,6 +42,8 @@
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 };
 
+static const int kHeaderExtensionIDs[] = {1, 4};
+
 // A typical PCMU RTP packet with header extensions.
 // PT=0, SN=1, TS=0, SSRC=1
 // all data FF
diff --git a/p2p/base/fakedtlstransport.h b/p2p/base/fakedtlstransport.h
index 508158f..fef00fd 100644
--- a/p2p/base/fakedtlstransport.h
+++ b/p2p/base/fakedtlstransport.h
@@ -97,6 +97,7 @@
         dest->SetDestination(this, true);
       }
       dtls_state_ = DTLS_TRANSPORT_CONNECTED;
+      SignalDtlsState(this, dtls_state_);
       ice_transport_->SetDestination(
           static_cast<FakeIceTransport*>(dest->ice_transport()), asymmetric);
     } else {
diff --git a/p2p/base/fakeicetransport.h b/p2p/base/fakeicetransport.h
index cc6cec9..ea730c3 100644
--- a/p2p/base/fakeicetransport.h
+++ b/p2p/base/fakeicetransport.h
@@ -207,6 +207,8 @@
 
   int GetError() override { return 0; }
 
+  rtc::CopyOnWriteBuffer last_sent_packet() { return last_sent_packet_; }
+
   rtc::Optional<rtc::NetworkRoute> network_route() const override {
     return network_route_;
   }
@@ -237,6 +239,7 @@
 
   void SendPacketInternal(const rtc::CopyOnWriteBuffer& packet) {
     if (dest_) {
+      last_sent_packet_ = packet;
       dest_->SignalReadPacket(dest_, packet.data<char>(), packet.size(),
                               rtc::CreatePacketTime(0), 0);
     }
@@ -266,6 +269,7 @@
   rtc::CopyOnWriteBuffer send_packet_;
   rtc::Optional<rtc::NetworkRoute> network_route_;
   std::map<rtc::Socket::Option, int> socket_options_;
+  rtc::CopyOnWriteBuffer last_sent_packet_;
 };
 
 }  // namespace cricket
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 01c571c..1b40575 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -38,6 +38,8 @@
     "channelmanager.h",
     "currentspeakermonitor.cc",
     "currentspeakermonitor.h",
+    "dtlssrtptransport.cc",
+    "dtlssrtptransport.h",
     "externalhmac.cc",
     "externalhmac.h",
     "mediamonitor.cc",
@@ -49,6 +51,7 @@
     "rtptransport.cc",
     "rtptransport.h",
     "rtptransportinternal.h",
+    "rtptransportinternaladapter.h",
     "srtpfilter.cc",
     "srtpfilter.h",
     "srtpsession.cc",
@@ -250,6 +253,7 @@
       "channel_unittest.cc",
       "channelmanager_unittest.cc",
       "currentspeakermonitor_unittest.cc",
+      "dtlssrtptransport_unittest.cc",
       "mediasession_unittest.cc",
       "rtcpmuxfilter_unittest.cc",
       "rtptransport_unittest.cc",
diff --git a/pc/dtlssrtptransport.cc b/pc/dtlssrtptransport.cc
new file mode 100644
index 0000000..1c2d21f
--- /dev/null
+++ b/pc/dtlssrtptransport.cc
@@ -0,0 +1,341 @@
+/*
+ *  Copyright 2017 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 "pc/dtlssrtptransport.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "media/base/rtputils.h"
+#include "rtc_base/sslstreamadapter.h"
+
+namespace {
+// Value specified in RFC 5764.
+static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp";
+}  // namespace
+
+namespace webrtc {
+
+DtlsSrtpTransport::DtlsSrtpTransport(
+    std::unique_ptr<webrtc::SrtpTransport> srtp_transport)
+    : RtpTransportInternalAdapter(srtp_transport.get()) {
+  srtp_transport_ = std::move(srtp_transport);
+  RTC_DCHECK(srtp_transport_);
+  srtp_transport_->SignalPacketReceived.connect(
+      this, &DtlsSrtpTransport::OnPacketReceived);
+  srtp_transport_->SignalReadyToSend.connect(this,
+                                             &DtlsSrtpTransport::OnReadyToSend);
+}
+
+void DtlsSrtpTransport::SetDtlsTransports(
+    cricket::DtlsTransportInternal* rtp_dtls_transport,
+    cricket::DtlsTransportInternal* rtcp_dtls_transport) {
+  // Transport names should be the same.
+  if (rtp_dtls_transport && rtcp_dtls_transport) {
+    RTC_DCHECK(rtp_dtls_transport->transport_name() ==
+               rtcp_dtls_transport->transport_name());
+  }
+
+  // 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 (IsActive()) {
+    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.";
+
+    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);
+  }
+
+  RTC_LOG(LS_INFO) << "Setting RTP Transport on "
+                   << rtp_dtls_transport->transport_name() << " transport "
+                   << rtp_dtls_transport;
+  SetRtpDtlsTransport(rtp_dtls_transport);
+  SetRtpPacketTransport(rtp_dtls_transport);
+
+  UpdateWritableStateAndMaybeSetupDtlsSrtp();
+}
+
+void DtlsSrtpTransport::SetRtcpMuxEnabled(bool enable) {
+  srtp_transport_->SetRtcpMuxEnabled(enable);
+  if (enable) {
+    UpdateWritableStateAndMaybeSetupDtlsSrtp();
+  }
+}
+
+void DtlsSrtpTransport::SetSendEncryptedHeaderExtensionIds(
+    const std::vector<int>& send_extension_ids) {
+  send_extension_ids_.emplace(send_extension_ids);
+  // Reset the crypto parameters to update the send_extension IDs.
+  SetupRtpDtlsSrtp();
+}
+
+void DtlsSrtpTransport::SetRecvEncryptedHeaderExtensionIds(
+    const std::vector<int>& recv_extension_ids) {
+  recv_extension_ids_.emplace(recv_extension_ids);
+  // Reset the crypto parameters to update the send_extension IDs.
+  SetupRtpDtlsSrtp();
+}
+
+bool DtlsSrtpTransport::IsDtlsActive() {
+  auto rtcp_dtls_transport =
+      rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
+  return (rtp_dtls_transport_ && rtp_dtls_transport_->IsDtlsActive() &&
+          (!rtcp_dtls_transport || rtcp_dtls_transport->IsDtlsActive()));
+}
+
+bool DtlsSrtpTransport::IsDtlsConnected() {
+  auto rtcp_dtls_transport =
+      rtcp_mux_enabled() ? nullptr : rtcp_dtls_transport_;
+  return (rtp_dtls_transport_ &&
+          rtp_dtls_transport_->dtls_state() ==
+              cricket::DTLS_TRANSPORT_CONNECTED &&
+          (!rtcp_dtls_transport || rtcp_dtls_transport->dtls_state() ==
+                                       cricket::DTLS_TRANSPORT_CONNECTED));
+}
+
+bool DtlsSrtpTransport::IsDtlsWritable() {
+  auto rtp_packet_transport = srtp_transport_->rtp_packet_transport();
+  auto rtcp_packet_transport =
+      rtcp_mux_enabled() ? nullptr : srtp_transport_->rtcp_packet_transport();
+  return rtp_packet_transport && rtp_packet_transport->writable() &&
+         (!rtcp_packet_transport || rtcp_packet_transport->writable());
+}
+
+bool DtlsSrtpTransport::DtlsHandshakeCompleted() {
+  return IsDtlsActive() && IsDtlsConnected();
+}
+
+void DtlsSrtpTransport::MaybeSetupDtlsSrtp() {
+  if (IsActive() || !DtlsHandshakeCompleted()) {
+    return;
+  }
+
+  SetupRtpDtlsSrtp();
+
+  if (!rtcp_mux_enabled() && rtcp_dtls_transport_) {
+    SetupRtcpDtlsSrtp();
+  }
+}
+
+void DtlsSrtpTransport::SetupRtpDtlsSrtp() {
+  // 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 (send_extension_ids_) {
+    send_extension_ids = *send_extension_ids_;
+  }
+  if (recv_extension_ids_) {
+    recv_extension_ids = *recv_extension_ids_;
+  }
+
+  int selected_crypto_suite;
+  std::vector<unsigned char> send_key;
+  std::vector<unsigned char> recv_key;
+
+  if (!ExtractParams(rtp_dtls_transport_, &selected_crypto_suite, &send_key,
+                     &recv_key) ||
+      !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)) {
+    SignalDtlsSrtpSetupFailure(this, /*rtcp=*/false);
+    RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTP failed";
+  }
+}
+
+void DtlsSrtpTransport::SetupRtcpDtlsSrtp() {
+  // Return if the DTLS-SRTP is active because the encrypted header extension
+  // IDs don't need to be updated for RTCP and the crypto params don't need to
+  // be reset.
+  if (IsActive()) {
+    return;
+  }
+
+  std::vector<int> send_extension_ids;
+  std::vector<int> recv_extension_ids;
+  if (send_extension_ids_) {
+    send_extension_ids = *send_extension_ids_;
+  }
+  if (recv_extension_ids_) {
+    recv_extension_ids = *recv_extension_ids_;
+  }
+
+  int selected_crypto_suite;
+  std::vector<unsigned char> rtcp_send_key;
+  std::vector<unsigned char> rtcp_recv_key;
+  if (!ExtractParams(rtcp_dtls_transport_, &selected_crypto_suite,
+                     &rtcp_send_key, &rtcp_recv_key) ||
+      !srtp_transport_->SetRtcpParams(
+          selected_crypto_suite, &rtcp_send_key[0],
+          static_cast<int>(rtcp_send_key.size()), send_extension_ids,
+          selected_crypto_suite, &rtcp_recv_key[0],
+          static_cast<int>(rtcp_recv_key.size()), recv_extension_ids)) {
+    SignalDtlsSrtpSetupFailure(this, /*rtcp=*/true);
+    RTC_LOG(LS_WARNING) << "DTLS-SRTP key installation for RTCP failed";
+  }
+}
+
+bool DtlsSrtpTransport::ExtractParams(
+    cricket::DtlsTransportInternal* dtls_transport,
+    int* selected_crypto_suite,
+    std::vector<unsigned char>* send_key,
+    std::vector<unsigned char>* recv_key) {
+  if (!dtls_transport || !dtls_transport->IsDtlsActive()) {
+    return false;
+  }
+
+  if (!dtls_transport->GetSrtpCryptoSuite(selected_crypto_suite)) {
+    RTC_LOG(LS_ERROR) << "No DTLS-SRTP selected crypto suite";
+    return false;
+  }
+
+  RTC_LOG(LS_INFO) << "Extracting keys from transport: "
+                   << dtls_transport->transport_name();
+
+  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 (!dtls_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);
+
+  rtc::SSLRole role;
+  if (!dtls_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;
+  }
+
+  return true;
+}
+
+void DtlsSrtpTransport::SetDtlsTransport(
+    cricket::DtlsTransportInternal* new_dtls_transport,
+    cricket::DtlsTransportInternal** old_dtls_transport) {
+  if (*old_dtls_transport) {
+    (*old_dtls_transport)->SignalDtlsState.disconnect(this);
+    (*old_dtls_transport)->SignalWritableState.disconnect(this);
+  }
+
+  *old_dtls_transport = new_dtls_transport;
+
+  if (new_dtls_transport) {
+    new_dtls_transport->SignalDtlsState.connect(
+        this, &DtlsSrtpTransport::OnDtlsState);
+    new_dtls_transport->SignalWritableState.connect(
+        this, &DtlsSrtpTransport::OnWritableState);
+  }
+}
+
+void DtlsSrtpTransport::SetRtpDtlsTransport(
+    cricket::DtlsTransportInternal* rtp_dtls_transport) {
+  SetDtlsTransport(rtp_dtls_transport, &rtp_dtls_transport_);
+}
+
+void DtlsSrtpTransport::SetRtcpDtlsTransport(
+    cricket::DtlsTransportInternal* rtcp_dtls_transport) {
+  SetDtlsTransport(rtcp_dtls_transport, &rtcp_dtls_transport_);
+}
+
+void DtlsSrtpTransport::UpdateWritableStateAndMaybeSetupDtlsSrtp() {
+  bool writable = IsDtlsWritable();
+  SetWritable(writable);
+  if (writable) {
+    MaybeSetupDtlsSrtp();
+  }
+}
+
+void DtlsSrtpTransport::SetWritable(bool writable) {
+  // Only fire the signal if the writable state changes.
+  if (writable_ != writable) {
+    writable_ = writable;
+    SignalWritableState(writable_);
+  }
+}
+
+void DtlsSrtpTransport::OnDtlsState(cricket::DtlsTransportInternal* transport,
+                                    cricket::DtlsTransportState state) {
+  RTC_DCHECK(transport == rtp_dtls_transport_ ||
+             transport == rtcp_dtls_transport_);
+
+  if (state != cricket::DTLS_TRANSPORT_CONNECTED) {
+    srtp_transport_->ResetParams();
+    return;
+  }
+
+  MaybeSetupDtlsSrtp();
+}
+
+void DtlsSrtpTransport::OnWritableState(
+    rtc::PacketTransportInternal* transport) {
+  RTC_DCHECK(transport == srtp_transport_->rtp_packet_transport() ||
+             transport == srtp_transport_->rtcp_packet_transport());
+  UpdateWritableStateAndMaybeSetupDtlsSrtp();
+}
+
+void DtlsSrtpTransport::OnPacketReceived(bool rtcp,
+                                         rtc::CopyOnWriteBuffer* packet,
+                                         const rtc::PacketTime& packet_time) {
+  SignalPacketReceived(rtcp, packet, packet_time);
+}
+
+void DtlsSrtpTransport::OnReadyToSend(bool ready) {
+  SignalReadyToSend(ready);
+}
+
+}  // namespace webrtc
diff --git a/pc/dtlssrtptransport.h b/pc/dtlssrtptransport.h
new file mode 100644
index 0000000..43f072a
--- /dev/null
+++ b/pc/dtlssrtptransport.h
@@ -0,0 +1,97 @@
+/*
+ *  Copyright 2017 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 PC_DTLSSRTPTRANSPORT_H_
+#define PC_DTLSSRTPTRANSPORT_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "p2p/base/dtlstransportinternal.h"
+#include "pc/rtptransportinternaladapter.h"
+#include "pc/srtptransport.h"
+
+namespace webrtc {
+
+// This class is intended to be used as an RtpTransport and it wraps both an
+// SrtpTransport and DtlsTransports(RTP/RTCP). When the DTLS handshake is
+// finished, it extracts the keying materials from DtlsTransport and sets them
+// to SrtpTransport.
+class DtlsSrtpTransport : public RtpTransportInternalAdapter {
+ public:
+  explicit DtlsSrtpTransport(
+      std::unique_ptr<webrtc::SrtpTransport> srtp_transport);
+
+  // Set P2P layer RTP/RTCP DtlsTransports. When using RTCP-muxing,
+  // |rtcp_dtls_transport| is null.
+  void SetDtlsTransports(cricket::DtlsTransportInternal* rtp_dtls_transport,
+                         cricket::DtlsTransportInternal* rtcp_dtls_transport);
+
+  void SetRtcpMuxEnabled(bool enable) override;
+
+  // Set the header extension ids that should be encrypted.
+  void SetSendEncryptedHeaderExtensionIds(
+      const std::vector<int>& send_extension_ids);
+
+  void SetRecvEncryptedHeaderExtensionIds(
+      const std::vector<int>& recv_extension_ids);
+
+  bool IsActive() { return srtp_transport_->IsActive(); }
+
+  // TODO(zhihuang): Remove this when we remove RtpTransportAdapter.
+  RtpTransportAdapter* GetInternal() override { return nullptr; }
+
+  sigslot::signal2<DtlsSrtpTransport*, bool> SignalDtlsSrtpSetupFailure;
+
+ private:
+  bool IsDtlsActive();
+  bool IsDtlsConnected();
+  bool IsDtlsWritable();
+  bool DtlsHandshakeCompleted();
+  void MaybeSetupDtlsSrtp();
+  void SetupRtpDtlsSrtp();
+  void SetupRtcpDtlsSrtp();
+  bool ExtractParams(cricket::DtlsTransportInternal* dtls_transport,
+                     int* selected_crypto_suite,
+                     std::vector<unsigned char>* send_key,
+                     std::vector<unsigned char>* recv_key);
+  void SetDtlsTransport(cricket::DtlsTransportInternal* new_dtls_transport,
+                        cricket::DtlsTransportInternal** old_dtls_transport);
+  void SetRtpDtlsTransport(cricket::DtlsTransportInternal* rtp_dtls_transport);
+  void SetRtcpDtlsTransport(
+      cricket::DtlsTransportInternal* rtcp_dtls_transport);
+  void UpdateWritableStateAndMaybeSetupDtlsSrtp();
+  // Set the writability and fire the SignalWritableState if the writability
+  // changes.
+  void SetWritable(bool writable);
+
+  void OnDtlsState(cricket::DtlsTransportInternal* dtls_transport,
+                   cricket::DtlsTransportState state);
+  void OnWritableState(rtc::PacketTransportInternal* transport);
+  void OnPacketReceived(bool rtcp,
+                        rtc::CopyOnWriteBuffer* packet,
+                        const rtc::PacketTime& packet_time);
+  void OnReadyToSend(bool ready);
+
+  bool writable_ = false;
+  std::unique_ptr<SrtpTransport> srtp_transport_;
+  // Owned by the TransportController.
+  cricket::DtlsTransportInternal* rtp_dtls_transport_ = nullptr;
+  cricket::DtlsTransportInternal* rtcp_dtls_transport_ = nullptr;
+
+  // The encrypted header extension IDs.
+  rtc::Optional<std::vector<int>> send_extension_ids_;
+  rtc::Optional<std::vector<int>> recv_extension_ids_;
+};
+
+}  // namespace webrtc
+
+#endif  // PC_DTLSSRTPTRANSPORT_H_
diff --git a/pc/dtlssrtptransport_unittest.cc b/pc/dtlssrtptransport_unittest.cc
new file mode 100644
index 0000000..7af9870
--- /dev/null
+++ b/pc/dtlssrtptransport_unittest.cc
@@ -0,0 +1,486 @@
+/*
+ *  Copyright 2017 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 "pc/dtlssrtptransport.h"
+
+#include <memory>
+#include <utility>
+
+#include "media/base/fakertp.h"
+#include "p2p/base/dtlstransportinternal.h"
+#include "p2p/base/fakedtlstransport.h"
+#include "p2p/base/fakepackettransport.h"
+#include "p2p/base/p2pconstants.h"
+#include "pc/rtptransport.h"
+#include "pc/rtptransporttestutil.h"
+#include "rtc_base/asyncpacketsocket.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/ptr_util.h"
+#include "rtc_base/sslstreamadapter.h"
+
+using cricket::FakeDtlsTransport;
+using cricket::FakeIceTransport;
+using webrtc::DtlsSrtpTransport;
+using webrtc::SrtpTransport;
+using webrtc::RtpTransport;
+
+const int kRtpAuthTagLen = 10;
+
+class TransportObserver : public sigslot::has_slots<> {
+ public:
+  void OnPacketReceived(bool rtcp,
+                        rtc::CopyOnWriteBuffer* packet,
+                        const rtc::PacketTime& packet_time) {
+    rtcp ? last_recv_rtcp_packet_ = *packet : last_recv_rtp_packet_ = *packet;
+  }
+
+  void OnReadyToSend(bool ready) { ready_to_send_ = ready; }
+
+  rtc::CopyOnWriteBuffer last_recv_rtp_packet() {
+    return last_recv_rtp_packet_;
+  }
+
+  rtc::CopyOnWriteBuffer last_recv_rtcp_packet() {
+    return last_recv_rtcp_packet_;
+  }
+
+  bool ready_to_send() { return ready_to_send_; }
+
+ private:
+  rtc::CopyOnWriteBuffer last_recv_rtp_packet_;
+  rtc::CopyOnWriteBuffer last_recv_rtcp_packet_;
+  bool ready_to_send_ = false;
+};
+
+class DtlsSrtpTransportTest : public testing::Test,
+                              public sigslot::has_slots<> {
+ protected:
+  DtlsSrtpTransportTest() {}
+
+  std::unique_ptr<DtlsSrtpTransport> MakeDtlsSrtpTransport(
+      FakeDtlsTransport* rtp_dtls,
+      FakeDtlsTransport* rtcp_dtls,
+      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);
+
+    auto srtp_transport =
+        rtc::MakeUnique<SrtpTransport>(std::move(rtp_transport), "content");
+    auto dtls_srtp_transport =
+        rtc::MakeUnique<DtlsSrtpTransport>(std::move(srtp_transport));
+
+    dtls_srtp_transport->SetDtlsTransports(rtp_dtls, rtcp_dtls);
+
+    return dtls_srtp_transport;
+  }
+
+  void MakeDtlsSrtpTransports(FakeDtlsTransport* rtp_dtls1,
+                              FakeDtlsTransport* rtcp_dtls1,
+                              FakeDtlsTransport* rtp_dtls2,
+                              FakeDtlsTransport* rtcp_dtls2,
+                              bool rtcp_mux_enabled) {
+    dtls_srtp_transport1_ =
+        MakeDtlsSrtpTransport(rtp_dtls1, rtcp_dtls1, rtcp_mux_enabled);
+    dtls_srtp_transport2_ =
+        MakeDtlsSrtpTransport(rtp_dtls2, rtcp_dtls2, rtcp_mux_enabled);
+
+    dtls_srtp_transport1_->SignalPacketReceived.connect(
+        &transport_observer1_, &TransportObserver::OnPacketReceived);
+    dtls_srtp_transport1_->SignalReadyToSend.connect(
+        &transport_observer1_, &TransportObserver::OnReadyToSend);
+
+    dtls_srtp_transport2_->SignalPacketReceived.connect(
+        &transport_observer2_, &TransportObserver::OnPacketReceived);
+    dtls_srtp_transport2_->SignalReadyToSend.connect(
+        &transport_observer2_, &TransportObserver::OnReadyToSend);
+  }
+
+  void CompleteDtlsHandshake(FakeDtlsTransport* fake_dtls1,
+                             FakeDtlsTransport* fake_dtls2) {
+    auto cert1 = rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
+        rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
+    fake_dtls1->SetLocalCertificate(cert1);
+    auto cert2 = rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
+        rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
+    fake_dtls2->SetLocalCertificate(cert2);
+    fake_dtls1->SetDestination(fake_dtls2);
+  }
+
+  void SendRecvRtpPackets() {
+    ASSERT_TRUE(dtls_srtp_transport1_);
+    ASSERT_TRUE(dtls_srtp_transport2_);
+    ASSERT_TRUE(dtls_srtp_transport1_->IsActive());
+    ASSERT_TRUE(dtls_srtp_transport2_->IsActive());
+
+    size_t rtp_len = sizeof(kPcmuFrame);
+    size_t packet_size = rtp_len + kRtpAuthTagLen;
+    rtc::Buffer rtp_packet_buffer(packet_size);
+    char* rtp_packet_data = rtp_packet_buffer.data<char>();
+    memcpy(rtp_packet_data, kPcmuFrame, rtp_len);
+    // In order to be able to run this test function multiple times we can not
+    // use the same sequence number twice. Increase the sequence number by one.
+    rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet_data) + 2,
+                 ++sequence_number_);
+    rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len,
+                                          packet_size);
+    rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len,
+                                          packet_size);
+
+    rtc::PacketOptions options;
+    // Send a packet from |srtp_transport1_| to |srtp_transport2_| and verify
+    // that the packet can be successfully received and decrypted.
+    ASSERT_TRUE(dtls_srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options,
+                                                     cricket::PF_SRTP_BYPASS));
+    ASSERT_TRUE(transport_observer2_.last_recv_rtp_packet().data());
+    EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtp_packet().data(),
+                        kPcmuFrame, rtp_len));
+    ASSERT_TRUE(dtls_srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options,
+                                                     cricket::PF_SRTP_BYPASS));
+    ASSERT_TRUE(transport_observer1_.last_recv_rtp_packet().data());
+    EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtp_packet().data(),
+                        kPcmuFrame, rtp_len));
+  }
+
+  void SendRecvRtcpPackets() {
+    size_t rtcp_len = sizeof(kRtcpReport);
+    size_t packet_size = rtcp_len + 4 + kRtpAuthTagLen;
+    rtc::Buffer rtcp_packet_buffer(packet_size);
+
+    // TODO(zhihuang): Remove the extra copy when the SendRtpPacket method
+    // doesn't take the CopyOnWriteBuffer by pointer.
+    rtc::CopyOnWriteBuffer rtcp_packet1to2(kRtcpReport, rtcp_len, packet_size);
+    rtc::CopyOnWriteBuffer rtcp_packet2to1(kRtcpReport, rtcp_len, packet_size);
+
+    rtc::PacketOptions options;
+    // Send a packet from |srtp_transport1_| to |srtp_transport2_| and verify
+    // that the packet can be successfully received and decrypted.
+    ASSERT_TRUE(dtls_srtp_transport1_->SendRtcpPacket(&rtcp_packet1to2, options,
+                                                      cricket::PF_SRTP_BYPASS));
+    ASSERT_TRUE(transport_observer2_.last_recv_rtcp_packet().data());
+    EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtcp_packet().data(),
+                        kRtcpReport, rtcp_len));
+
+    // Do the same thing in the opposite direction;
+    ASSERT_TRUE(dtls_srtp_transport2_->SendRtcpPacket(&rtcp_packet2to1, options,
+                                                      cricket::PF_SRTP_BYPASS));
+    ASSERT_TRUE(transport_observer1_.last_recv_rtcp_packet().data());
+    EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtcp_packet().data(),
+                        kRtcpReport, rtcp_len));
+  }
+
+  void SendRecvRtpPacketsWithHeaderExtension(
+      const std::vector<int>& encrypted_header_ids) {
+    ASSERT_TRUE(dtls_srtp_transport1_);
+    ASSERT_TRUE(dtls_srtp_transport2_);
+    ASSERT_TRUE(dtls_srtp_transport1_->IsActive());
+    ASSERT_TRUE(dtls_srtp_transport2_->IsActive());
+
+    size_t rtp_len = sizeof(kPcmuFrameWithExtensions);
+    size_t packet_size = rtp_len + kRtpAuthTagLen;
+    rtc::Buffer rtp_packet_buffer(packet_size);
+    char* rtp_packet_data = rtp_packet_buffer.data<char>();
+    memcpy(rtp_packet_data, kPcmuFrameWithExtensions, rtp_len);
+    // In order to be able to run this test function multiple times we can not
+    // use the same sequence number twice. Increase the sequence number by one.
+    rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet_data) + 2,
+                 ++sequence_number_);
+    rtc::CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len,
+                                          packet_size);
+    rtc::CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len,
+                                          packet_size);
+
+    char original_rtp_data[sizeof(kPcmuFrameWithExtensions)];
+    memcpy(original_rtp_data, rtp_packet_data, rtp_len);
+
+    rtc::PacketOptions options;
+    // Send a packet from |srtp_transport1_| to |srtp_transport2_| and verify
+    // that the packet can be successfully received and decrypted.
+    ASSERT_TRUE(dtls_srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options,
+                                                     cricket::PF_SRTP_BYPASS));
+    ASSERT_TRUE(transport_observer2_.last_recv_rtp_packet().data());
+    EXPECT_EQ(0, memcmp(transport_observer2_.last_recv_rtp_packet().data(),
+                        original_rtp_data, rtp_len));
+    // Get the encrypted packet from underneath packet transport and verify the
+    // data and header extension are actually encrypted.
+    auto fake_dtls_transport = static_cast<FakeDtlsTransport*>(
+        dtls_srtp_transport1_->rtp_packet_transport());
+    auto fake_ice_transport =
+        static_cast<FakeIceTransport*>(fake_dtls_transport->ice_transport());
+    EXPECT_NE(0, memcmp(fake_ice_transport->last_sent_packet().data(),
+                        original_rtp_data, rtp_len));
+    CompareHeaderExtensions(reinterpret_cast<const char*>(
+                                fake_ice_transport->last_sent_packet().data()),
+                            fake_ice_transport->last_sent_packet().size(),
+                            original_rtp_data, rtp_len, encrypted_header_ids,
+                            false);
+
+    // Do the same thing in the opposite direction.
+    ASSERT_TRUE(dtls_srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options,
+                                                     cricket::PF_SRTP_BYPASS));
+    ASSERT_TRUE(transport_observer1_.last_recv_rtp_packet().data());
+    EXPECT_EQ(0, memcmp(transport_observer1_.last_recv_rtp_packet().data(),
+                        original_rtp_data, rtp_len));
+    // Get the encrypted packet from underneath packet transport and verify the
+    // data and header extension are actually encrypted.
+    fake_dtls_transport = static_cast<FakeDtlsTransport*>(
+        dtls_srtp_transport2_->rtp_packet_transport());
+    fake_ice_transport =
+        static_cast<FakeIceTransport*>(fake_dtls_transport->ice_transport());
+    EXPECT_NE(0, memcmp(fake_ice_transport->last_sent_packet().data(),
+                        original_rtp_data, rtp_len));
+    CompareHeaderExtensions(reinterpret_cast<const char*>(
+                                fake_ice_transport->last_sent_packet().data()),
+                            fake_ice_transport->last_sent_packet().size(),
+                            original_rtp_data, rtp_len, encrypted_header_ids,
+                            false);
+  }
+
+  void SendRecvPackets() {
+    SendRecvRtpPackets();
+    SendRecvRtcpPackets();
+  }
+
+  std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport1_;
+  std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport2_;
+  TransportObserver transport_observer1_;
+  TransportObserver transport_observer2_;
+
+  int sequence_number_ = 0;
+};
+
+// Tests that if RTCP muxing is enabled and transports are set after RTP
+// transport finished the handshake, SRTP is set up.
+TEST_F(DtlsSrtpTransportTest, SetTransportsAfterHandshakeCompleteWithRtcpMux) {
+  auto rtp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+  MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
+                         /*rtcp_mux_enabled=*/true);
+
+  auto rtp_dtls3 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtp_dtls4 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+  CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
+
+  dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), nullptr);
+  dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), nullptr);
+
+  SendRecvPackets();
+}
+
+// Tests that if RTCP muxing is not enabled and transports are set after both
+// RTP and RTCP transports finished the handshake, SRTP is set up.
+TEST_F(DtlsSrtpTransportTest,
+       SetTransportsAfterHandshakeCompleteWithoutRtcpMux) {
+  auto rtp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "video", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+  auto rtp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "video", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "video", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+  MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+                         rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
+
+  auto rtp_dtls3 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls3 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+  auto rtp_dtls4 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls4 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+  CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
+  CompleteDtlsHandshake(rtcp_dtls3.get(), rtcp_dtls4.get());
+
+  dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), rtcp_dtls3.get());
+  dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), rtcp_dtls4.get());
+
+  SendRecvPackets();
+}
+
+// Tests if RTCP muxing is enabled, SRTP is set up as soon as the RTP DTLS
+// handshake is finished.
+TEST_F(DtlsSrtpTransportTest, SetTransportsBeforeHandshakeCompleteWithRtcpMux) {
+  auto rtp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+  auto rtp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+  MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+                         rtcp_dtls2.get(),
+                         /*rtcp_mux_enabled=*/false);
+
+  dtls_srtp_transport1_->SetRtcpMuxEnabled(true);
+  dtls_srtp_transport2_->SetRtcpMuxEnabled(true);
+  CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+  SendRecvPackets();
+}
+
+// Tests if RTCP muxing is not enabled, SRTP is set up when both the RTP and
+// RTCP DTLS handshake are finished.
+TEST_F(DtlsSrtpTransportTest,
+       SetTransportsBeforeHandshakeCompleteWithoutRtcpMux) {
+  auto rtp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+  auto rtp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+  MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+                         rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
+
+  CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+  EXPECT_FALSE(dtls_srtp_transport1_->IsActive());
+  EXPECT_FALSE(dtls_srtp_transport2_->IsActive());
+  CompleteDtlsHandshake(rtcp_dtls1.get(), rtcp_dtls2.get());
+  SendRecvPackets();
+}
+
+// Tests that if the DtlsTransport underneath is changed, the previous DTLS-SRTP
+// context will be reset and will be re-setup once the new transports' handshake
+// complete.
+TEST_F(DtlsSrtpTransportTest, DtlsSrtpResetAfterDtlsTransportChange) {
+  auto rtp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+  MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
+                         /*rtcp_mux_enabled=*/true);
+
+  CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+  EXPECT_TRUE(dtls_srtp_transport1_->IsActive());
+  EXPECT_TRUE(dtls_srtp_transport2_->IsActive());
+
+  auto rtp_dtls3 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtp_dtls4 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+  // The previous context is reset.
+  dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), nullptr);
+  dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), nullptr);
+  EXPECT_FALSE(dtls_srtp_transport1_->IsActive());
+  EXPECT_FALSE(dtls_srtp_transport2_->IsActive());
+
+  // Re-setup.
+  CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
+  SendRecvPackets();
+}
+
+// Tests if only the RTP DTLS handshake complete, and then RTCP muxing is
+// enabled, SRTP is set up.
+TEST_F(DtlsSrtpTransportTest,
+       RtcpMuxEnabledAfterRtpTransportHandshakeComplete) {
+  auto rtp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+  auto rtp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+  MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+                         rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
+
+  CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+  // Inactive because the RTCP transport handshake didn't complete.
+  EXPECT_FALSE(dtls_srtp_transport1_->IsActive());
+  EXPECT_FALSE(dtls_srtp_transport2_->IsActive());
+
+  dtls_srtp_transport1_->SetRtcpMuxEnabled(true);
+  dtls_srtp_transport2_->SetRtcpMuxEnabled(true);
+  // The transports should be active and be able to send packets when the
+  // RTCP muxing is enabled.
+  SendRecvPackets();
+}
+
+// Tests that when SetSend/RecvEncryptedHeaderExtensionIds is called, the SRTP
+// sessions are updated with new encryped header extension IDs immediately.
+TEST_F(DtlsSrtpTransportTest, EncryptedHeaderExtensionIdUpdated) {
+  auto rtp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+  MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
+                         /*rtcp_mux_enabled=*/true);
+  CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
+
+  std::vector<int> encrypted_headers;
+  encrypted_headers.push_back(kHeaderExtensionIDs[0]);
+  encrypted_headers.push_back(kHeaderExtensionIDs[1]);
+  RTC_LOG(INFO) << "here";
+
+  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);
+}
+
+// Tests if RTCP muxing is enabled. DtlsSrtpTransport is ready to send once the
+// RTP DtlsTransport is ready.
+TEST_F(DtlsSrtpTransportTest, SignalReadyToSendFiredWithRtcpMux) {
+  auto rtp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+
+  MakeDtlsSrtpTransports(rtp_dtls1.get(), nullptr, rtp_dtls2.get(), nullptr,
+                         /*rtcp_mux_enabled=*/true);
+
+  rtp_dtls1->SetDestination(rtp_dtls2.get());
+  EXPECT_TRUE(transport_observer1_.ready_to_send());
+  EXPECT_TRUE(transport_observer2_.ready_to_send());
+}
+
+// Tests if RTCP muxing is not enabled. DtlsSrtpTransport is ready to send once
+// both the RTP and RTCP DtlsTransport are ready.
+TEST_F(DtlsSrtpTransportTest, SignalReadyToSendFiredWithoutRtcpMux) {
+  auto rtp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls1 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+  auto rtp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  auto rtcp_dtls2 = rtc::MakeUnique<FakeDtlsTransport>(
+      "audio", cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+
+  MakeDtlsSrtpTransports(rtp_dtls1.get(), rtcp_dtls1.get(), rtp_dtls2.get(),
+                         rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
+
+  rtp_dtls1->SetDestination(rtp_dtls2.get());
+  EXPECT_FALSE(transport_observer1_.ready_to_send());
+  EXPECT_FALSE(transport_observer2_.ready_to_send());
+
+  rtcp_dtls1->SetDestination(rtcp_dtls2.get());
+  EXPECT_TRUE(transport_observer1_.ready_to_send());
+  EXPECT_TRUE(transport_observer2_.ready_to_send());
+}
diff --git a/pc/rtptransport.h b/pc/rtptransport.h
index 34ec1b4..a42493a 100644
--- a/pc/rtptransport.h
+++ b/pc/rtptransport.h
@@ -36,7 +36,7 @@
   explicit RtpTransport(bool rtcp_mux_enabled)
       : rtcp_mux_enabled_(rtcp_mux_enabled) {}
 
-  bool rtcp_mux_enabled() const { return rtcp_mux_enabled_; }
+  bool rtcp_mux_enabled() const override { return rtcp_mux_enabled_; }
   void SetRtcpMuxEnabled(bool enable) override;
 
   rtc::PacketTransportInternal* rtp_packet_transport() const override {
diff --git a/pc/rtptransportinternal.h b/pc/rtptransportinternal.h
index 04d2ef3..afede1e 100644
--- a/pc/rtptransportinternal.h
+++ b/pc/rtptransportinternal.h
@@ -38,6 +38,7 @@
   // TODO(zstein): Remove PacketTransport setters. Clients should pass these
   // in to constructors instead and construct a new RtpTransportInternal instead
   // of updating them.
+  virtual bool rtcp_mux_enabled() const = 0;
 
   virtual rtc::PacketTransportInternal* rtp_packet_transport() const = 0;
   virtual void SetRtpPacketTransport(rtc::PacketTransportInternal* rtp) = 0;
@@ -56,12 +57,18 @@
   sigslot::signal3<bool, rtc::CopyOnWriteBuffer*, const rtc::PacketTime&>
       SignalPacketReceived;
 
+  // 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;
 
   virtual bool IsWritable(bool rtcp) const = 0;
 
+  // TODO(zhihuang): Pass the |packet| by copy so that the original data
+  // wouldn't be modified.
   virtual bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
                              const rtc::PacketOptions& options,
                              int flags) = 0;
diff --git a/pc/rtptransportinternaladapter.h b/pc/rtptransportinternaladapter.h
new file mode 100644
index 0000000..402f9cf
--- /dev/null
+++ b/pc/rtptransportinternaladapter.h
@@ -0,0 +1,102 @@
+/*
+ *  Copyright 2017 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 PC_RTPTRANSPORTINTERNALADAPTER_H_
+#define PC_RTPTRANSPORTINTERNALADAPTER_H_
+
+#include <memory>
+#include <utility>
+
+#include "pc/rtptransportinternal.h"
+
+namespace webrtc {
+
+// This class is used by SrtpTransport and DtlsSrtpTransport in order to reduce
+// the duplicated code. Using this class, different subclasses can override only
+// part of RtpTransportInternal methods without implementing all the common
+// methods.
+class RtpTransportInternalAdapter : public RtpTransportInternal {
+ public:
+  explicit RtpTransportInternalAdapter(RtpTransportInternal* transport)
+      : transport_(transport) {
+    RTC_DCHECK(transport_);
+  }
+
+  void SetRtcpMuxEnabled(bool enable) override {
+    transport_->SetRtcpMuxEnabled(enable);
+  }
+
+  bool rtcp_mux_enabled() const override {
+    return transport_->rtcp_mux_enabled();
+  }
+
+  rtc::PacketTransportInternal* rtp_packet_transport() const override {
+    return transport_->rtp_packet_transport();
+  }
+  void SetRtpPacketTransport(rtc::PacketTransportInternal* rtp) override {
+    transport_->SetRtpPacketTransport(rtp);
+  }
+
+  rtc::PacketTransportInternal* rtcp_packet_transport() const override {
+    return transport_->rtcp_packet_transport();
+  }
+  void SetRtcpPacketTransport(rtc::PacketTransportInternal* rtcp) override {
+    transport_->SetRtcpPacketTransport(rtcp);
+  }
+
+  bool IsWritable(bool rtcp) const override {
+    return transport_->IsWritable(rtcp);
+  }
+
+  bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
+                     const rtc::PacketOptions& options,
+                     int flags) override {
+    return transport_->SendRtpPacket(packet, options, flags);
+  }
+
+  bool SendRtcpPacket(rtc::CopyOnWriteBuffer* packet,
+                      const rtc::PacketOptions& options,
+                      int flags) override {
+    return transport_->SendRtcpPacket(packet, options, flags);
+  }
+
+  bool HandlesPayloadType(int payload_type) const override {
+    return transport_->HandlesPayloadType(payload_type);
+  }
+
+  void AddHandledPayloadType(int payload_type) override {
+    return transport_->AddHandledPayloadType(payload_type);
+  }
+
+  // RtpTransportInterface overrides.
+  PacketTransportInterface* GetRtpPacketTransport() const override {
+    return transport_->GetRtpPacketTransport();
+  }
+
+  PacketTransportInterface* GetRtcpPacketTransport() const override {
+    return transport_->GetRtcpPacketTransport();
+  }
+
+  RTCError SetParameters(const RtpTransportParameters& parameters) override {
+    return transport_->SetParameters(parameters);
+  }
+
+  RtpTransportParameters GetParameters() const override {
+    return transport_->GetParameters();
+  }
+
+ protected:
+  // Owned by the subclasses.
+  RtpTransportInternal* transport_;
+};
+
+}  // namespace webrtc
+
+#endif  // PC_RTPTRANSPORTINTERNALADAPTER_H_
diff --git a/pc/srtptransport.cc b/pc/srtptransport.cc
index bb42ad4..98a3bea 100644
--- a/pc/srtptransport.cc
+++ b/pc/srtptransport.cc
@@ -26,14 +26,21 @@
 
 SrtpTransport::SrtpTransport(bool rtcp_mux_enabled,
                              const std::string& content_name)
-    : content_name_(content_name),
-      rtp_transport_(rtc::MakeUnique<RtpTransport>(rtcp_mux_enabled)) {
+    : RtpTransportInternalAdapter(new RtpTransport(rtcp_mux_enabled)),
+      content_name_(content_name) {
+  // Own the raw pointer |transport| from the base class.
+  rtp_transport_.reset(transport_);
+  RTC_DCHECK(rtp_transport_);
   ConnectToRtpTransport();
 }
 
-SrtpTransport::SrtpTransport(std::unique_ptr<RtpTransportInternal> transport,
-                             const std::string& content_name)
-    : content_name_(content_name), rtp_transport_(std::move(transport)) {
+SrtpTransport::SrtpTransport(
+    std::unique_ptr<RtpTransportInternal> rtp_transport,
+    const std::string& content_name)
+    : RtpTransportInternalAdapter(rtp_transport.get()),
+      content_name_(content_name),
+      rtp_transport_(std::move(rtp_transport)) {
+  RTC_DCHECK(rtp_transport_);
   ConnectToRtpTransport();
 }
 
diff --git a/pc/srtptransport.h b/pc/srtptransport.h
index 919d4b9..8704351 100644
--- a/pc/srtptransport.h
+++ b/pc/srtptransport.h
@@ -17,7 +17,7 @@
 #include <vector>
 
 #include "p2p/base/icetransportinternal.h"
-#include "pc/rtptransportinternal.h"
+#include "pc/rtptransportinternaladapter.h"
 #include "pc/srtpfilter.h"
 #include "pc/srtpsession.h"
 #include "rtc_base/checks.h"
@@ -26,40 +26,13 @@
 
 // This class will eventually be a wrapper around RtpTransportInternal
 // that protects and unprotects sent and received RTP packets.
-class SrtpTransport : public RtpTransportInternal {
+class SrtpTransport : public RtpTransportInternalAdapter {
  public:
   SrtpTransport(bool rtcp_mux_enabled, const std::string& content_name);
 
-  SrtpTransport(std::unique_ptr<RtpTransportInternal> transport,
+  SrtpTransport(std::unique_ptr<RtpTransportInternal> rtp_transport,
                 const std::string& content_name);
 
-  void SetRtcpMuxEnabled(bool enable) override {
-    rtp_transport_->SetRtcpMuxEnabled(enable);
-  }
-
-  rtc::PacketTransportInternal* rtp_packet_transport() const override {
-    return rtp_transport_->rtp_packet_transport();
-  }
-
-  void SetRtpPacketTransport(rtc::PacketTransportInternal* rtp) override {
-    rtp_transport_->SetRtpPacketTransport(rtp);
-  }
-
-  PacketTransportInterface* GetRtpPacketTransport() const override {
-    return rtp_transport_->GetRtpPacketTransport();
-  }
-
-  rtc::PacketTransportInternal* rtcp_packet_transport() const override {
-    return rtp_transport_->rtcp_packet_transport();
-  }
-  void SetRtcpPacketTransport(rtc::PacketTransportInternal* rtcp) override {
-    rtp_transport_->SetRtcpPacketTransport(rtcp);
-  }
-
-  PacketTransportInterface* GetRtcpPacketTransport() const override {
-    return rtp_transport_->GetRtcpPacketTransport();
-  }
-
   bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
                      const rtc::PacketOptions& options,
                      int flags) override;
@@ -68,30 +41,10 @@
                       const rtc::PacketOptions& options,
                       int flags) override;
 
-  bool IsWritable(bool rtcp) const override {
-    return rtp_transport_->IsWritable(rtcp);
-  }
-
   // The transport becomes active if the send_session_ and recv_session_ are
   // created.
   bool IsActive() const;
 
-  bool HandlesPayloadType(int payload_type) const override {
-    return rtp_transport_->HandlesPayloadType(payload_type);
-  }
-
-  void AddHandledPayloadType(int payload_type) override {
-    rtp_transport_->AddHandledPayloadType(payload_type);
-  }
-
-  RTCError SetParameters(const RtpTransportParameters& parameters) override {
-    return rtp_transport_->SetParameters(parameters);
-  }
-
-  RtpTransportParameters GetParameters() const override {
-    return rtp_transport_->GetParameters();
-  }
-
   // TODO(zstein): Remove this when we remove RtpTransportAdapter.
   RtpTransportAdapter* GetInternal() override { return nullptr; }
 
diff --git a/pc/srtptransport_unittest.cc b/pc/srtptransport_unittest.cc
index 3533863..d93ea13 100644
--- a/pc/srtptransport_unittest.cc
+++ b/pc/srtptransport_unittest.cc
@@ -304,9 +304,9 @@
                                             int key2_len,
                                             const std::string& cs_name) {
     std::vector<int> encrypted_headers;
-    encrypted_headers.push_back(1);
+    encrypted_headers.push_back(kHeaderExtensionIDs[0]);
     // Don't encrypt header ids 2 and 3.
-    encrypted_headers.push_back(4);
+    encrypted_headers.push_back(kHeaderExtensionIDs[1]);
     EXPECT_EQ(key1_len, key2_len);
     EXPECT_EQ(cs_name, rtc::SrtpCryptoSuiteToName(cs));
     EXPECT_TRUE(srtp_transport1_->SetRtpParams(cs, key1, key1_len,