Merge mozilla-central to inbound. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Mon, 11 Jun 2018 00:40:00 +0300
changeset 422288 95e129c56b0fa43caf9ee4c507f3d6dda6ab99e4
parent 422287 c87ea680f79b38f064405ad20dd4a8d5ee291c6b (current diff)
parent 422117 8ab6afabc78cd909ff90ba74c1eab098985f83ef (diff)
child 422289 9f1a58e8b2a7e33e2f4693ac3088f451f58c8b81
push id34122
push userebalazs@mozilla.com
push dateMon, 11 Jun 2018 09:37:00 +0000
treeherdermozilla-central@9941eb8c3b29 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
media/mtransport/databuffer.h
media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp
media/webrtc/signaling/src/mediapipeline/SrtpFlow.h
rename from media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp
rename to media/mtransport/SrtpFlow.cpp
--- a/media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp
+++ b/media/mtransport/SrtpFlow.cpp
@@ -8,26 +8,21 @@
 #include "SrtpFlow.h"
 
 #include "srtp.h"
 #include "ssl.h"
 #include "sslproto.h"
 
 #include "mozilla/RefPtr.h"
 
-static const char* sfLogTag = "SrtpFlow";
-#ifdef LOGTAG
-#undef LOGTAG
-#endif
-#define LOGTAG sfLogTag
-
 using namespace mozilla;
 
 namespace mozilla {
 
+MOZ_MTLOG_MODULE("mtransport")
 bool SrtpFlow::initialized;  // Static
 
 SrtpFlow::~SrtpFlow() {
   if (session_) {
     srtp_dealloc(session_);
   }
 }
 
@@ -37,97 +32,97 @@ RefPtr<SrtpFlow> SrtpFlow::Create(int ci
                                            size_t key_len) {
   nsresult res = Init();
   if (!NS_SUCCEEDED(res))
     return nullptr;
 
   RefPtr<SrtpFlow> flow = new SrtpFlow();
 
   if (!key) {
-    CSFLogError(LOGTAG, "Null SRTP key specified");
+    MOZ_MTLOG(ML_ERROR, "Null SRTP key specified");
     return nullptr;
   }
 
   if (key_len != SRTP_TOTAL_KEY_LENGTH) {
-    CSFLogError(LOGTAG, "Invalid SRTP key length");
+    MOZ_MTLOG(ML_ERROR, "Invalid SRTP key length");
     return nullptr;
   }
 
   srtp_policy_t policy;
   memset(&policy, 0, sizeof(srtp_policy_t));
 
   // Note that we set the same cipher suite for RTP and RTCP
   // since any flow can only have one cipher suite with DTLS-SRTP
   switch (cipher_suite) {
     case SRTP_AES128_CM_HMAC_SHA1_80:
-      CSFLogDebug(LOGTAG,
+      MOZ_MTLOG(ML_DEBUG,
                   "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_80");
       srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
       srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
       break;
     case SRTP_AES128_CM_HMAC_SHA1_32:
-      CSFLogDebug(LOGTAG,
+      MOZ_MTLOG(ML_DEBUG,
                   "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_32");
       srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
       srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // 80-bit per RFC 5764
       break;                                                   // S 4.1.2.
     default:
-      CSFLogError(LOGTAG, "Request to set unknown SRTP cipher suite");
+      MOZ_MTLOG(ML_ERROR, "Request to set unknown SRTP cipher suite");
       return nullptr;
   }
   // This key is copied into the srtp_t object, so we don't
   // need to keep it.
   policy.key = const_cast<unsigned char *>(
       static_cast<const unsigned char *>(key));
   policy.ssrc.type = inbound ? ssrc_any_inbound : ssrc_any_outbound;
   policy.ssrc.value = 0;
   policy.ekt = nullptr;
   policy.window_size = 1024;   // Use the Chrome value.  Needs to be revisited.  Default is 128
   policy.allow_repeat_tx = 1;  // Use Chrome value; needed for NACK mode to work
   policy.next = nullptr;
 
   // Now make the session
   srtp_err_status_t r = srtp_create(&flow->session_, &policy);
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error creating srtp session");
+    MOZ_MTLOG(ML_ERROR, "Error creating srtp session");
     return nullptr;
   }
 
   return flow;
 }
 
 
 nsresult SrtpFlow::CheckInputs(bool protect, void *in, int in_len,
                                int max_len, int *out_len) {
   MOZ_ASSERT(in);
   if (!in) {
-    CSFLogError(LOGTAG, "NULL input value");
+    MOZ_MTLOG(ML_ERROR, "NULL input value");
     return NS_ERROR_NULL_POINTER;
   }
 
   if (in_len < 0) {
-    CSFLogError(LOGTAG, "Input length is negative");
+    MOZ_MTLOG(ML_ERROR, "Input length is negative");
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (max_len < 0) {
-    CSFLogError(LOGTAG, "Max output length is negative");
+    MOZ_MTLOG(ML_ERROR, "Max output length is negative");
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (protect) {
     if ((max_len < SRTP_MAX_EXPANSION) ||
         ((max_len - SRTP_MAX_EXPANSION) < in_len)) {
-      CSFLogError(LOGTAG, "Output too short");
+      MOZ_MTLOG(ML_ERROR, "Output too short");
       return NS_ERROR_ILLEGAL_VALUE;
     }
   }
   else {
     if (in_len > max_len) {
-      CSFLogError(LOGTAG, "Output too short");
+      MOZ_MTLOG(ML_ERROR, "Output too short");
       return NS_ERROR_ILLEGAL_VALUE;
     }
   }
 
   return NS_OK;
 }
 
 nsresult SrtpFlow::ProtectRtp(void *in, int in_len,
@@ -135,117 +130,117 @@ nsresult SrtpFlow::ProtectRtp(void *in, 
   nsresult res = CheckInputs(true, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   srtp_err_status_t r = srtp_protect(session_, in, &len);
 
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error protecting SRTP packet");
+    MOZ_MTLOG(ML_ERROR, "Error protecting SRTP packet");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
 
-  CSFLogDebug(LOGTAG, "Successfully protected an SRTP packet of len %d",
-              *out_len);
+  MOZ_MTLOG(ML_DEBUG, "Successfully protected an SRTP packet of len "
+                      << *out_len);
 
   return NS_OK;
 }
 
 nsresult SrtpFlow::UnprotectRtp(void *in, int in_len,
                                 int max_len, int *out_len) {
   nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   srtp_err_status_t r = srtp_unprotect(session_, in, &len);
 
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error unprotecting SRTP packet error=%d", (int)r);
+    MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTP packet error=" << (int)r);
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
-  CSFLogDebug(LOGTAG, "Successfully unprotected an SRTP packet of len %d",
-              *out_len);
+  MOZ_MTLOG(ML_DEBUG, "Successfully unprotected an SRTP packet of len "
+                      << *out_len);
 
   return NS_OK;
 }
 
 nsresult SrtpFlow::ProtectRtcp(void *in, int in_len,
                                int max_len, int *out_len) {
   nsresult res = CheckInputs(true, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   srtp_err_status_t r = srtp_protect_rtcp(session_, in, &len);
 
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error protecting SRTCP packet");
+    MOZ_MTLOG(ML_ERROR, "Error protecting SRTCP packet");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
-  CSFLogDebug(LOGTAG, "Successfully protected an SRTCP packet of len %d",
-              *out_len);
+  MOZ_MTLOG(ML_DEBUG, "Successfully protected an SRTCP packet of len "
+                      << *out_len);
 
   return NS_OK;
 }
 
 nsresult SrtpFlow::UnprotectRtcp(void *in, int in_len,
                                  int max_len, int *out_len) {
   nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   srtp_err_status_t r = srtp_unprotect_rtcp(session_, in, &len);
 
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error unprotecting SRTCP packet error=%d", (int)r);
+    MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTCP packet error=" << (int)r);
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
-  CSFLogDebug(LOGTAG, "Successfully unprotected an SRTCP packet of len %d",
-              *out_len);
+  MOZ_MTLOG(ML_DEBUG, "Successfully unprotected an SRTCP packet of len "
+                      << *out_len);
 
   return NS_OK;
 }
 
 // Statics
 void SrtpFlow::srtp_event_handler(srtp_event_data_t *data) {
   // TODO(ekr@rtfm.com): Implement this
   MOZ_CRASH();
 }
 
 nsresult SrtpFlow::Init() {
   if (!initialized) {
     srtp_err_status_t r = srtp_init();
     if (r != srtp_err_status_ok) {
-      CSFLogError(LOGTAG, "Could not initialize SRTP");
+      MOZ_MTLOG(ML_ERROR, "Could not initialize SRTP");
       MOZ_ASSERT(PR_FALSE);
       return NS_ERROR_FAILURE;
     }
 
     r = srtp_install_event_handler(&SrtpFlow::srtp_event_handler);
     if (r != srtp_err_status_ok) {
-      CSFLogError(LOGTAG, "Could not install SRTP event handler");
+      MOZ_MTLOG(ML_ERROR, "Could not install SRTP event handler");
       MOZ_ASSERT(PR_FALSE);
       return NS_ERROR_FAILURE;
     }
 
     initialized = true;
   }
 
   return NS_OK;
rename from media/webrtc/signaling/src/mediapipeline/SrtpFlow.h
rename to media/mtransport/SrtpFlow.h
--- a/media/mtransport/build/moz.build
+++ b/media/mtransport/build/moz.build
@@ -4,31 +4,34 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include("/ipc/chromium/chromium-config.mozbuild")
 
 EXPORTS.mtransport += [
     '../dtlsidentity.h',
     '../m_cpp_utils.h',
+    '../mediapacket.h',
     '../nricectx.h',
     '../nricemediastream.h',
     '../nriceresolverfake.h',
     '../nricestunaddr.h',
     '../rlogconnector.h',
     '../runnable_utils.h',
     '../sigslot.h',
     '../simpletokenbucket.h',
+    '../SrtpFlow.h',
     '../stun_socket_filter.h',
     '../transportflow.h',
     '../transportlayer.h',
     '../transportlayerdtls.h',
     '../transportlayerice.h',
     '../transportlayerlog.h',
     '../transportlayerloopback.h',
+    '../transportlayersrtp.h',
 ]
 
 include('../common.build')
 
 # Add libFuzzer configuration directives
 include('/tools/fuzzing/libfuzzer-config.mozbuild')
 
 # These files cannot be built in unified mode because of the redefinition of
--- a/media/mtransport/common.build
+++ b/media/mtransport/common.build
@@ -1,35 +1,38 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 mtransport_lcppsrcs = [
     'dtlsidentity.cpp',
+    'mediapacket.cpp',
     'nr_socket_prsock.cpp',
     'nr_timer.cpp',
     'nricectx.cpp',
     'nricectxhandler.cpp',
     'nricemediastream.cpp',
     'nriceresolver.cpp',
     'nriceresolverfake.cpp',
     'nricestunaddr.cpp',
     'nrinterfaceprioritizer.cpp',
     'rlogconnector.cpp',
     'simpletokenbucket.cpp',
+    'SrtpFlow.cpp',
     'stun_socket_filter.cpp',
     'test_nr_socket.cpp',
     'transportflow.cpp',
     'transportlayer.cpp',
     'transportlayerdtls.cpp',
     'transportlayerice.cpp',
     'transportlayerlog.cpp',
     'transportlayerloopback.cpp',
+    'transportlayersrtp.cpp',
 ]
 
 mtransport_cppsrcs = [
     '/media/mtransport/%s' % s for s in sorted(mtransport_lcppsrcs)
 ]
 
 LOCAL_INCLUDES += [
     '/media/mtransport/',
@@ -42,16 +45,18 @@ LOCAL_INCLUDES += [
     '/media/mtransport/third_party/nrappkit/src/event',
     '/media/mtransport/third_party/nrappkit/src/log',
     '/media/mtransport/third_party/nrappkit/src/plugin',
     '/media/mtransport/third_party/nrappkit/src/port/generic/include',
     '/media/mtransport/third_party/nrappkit/src/registry',
     '/media/mtransport/third_party/nrappkit/src/share',
     '/media/mtransport/third_party/nrappkit/src/stats',
     '/media/mtransport/third_party/nrappkit/src/util/libekr',
+    '/netwerk/srtp/src/crypto/include',
+    '/netwerk/srtp/src/include',
 ]
 
 if CONFIG['OS_TARGET'] in ['Darwin', 'DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD']:
     if CONFIG['OS_TARGET'] == 'Darwin':
         DEFINES['DARWIN'] = True
     else:
         DEFINES['BSD'] = True
     LOCAL_INCLUDES += [
deleted file mode 100644
--- a/media/mtransport/databuffer.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// Original author: ekr@rtfm.com
-
-#ifndef databuffer_h__
-#define databuffer_h__
-#include <algorithm>
-#include <mozilla/UniquePtr.h>
-#include <m_cpp_utils.h>
-#include <nsISupportsImpl.h>
-
-namespace mozilla {
-
-class DataBuffer {
- public:
-  DataBuffer() : data_(nullptr), len_(0), capacity_(0) {}
-  DataBuffer(const uint8_t *data, size_t len) {
-    Assign(data, len, len);
-  }
-  DataBuffer(const uint8_t *data, size_t len, size_t capacity) {
-    Assign(data, len, capacity);
-  }
-
-  // to ensure extra space for expansion
-  void Assign(const uint8_t *data, size_t len, size_t capacity) {
-    MOZ_RELEASE_ASSERT(len <= capacity);
-    Allocate(capacity); // sets len_ = capacity
-    memcpy(static_cast<void *>(data_.get()),
-           static_cast<const void *>(data), len);
-    len_ = len;
-  }
-
-  void Allocate(size_t capacity) {
-    data_.reset(new uint8_t[capacity ? capacity : 1]);  // Don't depend on new [0].
-    len_ = capacity_ = capacity;
-  }
-
-  void EnsureCapacity(size_t capacity) {
-    if (capacity_ < capacity) {
-      uint8_t *new_data = new uint8_t[ capacity ? capacity : 1];
-      memcpy(static_cast<void *>(new_data),
-             static_cast<const void *>(data_.get()), len_);
-      data_.reset(new_data); // after copying!  Deletes old data
-      capacity_ = capacity;
-    }
-  }
-
-  // used when something writes to the buffer (having checked
-  // capacity() or used EnsureCapacity()) and increased the length.
-  void SetLength(size_t len) {
-    MOZ_RELEASE_ASSERT(len <= capacity_);
-    len_ = len;
-  }
-
-  const uint8_t *data() const { return data_.get(); }
-  uint8_t *data() { return data_.get(); }
-  size_t len() const { return len_; }
-  size_t capacity() const { return capacity_; }
-
-private:
-  UniquePtr<uint8_t[]> data_;
-  size_t len_;
-  size_t capacity_;
-
-  DISALLOW_COPY_ASSIGN(DataBuffer);
-};
-
-}
-
-#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/mediapacket.cpp
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mediapacket.h"
+
+#include <cstring>
+
+namespace mozilla {
+
+void
+MediaPacket::Copy(const uint8_t* data, size_t len, size_t capacity)
+{
+  if (capacity < len) {
+    capacity = len;
+  }
+  data_.reset(new uint8_t[capacity]);
+  len_ = len;
+  capacity_ = capacity;
+  memcpy(data_.get(), data, len);
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/mediapacket.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mediapacket_h__
+#define mediapacket_h__
+
+#include <cstddef>
+#include <cstdint>
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+// TODO: It might be worthwhile to teach this class how to "borrow" a buffer.
+// That would make it easier to misuse, however, so maybe not worth it.
+class MediaPacket {
+  public:
+    MediaPacket() = default;
+    MediaPacket(MediaPacket&& orig) = default;
+
+    // Takes ownership of the passed-in data
+    void Take(UniquePtr<uint8_t[]>&& data, size_t len, size_t capacity=0)
+    {
+      data_ = std::move(data);
+      len_ = len;
+      if (capacity < len) {
+        capacity = len;
+      }
+      capacity_ = capacity;
+    }
+
+    void Reset()
+    {
+      data_.reset();
+      len_ = 0;
+      capacity_ = 0;
+    }
+
+    // Copies the passed-in data
+    void Copy(const uint8_t* data, size_t len, size_t capacity=0);
+
+    uint8_t* data() const
+    {
+      return data_.get();
+    }
+
+    size_t len() const
+    {
+      return len_;
+    }
+
+    void SetLength(size_t length)
+    {
+      len_ = length;
+    }
+
+    size_t capacity() const
+    {
+      return capacity_;
+    }
+
+    Maybe<size_t>& sdp_level()
+    {
+      return sdp_level_;
+    }
+
+    void CopyDataToEncrypted()
+    {
+      encrypted_data_ = std::move(data_);
+      encrypted_len_ = len_;
+      Copy(encrypted_data_.get(), len_);
+    }
+
+    const uint8_t* encrypted_data() const
+    {
+      return encrypted_data_.get();
+    }
+
+    size_t encrypted_len() const
+    {
+      return encrypted_len_;
+    }
+
+    enum Type {
+      UNCLASSIFIED,
+      RTP,
+      RTCP,
+      SCTP
+    };
+
+    void SetType(Type type)
+    {
+      type_ = type;
+    }
+
+    Type type() const
+    {
+      return type_;
+    }
+
+  private:
+    UniquePtr<uint8_t[]> data_;
+    size_t len_ = 0;
+    size_t capacity_ = 0;
+    // Encrypted form of the data, if there is one.
+    UniquePtr<uint8_t[]> encrypted_data_;
+    size_t encrypted_len_ = 0;
+    // SDP level that this packet belongs to, if known.
+    Maybe<size_t> sdp_level_;
+    Type type_ = UNCLASSIFIED;
+};
+}
+#endif // mediapacket_h__
+
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -1180,17 +1180,18 @@ NS_IMETHODIMP NrUdpSocketIpc::CallListen
     // Use PR_IpAddrNull to avoid address being reset to 0.
     if (PR_SUCCESS != PR_SetNetAddr(PR_IpAddrNull, addr.raw.family, port, &addr)) {
       err_ = true;
       MOZ_ASSERT(false, "Failed to set port in PRNetAddr");
       return NS_OK;
     }
   }
 
-  nsAutoPtr<DataBuffer> buf(new DataBuffer(data, data_length));
+  nsAutoPtr<MediaPacket> buf(new MediaPacket);
+  buf->Copy(data, data_length);
   RefPtr<nr_udp_message> msg(new nr_udp_message(addr, buf));
 
   RUN_ON_THREAD(sts_thread_,
                 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
                                       &NrUdpSocketIpc::recv_callback_s,
                                       msg),
                 NS_DISPATCH_NORMAL);
   return NS_OK;
@@ -1371,17 +1372,18 @@ int NrUdpSocketIpc::sendto(const void *m
   if ((r=nr_transport_addr_to_netaddr(to, &addr))) {
     return r;
   }
 
   if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) {
     return R_WOULDBLOCK;
   }
 
-  nsAutoPtr<DataBuffer> buf(new DataBuffer(static_cast<const uint8_t*>(msg), len));
+  nsAutoPtr<MediaPacket> buf(new MediaPacket);
+  buf->Copy(static_cast<const uint8_t*>(msg), len);
 
   RUN_ON_THREAD(io_thread_,
                 mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
                                       &NrUdpSocketIpc::sendto_i,
                                       addr, buf),
                 NS_DISPATCH_NORMAL);
   return 0;
 }
@@ -1589,17 +1591,17 @@ void NrUdpSocketIpc::connect_i(const nsA
     err_ = true;
     MOZ_ASSERT(false, "Failed to connect UDP socket");
     mon.NotifyAll();
     return;
   }
 }
 
 
-void NrUdpSocketIpc::sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) {
+void NrUdpSocketIpc::sendto_i(const net::NetAddr &addr, nsAutoPtr<MediaPacket> buf) {
   ASSERT_ON_THREAD(io_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   if (!socket_child_) {
     MOZ_ASSERT(false);
     err_ = true;
     return;
@@ -1746,17 +1748,18 @@ NS_IMETHODIMP NrTcpSocketIpc::UpdateBuff
   return NS_OK;
 }
 
 NS_IMETHODIMP NrTcpSocketIpc::FireDataArrayEvent(const nsAString& aType,
                                                  const InfallibleTArray<uint8_t>& buffer) {
   // Called when we received data.
   uint8_t *buf = const_cast<uint8_t*>(buffer.Elements());
 
-  nsAutoPtr<DataBuffer> data_buf(new DataBuffer(buf, buffer.Length()));
+  nsAutoPtr<MediaPacket> data_buf(new MediaPacket);
+  data_buf->Copy(buf, buffer.Length());
   RefPtr<nr_tcp_message> msg = new nr_tcp_message(data_buf);
 
   RUN_ON_THREAD(sts_thread_,
                 mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
                                       &NrTcpSocketIpc::recv_message_s,
                                       msg),
                 NS_DISPATCH_NORMAL);
   return NS_OK;
--- a/media/mtransport/nr_socket_prsock.h
+++ b/media/mtransport/nr_socket_prsock.h
@@ -57,17 +57,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #include "nsISocketTransportService.h"
 #include "nsXPCOM.h"
 #include "nsIEventTarget.h"
 #include "nsIUDPSocketChild.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 #include "nsITCPSocketCallback.h"
-#include "databuffer.h"
+#include "mediapacket.h"
 #include "m_cpp_utils.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ClearOnShutdown.h"
 
 // Stub declaration for nICEr type
 typedef struct nr_socket_vtbl_ nr_socket_vtbl;
@@ -192,24 +192,24 @@ protected:
 
   DISALLOW_COPY_ASSIGN(NrSocket);
 
   PRFileDesc *fd_;
   nsCOMPtr<nsIEventTarget> ststhread_;
 };
 
 struct nr_udp_message {
-  nr_udp_message(const PRNetAddr &from, nsAutoPtr<DataBuffer> &data)
+  nr_udp_message(const PRNetAddr &from, nsAutoPtr<MediaPacket> &data)
       : from(from), data(data) {
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_udp_message);
 
   PRNetAddr from;
-  nsAutoPtr<DataBuffer> data;
+  nsAutoPtr<MediaPacket> data;
 
 private:
   ~nr_udp_message() {}
   DISALLOW_COPY_ASSIGN(nr_udp_message);
 };
 
 class NrSocketIpc : public NrSocketBase {
 public:
@@ -272,17 +272,17 @@ private:
 
   DISALLOW_COPY_ASSIGN(NrUdpSocketIpc);
 
   nsresult SetAddress();  // Set the local address from parent info.
 
   // Main or private thread executors of the NrSocketBase APIs
   void create_i(const nsACString &host, const uint16_t port);
   void connect_i(const nsACString &host, const uint16_t port);
-  void sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf);
+  void sendto_i(const net::NetAddr &addr, nsAutoPtr<MediaPacket> buf);
   void close_i();
 #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
   static void destroy_i(nsIUDPSocketChild* aChild,
                         nsCOMPtr<nsIEventTarget>& aStsThread);
 #endif
   // STS thread executor
   void recv_callback_s(RefPtr<nr_udp_message> msg);
 
@@ -307,17 +307,17 @@ public:
 private:
   virtual ~NrUdpSocketIpcProxy();
 
   RefPtr<NrUdpSocketIpc> socket_;
   nsCOMPtr<nsIEventTarget> sts_thread_;
 };
 
 struct nr_tcp_message {
-  explicit nr_tcp_message(nsAutoPtr<DataBuffer> &data)
+  explicit nr_tcp_message(nsAutoPtr<MediaPacket> &data)
     : read_bytes(0)
     , data(data) {
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_tcp_message);
 
   const uint8_t *reading_pointer() const {
     return data->data() + read_bytes;
@@ -328,17 +328,17 @@ struct nr_tcp_message {
   }
 
   size_t read_bytes;
 
 private:
   ~nr_tcp_message() {}
   DISALLOW_COPY_ASSIGN(nr_tcp_message);
 
-  nsAutoPtr<DataBuffer> data;
+  nsAutoPtr<MediaPacket> data;
 };
 
 #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
 class NrTcpSocketIpc : public NrSocketIpc,
                        public nsITCPSocketCallback {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITCPSOCKETCALLBACK
--- a/media/mtransport/test/buffered_stun_socket_unittest.cpp
+++ b/media/mtransport/test/buffered_stun_socket_unittest.cpp
@@ -15,17 +15,16 @@
 extern "C" {
 #include "nr_api.h"
 #include "nr_socket.h"
 #include "nr_socket_buffered_stun.h"
 #include "transport_addr.h"
 #include "stun.h"
 }
 
-#include "databuffer.h"
 #include "dummysocket.h"
 
 #include "nr_socket_prsock.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 #include "gtest_utils.h"
 
--- a/media/mtransport/test/dummysocket.h
+++ b/media/mtransport/test/dummysocket.h
@@ -10,33 +10,33 @@
 #define MTRANSPORT_DUMMY_SOCKET_H_
 
 #include "nr_socket_prsock.h"
 
 extern "C" {
 #include "transport_addr.h"
 }
 
-#include "databuffer.h"
+#include "mediapacket.h"
 #include "mozilla/UniquePtr.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 #include "gtest_utils.h"
 
 namespace mozilla {
 
-static UniquePtr<DataBuffer> merge(UniquePtr<DataBuffer> a, UniquePtr<DataBuffer> b) {
+static UniquePtr<MediaPacket> merge(UniquePtr<MediaPacket> a, UniquePtr<MediaPacket> b) {
   if (a && a->len() && b && b->len()) {
-    UniquePtr<DataBuffer> merged(new DataBuffer());
-    merged->Allocate(a->len() + b->len());
+    UniquePtr<uint8_t[]> data(new uint8_t[a->len() + b->len()]);
+    memcpy(data.get(), a->data(), a->len());
+    memcpy(data.get() + a->len(), b->data(), b->len());
 
-    memcpy(merged->data(), a->data(), a->len());
-    memcpy(merged->data() + a->len(), b->data(), b->len());
-
+    UniquePtr<MediaPacket> merged(new MediaPacket);
+    merged->Take(std::move(data), a->len() + b->len());
     return merged;
   }
 
   if (a && a->len()) {
     return a;
   }
 
   if (b && b->len()) {
@@ -95,17 +95,18 @@ class DummySocket : public NrSocketBase 
   virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) override {
     return 0;
   }
 
   virtual int write(const void *msg, size_t len, size_t *written) override {
     size_t to_write = std::min(len, writable_);
 
     if (to_write) {
-      UniquePtr<DataBuffer> msgbuf(new DataBuffer(static_cast<const uint8_t *>(msg), to_write));
+      UniquePtr<MediaPacket> msgbuf(new MediaPacket);
+      msgbuf->Copy(static_cast<const uint8_t *>(msg), to_write);
       write_buffer_ = merge(std::move(write_buffer_), std::move(msgbuf));
     }
 
     *written = to_write;
 
     return 0;
   }
 
@@ -116,18 +117,20 @@ class DummySocket : public NrSocketBase 
 
     size_t to_read = std::min(read_buffer_->len(),
                               std::min(maxlen, readable_));
 
     memcpy(buf, read_buffer_->data(), to_read);
     *len = to_read;
 
     if (to_read < read_buffer_->len()) {
-      read_buffer_.reset(new DataBuffer(read_buffer_->data() + to_read,
-                                    read_buffer_->len() - to_read));
+      MediaPacket* newPacket = new MediaPacket;
+      newPacket->Copy(read_buffer_->data() + to_read,
+                      read_buffer_->len() - to_read);
+      read_buffer_.reset(newPacket);
     } else {
       read_buffer_.reset();
     }
 
     return 0;
   }
 
   // Implementations of the async_event APIs.
@@ -175,17 +178,18 @@ class DummySocket : public NrSocketBase 
     cb_ = nullptr;
     cb_arg_ = nullptr;
 
     cb(this, NR_ASYNC_WAIT_WRITE, cb_arg);
   }
 
   void SetReadBuffer(const uint8_t *data, size_t len) {
     EXPECT_EQ(nullptr, write_buffer_.get());
-    read_buffer_.reset(new DataBuffer(data, len));
+    read_buffer_.reset(new MediaPacket);
+    read_buffer_->Copy(data, len);
   }
 
   void ClearReadBuffer() {
     read_buffer_.reset();
   }
 
   void SetReadable(size_t val) {
     readable_ = val;
@@ -209,19 +213,19 @@ class DummySocket : public NrSocketBase 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket, override);
 
  private:
   ~DummySocket() {}
 
   DISALLOW_COPY_ASSIGN(DummySocket);
 
   size_t writable_;  // Amount we allow someone to write.
-  UniquePtr<DataBuffer> write_buffer_;
+  UniquePtr<MediaPacket> write_buffer_;
   size_t readable_;   // Amount we allow someone to read.
-  UniquePtr<DataBuffer> read_buffer_;
+  UniquePtr<MediaPacket> read_buffer_;
 
   NR_async_cb cb_;
   void *cb_arg_;
   nr_socket *self_;
 
   nr_transport_addr connect_addr_;
 };
 
--- a/media/mtransport/test/proxy_tunnel_socket_unittest.cpp
+++ b/media/mtransport/test/proxy_tunnel_socket_unittest.cpp
@@ -15,17 +15,16 @@
 extern "C" {
 #include "nr_api.h"
 #include "nr_socket.h"
 #include "nr_proxy_tunnel.h"
 #include "transport_addr.h"
 #include "stun.h"
 }
 
-#include "databuffer.h"
 #include "dummysocket.h"
 
 #include "nr_socket_prsock.h"
 #include "nriceresolverfake.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 #include "gtest_utils.h"
--- a/media/mtransport/test/sctp_unittest.cpp
+++ b/media/mtransport/test/sctp_unittest.cpp
@@ -134,35 +134,36 @@ class TransportTestPeer : public sigslot
   void ConnectSocket(TransportTestPeer *peer) {
     test_utils_->sts_target()->Dispatch(WrapRunnable(
         this, &TransportTestPeer::ConnectSocket_s, peer),
                                        NS_DISPATCH_SYNC);
   }
 
   void ConnectSocket_s(TransportTestPeer *peer) {
     loopback_->Connect(peer->loopback_);
+    ASSERT_EQ((nsresult)NS_OK, loopback_->Init());
+    flow_->PushLayer(loopback_);
 
-    ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_));
-
-    flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
+    loopback_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
 
     // SCTP here!
     ASSERT_TRUE(sctp_);
     std::cerr << "Calling usrsctp_bind()" << std::endl;
     int r = usrsctp_bind(sctp_, reinterpret_cast<struct sockaddr *>(
         &local_addr_), sizeof(local_addr_));
     ASSERT_GE(0, r);
 
     std::cerr << "Calling usrsctp_connect()" << std::endl;
     r = usrsctp_connect(sctp_, reinterpret_cast<struct sockaddr *>(
         &remote_addr_), sizeof(remote_addr_));
     ASSERT_GE(0, r);
   }
 
   void Disconnect_s() {
+    disconnect_all();
     if (flow_) {
       flow_ = nullptr;
     }
   }
 
   void Disconnect() {
     loopback_->Disconnect();
   }
@@ -193,47 +194,45 @@ class TransportTestPeer : public sigslot
 
     ++sent_;
   }
 
   int sent() const { return sent_; }
   int received() const { return received_; }
   bool connected() const { return connected_; }
 
-  static TransportResult SendPacket_s(const unsigned char* data, size_t len,
-                                      const RefPtr<TransportFlow>& flow) {
-    TransportResult res = flow->SendPacket(data, len);
-    delete data; // we always allocate
-    return res;
+  static TransportResult SendPacket_s(nsAutoPtr<MediaPacket> packet,
+                                      const RefPtr<TransportFlow>& flow,
+                                      TransportLayer* layer) {
+    return layer->SendPacket(*packet);
   }
 
   TransportResult SendPacket(const unsigned char* data, size_t len) {
-    unsigned char *buffer = new unsigned char[len];
-    memcpy(buffer, data, len);
+    nsAutoPtr<MediaPacket> packet(new MediaPacket);
+    packet->Copy(data, len);
 
     // Uses DISPATCH_NORMAL to avoid possible deadlocks when we're called
     // from MainThread especially during shutdown (same as DataChannels).
     // RUN_ON_THREAD short-circuits if already on the STS thread, which is
     // normal for most transfers outside of connect() and close().  Passes
     // a refptr to flow_ to avoid any async deletion issues (since we can't
     // make 'this' into a refptr as it isn't refcounted)
     RUN_ON_THREAD(test_utils_->sts_target(), WrapRunnableNM(
-        &TransportTestPeer::SendPacket_s, buffer, len, flow_),
+        &TransportTestPeer::SendPacket_s, packet, flow_, loopback_),
                   NS_DISPATCH_NORMAL);
 
     return 0;
   }
 
-  void PacketReceived(TransportFlow * flow, const unsigned char* data,
-                      size_t len) {
-    std::cerr << "Received " << len << " bytes" << std::endl;
+  void PacketReceived(TransportLayer * layer, MediaPacket& packet) {
+    std::cerr << "Received " << packet.len() << " bytes" << std::endl;
 
     // Pass the data to SCTP
 
-    usrsctp_conninput(static_cast<void *>(this), data, len, 0);
+    usrsctp_conninput(static_cast<void *>(this), packet.data(), packet.len(), 0);
   }
 
   // Process SCTP notification
   void Notification(union sctp_notification *msg, size_t len) {
     ASSERT_EQ(msg->sn_header.sn_length, len);
 
     if (msg->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
       struct sctp_assoc_change *change = &msg->sn_assoc_change;
@@ -283,16 +282,17 @@ class TransportTestPeer : public sigslot
   }
 
 
  private:
   std::string name_;
   bool connected_;
   size_t sent_;
   size_t received_;
+  // Owns the TransportLayerLoopback, but basically does nothing else.
   RefPtr<TransportFlow> flow_;
   TransportLayerLoopback *loopback_;
 
   struct sockaddr_conn local_addr_;
   struct sockaddr_conn remote_addr_;
   struct socket *sctp_;
   nsCOMPtr<nsITimer> timer_;
   RefPtr<SendPeriodic> periodic_;
--- a/media/mtransport/test/stunserver.cpp
+++ b/media/mtransport/test/stunserver.cpp
@@ -75,17 +75,17 @@ nrappkit copyright:
    POSSIBILITY OF SUCH DAMAGE.
 
 
    ekr@rtfm.com  Thu Dec 20 20:14:49 2001
 */
 #include "logging.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
-#include "databuffer.h"
+#include "mediapacket.h"
 
 // mozilla/utils.h defines this as well
 #ifdef UNIMPLEMENTED
 #undef UNIMPLEMENTED
 #endif
 
 extern "C" {
 #include "nr_api.h"
@@ -387,23 +387,24 @@ void TestStunServer::ShutdownInstance() 
 
 
 struct DeferredStunOperation {
   DeferredStunOperation(TestStunServer *server,
                         const char *data, size_t len,
                         nr_transport_addr *addr,
                         nr_socket *sock) :
       server_(server),
-      buffer_(reinterpret_cast<const uint8_t *>(data), len),
+      buffer_(),
       sock_(sock) {
+    buffer_.Copy(reinterpret_cast<const uint8_t *>(data), len);
     nr_transport_addr_copy(&addr_, addr);
   }
 
   TestStunServer *server_;
-  DataBuffer buffer_;
+  MediaPacket buffer_;
   nr_transport_addr addr_;
   nr_socket *sock_;
 };
 
 void TestStunServer::Process(const uint8_t *msg, size_t len, nr_transport_addr *addr, nr_socket *sock) {
 
   if (!sock) {
     sock = send_sock_;
--- a/media/mtransport/test/transport_unittests.cpp
+++ b/media/mtransport/test/transport_unittests.cpp
@@ -19,17 +19,17 @@
 #include "nspr.h"
 #include "nss.h"
 #include "ssl.h"
 #include "sslproto.h"
 
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
 
-#include "databuffer.h"
+#include "mediapacket.h"
 #include "dtlsidentity.h"
 #include "nricectxhandler.h"
 #include "nricemediastream.h"
 #include "transportflow.h"
 #include "transportlayer.h"
 #include "transportlayerdtls.h"
 #include "transportlayerice.h"
 #include "transportlayerlog.h"
@@ -71,17 +71,17 @@ class TransportLayerDummy : public Trans
   virtual ~TransportLayerDummy() {
     *destroyed_ = true;
   }
 
   nsresult InitInternal() override {
     return allow_init_ ? NS_OK : NS_ERROR_FAILURE;
   }
 
-  TransportResult SendPacket(const unsigned char *data, size_t len) override {
+  TransportResult SendPacket(MediaPacket& packet) override {
     MOZ_CRASH();  // Should never be called.
     return 0;
   }
 
   TRANSPORT_LAYER_ID("lossy")
 
  private:
   bool allow_init_;
@@ -97,48 +97,47 @@ class Inspector {
 };
 
 // Class to simulate various kinds of network lossage
 class TransportLayerLossy : public TransportLayer {
  public:
   TransportLayerLossy() : loss_mask_(0), packet_(0), inspector_(nullptr) {}
   ~TransportLayerLossy () {}
 
-  TransportResult SendPacket(const unsigned char *data, size_t len) override {
-    MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "SendPacket(" << len << ")");
+  TransportResult SendPacket(MediaPacket& packet) override {
+    MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "SendPacket(" << packet.len() << ")");
 
     if (loss_mask_ & (1 << (packet_ % 32))) {
       MOZ_MTLOG(ML_NOTICE, "Dropping packet");
       ++packet_;
-      return len;
+      return packet.len();
     }
     if (inspector_) {
-      inspector_->Inspect(this, data, len);
+      inspector_->Inspect(this, packet.data(), packet.len());
     }
 
     ++packet_;
 
-    return downward_->SendPacket(data, len);
+    return downward_->SendPacket(packet);
   }
 
   void SetLoss(uint32_t packet) {
     loss_mask_ |= (1 << (packet & 32));
   }
 
   void SetInspector(UniquePtr<Inspector> inspector) {
     inspector_ = std::move(inspector);
   }
 
   void StateChange(TransportLayer *layer, State state) {
     TL_SET_STATE(state);
   }
 
-  void PacketReceived(TransportLayer *layer, const unsigned char *data,
-                      size_t len) {
-    SignalPacketReceived(this, data, len);
+  void PacketReceived(TransportLayer *layer, MediaPacket& packet) {
+    SignalPacketReceived(this, packet);
   }
 
   TRANSPORT_LAYER_ID("lossy")
 
  protected:
   void WasInserted() override {
     downward_->SignalPacketReceived.
         connect(this,
@@ -161,17 +160,19 @@ class TransportLayerLossy : public Trans
   do { \
     EXPECT_GE(remaining(), expected); \
     if (remaining() < expected) return false; \
   } while(0)
 
 class TlsParser {
  public:
   TlsParser(const unsigned char *data, size_t len)
-      : buffer_(data, len), offset_(0) {}
+      : buffer_(), offset_(0) {
+    buffer_.Copy(data, len);
+  }
 
   bool Read(unsigned char* val) {
     if (remaining() < 1) {
       return false;
     }
     *val = *ptr();
     consume(1);
     return true;
@@ -209,97 +210,97 @@ class TlsParser {
     return true;
   }
 
  private:
   size_t remaining() const { return buffer_.len() - offset_; }
   const uint8_t *ptr() const { return buffer_.data() + offset_; }
   void consume(size_t len) { offset_ += len; }
 
-  DataBuffer buffer_;
+  MediaPacket buffer_;
   size_t offset_;
 };
 
 class DtlsRecordParser {
  public:
   DtlsRecordParser(const unsigned char *data, size_t len)
-      : buffer_(data, len), offset_(0) {}
+      : buffer_(), offset_(0) {
+    buffer_.Copy(data, len);
+  }
 
-  bool NextRecord(uint8_t* ct, nsAutoPtr<DataBuffer>* buffer) {
+  bool NextRecord(uint8_t* ct, nsAutoPtr<MediaPacket>* buffer) {
     if (!remaining())
       return false;
 
     CHECK_LENGTH(13U);
     const uint8_t *ctp = reinterpret_cast<const uint8_t *>(ptr());
     consume(11); // ct + version + length
 
     const uint16_t *tmp = reinterpret_cast<const uint16_t*>(ptr());
     size_t length = ntohs(*tmp);
     consume(2);
 
     CHECK_LENGTH(length);
-    DataBuffer* db = new DataBuffer(ptr(), length);
+    MediaPacket* db = new MediaPacket;
+    db->Copy(ptr(), length);
     consume(length);
 
     *ct = *ctp;
     *buffer = db;
 
     return true;
   }
 
  private:
   size_t remaining() const { return buffer_.len() - offset_; }
   const uint8_t *ptr() const { return buffer_.data() + offset_; }
   void consume(size_t len) { offset_ += len; }
 
-  DataBuffer buffer_;
+  MediaPacket buffer_;
   size_t offset_;
 };
 
 
 // Inspector that parses out DTLS records and passes
 // them on.
 class DtlsRecordInspector : public Inspector {
  public:
   virtual void Inspect(TransportLayer* layer,
                        const unsigned char *data, size_t len) {
     DtlsRecordParser parser(data, len);
 
     uint8_t ct;
-    nsAutoPtr<DataBuffer> buf;
+    nsAutoPtr<MediaPacket> buf;
     while(parser.NextRecord(&ct, &buf)) {
       OnRecord(layer, ct, buf->data(), buf->len());
     }
   }
 
   virtual void OnRecord(TransportLayer* layer,
                         uint8_t content_type,
                         const unsigned char *record,
                         size_t len) = 0;
 };
 
 // Inspector that injects arbitrary packets based on
 // DTLS records of various types.
 class DtlsInspectorInjector : public DtlsRecordInspector {
  public:
   DtlsInspectorInjector(uint8_t packet_type, uint8_t handshake_type,
-                    const unsigned char *data, size_t len) :
+                        const unsigned char *data, size_t len) :
       packet_type_(packet_type),
-      handshake_type_(handshake_type),
-      injected_(false) {
-    data_.reset(new unsigned char[len]);
-    memcpy(data_.get(), data, len);
-    len_ = len;
+      handshake_type_(handshake_type) {
+    packet_.Copy(data, len);
   }
 
   virtual void OnRecord(TransportLayer* layer,
                         uint8_t content_type,
                         const unsigned char *data, size_t len) {
     // Only inject once.
-    if (injected_) {
+    if (!packet_.data()) {
       return;
     }
 
     // Check that the first byte is as requested.
     if (content_type != packet_type_) {
       return;
     }
 
@@ -310,25 +311,24 @@ class DtlsInspectorInjector : public Dtl
       }
 
       // Check that the handshake type is as requested.
       if (data[0] != handshake_type_) {
         return;
       }
     }
 
-    layer->SendPacket(data_.get(), len_);
+    layer->SendPacket(packet_);
+    packet_.Reset();
   }
 
  private:
   uint8_t packet_type_;
   uint8_t handshake_type_;
-  bool injected_;
-  UniquePtr<unsigned char[]> data_;
-  size_t len_;
+  MediaPacket packet_;
 };
 
 // Make a copy of the first instance of a message.
 class DtlsInspectorRecordHandshakeMessage : public DtlsRecordInspector {
  public:
   explicit DtlsInspectorRecordHandshakeMessage(uint8_t handshake_type)
       : handshake_type_(handshake_type),
         buffer_() {}
@@ -377,27 +377,28 @@ class DtlsInspectorRecordHandshakeMessag
     }
 
     if ((fragment_offset != 0) || (fragment_length != length)) {
       // This shouldn't happen because all current tests where we
       // are using this code don't fragment.
       return;
     }
 
-    buffer_.Allocate(length);
-    if (!parser.Read(buffer_.data(), length)) {
+    UniquePtr<uint8_t[]> buffer(new uint8_t[length]);
+    if (!parser.Read(buffer.get(), length)) {
       return;
     }
+    buffer_.Take(std::move(buffer), length);
   }
 
-  const DataBuffer& buffer() { return buffer_; }
+  const MediaPacket& buffer() { return buffer_; }
 
  private:
   uint8_t handshake_type_;
-  DataBuffer buffer_;
+  MediaPacket buffer_;
 };
 
 class TlsServerKeyExchangeECDHE {
  public:
   bool Parse(const unsigned char* data, size_t len) {
     TlsParser parser(data, len);
 
     uint8_t curve_type;
@@ -414,25 +415,26 @@ class TlsServerKeyExchangeECDHE {
       return false;
     }
 
     uint32_t point_length;
     if (!parser.Read(&point_length, 1)) {
       return false;
     }
 
-    public_key_.Allocate(point_length);
-    if (!parser.Read(public_key_.data(), point_length)) {
+    UniquePtr<uint8_t[]> key(new uint8_t[point_length]);
+    if (!parser.Read(key.get(), point_length)) {
       return false;
     }
+    public_key_.Take(std::move(key), point_length);
 
     return true;
   }
 
-  DataBuffer public_key_;
+  MediaPacket public_key_;
 };
 
 namespace {
 class TransportTestPeer : public sigslot::has_slots<> {
  public:
   TransportTestPeer(nsCOMPtr<nsIEventTarget> target, std::string name, MtransportTestUtils* utils)
       : name_(name), offerer_(name == "P1"), target_(target),
         received_packets_(0),received_bytes_(0),flow_(new TransportFlow(name)),
@@ -547,38 +549,45 @@ class TransportTestPeer : public sigslot
   }
 
   void ConnectSocket_s(TransportTestPeer *peer) {
     nsresult res;
     res = loopback_->Init();
     ASSERT_EQ((nsresult)NS_OK, res);
 
     loopback_->Connect(peer->loopback_);
+    ASSERT_EQ((nsresult)NS_OK, loopback_->Init());
+    ASSERT_EQ((nsresult)NS_OK, logging_->Init());
+    ASSERT_EQ((nsresult)NS_OK, lossy_->Init());
+    ASSERT_EQ((nsresult)NS_OK, dtls_->Init());
+    dtls_->Chain(lossy_);
+    lossy_->Chain(logging_);
+    logging_->Chain(loopback_);
 
-    ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_));
-    ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(logging_));
-    ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(lossy_));
-    ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(dtls_));
+    flow_->PushLayer(loopback_);
+    flow_->PushLayer(logging_);
+    flow_->PushLayer(lossy_);
+    flow_->PushLayer(dtls_);
 
     if (dtls_->state() != TransportLayer::TS_ERROR) {
       // Don't execute these blocks if DTLS didn't initialize.
       TweakCiphers(dtls_->internal_fd());
       if (reuse_dhe_key_) {
         // TransportLayerDtls automatically sets this pref to false
         // so set it back for test.
         // This is pretty gross. Dig directly into the NSS FD. The problem
         // is that we are testing a feature which TransaportLayerDtls doesn't
         // expose.
         SECStatus rv = SSL_OptionSet(dtls_->internal_fd(),
                                      SSL_REUSE_SERVER_ECDHE_KEY, PR_TRUE);
         ASSERT_EQ(SECSuccess, rv);
       }
     }
 
-    flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
+    dtls_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
   }
 
   void TweakCiphers(PRFileDesc* fd) {
     for (unsigned short& enabled_cipersuite : enabled_cipersuites_) {
       SSL_CipherPrefSet(fd, enabled_cipersuite, PR_TRUE);
     }
     for (unsigned short& disabled_cipersuite : disabled_cipersuites_) {
       SSL_CipherPrefSet(fd, disabled_cipersuite, PR_FALSE);
@@ -587,16 +596,27 @@ class TransportTestPeer : public sigslot
 
   void ConnectSocket(TransportTestPeer *peer) {
     RUN_ON_THREAD(test_utils_->sts_target(),
                   WrapRunnable(this, & TransportTestPeer::ConnectSocket_s,
                                peer),
                   NS_DISPATCH_SYNC);
   }
 
+  nsresult InitIce_s() {
+    nsresult rv = ice_->Init();
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = dtls_->Init();
+    NS_ENSURE_SUCCESS(rv, rv);
+    dtls_->Chain(ice_);
+    flow_->PushLayer(ice_);
+    flow_->PushLayer(dtls_);
+    return NS_OK;
+  }
+
   void InitIce() {
     nsresult res;
 
     // Attach our slots
     ice_ctx_->ctx()->SignalGatheringStateChange.
         connect(this, &TransportTestPeer::GatheringStateChange);
 
     char name[100];
@@ -614,31 +634,25 @@ class TransportTestPeer : public sigslot
     // Listen for candidates
     stream->SignalCandidate.
         connect(this, &TransportTestPeer::GotCandidate);
 
     // Create the transport layer
     ice_ = new TransportLayerIce();
     ice_->SetParameters(stream, 1);
 
-    // Assemble the stack
-    nsAutoPtr<std::queue<mozilla::TransportLayer *> > layers(
-      new std::queue<mozilla::TransportLayer *>);
-    layers->push(ice_);
-    layers->push(dtls_);
-
     test_utils_->sts_target()->Dispatch(
-      WrapRunnableRet(&res, flow_, &TransportFlow::PushLayers, layers),
+      WrapRunnableRet(&res, this, &TransportTestPeer::InitIce_s),
       NS_DISPATCH_SYNC);
 
     ASSERT_EQ((nsresult)NS_OK, res);
 
     // Listen for media events
-    flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
-    flow_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged);
+    dtls_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
+    dtls_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged);
 
     // Start gathering
     test_utils_->sts_target()->Dispatch(
         WrapRunnableRet(&res,
                         ice_ctx_->ctx(),
                         &NrIceCtx::StartGathering,
                         false,
                         false),
@@ -698,37 +712,47 @@ class TransportTestPeer : public sigslot
     // Start checks on the other peer.
     test_utils_->sts_target()->Dispatch(
       WrapRunnableRet(&res, peer_->ice_ctx_->ctx(), &NrIceCtx::StartChecks,
                       offerer_),
       NS_DISPATCH_SYNC);
     ASSERT_TRUE(NS_SUCCEEDED(res));
   }
 
-  TransportResult SendPacket(const unsigned char* data, size_t len) {
+  // WrapRunnable/lambda and move semantics (MediaPacket is not copyable) don't
+  // get along yet, so we need a wrapper. Gross.
+  static TransportResult SendPacketWrapper(TransportLayer* layer,
+                                           MediaPacket* packet) {
+    return layer->SendPacket(*packet);
+  }
+
+  TransportResult SendPacket(MediaPacket& packet) {
     TransportResult ret;
+
     test_utils_->sts_target()->Dispatch(
-      WrapRunnableRet(&ret, flow_, &TransportFlow::SendPacket, data, len),
+      WrapRunnableNMRet(&ret,
+                        &TransportTestPeer::SendPacketWrapper,
+                        dtls_,
+                        &packet),
       NS_DISPATCH_SYNC);
 
     return ret;
   }
 
 
-  void StateChanged(TransportFlow *flow, TransportLayer::State state) {
+  void StateChanged(TransportLayer *layer, TransportLayer::State state) {
     if (state == TransportLayer::TS_OPEN) {
       std::cerr << "Now connected" << std::endl;
     }
   }
 
-  void PacketReceived(TransportFlow * flow, const unsigned char* data,
-                      size_t len) {
-    std::cerr << "Received " << len << " bytes" << std::endl;
+  void PacketReceived(TransportLayer* layer, MediaPacket& packet) {
+    std::cerr << "Received " << packet.len() << " bytes" << std::endl;
     ++received_packets_;
-    received_bytes_ += len;
+    received_bytes_ += packet.len();
   }
 
   void SetLoss(uint32_t loss) {
     lossy_->SetLoss(loss);
   }
 
   void SetCombinePackets(bool combine) {
     loopback_->CombinePackets(combine);
@@ -753,17 +777,17 @@ class TransportTestPeer : public sigslot
   void SetReuseECDHEKey() {
     reuse_dhe_key_ = true;
   }
 
   TransportLayer::State state() {
     TransportLayer::State tstate;
 
     RUN_ON_THREAD(test_utils_->sts_target(),
-                  WrapRunnableRet(&tstate, flow_, &TransportFlow::state));
+                  WrapRunnableRet(&tstate, dtls_, &TransportLayer::state));
 
     return tstate;
   }
 
   bool connected() {
     return state() == TransportLayer::TS_OPEN;
   }
 
@@ -933,17 +957,19 @@ class TransportTest : public MtransportT
     ASSERT_TRUE_WAIT(p2_->connected(), 10000);
   }
 
   void TransferTest(size_t count, size_t bytes = 1024) {
     unsigned char buf[bytes];
 
     for (size_t i= 0; i<count; ++i) {
       memset(buf, count & 0xff, sizeof(buf));
-      TransportResult rv = p1_->SendPacket(buf, sizeof(buf));
+      MediaPacket packet;
+      packet.Copy(buf, sizeof(buf));
+      TransportResult rv = p1_->SendPacket(packet);
       ASSERT_TRUE(rv > 0);
     }
 
     std::cerr << "Received == " << p2_->receivedPackets() << " packets" << std::endl;
     ASSERT_TRUE_WAIT(count == p2_->receivedPackets(), 10000);
     ASSERT_TRUE((count * sizeof(buf)) == p2_->receivedBytes());
   }
 
@@ -1286,61 +1312,9 @@ TEST_F(TransportTest, TestDheOnlyFails) 
   SetDtlsPeer();
 
   // p2_ is the client
   // setting this on p1_ (the server) causes NSS to assert
   ConfigureOneCipher(p2_, TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
   ConnectSocketExpectFail();
 }
 
-TEST(PushTests, LayerFail) {
-  RefPtr<TransportFlow> flow = new TransportFlow();
-  nsresult rv;
-  bool destroyed1, destroyed2;
-
-  rv = flow->PushLayer(new TransportLayerDummy(true, &destroyed1));
-  ASSERT_TRUE(NS_SUCCEEDED(rv));
-
-  rv = flow->PushLayer(new TransportLayerDummy(false, &destroyed2));
-  ASSERT_TRUE(NS_FAILED(rv));
-
-  ASSERT_EQ(TransportLayer::TS_ERROR, flow->state());
-  ASSERT_EQ(true, destroyed1);
-  ASSERT_EQ(true, destroyed2);
-
-  rv = flow->PushLayer(new TransportLayerDummy(true, &destroyed1));
-  ASSERT_TRUE(NS_FAILED(rv));
-  ASSERT_EQ(true, destroyed1);
-}
-
-TEST(PushTests, LayersFail) {
-  RefPtr<TransportFlow> flow = new TransportFlow();
-  nsresult rv;
-  bool destroyed1, destroyed2, destroyed3;
-
-  rv = flow->PushLayer(new TransportLayerDummy(true, &destroyed1));
-  ASSERT_TRUE(NS_SUCCEEDED(rv));
-
-  nsAutoPtr<std::queue<TransportLayer *> > layers(
-      new std::queue<TransportLayer *>());
-
-  layers->push(new TransportLayerDummy(true, &destroyed2));
-  layers->push(new TransportLayerDummy(false, &destroyed3));
-
-  rv = flow->PushLayers(layers);
-  ASSERT_TRUE(NS_FAILED(rv));
-
-  ASSERT_EQ(TransportLayer::TS_ERROR, flow->state());
-  ASSERT_EQ(true, destroyed1);
-  ASSERT_EQ(true, destroyed2);
-  ASSERT_EQ(true, destroyed3);
-
-  layers = new std::queue<TransportLayer *>();
-  layers->push(new TransportLayerDummy(true, &destroyed2));
-  layers->push(new TransportLayerDummy(true, &destroyed3));
-  rv = flow->PushLayers(layers);
-
-  ASSERT_TRUE(NS_FAILED(rv));
-  ASSERT_EQ(true, destroyed2);
-  ASSERT_EQ(true, destroyed3);
-}
-
 }  // end namespace
--- a/media/mtransport/test_nr_socket.h
+++ b/media/mtransport/test_nr_socket.h
@@ -96,16 +96,17 @@ extern "C" {
 #include <set>
 #include <vector>
 #include <map>
 #include <list>
 #include <string>
 
 #include "mozilla/UniquePtr.h"
 #include "prinrval.h"
+#include "mediapacket.h"
 
 namespace mozilla {
 
 class TestNrSocket;
 
 /**
  * A group of TestNrSockets that behave as if they were behind the same NAT.
  * @note We deliberately avoid addref/release of TestNrSocket here to avoid
@@ -236,24 +237,25 @@ class TestNrSocket : public NrSocketBase
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNrSocket, override)
 
   private:
     virtual ~TestNrSocket();
 
     class UdpPacket {
       public:
         UdpPacket(const void *msg, size_t len, const nr_transport_addr &addr) :
-          buffer_(new DataBuffer(static_cast<const uint8_t*>(msg), len)) {
+          buffer_(new MediaPacket) {
+          buffer_->Copy(static_cast<const uint8_t*>(msg), len);
           // TODO(bug 1170299): Remove const_cast when no longer necessary
           nr_transport_addr_copy(&remote_address_,
                                  const_cast<nr_transport_addr*>(&addr));
         }
 
         nr_transport_addr remote_address_;
-        UniquePtr<DataBuffer> buffer_;
+        UniquePtr<MediaPacket> buffer_;
 
         NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UdpPacket);
       private:
         ~UdpPacket(){}
     };
 
     class PortMapping {
       public:
@@ -285,24 +287,25 @@ class TestNrSocket : public NrSocketBase
 
     struct DeferredPacket {
       DeferredPacket(TestNrSocket *sock,
                      const void *data, size_t len,
                      int flags,
                      nr_transport_addr *addr,
                      RefPtr<NrSocketBase> internal_socket) :
           socket_(sock),
-          buffer_(reinterpret_cast<const uint8_t *>(data), len),
+          buffer_(),
           flags_(flags),
           internal_socket_(internal_socket) {
+        buffer_.Copy(reinterpret_cast<const uint8_t *>(data), len);
         nr_transport_addr_copy(&to_, addr);
       }
 
       TestNrSocket *socket_;
-      DataBuffer buffer_;
+      MediaPacket buffer_;
       int flags_;
       nr_transport_addr to_;
       RefPtr<NrSocketBase> internal_socket_;
     };
 
     bool is_port_mapping_stale(const PortMapping &port_mapping) const;
     bool allow_ingress(const nr_transport_addr &from,
                        PortMapping **port_mapping_used) const;
--- a/media/mtransport/transportflow.cpp
+++ b/media/mtransport/transportflow.cpp
@@ -9,30 +9,21 @@
 
 #include "logging.h"
 #include "runnable_utils.h"
 #include "transportflow.h"
 #include "transportlayer.h"
 
 namespace mozilla {
 
-MOZ_MTLOG_MODULE("mtransport")
-
 NS_IMPL_ISUPPORTS0(TransportFlow)
 
 // There are some hacks here to allow destruction off of
 // the main thread.
 TransportFlow::~TransportFlow() {
-  // Make sure that if we are off the right thread, we have
-  // no more attached signals.
-  if (!CheckThreadInt()) {
-    MOZ_ASSERT(SignalStateChange.is_empty());
-    MOZ_ASSERT(SignalPacketReceived.is_empty());
-  }
-
   // Push the destruction onto the STS thread. Note that there
   // is still some possibility that someone is accessing this
   // object simultaneously, but as long as smart pointer discipline
   // is maintained, it shouldn't be possible to access and
   // destroy it simultaneously. The conversion to an nsAutoPtr
   // ensures automatic destruction of the queue at exit of
   // DestroyFinal.
   if (target_) {
@@ -42,216 +33,50 @@ TransportFlow::~TransportFlow() {
                   NS_DISPATCH_NORMAL);
   }
 }
 
 void TransportFlow::DestroyFinal(nsAutoPtr<std::deque<TransportLayer *> > layers) {
   ClearLayers(layers.get());
 }
 
-void TransportFlow::ClearLayers(std::queue<TransportLayer *>* layers) {
-  while (!layers->empty()) {
-    delete layers->front();
-    layers->pop();
-  }
-}
-
 void TransportFlow::ClearLayers(std::deque<TransportLayer *>* layers) {
   while (!layers->empty()) {
     delete layers->front();
     layers->pop_front();
   }
 }
 
-nsresult TransportFlow::PushLayer(TransportLayer *layer) {
-  CheckThread();
-  UniquePtr<TransportLayer> layer_tmp(layer);  // Destroy on failure.
-
-  // Don't allow pushes once we are in error state.
-  if (state_ == TransportLayer::TS_ERROR) {
-    MOZ_MTLOG(ML_ERROR, id_ + ": Can't call PushLayer in error state for flow");
-    return NS_ERROR_FAILURE;
-  }
-
-  nsresult rv = layer->Init();
-  if (!NS_SUCCEEDED(rv)) {
-    // Destroy the rest of the flow, because it's no longer in an acceptable
-    // state.
-    ClearLayers(layers_.get());
-
-    // Set ourselves to have failed.
-    MOZ_MTLOG(ML_ERROR, id_ << ": Layer initialization failed; invalidating");
-    StateChangeInt(TransportLayer::TS_ERROR);
-
-    return rv;
-  }
-  EnsureSameThread(layer);
-
-  TransportLayer *old_layer = layers_->empty() ? nullptr : layers_->front();
-
-  // Re-target my signals to the new layer
-  if (old_layer) {
-    old_layer->SignalStateChange.disconnect(this);
-    old_layer->SignalPacketReceived.disconnect(this);
-  }
-  layers_->push_front(layer_tmp.release());
-  layer->Inserted(this, old_layer);
-
-  layer->SignalStateChange.connect(this, &TransportFlow::StateChange);
-  layer->SignalPacketReceived.connect(this, &TransportFlow::PacketReceived);
-  StateChangeInt(layer->state());
-
-  return NS_OK;
-}
-
-// This is all-or-nothing.
-nsresult TransportFlow::PushLayers(nsAutoPtr<std::queue<TransportLayer *> > layers) {
+void TransportFlow::PushLayer(TransportLayer* layer) {
   CheckThread();
-
-  MOZ_ASSERT(!layers->empty());
-  if (layers->empty()) {
-    MOZ_MTLOG(ML_ERROR, id_ << ": Can't call PushLayers with empty layers");
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  // Don't allow pushes once we are in error state.
-  if (state_ == TransportLayer::TS_ERROR) {
-    MOZ_MTLOG(ML_ERROR,
-              id_ << ": Can't call PushLayers in error state for flow ");
-    ClearLayers(layers.get());
-    return NS_ERROR_FAILURE;
-  }
-
-  nsresult rv = NS_OK;
-
-  // Disconnect all the old signals.
-  disconnect_all();
-
-  TransportLayer *layer;
-
-  while (!layers->empty()) {
-    TransportLayer *old_layer = layers_->empty() ? nullptr : layers_->front();
-    layer = layers->front();
-
-    rv = layer->Init();
-    if (NS_FAILED(rv)) {
-      MOZ_MTLOG(ML_ERROR,
-                id_ << ": Layer initialization failed; invalidating flow ");
-      break;
-    }
-
-    EnsureSameThread(layer);
-
-    // Push the layer onto the queue.
-    layers_->push_front(layer);
-    layers->pop();
-    layer->Inserted(this, old_layer);
-  }
-
-  if (NS_FAILED(rv)) {
-    // Destroy any layers we could not push.
-    ClearLayers(layers.get());
-
-    // Now destroy the rest of the flow, because it's no longer
-    // in an acceptable state.
-    ClearLayers(layers_.get());
-
-    // Set ourselves to have failed.
-    StateChangeInt(TransportLayer::TS_ERROR);
-
-    // Return failure.
-    return rv;
-  }
-
-  // Finally, attach ourselves to the top layer.
-  layer->SignalStateChange.connect(this, &TransportFlow::StateChange);
-  layer->SignalPacketReceived.connect(this, &TransportFlow::PacketReceived);
-  StateChangeInt(layer->state());  // Signals if the state changes.
-
-  return NS_OK;
-}
-
-TransportLayer *TransportFlow::top() const {
-  CheckThread();
-
-  return layers_->empty() ? nullptr : layers_->front();
+  layers_->push_front(layer);
+  EnsureSameThread(layer);
+  layer->SetFlowId(id_);
 }
 
 TransportLayer *TransportFlow::GetLayer(const std::string& id) const {
   CheckThread();
 
-  for (std::deque<TransportLayer *>::const_iterator it = layers_->begin();
-       it != layers_->end(); ++it) {
-    if ((*it)->id() == id)
-      return *it;
+  if (layers_) {
+    for (TransportLayer* layer : *layers_) {
+      if (layer->id() == id)
+        return layer;
+    }
   }
 
   return nullptr;
 }
 
-TransportLayer::State TransportFlow::state() {
-  CheckThread();
-
-  return state_;
-}
-
-TransportResult TransportFlow::SendPacket(const unsigned char *data,
-                                          size_t len) {
-  CheckThread();
-
-  if (state_ != TransportLayer::TS_OPEN) {
-    return TE_ERROR;
-  }
-  return top() ? top()->SendPacket(data, len) : TE_ERROR;
-}
-
-bool TransportFlow::Contains(TransportLayer *layer) const {
-  if (layers_) {
-    for (auto& l : *layers_) {
-      if (l == layer) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
 void TransportFlow::EnsureSameThread(TransportLayer *layer)  {
   // Enforce that if any of the layers have a thread binding,
   // they all have the same binding.
   if (target_) {
     const nsCOMPtr<nsIEventTarget>& lthread = layer->GetThread();
 
     if (lthread && (lthread != target_))
       MOZ_CRASH();
   }
   else {
     target_ = layer->GetThread();
   }
 }
 
-void TransportFlow::StateChangeInt(TransportLayer::State state) {
-  CheckThread();
-
-  if (state == state_) {
-    return;
-  }
-
-  state_ = state;
-  SignalStateChange(this, state_);
-}
-
-void TransportFlow::StateChange(TransportLayer *layer,
-                                TransportLayer::State state) {
-  CheckThread();
-
-  StateChangeInt(state);
-}
-
-void TransportFlow::PacketReceived(TransportLayer* layer,
-                                   const unsigned char *data,
-                                   size_t len) {
-  CheckThread();
-
-  SignalPacketReceived(this, data, len);
-}
-
 }  // close namespace
--- a/media/mtransport/transportflow.h
+++ b/media/mtransport/transportflow.h
@@ -5,17 +5,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Original author: ekr@rtfm.com
 
 #ifndef transportflow_h__
 #define transportflow_h__
 
 #include <deque>
-#include <queue>
 #include <string>
 
 #include "nscore.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/UniquePtr.h"
 #include "transportlayer.h"
 #include "m_cpp_utils.h"
 #include "nsAutoPtr.h"
@@ -44,63 +43,37 @@
 // restriction by thread-locking the signals, but previous
 // attempts have caused deadlocks.
 //
 // Most of these invariants are enforced by hard asserts
 // (i.e., those which fire even in production builds).
 
 namespace mozilla {
 
-class TransportFlow final : public nsISupports,
-                            public sigslot::has_slots<> {
+class TransportFlow final : public nsISupports {
  public:
   TransportFlow()
     : id_("(anonymous)"),
-      state_(TransportLayer::TS_NONE),
       layers_(new std::deque<TransportLayer *>) {}
   explicit TransportFlow(const std::string id)
     : id_(id),
-      state_(TransportLayer::TS_NONE),
       layers_(new std::deque<TransportLayer *>) {}
 
   const std::string& id() const { return id_; }
 
   // Layer management. Note PushLayer() is not thread protected, so
   // either:
   // (a) Do it in the thread handling the I/O
   // (b) Do it before you activate the I/O system
   //
   // The flow takes ownership of the layers after a successful
   // push.
-  nsresult PushLayer(TransportLayer *layer);
-
-  // Convenience function to push multiple layers on. Layers
-  // are pushed on in the order that they are in the queue.
-  // Any failures cause the flow to become inoperable and
-  // destroys all the layers including those already pushed.
-  // TODO(ekr@rtfm.com): Change layers to be ref-counted.
-  nsresult PushLayers(nsAutoPtr<std::queue<TransportLayer *> > layers);
-
-  TransportLayer *top() const;
-  TransportLayer *GetLayer(const std::string& id) const;
+  void PushLayer(TransportLayer* layer);
 
-  // Wrappers for whatever TLayer happens to be the top layer
-  // at the time. This way you don't need to do top()->Foo().
-  TransportLayer::State state(); // Current state
-  TransportResult SendPacket(const unsigned char *data, size_t len);
-
-  // State has changed. Reflects the top flow.
-  sigslot::signal2<TransportFlow *, TransportLayer::State>
-    SignalStateChange;
-
-  // Data received on the flow
-  sigslot::signal3<TransportFlow*, const unsigned char *, size_t>
-    SignalPacketReceived;
-
-  bool Contains(TransportLayer *layer) const;
+  TransportLayer *GetLayer(const std::string& id) const;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
  private:
   ~TransportFlow();
 
   DISALLOW_COPY_ASSIGN(TransportFlow);
 
@@ -118,26 +91,20 @@ class TransportFlow final : public nsISu
     if (NS_FAILED(target_->IsOnCurrentThread(&on)))
       return false;
 
     return on;
   }
 
   void EnsureSameThread(TransportLayer *layer);
 
-  void StateChange(TransportLayer *layer, TransportLayer::State state);
-  void StateChangeInt(TransportLayer::State state);
-  void PacketReceived(TransportLayer* layer, const unsigned char *data,
-      size_t len);
   static void DestroyFinal(nsAutoPtr<std::deque<TransportLayer *> > layers);
 
   // Overload needed because we use deque internally and queue externally.
   static void ClearLayers(std::deque<TransportLayer *>* layers);
-  static void ClearLayers(std::queue<TransportLayer *>* layers);
 
   std::string id_;
-  TransportLayer::State state_;
   UniquePtr<std::deque<TransportLayer *>> layers_;
   nsCOMPtr<nsIEventTarget> target_;
 };
 
 }  // close namespace
 #endif
--- a/media/mtransport/transportlayer.cpp
+++ b/media/mtransport/transportlayer.cpp
@@ -25,19 +25,18 @@ nsresult TransportLayer::Init() {
     state_ = TS_ERROR;
     return rv;
   }
   state_ = TS_INIT;
 
   return NS_OK;
 }
 
-void TransportLayer::Inserted(TransportFlow *flow, TransportLayer *downward) {
+void TransportLayer::Chain(TransportLayer *downward) {
   downward_ = downward;
-  flow_id_ = flow->id();
   MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Inserted: downward='" <<
     (downward ? downward->id(): "none") << "'");
 
   WasInserted();
 }
 
 void TransportLayer::SetState(State state, const char *file, unsigned line) {
   if (state != state_) {
--- a/media/mtransport/transportlayer.h
+++ b/media/mtransport/transportlayer.h
@@ -12,16 +12,17 @@
 #include "sigslot.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIEventTarget.h"
 
 #include "m_cpp_utils.h"
+#include "mediapacket.h"
 
 namespace mozilla {
 
 class TransportFlow;
 
 typedef int TransportResult;
 
 enum {
@@ -46,38 +47,38 @@ class TransportLayer : public sigslot::h
     downward_(nullptr) {}
 
   virtual ~TransportLayer() {}
 
   // Called to initialize
   nsresult Init();  // Called by Insert() to set up -- do not override
   virtual nsresult InitInternal() { return NS_OK; } // Called by Init
 
-  // Called when inserted into a flow
-  virtual void Inserted(TransportFlow *flow, TransportLayer *downward);
+  void SetFlowId(const std::string& flow_id) {flow_id_ = flow_id;}
+
+  virtual void Chain(TransportLayer *downward);
 
   // Downward interface
   TransportLayer *downward() { return downward_; }
 
   // Get the state
   State state() const { return state_; }
   // Must be implemented by derived classes
-  virtual TransportResult SendPacket(const unsigned char *data, size_t len) = 0;
+  virtual TransportResult SendPacket(MediaPacket& packet) = 0;
 
   // Get the thread.
   const nsCOMPtr<nsIEventTarget> GetThread() const {
     return target_;
   }
 
   // Event definitions that one can register for
   // State has changed
   sigslot::signal2<TransportLayer*, State> SignalStateChange;
   // Data received on the flow
-  sigslot::signal3<TransportLayer*, const unsigned char *, size_t>
-                         SignalPacketReceived;
+  sigslot::signal2<TransportLayer*, MediaPacket&> SignalPacketReceived;
 
   // Return the layer id for this layer
   virtual const std::string id() const = 0;
 
   // The id of the flow
   const std::string& flow_id() const {
     return flow_id_;
   }
--- a/media/mtransport/transportlayerdtls.cpp
+++ b/media/mtransport/transportlayerdtls.cpp
@@ -10,17 +10,16 @@
 
 #include <algorithm>
 #include <queue>
 #include <sstream>
 
 #include "dtlsidentity.h"
 #include "keyhi.h"
 #include "logging.h"
-#include "mozilla/Move.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "nsCOMPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIEventTarget.h"
 #include "nsNetCID.h"
@@ -58,66 +57,57 @@ static PRDescIdentity transport_layer_id
 // - The TransportLayerNSPRAdapter is a PRFileDesc containing a
 //   FIFO.
 // - When TransportLayerDtls.PacketReceived() is called, we insert
 //   the packets in the FIFO and then do a PR_Recv() on the NSS
 //   PRFileDesc, which eventually reads off the FIFO.
 //
 // All of this stuff is assumed to happen solely in a single thread
 // (generally the SocketTransportService thread)
-struct Packet {
-  Packet() : data_(nullptr), len_(0) {}
 
-  void Assign(const void *data, int32_t len) {
-    data_.reset(new uint8_t[len]);
-    memcpy(data_.get(), data, len);
-    len_ = len;
-  }
-
-  UniquePtr<uint8_t[]> data_;
-  int32_t len_;
-};
-
-void TransportLayerNSPRAdapter::PacketReceived(const void *data, int32_t len) {
+void TransportLayerNSPRAdapter::PacketReceived(MediaPacket& packet) {
   if (enabled_) {
-    input_.push(new Packet());
-    input_.back()->Assign(data, len);
+    input_.push(new MediaPacket(std::move(packet)));
   }
 }
 
 int32_t TransportLayerNSPRAdapter::Recv(void *buf, int32_t buflen) {
   if (input_.empty()) {
     PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
     return -1;
   }
 
-  Packet* front = input_.front();
-  if (buflen < front->len_) {
+  MediaPacket* front = input_.front();
+  int32_t count = static_cast<int32_t>(front->len());
+
+  if (buflen < count) {
     MOZ_ASSERT(false, "Not enough buffer space to receive into");
     PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
     return -1;
   }
 
-  int32_t count = front->len_;
-  memcpy(buf, front->data_.get(), count);
+  memcpy(buf, front->data(), count);
 
   input_.pop();
   delete front;
 
   return count;
 }
 
 int32_t TransportLayerNSPRAdapter::Write(const void *buf, int32_t length) {
   if (!enabled_) {
     MOZ_MTLOG(ML_WARNING, "Writing to disabled transport layer");
     return -1;
   }
 
-  TransportResult r = output_->SendPacket(
-      static_cast<const unsigned char *>(buf), length);
+  MediaPacket packet;
+  // Copies. Oh well.
+  packet.Copy(static_cast<const uint8_t*>(buf), static_cast<size_t>(length));
+
+  TransportResult r = output_->SendPacket(packet);
   if (r >= 0) {
     return r;
   }
 
   if (r == TE_WOULDBLOCK) {
     PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
   } else {
     PR_SetError(PR_IO_ERROR, 0);
@@ -984,51 +974,64 @@ bool TransportLayerDtls::CheckAlpn() {
     return false;
   }
   alpn_ = chosen;
   return true;
 }
 
 
 void TransportLayerDtls::PacketReceived(TransportLayer* layer,
-                                        const unsigned char *data,
-                                        size_t len) {
+                                        MediaPacket& packet) {
   CheckThread();
-  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << len << ")");
+  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << packet.len() << ")");
 
   if (state_ != TS_CONNECTING && state_ != TS_OPEN) {
     MOZ_MTLOG(ML_DEBUG,
               LAYER_INFO << "Discarding packet in inappropriate state");
     return;
   }
 
-  // not DTLS per RFC 7983
-  if (data[0] < 20 || data[0] > 63) {
+  if (!packet.data()) {
+    // Something ate this, probably the SRTP layer
     return;
   }
 
-  nspr_io_adapter_->PacketReceived(data, len);
+  // not DTLS per RFC 7983
+  if (packet.data()[0] < 20 || packet.data()[0] > 63) {
+    return;
+  }
 
+  nspr_io_adapter_->PacketReceived(packet);
+  GetDecryptedPackets();
+}
+
+void
+TransportLayerDtls::GetDecryptedPackets()
+{
   // If we're still connecting, try to handshake
   if (state_ == TS_CONNECTING) {
     Handshake();
   }
 
   // Now try a recv if we're open, since there might be data left
   if (state_ == TS_OPEN) {
-    // nICEr uses a 9216 bytes buffer to allow support for jumbo frames
-    unsigned char buf[9216];
     int32_t rv;
     // One packet might contain several DTLS packets
     do {
-      rv = PR_Recv(ssl_fd_.get(), buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT);
+      // nICEr uses a 9216 bytes buffer to allow support for jumbo frames
+      // Can we peek to get a better idea of the actual size?
+      static const size_t kBufferSize = 9216;
+      auto buffer = MakeUnique<uint8_t[]>(kBufferSize);
+      rv = PR_Recv(ssl_fd_.get(), buffer.get(), kBufferSize, 0, PR_INTERVAL_NO_WAIT);
       if (rv > 0) {
         // We have data
         MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS");
-        SignalPacketReceived(this, buf, rv);
+        MediaPacket packet;
+        packet.Take(std::move(buffer), static_cast<size_t>(rv));
+        SignalPacketReceived(this, packet);
       } else if (rv == 0) {
         TL_SET_STATE(TS_CLOSED);
       } else {
         int32_t err = PR_GetError();
 
         if (err == PR_WOULD_BLOCK_ERROR) {
           // This gets ignored
           MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Receive would have blocked");
@@ -1064,26 +1067,26 @@ void TransportLayerDtls::SetState(State 
     }
   } else {
     MOZ_ASSERT(false, "Invalid state transition");
   }
 
   TransportLayer::SetState(state, file, line);
 }
 
-TransportResult TransportLayerDtls::SendPacket(const unsigned char *data,
-                                               size_t len) {
+TransportResult TransportLayerDtls::SendPacket(MediaPacket& packet) {
   CheckThread();
   if (state_ != TS_OPEN) {
     MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Can't call SendPacket() in state "
               << state_);
     return TE_ERROR;
   }
 
-  int32_t rv = PR_Send(ssl_fd_.get(), data, len, 0, PR_INTERVAL_NO_WAIT);
+  int32_t rv = PR_Send(ssl_fd_.get(), packet.data(), packet.len(), 0,
+      PR_INTERVAL_NO_WAIT);
 
   if (rv > 0) {
     // We have data
     MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Wrote " << rv << " bytes to SSL Layer");
     return rv;
   }
 
   if (rv == 0) {
--- a/media/mtransport/transportlayerdtls.h
+++ b/media/mtransport/transportlayerdtls.h
@@ -18,40 +18,39 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsIEventTarget.h"
 #include "nsITimer.h"
 #include "ScopedNSSTypes.h"
 #include "m_cpp_utils.h"
 #include "dtlsidentity.h"
-#include "transportflow.h"
 #include "transportlayer.h"
 
 namespace mozilla {
 
 struct Packet;
 
 class TransportLayerNSPRAdapter {
  public:
   explicit TransportLayerNSPRAdapter(TransportLayer *output) :
   output_(output),
   input_(),
   enabled_(true) {}
 
-  void PacketReceived(const void *data, int32_t len);
+  void PacketReceived(MediaPacket& packet);
   int32_t Recv(void *buf, int32_t buflen);
   int32_t Write(const void *buf, int32_t length);
   void SetEnabled(bool enabled) { enabled_ = enabled; }
 
  private:
   DISALLOW_COPY_ASSIGN(TransportLayerNSPRAdapter);
 
   TransportLayer *output_;
-  std::queue<Packet *> input_;
+  std::queue<MediaPacket *> input_;
   bool enabled_;
 };
 
 class TransportLayerDtls final : public TransportLayer {
  public:
   TransportLayerDtls() :
       role_(CLIENT),
       verification_mode_(VERIFY_UNSET),
@@ -90,22 +89,21 @@ class TransportLayerDtls final : public 
                                 bool use_context,
                                 const std::string& context,
                                 unsigned char *out,
                                 unsigned int outlen);
 
   // Transport layer overrides.
   nsresult InitInternal() override;
   void WasInserted() override;
-  TransportResult SendPacket(const unsigned char *data, size_t len) override;
+  TransportResult SendPacket(MediaPacket& packet) override;
 
   // Signals
   void StateChange(TransportLayer *layer, State state);
-  void PacketReceived(TransportLayer* layer, const unsigned char *data,
-                      size_t len);
+  void PacketReceived(TransportLayer* layer, MediaPacket& packet);
 
   // For testing use only.  Returns the fd.
   PRFileDesc* internal_fd() { CheckThread(); return ssl_fd_.get(); }
 
   TRANSPORT_LAYER_ID("dtls")
 
   protected:
   void SetState(State state, const char *file, unsigned line) override;
@@ -135,16 +133,17 @@ class TransportLayerDtls final : public 
     ~VerificationDigest() {}
     DISALLOW_COPY_ASSIGN(VerificationDigest);
   };
 
 
   bool Setup();
   bool SetupCipherSuites(UniquePRFileDesc& ssl_fd) const;
   bool SetupAlpn(UniquePRFileDesc& ssl_fd) const;
+  void GetDecryptedPackets();
   void Handshake();
 
   bool CheckAlpn();
 
   static SECStatus GetClientAuthDataHook(void *arg, PRFileDesc *fd,
                                          CERTDistNames *caNames,
                                          CERTCertificate **pRetCert,
                                          SECKEYPrivateKey **pRetKey);
--- a/media/mtransport/transportlayerice.cpp
+++ b/media/mtransport/transportlayerice.cpp
@@ -166,32 +166,31 @@ void TransportLayerIce::RestoreOldStream
   } else if (stream_->state() == NrIceMediaStream::ICE_CLOSED) {
     IceFailed(stream_);
   }
   // No events are fired when the stream is ICE_CONNECTING.  If the
   // restored stream is ICE_CONNECTING, IceReady/IceFailed will fire
   // later.
 }
 
-TransportResult TransportLayerIce::SendPacket(const unsigned char *data,
-                                              size_t len) {
+TransportResult TransportLayerIce::SendPacket(MediaPacket& packet) {
   CheckThread();
   // use old_stream_ until stream_ is ready
   nsresult res = (old_stream_?old_stream_:stream_)->SendPacket(component_,
-                                                               data,
-                                                               len);
+                                                               packet.data(),
+                                                               packet.len());
 
   if (!NS_SUCCEEDED(res)) {
     return (res == NS_BASE_STREAM_WOULD_BLOCK) ?
         TE_WOULDBLOCK : TE_ERROR;
   }
 
-  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " SendPacket(" << len << ") succeeded");
+  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " SendPacket(" << packet.len() << ") succeeded");
 
-  return len;
+  return packet.len();
 }
 
 
 void TransportLayerIce::IceCandidate(NrIceMediaStream *stream,
                                      const std::string&) {
   // NO-OP for now
 }
 
@@ -222,12 +221,17 @@ void TransportLayerIce::IcePacketReceive
   CheckThread();
   // We get packets for both components, so ignore the ones that aren't
   // for us.
   if (component_ != component)
     return;
 
   MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << stream->name() << ","
     << component << "," << len << ")");
-  SignalPacketReceived(this, data, len);
+  // Might be useful to allow MediaPacket to borrow a buffer (ie; not take
+  // ownership, but copy it if the MediaPacket is moved). This could be a
+  // footgun though with MediaPackets that end up on the heap.
+  MediaPacket packet;
+  packet.Copy(data, len);
+  SignalPacketReceived(this, packet);
 }
 
 }  // close namespace
--- a/media/mtransport/transportlayerice.h
+++ b/media/mtransport/transportlayerice.h
@@ -36,17 +36,17 @@ class TransportLayerIce : public Transpo
 
   void SetParameters(RefPtr<NrIceMediaStream> stream,
                      int component);
 
   void ResetOldStream(); // called after successful ice restart
   void RestoreOldStream(); // called after unsuccessful ice restart
 
   // Transport layer overrides.
-  TransportResult SendPacket(const unsigned char *data, size_t len) override;
+  TransportResult SendPacket(MediaPacket& packet) override;
 
   // Slots for ICE
   void IceCandidate(NrIceMediaStream *stream, const std::string&);
   void IceReady(NrIceMediaStream *stream);
   void IceFailed(NrIceMediaStream *stream);
   void IcePacketReceived(NrIceMediaStream *stream, int component,
                          const unsigned char *data, int len);
 
--- a/media/mtransport/transportlayerlog.cpp
+++ b/media/mtransport/transportlayerlog.cpp
@@ -20,34 +20,31 @@ void TransportLayerLogging::WasInserted(
         this, &TransportLayerLogging::StateChange);
     downward_->SignalPacketReceived.connect(
         this, &TransportLayerLogging::PacketReceived);
     TL_SET_STATE(downward_->state());
   }
 }
 
 TransportResult
-TransportLayerLogging::SendPacket(const unsigned char *data, size_t len) {
-  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << len << ")");
+TransportLayerLogging::SendPacket(MediaPacket& packet) {
+  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << packet.len() << ")");
 
   if (downward_) {
-    return downward_->SendPacket(data, len);
+    return downward_->SendPacket(packet);
   }
-  return static_cast<TransportResult>(len);
+  return static_cast<TransportResult>(packet.len());
 }
 
 void TransportLayerLogging::StateChange(TransportLayer *layer, State state) {
   MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Received StateChange to " << state);
 
   TL_SET_STATE(state);
 }
 
 void TransportLayerLogging::PacketReceived(TransportLayer* layer,
-                                           const unsigned char *data,
-                                           size_t len) {
-  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << len << ")");
+                                           MediaPacket& packet) {
+  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << packet.len() << ")");
 
-  SignalPacketReceived(this, data, len);
+  SignalPacketReceived(this, packet);
 }
 
-
-
 }  // close namespace
--- a/media/mtransport/transportlayerlog.h
+++ b/media/mtransport/transportlayerlog.h
@@ -15,22 +15,21 @@
 
 namespace mozilla {
 
 class TransportLayerLogging : public TransportLayer {
 public:
   TransportLayerLogging() {}
 
   // Overrides for TransportLayer
-  TransportResult SendPacket(const unsigned char *data, size_t len) override;
+  TransportResult SendPacket(MediaPacket& packet) override;
 
   // Signals (forwarded to upper layer)
   void StateChange(TransportLayer *layer, State state);
-  void PacketReceived(TransportLayer* layer, const unsigned char *data,
-                      size_t len);
+  void PacketReceived(TransportLayer* layer, MediaPacket& packet);
 
   TRANSPORT_LAYER_ID("log")
 
 protected:
   void WasInserted() override;
 
 private:
   DISALLOW_COPY_ASSIGN(TransportLayerLogging);
--- a/media/mtransport/transportlayerloopback.cpp
+++ b/media/mtransport/transportlayerloopback.cpp
@@ -55,70 +55,67 @@ nsresult TransportLayerLoopback::Init() 
 // Connect to the other side
 void TransportLayerLoopback::Connect(TransportLayerLoopback* peer) {
   peer_ = peer;
 
   TL_SET_STATE(TS_OPEN);
 }
 
 TransportResult
-TransportLayerLoopback::SendPacket(const unsigned char *data, size_t len) {
-  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << len << ")");
+TransportLayerLoopback::SendPacket(MediaPacket& packet) {
+  MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << packet.len() << ")");
 
   if (!peer_) {
     MOZ_MTLOG(ML_ERROR, "Discarding packet because peer not attached");
     return TE_ERROR;
   }
 
-  nsresult res = peer_->QueuePacket(data, len);
+  size_t len = packet.len();
+  nsresult res = peer_->QueuePacket(packet);
   if (!NS_SUCCEEDED(res))
     return TE_ERROR;
 
   return static_cast<TransportResult>(len);
 }
 
-nsresult TransportLayerLoopback::QueuePacket(const unsigned char *data,
-                                         size_t len) {
+nsresult TransportLayerLoopback::QueuePacket(MediaPacket& packet) {
   MOZ_ASSERT(packets_lock_);
 
   PR_Lock(packets_lock_);
 
   if (combinePackets_ && !packets_.empty()) {
-    QueuedPacket *packet = packets_.front();
-    packets_.pop();
+    MediaPacket *prevPacket = packets_.front();
 
-    MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing combined packets of length " << packet->len() << " and " << len);
-    packets_.push(new QueuedPacket());
-    packets_.back()->Assign(packet->data(), packet->len(),
-                            data, len);
+    MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing combined packets of length " << prevPacket->len() << " and " << packet.len());
+    auto combined = MakeUnique<uint8_t[]>(prevPacket->len() + packet.len());
+    memcpy(combined.get(), prevPacket->data(), prevPacket->len());
+    memcpy(combined.get() + prevPacket->len(), packet.data(), packet.len());
+    prevPacket->Take(std::move(combined), prevPacket->len() + packet.len());
   } else {
-    MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing packet of length " << len);
-    packets_.push(new QueuedPacket());
-    packets_.back()->Assign(data, len);
+    MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing packet of length " << packet.len());
+    packets_.push(new MediaPacket(std::move(packet)));
   }
 
   PRStatus r = PR_Unlock(packets_lock_);
   MOZ_ASSERT(r == PR_SUCCESS);
   if (r != PR_SUCCESS)
     return NS_ERROR_FAILURE;
 
   return NS_OK;
 }
 
 
 void TransportLayerLoopback::DeliverPackets() {
   while (!packets_.empty()) {
-    QueuedPacket *packet = packets_.front();
+    UniquePtr<MediaPacket> packet(packets_.front());
     packets_.pop();
 
     MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Delivering packet of length " <<
          packet->len());
-    SignalPacketReceived(this, packet->data(), packet->len());
-
-    delete packet;
+    SignalPacketReceived(this, *packet);
   }
 }
 
 NS_IMPL_ISUPPORTS(TransportLayerLoopback::Deliverer, nsITimerCallback, nsINamed)
 
 NS_IMETHODIMP TransportLayerLoopback::Deliverer::Notify(nsITimer *timer) {
   if (!layer_)
     return NS_OK;
--- a/media/mtransport/transportlayerloopback.h
+++ b/media/mtransport/transportlayerloopback.h
@@ -37,17 +37,17 @@ class TransportLayerLoopback : public Tr
       timer_(nullptr),
       packets_(),
       packets_lock_(nullptr),
       deliverer_(nullptr),
       combinePackets_(false) {}
 
   ~TransportLayerLoopback() {
     while (!packets_.empty()) {
-      QueuedPacket *packet = packets_.front();
+      MediaPacket *packet = packets_.front();
       packets_.pop();
       delete packet;
     }
     if (packets_lock_) {
       PR_DestroyLock(packets_lock_);
     }
     timer_->Cancel();
     deliverer_->Detach();
@@ -67,61 +67,26 @@ class TransportLayerLoopback : public Tr
     if (peer) {
       peer->Disconnect();
     }
   }
 
   void CombinePackets(bool combine) { combinePackets_ = combine; }
 
   // Overrides for TransportLayer
-  TransportResult SendPacket(const unsigned char *data, size_t len) override;
+  TransportResult SendPacket(MediaPacket& packet) override;
 
   // Deliver queued packets
   void DeliverPackets();
 
   TRANSPORT_LAYER_ID("loopback")
 
  private:
   DISALLOW_COPY_ASSIGN(TransportLayerLoopback);
 
-  // A queued packet
-  class QueuedPacket {
-   public:
-    QueuedPacket() : data_(nullptr), len_(0) {}
-    ~QueuedPacket() {
-      delete [] data_;
-    }
-
-    void Assign(const unsigned char *data, size_t len) {
-      data_ = new unsigned char[len];
-      memcpy(static_cast<void *>(data_),
-             static_cast<const void *>(data), len);
-      len_ = len;
-    }
-
-    void Assign(const unsigned char *data1, size_t len1,
-                const unsigned char *data2, size_t len2) {
-      data_ = new unsigned char[len1 + len2];
-      memcpy(static_cast<void *>(data_),
-             static_cast<const void *>(data1), len1);
-      memcpy(static_cast<void *>(data_ + len1),
-             static_cast<const void *>(data2), len2);
-      len_ = len1 + len2;
-    }
-
-    const unsigned char *data() const { return data_; }
-    size_t len() const { return len_; }
-
-   private:
-    DISALLOW_COPY_ASSIGN(QueuedPacket);
-
-    unsigned char *data_;
-    size_t len_;
-  };
-
   // A timer to deliver packets if some are available
   // Fires every 100 ms
   class Deliverer : public nsITimerCallback
                   , public nsINamed {
    public:
     explicit Deliverer(TransportLayerLoopback *layer) :
         layer_(layer) {}
     void Detach() {
@@ -137,20 +102,20 @@ class TransportLayerLoopback : public Tr
     }
 
     DISALLOW_COPY_ASSIGN(Deliverer);
 
     TransportLayerLoopback *layer_;
   };
 
   // Queue a packet for delivery
-  nsresult QueuePacket(const unsigned char *data, size_t len);
+  nsresult QueuePacket(MediaPacket& packet);
 
   TransportLayerLoopback* peer_;
   nsCOMPtr<nsITimer> timer_;
-  std::queue<QueuedPacket *> packets_;
+  std::queue<MediaPacket *> packets_;
   PRLock *packets_lock_;
   RefPtr<Deliverer> deliverer_;
   bool combinePackets_;
 };
 
 }  // close namespace
 #endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayersrtp.cpp
@@ -0,0 +1,273 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include "transportlayersrtp.h"
+#include "transportlayerdtls.h"
+
+#include "logging.h"
+#include "nsError.h"
+#include "mozilla/Assertions.h"
+#include "transportlayerdtls.h"
+#include "srtp.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp";
+
+TransportLayerSrtp::TransportLayerSrtp(TransportLayerDtls& dtls)
+{
+  // We need to connect to the dtls layer, not the ice layer, because even
+  // though the packets that DTLS decrypts don't flow through us, we do base our
+  // keying information on the keying information established by the DTLS layer.
+  dtls.SignalStateChange.connect(this, &TransportLayerSrtp::StateChange);
+
+  TL_SET_STATE(dtls.state());
+}
+
+void
+TransportLayerSrtp::WasInserted()
+{
+  // Connect to the lower layers
+  if (!Setup()) {
+    TL_SET_STATE(TS_ERROR);
+  }
+}
+
+bool
+TransportLayerSrtp::Setup()
+{
+  CheckThread();
+  if (!downward_) {
+    MOZ_MTLOG(ML_ERROR, "SRTP layer with nothing below. This is useless");
+    return false;
+  }
+
+  // downward_ is the TransportLayerIce
+  downward_->SignalPacketReceived.connect(this, &TransportLayerSrtp::PacketReceived);
+
+  return true;
+}
+
+static bool IsRtp(const unsigned char* data, size_t len)
+{
+  if (len < 2)
+    return false;
+
+  // Check if this is a RTCP packet. Logic based on the types listed in
+  // media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc
+
+  // Anything outside this range is RTP.
+  if ((data[1] < 192) || (data[1] > 207))
+    return true;
+
+  if (data[1] == 192) // FIR
+    return false;
+
+  if (data[1] == 193) // NACK, but could also be RTP. This makes us sad
+    return true;      // but it's how webrtc.org behaves.
+
+  if (data[1] == 194)
+    return true;
+
+  if (data[1] == 195) // IJ.
+    return false;
+
+  if ((data[1] > 195) && (data[1] < 200)) // the > 195 is redundant
+    return true;
+
+  if ((data[1] >= 200) && (data[1] <= 207)) // SR, RR, SDES, BYE,
+    return false;                           // APP, RTPFB, PSFB, XR
+
+  MOZ_ASSERT(false); // Not reached, belt and suspenders.
+  return true;
+}
+
+TransportResult
+TransportLayerSrtp::SendPacket(MediaPacket& packet)
+{
+  if (packet.len() < 4) {
+    MOZ_ASSERT(false);
+    return TE_ERROR;
+  }
+
+  MOZ_ASSERT(packet.capacity() - packet.len() >= SRTP_MAX_EXPANSION);
+
+  int out_len;
+  nsresult res;
+  switch (packet.type()) {
+    case MediaPacket::RTP:
+      MOZ_MTLOG(ML_INFO, "Attempting to protect RTP...");
+      res = mSendSrtp->ProtectRtp(packet.data(), packet.len(), packet.capacity(), &out_len);
+      break;
+    case MediaPacket::RTCP:
+      MOZ_MTLOG(ML_INFO, "Attempting to protect RTCP...");
+      res = mSendSrtp->ProtectRtcp(packet.data(), packet.len(), packet.capacity(), &out_len);
+      break;
+    default:
+      MOZ_CRASH("SRTP layer asked to send packet that is neither RTP or RTCP");
+  }
+
+  if (NS_FAILED(res)) {
+    MOZ_MTLOG(ML_ERROR,
+                "Error protecting RTP/RTCP len=" << packet.len()
+                << "[" << std::hex
+                << packet.data()[0] << " "
+                << packet.data()[1] << " "
+                << packet.data()[2] << " "
+                << packet.data()[3]
+                << "]");
+    return TE_ERROR;
+  }
+
+  size_t unencrypted_len = packet.len();
+  packet.SetLength(out_len);
+
+  TransportResult bytes = downward_->SendPacket(packet);
+  if (bytes == out_len) {
+    // Whole packet was written, but the encrypted length might be different.
+    // Don't confuse the caller.
+    return unencrypted_len;
+  }
+
+  if (bytes == TE_WOULDBLOCK) {
+    return TE_WOULDBLOCK;
+  }
+
+  return TE_ERROR;
+}
+
+void
+TransportLayerSrtp::StateChange(TransportLayer* layer, State state)
+{
+  if (state == TS_OPEN) {
+    TransportLayerDtls* dtls = static_cast<TransportLayerDtls*>(layer);
+    MOZ_ASSERT(dtls); // DTLS is mandatory
+
+    uint16_t cipher_suite;
+    nsresult res = dtls->GetSrtpCipher(&cipher_suite);
+    if (NS_FAILED(res)) {
+      MOZ_MTLOG(ML_ERROR, "Failed to negotiate DTLS-SRTP. This is an error");
+      TL_SET_STATE(TS_ERROR);
+      return;
+    }
+
+    // SRTP Key Exporter as per RFC 5764 S 4.2
+    unsigned char srtp_block[SRTP_TOTAL_KEY_LENGTH * 2];
+    res = dtls->ExportKeyingMaterial(
+      kDTLSExporterLabel, false, "", srtp_block, sizeof(srtp_block));
+    if (NS_FAILED(res)) {
+      MOZ_MTLOG(ML_ERROR, "Failed to compute DTLS-SRTP keys. This is an error");
+      TL_SET_STATE(TS_ERROR);
+      return;
+    }
+
+    // Slice and dice as per RFC 5764 S 4.2
+    unsigned char client_write_key[SRTP_TOTAL_KEY_LENGTH];
+    unsigned char server_write_key[SRTP_TOTAL_KEY_LENGTH];
+    int offset = 0;
+    memcpy(client_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
+    offset += SRTP_MASTER_KEY_LENGTH;
+    memcpy(server_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
+    offset += SRTP_MASTER_KEY_LENGTH;
+    memcpy(client_write_key + SRTP_MASTER_KEY_LENGTH,
+           srtp_block + offset,
+           SRTP_MASTER_SALT_LENGTH);
+    offset += SRTP_MASTER_SALT_LENGTH;
+    memcpy(server_write_key + SRTP_MASTER_KEY_LENGTH,
+           srtp_block + offset,
+           SRTP_MASTER_SALT_LENGTH);
+    offset += SRTP_MASTER_SALT_LENGTH;
+    MOZ_ASSERT(offset == sizeof(srtp_block));
+
+    unsigned char* write_key;
+    unsigned char* read_key;
+
+    if (dtls->role() == TransportLayerDtls::CLIENT) {
+      write_key = client_write_key;
+      read_key = server_write_key;
+    } else {
+      write_key = server_write_key;
+      read_key = client_write_key;
+    }
+
+    MOZ_ASSERT(!mSendSrtp && !mRecvSrtp);
+    mSendSrtp =
+      SrtpFlow::Create(cipher_suite, false, write_key, SRTP_TOTAL_KEY_LENGTH);
+    mRecvSrtp =
+      SrtpFlow::Create(cipher_suite, true, read_key, SRTP_TOTAL_KEY_LENGTH);
+    if (!mSendSrtp || !mRecvSrtp) {
+      MOZ_MTLOG(ML_ERROR, "Couldn't create SRTP flow.");
+      TL_SET_STATE(TS_ERROR);
+      return;
+    }
+
+    MOZ_MTLOG(ML_INFO, "Created SRTP flow!");
+  }
+
+  TL_SET_STATE(state);
+}
+
+void
+TransportLayerSrtp::PacketReceived(TransportLayer* layer, MediaPacket& packet)
+{
+  if (state() != TS_OPEN) {
+    return;
+  }
+
+  if (!packet.data()) {
+    // Something ate this, probably the DTLS layer
+    return;
+  }
+
+  if (packet.len() < 4) {
+    return;
+  }
+
+  // not RTP/RTCP per RFC 7983
+  if (packet.data()[0] <= 127 || packet.data()[0] >= 192) {
+    return;
+  }
+
+  // We want to keep the encrypted packet around for packet dumping
+  packet.CopyDataToEncrypted();
+  int outLen;
+  nsresult res;
+
+  if (IsRtp(packet.data(), packet.len())) {
+    packet.SetType(MediaPacket::RTP);
+    MOZ_MTLOG(ML_INFO, "Attempting to unprotect RTP...");
+    res = mRecvSrtp->UnprotectRtp(packet.data(), packet.len(), packet.len(), &outLen);
+  } else {
+    packet.SetType(MediaPacket::RTCP);
+    MOZ_MTLOG(ML_INFO, "Attempting to unprotect RTCP...");
+    res = mRecvSrtp->UnprotectRtcp(packet.data(), packet.len(), packet.len(), &outLen);
+  }
+
+  if (NS_SUCCEEDED(res)) {
+    packet.SetLength(outLen);
+    SignalPacketReceived(this, packet);
+  } else {
+    // TODO: What do we do wrt packet dumping here? Maybe signal an empty
+    // packet? Signal the still-encrypted packet?
+    MOZ_MTLOG(ML_ERROR,
+                "Error unprotecting RTP/RTCP len=" << packet.len()
+                << "[" << std::hex
+                << packet.data()[0] << " "
+                << packet.data()[1] << " "
+                << packet.data()[2] << " "
+                << packet.data()[3]
+                << "]");
+  }
+}
+
+} // namespace mozilla
+
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayersrtp.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef transportlayersrtp_h__
+#define transportlayersrtp_h__
+
+#include <string>
+
+#include "transportlayer.h"
+#include "mozilla/RefPtr.h"
+#include "SrtpFlow.h"
+
+namespace mozilla {
+
+class TransportLayerDtls;
+
+class TransportLayerSrtp final : public TransportLayer {
+  public:
+    explicit TransportLayerSrtp(TransportLayerDtls& dtls);
+    virtual ~TransportLayerSrtp() {};
+
+    // Transport layer overrides.
+    void WasInserted() override;
+    TransportResult SendPacket(MediaPacket& packet) override;
+
+    // Signals
+    void StateChange(TransportLayer *layer, State state);
+    void PacketReceived(TransportLayer* layer, MediaPacket& packet);
+
+    TRANSPORT_LAYER_ID("srtp")
+
+  private:
+    bool Setup();
+    DISALLOW_COPY_ASSIGN(TransportLayerSrtp);
+    RefPtr<SrtpFlow> mSendSrtp;
+    RefPtr<SrtpFlow> mRecvSrtp;
+};
+
+
+}  // close namespace
+#endif
--- a/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
+++ b/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
@@ -19,16 +19,17 @@
 #include "MediaPipeline.h"
 #include "MediaPipelineFilter.h"
 #include "MediaStreamGraph.h"
 #include "MediaStreamListener.h"
 #include "MediaStreamTrack.h"
 #include "transportflow.h"
 #include "transportlayerloopback.h"
 #include "transportlayerdtls.h"
+#include "transportlayersrtp.h"
 #include "mozilla/SyncRunnable.h"
 #include "mtransport_test_utils.h"
 #include "SharedBuffer.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 
 using namespace mozilla;
@@ -164,92 +165,69 @@ public:
       }
     }
 };
 
 class TransportInfo {
  public:
   TransportInfo() :
     flow_(nullptr),
-    loopback_(nullptr),
-    dtls_(nullptr) {}
+    loopback_(nullptr) {}
 
   static void InitAndConnect(TransportInfo &client, TransportInfo &server) {
     client.Init(true);
     server.Init(false);
-    client.PushLayers();
-    server.PushLayers();
     client.Connect(&server);
     server.Connect(&client);
   }
 
   void Init(bool client) {
-    nsresult res;
-
-    flow_ = new TransportFlow();
-    loopback_ = new TransportLayerLoopback();
-    dtls_ = new TransportLayerDtls();
-
-    res = loopback_->Init();
-    if (res != NS_OK) {
-      FreeLayers();
-    }
-    ASSERT_EQ((nsresult)NS_OK, res);
+    UniquePtr<TransportLayerLoopback> loopback(new TransportLayerLoopback);
+    UniquePtr<TransportLayerDtls> dtls(new TransportLayerDtls);
+    UniquePtr<TransportLayerSrtp> srtp(new TransportLayerSrtp(*dtls));
 
     std::vector<uint16_t> ciphers;
     ciphers.push_back(SRTP_AES128_CM_HMAC_SHA1_80);
-    dtls_->SetSrtpCiphers(ciphers);
-    dtls_->SetIdentity(DtlsIdentity::Generate());
-    dtls_->SetRole(client ? TransportLayerDtls::CLIENT :
+    dtls->SetSrtpCiphers(ciphers);
+    dtls->SetIdentity(DtlsIdentity::Generate());
+    dtls->SetRole(client ? TransportLayerDtls::CLIENT :
       TransportLayerDtls::SERVER);
-    dtls_->SetVerificationAllowAll();
-  }
-
-  void PushLayers() {
-    nsresult res;
+    dtls->SetVerificationAllowAll();
 
-    nsAutoPtr<std::queue<TransportLayer *> > layers(
-      new std::queue<TransportLayer *>);
-    layers->push(loopback_);
-    layers->push(dtls_);
-    res = flow_->PushLayers(layers);
-    if (res != NS_OK) {
-      FreeLayers();
-    }
-    ASSERT_EQ((nsresult)NS_OK, res);
+    ASSERT_EQ(NS_OK, loopback->Init());
+    ASSERT_EQ(NS_OK, dtls->Init());
+    ASSERT_EQ(NS_OK, srtp->Init());
+
+    dtls->Chain(loopback.get());
+    srtp->Chain(loopback.get());
+
+    flow_ = new TransportFlow();
+    loopback_ = loopback.release();
+    flow_->PushLayer(loopback_);
+    flow_->PushLayer(dtls.release());
+    flow_->PushLayer(srtp.release());
   }
 
   void Connect(TransportInfo* peer) {
     MOZ_ASSERT(loopback_);
     MOZ_ASSERT(peer->loopback_);
 
     loopback_->Connect(peer->loopback_);
   }
 
-  // Free the memory allocated at the beginning of Init
-  // if failure occurs before layers setup.
-  void FreeLayers() {
-    delete loopback_;
-    loopback_ = nullptr;
-    delete dtls_;
-    dtls_ = nullptr;
-  }
-
   void Shutdown() {
     if (loopback_) {
       loopback_->Disconnect();
     }
     loopback_ = nullptr;
-    dtls_ = nullptr;
     flow_ = nullptr;
   }
 
   RefPtr<TransportFlow> flow_;
   TransportLayerLoopback *loopback_;
-  TransportLayerDtls *dtls_;
 };
 
 class TestAgent {
  public:
   TestAgent() :
       audio_config_(109, "opus", 48000, 960, 2, 64000, false),
       audio_conduit_(mozilla::AudioSessionConduit::Create()),
       audio_pipeline_(),
@@ -364,19 +342,19 @@ class TestAgentSend : public TestAgent {
     }
 
     RefPtr<MediaPipelineTransmit> audio_pipeline =
       new mozilla::MediaPipelineTransmit(
         test_pc,
         nullptr,
         test_utils->sts_target(),
         false,
-        audio_stream_track_.get(),
         audio_conduit_);
 
+    audio_pipeline->SetTrack(audio_stream_track_.get());
     audio_pipeline->Start();
 
     audio_pipeline_ = audio_pipeline;
 
     RefPtr<TransportFlow> rtp(audio_rtp_transport_.flow_);
     RefPtr<TransportFlow> rtcp(audio_rtcp_transport_.flow_);
 
     if (use_bundle_) {
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -25,17 +25,16 @@
 #include "MediaStreamGraphImpl.h"
 #include "MediaStreamListener.h"
 #include "MediaStreamTrack.h"
 #include "MediaStreamVideoSink.h"
 #include "RtpLogger.h"
 #include "VideoSegment.h"
 #include "VideoStreamTrack.h"
 #include "VideoUtils.h"
-#include "databuffer.h"
 #include "libyuv/convert.h"
 #include "mozilla/PeerIdentity.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/dom/RTCStatsReportBinding.h"
@@ -670,33 +669,29 @@ protected:
   UniquePtr<AudioPacketizer<int16_t, int16_t>> mPacketizer;
   // A buffer to hold a single packet of audio.
   UniquePtr<int16_t[]> mPacket;
   nsTArray<int16_t> mInterleavedAudio;
   AlignedShortBuffer mOutputAudio;
   UniquePtr<AudioConverter> mAudioConverter;
 };
 
-static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp";
-
 MediaPipeline::MediaPipeline(const std::string& aPc,
                              DirectionType aDirection,
                              nsCOMPtr<nsIEventTarget> aMainThread,
                              nsCOMPtr<nsIEventTarget> aStsThread,
                              RefPtr<MediaSessionConduit> aConduit)
   : mDirection(aDirection)
   , mLevel(0)
   , mConduit(aConduit)
   , mRtp(nullptr, RTP)
   , mRtcp(nullptr, RTCP)
   , mMainThread(aMainThread)
   , mStsThread(aStsThread)
-  , mTransport(new PipelineTransport(this)) // PipelineTransport() will access
-                                            // this->mStsThread; moved here
-                                            // for safety
+  , mTransport(new PipelineTransport(aStsThread))
   , mRtpPacketsSent(0)
   , mRtcpPacketsSent(0)
   , mRtpPacketsReceived(0)
   , mRtcpPacketsReceived(0)
   , mRtpBytesSent(0)
   , mRtpBytesReceived(0)
   , mPc(aPc)
   , mRtpParser(webrtc::RtpHeaderParser::Create())
@@ -861,19 +856,19 @@ MediaPipeline::GetContributingSourceStat
       RTCRTPContributingSourceStats stats;
       info.second.GetWebidlInstance(stats, aInboundRtpStreamId);
       aArr.AppendElement(stats, fallible);
     }
   }
 }
 
 void
-MediaPipeline::StateChange(TransportFlow* aFlow, TransportLayer::State aState)
+MediaPipeline::StateChange(TransportLayer* aLayer, TransportLayer::State aState)
 {
-  TransportInfo* info = GetTransportInfo_s(aFlow);
+  TransportInfo* info = GetTransportInfo_s(aLayer);
   MOZ_ASSERT(info);
 
   if (aState == TransportLayer::TS_OPEN) {
     CSFLogInfo(LOGTAG, "Flow is ready");
     TransportReady_s(*info);
   } else if (aState == TransportLayer::TS_CLOSED ||
              aState == TransportLayer::TS_ERROR) {
     TransportFailed_s(*info);
@@ -916,111 +911,24 @@ MediaPipeline::TransportReady_s(Transpor
   }
 
   CSFLogInfo(LOGTAG,
              "Transport ready for pipeline %p flow %s: %s",
              this,
              mDescription.c_str(),
              ToString(aInfo.mType));
 
-  // TODO(bcampen@mozilla.com): Should we disconnect from the flow on failure?
-  nsresult res;
-
-  // Now instantiate the SRTP objects
-  TransportLayerDtls* dtls = static_cast<TransportLayerDtls*>(
-    aInfo.mTransport->GetLayer(TransportLayerDtls::ID()));
-  MOZ_ASSERT(dtls); // DTLS is mandatory
-
-  uint16_t cipher_suite;
-  res = dtls->GetSrtpCipher(&cipher_suite);
-  if (NS_FAILED(res)) {
-    CSFLogError(LOGTAG, "Failed to negotiate DTLS-SRTP. This is an error");
-    aInfo.mState = StateType::MP_CLOSED;
-    UpdateRtcpMuxState(aInfo);
-    return res;
-  }
-
-  // SRTP Key Exporter as per RFC 5764 S 4.2
-  unsigned char srtp_block[SRTP_TOTAL_KEY_LENGTH * 2];
-  res = dtls->ExportKeyingMaterial(
-    kDTLSExporterLabel, false, "", srtp_block, sizeof(srtp_block));
-  if (NS_FAILED(res)) {
-    CSFLogError(LOGTAG, "Failed to compute DTLS-SRTP keys. This is an error");
-    aInfo.mState = StateType::MP_CLOSED;
-    UpdateRtcpMuxState(aInfo);
-    MOZ_CRASH(); // TODO: Remove once we have enough field experience to
-                 // know it doesn't happen. bug 798797. Note that the
-                 // code after this never executes.
-    return res;
-  }
-
-  // Slice and dice as per RFC 5764 S 4.2
-  unsigned char client_write_key[SRTP_TOTAL_KEY_LENGTH];
-  unsigned char server_write_key[SRTP_TOTAL_KEY_LENGTH];
-  int offset = 0;
-  memcpy(client_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
-  offset += SRTP_MASTER_KEY_LENGTH;
-  memcpy(server_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
-  offset += SRTP_MASTER_KEY_LENGTH;
-  memcpy(client_write_key + SRTP_MASTER_KEY_LENGTH,
-         srtp_block + offset,
-         SRTP_MASTER_SALT_LENGTH);
-  offset += SRTP_MASTER_SALT_LENGTH;
-  memcpy(server_write_key + SRTP_MASTER_KEY_LENGTH,
-         srtp_block + offset,
-         SRTP_MASTER_SALT_LENGTH);
-  offset += SRTP_MASTER_SALT_LENGTH;
-  MOZ_ASSERT(offset == sizeof(srtp_block));
-
-  unsigned char* write_key;
-  unsigned char* read_key;
-
-  if (dtls->role() == TransportLayerDtls::CLIENT) {
-    write_key = client_write_key;
-    read_key = server_write_key;
-  } else {
-    write_key = server_write_key;
-    read_key = client_write_key;
-  }
-
-  MOZ_ASSERT(!aInfo.mSendSrtp && !aInfo.mRecvSrtp);
-  aInfo.mSendSrtp =
-    SrtpFlow::Create(cipher_suite, false, write_key, SRTP_TOTAL_KEY_LENGTH);
-  aInfo.mRecvSrtp =
-    SrtpFlow::Create(cipher_suite, true, read_key, SRTP_TOTAL_KEY_LENGTH);
-  if (!aInfo.mSendSrtp || !aInfo.mRecvSrtp) {
-    CSFLogError(
-      LOGTAG, "Couldn't create SRTP flow for %s", ToString(aInfo.mType));
-    aInfo.mState = StateType::MP_CLOSED;
-    UpdateRtcpMuxState(aInfo);
-    return NS_ERROR_FAILURE;
-  }
-
   if (mDirection == DirectionType::RECEIVE) {
     CSFLogInfo(LOGTAG,
                "Listening for %s packets received on %p",
                ToString(aInfo.mType),
-               dtls->downward());
+               aInfo.mSrtp);
 
-    switch (aInfo.mType) {
-      case RTP:
-        dtls->downward()->SignalPacketReceived.connect(
-          this, &MediaPipeline::RtpPacketReceived);
-        break;
-      case RTCP:
-        dtls->downward()->SignalPacketReceived.connect(
-          this, &MediaPipeline::RtcpPacketReceived);
-        break;
-      case MUX:
-        dtls->downward()->SignalPacketReceived.connect(
-          this, &MediaPipeline::PacketReceived);
-        break;
-      default:
-        MOZ_CRASH();
-    }
+    aInfo.mSrtp->SignalPacketReceived.connect(
+        this, &MediaPipeline::PacketReceived);
   }
 
   aInfo.mState = StateType::MP_OPEN;
   UpdateRtcpMuxState(aInfo);
   return NS_OK;
 }
 
 nsresult
@@ -1045,38 +953,29 @@ MediaPipeline::TransportFailed_s(Transpo
 }
 
 void
 MediaPipeline::UpdateRtcpMuxState(TransportInfo& aInfo)
 {
   if (aInfo.mType == MUX) {
     if (aInfo.mTransport == mRtcp.mTransport) {
       mRtcp.mState = aInfo.mState;
-      if (!mRtcp.mSendSrtp) {
-        mRtcp.mSendSrtp = aInfo.mSendSrtp;
-        mRtcp.mRecvSrtp = aInfo.mRecvSrtp;
-      }
     }
   }
 }
 
 nsresult
-MediaPipeline::SendPacket(const TransportFlow* aFlow, const void* aData, int aLen)
+MediaPipeline::SendPacket(TransportLayer* aLayer, MediaPacket& packet)
 {
   ASSERT_ON_THREAD(mStsThread);
 
-  // Note that we bypass the DTLS layer here
-  TransportLayerDtls* dtls =
-    static_cast<TransportLayerDtls*>(aFlow->GetLayer(TransportLayerDtls::ID()));
-  MOZ_ASSERT(dtls);
+  int len = packet.len();
+  TransportResult res = aLayer->SendPacket(packet);
 
-  TransportResult res =
-    dtls->downward()->SendPacket(static_cast<const unsigned char*>(aData), aLen);
-
-  if (res != aLen) {
+  if (res != len) {
     // Ignore blocking indications
     if (res == TE_WOULDBLOCK)
       return NS_OK;
 
     CSFLogError(LOGTAG, "Failed write on stream %s", mDescription.c_str());
     return NS_BASE_STREAM_CLOSED;
   }
 
@@ -1143,19 +1042,17 @@ MediaPipeline::IncrementRtcpPacketsRecei
                mDescription.c_str(),
                this,
                static_cast<void*>(mRtp.mTransport),
                mRtcpPacketsReceived);
   }
 }
 
 void
-MediaPipeline::RtpPacketReceived(TransportLayer* aLayer,
-                                 const unsigned char* aData,
-                                 size_t aLen)
+MediaPipeline::RtpPacketReceived(TransportLayer* aLayer, MediaPacket& packet)
 {
   if (mDirection == DirectionType::TRANSMIT) {
     return;
   }
 
   if (!mTransport->Pipeline()) {
     CSFLogError(LOGTAG, "Discarding incoming packet; transport disconnected");
     return;
@@ -1166,35 +1063,27 @@ MediaPipeline::RtpPacketReceived(Transpo
     return;
   }
 
   if (mRtp.mState != StateType::MP_OPEN) {
     CSFLogError(LOGTAG, "Discarding incoming packet; pipeline not open");
     return;
   }
 
-  if (mRtp.mTransport->state() != TransportLayer::TS_OPEN) {
+  if (mRtp.mSrtp->state() != TransportLayer::TS_OPEN) {
     CSFLogError(LOGTAG, "Discarding incoming packet; transport not open");
     return;
   }
 
-  // This should never happen.
-  MOZ_ASSERT(mRtp.mRecvSrtp);
-
-  if (!aLen) {
-    return;
-  }
-
-  // Filter out everything but RTP/RTCP
-  if (aData[0] < 128 || aData[0] > 191) {
+  if (!packet.len()) {
     return;
   }
 
   webrtc::RTPHeader header;
-  if (!mRtpParser->Parse(aData, aLen, &header, true)) {
+  if (!mRtpParser->Parse(packet.data(), packet.len(), &header, true)) {
     return;
   }
 
   if (mFilter && !mFilter->Filter(header)) {
     return;
   }
 
   // Make sure to only get the time once, and only if we need it by
@@ -1230,172 +1119,101 @@ MediaPipeline::RtpPacketReceived(Transpo
         mCsrcStats.insert(std::make_pair(
           header.arrOfCSRCs[i], RtpCSRCStats(header.arrOfCSRCs[i], now)));
       } else {
         csrcInfo->second.SetTimestamp(now);
       }
     }
   }
 
-  mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Srtp, false, aData, aLen);
-
-  // Make a copy rather than cast away constness
-  auto innerData = MakeUnique<unsigned char[]>(aLen);
-  memcpy(innerData.get(), aData, aLen);
-  int outLen = 0;
-  nsresult res =
-    mRtp.mRecvSrtp->UnprotectRtp(innerData.get(), aLen, aLen, &outLen);
-  if (!NS_SUCCEEDED(res)) {
-    char tmp[16];
-
-    SprintfLiteral(tmp,
-                   "%.2x %.2x %.2x %.2x",
-                   innerData[0],
-                   innerData[1],
-                   innerData[2],
-                   innerData[3]);
-
-    CSFLogError(LOGTAG,
-                "Error unprotecting RTP in %s len= %zu [%s]",
-                mDescription.c_str(),
-                aLen,
-                tmp);
-    return;
-  }
   CSFLogDebug(LOGTAG, "%s received RTP packet.", mDescription.c_str());
-  IncrementRtpPacketsReceived(outLen);
+  IncrementRtpPacketsReceived(packet.len());
   OnRtpPacketReceived();
 
-  RtpLogger::LogPacket(
-    innerData.get(), outLen, true, true, header.headerLength, mDescription);
+  RtpLogger::LogPacket(packet, true, header.headerLength, mDescription);
+
+  // Might be nice to pass ownership of the buffer in this case, but it is a
+  // small optimization in a rare case.
+  mPacketDumper->Dump(
+    mLevel, dom::mozPacketDumpType::Srtp, false, packet.encrypted_data(), packet.encrypted_len());
 
   mPacketDumper->Dump(
-    mLevel, dom::mozPacketDumpType::Rtp, false, innerData.get(), outLen);
+    mLevel, dom::mozPacketDumpType::Rtp, false, packet.data(), packet.len());
 
   (void)mConduit->ReceivedRTPPacket(
-    innerData.get(), outLen, header.ssrc); // Ignore error codes
+    packet.data(), packet.len(), header.ssrc); // Ignore error codes
 }
 
 void
-MediaPipeline::RtcpPacketReceived(TransportLayer* aLayer,
-                                  const unsigned char* aData,
-                                  size_t aLen)
+MediaPipeline::RtcpPacketReceived(TransportLayer* aLayer, MediaPacket& packet)
 {
   if (!mTransport->Pipeline()) {
     CSFLogDebug(LOGTAG, "Discarding incoming packet; transport disconnected");
     return;
   }
 
   if (!mConduit) {
     CSFLogDebug(LOGTAG, "Discarding incoming packet; media disconnected");
     return;
   }
 
   if (mRtcp.mState != StateType::MP_OPEN) {
     CSFLogDebug(LOGTAG, "Discarding incoming packet; pipeline not open");
     return;
   }
 
-  if (mRtcp.mTransport->state() != TransportLayer::TS_OPEN) {
+  if (mRtcp.mSrtp->state() != TransportLayer::TS_OPEN) {
     CSFLogError(LOGTAG, "Discarding incoming packet; transport not open");
     return;
   }
 
-  if (!aLen) {
-    return;
-  }
-
-  // Filter out everything but RTP/RTCP
-  if (aData[0] < 128 || aData[0] > 191) {
+  if (!packet.len()) {
     return;
   }
 
   // We do not filter receiver reports, since the webrtc.org code for
   // senders already has logic to ignore RRs that do not apply.
   // TODO bug 1279153: remove SR check for reduced size RTCP
-  if (mFilter && !mFilter->FilterSenderReport(aData, aLen)) {
+  if (mFilter && !mFilter->FilterSenderReport(packet.data(), packet.len())) {
     CSFLogWarn(LOGTAG, "Dropping incoming RTCP packet; filtered out");
     return;
   }
 
-  mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Srtcp, false, aData, aLen);
-
-  // Make a copy rather than cast away constness
-  auto innerData = MakeUnique<unsigned char[]>(aLen);
-  memcpy(innerData.get(), aData, aLen);
-  int outLen;
-
-  nsresult res =
-    mRtcp.mRecvSrtp->UnprotectRtcp(innerData.get(), aLen, aLen, &outLen);
-
-  if (!NS_SUCCEEDED(res))
-    return;
-
   CSFLogDebug(LOGTAG, "%s received RTCP packet.", mDescription.c_str());
   IncrementRtcpPacketsReceived();
 
-  RtpLogger::LogPacket(innerData.get(), outLen, true, false, 0, mDescription);
-
-  mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Rtcp, false, aData, aLen);
-
-  MOZ_ASSERT(mRtcp.mRecvSrtp); // This should never happen
-
-  (void)mConduit->ReceivedRTCPPacket(innerData.get(),
-                                     outLen); // Ignore error codes
-}
-
-bool
-MediaPipeline::IsRtp(const unsigned char* aData, size_t aLen) const
-{
-  if (aLen < 2)
-    return false;
-
-  // Check if this is a RTCP packet. Logic based on the types listed in
-  // media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc
+  RtpLogger::LogPacket(packet, true, 0, mDescription);
 
-  // Anything outside this range is RTP.
-  if ((aData[1] < 192) || (aData[1] > 207))
-    return true;
-
-  if (aData[1] == 192) // FIR
-    return false;
-
-  if (aData[1] == 193) // NACK, but could also be RTP. This makes us sad
-    return true;      // but it's how webrtc.org behaves.
+  // Might be nice to pass ownership of the buffer in this case, but it is a
+  // small optimization in a rare case.
+  mPacketDumper->Dump(
+    mLevel, dom::mozPacketDumpType::Srtcp, false, packet.encrypted_data(), packet.encrypted_len());
 
-  if (aData[1] == 194)
-    return true;
-
-  if (aData[1] == 195) // IJ.
-    return false;
+  mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Rtcp, false, packet.data(), packet.len());
 
-  if ((aData[1] > 195) && (aData[1] < 200)) // the > 195 is redundant
-    return true;
-
-  if ((aData[1] >= 200) && (aData[1] <= 207)) // SR, RR, SDES, BYE,
-    return false;                           // APP, RTPFB, PSFB, XR
-
-  MOZ_ASSERT(false); // Not reached, belt and suspenders.
-  return true;
+  (void)mConduit->ReceivedRTCPPacket(packet.data(), packet.len()); // Ignore error codes
 }
 
 void
-MediaPipeline::PacketReceived(TransportLayer* aLayer,
-                              const unsigned char* aData,
-                              size_t aLen)
+MediaPipeline::PacketReceived(TransportLayer* aLayer, MediaPacket& packet)
 {
   if (!mTransport->Pipeline()) {
     CSFLogDebug(LOGTAG, "Discarding incoming packet; transport disconnected");
     return;
   }
 
-  if (IsRtp(aData, aLen)) {
-    RtpPacketReceived(aLayer, aData, aLen);
-  } else {
-    RtcpPacketReceived(aLayer, aData, aLen);
+  switch (packet.type()) {
+    case MediaPacket::RTP:
+      RtpPacketReceived(aLayer, packet);
+      break;
+    case MediaPacket::RTCP:
+      RtcpPacketReceived(aLayer, packet);
+      break;
+    default:
+      MOZ_CRASH("TransportLayerSrtp let something other than RTP/RTCP through");
   }
 }
 
 class MediaPipelineTransmit::PipelineListener : public MediaStreamVideoSink
 {
   friend class MediaPipelineTransmit;
 
 public:
@@ -1514,35 +1332,32 @@ protected:
   RefPtr<PipelineListener> mListener;
 };
 
 MediaPipelineTransmit::MediaPipelineTransmit(
   const std::string& aPc,
   nsCOMPtr<nsIEventTarget> aMainThread,
   nsCOMPtr<nsIEventTarget> aStsThread,
   bool aIsVideo,
-  dom::MediaStreamTrack* aDomTrack,
   RefPtr<MediaSessionConduit> aConduit)
   : MediaPipeline(aPc,
                   DirectionType::TRANSMIT,
                   aMainThread,
                   aStsThread,
                   aConduit)
   , mIsVideo(aIsVideo)
   , mListener(new PipelineListener(aConduit))
   , mFeeder(aIsVideo ? MakeAndAddRef<VideoFrameFeeder>(mListener)
                      : nullptr) // For video we send frames to an
                                 // async VideoFrameConverter that
                                 // calls back to a VideoFrameFeeder
                                 // that feeds I420 frames to
                                 // VideoConduit.
-  , mDomTrack(aDomTrack)
   , mTransmitting(false)
 {
-  SetDescription();
   if (!IsVideo()) {
     mAudioProcessing = MakeAndAddRef<AudioProxyThread>(
       static_cast<AudioSessionConduit*>(aConduit.get()));
     mListener->SetAudioProxy(mAudioProcessing);
   } else { // Video
     mConverter = MakeAndAddRef<VideoFrameConverter>();
     mConverter->AddListener(mFeeder);
     mListener->SetVideoFrameConverter(mConverter);
@@ -1714,17 +1529,17 @@ MediaPipelineTransmit::TransportReady_s(
   if (&aInfo == &mRtp) {
     mListener->SetActive(true);
   }
 
   return NS_OK;
 }
 
 nsresult
-MediaPipelineTransmit::ReplaceTrack(RefPtr<MediaStreamTrack>& aDomTrack)
+MediaPipelineTransmit::SetTrack(MediaStreamTrack* aDomTrack)
 {
   // MainThread, checked in calls we make
   if (aDomTrack) {
     nsString nsTrackId;
     aDomTrack->GetId(nsTrackId);
     std::string track_id(NS_ConvertUTF16toUTF8(nsTrackId).get());
     CSFLogDebug(
       LOGTAG,
@@ -1745,177 +1560,144 @@ MediaPipelineTransmit::ReplaceTrack(RefP
   }
   return NS_OK;
 }
 
 nsresult
 MediaPipeline::ConnectTransport_s(TransportInfo& aInfo)
 {
   MOZ_ASSERT(aInfo.mTransport);
+  MOZ_ASSERT(aInfo.mSrtp);
   ASSERT_ON_THREAD(mStsThread);
 
   // Look to see if the transport is ready
-  if (aInfo.mTransport->state() == TransportLayer::TS_OPEN) {
+  if (aInfo.mSrtp->state() == TransportLayer::TS_OPEN) {
     nsresult res = TransportReady_s(aInfo);
     if (NS_FAILED(res)) {
       CSFLogError(LOGTAG,
                   "Error calling TransportReady(); res=%u in %s",
                   static_cast<uint32_t>(res),
                   __FUNCTION__);
       return res;
     }
-  } else if (aInfo.mTransport->state() == TransportLayer::TS_ERROR) {
+  } else if (aInfo.mSrtp->state() == TransportLayer::TS_ERROR) {
     CSFLogError(
       LOGTAG, "%s transport is already in error state", ToString(aInfo.mType));
     TransportFailed_s(aInfo);
     return NS_ERROR_FAILURE;
   }
 
-  aInfo.mTransport->SignalStateChange.connect(this, &MediaPipeline::StateChange);
+  aInfo.mSrtp->SignalStateChange.connect(this, &MediaPipeline::StateChange);
 
   return NS_OK;
 }
 
 MediaPipeline::TransportInfo*
-MediaPipeline::GetTransportInfo_s(TransportFlow* aFlow)
+MediaPipeline::GetTransportInfo_s(TransportLayer* aLayer)
 {
   ASSERT_ON_THREAD(mStsThread);
-  if (aFlow == mRtp.mTransport) {
+  if (aLayer == mRtp.mSrtp) {
     return &mRtp;
   }
 
-  if (aFlow == mRtcp.mTransport) {
+  if (aLayer == mRtcp.mSrtp) {
     return &mRtcp;
   }
 
   return nullptr;
 }
 
 nsresult
 MediaPipeline::PipelineTransport::SendRtpPacket(const uint8_t* aData, size_t aLen)
 {
-
-  nsAutoPtr<DataBuffer> buf(
-    new DataBuffer(aData, aLen, aLen + SRTP_MAX_EXPANSION));
+  nsAutoPtr<MediaPacket> packet(new MediaPacket);
+  packet->Copy(aData, aLen, aLen + SRTP_MAX_EXPANSION);
+  packet->SetType(MediaPacket::RTP);
 
   RUN_ON_THREAD(
     mStsThread,
     WrapRunnable(RefPtr<MediaPipeline::PipelineTransport>(this),
                  &MediaPipeline::PipelineTransport::SendRtpRtcpPacket_s,
-                 buf,
-                 true),
+                 packet),
     NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
 
 nsresult
 MediaPipeline::PipelineTransport::SendRtpRtcpPacket_s(
-  nsAutoPtr<DataBuffer> aData,
-  bool aIsRtp)
+  nsAutoPtr<MediaPacket> aPacket)
 {
+  bool isRtp = aPacket->type() == MediaPacket::RTP;
 
   ASSERT_ON_THREAD(mStsThread);
   if (!mPipeline) {
     return NS_OK; // Detached
   }
-  TransportInfo& transport = aIsRtp ? mPipeline->mRtp : mPipeline->mRtcp;
+
+  TransportInfo& transport = isRtp ? mPipeline->mRtp : mPipeline->mRtcp;
 
-  if (!transport.mSendSrtp) {
-    CSFLogDebug(LOGTAG, "Couldn't write RTP/RTCP packet; SRTP not set up yet");
+  if (transport.mSrtp->state() != TransportLayer::TS_OPEN) {
+    // SRTP not ready yet.
     return NS_OK;
   }
 
   MOZ_ASSERT(transport.mTransport);
   NS_ENSURE_TRUE(transport.mTransport, NS_ERROR_NULL_POINTER);
 
-  // libsrtp enciphers in place, so we need a big enough buffer.
-  MOZ_ASSERT(aData->capacity() >= aData->len() + SRTP_MAX_EXPANSION);
+  MediaPacket packet(std::move(*aPacket));
+  packet.sdp_level() = Some(mPipeline->Level());
 
   if (RtpLogger::IsPacketLoggingOn()) {
     int headerLen = 12;
     webrtc::RTPHeader header;
     if (mPipeline->mRtpParser &&
-        mPipeline->mRtpParser->Parse(aData->data(), aData->len(), &header)) {
+        mPipeline->mRtpParser->Parse(packet.data(), packet.len(), &header)) {
       headerLen = header.headerLength;
     }
-    RtpLogger::LogPacket(aData->data(),
-                         aData->len(),
-                         false,
-                         aIsRtp,
-                         headerLen,
-                         mPipeline->mDescription);
+    RtpLogger::LogPacket(packet, false, headerLen, mPipeline->mDescription);
   }
 
-  int out_len;
-  nsresult res;
-  if (aIsRtp) {
+  if (isRtp) {
     mPipeline->mPacketDumper->Dump(mPipeline->Level(),
                                     dom::mozPacketDumpType::Rtp,
                                     true,
-                                    aData->data(),
-                                    aData->len());
-
-    res = transport.mSendSrtp->ProtectRtp(
-      aData->data(), aData->len(), aData->capacity(), &out_len);
+                                    packet.data(),
+                                    packet.len());
+    mPipeline->IncrementRtpPacketsSent(packet.len());
   } else {
     mPipeline->mPacketDumper->Dump(mPipeline->Level(),
                                     dom::mozPacketDumpType::Rtcp,
                                     true,
-                                    aData->data(),
-                                    aData->len());
-
-    res = transport.mSendSrtp->ProtectRtcp(
-      aData->data(), aData->len(), aData->capacity(), &out_len);
+                                    packet.data(),
+                                    packet.len());
+    mPipeline->IncrementRtcpPacketsSent();
   }
-  if (!NS_SUCCEEDED(res)) {
-    return res;
-  }
-
-  // paranoia; don't have uninitialized bytes included in data->len()
-  aData->SetLength(out_len);
 
   CSFLogDebug(LOGTAG,
               "%s sending %s packet",
               mPipeline->mDescription.c_str(),
-              (aIsRtp ? "RTP" : "RTCP"));
-  if (aIsRtp) {
-    mPipeline->mPacketDumper->Dump(mPipeline->Level(),
-                                    dom::mozPacketDumpType::Srtp,
-                                    true,
-                                    aData->data(),
-                                    out_len);
+              (isRtp ? "RTP" : "RTCP"));
 
-    mPipeline->IncrementRtpPacketsSent(out_len);
-  } else {
-    mPipeline->mPacketDumper->Dump(mPipeline->Level(),
-                                    dom::mozPacketDumpType::Srtcp,
-                                    true,
-                                    aData->data(),
-                                    out_len);
-
-    mPipeline->IncrementRtcpPacketsSent();
-  }
-  return mPipeline->SendPacket(transport.mTransport, aData->data(), out_len);
+  return mPipeline->SendPacket(transport.mSrtp, packet);
 }
 
 nsresult
 MediaPipeline::PipelineTransport::SendRtcpPacket(const uint8_t* aData,
                                                  size_t aLen)
 {
-
-  nsAutoPtr<DataBuffer> buf(
-    new DataBuffer(aData, aLen, aLen + SRTP_MAX_EXPANSION));
+  nsAutoPtr<MediaPacket> packet(new MediaPacket);
+  packet->Copy(aData, aLen, aLen + SRTP_MAX_EXPANSION);
+  packet->SetType(MediaPacket::RTCP);
 
   RUN_ON_THREAD(
     mStsThread,
     WrapRunnable(RefPtr<MediaPipeline::PipelineTransport>(this),
                  &MediaPipeline::PipelineTransport::SendRtpRtcpPacket_s,
-                 buf,
-                 false),
+                 packet),
     NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
 
 // Called if we're attached with AddDirectListener()
 void
 MediaPipelineTransmit::PipelineListener::NotifyRealtimeTrackData(
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -11,17 +11,17 @@
 #include <map>
 
 #include "sigslot.h"
 
 #include "signaling/src/media-conduit/MediaConduitInterface.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/Atomics.h"
 #include "SrtpFlow.h"
-#include "databuffer.h"
+#include "mediapacket.h"
 #include "mtransport/runnable_utils.h"
 #include "mtransport/transportflow.h"
 #include "AudioPacketizer.h"
 #include "StreamTracks.h"
 #include "signaling/src/peerconnection/PacketDumper.h"
 
 #include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h"
 
@@ -180,96 +180,88 @@ public:
 
   typedef enum { RTP, RTCP, MUX, MAX_RTP_TYPE } RtpType;
 
   // Separate class to allow ref counting
   class PipelineTransport : public TransportInterface
   {
   public:
     // Implement the TransportInterface functions
-    explicit PipelineTransport(MediaPipeline* aPipeline)
-      : mPipeline(aPipeline)
-      , mStsThread(aPipeline->mStsThread)
+    explicit PipelineTransport(nsIEventTarget* aStsThread)
+      : mPipeline(nullptr)
+      , mStsThread(aStsThread)
     {
     }
 
     void Attach(MediaPipeline* pipeline) { mPipeline = pipeline; }
     void Detach() { mPipeline = nullptr; }
     MediaPipeline* Pipeline() const { return mPipeline; }
 
     virtual nsresult SendRtpPacket(const uint8_t* aData, size_t aLen) override;
     virtual nsresult SendRtcpPacket(const uint8_t* aData, size_t aLen) override;
 
   private:
-    nsresult SendRtpRtcpPacket_s(nsAutoPtr<DataBuffer> aData, bool aIsRtp);
+    nsresult SendRtpRtcpPacket_s(nsAutoPtr<MediaPacket> aData);
 
     // Creates a cycle, which we break with Detach
     RefPtr<MediaPipeline> mPipeline;
     const nsCOMPtr<nsIEventTarget> mStsThread;
   };
 
 protected:
   virtual ~MediaPipeline();
   nsresult AttachTransport_s();
   friend class PipelineTransport;
 
   struct TransportInfo
   {
     TransportInfo(RefPtr<TransportFlow> aFlow, RtpType aType)
       : mTransport(aFlow)
+      , mSrtp(mTransport ? mTransport->GetLayer("srtp") : nullptr)
       , mState(StateType::MP_CONNECTING)
       , mType(aType)
     {
     }
 
     void Detach()
     {
       mTransport = nullptr;
-      mSendSrtp = nullptr;
-      mRecvSrtp = nullptr;
+      mSrtp = nullptr;
     }
 
     RefPtr<TransportFlow> mTransport;
+    TransportLayer* mSrtp;
     StateType mState;
-    RefPtr<SrtpFlow> mSendSrtp;
-    RefPtr<SrtpFlow> mRecvSrtp;
     RtpType mType;
   };
 
   // The transport is down
   virtual nsresult TransportFailed_s(TransportInfo& aInfo);
   // The transport is ready
   virtual nsresult TransportReady_s(TransportInfo& aInfo);
   void UpdateRtcpMuxState(TransportInfo& aInfo);
 
   nsresult ConnectTransport_s(TransportInfo& aInfo);
 
-  TransportInfo* GetTransportInfo_s(TransportFlow* aFlow);
+  TransportInfo* GetTransportInfo_s(TransportLayer* aLayer);
 
   void IncrementRtpPacketsSent(int aBytes);
   void IncrementRtcpPacketsSent();
   void IncrementRtpPacketsReceived(int aBytes);
   virtual void OnRtpPacketReceived() {};
   void IncrementRtcpPacketsReceived();
 
-  virtual nsresult SendPacket(const TransportFlow* aFlow,
-                              const void* aData,
-                              int aLen);
+  virtual nsresult SendPacket(TransportLayer* aLayer,
+                              MediaPacket& packet);
 
   // Process slots on transports
-  void StateChange(TransportFlow* aFlow, TransportLayer::State);
-  void RtpPacketReceived(TransportLayer* aLayer,
-                         const unsigned char* aData,
-                         size_t aLen);
-  void RtcpPacketReceived(TransportLayer* aLayer,
-                          const unsigned char* aData,
-                          size_t aLen);
-  void PacketReceived(TransportLayer* aLayer,
-                      const unsigned char* aData,
-                      size_t aLen);
+  void StateChange(TransportLayer* aLayer, TransportLayer::State);
+  void RtpPacketReceived(TransportLayer* aLayer, MediaPacket& packet);
+  void RtcpPacketReceived(TransportLayer* aLayer, MediaPacket& packet);
+  void PacketReceived(TransportLayer* aLayer, MediaPacket& packet);
 
   void SetDescription_s(const std::string& description);
 
   const DirectionType mDirection;
   size_t mLevel;
   RefPtr<MediaSessionConduit> mConduit; // Our conduit. Written on the main
                                         // thread. Read on STS thread.
 
@@ -321,17 +313,16 @@ private:
 class MediaPipelineTransmit : public MediaPipeline
 {
 public:
   // Set aRtcpTransport to nullptr to use rtcp-mux
   MediaPipelineTransmit(const std::string& aPc,
                         nsCOMPtr<nsIEventTarget> aMainThread,
                         nsCOMPtr<nsIEventTarget> aStsThread,
                         bool aIsVideo,
-                        dom::MediaStreamTrack* aDomTrack,
                         RefPtr<MediaSessionConduit> aConduit);
 
   void Start() override;
   void Stop() override;
 
   // written and used from MainThread
   bool IsVideo() const override;
 
@@ -347,17 +338,17 @@ public:
 
   // Override MediaPipeline::TransportReady.
   nsresult TransportReady_s(TransportInfo& aInfo) override;
 
   // Replace a track with a different one
   // In non-compliance with the likely final spec, allow the new
   // track to be part of a different stream (since we don't support
   // multiple tracks of a type in a stream yet).  bug 1056650
-  virtual nsresult ReplaceTrack(RefPtr<dom::MediaStreamTrack>& aDomTrack);
+  virtual nsresult SetTrack(dom::MediaStreamTrack* aDomTrack);
 
   // Separate classes to allow ref counting
   class PipelineListener;
   class VideoFrameFeeder;
 
 protected:
   ~MediaPipelineTransmit();
 
--- a/media/webrtc/signaling/src/mediapipeline/RtpLogger.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/RtpLogger.cpp
@@ -28,18 +28,19 @@ static const char* rlLogTag = "RtpLogger
 #define LOGTAG rlLogTag
 
 namespace mozilla {
 
 bool RtpLogger::IsPacketLoggingOn() {
   return CSFLogTestLevel(CSF_LOG_DEBUG);
 }
 
-void RtpLogger::LogPacket(const unsigned char *data, int len, bool input,
-                          bool isRtp, int headerLength, std::string desc) {
+void RtpLogger::LogPacket(const MediaPacket& packet, bool input,
+                          size_t headerLength, std::string desc) {
+  bool isRtp = (packet.type() == MediaPacket::RTP);
   if (CSFLogTestLevel(CSF_LOG_DEBUG)) {
     std::stringstream ss;
     /* This creates text2pcap compatible format, e.g.:
      *   O 10:36:26.864934  000000 80 c8 00 06 6d ... RTCP_PACKET
      */
     ss << (input ? "I " : "O ");
     std::time_t t = std::time(nullptr);
     std::tm tm = *std::localtime(&t);
@@ -54,27 +55,27 @@ void RtpLogger::LogPacket(const unsigned
     ss << "." << (tb.millitm) << " ";
 #else
     struct timeval tv;
     gettimeofday(&tv, NULL);
     ss << "." << (tv.tv_usec) << " ";
 #endif
     ss << " 000000";
     ss << std::hex << std::setfill('0');
-    int offset_ = headerLength;
-    if (isRtp && (offset_ + 5 < len)) {
+    size_t offset_ = headerLength;
+    if (isRtp && (offset_ + 5 < packet.len())) {
       // Allow the first 5 bytes of the payload in clear
       offset_ += 5;
     }
-    for (int i=0; i < len; ++i) {
+    for (size_t i=0; i < packet.len(); ++i) {
       if (isRtp && i > offset_) {
         ss << " 00";
       }
       else {
-        ss << " " << std::setw(2) << (int)data[i];
+        ss << " " << std::setw(2) << (int)packet.data()[i];
       }
     }
     CSFLogDebug(LOGTAG, "%s%s%s", ss.str().c_str(),
                 (isRtp ? " RTP_PACKET " : " RTCP_PACKET "), desc.c_str());
   }
 }
 
 }  // end of namespace
--- a/media/webrtc/signaling/src/mediapipeline/RtpLogger.h
+++ b/media/webrtc/signaling/src/mediapipeline/RtpLogger.h
@@ -3,26 +3,27 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Original author: nohlmeier@mozilla.com
 
 #ifndef rtplogger_h__
 #define rtplogger_h__
 
 #include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h"
+#include "mtransport/mediapacket.h"
 
 namespace mozilla {
 
 /* This class logs RTP and RTCP packets in hex in a format compatible to
  * text2pcap.
  * Example to convert the MOZ log file into a PCAP file:
  *   egrep '(RTP_PACKET|RTCP_PACKET)' moz.log | text2pcap -D -n -l 1 -i 17 -u 1234,1235 -t '%H:%M:%S.' - rtp.pcap
  */
 class RtpLogger {
 public:
   static bool IsPacketLoggingOn();
-  static void LogPacket(const unsigned char *data, int len, bool input,
-                        bool isRtp, int headerLength, std::string desc);
+  static void LogPacket(const MediaPacket& packet, bool input,
+                        size_t headerLength, std::string desc);
 };
 
 }  // End of namespace
 #endif
 
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/mediapipeline/TransportLayerPacketDumper.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include "TransportLayerPacketDumper.h"
+
+#include "logging.h"
+#include "nsError.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+TransportLayerPacketDumper::TransportLayerPacketDumper(
+    nsAutoPtr<PacketDumper>&& aPacketDumper, dom::mozPacketDumpType aType) :
+  mPacketDumper(std::move(aPacketDumper)),
+  mType(aType)
+{}
+
+void
+TransportLayerPacketDumper::WasInserted()
+{
+  CheckThread();
+  if (!downward_) {
+    MOZ_MTLOG(ML_ERROR, "Packet dumper with nothing below. This is useless");
+    TL_SET_STATE(TS_ERROR);
+  }
+
+  downward_->SignalStateChange.connect(this,
+      &TransportLayerPacketDumper::StateChange);
+  downward_->SignalPacketReceived.connect(this,
+      &TransportLayerPacketDumper::PacketReceived);
+}
+
+TransportResult
+TransportLayerPacketDumper::SendPacket(MediaPacket& packet)
+{
+  if (packet.sdp_level().isSome()) {
+    dom::mozPacketDumpType dumpType = mType;
+    if (mType == dom::mozPacketDumpType::Srtp &&
+        packet.type() == MediaPacket::RTCP) {
+      dumpType = dom::mozPacketDumpType::Srtcp;
+    }
+
+    mPacketDumper->Dump(*packet.sdp_level(),
+                        dumpType,
+                        true,
+                        packet.data(),
+                        packet.len());
+  }
+  return downward_->SendPacket(packet);
+}
+
+void
+TransportLayerPacketDumper::StateChange(TransportLayer* aLayer, State aState)
+{
+  TL_SET_STATE(aState);
+}
+
+void
+TransportLayerPacketDumper::PacketReceived(TransportLayer* aLayer,
+                                           MediaPacket& packet)
+{
+  // There's no way to know the level yet, so we can't use the packet dumper
+  // yet. We rely on the SRTP layer saving the encrypted packet in
+  // MediaPacket::encrypted_, to allow MediaPipeline to dump it later.
+  SignalPacketReceived(this, packet);
+}
+
+} // namespace mozilla
+
+
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/mediapipeline/TransportLayerPacketDumper.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef transportlayerpacketdumper_h__
+#define transportlayerpacketdumper_h__
+
+#include "transportlayer.h"
+#include "signaling/src/peerconnection/PacketDumper.h"
+#include "mozilla/dom/RTCPeerConnectionBinding.h"
+
+namespace mozilla {
+
+class TransportLayerPacketDumper final : public TransportLayer {
+  public:
+    explicit TransportLayerPacketDumper(nsAutoPtr<PacketDumper>&& aPacketDumper,
+                                        dom::mozPacketDumpType aType);
+    virtual ~TransportLayerPacketDumper() {};
+
+    // Transport layer overrides.
+    void WasInserted() override;
+    TransportResult SendPacket(MediaPacket& packet) override;
+
+    // Signals
+    void StateChange(TransportLayer *aLayer, State state);
+    void PacketReceived(TransportLayer* aLayer, MediaPacket& packet);
+
+    TRANSPORT_LAYER_ID("packet-dumper")
+
+  private:
+    DISALLOW_COPY_ASSIGN(TransportLayerPacketDumper);
+    nsAutoPtr<PacketDumper> mPacketDumper;
+    dom::mozPacketDumpType mType;
+};
+
+
+}  // close namespace
+#endif
--- a/media/webrtc/signaling/src/mediapipeline/moz.build
+++ b/media/webrtc/signaling/src/mediapipeline/moz.build
@@ -18,12 +18,12 @@ LOCAL_INCLUDES += [
     '/netwerk/srtp/src/crypto/include',
     '/netwerk/srtp/src/include',
 ]
 
 UNIFIED_SOURCES += [
     'MediaPipeline.cpp',
     'MediaPipelineFilter.cpp',
     'RtpLogger.cpp',
-    'SrtpFlow.cpp',
+    'TransportLayerPacketDumper.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -14,18 +14,21 @@
 #include "nricemediastream.h"
 #include "MediaPipelineFilter.h"
 #include "MediaPipeline.h"
 #include "PeerConnectionImpl.h"
 #include "PeerConnectionMedia.h"
 #include "runnable_utils.h"
 #include "transportlayerice.h"
 #include "transportlayerdtls.h"
+#include "transportlayersrtp.h"
 #include "signaling/src/jsep/JsepSession.h"
 #include "signaling/src/jsep/JsepTransport.h"
+#include "signaling/src/mediapipeline/TransportLayerPacketDumper.h"
+#include "signaling/src/peerconnection/PacketDumper.h"
 
 #include "nsContentUtils.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIURI.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsICancelable.h"
 #include "nsILoadInfo.h"
@@ -522,31 +525,40 @@ PeerConnectionMedia::UpdateTransportFlow
   return UpdateTransportFlow(transportLevel, true, *aTransceiver.mTransport);
 }
 
 // Accessing the PCMedia should be safe here because we shouldn't
 // have enqueued this function unless it was still active and
 // the ICE data is destroyed on the STS.
 static void
 FinalizeTransportFlow_s(RefPtr<PeerConnectionMedia> aPCMedia,
+                        nsAutoPtr<PacketDumper> aPacketDumper,
                         RefPtr<TransportFlow> aFlow, size_t aLevel,
                         bool aIsRtcp,
-                        nsAutoPtr<PtrVector<TransportLayer> > aLayerList)
+                        TransportLayerIce* aIceLayer,
+                        TransportLayerDtls* aDtlsLayer,
+                        TransportLayerSrtp* aSrtpLayer)
 {
-  TransportLayerIce* ice =
-      static_cast<TransportLayerIce*>(aLayerList->values.front());
-  ice->SetParameters(aPCMedia->ice_media_stream(aLevel),
-                     aIsRtcp ? 2 : 1);
-  nsAutoPtr<std::queue<TransportLayer*> > layerQueue(
-      new std::queue<TransportLayer*>);
-  for (auto& value : aLayerList->values) {
-    layerQueue->push(value);
-  }
-  aLayerList->values.clear();
-  (void)aFlow->PushLayers(layerQueue); // TODO(bug 854518): Process errors.
+  TransportLayerPacketDumper* srtpDumper(new TransportLayerPacketDumper(
+        std::move(aPacketDumper), dom::mozPacketDumpType::Srtp));
+
+  aIceLayer->SetParameters(aPCMedia->ice_media_stream(aLevel),
+                           aIsRtcp ? 2 : 1);
+  // TODO(bug 854518): Process errors.
+  (void)aIceLayer->Init();
+  (void)aDtlsLayer->Init();
+  (void)srtpDumper->Init();
+  (void)aSrtpLayer->Init();
+  aDtlsLayer->Chain(aIceLayer);
+  srtpDumper->Chain(aIceLayer);
+  aSrtpLayer->Chain(srtpDumper);
+  aFlow->PushLayer(aIceLayer);
+  aFlow->PushLayer(aDtlsLayer);
+  aFlow->PushLayer(srtpDumper);
+  aFlow->PushLayer(aSrtpLayer);
 }
 
 static void
 AddNewIceStreamForRestart_s(RefPtr<PeerConnectionMedia> aPCMedia,
                             RefPtr<TransportFlow> aFlow,
                             size_t aLevel,
                             bool aIsRtcp)
 {
@@ -596,16 +608,17 @@ PeerConnectionMedia::UpdateTransportFlow
 
   std::ostringstream osId;
   osId << mParentHandle << ":" << aLevel << "," << (aIsRtcp ? "rtcp" : "rtp");
   flow = new TransportFlow(osId.str());
 
   // The media streams are made on STS so we need to defer setup.
   auto ice = MakeUnique<TransportLayerIce>();
   auto dtls = MakeUnique<TransportLayerDtls>();
+  auto srtp = MakeUnique<TransportLayerSrtp>(*dtls);
   dtls->SetRole(aTransport.mDtls->GetRole() ==
                         JsepDtlsTransport::kJsepDtlsClient
                     ? TransportLayerDtls::CLIENT
                     : TransportLayerDtls::SERVER);
 
   RefPtr<DtlsIdentity> pcid = mParent->Identity();
   if (!pcid) {
     CSFLogError(LOGTAG, "Failed to get DTLS identity.");
@@ -647,24 +660,23 @@ PeerConnectionMedia::UpdateTransportFlow
     alpn.insert(alpnDefault);
   }
   rv = dtls->SetAlpn(alpn, alpnDefault);
   if (NS_FAILED(rv)) {
     CSFLogError(LOGTAG, "Couldn't set ALPN");
     return rv;
   }
 
-  nsAutoPtr<PtrVector<TransportLayer> > layers(new PtrVector<TransportLayer>);
-  layers->values.push_back(ice.release());
-  layers->values.push_back(dtls.release());
+  nsAutoPtr<PacketDumper> packetDumper(new PacketDumper(mParent));
 
   RefPtr<PeerConnectionMedia> pcMedia(this);
   rv = GetSTSThread()->Dispatch(
-      WrapRunnableNM(FinalizeTransportFlow_s, pcMedia, flow, aLevel, aIsRtcp,
-                     layers),
+      WrapRunnableNM(FinalizeTransportFlow_s, pcMedia, packetDumper, flow,
+                     aLevel, aIsRtcp,
+                     ice.release(), dtls.release(), srtp.release()),
       NS_DISPATCH_NORMAL);
   if (NS_FAILED(rv)) {
     CSFLogError(LOGTAG, "Failed to dispatch FinalizeTransportFlow_s");
     return rv;
   }
 
   AddTransportFlow(aLevel, aIsRtcp, flow);
 
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
@@ -64,18 +64,19 @@ TransceiverImpl::TransceiverImpl(
 
   mConduit->SetPCHandle(mPCHandle);
 
   mTransmitPipeline = new MediaPipelineTransmit(
       mPCHandle,
       mMainThread.get(),
       mStsThread.get(),
       IsVideo(),
-      mSendTrack,
       mConduit);
+
+  mTransmitPipeline->SetTrack(mSendTrack);
 }
 
 TransceiverImpl::~TransceiverImpl() = default;
 
 NS_IMPL_ISUPPORTS0(TransceiverImpl)
 
 void
 TransceiverImpl::InitAudio()
@@ -153,17 +154,17 @@ TransceiverImpl::UpdateSendTrack(dom::Me
 {
   if (mJsepTransceiver->IsStopped()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   MOZ_MTLOG(ML_DEBUG, mPCHandle << "[" << mMid << "]: " << __FUNCTION__ <<
                       "(" << aSendTrack << ")");
   mSendTrack = aSendTrack;
-  return mTransmitPipeline->ReplaceTrack(mSendTrack);
+  return mTransmitPipeline->SetTrack(mSendTrack);
 }
 
 nsresult
 TransceiverImpl::UpdateTransport(PeerConnectionMedia& aTransportManager)
 {
   if (!mJsepTransceiver->HasLevel()) {
     return NS_OK;
   }
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -308,16 +308,17 @@ DataChannelConnection::DataChannelConnec
   : NeckoTargetHolder(aTarget)
   , mLock("netwerk::sctp::DataChannelConnection")
 {
   mCurrentStream = 0;
   mState = CLOSED;
   mSocket = nullptr;
   mMasterSocket = nullptr;
   mListener = listener;
+  mDtls = nullptr;
   mLocalPort = 0;
   mRemotePort = 0;
   mPendingType = PENDING_NONE;
   LOG(("Constructor DataChannelConnection=%p, listener=%p", this, mListener.get()));
   mInternalIOThread = nullptr;
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   mShutdown = false;
 #endif
@@ -325,17 +326,17 @@ DataChannelConnection::DataChannelConnec
 
 DataChannelConnection::~DataChannelConnection()
 {
   LOG(("Deleting DataChannelConnection %p", (void *) this));
   // This may die on the MainThread, or on the STS thread
   ASSERT_WEBRTC(mState == CLOSED);
   MOZ_ASSERT(!mMasterSocket);
   MOZ_ASSERT(mPending.GetSize() == 0);
-  MOZ_ASSERT(!mTransportFlow);
+  MOZ_ASSERT(!mDtls);
 
   // Already disconnected from sigslot/mTransportFlow
   // TransportFlows must be released from the STS thread
   if (!IsSTSThread()) {
     ASSERT_WEBRTC(NS_IsMainThread());
 
     if (mInternalIOThread) {
       // Avoid spinning the event thread from here (which if we're mainthread
@@ -410,16 +411,17 @@ void DataChannelConnection::DestroyOnSTS
   mSTS->Dispatch(WrapRunnable(RefPtr<DataChannelConnection>(this),
                               &DataChannelConnection::DestroyOnSTSFinal),
                  NS_DISPATCH_NORMAL);
 }
 
 void DataChannelConnection::DestroyOnSTSFinal()
 {
   mTransportFlow = nullptr;
+  mDtls = nullptr;
   sDataChannelShutdown->CreateConnectionShutdown(this);
 }
 
 bool
 DataChannelConnection::Init(unsigned short aPort, uint16_t aNumStreams, bool aMaxMessageSizeSet,
                             uint64_t aMaxMessageSize)
 {
   struct sctp_initmsg initmsg;
@@ -665,20 +667,18 @@ DataChannelConnection::GetMaxMessageSize
 }
 
 #ifdef MOZ_PEERCONNECTION
 void
 DataChannelConnection::SetEvenOdd()
 {
   ASSERT_WEBRTC(IsSTSThread());
 
-  TransportLayerDtls *dtls = static_cast<TransportLayerDtls *>(
-      mTransportFlow->GetLayer(TransportLayerDtls::ID()));
-  MOZ_ASSERT(dtls);  // DTLS is mandatory
-  mAllocateEven = (dtls->role() == TransportLayerDtls::CLIENT);
+  MOZ_ASSERT(mDtls);  // DTLS is mandatory
+  mAllocateEven = (mDtls->role() == TransportLayerDtls::CLIENT);
 }
 
 bool
 DataChannelConnection::ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport)
 {
   LOG(("Connect DTLS local %u, remote %u", localport, remoteport));
 
   MOZ_ASSERT(mMasterSocket, "SCTP wasn't initialized before ConnectViaTransportFlow!");
@@ -696,26 +696,27 @@ DataChannelConnection::ConnectViaTranspo
                 NS_DISPATCH_NORMAL);
   return true;
 }
 
 void
 DataChannelConnection::SetSignals()
 {
   ASSERT_WEBRTC(IsSTSThread());
-  ASSERT_WEBRTC(mTransportFlow);
-  LOG(("Setting transport signals, state: %d", mTransportFlow->state()));
-  mTransportFlow->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput);
+  mDtls = static_cast<TransportLayerDtls*>(mTransportFlow->GetLayer("dtls"));
+  ASSERT_WEBRTC(mDtls);
+  LOG(("Setting transport signals, state: %d", mDtls->state()));
+  mDtls->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput);
   // SignalStateChange() doesn't call you with the initial state
-  mTransportFlow->SignalStateChange.connect(this, &DataChannelConnection::CompleteConnect);
-  CompleteConnect(mTransportFlow, mTransportFlow->state());
+  mDtls->SignalStateChange.connect(this, &DataChannelConnection::CompleteConnect);
+  CompleteConnect(mDtls, mDtls->state());
 }
 
 void
-DataChannelConnection::CompleteConnect(TransportFlow *flow, TransportLayer::State state)
+DataChannelConnection::CompleteConnect(TransportLayer *layer, TransportLayer::State state)
 {
   LOG(("Data transport state: %d", state));
   MutexAutoLock lock(mLock);
   ASSERT_WEBRTC(IsSTSThread());
   // We should abort connection on TS_ERROR.
   // Note however that the association will also fail (perhaps with a delay) and
   // notify us in that way
   if (state != TransportLayer::TS_OPEN || !mMasterSocket)
@@ -811,87 +812,79 @@ DataChannelConnection::ProcessQueuedOpen
       // OpenFinish returns a reference itself, so we need to take it can Release it
       channel = OpenFinish(channel.forget()); // may reset the flag and re-push
     } else {
       NS_ASSERTION(false, "How did a DataChannel get queued without the FINISH_OPEN flag?");
     }
   }
 
 }
+
 void
-DataChannelConnection::SctpDtlsInput(TransportFlow *flow,
-                                     const unsigned char *data, size_t len)
+DataChannelConnection::SctpDtlsInput(TransportLayer *layer, MediaPacket& packet)
 {
   if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)) {
     char *buf;
 
-    if ((buf = usrsctp_dumppacket((void *)data, len, SCTP_DUMP_INBOUND)) != nullptr) {
+    if ((buf = usrsctp_dumppacket((void *)packet.data(),
+                                  packet.len(),
+                                  SCTP_DUMP_INBOUND)) != nullptr) {
       SCTP_LOG(("%s", buf));
       usrsctp_freedumpbuffer(buf);
     }
   }
   // Pass the data to SCTP
   MutexAutoLock lock(mLock);
-  usrsctp_conninput(static_cast<void *>(this), data, len, 0);
+  usrsctp_conninput(static_cast<void *>(this), packet.data(), packet.len(), 0);
 }
 
 int
-DataChannelConnection::SendPacket(unsigned char data[], size_t len, bool release)
+DataChannelConnection::SendPacket(nsAutoPtr<MediaPacket> packet)
 {
   //LOG(("%p: SCTP/DTLS sent %ld bytes", this, len));
-  int res = 0;
-  if (mTransportFlow) {
-    res = mTransportFlow->SendPacket(data, len) < 0 ? 1 : 0;
+  if (mDtls) {
+    return mDtls->SendPacket(*packet) < 0 ? 1 : 0;
   }
-  if (release)
-    delete [] data;
-  return res;
+  return 0;
 }
 
 /* static */
 int
 DataChannelConnection::SctpDtlsOutput(void *addr, void *buffer, size_t length,
                                       uint8_t tos, uint8_t set_df)
 {
   DataChannelConnection *peer = static_cast<DataChannelConnection *>(addr);
-  int res;
   MOZ_DIAGNOSTIC_ASSERT(!peer->mShutdown);
 
   if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)) {
     char *buf;
 
     if ((buf = usrsctp_dumppacket(buffer, length, SCTP_DUMP_OUTBOUND)) != nullptr) {
       SCTP_LOG(("%s", buf));
       usrsctp_freedumpbuffer(buf);
     }
   }
+
   // We're async proxying even if on the STSThread because this is called
   // with internal SCTP locks held in some cases (such as in usrsctp_connect()).
   // SCTP has an option for Apple, on IP connections only, to release at least
   // one of the locks before calling a packet output routine; with changes to
   // the underlying SCTP stack this might remove the need to use an async proxy.
-  if ((false /*peer->IsSTSThread()*/)) {
-    res = peer->SendPacket(static_cast<unsigned char *>(buffer), length, false);
-  } else {
-    auto *data = new unsigned char[length];
-    memcpy(data, buffer, length);
-    // Commented out since we have to Dispatch SendPacket to avoid deadlock"
-    // res = -1;
-
-    // XXX It might be worthwhile to add an assertion against the thread
-    // somehow getting into the DataChannel/SCTP code again, as
-    // DISPATCH_SYNC is not fully blocking.  This may be tricky, as it
-    // needs to be a per-thread check, not a global.
-    peer->mSTS->Dispatch(WrapRunnable(
-                           RefPtr<DataChannelConnection>(peer),
-                           &DataChannelConnection::SendPacket, data, length, true),
-                                   NS_DISPATCH_NORMAL);
-    res = 0; // cheat!  Packets can always be dropped later anyways
-  }
-  return res;
+  nsAutoPtr<MediaPacket> packet(new MediaPacket);
+  packet->Copy(static_cast<const uint8_t*>(buffer), length);
+
+  // XXX It might be worthwhile to add an assertion against the thread
+  // somehow getting into the DataChannel/SCTP code again, as
+  // DISPATCH_SYNC is not fully blocking.  This may be tricky, as it
+  // needs to be a per-thread check, not a global.
+  peer->mSTS->Dispatch(WrapRunnable(
+                         RefPtr<DataChannelConnection>(peer),
+                         &DataChannelConnection::SendPacket, packet),
+                                 NS_DISPATCH_NORMAL);
+  return 0; // cheat!  Packets can always be dropped later anyways
 }
 #endif
 
 #ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT
 // listen for incoming associations
 // Blocks! - Don't call this from main thread!
 
 #error This code will not work as-is since SetEvenOdd() runs on Mainthread
--- a/netwerk/sctp/datachannel/DataChannel.h
+++ b/netwerk/sctp/datachannel/DataChannel.h
@@ -161,17 +161,17 @@ public:
   bool Listen(unsigned short port);
   bool Connect(const char *addr, unsigned short port);
 #endif
 
 #ifdef SCTP_DTLS_SUPPORTED
   // Connect using a TransportFlow (DTLS) channel
   void SetEvenOdd();
   bool ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport);
-  void CompleteConnect(TransportFlow *flow, TransportLayer::State state);
+  void CompleteConnect(TransportLayer *layer, TransportLayer::State state);
   void SetSignals();
 #endif
 
   typedef enum {
     RELIABLE=0,
     PARTIAL_RELIABLE_REXMIT = 1,
     PARTIAL_RELIABLE_TIMED = 2
   } Type;
@@ -236,18 +236,18 @@ protected:
   // Use from main thread only as WeakPtr is not threadsafe
   WeakPtr<DataConnectionListener> mListener;
 
 private:
   friend class DataChannelConnectRunnable;
 
 #ifdef SCTP_DTLS_SUPPORTED
   static void DTLSConnectThread(void *data);
-  int SendPacket(unsigned char data[], size_t len, bool release);
-  void SctpDtlsInput(TransportFlow *flow, const unsigned char *data, size_t len);
+  int SendPacket(nsAutoPtr<MediaPacket> packet);
+  void SctpDtlsInput(TransportLayer *layer, MediaPacket& packet);
   static int SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df);
 #endif
   DataChannel* FindChannelByStream(uint16_t stream);
   uint16_t FindFreeStream();
   bool RequestMoreStreams(int32_t aNeeded = 16);
   uint32_t UpdateCurrentStreamIndex();
   uint32_t GetCurrentStreamIndex();
   int SendControlMessage(const uint8_t *data, uint32_t len, uint16_t stream);
@@ -329,16 +329,17 @@ private:
   AutoTArray<uint16_t,4> mStreamsResetting;
 
   struct socket *mMasterSocket; // accessed from STS thread
   struct socket *mSocket; // cloned from mMasterSocket on successful Connect on STS thread
   uint16_t mState; // Protected with mLock
 
 #ifdef SCTP_DTLS_SUPPORTED
   RefPtr<TransportFlow> mTransportFlow;
+  TransportLayerDtls* mDtls;
   nsCOMPtr<nsIEventTarget> mSTS;
 #endif
   uint16_t mLocalPort; // Accessed from connect thread
   uint16_t mRemotePort;
 
   nsCOMPtr<nsIThread> mInternalIOThread;
   uint8_t mPendingType;
   nsCString mRecvBuffer;
--- a/servo/components/selectors/builder.rs
+++ b/servo/components/selectors/builder.rs
@@ -31,16 +31,17 @@ use std::slice;
 /// consumer and never moved (because it contains a lot of inline data that
 /// would be slow to memmov).
 ///
 /// After instantation, callers may call the push_simple_selector() and
 /// push_combinator() methods to append selector data as it is encountered
 /// (from left to right). Once the process is complete, callers should invoke
 /// build(), which transforms the contents of the SelectorBuilder into a heap-
 /// allocated Selector and leaves the builder in a drained state.
+#[derive(Debug)]
 pub struct SelectorBuilder<Impl: SelectorImpl> {
     /// The entire sequence of simple selectors, from left to right, without combinators.
     ///
     /// We make this large because the result of parsing a selector is fed into a new
     /// Arc-ed allocation, so any spilled vec would be a wasted allocation. Also,
     /// Components are large enough that we don't have much cache locality benefit
     /// from reserving stack space for fewer of them.
     simple_selectors: SmallVec<[Component<Impl>; 32]>,
@@ -99,17 +100,17 @@ impl<Impl: SelectorImpl> SelectorBuilder
     /// Consumes the builder, producing a Selector.
     #[inline(always)]
     pub fn build(
         &mut self,
         parsed_pseudo: bool,
         parsed_slotted: bool,
     ) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
         // Compute the specificity and flags.
-        let mut spec = SpecificityAndFlags(specificity(self.simple_selectors.iter()));
+        let mut spec = SpecificityAndFlags(specificity(&*self, self.simple_selectors.iter()));
         if parsed_pseudo {
             spec.0 |= HAS_PSEUDO_BIT;
         }
 
         if parsed_slotted {
             spec.0 |= HAS_SLOTTED_BIT;
         }
 
@@ -263,35 +264,45 @@ impl From<u32> for Specificity {
 impl From<Specificity> for u32 {
     fn from(specificity: Specificity) -> u32 {
         cmp::min(specificity.id_selectors, MAX_10BIT) << 20 |
             cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10 |
             cmp::min(specificity.element_selectors, MAX_10BIT)
     }
 }
 
-fn specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> u32
+fn specificity<Impl>(builder: &SelectorBuilder<Impl>, iter: slice::Iter<Component<Impl>>) -> u32
 where
     Impl: SelectorImpl,
 {
-    complex_selector_specificity(iter).into()
+    complex_selector_specificity(builder, iter).into()
 }
 
-fn complex_selector_specificity<Impl>(mut iter: slice::Iter<Component<Impl>>) -> Specificity
+fn complex_selector_specificity<Impl>(
+    builder: &SelectorBuilder<Impl>,
+    mut iter: slice::Iter<Component<Impl>>,
+) -> Specificity
 where
     Impl: SelectorImpl,
 {
     fn simple_selector_specificity<Impl>(
+        builder: &SelectorBuilder<Impl>,
         simple_selector: &Component<Impl>,
         specificity: &mut Specificity,
     ) where
         Impl: SelectorImpl,
     {
         match *simple_selector {
-            Component::Combinator(..) => unreachable!(),
+            Component::Combinator(ref combinator) => {
+                unreachable!(
+                    "Found combinator {:?} in simple selectors vector? {:?}",
+                    combinator,
+                    builder,
+                );
+            }
             // FIXME(emilio): Spec doesn't define any particular specificity for
             // ::slotted(), so apply the general rule for pseudos per:
             //
             // https://github.com/w3c/csswg-drafts/issues/1915
             //
             // Though other engines compute it dynamically, so maybe we should
             // do that instead, eventually.
             Component::Slotted(..) | Component::PseudoElement(..) | Component::LocalName(..) => {
@@ -321,20 +332,20 @@ where
             Component::ExplicitAnyNamespace |
             Component::ExplicitNoNamespace |
             Component::DefaultNamespace(..) |
             Component::Namespace(..) => {
                 // Does not affect specificity
             },
             Component::Negation(ref negated) => {
                 for ss in negated.iter() {
-                    simple_selector_specificity(&ss, specificity);
+                    simple_selector_specificity(builder, &ss, specificity);
                 }
             },
         }
     }
 
     let mut specificity = Default::default();
     for simple_selector in &mut iter {
-        simple_selector_specificity(&simple_selector, &mut specificity);
+        simple_selector_specificity(builder, &simple_selector, &mut specificity);
     }
     specificity
 }
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -87,17 +87,17 @@ macro_rules! with_all_bounds {
         [ $( $FromStr: tt )* ]
     ) => {
         /// This trait allows to define the parser implementation in regards
         /// of pseudo-classes/elements
         ///
         /// NB: We need Clone so that we can derive(Clone) on struct with that
         /// are parameterized on SelectorImpl. See
         /// <https://github.com/rust-lang/rust/issues/26925>
-        pub trait SelectorImpl: Clone + Sized + 'static {
+        pub trait SelectorImpl: Clone + Debug + Sized + 'static {
             type ExtraMatchingData: Sized + Default + 'static;
             type AttrValue: $($InSelector)*;
             type Identifier: $($InSelector)*;
             type ClassName: $($InSelector)*;
             type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>;
             type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl>;
             type NamespacePrefix: $($InSelector)* + Default;
             type BorrowedNamespaceUrl: ?Sized + Eq;