Bug 790517: mtransport - Generic media transport subsystem for ICE and DTLS r=jesup,bsmith,mcmanus
authorEKR <ekr@rtfm.com>
Tue, 02 Oct 2012 13:04:58 -0700
changeset 116428 a8fac69129b0bac8b55e33574f4c4338d494b150
parent 116427 da39ec3b97783342c892e6a9b8abaedeb372d8fc
child 116429 2b08abdcc8a9de9e9b03d5e6f8961d1103376225
push id239
push userakeybl@mozilla.com
push dateThu, 03 Jan 2013 21:54:43 +0000
treeherdermozilla-release@3a7b66445659 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, bsmith, mcmanus
bugs790517
milestone18.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
Bug 790517: mtransport - Generic media transport subsystem for ICE and DTLS r=jesup,bsmith,mcmanus
media/mtransport/README
media/mtransport/build/Makefile.in
media/mtransport/dtlsidentity.cpp
media/mtransport/dtlsidentity.h
media/mtransport/logging.h
media/mtransport/m_cpp_utils.h
media/mtransport/nr_socket_prsock.cpp
media/mtransport/nr_socket_prsock.h
media/mtransport/nr_timer.cpp
media/mtransport/nricectx.cpp
media/mtransport/nricectx.h
media/mtransport/nricemediastream.cpp
media/mtransport/nricemediastream.h
media/mtransport/objs.mk
media/mtransport/runnable_utils.h
media/mtransport/runnable_utils.py
media/mtransport/runnable_utils_generated.h
media/mtransport/sigslot.h
media/mtransport/standalone/Makefile.in
media/mtransport/test/Makefile.in
media/mtransport/test/gtest_utils.h
media/mtransport/test/ice_unittest.cpp
media/mtransport/test/mtransport_test_utils.h
media/mtransport/test/nrappkit_unittest.cpp
media/mtransport/test/runnable_utils_unittest.cpp
media/mtransport/test/sctp_unittest.cpp
media/mtransport/test/sockettransportservice_unittest.cpp
media/mtransport/test/transport_unittests.cpp
media/mtransport/third_party/nICEr/nicer.gyp
media/mtransport/transportflow.cpp
media/mtransport/transportflow.h
media/mtransport/transportlayer.cpp
media/mtransport/transportlayer.h
media/mtransport/transportlayerdtls.cpp
media/mtransport/transportlayerdtls.h
media/mtransport/transportlayerice.cpp
media/mtransport/transportlayerice.h
media/mtransport/transportlayerlog.cpp
media/mtransport/transportlayerlog.h
media/mtransport/transportlayerloopback.cpp
media/mtransport/transportlayerloopback.h
media/mtransport/transportlayerprsock.cpp
media/mtransport/transportlayerprsock.h
media/webrtc/shared_libs.mk
netwerk/sctp/datachannel/DataChannel.h
security/manager/ssl/src/ScopedNSSTypes.h
toolkit/toolkit-makefiles.sh
toolkit/toolkit-tiers.mk
new file mode 100644
--- /dev/null
+++ b/media/mtransport/README
@@ -0,0 +1,59 @@
+This is a generic media transport system for WebRTC.
+
+The basic model is that you have a TransportFlow which contains a
+series of TransportLayers, each of which gets an opportunity to
+manipulate data up and down the stack (think SysV STREAMS or a
+standard networking stack). You can also address individual
+sublayers to manipulate them or to bypass reading and writing
+at an upper layer; WebRTC uses this to implement DTLS-SRTP.
+
+
+DATAFLOW MODEL
+Unlike the existing nsSocket I/O system, this is a push rather
+than a pull system. Clients of the interface do writes downward
+with SendPacket() and receive notification of incoming packets
+via callbacks registed via sigslot.h. It is the responsibility
+of the bottom layer (or any other layer which needs to reference
+external events) to arrange for that somehow; typically by
+using nsITimer or the SocketTansportService.
+
+This sort of push model is a much better fit for the demands
+of WebRTC, expecially because ICE contexts span multiple
+network transports.
+
+
+THREADING MODEL
+There are no thread locks. It is the responsibility of the caller to
+arrange that any given TransportLayer/TransportFlow is only
+manipulated in one thread at once. One good way to do this is to run
+everything on the STS thread. Many of the existing layer implementations
+(TransportLayerPrsock, TransportLayerIce, TransportLayerLoopback)
+already run on STS so in those cases you must run on STS, though
+you can do setup on the main thread and then activate them on the
+STS.
+
+
+EXISTING TRANSPORT LAYERS
+The following transport layers are currently implemented:
+
+* DTLS -- a wrapper around NSS's DTLS [RFC 6347] stack
+* ICE  -- a wrapper around the nICEr ICE [RFC 5245] stack.
+* Prsock -- a wrapper around NSPR sockets
+* Loopback -- a loopback IO mechanism
+* Logging -- a passthrough that just logs its data
+
+The last three are primarily for debugging.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/build/Makefile.in
@@ -0,0 +1,64 @@
+# -*- Mode: makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- #
+# 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/.
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+EXTRA_DEPS += $(srcdir)/../objs.mk
+
+MODULE = mtransport
+LIBRARY_NAME = mtransport
+FORCE_STATIC_LIB= 1
+ifeq (WINNT,$(OS_TARGET))
+VISIBILITY_FLAGS =
+endif
+
+LIBXUL_LIBRARY=1
+
+SRCS_IN_OBJDIR	= 1
+
+EXPORTS_NAMESPACES = mtransport
+
+EXPORTS_mtransport = \
+  ../dtlsidentity.h \
+  ../nricectx.h \
+  ../nricemediastream.h \
+  ../transportflow.h \
+  ../transportlayer.h \
+  ../transportlayerdtls.h \
+  ../transportlayerice.h \
+  ../transportlayerlog.h \
+  ../transportlayerloopback.h \
+  ../transportlayerprsock.h \
+  ../m_cpp_utils.h \
+  ../runnable_utils.h \
+  ../sigslot.h \
+  $(NULL)
+
+CPPSRCS = \
+	$(MTRANSPORT_LCPPSRCS) \
+	$(NULL)
+
+include $(srcdir)/../objs.mk
+
+
+# Make a copy into the local directory for dual compilation
+export:: $(MTRANSPORT_CPPSRCS)
+	$(INSTALL) $^ .
+
+# for stun.h
+ifeq (WINNT,$(OS_TARGET))
+DEFINES += \
+  -DWIN32 \
+  -DNOMINMAX \
+  $(NULL)
+else ifeq (Linux,$(OS_TARGET))
+DEFINES += -DLINUX
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/media/mtransport/dtlsidentity.cpp
@@ -0,0 +1,272 @@
+/* -*- 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 "nspr.h"
+#include "cryptohi.h"
+#include "ssl.h"
+#include "keyhi.h"
+#include "pk11pub.h"
+#include "sechash.h"
+#include "nsError.h"
+#include "dtlsidentity.h"
+#include "logging.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport");
+
+DtlsIdentity::~DtlsIdentity() {
+  // XXX: make cert_ a smart pointer to avoid this, after we figure
+  // out the linking problem.
+  if (cert_)
+    CERT_DestroyCertificate(cert_);
+}
+
+TemporaryRef<DtlsIdentity> DtlsIdentity::Generate() {
+
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  if (!slot) {
+    return nullptr;
+  }
+
+  uint8_t random_name[16];
+
+  SECStatus rv = PK11_GenerateRandomOnSlot(slot, random_name,
+                                           sizeof(random_name));
+  if (rv != SECSuccess)
+    return nullptr;
+
+  std::string name;
+  char chunk[3];
+  for (int i=0; i<sizeof(random_name); ++i) {
+    PR_snprintf(chunk, sizeof(chunk), "%.2x", random_name[i]);
+    name += chunk;
+  }
+
+  std::string subject_name_string = "CN=" + name;
+  ScopedCERTName subject_name(CERT_AsciiToName(subject_name_string.c_str()));
+  if (!subject_name) {
+    return nullptr;
+  }
+
+  PK11RSAGenParams rsaparams;
+  rsaparams.keySizeInBits = 2048; // Minimum value Mozilla recommends for TLS
+  rsaparams.pe = 65537; // We are too paranoid to use 3 as the exponent.
+
+  ScopedSECKEYPrivateKey private_key;
+  ScopedSECKEYPublicKey public_key;
+  SECKEYPublicKey *pubkey;
+
+  private_key =
+      PK11_GenerateKeyPair(slot,
+                           CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaparams, &pubkey,
+                           PR_FALSE, PR_TRUE, nullptr);
+  if (private_key == nullptr)
+    return nullptr;
+  public_key = pubkey;
+
+  ScopedCERTSubjectPublicKeyInfo spki(
+      SECKEY_CreateSubjectPublicKeyInfo(pubkey));
+  if (!spki) {
+    return nullptr;
+  }
+
+  ScopedCERTCertificateRequest certreq(
+      CERT_CreateCertificateRequest(subject_name, spki, nullptr));
+  if (!certreq) {
+    return nullptr;
+  }
+
+  // From 1 day before todayto 30 days after.
+  // This is a sort of arbitrary range designed to be valid
+  // now with some slack in case the other side expects
+  // some before expiry.
+  //
+  // Note: explicit casts necessary to avoid 
+  //       warning C4307: '*' : integral constant overflow
+  static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
+                             * PRTime(60)  // sec
+                             * PRTime(60)  // min
+                             * PRTime(24); // hours
+  PRTime now = PR_Now();
+  PRTime notBefore = now - oneDay;
+  PRTime notAfter = now + (PRTime(30) * oneDay);
+
+  ScopedCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
+  if (!validity) {
+    return nullptr;
+  }
+
+  unsigned long serial;
+  // Note: This serial in principle could collide, but it's unlikely
+  rv = PK11_GenerateRandomOnSlot(slot,
+                                 reinterpret_cast<unsigned char *>(&serial),
+                                 sizeof(serial));
+  if (rv != SECSuccess) {
+    return nullptr;
+  }
+
+  ScopedCERTCertificate certificate(
+      CERT_CreateCertificate(serial, subject_name, validity, certreq));
+  if (!certificate) {
+    return nullptr;
+  }
+
+  PRArenaPool *arena = certificate->arena;
+
+  rv = SECOID_SetAlgorithmID(arena, &certificate->signature,
+                             SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, 0);
+  if (rv != SECSuccess)
+    return nullptr;
+
+  // Set version to X509v3.
+  *(certificate->version.data) = SEC_CERTIFICATE_VERSION_3;
+  certificate->version.len = 1;
+
+  SECItem innerDER;
+  innerDER.len = 0;
+  innerDER.data = nullptr;
+
+  if (!SEC_ASN1EncodeItem(arena, &innerDER, certificate,
+                          SEC_ASN1_GET(CERT_CertificateTemplate))) {
+    return nullptr;
+  }
+
+  SECItem *signedCert = PORT_ArenaZNew(arena, SECItem);
+  if (!signedCert) {
+    return nullptr;
+  }
+
+  rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len,
+                       private_key,
+                       SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION);
+  if (rv != SECSuccess) {
+    return nullptr;
+  }
+  certificate->derCert = *signedCert;
+
+  return new DtlsIdentity(private_key.forget(), certificate.forget());
+}
+
+
+nsresult DtlsIdentity::ComputeFingerprint(const std::string algorithm,
+                                          unsigned char *digest,
+                                          std::size_t size,
+                                          std::size_t *digest_length) {
+  MOZ_ASSERT(cert_);
+
+  return ComputeFingerprint(cert_, algorithm, digest, size, digest_length);
+}
+
+nsresult DtlsIdentity::ComputeFingerprint(const CERTCertificate *cert,
+                                          const std::string algorithm,
+                                          unsigned char *digest,
+                                          std::size_t size,
+                                          std::size_t *digest_length) {
+  MOZ_ASSERT(cert);
+
+  HASH_HashType ht;
+
+  if (algorithm == "sha-1") {
+    ht = HASH_AlgSHA1;
+  } else if (algorithm == "sha-224") {
+    ht = HASH_AlgSHA224;
+  } else if (algorithm == "sha-256") {
+    ht = HASH_AlgSHA256;
+  } else if (algorithm == "sha-384") {
+    ht = HASH_AlgSHA384;
+  }  else if (algorithm == "sha-512") {
+    ht = HASH_AlgSHA512;
+  } else {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  const SECHashObject *ho = HASH_GetHashObject(ht);
+  MOZ_ASSERT(ho);
+  if (!ho)
+    return NS_ERROR_INVALID_ARG;
+
+  MOZ_ASSERT(ho->length >= 20);  // Double check
+
+  if (size < ho->length)
+    return NS_ERROR_INVALID_ARG;
+
+  SECStatus rv = HASH_HashBuf(ho->type, digest,
+                              cert->derCert.data,
+                              cert->derCert.len);
+  if (rv != SECSuccess)
+    return NS_ERROR_FAILURE;
+
+  *digest_length = ho->length;
+
+  return NS_OK;
+}
+
+// Format the fingerprint in RFC 4572 Section 5 format, colons and
+// all.
+std::string DtlsIdentity::FormatFingerprint(const unsigned char *digest,
+                                            std::size_t size) {
+  std::string str("");
+  char group[3];
+
+  for (std::size_t i=0; i < size; i++) {
+    PR_snprintf(group, sizeof(group), "%.2X", digest[i]);
+    if (i != 0){
+      str += ":";
+    }
+    str += group;
+  }
+
+  MOZ_ASSERT(str.size() == (size * 3 - 1));  // Check result length
+  return str;
+}
+
+// Parse a fingerprint in RFC 4572 format.
+// Note that this tolerates some badly formatted data, in particular:
+// (a) arbitrary runs of colons
+// (b) colons at the beginning or end.
+nsresult DtlsIdentity::ParseFingerprint(const std::string fp,
+                                        unsigned char *digest,
+                                        size_t size,
+                                        size_t *length) {
+  size_t offset = 0;
+  bool top_half = true;
+  uint8_t val = 0;
+
+  for (size_t i=0; i<fp.length(); i++) {
+    if (offset >= size) {
+      // Note: no known way for offset to get > size
+      MOZ_MTLOG(PR_LOG_ERROR, "Fingerprint too long for buffer");
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    if (top_half && (fp[i] == ':')) {
+      continue;
+    } else if ((fp[i] >= '0') && (fp[i] <= '9')) {
+      val |= fp[i] - '0';
+    } else if ((fp[i] >= 'A') && (fp[i] <= 'F')) {
+      val |= fp[i] - 'A' + 10;
+    } else {
+      MOZ_MTLOG(PR_LOG_ERROR, "Invalid fingerprint value " << fp[i]);
+      return NS_ERROR_ILLEGAL_VALUE;
+    }
+
+    if (top_half) {
+      val <<= 4;
+      top_half = false;
+    } else {
+      digest[offset++] = val;
+      top_half = true;
+      val = 0;
+    }
+  }
+
+  *length = offset;
+
+  return NS_OK;
+}
+
+}  // close namespace
new file mode 100644
--- /dev/null
+++ b/media/mtransport/dtlsidentity.h
@@ -0,0 +1,64 @@
+/* -*- 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 dtls_identity_h__
+#define dtls_identity_h__
+
+#include <string>
+
+#include "m_cpp_utils.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+#include "ScopedNSSTypes.h"
+
+// All code in this module requires NSS to be live.
+// Callers must initialize NSS and implement the nsNSSShutdownObject
+// protocol.
+namespace mozilla {
+
+class DtlsIdentity {
+ public:
+  ~DtlsIdentity();
+
+  // Generate an identity with a random name.
+  static TemporaryRef<DtlsIdentity> Generate();
+
+  // Note: the following two functions just provide access. They
+  // do not transfer ownership. If you want a pointer that lasts
+  // past the lifetime of the DtlsIdentity, you must make
+  // a copy yourself.
+  CERTCertificate *cert() { return cert_; }
+  SECKEYPrivateKey *privkey() { return privkey_; }
+
+  nsresult ComputeFingerprint(const std::string algorithm,
+                              unsigned char *digest,
+                              std::size_t size,
+                              std::size_t *digest_length);
+
+  static nsresult ComputeFingerprint(const CERTCertificate *cert,
+                                     const std::string algorithm,
+                                     unsigned char *digest,
+                                     std::size_t size,
+                                     std::size_t *digest_length);
+
+  static std::string FormatFingerprint(const unsigned char *digest,
+                                       std::size_t size);
+  static nsresult ParseFingerprint(const std::string fp,
+                                   unsigned char *digest,
+                                   size_t size, size_t *length);
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DtlsIdentity)
+
+ private:
+  DtlsIdentity(SECKEYPrivateKey *privkey, CERTCertificate *cert)
+      : privkey_(privkey), cert_(cert) {}
+  DISALLOW_COPY_ASSIGN(DtlsIdentity);
+
+  ScopedSECKEYPrivateKey privkey_;
+  CERTCertificate *cert_;  // TODO: Using a smart pointer here causes link
+                           // errors.
+};
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/logging.h
@@ -0,0 +1,42 @@
+/* -*- 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 logging_h__
+#define logging_h__
+
+#include <sstream>
+
+#include <prlog.h>
+#include "mozilla/Scoped.h"
+
+namespace mozilla {
+
+class LogCtx {
+ public:
+  LogCtx(const char* name) : module_(PR_NewLogModule(name)) {}
+  LogCtx(std::string& name) : module_(PR_NewLogModule(name.c_str())) {}
+
+  PRLogModuleInfo* module() const { return module_; }
+
+private:
+  PRLogModuleInfo* module_;
+};
+
+
+#define MOZ_MTLOG_MODULE(n) \
+  static ScopedDeletePtr<LogCtx> mlog_ctx;      \
+  static const char *mlog_name = n
+
+#define MOZ_MTLOG(level, b) \
+  do { if (!mlog_ctx) mlog_ctx = new LogCtx(mlog_name);    \
+    if (mlog_ctx) {                                             \
+    std::stringstream str;                                              \
+    str << b;                                                           \
+    PR_LOG(mlog_ctx->module(), level, ("%s", str.str().c_str())); }} while(0)
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/m_cpp_utils.h
@@ -0,0 +1,28 @@
+/* -*- 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 m_cpp_utils_h__
+#define m_cpp_utils_h__
+
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+
+#define DISALLOW_ASSIGNMENT(T) \
+  void operator=(const T& other) MOZ_DELETE
+
+#define DISALLOW_COPY(T) \
+  T(const T& other) MOZ_DELETE
+
+
+#define DISALLOW_COPY_ASSIGN(T) \
+  DISALLOW_COPY(T); \
+  DISALLOW_ASSIGNMENT(T)
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -0,0 +1,518 @@
+/* -*- 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/. */
+/*
+Modified version of nr_socket_local, adapted for NSPR
+*/
+
+/* 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 code from nICEr and nrappkit.
+
+nICEr copyright:
+
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+nrappkit copyright:
+
+   Copyright (C) 2001-2003, Network Resonance, Inc.
+   Copyright (C) 2006, Network Resonance, Inc.
+   All Rights Reserved
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+   3. Neither the name of Network Resonance, Inc. nor the name of any
+      contributors to this software may be used to endorse or promote
+      products derived from this software without specific prior written
+      permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE.
+
+
+   ekr@rtfm.com  Thu Dec 20 20:14:49 2001
+*/
+
+#include <csi_platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "nspr.h"
+#include "prerror.h"
+#include "prio.h"
+#include "prnetdb.h"
+
+#include "nsCOMPtr.h"
+#include "nsASocketHandler.h"
+#include "nsISocketTransportService.h"
+#include "nsNetCID.h"
+#include "nsISupportsImpl.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXPCOM.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "async_wait.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+}
+
+#include "nr_socket_prsock.h"
+
+// Implement the nsISupports ref counting
+namespace mozilla {
+
+NS_IMPL_THREADSAFE_ISUPPORTS0(NrSocket)
+
+
+// The nsASocket callbacks
+void NrSocket::OnSocketReady(PRFileDesc *fd, int16_t outflags) {
+  if (outflags & PR_POLL_READ)
+    fire_callback(NR_ASYNC_WAIT_READ);
+  if (outflags & PR_POLL_WRITE)
+    fire_callback(NR_ASYNC_WAIT_WRITE);
+}
+
+void NrSocket::OnSocketDetached(PRFileDesc *fd) {
+  ;  // TODO: Log?
+}
+
+void NrSocket::IsLocal(bool *aIsLocal) {
+  // TODO(jesup): better check? Does it matter? (likely no)
+  *aIsLocal = false;
+}
+
+// async_event APIs
+int NrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg,
+                         char *function, int line) {
+  uint16_t flag;
+
+  switch (how) {
+    case NR_ASYNC_WAIT_READ:
+      flag = PR_POLL_READ;
+      break;
+    case NR_ASYNC_WAIT_WRITE:
+      flag = PR_POLL_WRITE;
+      break;
+    default:
+      return R_BAD_ARGS;
+  }
+
+  cbs_[how] = cb;
+  cb_args_[how] = cb_arg;
+  mPollFlags |= flag;
+
+  return 0;
+}
+
+int NrSocket::cancel(int how) {
+  uint16_t flag;
+
+  switch (how) {
+    case NR_ASYNC_WAIT_READ:
+      flag = PR_POLL_READ;
+      break;
+    case NR_ASYNC_WAIT_WRITE:
+      flag = PR_POLL_WRITE;
+      break;
+    default:
+      return R_BAD_ARGS;
+  }
+
+  mPollFlags &= ~flag;
+
+  return 0;
+}
+
+void NrSocket::fire_callback(int how) {
+  // This can't happen unless we are armed because we only set
+  // the flags if we are armed
+  MOZ_ASSERT(cbs_[how]);
+
+  // Now cancel so that we need to be re-armed. Note that
+  // the re-arming probably happens in the callback we are
+  // about to fire.
+  cancel(how);
+
+  cbs_[how](this, how, cb_args_[how]);
+}
+
+// Helper functions for addresses
+static int nr_transport_addr_to_praddr(nr_transport_addr *addr,
+  PRNetAddr *naddr)
+  {
+    int _status;
+
+    memset(naddr, 0, sizeof(*naddr));
+
+    switch(addr->protocol){
+      case IPPROTO_TCP:
+        ABORT(R_INTERNAL); /* Can't happen for now */
+        break;
+      case IPPROTO_UDP:
+        break;
+      default:
+        ABORT(R_BAD_ARGS);
+    }
+
+    switch(addr->ip_version){
+      case NR_IPV4:
+        naddr->inet.family = PR_AF_INET;
+        naddr->inet.port = addr->u.addr4.sin_port;
+        naddr->inet.ip = addr->u.addr4.sin_addr.s_addr;
+        break;
+      case NR_IPV6:
+#if 0
+        naddr->ipv6.family = PR_AF_INET6;
+        naddr->ipv6.port = addr->u.addr6.sin6_port;
+#ifdef LINUX
+        memcpy(naddr->ipv6.ip._S6_un._S6_u8,
+               &addr->u.addr6.sin6_addr.__in6_u.__u6_addr8, 16);
+#else
+        memcpy(naddr->ipv6.ip._S6_un._S6_u8,
+               &addr->u.addr6.sin6_addr.__u6_addr.__u6_addr8, 16);
+#endif
+#else
+        // TODO: make IPv6 work
+        ABORT(R_INTERNAL);
+#endif
+        break;
+      default:
+        ABORT(R_BAD_ARGS);
+    }
+
+    _status = 0;
+  abort:
+    return(_status);
+  }
+
+static int nr_praddr_to_transport_addr(PRNetAddr *praddr,
+  nr_transport_addr *addr, int keep)
+  {
+    int _status;
+    int r;
+    struct sockaddr_in ip4;
+
+    switch(praddr->raw.family) {
+      case PR_AF_INET:
+        ip4.sin_family = PF_INET;
+        ip4.sin_addr.s_addr = praddr->inet.ip;
+        ip4.sin_port = praddr->inet.port;
+        if ((r = nr_sockaddr_to_transport_addr((sockaddr *)&ip4,
+                                              sizeof(ip4),
+                                              IPPROTO_UDP, 1,
+              addr)))
+          ABORT(r);
+        break;
+      case PR_AF_INET6:
+#if 0
+        r = nr_sockaddr_to_transport_addr((sockaddr *)&praddr->raw,
+          sizeof(struct sockaddr_in6),IPPROTO_UDP,keep,addr);
+        break;
+#endif
+        ABORT(R_BAD_ARGS);
+      default:
+        ABORT(R_BAD_ARGS);
+    }
+
+    _status=0;
+ abort:
+    return(_status);
+  }
+
+
+// nr_socket APIs (as member functions)
+int NrSocket::create(nr_transport_addr *addr) {
+  int r,_status;
+
+  PRStatus status;
+  PRNetAddr naddr;
+
+  nsresult rv;
+  nsCOMPtr<nsISocketTransportService> stservice =
+      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+  if (!NS_SUCCEEDED(rv)) {
+    ABORT(R_INTERNAL);
+  }
+
+  if((r=nr_transport_addr_to_praddr(addr, &naddr)))
+    ABORT(r);
+
+  if (!(fd_ = PR_NewUDPSocket())) {
+    r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create socket");
+    ABORT(R_INTERNAL);
+  }
+
+  status = PR_Bind(fd_, &naddr);
+  if (status != PR_SUCCESS) {
+    r_log(LOG_GENERIC,LOG_CRIT,"Couldn't bind socket to address %s",
+          addr->as_string);
+    ABORT(R_INTERNAL);
+  }
+
+  r_log(LOG_GENERIC,LOG_DEBUG,"Creating socket %d with addr %s",
+        fd_, addr->as_string);
+  nr_transport_addr_copy(&my_addr_,addr);
+
+  /* If we have a wildcard port, patch up the addr */
+  if(nr_transport_addr_is_wildcard(addr)){
+    status = PR_GetSockName(fd_, &naddr);
+    if (status != PR_SUCCESS){
+      r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
+      ABORT(R_INTERNAL);
+    }
+
+    if((r=nr_praddr_to_transport_addr(&naddr,&my_addr_,1)))
+      ABORT(r);
+  }
+
+
+  // Set nonblocking
+  PRSocketOptionData option;
+  option.option = PR_SockOpt_Nonblocking;
+  option.value.non_blocking = PR_TRUE;
+  status = PR_SetSocketOption(fd_, &option);
+  if (status != PR_SUCCESS) {
+    r_log(LOG_GENERIC, LOG_CRIT, "Couldn't make socket nonblocking");
+    ABORT(R_INTERNAL);
+  }
+
+  // Finally, register with the STS
+  rv = stservice->AttachSocket(fd_, this);
+  if (!NS_SUCCEEDED(rv)) {
+    ABORT(R_INTERNAL);
+  }
+
+  _status = 0;
+
+abort:
+  return(_status);
+}
+
+// This should be called on the STS thread.
+int NrSocket::sendto(const void *msg, size_t len,
+                     int flags, nr_transport_addr *to) {
+  int r,_status;
+  PRNetAddr naddr;
+  int32_t status;
+
+  if ((r=nr_transport_addr_to_praddr(to, &naddr)))
+    ABORT(r);
+
+  if(fd_==nullptr)
+    ABORT(R_EOD);
+
+  // TODO: Convert flags?
+  status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT);
+  if (status < 0 || (size_t)status != len) {
+    r_log_e(LOG_GENERIC, LOG_INFO, "Error in sendto %s", to->as_string);
+
+    ABORT(R_IO_ERROR);
+  }
+
+  _status=0;
+abort:
+  return(_status);
+}
+
+int NrSocket::recvfrom(void * buf, size_t maxlen,
+                                       size_t *len, int flags,
+                                       nr_transport_addr *from) {
+  int r,_status;
+  PRNetAddr nfrom;
+  int32_t status;
+
+  status = PR_RecvFrom(fd_, buf, maxlen, flags, &nfrom, PR_INTERVAL_NO_WAIT);
+  if (status <= 0) {
+    r_log_e(LOG_GENERIC,LOG_ERR,"Error in recvfrom");
+    ABORT(R_IO_ERROR);
+  }
+  *len=status;
+
+  if((r=nr_praddr_to_transport_addr(&nfrom,from,0)))
+    ABORT(r);
+
+  //r_log(LOG_GENERIC,LOG_DEBUG,"Read %d bytes from %s",*len,addr->as_string);
+
+  _status=0;
+abort:
+  return(_status);
+}
+
+int NrSocket::getaddr(nr_transport_addr *addrp) {
+  return nr_transport_addr_copy(addrp, &my_addr_);
+}
+
+// Close the socket so that the STS will detach and then kill it
+void NrSocket::close() {
+  mCondition = NS_BASE_STREAM_CLOSED;
+}
+}  // close namespace
+
+
+using namespace mozilla;
+
+// Bridge to the nr_socket interface
+static int nr_socket_local_destroy(void **objp);
+static int nr_socket_local_sendto(void *obj,const void *msg, size_t len,
+                                  int flags, nr_transport_addr *to);
+static int nr_socket_local_recvfrom(void *obj,void * restrict buf,
+  size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
+static int nr_socket_local_getfd(void *obj, NR_SOCKET *fd);
+static int nr_socket_local_getaddr(void *obj, nr_transport_addr *addrp);
+static int nr_socket_local_close(void *obj);
+
+static nr_socket_vtbl nr_socket_local_vtbl={
+  nr_socket_local_destroy,
+  nr_socket_local_sendto,
+  nr_socket_local_recvfrom,
+  nr_socket_local_getfd,
+  nr_socket_local_getaddr,
+  nr_socket_local_close
+};
+
+
+int nr_socket_local_create(nr_transport_addr *addr, nr_socket **sockp) {
+  NrSocket * sock = new NrSocket();
+  int r, _status;
+
+  r = sock->create(addr);
+  if (r)
+    ABORT(r);
+
+  r = nr_socket_create_int(static_cast<void *>(sock), &nr_socket_local_vtbl, sockp);
+  if (r)
+    ABORT(r);
+
+  // Add a reference so that we can delete it in destroy()
+  sock->AddRef();
+
+  _status =0;
+
+abort:
+  if (_status) {
+    delete sock;
+  }
+  return _status;
+}
+
+
+static int nr_socket_local_destroy(void **objp) {
+  if(!objp || !*objp)
+    return 0;
+
+  NrSocket *sock = static_cast<NrSocket *>(*objp);
+  *objp=0;
+
+  sock->close();  // Signal STS that we want not to listen
+  sock->Release();  // Decrement the ref count
+
+  return 0;
+}
+
+static int nr_socket_local_sendto(void *obj,const void *msg, size_t len,
+                                  int flags, nr_transport_addr *addr) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  return sock->sendto(msg, len, flags, addr);
+}
+
+static int nr_socket_local_recvfrom(void *obj,void * restrict buf,
+                                    size_t maxlen, size_t *len, int flags,
+                                    nr_transport_addr *addr) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  return sock->recvfrom(buf, maxlen, len, flags, addr);
+}
+
+static int nr_socket_local_getfd(void *obj, NR_SOCKET *fd) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  *fd = sock;
+
+  return 0;
+}
+
+static int nr_socket_local_getaddr(void *obj, nr_transport_addr *addrp) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  return sock->getaddr(addrp);
+}
+
+
+static int nr_socket_local_close(void *obj) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  sock->close();
+
+  return 0;
+}
+
+// Implement async api
+int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb,void *cb_arg,
+                  char *function,int line) {
+  NrSocket *s = static_cast<NrSocket *>(sock);
+
+  return s->async_wait(how, cb, cb_arg, function, line);
+}
+
+int NR_async_cancel(NR_SOCKET sock,int how) {
+  NrSocket *s = static_cast<NrSocket *>(sock);
+
+  return s->cancel(how);
+}
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/nr_socket_prsock.h
@@ -0,0 +1,111 @@
+/* -*- 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
+
+/* Some source code here from nICEr. Copyright is:
+
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+// Implementation of nICEr/nr_socket that is tied to the Gecko
+// SocketTransportService.
+
+#ifndef nr_socket_prsock__
+#define nr_socket_prsock__
+
+#include <vector>
+
+#include "nspr.h"
+#include "prio.h"
+
+#include "nsCOMPtr.h"
+#include "nsASocketHandler.h"
+#include "nsISocketTransportService.h"
+#include "nsXPCOM.h"
+
+#include "m_cpp_utils.h"
+
+namespace mozilla {
+
+class NrSocket : public nsASocketHandler {
+public:
+  NrSocket() : fd_(nullptr) {
+    memset(&my_addr_, 0, sizeof(my_addr_));
+    memset(cbs_, 0, sizeof(cbs_));
+    memset(cb_args_, 0, sizeof(cb_args_));
+  }
+  virtual ~NrSocket() {
+    PR_Close(fd_);
+  }
+
+  // Implement nsASocket
+  virtual void OnSocketReady(PRFileDesc *fd, int16_t outflags);
+  virtual void OnSocketDetached(PRFileDesc *fd);
+  virtual void IsLocal(bool *aIsLocal);
+
+  // nsISupports methods
+  NS_DECL_ISUPPORTS
+
+  // Implementations of the async_event APIs
+  int async_wait(int how, NR_async_cb cb, void *cb_arg,
+                 char *function, int line);
+  int cancel(int how);
+
+
+  // Implementations of the nr_socket APIs
+  int create(nr_transport_addr *addr); // (really init, but it's called create)
+  int sendto(const void *msg, size_t len,
+             int flags, nr_transport_addr *to);
+  int recvfrom(void * buf, size_t maxlen,
+               size_t *len, int flags,
+               nr_transport_addr *from);
+  int getaddr(nr_transport_addr *addrp);
+  void close();
+
+private:
+  DISALLOW_COPY_ASSIGN(NrSocket);
+
+  void fire_callback(int how);
+
+  PRFileDesc *fd_;
+  nr_transport_addr my_addr_;
+  NR_async_cb cbs_[NR_ASYNC_WAIT_WRITE + 1];
+  void *cb_args_[NR_ASYNC_WAIT_WRITE + 1];
+  nsCOMPtr<nsISocketTransportService> stservice_;
+};
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/nr_timer.cpp
@@ -0,0 +1,99 @@
+/* -*- 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/. */
+
+/* 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 code by: ekr@rtfm.com
+
+// Implementation of the NR timer interface
+
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIEventTarget.h"
+#include "nsITimer.h"
+#include "nsNetCID.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "async_timer.h"
+}
+
+
+namespace mozilla {
+
+class nrappkitTimerCallback : public nsITimerCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITIMERCALLBACK
+
+  nrappkitTimerCallback(NR_async_cb cb, void *cb_arg) : cb_(cb), cb_arg_(cb_arg) {}
+
+private:
+  virtual ~nrappkitTimerCallback() {}
+
+protected:
+  /* additional members */
+  NR_async_cb cb_;
+  void *cb_arg_;
+};
+
+NS_IMPL_ISUPPORTS1(nrappkitTimerCallback, nsITimerCallback)
+
+NS_IMETHODIMP nrappkitTimerCallback::Notify(nsITimer *timer) {
+  r_log(LOG_GENERIC, LOG_DEBUG, "Timer callback fired");
+  cb_(0, 0, cb_arg_);
+
+  // Allow the timer to go away.
+  timer->Release();
+  return NS_OK;
+}
+}  // close namespace
+
+
+using namespace mozilla;
+
+int NR_async_timer_set(int timeout, NR_async_cb cb, void *arg, char *func,
+                       int l, void **handle) {
+  nsresult rv;
+
+  nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+
+  if (NS_FAILED(rv)) {
+    return(R_FAILED);
+  }
+
+  rv = timer->InitWithCallback(new nrappkitTimerCallback(cb, arg),
+                                        timeout, nsITimer::TYPE_ONE_SHOT);
+  if (NS_FAILED(rv)) {
+    return R_FAILED;
+  }
+
+  // We need an AddRef here to keep the timer alive, per the spec.
+  timer->AddRef();
+
+  if (handle)
+    *handle = timer.get();
+
+  return 0;
+}
+
+int NR_async_schedule(NR_async_cb cb, void *arg, char *func, int l) {
+  return NR_async_timer_set(0, cb, arg, func, l, nullptr);
+}
+
+int NR_async_timer_cancel(void *handle) {
+  nsITimer *timer = static_cast<nsITimer *>(handle);
+
+  timer->Cancel();
+  // Allow the timer to go away.
+  timer->Release();
+
+  return 0;
+}
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/nricectx.cpp
@@ -0,0 +1,508 @@
+/* -*- 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
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string>
+#include <vector>
+
+#include "nspr.h"
+#include "nss.h"
+#include "pk11pub.h"
+#include "prlog.h"
+
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsError.h"
+#include "nsIEventTarget.h"
+#include "nsNetCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "ScopedNSSTypes.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "registry.h"
+#include "async_timer.h"
+#include "r_crc32.h"
+#include "ice_util.h"
+#include "transport_addr.h"
+#include "nr_crypto.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "stun_client_ctx.h"
+#include "stun_server_ctx.h"
+#include "ice_codeword.h"
+#include "ice_ctx.h"
+#include "ice_candidate.h"
+#include "ice_handler.h"
+}
+
+// Local includes
+#include "logging.h"
+#include "nricectx.h"
+#include "nricemediastream.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport");
+
+static bool initialized = false;
+
+// Implement NSPR-based crypto algorithms
+static int nr_crypto_nss_random_bytes(UCHAR *buf, int len) {
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  if (!slot)
+    return R_INTERNAL;
+
+  SECStatus rv = PK11_GenerateRandomOnSlot(slot, buf, len);
+  if (rv != SECSuccess)
+    return R_INTERNAL;
+
+  return 0;
+}
+
+static int nr_crypto_nss_hmac(UCHAR *key, int keyl, UCHAR *buf, int bufl,
+                              UCHAR *result) {
+  CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC;
+  PK11SlotInfo *slot = 0;
+  MOZ_ASSERT(keyl > 0);
+  SECItem keyi = { siBuffer, key, static_cast<unsigned int>(keyl)};
+  PK11SymKey *skey = 0;
+  PK11Context *hmac_ctx = 0;
+  SECStatus status;
+  unsigned int hmac_len;
+  SECItem param = { siBuffer, nullptr, 0 };
+  int err = R_INTERNAL;
+
+  slot = PK11_GetInternalKeySlot();
+  if (!slot)
+    goto abort;
+
+  skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap,
+                          CKA_SIGN, &keyi, nullptr);
+  if (!skey)
+    goto abort;
+
+
+  hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN,
+                                        skey, &param);
+
+  status = PK11_DigestBegin(hmac_ctx);
+  if (status != SECSuccess)
+    goto abort;
+
+  status = PK11_DigestOp(hmac_ctx, buf, bufl);
+  if (status != SECSuccess)
+    goto abort;
+
+  status = PK11_DigestFinal(hmac_ctx, result, &hmac_len, 20);
+  if (status != SECSuccess)
+    goto abort;
+
+  MOZ_ASSERT(hmac_len == 20);
+
+  err = 0;
+
+ abort:
+  if(hmac_ctx) PK11_DestroyContext(hmac_ctx, PR_TRUE);
+  if (skey) PK11_FreeSymKey(skey);
+  if (slot) PK11_FreeSlot(slot);
+
+  return err;
+}
+
+static nr_ice_crypto_vtbl nr_ice_crypto_nss_vtbl = {
+  nr_crypto_nss_random_bytes,
+  nr_crypto_nss_hmac
+};
+
+
+
+// NrIceCtx
+
+// Handler callbacks
+int NrIceCtx::select_pair(void *obj,nr_ice_media_stream *stream,
+                   int component_id, nr_ice_cand_pair **potentials,
+                   int potential_ct) {
+  MOZ_MTLOG(PR_LOG_DEBUG, "select pair called: potential_ct = " << potential_ct);
+
+  return 0;
+}
+
+int NrIceCtx::stream_ready(void *obj, nr_ice_media_stream *stream) {
+  MOZ_MTLOG(PR_LOG_DEBUG, "stream_ready called");
+
+  // Get the ICE ctx
+  NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+
+  RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
+
+  // Streams which do not exist should never be ready.
+  MOZ_ASSERT(s);
+
+  s->Ready();
+
+  return 0;
+}
+
+int NrIceCtx::stream_failed(void *obj, nr_ice_media_stream *stream) {
+  MOZ_MTLOG(PR_LOG_DEBUG, "stream_failed called");
+
+  // Get the ICE ctx
+  NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+  RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
+
+  // Streams which do not exist should never fail.
+  MOZ_ASSERT(s);
+
+  ctx->SetState(ICE_CTX_FAILED);
+  s -> SignalFailed(s);
+  return 0;
+}
+
+int NrIceCtx::ice_completed(void *obj, nr_ice_peer_ctx *pctx) {
+  MOZ_MTLOG(PR_LOG_DEBUG, "ice_completed called");
+
+  // Get the ICE ctx
+  NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+
+  ctx->SetState(ICE_CTX_OPEN);
+
+  // Signal that we are done
+  ctx->SignalCompleted(ctx);
+
+  return 0;
+}
+
+int NrIceCtx::msg_recvd(void *obj, nr_ice_peer_ctx *pctx,
+                        nr_ice_media_stream *stream, int component_id,
+                        UCHAR *msg, int len) {
+  // Get the ICE ctx
+  NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+  RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
+
+  // Streams which do not exist should never have packets.
+  MOZ_ASSERT(s);
+
+  s->SignalPacketReceived(s, component_id, msg, len);
+
+  return 0;
+}
+
+
+RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& name,
+                                           bool offerer,
+                                           bool set_interface_priorities) {
+  RefPtr<NrIceCtx> ctx = new NrIceCtx(name, offerer);
+
+  // Initialize the crypto callbacks
+  if (!initialized) {
+    NR_reg_init(NR_REG_MODE_LOCAL);
+    nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl;
+    initialized = true;
+
+    // Set the priorites for candidate type preferences
+    NR_reg_set_uchar((char *)"ice.pref.type.srv_rflx",100);
+    NR_reg_set_uchar((char *)"ice.pref.type.peer_rflx",105);
+    NR_reg_set_uchar((char *)"ice.pref.type.prflx",99);
+    NR_reg_set_uchar((char *)"ice.pref.type.host",125);
+    NR_reg_set_uchar((char *)"ice.pref.type.relayed",126);
+
+    if (set_interface_priorities) {
+      NR_reg_set_uchar((char *)"ice.pref.interface.rl0", 255);
+      NR_reg_set_uchar((char *)"ice.pref.interface.wi0", 254);
+      NR_reg_set_uchar((char *)"ice.pref.interface.lo0", 253);
+      NR_reg_set_uchar((char *)"ice.pref.interface.en1", 252);
+      NR_reg_set_uchar((char *)"ice.pref.interface.en0", 251);
+      NR_reg_set_uchar((char *)"ice.pref.interface.eth0", 252);
+      NR_reg_set_uchar((char *)"ice.pref.interface.eth1", 251);
+      NR_reg_set_uchar((char *)"ice.pref.interface.eth2", 249);
+      NR_reg_set_uchar((char *)"ice.pref.interface.ppp", 250);
+      NR_reg_set_uchar((char *)"ice.pref.interface.ppp0", 249);
+      NR_reg_set_uchar((char *)"ice.pref.interface.en2", 248);
+      NR_reg_set_uchar((char *)"ice.pref.interface.en3", 247);
+      NR_reg_set_uchar((char *)"ice.pref.interface.em0", 251);
+      NR_reg_set_uchar((char *)"ice.pref.interface.em1", 252);
+      NR_reg_set_uchar((char *)"ice.pref.interface.vmnet0", 240);
+      NR_reg_set_uchar((char *)"ice.pref.interface.vmnet1", 241);
+      NR_reg_set_uchar((char *)"ice.pref.interface.vmnet3", 239);
+      NR_reg_set_uchar((char *)"ice.pref.interface.vmnet4", 238);
+      NR_reg_set_uchar((char *)"ice.pref.interface.vmnet5", 237);
+      NR_reg_set_uchar((char *)"ice.pref.interface.vmnet6", 236);
+      NR_reg_set_uchar((char *)"ice.pref.interface.vmnet7", 235);
+      NR_reg_set_uchar((char *)"ice.pref.interface.vmnet8", 234);
+      NR_reg_set_uchar((char *)"ice.pref.interface.virbr0", 233);
+      NR_reg_set_uchar((char *)"ice.pref.interface.wlan0", 232);
+    }
+
+    NR_reg_set_string((char *)"ice.stun.server.0.addr", (char *)"216.93.246.14");
+    NR_reg_set_uint2((char *)"ice.stun.server.0.port",3478);
+    NR_reg_set_uint4((char *)"stun.client.maximum_transmits",4);
+  }
+
+  // Create the ICE context
+  int r;
+
+  UINT4 flags = offerer ? NR_ICE_CTX_FLAGS_OFFERER:
+      NR_ICE_CTX_FLAGS_ANSWERER;
+  flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION;
+
+  r = nr_ice_ctx_create(const_cast<char *>(name.c_str()), flags,
+                        &ctx->ctx_);
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't create ICE ctx for '" << name << "'");
+    return nullptr;
+  }
+
+  // Create the handler objects
+  ctx->ice_handler_vtbl_ = new nr_ice_handler_vtbl();
+  ctx->ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair;
+  ctx->ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready;
+  ctx->ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed;
+  ctx->ice_handler_vtbl_->ice_completed = &NrIceCtx::ice_completed;
+  ctx->ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd;
+
+  ctx->ice_handler_ = new nr_ice_handler();
+  ctx->ice_handler_->vtbl = ctx->ice_handler_vtbl_;
+  ctx->ice_handler_->obj = ctx;
+
+  // Create the peer ctx. Because we do not support parallel forking, we
+  // only have one peer ctx.
+  std::string peer_name = name + ":default";
+  r = nr_ice_peer_ctx_create(ctx->ctx_, ctx->ice_handler_,
+                             const_cast<char *>(peer_name.c_str()),
+                             &ctx->peer_);
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't create ICE peer ctx for '" << name << "'");
+    return nullptr;
+  }
+
+  nsresult rv;
+  ctx->sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+  if (!NS_SUCCEEDED(rv))
+    return nullptr;
+
+  return ctx;
+}
+
+
+NrIceCtx::~NrIceCtx() {
+  MOZ_MTLOG(PR_LOG_DEBUG, "Destroying ICE ctx '" << name_ <<"'");
+  nr_ice_peer_ctx_destroy(&peer_);
+  nr_ice_ctx_destroy(&ctx_);
+  delete ice_handler_vtbl_;
+  delete ice_handler_;
+}
+
+RefPtr<NrIceMediaStream>
+NrIceCtx::CreateStream(const std::string& name, int components) {
+  RefPtr<NrIceMediaStream> stream =
+    NrIceMediaStream::Create(this, name, components);
+
+  streams_.push_back(stream);
+
+  return stream;
+}
+
+
+nsresult NrIceCtx::StartGathering() {
+  this->AddRef();
+  int r = nr_ice_initialize(ctx_, &NrIceCtx::initialized_cb,
+                            this);
+
+  if (r && r != R_WOULDBLOCK) {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't gather ICE candidates for '"
+           << name_ << "'");
+      this->Release();
+      return NS_ERROR_FAILURE;
+  }
+
+  SetState(ICE_CTX_GATHERING);
+
+  return NS_OK;
+}
+
+void NrIceCtx::EmitAllCandidates() {
+  MOZ_MTLOG(PR_LOG_NOTICE, "Gathered all ICE candidates for '"
+       << name_ << "'");
+
+  for(size_t i=0; i<streams_.size(); ++i) {
+    streams_[i]->EmitAllCandidates();
+  }
+
+  SignalGatheringCompleted(this);
+}
+
+RefPtr<NrIceMediaStream> NrIceCtx::FindStream(
+    nr_ice_media_stream *stream) {
+  for (size_t i=0; i<streams_.size(); ++i) {
+    if (streams_[i]->stream() == stream) {
+      return streams_[i];
+    }
+  }
+
+  return nullptr;
+}
+
+std::vector<std::string> NrIceCtx::GetGlobalAttributes() {
+  char **attrs = 0;
+  int attrct;
+  int r;
+  std::vector<std::string> ret;
+
+  r = nr_ice_get_global_attributes(ctx_, &attrs, &attrct);
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't get ufrag and password for '"
+         << name_ << "'");
+    return ret;
+  }
+
+  for (int i=0; i<attrct; i++) {
+    ret.push_back(std::string(attrs[i]));
+    RFREE(attrs[i]);
+  }
+  RFREE(attrs);
+
+  return ret;
+}
+
+nsresult NrIceCtx::ParseGlobalAttributes(std::vector<std::string> attrs) {
+  std::vector<char *> attrs_in;
+
+  for (size_t i=0; i<attrs.size(); ++i) {
+    attrs_in.push_back(const_cast<char *>(attrs[i].c_str()));
+  }
+
+  int r = nr_ice_peer_ctx_parse_global_attributes(peer_,
+                                                  attrs_in.size() ?
+                                                  &attrs_in[0] : nullptr,
+                                                  attrs_in.size());
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't parse global attributes for "
+         << name_ << "'");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult NrIceCtx::StartChecks() {
+  int r;
+
+  r=nr_ice_peer_ctx_pair_candidates(peer_);
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't pair candidates on "
+         << name_ << "'");
+    return NS_ERROR_FAILURE;
+  }
+
+  r = nr_ice_peer_ctx_start_checks2(peer_,1);
+  if (r) {
+    if (r == R_NOT_FOUND) {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't start peer checks on "
+           << name_ << "' assuming trickle ICE");
+    } else {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't start peer checks on "
+           << name_ << "'");
+      return NS_ERROR_FAILURE;
+    }
+  } else {
+    SetState(ICE_CTX_CHECKING);
+  }
+
+  return NS_OK;
+}
+
+
+void NrIceCtx::initialized_cb(NR_SOCKET s, int h, void *arg) {
+  NrIceCtx *ctx = static_cast<NrIceCtx *>(arg);
+
+  ctx->SetState(ICE_CTX_GATHERED);
+
+  // Report that we are done gathering
+  ctx->EmitAllCandidates();
+
+  ctx->Release();
+}
+
+nsresult NrIceCtx::Finalize() {
+  int r = nr_ice_ctx_finalize(ctx_, peer_);
+
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't finalize "
+         << name_ << "'");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+void NrIceCtx::SetState(State state) {
+  MOZ_MTLOG(PR_LOG_DEBUG, "NrIceCtx(" << name_ << "): state " <<
+       state_ << "->" << state);
+  state_ = state;
+}
+}  // close namespace
+
+
+extern "C" {
+int nr_bin2hex(UCHAR *in,int len,UCHAR *out);
+}
+
+// Reimplement nr_ice_compute_codeword to avoid copyright issues
+void nr_ice_compute_codeword(char *buf, int len,char *codeword) {
+    UINT4 c;
+    UCHAR cc[2];
+
+    r_crc32(buf,len,&c);
+    c %= 2048;
+
+    cc[0] = (c >> 8) & 0xff;
+    cc[1] = c & 0xff;
+
+    nr_bin2hex(cc, 2, reinterpret_cast<UCHAR *>(codeword));
+
+    return;
+}
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/nricectx.h
@@ -0,0 +1,181 @@
+/* -*- 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
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* 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
+
+// This is a wrapper around the nICEr ICE stack
+#ifndef nricectx_h__
+#define nricectx_h__
+
+#include <vector>
+
+#include "sigslot.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/Scoped.h"
+#include "nsAutoPtr.h"
+#include "nsIEventTarget.h"
+#include "nsITimer.h"
+
+#include "m_cpp_utils.h"
+
+namespace mozilla {
+
+typedef void* NR_SOCKET;
+typedef struct nr_ice_ctx_ nr_ice_ctx;
+typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx;
+typedef struct nr_ice_media_stream_ nr_ice_media_stream;
+typedef struct nr_ice_handler_ nr_ice_handler;
+typedef struct nr_ice_handler_vtbl_ nr_ice_handler_vtbl;
+typedef struct nr_ice_cand_pair_ nr_ice_cand_pair;
+
+class NrIceMediaStream;
+
+class NrIceCtx {
+ public:
+  enum State { ICE_CTX_INIT,
+               ICE_CTX_GATHERING,
+               ICE_CTX_GATHERED,
+               ICE_CTX_CHECKING,
+               ICE_CTX_OPEN,
+               ICE_CTX_FAILED
+  };
+
+  static RefPtr<NrIceCtx> Create(const std::string& name,
+                                          bool offerer,
+                                          bool set_interface_priorities = true);
+  virtual ~NrIceCtx();
+
+  nr_ice_ctx *ctx() { return ctx_; }
+  nr_ice_peer_ctx *peer() { return peer_; }
+
+  // Create a media stream
+  RefPtr<NrIceMediaStream> CreateStream(const std::string& name,
+                                                 int components);
+
+  // The name of the ctx
+  const std::string& name() const { return name_; }
+
+  // Current state
+  State state() const { return state_; }
+
+  // Get the global attributes
+  std::vector<std::string> GetGlobalAttributes();
+
+  // Set the other side's global attributes
+  nsresult ParseGlobalAttributes(std::vector<std::string> attrs);
+
+  // Start ICE gathering
+  nsresult StartGathering();
+
+  // Start checking
+  nsresult StartChecks();
+
+  // Finalize the ICE negotiation. I.e., there will be no
+  // more forking.
+  nsresult Finalize();
+
+  // Signals to indicate events. API users can (and should)
+  // register for these.
+  sigslot::signal1<NrIceCtx *> SignalGatheringCompleted;  // Done gathering
+  sigslot::signal1<NrIceCtx *> SignalCompleted;  // Done handshaking
+
+  // The thread to direct method calls to
+  nsCOMPtr<nsIEventTarget> thread() { return sts_target_; }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceCtx);
+
+ private:
+  NrIceCtx(const std::string& name, bool offerer)
+      : state_(ICE_CTX_INIT),
+      name_(name),
+      offerer_(offerer),
+      streams_(),
+      ctx_(nullptr),
+      peer_(nullptr),
+      ice_handler_vtbl_(nullptr),
+      ice_handler_(nullptr) {}
+
+  DISALLOW_COPY_ASSIGN(NrIceCtx);
+
+  // Callbacks for nICEr
+  static void initialized_cb(NR_SOCKET s, int h, void *arg);  // ICE initialized
+
+  // Handler implementation
+  static int select_pair(void *obj,nr_ice_media_stream *stream,
+                         int component_id, nr_ice_cand_pair **potentials,
+                         int potential_ct);
+  static int stream_ready(void *obj, nr_ice_media_stream *stream);
+  static int stream_failed(void *obj, nr_ice_media_stream *stream);
+  static int ice_completed(void *obj, nr_ice_peer_ctx *pctx);
+  static int msg_recvd(void *obj, nr_ice_peer_ctx *pctx,
+                       nr_ice_media_stream *stream, int component_id,
+                       unsigned char *msg, int len);
+
+  // Iterate through all media streams and emit the candidates
+  // Note that we don't do trickle ICE yet
+  void EmitAllCandidates();
+
+  // Find a media stream by stream ptr. Gross
+  RefPtr<NrIceMediaStream> FindStream(nr_ice_media_stream *stream);
+
+  // Set the state
+  void SetState(State state);
+
+  State state_;
+  const std::string name_;
+  bool offerer_;
+  std::vector<RefPtr<NrIceMediaStream> > streams_;
+  nr_ice_ctx *ctx_;
+  nr_ice_peer_ctx *peer_;
+  nr_ice_handler_vtbl* ice_handler_vtbl_;  // Must be pointer
+  nr_ice_handler* ice_handler_;  // Must be pointer
+  nsCOMPtr<nsIEventTarget> sts_target_; // The thread to run on
+};
+
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/nricemediastream.cpp
@@ -0,0 +1,264 @@
+/* -*- 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
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <string>
+#include <vector>
+
+#include "nsError.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "registry.h"
+#include "async_timer.h"
+#include "ice_util.h"
+#include "transport_addr.h"
+#include "nr_crypto.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "stun_client_ctx.h"
+#include "stun_server_ctx.h"
+#include "ice_ctx.h"
+#include "ice_candidate.h"
+#include "ice_handler.h"
+}
+
+// Local includes
+#include "logging.h"
+#include "nricectx.h"
+#include "nricemediastream.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport");
+
+// NrIceMediaStream
+RefPtr<NrIceMediaStream>
+NrIceMediaStream::Create(NrIceCtx *ctx,
+                         const std::string& name,
+                         int components) {
+  RefPtr<NrIceMediaStream> stream =
+    new NrIceMediaStream(ctx, name, components);
+
+  int r = nr_ice_add_media_stream(ctx->ctx(),
+                                  const_cast<char *>(name.c_str()),
+                                  components, &stream->stream_);
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't create ICE media stream for '"
+         << name << "'");
+    return nullptr;
+  }
+
+  return stream;
+}
+
+NrIceMediaStream::~NrIceMediaStream() {
+  // We do not need to destroy anything. All major resources
+  // are attached to the ice ctx.
+}
+
+nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>&
+                                           attributes) {
+  if (!stream_)
+    return NS_ERROR_FAILURE;
+
+  std::vector<char *> attributes_in;
+
+  for (size_t i=0; i<attributes.size(); ++i) {
+    attributes_in.push_back(const_cast<char *>(attributes[i].c_str()));
+  }
+  
+  // Still need to call nr_ice_ctx_parse_stream_attributes.
+  int r = nr_ice_peer_ctx_parse_stream_attributes(ctx_->peer(),
+                                                  stream_,
+                                                  attributes_in.size() ?
+                                                  &attributes_in[0] : nullptr,
+                                                  attributes_in.size());
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't parse attributes for stream "
+         << name_ << "'");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+// Parse trickle ICE candidate
+nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate) {
+  int r;
+
+  MOZ_MTLOG(PR_LOG_DEBUG, "NrIceCtx(" << ctx_->name() << "): parsing trickle candidate " << candidate);
+
+  r = nr_ice_peer_ctx_parse_trickle_candidate(ctx_->peer(),
+                                              stream_,
+                                              const_cast<char *>(
+                                                  candidate.c_str()));
+  if (r) {
+    if (r == R_ALREADY) {
+      MOZ_MTLOG(PR_LOG_ERROR, "Trickle candidates are redundant for stream '"
+         << name_ << "' because it is completed");
+
+    } else {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't parse trickle candidate for stream '"
+         << name_ << "'");
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  if (ctx_->state() == NrIceCtx::ICE_CTX_GATHERED) {
+    // Try to start checks if they are not already started
+    return ctx_->StartChecks();
+  }
+
+  return NS_OK;
+}
+
+
+void NrIceMediaStream::EmitAllCandidates() {
+  char **attrs = 0;
+  int attrct;
+  int r;
+  r = nr_ice_media_stream_get_attributes(stream_,
+                                         &attrs, &attrct);
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't get ICE candidates for '"
+         << name_ << "'");
+    return;
+  }
+
+  for (int i=0; i<attrct; i++) {
+    SignalCandidate(this, attrs[i]);
+    RFREE(attrs[i]);
+  }
+
+  RFREE(attrs);
+}
+
+nsresult NrIceMediaStream::GetDefaultCandidate(int component,
+                                               std::string *addrp,
+                                               int *portp) {
+  nr_ice_candidate *cand;
+  int r;
+
+  r = nr_ice_media_stream_get_default_candidate(stream_,
+                                                component, &cand);
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't get default ICE candidate for '"
+         << name_ << "'");
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  char addr[64];  // Enough for IPv6 with colons.
+  r = nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr));
+  if (r)
+    return NS_ERROR_FAILURE;
+
+  int port;
+  r=nr_transport_addr_get_port(&cand->addr,&port);
+  if (r)
+    return NS_ERROR_FAILURE;
+
+  *addrp = addr;
+  *portp = port;
+
+  return NS_OK;
+}
+
+std::vector<std::string> NrIceMediaStream::GetCandidates() const {
+  char **attrs = 0;
+  int attrct;
+  int r;
+  std::vector<std::string> ret;
+
+  r = nr_ice_media_stream_get_attributes(stream_,
+                                         &attrs, &attrct);
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't get ICE candidates for '"
+         << name_ << "'");
+    return ret;
+  }
+
+  for (int i=0; i<attrct; i++) {
+    ret.push_back(attrs[i]);
+    RFREE(attrs[i]);
+  }
+
+  RFREE(attrs);
+
+  return ret;
+}
+
+nsresult NrIceMediaStream::SendPacket(int component_id,
+                                      const unsigned char *data,
+                                      size_t len) {
+  if (!stream_)
+    return NS_ERROR_FAILURE;
+
+  int r = nr_ice_media_stream_send(ctx_->peer(), stream_,
+                                   component_id,
+                                   const_cast<unsigned char *>(data), len);
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't send media on '" << name_ << "'");
+    if (r == R_WOULDBLOCK) {
+      return NS_BASE_STREAM_WOULD_BLOCK;
+    }
+
+    return NS_BASE_STREAM_OSERROR;
+  }
+
+  return NS_OK;
+}
+
+
+void NrIceMediaStream::Ready() {
+  MOZ_MTLOG(PR_LOG_DEBUG, "Marking stream ready '" << name_ << "'");
+  state_ = ICE_OPEN;
+  SignalReady(this);
+}
+
+void NrIceMediaStream::Close() {
+  MOZ_MTLOG(PR_LOG_DEBUG, "Marking stream closed '" << name_ << "'");
+  state_ = ICE_CLOSED;
+  stream_ = nullptr;
+}
+}  // close namespace
new file mode 100644
--- /dev/null
+++ b/media/mtransport/nricemediastream.h
@@ -0,0 +1,119 @@
+/* -*- 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/. */
+/**
+   nricemediastream.h
+
+   Copyright (C) 2012, RTFM, Inc.
+   All Rights Reserved.
+
+   ekr@rtfm.com  Mon Jul 30 09:48:47 2012
+*/
+/* 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
+
+// This is a wrapper around the nICEr ICE stack
+#ifndef nricemediastream_h__
+#define nricemediastream_h__
+
+#include <vector>
+
+#include "sigslot.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/Scoped.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsITimer.h"
+
+#include "m_cpp_utils.h"
+
+
+namespace mozilla {
+
+typedef struct nr_ice_media_stream_ nr_ice_media_stream;
+
+class NrIceCtx;
+
+class NrIceMediaStream {
+ public:
+  static RefPtr<NrIceMediaStream> Create(NrIceCtx *ctx,
+                                         const std::string& name,
+                                         int components);
+  ~NrIceMediaStream();
+
+  enum State { ICE_CONNECTING, ICE_OPEN, ICE_CLOSED};
+
+  State state() const { return state_; }
+
+  // The name of the stream
+  const std::string& name() const { return name_; }
+
+  // Get all the candidates
+  std::vector<std::string> GetCandidates() const;
+
+  // Get the default candidate as host and port
+  nsresult GetDefaultCandidate(int component, std::string *host, int *port);
+
+  // Parse remote attributes
+  nsresult ParseAttributes(std::vector<std::string>& candidates);
+
+  // Parse trickle ICE candidate
+  nsresult ParseTrickleCandidate(const std::string& candidate);
+
+  // The underlying nICEr stream
+  nr_ice_media_stream *stream() { return stream_; }
+  // Signals to indicate events. API users can (and should)
+  // register for these.
+
+  // Send a packet
+  nsresult SendPacket(int component_id, const unsigned char *data, size_t len);
+
+  // Set your state to ready. Called by the NrIceCtx;
+  void Ready();
+
+  // Close the stream. Called by the NrIceCtx.
+  // Different from the destructor because other people
+  // might be holding RefPtrs but we want those writes to fail once
+  // the context has been destroyed.
+  void Close();
+
+  sigslot::signal2<NrIceMediaStream *, const std::string& >
+  SignalCandidate;  // A new ICE candidate:
+  sigslot::signal1<NrIceMediaStream *> SignalReady;  // Candidate pair ready.
+  sigslot::signal1<NrIceMediaStream *> SignalFailed;  // Candidate pair failed.
+  sigslot::signal4<NrIceMediaStream *, int, const unsigned char *, int>
+  SignalPacketReceived;  // Incoming packet
+
+  // Emit all the ICE candidates. Note that this doesn't
+  // work for trickle ICE yet--called internally
+  void EmitAllCandidates();
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceMediaStream);
+
+ private:
+  NrIceMediaStream(NrIceCtx *ctx,  const std::string& name,
+                   int components) :
+      state_(ICE_CONNECTING),
+      ctx_(ctx),
+      name_(name),
+      components_(components),
+      stream_(nullptr)  {}
+
+  DISALLOW_COPY_ASSIGN(NrIceMediaStream);
+
+  State state_;
+  NrIceCtx *ctx_;
+  const std::string name_;
+  const int components_;
+  nr_ice_media_stream *stream_;
+};
+
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/objs.mk
@@ -0,0 +1,66 @@
+# -*- Mode: makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- #
+# 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/.
+DEFINES += -DHAVE_STRDUP -DNR_SOCKET_IS_VOID_PTR
+
+LOCAL_INCLUDES += \
+ -I. \
+ -I$(topsrcdir)/media/webrtc/trunk/third_party/libjingle/source/ \
+ -I$(topsrcdir)/media/mtransport/ \
+ -I$(topsrcdir)/media/mtransport/third_party/ \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/crypto \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/ice \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/net \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/stun \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/util \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/share \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/util/libekr \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/log \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/registry \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/stats \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/plugin \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/event \
+ $(NULL)
+
+ifeq ($(OS_ARCH), Darwin)
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/darwin/include \
+  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/generic/include \
+  $(NULL)
+DEFINES += -DDARWIN
+endif
+
+ifeq ($(OS_ARCH), Linux)
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/linux/include \
+  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/generic/include \
+  $(NULL)
+DEFINES += -DLINUX
+endif
+
+ifeq ($(OS_ARCH), WINNT)
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/win32/include \
+  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/generic/include \
+  $(NULL)
+DEFINES += -DWIN
+endif
+
+MTRANSPORT_LCPPSRCS = \
+  dtlsidentity.cpp \
+  nricectx.cpp \
+  nricemediastream.cpp \
+  nr_socket_prsock.cpp \
+  nr_timer.cpp \
+  transportflow.cpp \
+  transportlayer.cpp \
+  transportlayerice.cpp \
+  transportlayerdtls.cpp \
+  transportlayerlog.cpp \
+  transportlayerloopback.cpp \
+  transportlayerprsock.cpp \
+  $(NULL)
+
+MTRANSPORT_CPPSRCS = $(addprefix $(topsrcdir)/media/mtransport/, $(MTRANSPORT_LCPPSRCS))
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/runnable_utils.h
@@ -0,0 +1,31 @@
+/* -*- 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 runnable_utils_h__
+#define runnable_utils_h__
+
+#include "nsThreadUtils.h"
+
+// Abstract base class for all of our templates
+namespace mozilla {
+
+class runnable_args_base : public nsRunnable {
+ public:
+
+  NS_IMETHOD Run() = 0;
+};
+
+
+#include "runnable_utils_generated.h"
+
+// Temporary hack. Really we want to have a template which will do this
+#define RUN_ON_THREAD(t, r, h) ((t && (t != nsRefPtr<nsIThread>(do_GetCurrentThread()))) ? t->Dispatch(r, h) : r->Run())
+}
+
+#endif
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/runnable_utils.py
@@ -0,0 +1,111 @@
+MAX_ARGS = 10
+
+def gen_args_type(args):
+    ret = ["C o", "M m"]
+    for arg in range(0, args):
+        ret.append("A%d a%d"%(arg, arg))
+    return ", ".join(ret)
+
+def gen_args(args):
+    ret = ["o", "m"]
+    for arg in range(0, args):
+        ret.append("a%d"%(arg))
+    return ", ".join(ret)
+
+def gen_args_(args):
+    ret = []
+    for arg in range(0, args):
+        ret.append("a%d_"%(arg))
+    return ", ".join(ret)
+
+def gen_init(args, r = False):
+    ret = ["o_(o)", "m_(m)"]
+    if r:
+        ret.append("r_(r)")
+
+    for arg in range(0, args):
+        ret.append("a%d_(a%d)"%(arg, arg))
+    return ", ".join(ret)
+
+def gen_typenames(args):
+    ret = ["typename C", "typename M"]
+    for arg in range(0, args):
+        ret.append("typename A%d"%(arg))
+    return ", ".join(ret)
+
+def gen_types(args):
+    ret = ["C", "M"]
+    for arg in range(0, args):
+        ret.append("A%d"%(arg))
+    return ", ".join(ret)
+
+
+def generate_class_template(args, ret = False):
+    print "// %d arguments --"%args
+    if not ret:
+        print "template<"+ gen_typenames(args) + "> class runnable_args%d : public runnable_args_base {"%args
+    else:
+        print "template<"+ gen_typenames(args) + ", typename R> class runnable_args%d_ret : public runnable_args_base {"%args
+
+    print " public:"
+
+    if not ret:
+        print "  runnable_args%d("%args + gen_args_type(args) + ") :"
+        print "    " + gen_init(args) + "  {}"
+    else:
+        print "  runnable_args%d_ret("%args + gen_args_type(args) + ", R *r) :"
+        print "    " + gen_init(args, True) + "  {}"
+    print
+    print "  NS_IMETHOD Run() {"
+    if not ret:
+        print "    ((*o_).*m_)(" + gen_args_(args) + ");"
+    else:
+        print "    *r_ = ((*o_).*m_)(" + gen_args_(args) + ");"
+    print "    return NS_OK;"
+    print "  }"
+    print
+    print " private:"
+    print "  C o_;"
+    print "  M m_;"
+    if ret:
+        print "  R* r_;"
+    for arg in range(0, args):
+        print "  A%d a%d_;"%(arg, arg)
+    print "};"
+    print
+    print
+    print
+
+def generate_function_template(args):
+    print "// %d arguments --"%args
+    print "template<" + gen_typenames(args) + ">"
+    print "runnable_args%d<"%args + gen_types(args) + ">* WrapRunnable(" + gen_args_type(args) + ") {"
+    print "  return new runnable_args%d<"%args + gen_types(args) + ">"
+    print "    (" + gen_args(args) + ");"
+    print "}"
+    print
+
+def generate_function_template_ret(args):
+    print "// %d arguments --"%args
+    print "template<" + gen_typenames(args) + ", typename R>"
+    print "runnable_args%d_ret<"%args + gen_types(args) + ", R>* WrapRunnableRet(" + gen_args_type(args) + ", R* r) {"
+    print "  return new runnable_args%d_ret<"%args + gen_types(args) + ", R>"
+    print "    (" + gen_args(args) + ", r);"
+    print "}"
+    print
+
+for num_args in range (0, MAX_ARGS):
+    generate_class_template(num_args)
+    generate_class_template(num_args, True)
+
+print
+print
+print
+
+for num_args in range(0, MAX_ARGS):
+    generate_function_template(num_args)
+    generate_function_template_ret(num_args)
+
+
+
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/runnable_utils_generated.h
@@ -0,0 +1,608 @@
+/* -*- 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/. */
+// 0 arguments --
+template<typename C, typename M> class runnable_args0 : public runnable_args_base {
+ public:
+  runnable_args0(C o, M m) :
+    o_(o), m_(m)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)();
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+};
+
+
+
+// 0 arguments --
+template<typename C, typename M, typename R> class runnable_args0_ret : public runnable_args_base {
+ public:
+  runnable_args0_ret(C o, M m, R *r) :
+    o_(o), m_(m), r_(r)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)();
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+};
+
+
+
+// 1 arguments --
+template<typename C, typename M, typename A0> class runnable_args1 : public runnable_args_base {
+ public:
+  runnable_args1(C o, M m, A0 a0) :
+    o_(o), m_(m), a0_(a0)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)(a0_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+};
+
+
+
+// 1 arguments --
+template<typename C, typename M, typename A0, typename R> class runnable_args1_ret : public runnable_args_base {
+ public:
+  runnable_args1_ret(C o, M m, A0 a0, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)(a0_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+};
+
+
+
+// 2 arguments --
+template<typename C, typename M, typename A0, typename A1> class runnable_args2 : public runnable_args_base {
+ public:
+  runnable_args2(C o, M m, A0 a0, A1 a1) :
+    o_(o), m_(m), a0_(a0), a1_(a1)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)(a0_, a1_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+};
+
+
+
+// 2 arguments --
+template<typename C, typename M, typename A0, typename A1, typename R> class runnable_args2_ret : public runnable_args_base {
+ public:
+  runnable_args2_ret(C o, M m, A0 a0, A1 a1, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+};
+
+
+
+// 3 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2> class runnable_args3 : public runnable_args_base {
+ public:
+  runnable_args3(C o, M m, A0 a0, A1 a1, A2 a2) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)(a0_, a1_, a2_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+};
+
+
+
+// 3 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename R> class runnable_args3_ret : public runnable_args_base {
+ public:
+  runnable_args3_ret(C o, M m, A0 a0, A1 a1, A2 a2, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+};
+
+
+
+// 4 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3> class runnable_args4 : public runnable_args_base {
+ public:
+  runnable_args4(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+};
+
+
+
+// 4 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename R> class runnable_args4_ret : public runnable_args_base {
+ public:
+  runnable_args4_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+};
+
+
+
+// 5 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4> class runnable_args5 : public runnable_args_base {
+ public:
+  runnable_args5(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+};
+
+
+
+// 5 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R> class runnable_args5_ret : public runnable_args_base {
+ public:
+  runnable_args5_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+};
+
+
+
+// 6 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> class runnable_args6 : public runnable_args_base {
+ public:
+  runnable_args6(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+};
+
+
+
+// 6 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R> class runnable_args6_ret : public runnable_args_base {
+ public:
+  runnable_args6_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+};
+
+
+
+// 7 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> class runnable_args7 : public runnable_args_base {
+ public:
+  runnable_args7(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+};
+
+
+
+// 7 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R> class runnable_args7_ret : public runnable_args_base {
+ public:
+  runnable_args7_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+};
+
+
+
+// 8 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> class runnable_args8 : public runnable_args_base {
+ public:
+  runnable_args8(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+};
+
+
+
+// 8 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R> class runnable_args8_ret : public runnable_args_base {
+ public:
+  runnable_args8_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+};
+
+
+
+// 9 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> class runnable_args9 : public runnable_args_base {
+ public:
+  runnable_args9(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
+
+  NS_IMETHOD Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+};
+
+
+
+// 9 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R> class runnable_args9_ret : public runnable_args_base {
+ public:
+  runnable_args9_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
+
+  NS_IMETHOD Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
+    return NS_OK;
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+};
+
+
+
+
+
+
+// 0 arguments --
+template<typename C, typename M>
+runnable_args0<C, M>* WrapRunnable(C o, M m) {
+  return new runnable_args0<C, M>
+    (o, m);
+}
+
+// 0 arguments --
+template<typename C, typename M, typename R>
+runnable_args0_ret<C, M, R>* WrapRunnableRet(C o, M m, R* r) {
+  return new runnable_args0_ret<C, M, R>
+    (o, m, r);
+}
+
+// 1 arguments --
+template<typename C, typename M, typename A0>
+runnable_args1<C, M, A0>* WrapRunnable(C o, M m, A0 a0) {
+  return new runnable_args1<C, M, A0>
+    (o, m, a0);
+}
+
+// 1 arguments --
+template<typename C, typename M, typename A0, typename R>
+runnable_args1_ret<C, M, A0, R>* WrapRunnableRet(C o, M m, A0 a0, R* r) {
+  return new runnable_args1_ret<C, M, A0, R>
+    (o, m, a0, r);
+}
+
+// 2 arguments --
+template<typename C, typename M, typename A0, typename A1>
+runnable_args2<C, M, A0, A1>* WrapRunnable(C o, M m, A0 a0, A1 a1) {
+  return new runnable_args2<C, M, A0, A1>
+    (o, m, a0, a1);
+}
+
+// 2 arguments --
+template<typename C, typename M, typename A0, typename A1, typename R>
+runnable_args2_ret<C, M, A0, A1, R>* WrapRunnableRet(C o, M m, A0 a0, A1 a1, R* r) {
+  return new runnable_args2_ret<C, M, A0, A1, R>
+    (o, m, a0, a1, r);
+}
+
+// 3 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2>
+runnable_args3<C, M, A0, A1, A2>* WrapRunnable(C o, M m, A0 a0, A1 a1, A2 a2) {
+  return new runnable_args3<C, M, A0, A1, A2>
+    (o, m, a0, a1, a2);
+}
+
+// 3 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename R>
+runnable_args3_ret<C, M, A0, A1, A2, R>* WrapRunnableRet(C o, M m, A0 a0, A1 a1, A2 a2, R* r) {
+  return new runnable_args3_ret<C, M, A0, A1, A2, R>
+    (o, m, a0, a1, a2, r);
+}
+
+// 4 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3>
+runnable_args4<C, M, A0, A1, A2, A3>* WrapRunnable(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3) {
+  return new runnable_args4<C, M, A0, A1, A2, A3>
+    (o, m, a0, a1, a2, a3);
+}
+
+// 4 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename R>
+runnable_args4_ret<C, M, A0, A1, A2, A3, R>* WrapRunnableRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, R* r) {
+  return new runnable_args4_ret<C, M, A0, A1, A2, A3, R>
+    (o, m, a0, a1, a2, a3, r);
+}
+
+// 5 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4>
+runnable_args5<C, M, A0, A1, A2, A3, A4>* WrapRunnable(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) {
+  return new runnable_args5<C, M, A0, A1, A2, A3, A4>
+    (o, m, a0, a1, a2, a3, a4);
+}
+
+// 5 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R>
+runnable_args5_ret<C, M, A0, A1, A2, A3, A4, R>* WrapRunnableRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R* r) {
+  return new runnable_args5_ret<C, M, A0, A1, A2, A3, A4, R>
+    (o, m, a0, a1, a2, a3, a4, r);
+}
+
+// 6 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
+runnable_args6<C, M, A0, A1, A2, A3, A4, A5>* WrapRunnable(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+  return new runnable_args6<C, M, A0, A1, A2, A3, A4, A5>
+    (o, m, a0, a1, a2, a3, a4, a5);
+}
+
+// 6 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R>
+runnable_args6_ret<C, M, A0, A1, A2, A3, A4, A5, R>* WrapRunnableRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R* r) {
+  return new runnable_args6_ret<C, M, A0, A1, A2, A3, A4, A5, R>
+    (o, m, a0, a1, a2, a3, a4, a5, r);
+}
+
+// 7 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
+runnable_args7<C, M, A0, A1, A2, A3, A4, A5, A6>* WrapRunnable(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
+  return new runnable_args7<C, M, A0, A1, A2, A3, A4, A5, A6>
+    (o, m, a0, a1, a2, a3, a4, a5, a6);
+}
+
+// 7 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R>
+runnable_args7_ret<C, M, A0, A1, A2, A3, A4, A5, A6, R>* WrapRunnableRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R* r) {
+  return new runnable_args7_ret<C, M, A0, A1, A2, A3, A4, A5, A6, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, r);
+}
+
+// 8 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
+runnable_args8<C, M, A0, A1, A2, A3, A4, A5, A6, A7>* WrapRunnable(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
+  return new runnable_args8<C, M, A0, A1, A2, A3, A4, A5, A6, A7>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7);
+}
+
+// 8 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R>
+runnable_args8_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, R>* WrapRunnableRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R* r) {
+  return new runnable_args8_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, r);
+}
+
+// 9 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
+runnable_args9<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8>* WrapRunnable(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) {
+  return new runnable_args9<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8);
+}
+
+// 9 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R>
+runnable_args9_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>* WrapRunnableRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R* r) {
+  return new runnable_args9_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, r);
+}
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/sigslot.h
@@ -0,0 +1,2850 @@
+// sigslot.h: Signal/Slot classes
+// 
+// Written by Sarah Thompson (sarah@telergy.com) 2002.
+//
+// License: Public domain. You are free to use this code however you like, with the proviso that
+//          the author takes on no responsibility or liability for any use.
+//
+// QUICK DOCUMENTATION 
+//		
+//				(see also the full documentation at http://sigslot.sourceforge.net/)
+//
+//		#define switches
+//			SIGSLOT_PURE_ISO			- Define this to force ISO C++ compliance. This also disables
+//										  all of the thread safety support on platforms where it is 
+//										  available.
+//
+//			SIGSLOT_USE_POSIX_THREADS	- Force use of Posix threads when using a C++ compiler other than
+//										  gcc on a platform that supports Posix threads. (When using gcc,
+//										  this is the default - use SIGSLOT_PURE_ISO to disable this if 
+//										  necessary)
+//
+//			SIGSLOT_DEFAULT_MT_POLICY	- Where thread support is enabled, this defaults to multi_threaded_global.
+//										  Otherwise, the default is single_threaded. #define this yourself to
+//										  override the default. In pure ISO mode, anything other than
+//										  single_threaded will cause a compiler error.
+//
+//		PLATFORM NOTES
+//
+//			Win32						- On Win32, the WIN32 symbol must be #defined. Most mainstream
+//										  compilers do this by default, but you may need to define it
+//										  yourself if your build environment is less standard. This causes
+//										  the Win32 thread support to be compiled in and used automatically.
+//
+//			Unix/Linux/BSD, etc.		- If you're using gcc, it is assumed that you have Posix threads
+//										  available, so they are used automatically. You can override this
+//										  (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using
+//										  something other than gcc but still want to use Posix threads, you
+//										  need to #define SIGSLOT_USE_POSIX_THREADS.
+//
+//			ISO C++						- If none of the supported platforms are detected, or if
+//										  SIGSLOT_PURE_ISO is defined, all multithreading support is turned off,
+//										  along with any code that might cause a pure ISO C++ environment to
+//										  complain. Before you ask, gcc -ansi -pedantic won't compile this 
+//										  library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of
+//										  errors that aren't really there. If you feel like investigating this,
+//										  please contact the author.
+//
+//		
+//		THREADING MODES
+//
+//			single_threaded				- Your program is assumed to be single threaded from the point of view
+//										  of signal/slot usage (i.e. all objects using signals and slots are
+//										  created and destroyed from a single thread). Behaviour if objects are
+//										  destroyed concurrently is undefined (i.e. you'll get the occasional
+//										  segmentation fault/memory exception).
+//
+//			multi_threaded_global		- Your program is assumed to be multi threaded. Objects using signals and
+//										  slots can be safely created and destroyed from any thread, even when
+//										  connections exist. In multi_threaded_global mode, this is achieved by a
+//										  single global mutex (actually a critical section on Windows because they
+//										  are faster). This option uses less OS resources, but results in more
+//										  opportunities for contention, possibly resulting in more context switches
+//										  than are strictly necessary.
+//
+//			multi_threaded_local		- Behaviour in this mode is essentially the same as multi_threaded_global,
+//										  except that each signal, and each object that inherits has_slots, all 
+//										  have their own mutex/critical section. In practice, this means that
+//										  mutex collisions (and hence context switches) only happen if they are
+//										  absolutely essential. However, on some platforms, creating a lot of 
+//										  mutexes can slow down the whole OS, so use this option with care.
+//
+//		USING THE LIBRARY
+//
+//			See the full documentation at http://sigslot.sourceforge.net/
+//
+//
+// Libjingle specific:
+// This file has been modified such that has_slots and signalx do not have to be
+// using the same threading requirements. E.g. it is possible to connect a
+// has_slots<single_threaded> and signal0<multi_threaded_local> or
+// has_slots<multi_threaded_local> and signal0<single_threaded>.
+// If has_slots is single threaded the user must ensure that it is not trying
+// to connect or disconnect to signalx concurrently or data race may occur.
+// If signalx is single threaded the user must ensure that disconnect, connect
+// or signal is not happening concurrently or data race may occur.
+
+#ifndef TALK_BASE_SIGSLOT_H__
+#define TALK_BASE_SIGSLOT_H__
+
+#include <list>
+#include <set>
+#include <stdlib.h>
+
+// On our copy of sigslot.h, we set single threading as default.
+#define SIGSLOT_DEFAULT_MT_POLICY single_threaded
+
+#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS))
+#	define _SIGSLOT_SINGLE_THREADED
+#elif defined(WIN32)
+#	define _SIGSLOT_HAS_WIN32_THREADS
+#	if !defined(WIN32_LEAN_AND_MEAN)
+#		define WIN32_LEAN_AND_MEAN
+#	endif
+#	include "windows.h"
+#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS)
+#	define _SIGSLOT_HAS_POSIX_THREADS
+#	include <pthread.h>
+#else
+#	define _SIGSLOT_SINGLE_THREADED
+#endif
+
+#ifndef SIGSLOT_DEFAULT_MT_POLICY
+#	ifdef _SIGSLOT_SINGLE_THREADED
+#		define SIGSLOT_DEFAULT_MT_POLICY single_threaded
+#	else
+#		define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
+#	endif
+#endif
+
+// TODO: change this namespace to talk_base?
+namespace sigslot {
+
+	class single_threaded
+	{
+	public:
+		single_threaded()
+		{
+			;
+		}
+
+		virtual ~single_threaded()
+		{
+			;
+		}
+
+		virtual void lock()
+		{
+			;
+		}
+
+		virtual void unlock()
+		{
+			;
+		}
+	};
+
+#ifdef _SIGSLOT_HAS_WIN32_THREADS
+	// The multi threading policies only get compiled in if they are enabled.
+	class multi_threaded_global
+	{
+	public:
+		multi_threaded_global()
+		{
+			static bool isinitialised = false;
+
+			if(!isinitialised)
+			{
+				InitializeCriticalSection(get_critsec());
+				isinitialised = true;
+			}
+		}
+
+		multi_threaded_global(const multi_threaded_global&)
+		{
+			;
+		}
+
+		virtual ~multi_threaded_global()
+		{
+			;
+		}
+
+		virtual void lock()
+		{
+			EnterCriticalSection(get_critsec());
+		}
+
+		virtual void unlock()
+		{
+			LeaveCriticalSection(get_critsec());
+		}
+
+	private:
+		CRITICAL_SECTION* get_critsec()
+		{
+			static CRITICAL_SECTION g_critsec;
+			return &g_critsec;
+		}
+	};
+
+	class multi_threaded_local
+	{
+	public:
+		multi_threaded_local()
+		{
+			InitializeCriticalSection(&m_critsec);
+		}
+
+		multi_threaded_local(const multi_threaded_local&)
+		{
+			InitializeCriticalSection(&m_critsec);
+		}
+
+		virtual ~multi_threaded_local()
+		{
+			DeleteCriticalSection(&m_critsec);
+		}
+
+		virtual void lock()
+		{
+			EnterCriticalSection(&m_critsec);
+		}
+
+		virtual void unlock()
+		{
+			LeaveCriticalSection(&m_critsec);
+		}
+
+	private:
+		CRITICAL_SECTION m_critsec;
+	};
+#endif // _SIGSLOT_HAS_WIN32_THREADS
+
+#ifdef _SIGSLOT_HAS_POSIX_THREADS
+	// The multi threading policies only get compiled in if they are enabled.
+	class multi_threaded_global
+	{
+	public:
+		multi_threaded_global()
+		{
+			pthread_mutex_init(get_mutex(), NULL);
+		}
+
+		multi_threaded_global(const multi_threaded_global&)
+		{
+			;
+		}
+
+		virtual ~multi_threaded_global()
+		{
+			;
+		}
+
+		virtual void lock()
+		{
+			pthread_mutex_lock(get_mutex());
+		}
+
+		virtual void unlock()
+		{
+			pthread_mutex_unlock(get_mutex());
+		}
+
+	private:
+		pthread_mutex_t* get_mutex()
+		{
+			static pthread_mutex_t g_mutex;
+			return &g_mutex;
+		}
+	};
+
+	class multi_threaded_local
+	{
+	public:
+		multi_threaded_local()
+		{
+			pthread_mutex_init(&m_mutex, NULL);
+		}
+
+		multi_threaded_local(const multi_threaded_local&)
+		{
+			pthread_mutex_init(&m_mutex, NULL);
+		}
+
+		virtual ~multi_threaded_local()
+		{
+			pthread_mutex_destroy(&m_mutex);
+		}
+
+		virtual void lock()
+		{
+			pthread_mutex_lock(&m_mutex);
+		}
+
+		virtual void unlock()
+		{
+			pthread_mutex_unlock(&m_mutex);
+		}
+
+	private:
+		pthread_mutex_t m_mutex;
+	};
+#endif // _SIGSLOT_HAS_POSIX_THREADS
+
+	template<class mt_policy>
+	class lock_block
+	{
+	public:
+		mt_policy *m_mutex;
+
+		lock_block(mt_policy *mtx)
+			: m_mutex(mtx)
+		{
+			m_mutex->lock();
+		}
+
+		~lock_block()
+		{
+			m_mutex->unlock();
+		}
+	};
+
+	class has_slots_interface;
+
+	template<class mt_policy>
+	class _connection_base0
+	{
+	public:
+		virtual ~_connection_base0() {}
+		virtual has_slots_interface* getdest() const = 0;
+		virtual void emit() = 0;
+		virtual _connection_base0* clone() = 0;
+		virtual _connection_base0* duplicate(has_slots_interface* pnewdest) = 0;
+	};
+
+	template<class arg1_type, class mt_policy>
+	class _connection_base1
+	{
+	public:
+		virtual ~_connection_base1() {}
+		virtual has_slots_interface* getdest() const = 0;
+		virtual void emit(arg1_type) = 0;
+		virtual _connection_base1<arg1_type, mt_policy>* clone() = 0;
+		virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+	};
+
+	template<class arg1_type, class arg2_type, class mt_policy>
+	class _connection_base2
+	{
+	public:
+		virtual ~_connection_base2() {}
+		virtual has_slots_interface* getdest() const = 0;
+		virtual void emit(arg1_type, arg2_type) = 0;
+		virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone() = 0;
+		virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+	class _connection_base3
+	{
+	public:
+		virtual ~_connection_base3() {}
+		virtual has_slots_interface* getdest() const = 0;
+		virtual void emit(arg1_type, arg2_type, arg3_type) = 0;
+		virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone() = 0;
+		virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy>
+	class _connection_base4
+	{
+	public:
+		virtual ~_connection_base4() {}
+		virtual has_slots_interface* getdest() const = 0;
+		virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0;
+		virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone() = 0;
+		virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class mt_policy>
+	class _connection_base5
+	{
+	public:
+		virtual ~_connection_base5() {}
+		virtual has_slots_interface* getdest() const = 0;
+		virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type) = 0;
+		virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, mt_policy>* clone() = 0;
+		virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class arg6_type, class mt_policy>
+	class _connection_base6
+	{
+	public:
+		virtual ~_connection_base6() {}
+		virtual has_slots_interface* getdest() const = 0;
+		virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+			arg6_type) = 0;
+		virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, mt_policy>* clone() = 0;
+		virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+	class _connection_base7
+	{
+	public:
+		virtual ~_connection_base7() {}
+		virtual has_slots_interface* getdest() const = 0;
+		virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+			arg6_type, arg7_type) = 0;
+		virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, mt_policy>* clone() = 0;
+		virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy>
+	class _connection_base8
+	{
+	public:
+		virtual ~_connection_base8() {}
+		virtual has_slots_interface* getdest() const = 0;
+		virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+			arg6_type, arg7_type, arg8_type) = 0;
+		virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone() = 0;
+		virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+	};
+
+	class _signal_base_interface
+	{
+	public:
+		virtual void slot_disconnect(has_slots_interface* pslot) = 0;
+		virtual void slot_duplicate(const has_slots_interface* poldslot, has_slots_interface* pnewslot) = 0;
+	};
+
+	template<class mt_policy>
+	class _signal_base : public _signal_base_interface, public mt_policy
+	{
+	};
+
+	class has_slots_interface
+	{
+	public:
+		has_slots_interface()
+		{
+			;
+		}
+
+		virtual void signal_connect(_signal_base_interface* sender) = 0;
+
+		virtual void signal_disconnect(_signal_base_interface* sender) = 0;
+
+		virtual ~has_slots_interface()
+		{
+		}
+
+		virtual void disconnect_all() = 0;
+	};
+
+	template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class has_slots : public has_slots_interface, public mt_policy
+	{
+	private:
+		typedef std::set<_signal_base_interface*> sender_set;
+		typedef sender_set::const_iterator const_iterator;
+
+	public:
+		has_slots()
+		{
+			;
+		}
+
+		has_slots(const has_slots& hs)
+		{
+			lock_block<mt_policy> lock(this);
+			const_iterator it = hs.m_senders.begin();
+			const_iterator itEnd = hs.m_senders.end();
+
+			while(it != itEnd)
+			{
+				(*it)->slot_duplicate(&hs, this);
+				m_senders.insert(*it);
+				++it;
+			}
+		} 
+
+		void signal_connect(_signal_base_interface* sender)
+		{
+			lock_block<mt_policy> lock(this);
+			m_senders.insert(sender);
+		}
+
+		void signal_disconnect(_signal_base_interface* sender)
+		{
+			lock_block<mt_policy> lock(this);
+			m_senders.erase(sender);
+		}
+
+		virtual ~has_slots()
+		{
+			disconnect_all();
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			const_iterator it = m_senders.begin();
+			const_iterator itEnd = m_senders.end();
+
+			while(it != itEnd)
+			{
+				(*it)->slot_disconnect(this);
+				++it;
+			}
+
+			m_senders.erase(m_senders.begin(), m_senders.end());
+		}
+
+	private:
+		sender_set m_senders;
+	};
+
+	template<class mt_policy>
+	class _signal_base0 : public _signal_base<mt_policy>
+	{
+	public:
+		typedef std::list<_connection_base0<mt_policy> *>  connections_list;
+
+		_signal_base0()
+		{
+			;
+		}
+
+		_signal_base0(const _signal_base0& s)
+			: _signal_base<mt_policy>(s)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = s.m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_connect(this);
+				m_connected_slots.push_back((*it)->clone());
+
+				++it;
+			}
+		}
+
+		~_signal_base0()
+		{
+			disconnect_all();
+		}
+
+		bool is_empty()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			return it == itEnd;
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_disconnect(this);
+				delete *it;
+
+				++it;
+			}
+
+			m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+		}
+
+#ifdef _DEBUG
+			bool connected(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+				if ((*it)->getdest() == pclass)
+					return true;
+				it = itNext;
+			}
+			return false;
+		}
+#endif
+
+		void disconnect(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == pclass)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+					pclass->signal_disconnect(this);
+					return;
+				}
+
+				++it;
+			}
+		}
+
+		void slot_disconnect(has_slots_interface* pslot)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				typename connections_list::iterator itNext = it;
+				++itNext;
+
+				if((*it)->getdest() == pslot)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+				}
+
+				it = itNext;
+			}
+		}
+
+		void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == oldtarget)
+				{
+					m_connected_slots.push_back((*it)->duplicate(newtarget));
+				}
+
+				++it;
+			}
+		}
+
+	protected:
+		connections_list m_connected_slots;
+	};
+
+	template<class arg1_type, class mt_policy>
+	class _signal_base1 : public _signal_base<mt_policy>
+	{
+	public:
+		typedef std::list<_connection_base1<arg1_type, mt_policy> *>  connections_list;
+
+		_signal_base1()
+		{
+			;
+		}
+
+		_signal_base1(const _signal_base1<arg1_type, mt_policy>& s)
+			: _signal_base<mt_policy>(s)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = s.m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_connect(this);
+				m_connected_slots.push_back((*it)->clone());
+
+				++it;
+			}
+		}
+
+		void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == oldtarget)
+				{
+					m_connected_slots.push_back((*it)->duplicate(newtarget));
+				}
+
+				++it;
+			}
+		}
+
+		~_signal_base1()
+		{
+			disconnect_all();
+		}
+
+		bool is_empty()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			return it == itEnd;
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_disconnect(this);
+				delete *it;
+
+				++it;
+			}
+
+			m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+		}
+
+#ifdef _DEBUG
+			bool connected(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+				if ((*it)->getdest() == pclass)
+					return true;
+				it = itNext;
+			}
+			return false;
+		}
+#endif
+
+		void disconnect(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == pclass)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+					pclass->signal_disconnect(this);
+					return;
+				}
+
+				++it;
+			}
+		}
+
+		void slot_disconnect(has_slots_interface* pslot)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				typename connections_list::iterator itNext = it;
+				++itNext;
+
+				if((*it)->getdest() == pslot)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+				}
+
+				it = itNext;
+			}
+		}
+
+
+	protected:
+		connections_list m_connected_slots;
+	};
+
+	template<class arg1_type, class arg2_type, class mt_policy>
+	class _signal_base2 : public _signal_base<mt_policy>
+	{
+	public:
+		typedef std::list<_connection_base2<arg1_type, arg2_type, mt_policy> *>
+			connections_list;
+
+		_signal_base2()
+		{
+			;
+		}
+
+		_signal_base2(const _signal_base2<arg1_type, arg2_type, mt_policy>& s)
+			: _signal_base<mt_policy>(s)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = s.m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_connect(this);
+				m_connected_slots.push_back((*it)->clone());
+
+				++it;
+			}
+		}
+
+		void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == oldtarget)
+				{
+					m_connected_slots.push_back((*it)->duplicate(newtarget));
+				}
+
+				++it;
+			}
+		}
+
+		~_signal_base2()
+		{
+			disconnect_all();
+		}
+
+		bool is_empty()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			return it == itEnd;
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_disconnect(this);
+				delete *it;
+
+				++it;
+			}
+
+			m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+		}
+
+#ifdef _DEBUG
+			bool connected(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+				if ((*it)->getdest() == pclass)
+					return true;
+				it = itNext;
+			}
+			return false;
+		}
+#endif
+
+		void disconnect(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == pclass)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+					pclass->signal_disconnect(this);
+					return;
+				}
+
+				++it;
+			}
+		}
+
+		void slot_disconnect(has_slots_interface* pslot)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				typename connections_list::iterator itNext = it;
+				++itNext;
+
+				if((*it)->getdest() == pslot)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+				}
+
+				it = itNext;
+			}
+		}
+
+	protected:
+		connections_list m_connected_slots;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+	class _signal_base3 : public _signal_base<mt_policy>
+	{
+	public:
+		typedef std::list<_connection_base3<arg1_type, arg2_type, arg3_type, mt_policy> *>
+			connections_list;
+
+		_signal_base3()
+		{
+			;
+		}
+
+		_signal_base3(const _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
+			: _signal_base<mt_policy>(s)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = s.m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_connect(this);
+				m_connected_slots.push_back((*it)->clone());
+
+				++it;
+			}
+		}
+
+		void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == oldtarget)
+				{
+					m_connected_slots.push_back((*it)->duplicate(newtarget));
+				}
+
+				++it;
+			}
+		}
+
+		~_signal_base3()
+		{
+			disconnect_all();
+		}
+
+		bool is_empty()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			return it == itEnd;
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_disconnect(this);
+				delete *it;
+
+				++it;
+			}
+
+			m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+		}
+
+#ifdef _DEBUG
+			bool connected(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+				if ((*it)->getdest() == pclass)
+					return true;
+				it = itNext;
+			}
+			return false;
+		}
+#endif
+
+		void disconnect(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == pclass)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+					pclass->signal_disconnect(this);
+					return;
+				}
+
+				++it;
+			}
+		}
+
+		void slot_disconnect(has_slots_interface* pslot)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				typename connections_list::iterator itNext = it;
+				++itNext;
+
+				if((*it)->getdest() == pslot)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+				}
+
+				it = itNext;
+			}
+		}
+
+	protected:
+		connections_list m_connected_slots;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy>
+	class _signal_base4 : public _signal_base<mt_policy>
+	{
+	public:
+		typedef std::list<_connection_base4<arg1_type, arg2_type, arg3_type,
+			arg4_type, mt_policy> *>  connections_list;
+
+		_signal_base4()
+		{
+			;
+		}
+
+		_signal_base4(const _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s)
+			: _signal_base<mt_policy>(s)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = s.m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_connect(this);
+				m_connected_slots.push_back((*it)->clone());
+
+				++it;
+			}
+		}
+
+		void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == oldtarget)
+				{
+					m_connected_slots.push_back((*it)->duplicate(newtarget));
+				}
+
+				++it;
+			}
+		}
+
+		~_signal_base4()
+		{
+			disconnect_all();
+		}
+
+		bool is_empty()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			return it == itEnd;
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_disconnect(this);
+				delete *it;
+
+				++it;
+			}
+
+			m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+		}
+
+#ifdef _DEBUG
+			bool connected(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+				if ((*it)->getdest() == pclass)
+					return true;
+				it = itNext;
+			}
+			return false;
+		}
+#endif
+
+		void disconnect(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == pclass)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+					pclass->signal_disconnect(this);
+					return;
+				}
+
+				++it;
+			}
+		}
+
+		void slot_disconnect(has_slots_interface* pslot)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				typename connections_list::iterator itNext = it;
+				++itNext;
+
+				if((*it)->getdest() == pslot)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+				}
+
+				it = itNext;
+			}
+		}
+
+	protected:
+		connections_list m_connected_slots;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class mt_policy>
+	class _signal_base5 : public _signal_base<mt_policy>
+	{
+	public:
+		typedef std::list<_connection_base5<arg1_type, arg2_type, arg3_type,
+			arg4_type, arg5_type, mt_policy> *>  connections_list;
+
+		_signal_base5()
+		{
+			;
+		}
+
+		_signal_base5(const _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, mt_policy>& s)
+			: _signal_base<mt_policy>(s)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = s.m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_connect(this);
+				m_connected_slots.push_back((*it)->clone());
+
+				++it;
+			}
+		}
+
+		void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == oldtarget)
+				{
+					m_connected_slots.push_back((*it)->duplicate(newtarget));
+				}
+
+				++it;
+			}
+		}
+
+		~_signal_base5()
+		{
+			disconnect_all();
+		}
+
+		bool is_empty()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			return it == itEnd;
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_disconnect(this);
+				delete *it;
+
+				++it;
+			}
+
+			m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+		}
+
+#ifdef _DEBUG
+			bool connected(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+				if ((*it)->getdest() == pclass)
+					return true;
+				it = itNext;
+			}
+			return false;
+		}
+#endif
+
+		void disconnect(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == pclass)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+					pclass->signal_disconnect(this);
+					return;
+				}
+
+				++it;
+			}
+		}
+
+		void slot_disconnect(has_slots_interface* pslot)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				typename connections_list::iterator itNext = it;
+				++itNext;
+
+				if((*it)->getdest() == pslot)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+				}
+
+				it = itNext;
+			}
+		}
+
+	protected:
+		connections_list m_connected_slots;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class arg6_type, class mt_policy>
+	class _signal_base6 : public _signal_base<mt_policy>
+	{
+	public:
+		typedef std::list<_connection_base6<arg1_type, arg2_type, arg3_type, 
+			arg4_type, arg5_type, arg6_type, mt_policy> *>  connections_list;
+
+		_signal_base6()
+		{
+			;
+		}
+
+		_signal_base6(const _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, mt_policy>& s)
+			: _signal_base<mt_policy>(s)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = s.m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_connect(this);
+				m_connected_slots.push_back((*it)->clone());
+
+				++it;
+			}
+		}
+
+		void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == oldtarget)
+				{
+					m_connected_slots.push_back((*it)->duplicate(newtarget));
+				}
+
+				++it;
+			}
+		}
+
+		~_signal_base6()
+		{
+			disconnect_all();
+		}
+
+		bool is_empty()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			return it == itEnd;
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_disconnect(this);
+				delete *it;
+
+				++it;
+			}
+
+			m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+		}
+
+#ifdef _DEBUG
+			bool connected(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+				if ((*it)->getdest() == pclass)
+					return true;
+				it = itNext;
+			}
+			return false;
+		}
+#endif
+
+		void disconnect(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == pclass)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+					pclass->signal_disconnect(this);
+					return;
+				}
+
+				++it;
+			}
+		}
+
+		void slot_disconnect(has_slots_interface* pslot)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				typename connections_list::iterator itNext = it;
+				++itNext;
+
+				if((*it)->getdest() == pslot)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+				}
+
+				it = itNext;
+			}
+		}
+
+	protected:
+		connections_list m_connected_slots;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+	class _signal_base7 : public _signal_base<mt_policy>
+	{
+	public:
+		typedef std::list<_connection_base7<arg1_type, arg2_type, arg3_type, 
+			arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> *>  connections_list;
+
+		_signal_base7()
+		{
+			;
+		}
+
+		_signal_base7(const _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, mt_policy>& s)
+			: _signal_base<mt_policy>(s)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = s.m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_connect(this);
+				m_connected_slots.push_back((*it)->clone());
+
+				++it;
+			}
+		}
+
+		void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == oldtarget)
+				{
+					m_connected_slots.push_back((*it)->duplicate(newtarget));
+				}
+
+				++it;
+			}
+		}
+
+		~_signal_base7()
+		{
+			disconnect_all();
+		}
+
+		bool is_empty()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			return it == itEnd;
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_disconnect(this);
+				delete *it;
+
+				++it;
+			}
+
+			m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+		}
+
+#ifdef _DEBUG
+			bool connected(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+				if ((*it)->getdest() == pclass)
+					return true;
+				it = itNext;
+			}
+			return false;
+		}
+#endif
+
+		void disconnect(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == pclass)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+					pclass->signal_disconnect(this);
+					return;
+				}
+
+				++it;
+			}
+		}
+
+		void slot_disconnect(has_slots_interface* pslot)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				typename connections_list::iterator itNext = it;
+				++itNext;
+
+				if((*it)->getdest() == pslot)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+				}
+
+				it = itNext;
+			}
+		}
+
+	protected:
+		connections_list m_connected_slots;
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy>
+	class _signal_base8 : public _signal_base<mt_policy>
+	{
+	public:
+		typedef std::list<_connection_base8<arg1_type, arg2_type, arg3_type, 
+			arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> *>
+			connections_list;
+
+		_signal_base8()
+		{
+			;
+		}
+
+		_signal_base8(const _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s)
+			: _signal_base<mt_policy>(s)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = s.m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_connect(this);
+				m_connected_slots.push_back((*it)->clone());
+
+				++it;
+			}
+		}
+
+		void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == oldtarget)
+				{
+					m_connected_slots.push_back((*it)->duplicate(newtarget));
+				}
+
+				++it;
+			}
+		}
+
+		~_signal_base8()
+		{
+			disconnect_all();
+		}
+
+		bool is_empty()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			return it == itEnd;
+		}
+
+		void disconnect_all()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				(*it)->getdest()->signal_disconnect(this);
+				delete *it;
+
+				++it;
+			}
+
+			m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+		}
+
+#ifdef _DEBUG
+			bool connected(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+				if ((*it)->getdest() == pclass)
+					return true;
+				it = itNext;
+			}
+			return false;
+		}
+#endif
+
+		void disconnect(has_slots_interface* pclass)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				if((*it)->getdest() == pclass)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+					pclass->signal_disconnect(this);
+					return;
+				}
+
+				++it;
+			}
+		}
+
+		void slot_disconnect(has_slots_interface* pslot)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::iterator it = m_connected_slots.begin();
+			typename connections_list::iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				typename connections_list::iterator itNext = it;
+				++itNext;
+
+				if((*it)->getdest() == pslot)
+				{
+					delete *it;
+					m_connected_slots.erase(it);
+				}
+
+				it = itNext;
+			}
+		}
+
+	protected:
+		connections_list m_connected_slots;
+	};
+
+
+	template<class dest_type, class mt_policy>
+	class _connection0 : public _connection_base0<mt_policy>
+	{
+	public:
+		_connection0()
+		{
+			m_pobject = NULL;
+			m_pmemfun = NULL;
+		}
+
+		_connection0(dest_type* pobject, void (dest_type::*pmemfun)())
+		{
+			m_pobject = pobject;
+			m_pmemfun = pmemfun;
+		}
+
+		virtual ~_connection0()
+		{
+                }
+
+		virtual _connection_base0<mt_policy>* clone()
+		{
+			return new _connection0<dest_type, mt_policy>(*this);
+		}
+
+		virtual _connection_base0<mt_policy>* duplicate(has_slots_interface* pnewdest)
+		{
+			return new _connection0<dest_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+		}
+
+		virtual void emit()
+		{
+			(m_pobject->*m_pmemfun)();
+		}
+
+		virtual has_slots_interface* getdest() const
+		{
+			return m_pobject;
+		}
+
+	private:
+		dest_type* m_pobject;
+		void (dest_type::* m_pmemfun)();
+	};
+
+	template<class dest_type, class arg1_type, class mt_policy>
+	class _connection1 : public _connection_base1<arg1_type, mt_policy>
+	{
+	public:
+		_connection1()
+		{
+			m_pobject = NULL;
+			m_pmemfun = NULL;
+		}
+
+		_connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type))
+		{
+			m_pobject = pobject;
+			m_pmemfun = pmemfun;
+		}
+
+		virtual ~_connection1()
+		{
+                }
+
+		virtual _connection_base1<arg1_type, mt_policy>* clone()
+		{
+			return new _connection1<dest_type, arg1_type, mt_policy>(*this);
+		}
+
+		virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+		{
+			return new _connection1<dest_type, arg1_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+		}
+
+		virtual void emit(arg1_type a1)
+		{
+			(m_pobject->*m_pmemfun)(a1);
+		}
+
+		virtual has_slots_interface* getdest() const
+		{
+			return m_pobject;
+		}
+
+	private:
+		dest_type* m_pobject;
+		void (dest_type::* m_pmemfun)(arg1_type);
+	};
+
+	template<class dest_type, class arg1_type, class arg2_type, class mt_policy>
+	class _connection2 : public _connection_base2<arg1_type, arg2_type, mt_policy>
+	{
+	public:
+		_connection2()
+		{
+			m_pobject = NULL;
+			m_pmemfun = NULL;
+		}
+
+		_connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+			arg2_type))
+		{
+			m_pobject = pobject;
+			m_pmemfun = pmemfun;
+		}
+
+		virtual ~_connection2()
+		{
+                }
+
+		virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone()
+		{
+			return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>(*this);
+		}
+
+		virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+		{
+			return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+		}
+
+		virtual void emit(arg1_type a1, arg2_type a2)
+		{
+			(m_pobject->*m_pmemfun)(a1, a2);
+		}
+
+		virtual has_slots_interface* getdest() const
+		{
+			return m_pobject;
+		}
+
+	private:
+		dest_type* m_pobject;
+		void (dest_type::* m_pmemfun)(arg1_type, arg2_type);
+	};
+
+	template<class dest_type, class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+	class _connection3 : public _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>
+	{
+	public:
+		_connection3()
+		{
+			m_pobject = NULL;
+			m_pmemfun = NULL;
+		}
+
+		_connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+			arg2_type, arg3_type))
+		{
+			m_pobject = pobject;
+			m_pmemfun = pmemfun;
+		}
+
+		virtual ~_connection3()
+		{
+                }
+
+		virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone()
+		{
+			return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>(*this);
+		}
+
+		virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+		{
+			return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+		}
+
+		virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3)
+		{
+			(m_pobject->*m_pmemfun)(a1, a2, a3);
+		}
+
+		virtual has_slots_interface* getdest() const
+		{
+			return m_pobject;
+		}
+
+	private:
+		dest_type* m_pobject;
+		void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type);
+	};
+
+	template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+	class arg4_type, class mt_policy>
+	class _connection4 : public _connection_base4<arg1_type, arg2_type,
+		arg3_type, arg4_type, mt_policy>
+	{
+	public:
+		_connection4()
+		{
+			m_pobject = NULL;
+			m_pmemfun = NULL;
+		}
+
+		_connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type))
+		{
+			m_pobject = pobject;
+			m_pmemfun = pmemfun;
+		}
+
+		virtual ~_connection4()
+		{
+                }
+
+		virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone()
+		{
+			return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(*this);
+		}
+
+		virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+		{
+			return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+		}
+
+		virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, 
+			arg4_type a4)
+		{
+			(m_pobject->*m_pmemfun)(a1, a2, a3, a4);
+		}
+
+		virtual has_slots_interface* getdest() const
+		{
+			return m_pobject;
+		}
+
+	private:
+		dest_type* m_pobject;
+		void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type,
+			arg4_type);
+	};
+
+	template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+	class arg4_type, class arg5_type, class mt_policy>
+	class _connection5 : public _connection_base5<arg1_type, arg2_type,
+		arg3_type, arg4_type, arg5_type, mt_policy>
+	{
+	public:
+		_connection5()
+		{
+			m_pobject = NULL;
+			m_pmemfun = NULL;
+		}
+
+		_connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type, arg5_type))
+		{
+			m_pobject = pobject;
+			m_pmemfun = pmemfun;
+		}
+
+		virtual ~_connection5()
+		{
+                }
+
+		virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, mt_policy>* clone()
+		{
+			return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, 
+				arg5_type, mt_policy>(*this);
+		}
+
+		virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+		{
+			return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, 
+				arg5_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+		}
+
+		virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5)
+		{
+			(m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5);
+		}
+
+		virtual has_slots_interface* getdest() const
+		{
+			return m_pobject;
+		}
+
+	private:
+		dest_type* m_pobject;
+		void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type);
+	};
+
+	template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+	class arg4_type, class arg5_type, class arg6_type, class mt_policy>
+	class _connection6 : public _connection_base6<arg1_type, arg2_type,
+		arg3_type, arg4_type, arg5_type, arg6_type, mt_policy>
+	{
+	public:
+		_connection6()
+		{
+			m_pobject = NULL;
+			m_pmemfun = NULL;
+		}
+
+		_connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type, arg5_type, arg6_type))
+		{
+			m_pobject = pobject;
+			m_pmemfun = pmemfun;
+		}
+
+		virtual ~_connection6()
+		{
+                }
+
+		virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, arg6_type, mt_policy>* clone()
+		{
+			return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, 
+				arg5_type, arg6_type, mt_policy>(*this);
+		}
+
+		virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, arg6_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+		{
+			return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, 
+				arg5_type, arg6_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+		}
+
+		virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5, arg6_type a6)
+		{
+			(m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6);
+		}
+
+		virtual has_slots_interface* getdest() const
+		{
+			return m_pobject;
+		}
+
+	private:
+		dest_type* m_pobject;
+		void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type);
+	};
+
+	template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+	class arg4_type, class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+	class _connection7 : public _connection_base7<arg1_type, arg2_type,
+		arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>
+	{
+	public:
+		_connection7()
+		{
+			m_pobject = NULL;
+			m_pmemfun = NULL;
+		}
+
+		_connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type))
+		{
+			m_pobject = pobject;
+			m_pmemfun = pmemfun;
+		}
+
+		virtual ~_connection7()
+		{
+                }
+
+		virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, arg6_type, arg7_type, mt_policy>* clone()
+		{
+			return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, 
+				arg5_type, arg6_type, arg7_type, mt_policy>(*this);
+		}
+
+		virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+		{
+			return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, 
+				arg5_type, arg6_type, arg7_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+		}
+
+		virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5, arg6_type a6, arg7_type a7)
+		{
+			(m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7);
+		}
+
+		virtual has_slots_interface* getdest() const
+		{
+			return m_pobject;
+		}
+
+	private:
+		dest_type* m_pobject;
+		void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type);
+	};
+
+	template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+	class arg4_type, class arg5_type, class arg6_type, class arg7_type, 
+	class arg8_type, class mt_policy>
+	class _connection8 : public _connection_base8<arg1_type, arg2_type,
+		arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>
+	{
+	public:
+		_connection8()
+		{
+			m_pobject = NULL;
+			m_pmemfun = NULL;
+		}
+
+		_connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, 
+			arg7_type, arg8_type))
+		{
+			m_pobject = pobject;
+			m_pmemfun = pmemfun;
+		}
+
+		virtual ~_connection8()
+		{
+                }
+
+		virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone()
+		{
+			return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, 
+				arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(*this);
+		}
+
+		virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+		{
+			return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, 
+				arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+		}
+
+		virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+		{
+			(m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8);
+		}
+
+		virtual has_slots_interface* getdest() const
+		{
+			return m_pobject;
+		}
+
+	private:
+		dest_type* m_pobject;
+		void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, arg8_type);
+	};
+
+	template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class signal0 : public _signal_base0<mt_policy>
+	{
+	public:
+		typedef _signal_base0<mt_policy> base;
+		typedef typename base::connections_list connections_list;
+		using base::m_connected_slots;
+
+		signal0()
+		{
+			;
+		}
+
+		signal0(const signal0<mt_policy>& s)
+			: _signal_base0<mt_policy>(s)
+		{
+			;
+		}
+
+		template<class desttype>
+			void connect(desttype* pclass, void (desttype::*pmemfun)())
+		{
+			lock_block<mt_policy> lock(this);
+			_connection0<desttype, mt_policy>* conn = 
+				new _connection0<desttype, mt_policy>(pclass, pmemfun);
+			m_connected_slots.push_back(conn);
+			pclass->signal_connect(this);
+		}
+
+		void emit()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit();
+
+				it = itNext;
+			}
+		}
+
+		void operator()()
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit();
+
+				it = itNext;
+			}
+		}
+	};
+
+	template<class arg1_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class signal1 : public _signal_base1<arg1_type, mt_policy>
+	{
+	public:
+		typedef _signal_base1<arg1_type, mt_policy> base;
+		typedef typename base::connections_list connections_list;
+		using base::m_connected_slots;
+
+		signal1()
+		{
+			;
+		}
+
+		signal1(const signal1<arg1_type, mt_policy>& s)
+			: _signal_base1<arg1_type, mt_policy>(s)
+		{
+			;
+		}
+
+		template<class desttype>
+			void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type))
+		{
+			lock_block<mt_policy> lock(this);
+			_connection1<desttype, arg1_type, mt_policy>* conn = 
+				new _connection1<desttype, arg1_type, mt_policy>(pclass, pmemfun);
+			m_connected_slots.push_back(conn);
+			pclass->signal_connect(this);
+		}
+
+		void emit(arg1_type a1)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1);
+
+				it = itNext;
+			}
+		}
+
+		void operator()(arg1_type a1)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1);
+
+				it = itNext;
+			}
+		}
+	};
+
+	template<class arg1_type, class arg2_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class signal2 : public _signal_base2<arg1_type, arg2_type, mt_policy>
+	{
+	public:
+		typedef _signal_base2<arg1_type, arg2_type, mt_policy> base;
+		typedef typename base::connections_list connections_list;
+		using base::m_connected_slots;
+
+		signal2()
+		{
+			;
+		}
+
+		signal2(const signal2<arg1_type, arg2_type, mt_policy>& s)
+			: _signal_base2<arg1_type, arg2_type, mt_policy>(s)
+		{
+			;
+		}
+
+		template<class desttype>
+			void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+			arg2_type))
+		{
+			lock_block<mt_policy> lock(this);
+			_connection2<desttype, arg1_type, arg2_type, mt_policy>* conn = new
+				_connection2<desttype, arg1_type, arg2_type, mt_policy>(pclass, pmemfun);
+			m_connected_slots.push_back(conn);
+			pclass->signal_connect(this);
+		}
+
+		void emit(arg1_type a1, arg2_type a2)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2);
+
+				it = itNext;
+			}
+		}
+
+		void operator()(arg1_type a1, arg2_type a2)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2);
+
+				it = itNext;
+			}
+		}
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class signal3 : public _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>
+	{
+	public:
+		typedef _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy> base;
+		typedef typename base::connections_list connections_list;
+		using base::m_connected_slots;
+
+		signal3()
+		{
+			;
+		}
+
+		signal3(const signal3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
+			: _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>(s)
+		{
+			;
+		}
+
+		template<class desttype>
+			void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+			arg2_type, arg3_type))
+		{
+			lock_block<mt_policy> lock(this);
+			_connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>* conn = 
+				new _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>(pclass,
+				pmemfun);
+			m_connected_slots.push_back(conn);
+			pclass->signal_connect(this);
+		}
+
+		void emit(arg1_type a1, arg2_type a2, arg3_type a3)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3);
+
+				it = itNext;
+			}
+		}
+
+		void operator()(arg1_type a1, arg2_type a2, arg3_type a3)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3);
+
+				it = itNext;
+			}
+		}
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class signal4 : public _signal_base4<arg1_type, arg2_type, arg3_type,
+		arg4_type, mt_policy>
+	{
+	public:
+		typedef _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy> base;
+		typedef typename base::connections_list connections_list;
+		using base::m_connected_slots;
+
+		signal4()
+		{
+			;
+		}
+
+		signal4(const signal4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s)
+			: _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(s)
+		{
+			;
+		}
+
+		template<class desttype>
+			void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type))
+		{
+			lock_block<mt_policy> lock(this);
+			_connection4<desttype, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>*
+				conn = new _connection4<desttype, arg1_type, arg2_type, arg3_type,
+				arg4_type, mt_policy>(pclass, pmemfun);
+			m_connected_slots.push_back(conn);
+			pclass->signal_connect(this);
+		}
+
+		void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4);
+
+				it = itNext;
+			}
+		}
+
+		void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4);
+
+				it = itNext;
+			}
+		}
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class signal5 : public _signal_base5<arg1_type, arg2_type, arg3_type,
+		arg4_type, arg5_type, mt_policy>
+	{
+	public:
+		typedef _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, mt_policy> base;
+		typedef typename base::connections_list connections_list;
+		using base::m_connected_slots;
+
+		signal5()
+		{
+			;
+		}
+
+		signal5(const signal5<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, mt_policy>& s)
+			: _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, mt_policy>(s)
+		{
+			;
+		}
+
+		template<class desttype>
+			void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type, arg5_type))
+		{
+			lock_block<mt_policy> lock(this);
+			_connection5<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+				arg5_type, mt_policy>* conn = new _connection5<desttype, arg1_type, arg2_type,
+				arg3_type, arg4_type, arg5_type, mt_policy>(pclass, pmemfun);
+			m_connected_slots.push_back(conn);
+			pclass->signal_connect(this);
+		}
+
+		void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4, a5);
+
+				it = itNext;
+			}
+		}
+
+		void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4, a5);
+
+				it = itNext;
+			}
+		}
+	};
+
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class arg6_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class signal6 : public _signal_base6<arg1_type, arg2_type, arg3_type,
+		arg4_type, arg5_type, arg6_type, mt_policy>
+	{
+	public:
+		typedef _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, mt_policy> base;
+		typedef typename base::connections_list connections_list;
+		using base::m_connected_slots;
+
+		signal6()
+		{
+			;
+		}
+
+		signal6(const signal6<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, mt_policy>& s)
+			: _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, mt_policy>(s)
+		{
+			;
+		}
+
+		template<class desttype>
+			void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type, arg5_type, arg6_type))
+		{
+			lock_block<mt_policy> lock(this);
+			_connection6<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+				arg5_type, arg6_type, mt_policy>* conn = 
+				new _connection6<desttype, arg1_type, arg2_type, arg3_type,
+				arg4_type, arg5_type, arg6_type, mt_policy>(pclass, pmemfun);
+			m_connected_slots.push_back(conn);
+			pclass->signal_connect(this);
+		}
+
+		void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5, arg6_type a6)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4, a5, a6);
+
+				it = itNext;
+			}
+		}
+
+		void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5, arg6_type a6)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4, a5, a6);
+
+				it = itNext;
+			}
+		}
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class arg6_type, class arg7_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class signal7 : public _signal_base7<arg1_type, arg2_type, arg3_type,
+		arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>
+	{
+	public:
+		typedef _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, arg6_type, arg7_type, mt_policy> base;
+		typedef typename base::connections_list connections_list;
+		using base::m_connected_slots;
+
+		signal7()
+		{
+			;
+		}
+
+		signal7(const signal7<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, mt_policy>& s)
+			: _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, mt_policy>(s)
+		{
+			;
+		}
+
+		template<class desttype>
+			void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, 
+			arg7_type))
+		{
+			lock_block<mt_policy> lock(this);
+			_connection7<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+				arg5_type, arg6_type, arg7_type, mt_policy>* conn = 
+				new _connection7<desttype, arg1_type, arg2_type, arg3_type,
+				arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>(pclass, pmemfun);
+			m_connected_slots.push_back(conn);
+			pclass->signal_connect(this);
+		}
+
+		void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5, arg6_type a6, arg7_type a7)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4, a5, a6, a7);
+
+				it = itNext;
+			}
+		}
+
+		void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5, arg6_type a6, arg7_type a7)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4, a5, a6, a7);
+
+				it = itNext;
+			}
+		}
+	};
+
+	template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+	class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+	class signal8 : public _signal_base8<arg1_type, arg2_type, arg3_type,
+		arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>
+	{
+	public:
+		typedef _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type, 
+			arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> base;
+		typedef typename base::connections_list connections_list;
+		using base::m_connected_slots;
+
+		signal8()
+		{
+			;
+		}
+
+		signal8(const signal8<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s)
+			: _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+			arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(s)
+		{
+			;
+		}
+
+		template<class desttype>
+			void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+			arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, 
+			arg7_type, arg8_type))
+		{
+			lock_block<mt_policy> lock(this);
+			_connection8<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+				arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* conn = 
+				new _connection8<desttype, arg1_type, arg2_type, arg3_type,
+				arg4_type, arg5_type, arg6_type, arg7_type, 
+				arg8_type, mt_policy>(pclass, pmemfun);
+			m_connected_slots.push_back(conn);
+			pclass->signal_connect(this);
+		}
+
+		void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8);
+
+				it = itNext;
+			}
+		}
+
+		void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+			arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+		{
+			lock_block<mt_policy> lock(this);
+			typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+			typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+			while(it != itEnd)
+			{
+				itNext = it;
+				++itNext;
+
+				(*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8);
+
+				it = itNext;
+			}
+		}
+	};
+
+} // namespace sigslot
+
+#endif // TALK_BASE_SIGSLOT_H__
new file mode 100644
--- /dev/null
+++ b/media/mtransport/standalone/Makefile.in
@@ -0,0 +1,60 @@
+# -*- Mode: makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- #
+# 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/.
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+include $(srcdir)/../objs.mk
+
+EXTRA_DEPS += $(srcdir)/../objs.mk
+
+MODULE = mtransport_s
+LIBRARY_NAME = mtransport_s
+FORCE_STATIC_LIB= 1
+ifeq (WINNT,$(OS_TARGET))
+VISIBILITY_FLAGS =
+endif
+
+SRCS_IN_OBJDIR	= 1
+
+EXPORTS_NAMESPACES = mtransport
+
+EXPORTS_mtransport = \
+  ../dtlsidentity.h \
+  ../nricectx.h \
+  ../nricemediastream.h \
+  ../transportflow.h \
+  ../transportlayer.h \
+  ../transportlayerdtls.h \
+  ../transportlayerice.h \
+  ../transportlayerlog.h \
+  ../transportlayerloopback.h \
+  ../transportlayerprsock.h \
+  $(NULL)
+
+CPPSRCS = \
+	$(MTRANSPORT_LCPPSRCS) \
+	$(NULL)
+
+
+# Make a copy into the local directory for dual compilation
+export:: $(MTRANSPORT_CPPSRCS)
+	$(INSTALL) $^ .
+
+# for stun.h
+ifeq (WINNT,$(OS_TARGET))
+DEFINES += \
+  -DWIN32 \
+  -DNOMINMAX \
+  $(NULL)
+else ifeq (Linux,$(OS_TARGET))
+DEFINES += -DLINUX
+endif
+
+include $(srcdir)/../objs.mk
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/Makefile.in
@@ -0,0 +1,100 @@
+# -*- Mode: makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- #
+# 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/.
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = test_mtransport
+
+DEFINES += \
+  -DHAVE_STRDUP -DNR_SOCKET_IS_VOID_PTR -DSCTP_DEBUG -DINET -DINET6
+LIBS = $(EXTRA_DSO_LIBS) \
+  $(XPCOM_LIBS) \
+  $(NSPR_LIBS) \
+  $(NSS_LIBS) \
+  $(DEPTH)/media/mtransport/standalone/$(LIB_PREFIX)mtransport_s.$(LIB_SUFFIX) \
+  $(DEPTH)/media/mtransport/third_party/nICEr/nicer_nicer/$(LIB_PREFIX)nicer.$(LIB_SUFFIX) \
+  $(DEPTH)/media/mtransport/third_party/nrappkit/nrappkit_nrappkit/$(LIB_PREFIX)nrappkit.$(LIB_SUFFIX) \
+  $(DEPTH)/media/webrtc/trunk/testing/gtest_gtest/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) \
+  $(NULL)
+
+ifdef MOZ_SCTP
+LIBS += $(DEPTH)/netwerk/sctp/src/$(LIB_PREFIX)nksctp_s.$(LIB_SUFFIX)
+endif
+
+LOCAL_INCLUDES += \
+  -I. \
+  -I$(topsrcdir)/media/webrtc/trunk/testing/gtest/include/ \
+  -I$(topsrcdir)/media/mtransport/ \
+  -I$(topsrcdir)/netwerk/sctp/src/ \
+ $(NULL)
+
+LOCAL_INCLUDES += \
+ -I. \
+ -I$(topsrcdir)/media/mtransport/third_party/ \
+ -I$(topsrcdir)/media/mtransport/third_party/ \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/crypto \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/ice \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/net \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/stun \
+ -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/util \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/share \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/util/libekr \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/log \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/registry \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/stats \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/plugin \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/event \
+ $(NULL)
+
+# SCTP DEFINES
+ifeq ($(OS_TARGET),WINNT)
+DEFINES += -D__Userspace_os_Windows=1
+else
+ifeq ($(OS_TARGET),Darwin)
+DEFINES += -D__Userspace_os_Darwin=1
+else
+ifeq ($(OS_TARGET),Linux)
+DEFINES += -D__Userspace_os_Linux=1
+else
+ifeq ($(OS_TARGET),FreeBSD)
+DEFINES += -D__Userspace_os_FreeBSD=1
+else
+#default_fallback; probably doesn't work
+DEFINES += -D__Userspace_os_$(OS_TARGET)=1
+endif
+endif
+endif
+endif
+
+ifeq ($(OS_ARCH), Darwin)
+LOCAL_INCLUDES +=  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/darwin/include
+DEFINES += \
+  -DGTEST_USE_OWN_TR1_TUPLE=1 \
+  $(NULL)
+endif
+
+ifeq ($(OS_ARCH), Linux)
+LOCAL_INCLUDES +=  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/port/linux/include
+endif
+
+ifneq ($(OS_TARGET),WINNT)
+CPP_UNIT_TESTS = \
+  ice_unittest.cpp \
+  nrappkit_unittest.cpp \
+  sockettransportservice_unittest.cpp \
+  transport_unittests.cpp \
+  runnable_utils_unittest.cpp \
+  $(NULL)
+ifdef MOZ_SCTP
+CPP_UNIT_TESTS += sctp_unittest.cpp
+endif
+
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/gtest_utils.h
@@ -0,0 +1,57 @@
+/* -*- 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/. */
+
+// Utilities to wrap gtest, modeled on libjingle's gunit
+
+// Original author: ekr@rtfm.com
+#ifndef gtest_utils_h__
+#define gtest_utils_h__
+
+#include <iostream>
+
+#include "nspr.h"
+#include "prinrval.h"
+#include "prthread.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+
+// Wait up to timeout seconds for expression to be true
+#define WAIT(expression, timeout) \
+  do { \
+  for (PRIntervalTime start = PR_IntervalNow(); !(expression) &&        \
+           ! ((PR_IntervalNow() - start) > PR_MillisecondsToInterval(timeout));) \
+    PR_Sleep(200); \
+  } while(0)
+
+// Same as GTEST_WAIT, but stores the result in res. Used when
+// you also want the result of expression but wish to avoid
+// double evaluation.
+#define WAIT_(expression, timeout, res)                      \
+  do { \
+  for (PRIntervalTime start = PR_IntervalNow(); !(res = (expression)) && \
+           ! ((PR_IntervalNow() - start) > PR_MillisecondsToInterval(timeout));) \
+    PR_Sleep(200); \
+  } while(0)
+
+#define ASSERT_TRUE_WAIT(expression, timeout) \
+  do { \
+  bool res; \
+  WAIT_(expression, timeout, res); \
+  ASSERT_TRUE(res); \
+  } while(0);
+
+#define EXPECT_TRUE_WAIT(expression, timeout) \
+  do { \
+  bool res; \
+  WAIT_(expression, timeout, res); \
+  EXPECT_TRUE(res); \
+  } while(0);
+
+#endif
+
+
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -0,0 +1,315 @@
+/* -*- 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 <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "sigslot.h"
+
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+#include "logging.h"
+#include "nricectx.h"
+#include "nricemediastream.h"
+#include "mtransport_test_utils.h"
+#include "runnable_utils.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+MtransportTestUtils test_utils;
+
+bool stream_added = false;
+
+namespace {
+
+enum TrickleMode { TRICKLE_NONE, TRICKLE_DEFERRED };
+
+class IceTestPeer : public sigslot::has_slots<> {
+ public:
+
+  IceTestPeer(const std::string& name, bool offerer, bool set_priorities) :
+      name_(name),
+      ice_ctx_(NrIceCtx::Create(name, offerer, set_priorities)),
+      streams_(),
+      candidates_(),
+      gathering_complete_(false),
+      ready_ct_(0),
+      ice_complete_(false),
+      received_(0),
+      sent_(0) {
+    ice_ctx_->SignalGatheringCompleted.connect(this,
+                                              &IceTestPeer::GatheringComplete);
+    ice_ctx_->SignalCompleted.connect(this, &IceTestPeer::IceCompleted);
+  }
+
+  void AddStream(int components) {
+    char name[100];
+    snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(), (int)streams_.size());
+
+    mozilla::RefPtr<NrIceMediaStream> stream =
+        ice_ctx_->CreateStream(static_cast<char *>(name), components);
+
+    ASSERT_TRUE(stream);
+    streams_.push_back(stream);
+    stream->SignalCandidate.connect(this, &IceTestPeer::GotCandidate);
+    stream->SignalReady.connect(this, &IceTestPeer::StreamReady);
+    stream->SignalPacketReceived.connect(this, &IceTestPeer::PacketReceived);
+  }
+
+  void Gather() {
+    nsresult res;
+
+    test_utils.sts_target()->Dispatch(
+        WrapRunnableRet(ice_ctx_, &NrIceCtx::StartGathering, &res),
+        NS_DISPATCH_SYNC);
+
+    ASSERT_TRUE(NS_SUCCEEDED(res));
+  }
+
+  // Get various pieces of state
+  std::vector<std::string> GetGlobalAttributes() {
+    return ice_ctx_->GetGlobalAttributes();
+  }
+
+  std::vector<std::string>& GetCandidates(const std::string &name) {
+    return candidates_[name];
+  }
+
+  bool gathering_complete() { return gathering_complete_; }
+  int ready_ct() { return ready_ct_; }
+  bool ice_complete() { return ice_complete_; }
+  size_t received() { return received_; }
+  size_t sent() { return sent_; }
+
+  // Start connecting to another peer
+  void Connect(IceTestPeer *remote, TrickleMode trickle_mode) {
+    nsresult res;
+
+    test_utils.sts_target()->Dispatch(
+      WrapRunnableRet(ice_ctx_,
+        &NrIceCtx::ParseGlobalAttributes, remote->GetGlobalAttributes(), &res),
+      NS_DISPATCH_SYNC);
+    ASSERT_TRUE(NS_SUCCEEDED(res));
+
+    if (trickle_mode == TRICKLE_NONE) {
+      for (size_t i=0; i<streams_.size(); ++i) {
+        test_utils.sts_target()->Dispatch(
+            WrapRunnableRet(streams_[i], &NrIceMediaStream::ParseAttributes,
+                            remote->GetCandidates(remote->streams_[i]->name()),
+                            &res), NS_DISPATCH_SYNC);
+
+        ASSERT_TRUE(NS_SUCCEEDED(res));
+      }
+    } else {
+      // Parse empty attributes and then trickle them out later
+      for (size_t i=0; i<streams_.size(); ++i) {
+        std::vector<std::string> empty_attrs;
+        test_utils.sts_target()->Dispatch(
+            WrapRunnableRet(streams_[i], &NrIceMediaStream::ParseAttributes,
+                            empty_attrs,
+                            &res), NS_DISPATCH_SYNC);
+
+        ASSERT_TRUE(NS_SUCCEEDED(res));
+      }
+    }
+
+    // Now start checks
+    test_utils.sts_target()->Dispatch(
+        WrapRunnableRet(ice_ctx_, &NrIceCtx::StartChecks, &res),
+        NS_DISPATCH_SYNC);
+    ASSERT_TRUE(NS_SUCCEEDED(res));
+
+    if (trickle_mode == TRICKLE_DEFERRED) {
+      // If we are in trickle deferred mode, now trickle in the candidates
+      // after ICE has started
+      for (size_t i=0; i<streams_.size(); ++i) {
+        std::vector<std::string> candidates =
+            remote->GetCandidates(remote->streams_[i]->name());
+
+        for (size_t j=0; j<candidates.size(); j++) {
+          test_utils.sts_target()->Dispatch(
+              WrapRunnableRet(streams_[i], &NrIceMediaStream::ParseTrickleCandidate,
+                              candidates[j],
+                              &res), NS_DISPATCH_SYNC);
+
+          ASSERT_TRUE(NS_SUCCEEDED(res));
+        }
+      }
+    }
+  }
+
+  // Handle events
+  void GatheringComplete(NrIceCtx *ctx) {
+    gathering_complete_ = true;
+  }
+
+  void GotCandidate(NrIceMediaStream *stream, const std::string &candidate) {
+    std::cout << "Got candidate " << candidate << std::endl;
+    candidates_[stream->name()].push_back(candidate);
+  }
+
+  void StreamReady(NrIceMediaStream *stream) {
+    std::cout << "Stream ready " << stream->name() << std::endl;
+    ++ready_ct_;
+  }
+
+  void IceCompleted(NrIceCtx *ctx) {
+    std::cout << "ICE completed " << name_ << std::endl;
+    ice_complete_ = true;
+  }
+
+  void PacketReceived(NrIceMediaStream *stream, int component, const unsigned char *data,
+                      int len) {
+    std::cerr << "Received " << len << " bytes" << std::endl;
+    ++received_;
+  }
+
+  void SendPacket(int stream, int component, const unsigned char *data,
+                  int len) {
+    ASSERT_TRUE(NS_SUCCEEDED(streams_[stream]->SendPacket(component, data, len)));
+
+    ++sent_;
+    std::cerr << "Sent " << len << " bytes" << std::endl;
+  }
+
+ private:
+  std::string name_;
+  nsRefPtr<NrIceCtx> ice_ctx_;
+  std::vector<mozilla::RefPtr<NrIceMediaStream> > streams_;
+  std::map<std::string, std::vector<std::string> > candidates_;
+  bool gathering_complete_;
+  int ready_ct_;
+  bool ice_complete_;
+  size_t received_;
+  size_t sent_;
+};
+
+class IceTest : public ::testing::Test {
+ public:
+  IceTest() : initted_(false) {}
+
+  void SetUp() {
+    nsresult rv;
+    target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+    ASSERT_TRUE(NS_SUCCEEDED(rv));
+  }
+
+  void AddStream(const std::string& name, int components) {
+    Init(false);
+    p1_->AddStream(components);
+    p2_->AddStream(components);
+  }
+
+  void Init(bool set_priorities) {
+    if (!initted_) {
+      p1_ = new IceTestPeer("P1", true, set_priorities);
+      p2_ = new IceTestPeer("P2", false, set_priorities);
+    }
+    initted_ = true;
+  }
+
+  bool Gather(bool wait) {
+    Init(false);
+    p1_->Gather();
+    p2_->Gather();
+
+    EXPECT_TRUE_WAIT(p1_->gathering_complete(), 10000);
+    if (!p1_->gathering_complete())
+      return false;
+    EXPECT_TRUE_WAIT(p2_->gathering_complete(), 10000);
+    if (!p2_->gathering_complete())
+      return false;
+
+    return true;
+  }
+
+  void Connect(TrickleMode trickle_mode = TRICKLE_NONE) {
+    p1_->Connect(p2_, trickle_mode);
+    p2_->Connect(p1_, trickle_mode);
+
+    ASSERT_TRUE_WAIT(p1_->ready_ct() == 1 && p2_->ready_ct() == 1, 5000);
+    ASSERT_TRUE_WAIT(p1_->ice_complete() && p2_->ice_complete(), 5000);
+  }
+
+  void SendReceive() {
+    //    p1_->Send(2);
+    p1_->SendPacket(0, 1, reinterpret_cast<const unsigned char *>("TEST"), 4);
+    ASSERT_EQ(1, p1_->sent());
+    ASSERT_TRUE_WAIT(p2_->received() == 1, 1000);
+  }
+
+ protected:
+  bool initted_;
+  nsCOMPtr<nsIEventTarget> target_;
+  mozilla::ScopedDeletePtr<IceTestPeer> p1_;
+  mozilla::ScopedDeletePtr<IceTestPeer> p2_;
+};
+
+}  // end namespace
+
+
+TEST_F(IceTest, TestGather) {
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(true));
+}
+
+TEST_F(IceTest, TestGatherAutoPrioritize) {
+  Init(false);
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(true));
+}
+
+
+TEST_F(IceTest, TestConnect) {
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(true));
+  Connect();
+}
+
+TEST_F(IceTest, TestConnectAutoPrioritize) {
+  Init(false);
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(true));
+  Connect();
+}
+
+TEST_F(IceTest, TestConnectTrickle) {
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(true));
+  Connect(TRICKLE_DEFERRED);
+}
+
+TEST_F(IceTest, TestSendReceive) {
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(true));
+  Connect();
+  SendReceive();
+}
+
+
+int main(int argc, char **argv)
+{
+  test_utils.InitServices();
+  NSS_NoDB_Init(nullptr);
+  NSS_SetDomesticPolicy();
+
+  // Start the tests
+  ::testing::InitGoogleTest(&argc, argv);
+
+  return RUN_ALL_TESTS();
+}
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/mtransport_test_utils.h
@@ -0,0 +1,88 @@
+/* -*- 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 mtransport_test_utils_h__
+#define mtransport_test_utils_h__
+
+#include "nspr.h"
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsXPCOMGlue.h"
+#include "nsXPCOM.h"
+
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIIOService.h"
+#include "nsIServiceManager.h"
+#include "nsISocketTransportService.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsICrashReporter.h"
+#endif
+
+#include "nsServiceManagerUtils.h"
+
+
+class MtransportTestUtils {
+ public:
+  bool InitServices() {
+    nsresult rv;
+
+    NS_InitXPCOM2(getter_AddRefs(servMan_), nullptr, nullptr);
+    manager_ = do_QueryInterface(servMan_);
+    rv = manager_->CreateInstanceByContractID(NS_IOSERVICE_CONTRACTID,
+                                             nullptr, NS_GET_IID(nsIIOService),
+                                              getter_AddRefs(ioservice_));
+    if (!NS_SUCCEEDED(rv))
+      return false;
+
+    sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+    if (!NS_SUCCEEDED(rv))
+      return false;
+
+#ifdef MOZ_CRASHREPORTER
+    char *crashreporter = PR_GetEnv("MOZ_CRASHREPORTER");
+    if (crashreporter && !strcmp(crashreporter, "1")) {
+      //TODO: move this to an even-more-common location to use in all
+      // C++ unittests
+      crashreporter_ = do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+      if (crashreporter_) {
+        std::cerr << "Setting up crash reporting" << std::endl;
+
+        nsCOMPtr<nsIProperties> dirsvc =
+            do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+        nsCOMPtr<nsIFile> cwd;
+        rv = dirsvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+                         NS_GET_IID(nsIFile),
+                         getter_AddRefs(cwd));
+        if (!NS_SUCCEEDED(rv))
+          return false;
+        crashreporter_->SetEnabled(true);
+        crashreporter_->SetMinidumpPath(cwd);
+      }
+    }
+#endif
+    return true;
+  }
+
+  nsCOMPtr<nsIEventTarget> sts_target() { return sts_target_; }
+
+ private:
+  nsCOMPtr<nsIServiceManager> servMan_;
+  nsCOMPtr<nsIComponentManager> manager_;
+  nsCOMPtr<nsIIOService> ioservice_;
+  nsCOMPtr<nsIEventTarget> sts_target_;
+#ifdef MOZ_CRASHREPORTER
+  nsCOMPtr<nsICrashReporter> crashreporter_;
+#endif
+};
+
+
+#endif
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/nrappkit_unittest.cpp
@@ -0,0 +1,99 @@
+
+/* -*- 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 <iostream>
+
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+// nrappkit includes
+extern "C" {
+#include "nr_api.h"
+#include "async_timer.h"
+}
+
+#include "mtransport_test_utils.h"
+#include "runnable_utils.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+
+MtransportTestUtils test_utils;
+
+namespace {
+
+class TimerTest : public ::testing::Test {
+ public:
+  TimerTest() : handle_(nullptr), fired_(false) {}
+
+  int ArmTimer(int timeout) {
+    int ret;
+
+    test_utils.sts_target()->Dispatch(
+        WrapRunnableRet(this, &TimerTest::ArmTimer_w, timeout, &ret),
+        NS_DISPATCH_SYNC);
+
+    return ret;
+  }
+
+  int ArmTimer_w(int timeout) {
+    return NR_ASYNC_TIMER_SET(timeout, cb, this, &handle_);
+  }
+
+  int CancelTimer() {
+    int ret;
+
+    test_utils.sts_target()->Dispatch(
+        WrapRunnableRet(this, &TimerTest::CancelTimer_w, &ret),
+        NS_DISPATCH_SYNC);
+
+    return ret;
+  }
+
+  int CancelTimer_w() {
+    return NR_async_timer_cancel(handle_);
+  }
+
+  static void cb(NR_SOCKET r, int how, void *arg) {
+    std::cerr << "Timer fired " << std::endl;
+
+    TimerTest *t = static_cast<TimerTest *>(arg);
+
+    t->fired_ = true;
+  }
+
+ protected:
+  void *handle_;
+  bool fired_;
+};
+}
+
+TEST_F(TimerTest, SimpleTimer) {
+  ArmTimer(100);
+  ASSERT_TRUE_WAIT(fired_, 1000);
+}
+
+TEST_F(TimerTest, CancelTimer) {
+  ArmTimer(1000);
+  CancelTimer();
+  PR_Sleep(2000);
+  ASSERT_FALSE(fired_);
+}
+
+int main(int argc, char **argv)
+{
+  test_utils.InitServices();
+
+  // Start the tests
+  ::testing::InitGoogleTest(&argc, argv);
+
+  return RUN_ALL_TESTS();
+}
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/runnable_utils_unittest.cpp
@@ -0,0 +1,167 @@
+/* -*- 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 <iostream>
+
+#include "prio.h"
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMGlue.h"
+
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIIOService.h"
+#include "nsIServiceManager.h"
+#include "nsISocketTransportService.h"
+
+#include "nsASocketHandler.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+
+#include "runnable_utils.h"
+#include "mtransport_test_utils.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+MtransportTestUtils test_utils;
+
+namespace {
+
+class TargetClass {
+ public:
+  TargetClass(int *ran) : ran_(ran) {}
+
+  void m1(int x) {
+    std::cerr << __FUNCTION__ << " " << x << std::endl;
+    *ran_ = 1;
+  }
+
+  void m2(int x, int y) {
+    std::cerr << __FUNCTION__ << " " << x << " " << y << std::endl;
+    *ran_ = 2;
+  }
+
+  void m1set(bool *z) {
+    std::cerr << __FUNCTION__ << std::endl;
+    *z = true;
+  }
+  int return_int(int x) {
+    std::cerr << __FUNCTION__ << std::endl;
+    return x;
+  }
+
+  int *ran_;
+};
+
+class RunnableArgsTest : public ::testing::Test {
+ public:
+  RunnableArgsTest() : ran_(0), cl_(&ran_){}
+
+  void Test1Arg() {
+    nsRunnable * r = WrapRunnable(&cl_, &TargetClass::m1, 1);
+    r->Run();
+    ASSERT_EQ(1, ran_);
+  }
+
+  void Test2Args() {
+    nsRunnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2);
+    r->Run();
+    ASSERT_EQ(2, ran_);
+  }
+
+ private:
+  int ran_;
+  TargetClass cl_;
+};
+
+class DispatchTest : public ::testing::Test {
+ public:
+  DispatchTest() : ran_(0), cl_(&ran_) {}
+
+  void SetUp() {
+    nsresult rv;
+    target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+    ASSERT_TRUE(NS_SUCCEEDED(rv));
+  }
+
+  void Test1Arg() {
+    nsRunnable* r = WrapRunnable(&cl_, &TargetClass::m1, 1);
+    target_->Dispatch(r, NS_DISPATCH_SYNC);
+    ASSERT_EQ(1, ran_);
+  }
+
+  void Test2Args() {
+    nsRunnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2);
+    target_->Dispatch(r, NS_DISPATCH_SYNC);
+    ASSERT_EQ(2, ran_);
+  }
+
+  void Test1Set() {
+    bool x = false;
+    target_->Dispatch(WrapRunnable(&cl_, &TargetClass::m1set, &x),
+                      NS_DISPATCH_SYNC);
+    ASSERT_TRUE(x);
+  }
+
+  void TestRet() {
+    int z;
+    int x = 10;
+
+    target_->Dispatch(WrapRunnableRet(&cl_, &TargetClass::return_int, x, &z),
+                      NS_DISPATCH_SYNC);
+    ASSERT_EQ(10, z);
+  }
+
+ private:
+  int ran_;
+  TargetClass cl_;
+  nsCOMPtr<nsIEventTarget> target_;
+};
+
+
+TEST_F(RunnableArgsTest, OneArgument) {
+  Test1Arg();
+}
+
+TEST_F(RunnableArgsTest, TwoArguments) {
+  Test2Args();
+}
+
+TEST_F(DispatchTest, OneArgument) {
+  Test1Arg();
+}
+
+TEST_F(DispatchTest, TwoArguments) {
+  Test2Args();
+}
+
+TEST_F(DispatchTest, Test1Set) {
+  Test1Set();
+}
+
+TEST_F(DispatchTest, TestRet) {
+  TestRet();
+}
+
+
+} // end of namespace
+
+
+int main(int argc, char **argv) {
+    test_utils.InitServices();
+
+  // Start the tests
+  ::testing::InitGoogleTest(&argc, argv);
+
+  return RUN_ALL_TESTS();
+}
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/sctp_unittest.cpp
@@ -0,0 +1,362 @@
+/* -*- 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 <iostream>
+#include <string>
+#include <map>
+
+#include "sigslot.h"
+
+#include "nsNetCID.h"
+#include "nsITimer.h"
+#include "nsComponentManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+#include "transportflow.h"
+#include "transportlayer.h"
+#include "transportlayerloopback.h"
+
+#include "logging.h"
+#include "mtransport_test_utils.h"
+#include "runnable_utils.h"
+#include "usrsctp.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+
+using namespace mozilla;
+MOZ_MTLOG_MODULE("mtransport");
+
+MtransportTestUtils test_utils;
+static bool sctp_logging = false;
+static int port_number = 5000;
+
+namespace {
+
+class TransportTestPeer;
+
+class SendPeriodic : public nsITimerCallback {
+ public:
+  SendPeriodic(TransportTestPeer *peer, int to_send) :
+      peer_(peer),
+      to_send_(to_send) {}
+  virtual ~SendPeriodic() {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITIMERCALLBACK
+
+ protected:
+  TransportTestPeer *peer_;
+  int to_send_;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(SendPeriodic, nsITimerCallback)
+
+
+class TransportTestPeer : public sigslot::has_slots<> {
+ public:
+  TransportTestPeer(std::string name, int local_port, int remote_port)
+      : name_(name), connected_(false),
+        sent_(0), received_(0),
+        flow_(new TransportFlow()),
+        loopback_(new TransportLayerLoopback()),
+        peer_(nullptr),
+        gathering_complete_(false),
+        sctp_(usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, receive_cb, nullptr, 0, nullptr)),
+        timer_(do_CreateInstance(NS_TIMER_CONTRACTID)),
+        periodic_(nullptr) {
+    std::cerr << "Creating TransportTestPeer; flow=" <<
+        static_cast<void *>(flow_.get()) <<
+        " local=" << local_port <<
+        " remote=" << remote_port << std::endl;
+
+    int r = usrsctp_set_non_blocking(sctp_, 1);
+    EXPECT_GE(r, 0);
+
+    struct sctp_event subscription;
+    memset(&subscription, 0, sizeof(subscription));
+    subscription.se_assoc_id = SCTP_ALL_ASSOC;
+    subscription.se_on = 1;
+    subscription.se_type = SCTP_ASSOC_CHANGE;
+    r = usrsctp_setsockopt(sctp_, IPPROTO_SCTP, SCTP_EVENT, &subscription,
+                           sizeof(subscription));
+    EXPECT_GE(r, 0);
+
+    memset(&local_addr_, 0, sizeof(local_addr_));
+    local_addr_.sconn_family = AF_CONN;
+#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows)
+    local_addr_.sconn_len = sizeof(struct sockaddr_conn);
+#endif
+    local_addr_.sconn_port = htons(local_port);
+    local_addr_.sconn_addr = nullptr;
+
+
+    memset(&remote_addr_, 0, sizeof(remote_addr_));
+    remote_addr_.sconn_family = AF_CONN;
+#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows)
+    remote_addr_.sconn_len = sizeof(struct sockaddr_conn);
+#endif
+    remote_addr_.sconn_port = htons(remote_port);
+    remote_addr_.sconn_addr = static_cast<void *>(this);
+
+    nsresult res;
+    res = loopback_->Init();
+    EXPECT_EQ((nsresult)NS_OK, res);
+  }
+
+  ~TransportTestPeer() {
+    std::cerr << "Destroying sctp connection flow=" <<
+        static_cast<void *>(flow_.get()) << std::endl;
+    usrsctp_close(sctp_);
+
+    test_utils.sts_target()->Dispatch(WrapRunnable(this,
+                                                   &TransportTestPeer::DisconnectInt),
+                                      NS_DISPATCH_SYNC);
+
+    std::cerr << "~TransportTestPeer() completed" << std::endl;
+  }
+
+  void ConnectSocket(TransportTestPeer *peer) {
+    loopback_->Connect(peer->loopback_);
+
+    ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_));
+
+    flow_->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 DisconnectInt() {
+    if (flow_) {
+      flow_ = nullptr;
+    }
+  }
+
+  void Disconnect() {
+    loopback_->Disconnect();
+  }
+
+
+  void StartTransfer(size_t to_send) {
+    periodic_ = new SendPeriodic(this, to_send);
+    timer_->SetTarget(test_utils.sts_target());
+    timer_->InitWithCallback(periodic_, 10, nsITimer::TYPE_REPEATING_SLACK);
+  }
+
+  void SendOne() {
+    unsigned char buf[100];
+    memset(buf, sent_ & 0xff, sizeof(buf));
+
+    struct sctp_sndinfo info;
+    info.snd_sid = 1;
+    info.snd_flags = 0;
+    info.snd_ppid = 50;  // What the heck is this?
+    info.snd_context = 0;
+    info.snd_assoc_id = 0;
+
+    int r = usrsctp_sendv(sctp_, buf, sizeof(buf), nullptr, 0,
+                          static_cast<void *>(&info),
+                          sizeof(info), SCTP_SENDV_SNDINFO, 0);
+    ASSERT_EQ(sizeof(buf), r);
+
+    ++sent_;
+  }
+
+  int sent() const { return sent_; }
+  int received() const { return received_; }
+  bool connected() const { return connected_; }
+
+  TransportResult SendPacket(const unsigned char* data, size_t len) {
+    return flow_->SendPacket(data, len);
+  }
+
+  void PacketReceived(TransportFlow * flow, const unsigned char* data,
+                      size_t len) {
+    std::cerr << "Received " << len << " bytes" << std::endl;
+
+    // Pass the data to SCTP
+
+    usrsctp_conninput(static_cast<void *>(this), data, 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;
+
+      if (change->sac_state == SCTP_COMM_UP) {
+        std::cerr << "Connection up" << std::endl;
+        SetConnected(true);
+      } else {
+        std::cerr << "Connection down" << std::endl;
+        SetConnected(false);
+      }
+    }
+  }
+
+  void SetConnected(bool state) {
+    connected_ = state;
+  }
+
+  static int conn_output(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df) {
+    TransportTestPeer *peer = static_cast<TransportTestPeer *>(addr);
+
+    peer->SendPacket(static_cast<unsigned char *>(buffer), length);
+
+    return 0;
+  }
+
+  static int receive_cb(struct socket* sock, union sctp_sockstore addr,
+                        void *data, size_t datalen,
+                        struct sctp_rcvinfo rcv, int flags, void *ulp_info) {
+    TransportTestPeer *me = static_cast<TransportTestPeer *>(
+        addr.sconn.sconn_addr);
+    MOZ_ASSERT(me);
+
+    if (flags & MSG_NOTIFICATION) {
+      union sctp_notification *notif =
+          static_cast<union sctp_notification *>(data);
+
+      me->Notification(notif, datalen);
+      return 0;
+    }
+
+    me->received_ += datalen;
+
+    std::cerr << "receive_cb: sock " << sock << " data " << data << "(" << datalen << ") total received bytes = " << me->received_ << std::endl;
+
+    return 0;
+  }
+
+
+ private:
+  std::string name_;
+  bool connected_;
+  size_t sent_;
+  size_t received_;
+  mozilla::RefPtr<TransportFlow> flow_;
+  TransportLayerLoopback *loopback_;
+  TransportTestPeer *peer_;
+
+  struct sockaddr_conn local_addr_;
+  struct sockaddr_conn remote_addr_;
+  bool gathering_complete_;
+  struct socket *sctp_;
+  size_t to_send_;
+  nsCOMPtr<nsITimer> timer_;
+  nsRefPtr<SendPeriodic> periodic_;
+};
+
+
+// Implemented here because it calls a method of TransportTestPeer
+NS_IMETHODIMP SendPeriodic::Notify(nsITimer *timer) {
+  peer_->SendOne();
+  --to_send_;
+  if (!to_send_) {
+    timer->Cancel();
+  }
+  return NS_OK;
+}
+
+class TransportTest : public ::testing::Test {
+ public:
+  TransportTest() {
+  }
+
+  ~TransportTest() {
+    if (p1_)
+      p1_->Disconnect();
+    if (p2_)
+      p2_->Disconnect();
+    delete p1_;
+    delete p2_;
+  }
+
+  static void SetUpTestCase() {
+    usrsctp_init(0, &TransportTestPeer::conn_output);
+    if (sctp_logging) {
+      usrsctp_sysctl_set_sctp_debug_on(0xffffffff);
+    }
+  }
+
+  void SetUp() {
+  }
+
+  void ConnectSocket(int p1port = 0, int p2port = 0) {
+    if (!p1port)
+      p1port = port_number++;
+    if (!p2port)
+      p2port = port_number++;
+
+    p1_ = new TransportTestPeer("P1", p1port, p2port);
+    p2_ = new TransportTestPeer("P2", p2port, p1port);
+
+    p1_->ConnectSocket(p2_);
+    p2_->ConnectSocket(p1_);
+    ASSERT_TRUE_WAIT(p1_->connected(), 2000);
+    ASSERT_TRUE_WAIT(p2_->connected(), 2000);
+  }
+
+  void TestTransfer(int expected = 1) {
+    std::cerr << "Starting trasnsfer test" << std::endl;
+    p1_->StartTransfer(expected);
+    ASSERT_TRUE_WAIT(p1_->sent() == expected, 10000);
+    ASSERT_TRUE_WAIT(p2_->received() == (expected * 100), 10000);
+    std::cerr << "P2 received " << p2_->received() << std::endl;
+  }
+
+ protected:
+  TransportTestPeer *p1_;
+  TransportTestPeer *p2_;
+};
+
+TEST_F(TransportTest, TestConnect) {
+  ConnectSocket();
+}
+
+TEST_F(TransportTest, DISABLED_TestConnectSymmetricalPorts) {
+  ConnectSocket(5002,5002);
+}
+
+TEST_F(TransportTest, TestTransfer) {
+  ConnectSocket();
+  TestTransfer(50);
+}
+
+
+}  // end namespace
+
+int main(int argc, char **argv)
+{
+  test_utils.InitServices();
+  // Start the tests
+  ::testing::InitGoogleTest(&argc, argv);
+
+  for(int i=0; i<argc; i++) {
+    if (!strcmp(argv[i],"-v")) {
+      sctp_logging = true;
+    }
+  }
+
+  return RUN_ALL_TESTS();
+}
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/sockettransportservice_unittest.cpp
@@ -0,0 +1,211 @@
+/* -*- 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 <iostream>
+
+#include "prio.h"
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMGlue.h"
+
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIIOService.h"
+#include "nsIServiceManager.h"
+#include "nsISocketTransportService.h"
+
+#include "nsASocketHandler.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+
+#include "mtransport_test_utils.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+MtransportTestUtils test_utils;
+
+namespace {
+class SocketTransportServiceTest : public ::testing::Test {
+ public:
+  SocketTransportServiceTest() : received_(0),
+                                 readpipe_(nullptr),
+                                 writepipe_(nullptr),
+                                 registered_(false) {
+  }
+
+  ~SocketTransportServiceTest() {
+    if (readpipe_)
+      PR_Close(readpipe_);
+    if (writepipe_)
+      PR_Close(writepipe_);
+  }
+
+  void SetUp();
+  void RegisterHandler();
+  void SendEvent();
+  void SendPacket();
+
+  void ReceivePacket() {
+    ++received_;
+  }
+
+  void ReceiveEvent() {
+    ++received_;
+  }
+
+  size_t Received() {
+    return received_;
+  }
+
+ private:
+  nsCOMPtr<nsISocketTransportService> stservice_;
+  nsCOMPtr<nsIEventTarget> target_;
+  size_t received_;
+  PRFileDesc *readpipe_;
+  PRFileDesc *writepipe_;
+  bool registered_;
+};
+
+
+// Received an event.
+class EventReceived : public nsRunnable {
+public:
+  EventReceived(SocketTransportServiceTest *test) :
+      test_(test) {};
+
+  NS_IMETHOD Run() {
+    test_->ReceiveEvent();
+    return NS_OK;
+  }
+
+  SocketTransportServiceTest *test_;
+};
+
+
+// Register our listener on the socket
+class RegisterEvent : public nsRunnable {
+public:
+  RegisterEvent(SocketTransportServiceTest *test) :
+      test_(test) {};
+
+  NS_IMETHOD Run() {
+    test_->RegisterHandler();
+    return NS_OK;
+  }
+
+  SocketTransportServiceTest *test_;
+};
+
+
+class SocketHandler : public nsASocketHandler {
+ public:
+  SocketHandler(SocketTransportServiceTest *test) : test_(test) {
+  }
+  virtual ~SocketHandler() {}
+
+  void OnSocketReady(PRFileDesc *fd, int16_t outflags) {
+    unsigned char buf[1600];
+
+    int32_t rv;
+    rv = PR_Recv(fd, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT);
+    if (rv > 0) {
+      std::cerr << "Read " << rv << " bytes" << std::endl;
+      test_->ReceivePacket();
+    }
+  }
+
+  void OnSocketDetached(PRFileDesc *fd) {}
+
+  void IsLocal(bool *aIsLocal) {
+    // TODO(jesup): better check? Does it matter? (likely no)
+    *aIsLocal = false;
+  }
+
+  NS_DECL_ISUPPORTS
+
+ private:
+  SocketTransportServiceTest *test_;
+};
+
+NS_IMPL_ISUPPORTS0(SocketHandler);
+
+void SocketTransportServiceTest::SetUp() {
+  // Get the transport service as a dispatch target
+  nsresult rv;
+  target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Get the transport service as a transport service
+  stservice_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Create a loopback pipe
+  PRStatus status = PR_CreatePipe(&readpipe_, &writepipe_);
+  ASSERT_EQ(status, PR_SUCCESS);
+
+  // Register ourselves as a listener for the read side of the
+  // socket. The registration has to happen on the STS thread,
+  // hence this event stuff.
+  rv = target_->Dispatch(new RegisterEvent(this), 0);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  ASSERT_TRUE_WAIT(registered_, 10000);
+
+}
+
+void SocketTransportServiceTest::RegisterHandler() {
+  nsresult rv;
+
+  rv = stservice_->AttachSocket(readpipe_, new SocketHandler(this));
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  registered_ = true;
+}
+
+void SocketTransportServiceTest::SendEvent() {
+  nsresult rv;
+
+  rv = target_->Dispatch(new EventReceived(this), 0);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  ASSERT_TRUE_WAIT(Received() == 1, 10000);
+}
+
+void SocketTransportServiceTest::SendPacket() {
+  unsigned char buffer[1024];
+  memset(buffer, 0, sizeof(buffer));
+
+  int32_t status = PR_Write(writepipe_, buffer, sizeof(buffer));
+  uint32_t size = status & 0xffff;
+  ASSERT_EQ(sizeof(buffer), size);
+}
+
+
+
+// The unit tests themselves
+TEST_F(SocketTransportServiceTest, SendEvent) {
+  SendEvent();
+}
+
+TEST_F(SocketTransportServiceTest, SendPacket) {
+  SendPacket();
+}
+
+
+}  // end namespace
+
+
+int main(int argc, char **argv) {
+    test_utils.InitServices();
+
+  // Start the tests
+  ::testing::InitGoogleTest(&argc, argv);
+
+  return RUN_ALL_TESTS();
+}
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/transport_unittests.cpp
@@ -0,0 +1,485 @@
+/* -*- 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 <iostream>
+#include <string>
+#include <map>
+
+#include "sigslot.h"
+
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+#include "dtlsidentity.h"
+#include "nricectx.h"
+#include "nricemediastream.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+#include "transportlayerdtls.h"
+#include "transportlayerice.h"
+#include "transportlayerlog.h"
+#include "transportlayerloopback.h"
+
+#include "logging.h"
+#include "mtransport_test_utils.h"
+#include "runnable_utils.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+MOZ_MTLOG_MODULE("mtransport");
+
+MtransportTestUtils test_utils;
+
+
+// Class to simulate various kinds of network lossage
+class TransportLayerLossy : public TransportLayer {
+ public:
+  TransportLayerLossy() : loss_mask_(0), packet_(0) {}
+
+  virtual TransportResult SendPacket(const unsigned char *data, size_t len) {
+    MOZ_MTLOG(PR_LOG_NOTICE, LAYER_INFO << "SendPacket(" << len << ")");
+
+    if (loss_mask_ & (1 << (packet_ % 32))) {
+      MOZ_MTLOG(PR_LOG_NOTICE, "Dropping packet");
+      ++packet_;
+      return len;
+    }
+
+    ++packet_;
+
+    return downward_->SendPacket(data, len);
+  }
+
+  void SetLoss(uint32_t packet) {
+    loss_mask_ |= (1 << (packet & 32));
+  }
+
+  void StateChange(TransportLayer *layer, State state) {
+    SetState(state);
+  }
+
+  void PacketReceived(TransportLayer *layer, const unsigned char *data,
+                      size_t len) {
+    SignalPacketReceived(this, data, len);
+  }
+
+  TRANSPORT_LAYER_ID("lossy");
+
+ protected:
+  virtual void WasInserted() {
+    downward_->SignalPacketReceived.
+        connect(this,
+                &TransportLayerLossy::PacketReceived);
+    downward_->SignalStateChange.
+        connect(this,
+                &TransportLayerLossy::StateChange);
+
+    SetState(downward_->state());
+  }
+
+ private:
+  uint32_t loss_mask_;
+  uint32_t packet_;
+};
+
+namespace {
+class TransportTestPeer : public sigslot::has_slots<> {
+ public:
+  TransportTestPeer(nsCOMPtr<nsIEventTarget> target, std::string name)
+      : name_(name), target_(target),
+        received_(0), flow_(new TransportFlow(name)),
+        loopback_(new TransportLayerLoopback()),
+        logging_(new TransportLayerLogging()),
+        lossy_(new TransportLayerLossy()),
+        dtls_(new TransportLayerDtls()),
+        identity_(DtlsIdentity::Generate()),
+        ice_ctx_(NrIceCtx::Create(name,
+                                  name == "P2" ?
+                                  TransportLayerDtls::CLIENT :
+                                  TransportLayerDtls::SERVER)),
+        streams_(), candidates_(),
+        peer_(nullptr),
+        gathering_complete_(false)
+ {
+    dtls_->SetIdentity(identity_);
+    dtls_->SetRole(name == "P2" ?
+                   TransportLayerDtls::CLIENT :
+                   TransportLayerDtls::SERVER);
+
+    nsresult res = identity_->ComputeFingerprint("sha-1",
+                                             fingerprint_,
+                                             sizeof(fingerprint_),
+                                             &fingerprint_len_);
+    EXPECT_TRUE(NS_SUCCEEDED(res));
+    EXPECT_EQ(20, fingerprint_len_);
+  }
+
+  ~TransportTestPeer() {
+    test_utils.sts_target()->Dispatch(
+      WrapRunnable(this, &TransportTestPeer::DestroyFlow),
+      NS_DISPATCH_SYNC);
+  }
+
+  void DestroyFlow() {
+    loopback_->Disconnect();
+    flow_ = nullptr;
+  }
+
+  void SetDtlsAllowAll() {
+    nsresult res = dtls_->SetVerificationAllowAll();
+    ASSERT_TRUE(NS_SUCCEEDED(res));
+  }
+  void SetDtlsPeer(TransportTestPeer *peer, int digests, unsigned int damage) {
+    unsigned int mask = 1;
+
+    for (int i=0; i<digests; i++) {
+      unsigned char fingerprint_to_set[TransportLayerDtls::kMaxDigestLength];
+
+      memcpy(fingerprint_to_set,
+             peer->fingerprint_,
+             peer->fingerprint_len_);
+      if (damage & mask)
+        fingerprint_to_set[0]++;
+
+      nsresult res = dtls_->SetVerificationDigest(
+          "sha-1",
+          fingerprint_to_set,
+          peer->fingerprint_len_);
+
+      ASSERT_TRUE(NS_SUCCEEDED(res));
+
+      mask <<= 1;
+    }
+  }
+
+
+  void ConnectSocket(TransportTestPeer *peer) {
+    nsresult res;
+    res = loopback_->Init();
+    ASSERT_EQ((nsresult)NS_OK, res);
+
+    loopback_->Connect(peer->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_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
+  }
+
+  void InitIce() {
+    nsresult res;
+
+    // Attach our slots
+    ice_ctx_->SignalGatheringCompleted.
+        connect(this, &TransportTestPeer::GatheringComplete);
+
+    char name[100];
+    snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(),
+             (int)streams_.size());
+
+    // Create the media stream
+    mozilla::RefPtr<NrIceMediaStream> stream =
+        ice_ctx_->CreateStream(static_cast<char *>(name), 1);
+    ASSERT_TRUE(stream != nullptr);
+    streams_.push_back(stream);
+
+    // Listen for candidates
+    stream->SignalCandidate.
+        connect(this, &TransportTestPeer::GotCandidate);
+
+    // Create the transport layer
+    ice_ = new TransportLayerIce(name, ice_ctx_, stream, 1);
+
+    // Assemble the stack
+    ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(ice_));
+    ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(dtls_));
+
+    // Listen for media events
+    flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
+    flow_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged);
+
+    // Start gathering
+    test_utils.sts_target()->Dispatch(
+        WrapRunnableRet(ice_ctx_, &NrIceCtx::StartGathering, &res),
+        NS_DISPATCH_SYNC);
+    ASSERT_TRUE(NS_SUCCEEDED(res));
+  }
+
+  void ConnectIce(TransportTestPeer *peer) {
+    peer_ = peer;
+
+    // If gathering is already complete, push the candidates over
+    if (gathering_complete_)
+      GatheringComplete(ice_ctx_);
+  }
+
+  // New candidate
+  void GotCandidate(NrIceMediaStream *stream, const std::string &candidate) {
+    std::cerr << "Got candidate " << candidate << std::endl;
+    candidates_[stream->name()].push_back(candidate);
+  }
+
+  // Gathering complete, so send our candidates and start
+  // connecting on the other peer.
+  void GatheringComplete(NrIceCtx *ctx) {
+    nsresult res;
+
+    // Don't send to the other side
+    if (!peer_) {
+      gathering_complete_ = true;
+      return;
+    }
+
+    // First send attributes
+    test_utils.sts_target()->Dispatch(
+      WrapRunnableRet(peer_->ice_ctx_,
+                      &NrIceCtx::ParseGlobalAttributes,
+                      ice_ctx_->GetGlobalAttributes(), &res),
+      NS_DISPATCH_SYNC);
+    ASSERT_TRUE(NS_SUCCEEDED(res));
+
+    for (size_t i=0; i<streams_.size(); ++i) {
+      test_utils.sts_target()->Dispatch(
+        WrapRunnableRet(peer_->streams_[i], &NrIceMediaStream::ParseAttributes,
+                        candidates_[streams_[i]->name()], &res), NS_DISPATCH_SYNC);
+
+      ASSERT_TRUE(NS_SUCCEEDED(res));
+    }
+
+    // Start checks on the other peer.
+    test_utils.sts_target()->Dispatch(
+      WrapRunnableRet(peer_->ice_ctx_, &NrIceCtx::StartChecks, &res),
+      NS_DISPATCH_SYNC);
+    ASSERT_TRUE(NS_SUCCEEDED(res));
+  }
+
+  TransportResult SendPacket(const unsigned char* data, size_t len) {
+    return flow_->SendPacket(data, len);
+  }
+
+
+  void StateChanged(TransportFlow *flow, 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;
+    ++received_;
+  }
+
+  void SetLoss(uint32_t loss) {
+    lossy_->SetLoss(loss);
+  }
+
+  bool connected() {
+    return flow_->state() == TransportLayer::TS_OPEN;
+  }
+
+  bool failed() {
+    return flow_->state() == TransportLayer::TS_ERROR;
+  }
+
+  size_t received() { return received_; }
+
+ private:
+  std::string name_;
+  nsCOMPtr<nsIEventTarget> target_;
+  size_t received_;
+    mozilla::RefPtr<TransportFlow> flow_;
+  TransportLayerLoopback *loopback_;
+  TransportLayerLogging *logging_;
+  TransportLayerLossy *lossy_;
+  TransportLayerDtls *dtls_;
+  TransportLayerIce *ice_;
+  mozilla::RefPtr<DtlsIdentity> identity_;
+  mozilla::RefPtr<NrIceCtx> ice_ctx_;
+  std::vector<mozilla::RefPtr<NrIceMediaStream> > streams_;
+  std::map<std::string, std::vector<std::string> > candidates_;
+  TransportTestPeer *peer_;
+  bool gathering_complete_;
+  unsigned char fingerprint_[TransportLayerDtls::kMaxDigestLength];
+  size_t fingerprint_len_;
+};
+
+
+class TransportTest : public ::testing::Test {
+ public:
+  TransportTest() {
+    fds_[0] = nullptr;
+    fds_[1] = nullptr;
+  }
+
+  ~TransportTest() {
+    delete p1_;
+    delete p2_;
+
+    //    Can't detach these
+    //    PR_Close(fds_[0]);
+    //    PR_Close(fds_[1]);
+  }
+
+  void SetUp() {
+    nsresult rv;
+    target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+    ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+    p1_ = new TransportTestPeer(target_, "P1");
+    p2_ = new TransportTestPeer(target_, "P2");
+  }
+
+  void SetDtlsPeer(int digests = 1, unsigned int damage = 0) {
+    p1_->SetDtlsPeer(p2_, digests, damage);
+    p2_->SetDtlsPeer(p1_, digests, damage);
+  }
+
+  void SetDtlsAllowAll() {
+    p1_->SetDtlsAllowAll();
+    p2_->SetDtlsAllowAll();
+  }
+
+  void ConnectSocket() {
+    p1_->ConnectSocket(p2_);
+    p2_->ConnectSocket(p1_);
+    ASSERT_TRUE_WAIT(p1_->connected(), 10000);
+    ASSERT_TRUE_WAIT(p2_->connected(), 10000);
+  }
+
+  void ConnectSocketExpectFail() {
+    p1_->ConnectSocket(p2_);
+    p2_->ConnectSocket(p1_);
+    ASSERT_TRUE_WAIT(p1_->failed(), 10000);
+    ASSERT_TRUE_WAIT(p2_->failed(), 10000);
+  }
+
+  void InitIce() {
+    p1_->InitIce();
+    p2_->InitIce();
+  }
+
+  void ConnectIce() {
+    p1_->InitIce();
+    p2_->InitIce();
+    p1_->ConnectIce(p2_);
+    p2_->ConnectIce(p1_);
+    ASSERT_TRUE_WAIT(p1_->connected(), 10000);
+    ASSERT_TRUE_WAIT(p2_->connected(), 10000);
+  }
+
+  void TransferTest(size_t count) {
+    unsigned char buf[1000];
+
+    for (size_t i= 0; i<count; ++i) {
+      memset(buf, count & 0xff, sizeof(buf));
+      TransportResult rv = p1_->SendPacket(buf, sizeof(buf));
+      ASSERT_TRUE(rv > 0);
+    }
+
+    std::cerr << "Received == " << p2_->received() << std::endl;
+    ASSERT_TRUE_WAIT(count == p2_->received(), 10000);
+  }
+
+ protected:
+  PRFileDesc *fds_[2];
+  TransportTestPeer *p1_;
+  TransportTestPeer *p2_;
+  nsCOMPtr<nsIEventTarget> target_;
+};
+
+
+TEST_F(TransportTest, TestNoDtlsVerificationSettings) {
+  ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestConnect) {
+  SetDtlsPeer();
+  ConnectSocket();
+}
+
+TEST_F(TransportTest, TestConnectAllowAll) {
+  SetDtlsAllowAll();
+  ConnectSocket();
+}
+
+TEST_F(TransportTest, TestConnectBadDigest) {
+  SetDtlsPeer(1, 1);
+
+  ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestConnectTwoDigests) {
+  SetDtlsPeer(2, 0);
+
+  ConnectSocket();
+}
+
+TEST_F(TransportTest, TestConnectTwoDigestsFirstBad) {
+  SetDtlsPeer(2, 1);
+
+  ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestConnectTwoDigestsSecondBad) {
+  SetDtlsPeer(2, 2);
+
+  ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestConnectTwoDigestsBothBad) {
+  SetDtlsPeer(2, 3);
+
+  ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestTransfer) {
+  SetDtlsPeer();
+  ConnectSocket();
+  TransferTest(1);
+}
+
+TEST_F(TransportTest, TestConnectLoseFirst) {
+  SetDtlsPeer();
+  p1_->SetLoss(0);
+  ConnectSocket();
+  TransferTest(1);
+}
+
+TEST_F(TransportTest, TestConnectIce) {
+  SetDtlsPeer();
+  ConnectIce();
+}
+
+TEST_F(TransportTest, TestTransferIce) {
+  SetDtlsPeer();
+  ConnectIce();
+  TransferTest(1);
+}
+
+}  // end namespace
+
+int main(int argc, char **argv)
+{
+  test_utils.InitServices();
+  NSS_NoDB_Init(nullptr);
+  NSS_SetDomesticPolicy();
+  // Start the tests
+  ::testing::InitGoogleTest(&argc, argv);
+
+  return RUN_ALL_TESTS();
+}
--- a/media/mtransport/third_party/nICEr/nicer.gyp
+++ b/media/mtransport/third_party/nICEr/nicer.gyp
@@ -17,16 +17,17 @@
 	      '../nrappkit/src/event',
 	      '../nrappkit/src/log',
               '../nrappkit/src/plugin',
 	      '../nrappkit/src/registry',
 	      '../nrappkit/src/share',
 	      '../nrappkit/src/stats',
 	      '../nrappkit/src/util',
 	      '../nrappkit/src/util/libekr',
+ 	      '../nrappkit/src/port/generic/include',
 
               # INTERNAL
               "./src/crypto",
               "./src/ice",
               "./src/net",
               "./src/stun",
               "./src/util",
           ],
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportflow.cpp
@@ -0,0 +1,82 @@
+/* -*- 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 <deque>
+
+#include "transportflow.h"
+#include "transportlayer.h"
+
+namespace mozilla {
+
+TransportFlow::~TransportFlow() {
+  for (std::deque<TransportLayer *>::iterator it = layers_.begin();
+       it != layers_.end(); ++it) {
+    delete *it;
+  }
+}
+
+nsresult TransportFlow::PushLayer(TransportLayer *layer) {
+  nsresult rv = layer->Init();
+  if (!NS_SUCCEEDED(rv))
+    return rv;
+
+
+  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);
+  }
+  layer->SignalStateChange.connect(this, &TransportFlow::StateChange);
+  layer->SignalPacketReceived.connect(this, &TransportFlow::PacketReceived);
+
+  layers_.push_front(layer);
+  layer->Inserted(this, old_layer);
+  return NS_OK;
+}
+
+TransportLayer *TransportFlow::top() const {
+  return layers_.empty() ? nullptr : layers_.front();
+}
+
+TransportLayer *TransportFlow::GetLayer(const std::string& id) const {
+  for (std::deque<TransportLayer *>::const_iterator it = layers_.begin();
+       it != layers_.end(); ++it) {
+    if ((*it)->id() == id)
+      return *it;
+  }
+
+  return nullptr;
+}
+
+TransportLayer::State TransportFlow::state() {
+  return top() ? top()->state() : TransportLayer::TS_NONE;
+}
+
+TransportResult TransportFlow::SendPacket(const unsigned char *data,
+                                          size_t len) {
+  return top() ? top()->SendPacket(data, len) : TE_ERROR;
+}
+
+void TransportFlow::StateChange(TransportLayer *layer,
+                                TransportLayer::State state) {
+  SignalStateChange(this, state);
+}
+
+void TransportFlow::PacketReceived(TransportLayer* layer,
+                                   const unsigned char *data,
+                                   size_t len) {
+  SignalPacketReceived(this, data, len);
+}
+
+
+
+
+
+
+}  // close namespace
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportflow.h
@@ -0,0 +1,67 @@
+/* -*- 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 transportflow_h__
+#define transportflow_h__
+
+#include <deque>
+#include <string>
+
+#include "nscore.h"
+#include "nsISupportsImpl.h"
+#include "transportlayer.h"
+#include "m_cpp_utils.h"
+
+// A stack of transport layers acts as a flow.
+// Generally, one reads and writes to the top layer.
+namespace mozilla {
+
+class TransportFlow : public sigslot::has_slots<> {
+ public:
+  TransportFlow() : id_("(anonymous)") {}
+  TransportFlow(const std::string id) : id_(id) {}
+  ~TransportFlow();
+
+  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
+  nsresult PushLayer(TransportLayer *layer);
+  TransportLayer *top() const;
+  TransportLayer *GetLayer(const std::string& id) const;
+
+  // 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;
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TransportFlow)
+
+ private:
+  DISALLOW_COPY_ASSIGN(TransportFlow);
+
+  void StateChange(TransportLayer *layer, TransportLayer::State state);
+  void PacketReceived(TransportLayer* layer, const unsigned char *data,
+      size_t len);
+
+  std::string id_;
+  std::deque<TransportLayer *> layers_;
+};
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayer.cpp
@@ -0,0 +1,57 @@
+/* -*- 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 <prlog.h>
+
+#include "logging.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+// Logging context
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport");
+
+nsresult TransportLayer::Init() {
+  if (state_ != TS_NONE)
+    return state_ == TS_ERROR ? NS_ERROR_FAILURE : NS_OK;
+
+  nsresult rv = InitInternal();
+
+  if (!NS_SUCCEEDED(rv)) {
+    state_ = TS_ERROR;
+    return rv;
+  }
+  state_ = TS_INIT;
+
+  return NS_OK;
+}
+
+void TransportLayer::Inserted(TransportFlow *flow, TransportLayer *downward) {
+  flow_ = flow;
+  downward_ = downward;
+
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "Inserted: downward='" <<
+    (downward ? downward->id(): "none") << "'");
+
+  WasInserted();
+}
+
+void TransportLayer::SetState(State state) {
+  if (state != state_) {
+    MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "state " << state_ << "->" << state);
+    state_ = state;
+    SignalStateChange(this, state);
+  }
+}
+
+const std::string& TransportLayer::flow_id() {
+    static const std::string empty;
+
+    return flow_ ? flow_->id() : empty;
+  }
+}  // close namespace
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayer.h
@@ -0,0 +1,93 @@
+/* -*- 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 transportlayer_h__
+#define transportlayer_h__
+
+#include "sigslot.h"
+
+#include "mozilla/RefPtr.h"
+
+#include "m_cpp_utils.h"
+
+namespace mozilla {
+
+class TransportFlow;
+
+typedef int TransportResult;
+
+enum {
+  TE_WOULDBLOCK = -1, TE_ERROR = -2, TE_INTERNAL = -3
+};
+
+#define TRANSPORT_LAYER_ID(name) \
+  virtual const std::string id() { return name; } \
+  static std::string ID() { return name; }
+
+// Abstract base class for network transport layers.
+class TransportLayer : public sigslot::has_slots<> {
+ public:
+  // The state of the transport flow
+  // We can't use "ERROR" because Windows has a macro named "ERROR"
+  enum State { TS_NONE, TS_INIT, TS_CONNECTING, TS_OPEN, TS_CLOSED, TS_ERROR };
+  enum Mode { STREAM, DGRAM };
+
+  // Is this a stream or datagram flow
+  TransportLayer(Mode mode = STREAM) :
+    mode_(mode),
+    state_(TS_NONE),
+    flow_(nullptr),
+    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);
+
+  // 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;
+
+  // 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;
+
+  // Return the layer id for this layer
+  virtual const std::string id() = 0;
+
+  // The id of the flow
+  virtual const std::string& flow_id();
+
+ protected:
+  virtual void WasInserted() {}
+  virtual void SetState(State state);
+
+  Mode mode_;
+  State state_;
+  TransportFlow *flow_;  // The flow this is part of
+  TransportLayer *downward_; // The next layer in the stack
+
+ private:
+  DISALLOW_COPY_ASSIGN(TransportLayer);
+};
+
+#define LAYER_INFO "Flow[" << flow_id() << "(none)" << "]; Layer[" << id() << "]: "
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayerdtls.cpp
@@ -0,0 +1,939 @@
+/* -*- 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 <queue>
+#include <algorithm>
+
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+#include "keyhi.h"
+
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIEventTarget.h"
+#include "nsNetCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "dtlsidentity.h"
+#include "logging.h"
+#include "transportflow.h"
+#include "transportlayerdtls.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport");
+
+static PRDescIdentity transport_layer_identity = PR_INVALID_IO_LAYER;
+
+// TODO: Implement a mode for this where
+// the channel is not ready until confirmed externally
+// (e.g., after cert check).
+
+#define UNIMPLEMENTED                                           \
+  MOZ_MTLOG(PR_LOG_ERROR,                                            \
+       "Call to unimplemented function "<< __FUNCTION__);       \
+  MOZ_ASSERT(false);                                             \
+  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0)
+
+
+// We need to adapt the NSPR/libssl model to the TransportFlow model.
+// The former wants pull semantics and TransportFlow wants push.
+//
+// - A TransportLayerDtls assumes it is sitting on top of another
+//   TransportLayer, which means that events come in asynchronously.
+// - NSS (libssl) wants to sit on top of a PRFileDesc and poll.
+// - 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), offset_(0) {}
+
+  void Assign(const void *data, int32_t len) {
+    data_ = new uint8_t[len];
+    memcpy(data_, data, len);
+    len_ = len;
+  }
+
+  ScopedDeleteArray<uint8_t> data_;
+  int32_t len_;
+  int32_t offset_;
+};
+
+void TransportLayerNSPRAdapter::PacketReceived(const void *data, int32_t len) {
+  input_.push(new Packet());
+  input_.back()->Assign(data, len);
+}
+
+int32_t TransportLayerNSPRAdapter::Read(void *data, int32_t len) {
+  if (input_.empty()) {
+    PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
+    return TE_WOULDBLOCK;
+  }
+
+  Packet* front = input_.front();
+  int32_t to_read = std::min(len, front->len_ - front->offset_);
+  memcpy(data, front->data_, to_read);
+  front->offset_ += to_read;
+
+  if (front->offset_ == front->len_) {
+    input_.pop();
+    delete front;
+  }
+
+  return to_read;
+}
+
+int32_t TransportLayerNSPRAdapter::Write(const void *buf, int32_t length) {
+  TransportResult r = output_->SendPacket(
+      static_cast<const unsigned char *>(buf), length);
+  if (r >= 0) {
+    return r;
+  }
+
+  if (r == TE_WOULDBLOCK) {
+    PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
+  } else {
+    PR_SetError(PR_IO_ERROR, 0);
+  }
+
+  return -1;
+}
+
+
+// Implementation of NSPR methods
+static PRStatus TransportLayerClose(PRFileDesc *f) {
+  f->secret = nullptr;
+  return PR_SUCCESS;
+}
+
+static int32_t TransportLayerRead(PRFileDesc *f, void *buf, int32_t length) {
+  TransportLayerNSPRAdapter *io = reinterpret_cast<TransportLayerNSPRAdapter *>(f->secret);
+  return io->Read(buf, length);
+}
+
+static int32_t TransportLayerWrite(PRFileDesc *f, const void *buf, int32_t length) {
+  TransportLayerNSPRAdapter *io = reinterpret_cast<TransportLayerNSPRAdapter *>(f->secret);
+  return io->Write(buf, length);
+}
+
+static int32_t TransportLayerAvailable(PRFileDesc *f) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+int64_t TransportLayerAvailable64(PRFileDesc *f) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static PRStatus TransportLayerSync(PRFileDesc *f) {
+  UNIMPLEMENTED;
+  return PR_FAILURE;
+}
+
+static int32_t TransportLayerSeek(PRFileDesc *f, int32_t offset,
+                                  PRSeekWhence how) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static int64_t TransportLayerSeek64(PRFileDesc *f, int64_t offset,
+                                    PRSeekWhence how) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static PRStatus TransportLayerFileInfo(PRFileDesc *f, PRFileInfo *info) {
+  UNIMPLEMENTED;
+  return PR_FAILURE;
+}
+
+static PRStatus TransportLayerFileInfo64(PRFileDesc *f, PRFileInfo64 *info) {
+  UNIMPLEMENTED;
+  return PR_FAILURE;
+}
+
+static int32_t TransportLayerWritev(PRFileDesc *f, const PRIOVec *iov,
+                                    int32_t iov_size, PRIntervalTime to) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static PRStatus TransportLayerConnect(PRFileDesc *f, const PRNetAddr *addr,
+                                      PRIntervalTime to) {
+  UNIMPLEMENTED;
+  return PR_FAILURE;
+}
+
+static PRFileDesc *TransportLayerAccept(PRFileDesc *sd, PRNetAddr *addr,
+                                        PRIntervalTime to) {
+  UNIMPLEMENTED;
+  return nullptr;
+}
+
+static PRStatus TransportLayerBind(PRFileDesc *f, const PRNetAddr *addr) {
+  UNIMPLEMENTED;
+  return PR_FAILURE;
+}
+
+static PRStatus TransportLayerListen(PRFileDesc *f, int32_t depth) {
+  UNIMPLEMENTED;
+  return PR_FAILURE;
+}
+
+static PRStatus TransportLayerShutdown(PRFileDesc *f, int32_t how) {
+  UNIMPLEMENTED;
+  return PR_FAILURE;
+}
+
+// This function does not support peek.
+static int32_t TransportLayerRecv(PRFileDesc *f, void *buf, int32_t amount,
+                                  int32_t flags, PRIntervalTime to) {
+  MOZ_ASSERT(flags == 0);
+  if (flags != 0) {
+    PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+    return -1;
+  }
+
+  return TransportLayerRead(f, buf, amount);
+}
+
+// Note: this is always nonblocking and assumes a zero timeout.
+static int32_t TransportLayerSend(PRFileDesc *f, const void *buf, int32_t amount,
+                                  int32_t flags, PRIntervalTime to) {
+  int32_t written = TransportLayerWrite(f, buf, amount);
+  return written;
+}
+
+static int32_t TransportLayerRecvfrom(PRFileDesc *f, void *buf, int32_t amount,
+                                      int32_t flags, PRNetAddr *addr, PRIntervalTime to) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static int32_t TransportLayerSendto(PRFileDesc *f, const void *buf, int32_t amount,
+                                    int32_t flags, const PRNetAddr *addr, PRIntervalTime to) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static int16_t TransportLayerPoll(PRFileDesc *f, int16_t in_flags, int16_t *out_flags) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static int32_t TransportLayerAcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+                                        PRNetAddr **raddr,
+                                        void *buf, int32_t amount, PRIntervalTime t) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static int32_t TransportLayerTransmitFile(PRFileDesc *sd, PRFileDesc *f,
+                                          const void *headers, int32_t hlen,
+                                          PRTransmitFileFlags flags, PRIntervalTime t) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static PRStatus TransportLayerGetpeername(PRFileDesc *f, PRNetAddr *addr) {
+  // TODO: Modify to return unique names for each channel
+  // somehow, as opposed to always the same static address. The current
+  // implementation messes up the session cache, which is why it's off
+  // elsewhere
+  addr->inet.family = PR_AF_INET;
+  addr->inet.port = 0;
+  addr->inet.ip = 0;
+
+  return PR_SUCCESS;
+}
+
+static PRStatus TransportLayerGetsockname(PRFileDesc *f, PRNetAddr *addr) {
+  UNIMPLEMENTED;
+  return PR_FAILURE;
+}
+
+static PRStatus TransportLayerGetsockoption(PRFileDesc *f, PRSocketOptionData *opt) {
+  switch (opt->option) {
+    case PR_SockOpt_Nonblocking:
+      opt->value.non_blocking = PR_TRUE;
+      return PR_SUCCESS;
+    default:
+      UNIMPLEMENTED;
+      break;
+  }
+
+  return PR_FAILURE;
+}
+
+// Imitate setting socket options. These are mostly noops.
+static PRStatus TransportLayerSetsockoption(PRFileDesc *f,
+                                            const PRSocketOptionData *opt) {
+  switch (opt->option) {
+    case PR_SockOpt_Nonblocking:
+      return PR_SUCCESS;
+    case PR_SockOpt_NoDelay:
+      return PR_SUCCESS;
+    default:
+      UNIMPLEMENTED;
+      break;
+  }
+
+  return PR_FAILURE;
+}
+
+static int32_t TransportLayerSendfile(PRFileDesc *out, PRSendFileData *in,
+                                      PRTransmitFileFlags flags, PRIntervalTime to) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static PRStatus TransportLayerConnectContinue(PRFileDesc *f, int16_t flags) {
+  UNIMPLEMENTED;
+  return PR_FAILURE;
+}
+
+static int32_t TransportLayerReserved(PRFileDesc *f) {
+  UNIMPLEMENTED;
+  return -1;
+}
+
+static const struct PRIOMethods TransportLayerMethods = {
+  PR_DESC_LAYERED,
+  TransportLayerClose,
+  TransportLayerRead,
+  TransportLayerWrite,
+  TransportLayerAvailable,
+  TransportLayerAvailable64,
+  TransportLayerSync,
+  TransportLayerSeek,
+  TransportLayerSeek64,
+  TransportLayerFileInfo,
+  TransportLayerFileInfo64,
+  TransportLayerWritev,
+  TransportLayerConnect,
+  TransportLayerAccept,
+  TransportLayerBind,
+  TransportLayerListen,
+  TransportLayerShutdown,
+  TransportLayerRecv,
+  TransportLayerSend,
+  TransportLayerRecvfrom,
+  TransportLayerSendto,
+  TransportLayerPoll,
+  TransportLayerAcceptRead,
+  TransportLayerTransmitFile,
+  TransportLayerGetsockname,
+  TransportLayerGetpeername,
+  TransportLayerReserved,
+  TransportLayerReserved,
+  TransportLayerGetsockoption,
+  TransportLayerSetsockoption,
+  TransportLayerSendfile,
+  TransportLayerConnectContinue,
+  TransportLayerReserved,
+  TransportLayerReserved,
+  TransportLayerReserved,
+  TransportLayerReserved
+};
+
+TransportLayerDtls::~TransportLayerDtls() {
+  if (timer_) {
+    timer_->Cancel();
+  }
+}
+
+nsresult TransportLayerDtls::InitInternal() {
+  // Get the transport service as an event target
+  nsresult rv;
+  target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+  if (NS_FAILED(rv)) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't get socket transport service");
+    return rv;
+  }
+
+  timer_ = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't get timer");
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+
+void TransportLayerDtls::WasInserted() {
+  // Connect to the lower layers
+  if (!Setup()) {
+    SetState(TS_ERROR);
+  }
+};
+
+
+nsresult TransportLayerDtls::SetVerificationAllowAll() {
+  // Defensive programming
+  if (verification_mode_ != VERIFY_UNSET)
+    return NS_ERROR_ALREADY_INITIALIZED;
+
+  verification_mode_ = VERIFY_ALLOW_ALL;
+
+  return NS_OK;
+}
+
+nsresult
+TransportLayerDtls::SetVerificationDigest(const std::string digest_algorithm,
+                                          const unsigned char *digest_value,
+                                          size_t digest_len) {
+  // Defensive programming
+  if (verification_mode_ != VERIFY_UNSET &&
+      verification_mode_ != VERIFY_DIGEST) {
+    return NS_ERROR_ALREADY_INITIALIZED;
+  }
+
+  // Note that we do not sanity check these values for length.
+  // We merely ensure they will fit into the buffer.
+  // TODO: is there a Data construct we could use?
+  if (digest_len > kMaxDigestLength)
+    return NS_ERROR_INVALID_ARG;
+
+  digests_.push_back(new VerificationDigest(
+      digest_algorithm, digest_value, digest_len));
+
+  verification_mode_ = VERIFY_DIGEST;
+
+  return NS_OK;
+}
+
+// TODO: make sure this is called from STS. Otherwise
+// we have thread safety issues
+bool TransportLayerDtls::Setup() {
+  SECStatus rv;
+
+  if (!downward_) {
+    MOZ_MTLOG(PR_LOG_ERROR, "DTLS layer with nothing below. This is useless");
+    return false;
+  }
+  nspr_io_adapter_ = new TransportLayerNSPRAdapter(downward_);
+
+  if (!identity_) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Can't start DTLS without an identity");
+    return false;
+  }
+
+  if (verification_mode_ == VERIFY_UNSET) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Can't start DTLS without specifying a verification mode");
+    return false;
+  }
+
+  if (transport_layer_identity == PR_INVALID_IO_LAYER) {
+    transport_layer_identity = PR_GetUniqueIdentity("nssstreamadapter");
+  }
+
+  ScopedPRFileDesc pr_fd(PR_CreateIOLayerStub(transport_layer_identity,
+                                              &TransportLayerMethods));
+  MOZ_ASSERT(pr_fd != nullptr);
+  if (!pr_fd)
+    return false;
+  pr_fd->secret = reinterpret_cast<PRFilePrivate *>(nspr_io_adapter_.get());
+
+  ScopedPRFileDesc ssl_fd;
+  if (mode_ == DGRAM) {
+    ssl_fd = DTLS_ImportFD(nullptr, pr_fd);
+  } else {
+    ssl_fd = SSL_ImportFD(nullptr, pr_fd);
+  }
+
+  MOZ_ASSERT(ssl_fd != nullptr);  // This should never happen
+  if (!ssl_fd) {
+    return false;
+  }
+
+  pr_fd.forget(); // ownership transfered to ssl_fd;
+
+  if (role_ == CLIENT) {
+    rv = SSL_GetClientAuthDataHook(ssl_fd, GetClientAuthDataHook,
+                                   this);
+    if (rv != SECSuccess) {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't set identity");
+      return false;
+    }
+  } else {
+    // Server side
+    rv = SSL_ConfigSecureServer(ssl_fd, identity_->cert(),
+                                identity_->privkey(),
+                                kt_rsa);
+    if (rv != SECSuccess) {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't set identity");
+      return false;
+    }
+
+    // Insist on a certificate from the client
+    rv = SSL_OptionSet(ssl_fd, SSL_REQUEST_CERTIFICATE, PR_TRUE);
+    if (rv != SECSuccess) {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't request certificate");
+      return false;
+    }
+
+    rv = SSL_OptionSet(ssl_fd, SSL_REQUIRE_CERTIFICATE, PR_TRUE);
+    if (rv != SECSuccess) {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't require certificate");
+      return false;
+    }
+  }
+
+  // Require TLS 1.1. Perhaps some day in the future we will allow
+  // TLS 1.0 for stream modes.
+  SSLVersionRange version_range = {
+    SSL_LIBRARY_VERSION_TLS_1_1,
+    SSL_LIBRARY_VERSION_TLS_1_1
+  };
+
+  rv = SSL_VersionRangeSet(ssl_fd, &version_range);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Can't disable SSLv3");
+    return false;
+  }
+
+  rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_SESSION_TICKETS, PR_FALSE);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't disable session tickets");
+    return false;
+  }
+
+  rv = SSL_OptionSet(ssl_fd, SSL_NO_CACHE, PR_TRUE);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't disable session caching");
+    return false;
+  }
+
+  rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_DEFLATE, PR_FALSE);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't disable deflate");
+    return false;
+  }
+
+  rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_NEVER);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't disable renegotiation");
+    return false;
+  }
+
+  rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_FALSE_START, PR_FALSE);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't disable false start");
+    return false;
+  }
+
+  rv = SSL_OptionSet(ssl_fd, SSL_NO_LOCKS, PR_TRUE);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't disable locks");
+    return false;
+  }
+
+  // Set the SRTP ciphers
+  if (srtp_ciphers_.size()) {
+    // Note: std::vector is guaranteed to contiguous
+    rv = SSL_SetSRTPCiphers(ssl_fd, &srtp_ciphers_[0],
+                            srtp_ciphers_.size());
+
+    if (rv != SECSuccess) {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't set SRTP cipher suite");
+      return false;
+    }
+  }
+
+  // Certificate validation
+  rv = SSL_AuthCertificateHook(ssl_fd, AuthCertificateHook,
+                               reinterpret_cast<void *>(this));
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't set certificate validation hook");
+    return false;
+  }
+
+  // Now start the handshake
+  rv = SSL_ResetHandshake(ssl_fd, role_ == SERVER ? PR_TRUE : PR_FALSE);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't reset handshake");
+    return false;
+  }
+  ssl_fd_ = ssl_fd.forget();
+
+  // Finally, get ready to receive data
+  downward_->SignalStateChange.connect(this, &TransportLayerDtls::StateChange);
+  downward_->SignalPacketReceived.connect(this, &TransportLayerDtls::PacketReceived);
+
+  if (downward_->state() == TS_OPEN) {
+    Handshake();
+  }
+
+  return true;
+}
+
+
+void TransportLayerDtls::StateChange(TransportLayer *layer, State state) {
+  if (state <= state_) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Lower layer state is going backwards from ours");
+    SetState(TS_ERROR);
+    return;
+  }
+
+  switch (state) {
+    case TS_NONE:
+      MOZ_ASSERT(false);  // Can't happen
+      break;
+
+    case TS_INIT:
+      MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "State change of lower layer to INIT forbidden");
+      SetState(TS_ERROR);
+      break;
+
+    case TS_CONNECTING:
+      MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "Lower lower is connecting.");
+      break;
+
+    case TS_OPEN:
+      MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "Lower lower is now open; starting TLS");
+      Handshake();
+      break;
+
+    case TS_CLOSED:
+      MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "Lower lower is now closed");
+      SetState(TS_CLOSED);
+      break;
+
+    case TS_ERROR:
+      MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "Lower lower experienced an error");
+      SetState(TS_ERROR);
+      break;
+  }
+}
+
+void TransportLayerDtls::Handshake() {
+  SetState(TS_CONNECTING);
+
+  // Clear the retransmit timer
+  timer_->Cancel();
+
+  SECStatus rv = SSL_ForceHandshake(ssl_fd_);
+
+  if (rv == SECSuccess) {
+    MOZ_MTLOG(PR_LOG_NOTICE, LAYER_INFO << "****** SSL handshake completed ******");
+    if (!cert_ok_) {
+      MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "Certificate check never occurred");
+      SetState(TS_ERROR);
+      return;
+    }
+    SetState(TS_OPEN);
+  } else {
+    int32_t err = PR_GetError();
+    switch(err) {
+      case SSL_ERROR_RX_MALFORMED_HANDSHAKE:
+        if (mode_ != DGRAM) {
+          MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "Malformed TLS message");
+          SetState(TS_ERROR);
+        } else {
+          MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "Malformed DTLS message; ignoring");
+        }
+        // Fall through
+      case PR_WOULD_BLOCK_ERROR:
+        MOZ_MTLOG(PR_LOG_NOTICE, LAYER_INFO << "Would have blocked");
+        if (mode_ == DGRAM) {
+          PRIntervalTime timeout;
+          rv = DTLS_GetHandshakeTimeout(ssl_fd_, &timeout);
+          if (rv == SECSuccess) {
+            uint32_t timeout_ms = PR_IntervalToMilliseconds(timeout);
+
+            MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "Setting DTLS timeout to " <<
+                 timeout_ms);
+            timer_->SetTarget(target_);
+            timer_->InitWithFuncCallback(TimerCallback,
+                                         this, timeout_ms,
+                                         nsITimer::TYPE_ONE_SHOT);
+          }
+        }
+        break;
+      default:
+        MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "SSL handshake error "<< err);
+        SetState(TS_ERROR);
+        break;
+    }
+  }
+}
+
+void TransportLayerDtls::PacketReceived(TransportLayer* layer,
+                                        const unsigned char *data,
+                                        size_t len) {
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "PacketReceived(" << len << ")");
+
+  if (state_ != TS_CONNECTING && state_ != TS_OPEN) {
+    MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "Discarding packet in inappropriate state");
+    return;
+  }
+
+  nspr_io_adapter_->PacketReceived(data, len);
+
+  // 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) {
+    unsigned char buf[2000];
+
+    int32_t rv = PR_Recv(ssl_fd_, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT);
+    if (rv > 0) {
+      // We have data
+      MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS");
+      SignalPacketReceived(this, buf, rv);
+    } else if (rv == 0) {
+      SetState(TS_CLOSED);
+    } else {
+      int32_t err = PR_GetError();
+
+      if (err == PR_WOULD_BLOCK_ERROR) {
+        // This gets ignored
+        MOZ_MTLOG(PR_LOG_NOTICE, LAYER_INFO << "Would have blocked");
+      } else {
+        MOZ_MTLOG(PR_LOG_NOTICE, LAYER_INFO << "NSS Error " << err);
+        SetState(TS_ERROR);
+      }
+    }
+  }
+}
+
+TransportResult TransportLayerDtls::SendPacket(const unsigned char *data,
+                                               size_t len) {
+  if (state_ != TS_OPEN) {
+    MOZ_MTLOG(PR_LOG_ERROR, LAYER_INFO << "Can't call SendPacket() in state "
+         << state_);
+    return TE_ERROR;
+  }
+
+  int32_t rv = PR_Send(ssl_fd_, data, len, 0, PR_INTERVAL_NO_WAIT);
+
+  if (rv > 0) {
+    // We have data
+    MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "Wrote " << rv << " bytes to SSL Layer");
+    return rv;
+  }
+
+  if (rv == 0) {
+    SetState(TS_CLOSED);
+    return 0;
+  }
+
+  int32_t err = PR_GetError();
+
+  if (err == PR_WOULD_BLOCK_ERROR) {
+    // This gets ignored
+    MOZ_MTLOG(PR_LOG_NOTICE, LAYER_INFO << "Would have blocked");
+    return TE_WOULDBLOCK;
+  }
+
+  MOZ_MTLOG(PR_LOG_NOTICE, LAYER_INFO << "NSS Error " << err);
+  SetState(TS_ERROR);
+  return TE_ERROR;
+}
+
+SECStatus TransportLayerDtls::GetClientAuthDataHook(void *arg, PRFileDesc *fd,
+                                                    CERTDistNames *caNames,
+                                                    CERTCertificate **pRetCert,
+                                                    SECKEYPrivateKey **pRetKey) {
+  MOZ_MTLOG(PR_LOG_DEBUG, "Server requested client auth");
+
+  TransportLayerDtls *stream = reinterpret_cast<TransportLayerDtls *>(arg);
+
+  if (!stream->identity_) {
+    MOZ_MTLOG(PR_LOG_ERROR, "No identity available");
+    PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
+    return SECFailure;
+  }
+
+  *pRetCert = CERT_DupCertificate(stream->identity_->cert());
+  if (!pRetCert) {
+    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+    return SECFailure;
+  }
+
+  *pRetKey = SECKEY_CopyPrivateKey(stream->identity_->privkey());
+  if (!pRetKey) {
+    CERT_DestroyCertificate(*pRetCert);
+    *pRetCert = nullptr;
+    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+    return SECFailure;
+  }
+
+  return SECSuccess;
+}
+
+nsresult TransportLayerDtls::SetSrtpCiphers(std::vector<uint16_t> ciphers) {
+  // TODO: We should check these
+  srtp_ciphers_ = ciphers;
+
+  return NS_OK;
+}
+
+nsresult TransportLayerDtls::GetSrtpCipher(uint16_t *cipher) {
+  SECStatus rv = SSL_GetSRTPCipher(ssl_fd_, cipher);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_DEBUG, "No SRTP cipher negotiated");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult TransportLayerDtls::ExportKeyingMaterial(const std::string& label,
+                                                  bool use_context,
+                                                  const std::string& context,
+                                                  unsigned char *out,
+                                                  unsigned int outlen) {
+  SECStatus rv = SSL_ExportKeyingMaterial(ssl_fd_,
+                                          label.c_str(),
+                                          label.size(),
+                                          use_context,
+                                          reinterpret_cast<const unsigned char *>(
+                                              context.c_str()),
+                                          context.size(),
+                                          out,
+                                          outlen);
+  if (rv != SECSuccess) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't export SSL keying material");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+SECStatus TransportLayerDtls::AuthCertificateHook(void *arg,
+                                                  PRFileDesc *fd,
+                                                  PRBool checksig,
+                                                  PRBool isServer) {
+  TransportLayerDtls *stream = reinterpret_cast<TransportLayerDtls *>(arg);
+
+  return stream->AuthCertificateHook(fd, checksig, isServer);
+}
+
+SECStatus
+TransportLayerDtls::CheckDigest(const RefPtr<VerificationDigest>&
+                                digest,
+                                CERTCertificate *peer_cert) {
+  unsigned char computed_digest[kMaxDigestLength];
+  size_t computed_digest_len;
+
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "Checking digest, algorithm=" << digest->algorithm_);
+  nsresult res =
+      DtlsIdentity::ComputeFingerprint(peer_cert,
+                                       digest->algorithm_,
+                                       computed_digest,
+                                       sizeof(computed_digest),
+                                       &computed_digest_len);
+  if (NS_FAILED(res)) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Could not compute peer fingerprint for digest " <<
+         digest->algorithm_);
+    // Go to end
+    PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
+    return SECFailure;
+  }
+
+  if (computed_digest_len != digest->len_) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Digest is wrong length " << digest->len_ <<
+         " should be " << computed_digest_len << " for algorithm " <<
+         digest->algorithm_);
+    PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
+    return SECFailure;
+  }
+
+  if (memcmp(digest->value_, computed_digest, computed_digest_len) != 0) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Digest does not match");
+    PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
+    return SECFailure;
+  }
+
+  return SECSuccess;
+}
+
+
+SECStatus TransportLayerDtls::AuthCertificateHook(PRFileDesc *fd,
+                                                  PRBool checksig,
+                                                  PRBool isServer) {
+  ScopedCERTCertificate peer_cert;
+  peer_cert = SSL_PeerCertificate(fd);
+
+
+  // We are not set up to take this being called multiple
+  // times. Change this if we ever add renegotiation.
+  MOZ_ASSERT(!auth_hook_called_);
+  if (auth_hook_called_) {
+    PR_SetError(PR_UNKNOWN_ERROR, 0);
+    return SECFailure;
+  }
+  auth_hook_called_ = true;
+
+  MOZ_ASSERT(verification_mode_ != VERIFY_UNSET);
+  MOZ_ASSERT(peer_cert_ == nullptr);
+
+  switch (verification_mode_) {
+    case VERIFY_UNSET:
+      // Break out to error exit
+      PR_SetError(PR_UNKNOWN_ERROR, 0);
+      break;
+
+    case VERIFY_ALLOW_ALL:
+      peer_cert_ = peer_cert.forget();
+      cert_ok_ = true;
+      return SECSuccess;
+
+    case VERIFY_DIGEST:
+      {
+        MOZ_ASSERT(digests_.size() != 0);
+        // Check all the provided digests
+
+        // Checking functions call PR_SetError()
+        SECStatus rv = SECFailure;
+        for (size_t i = 0; i < digests_.size(); i++) {
+          RefPtr<VerificationDigest> digest = digests_[i];
+          rv = CheckDigest(digest, peer_cert);
+
+          if (rv != SECSuccess)
+            break;
+        }
+
+        if (rv == SECSuccess) {
+          // Matches all digests, we are good to go
+          cert_ok_ = true;
+          peer_cert = peer_cert.forget();
+          return SECSuccess;
+        }
+      }
+      break;
+    default:
+      MOZ_CRASH();  // Can't happen
+  }
+    
+  return SECFailure;
+}
+
+void TransportLayerDtls::TimerCallback(nsITimer *timer, void *arg) {
+  TransportLayerDtls *dtls = reinterpret_cast<TransportLayerDtls *>(arg);
+
+  MOZ_MTLOG(PR_LOG_DEBUG, "DTLS timer expired");
+
+  dtls->Handshake();
+}
+
+}  // close namespace
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayerdtls.h
@@ -0,0 +1,168 @@
+/* -*- 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 transportlayerdtls_h__
+#define transportlayerdtls_h__
+
+#include <queue>
+
+#include "sigslot.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/Scoped.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:
+  TransportLayerNSPRAdapter(TransportLayer *output) :
+  output_(output),
+  input_() {}
+
+  void PacketReceived(const void *data, int32_t len);
+  int32_t Read(void *data, int32_t len);
+  int32_t Write(const void *buf, int32_t length);
+
+ private:
+  DISALLOW_COPY_ASSIGN(TransportLayerNSPRAdapter);
+
+  TransportLayer *output_;
+  std::queue<Packet *> input_;
+};
+
+class TransportLayerDtls : public TransportLayer {
+ public:
+  TransportLayerDtls() :
+      TransportLayer(DGRAM),
+      role_(CLIENT),
+      verification_mode_(VERIFY_UNSET),
+      ssl_fd_(nullptr),
+      auth_hook_called_(false),
+      cert_ok_(false) {}
+
+  virtual ~TransportLayerDtls();
+
+  enum Role { CLIENT, SERVER};
+  enum Verification { VERIFY_UNSET, VERIFY_ALLOW_ALL, VERIFY_DIGEST};
+  const static int kMaxDigestLength = HASH_LENGTH_MAX;
+
+  // DTLS-specific operations
+  void SetRole(Role role) { role_ = role;}
+  Role role() { return role_; }
+
+  void SetIdentity(const RefPtr<DtlsIdentity>& identity) {
+    identity_ = identity;
+  }
+  nsresult SetVerificationAllowAll();
+  nsresult SetVerificationDigest(const std::string digest_algorithm,
+                                 const unsigned char *digest_value,
+                                 size_t digest_len);
+
+  nsresult SetSrtpCiphers(std::vector<uint16_t> ciphers);
+  nsresult GetSrtpCipher(uint16_t *cipher);
+
+  nsresult ExportKeyingMaterial(const std::string& label,
+                                bool use_context,
+                                const std::string& context,
+                                unsigned char *out,
+                                unsigned int outlen);
+
+  const CERTCertificate *GetPeerCert() const {
+    return peer_cert_;
+  }
+
+  // Transport layer overrides.
+  virtual nsresult InitInternal();
+  virtual void WasInserted();
+  virtual TransportResult SendPacket(const unsigned char *data, size_t len);
+
+  // Signals
+  void StateChange(TransportLayer *layer, State state);
+  void PacketReceived(TransportLayer* layer, const unsigned char *data,
+                      size_t len);
+
+  TRANSPORT_LAYER_ID("dtls")
+
+  private:
+  DISALLOW_COPY_ASSIGN(TransportLayerDtls);
+
+  // A single digest to check
+  class VerificationDigest {
+   public:
+    VerificationDigest(std::string algorithm,
+                       const unsigned char *value, size_t len) {
+      MOZ_ASSERT(len <= sizeof(value_));
+
+      algorithm_ = algorithm;
+      memcpy(value_, value, len);
+      len_ = len;
+    }
+
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VerificationDigest);
+
+    std::string algorithm_;
+    size_t len_;
+    unsigned char value_[kMaxDigestLength];
+
+   private:
+    DISALLOW_COPY_ASSIGN(VerificationDigest);
+  };
+
+
+  bool Setup();
+  void Handshake();
+
+  static SECStatus GetClientAuthDataHook(void *arg, PRFileDesc *fd,
+                                         CERTDistNames *caNames,
+                                         CERTCertificate **pRetCert,
+                                         SECKEYPrivateKey **pRetKey);
+  static SECStatus AuthCertificateHook(void *arg,
+                                       PRFileDesc *fd,
+                                       PRBool checksig,
+                                       PRBool isServer);
+  SECStatus AuthCertificateHook(PRFileDesc *fd,
+                                PRBool checksig,
+                                PRBool isServer);
+
+  static void TimerCallback(nsITimer *timer, void *arg);
+
+  SECStatus CheckDigest(const RefPtr<VerificationDigest>& digest,
+                        CERTCertificate *cert);
+
+  RefPtr<DtlsIdentity> identity_;
+  std::vector<uint16_t> srtp_ciphers_;
+
+  Role role_;
+  Verification verification_mode_;
+  std::vector<RefPtr<VerificationDigest> > digests_;
+
+  // Must delete nspr_io_adapter after ssl_fd_ b/c ssl_fd_ causes an alert
+  // (ssl_fd_ contains an un-owning pointer to nspr_io_adapter_)
+  ScopedDeletePtr<TransportLayerNSPRAdapter> nspr_io_adapter_;
+  ScopedPRFileDesc ssl_fd_;
+
+  ScopedCERTCertificate peer_cert_;
+  nsCOMPtr<nsIEventTarget> target_;
+  nsCOMPtr<nsITimer> timer_;
+  bool auth_hook_called_;
+  bool cert_ok_;
+};
+
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayerice.cpp
@@ -0,0 +1,146 @@
+/* -*- 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
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <string>
+#include <vector>
+
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsError.h"
+#include "nsIEventTarget.h"
+#include "nsNetCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "registry.h"
+#include "async_timer.h"
+#include "ice_util.h"
+#include "transport_addr.h"
+#include "nr_crypto.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "stun_client_ctx.h"
+#include "stun_server_ctx.h"
+#include "ice_ctx.h"
+#include "ice_candidate.h"
+#include "ice_handler.h"
+}
+
+// Local includes
+#include "logging.h"
+#include "nricectx.h"
+#include "nricemediastream.h"
+#include "transportflow.h"
+#include "transportlayerice.h"
+
+namespace mozilla {
+
+#ifdef ERROR
+#undef ERROR
+#endif
+
+MOZ_MTLOG_MODULE("mtransport");
+
+static bool initialized = false;
+
+TransportLayerIce::TransportLayerIce(const std::string& name,
+    RefPtr<NrIceCtx> ctx, RefPtr<NrIceMediaStream> stream,
+                                     int component)
+    : name_(name), ctx_(ctx), stream_(stream), component_(component) {
+  stream_->SignalReady.connect(this, &TransportLayerIce::IceReady);
+  stream_->SignalFailed.connect(this, &TransportLayerIce::IceFailed);
+  stream_->SignalPacketReceived.connect(this,
+                                        &TransportLayerIce::IcePacketReceived);
+  if (stream_->state() == NrIceMediaStream::ICE_OPEN) {
+    SetState(TS_OPEN);
+  }
+}
+
+TransportLayerIce::~TransportLayerIce() {
+  // No need to do anything here, since we use smart pointers
+}
+
+TransportResult TransportLayerIce::SendPacket(const unsigned char *data,
+                                              size_t len) {
+  nsresult res = stream_->SendPacket(component_, data, len);
+
+  if (!NS_SUCCEEDED(res)) {
+    return (res == NS_BASE_STREAM_WOULD_BLOCK) ?
+        TE_WOULDBLOCK : TE_ERROR;
+  }
+
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << " SendPacket(" << len << ") succeeded");
+
+  return len;
+}
+
+
+void TransportLayerIce::IceCandidate(NrIceMediaStream *stream,
+                                     const std::string&) {
+  // NO-OP for now
+}
+
+void TransportLayerIce::IceReady(NrIceMediaStream *stream) {
+  SetState(TS_OPEN);
+}
+
+void TransportLayerIce::IceFailed(NrIceMediaStream *stream) {
+  SetState(TS_ERROR);
+}
+
+void TransportLayerIce::IcePacketReceived(NrIceMediaStream *stream, int component,
+                       const unsigned char *data, int len) {
+  // We get packets for both components, so ignore the ones that aren't
+  // for us.
+  if (component_ != component)
+    return;
+
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "PacketReceived(" << stream->name() << ","
+    << component << "," << len << ")");
+  SignalPacketReceived(this, data, len);
+}
+
+}  // close namespace
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayerice.h
@@ -0,0 +1,62 @@
+/* -*- 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
+
+// This is a wrapper around the nICEr ICE stack
+#ifndef transportlayerice_h__
+#define transportlayerice_h__
+
+#include <vector>
+
+#include "sigslot.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/Scoped.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsITimer.h"
+
+#include "m_cpp_utils.h"
+
+#include "nricemediastream.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+// An ICE transport layer -- corresponds to a single ICE
+namespace mozilla {
+
+class TransportLayerIce : public TransportLayer {
+ public:
+  TransportLayerIce(const std::string& name,
+                    RefPtr<NrIceCtx> ctx,
+                    RefPtr<NrIceMediaStream> stream,
+                    int component);
+  virtual ~TransportLayerIce();
+
+  // Transport layer overrides.
+  virtual TransportResult SendPacket(const unsigned char *data, size_t len);
+
+  // 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);
+
+  TRANSPORT_LAYER_ID("ice")
+
+ private:
+  DISALLOW_COPY_ASSIGN(TransportLayerIce);
+
+  const std::string name_;
+  RefPtr<NrIceCtx> ctx_;
+  RefPtr<NrIceMediaStream> stream_;
+  int component_;
+};
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayerlog.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 "logging.h"
+#include "transportflow.h"
+#include "transportlayerlog.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport");
+
+void TransportLayerLogging::WasInserted() {
+  if (downward_) {
+    downward_->SignalStateChange.connect(
+        this, &TransportLayerLogging::StateChange);
+    downward_->SignalPacketReceived.connect(
+        this, &TransportLayerLogging::PacketReceived);
+    SetState(downward_->state());
+  }
+}
+
+TransportResult
+TransportLayerLogging::SendPacket(const unsigned char *data, size_t len) {
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "SendPacket(" << len << ")");
+
+  if (downward_) {
+    return downward_->SendPacket(data, len);
+  }
+  else {
+    return static_cast<TransportResult>(len);
+  }
+}
+
+void TransportLayerLogging::StateChange(TransportLayer *layer, State state) {
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "Received StateChange to " << state);
+
+  SetState(state);
+}
+
+void TransportLayerLogging::PacketReceived(TransportLayer* layer,
+                                           const unsigned char *data,
+                                           size_t len) {
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "PacketReceived(" << len << ")");
+
+  SignalPacketReceived(this, data, len);
+}
+
+
+
+}  // close namespace
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayerlog.h
@@ -0,0 +1,41 @@
+/* -*- 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 transportlayerlog_h__
+#define transportlayerlog_h__
+
+#include "m_cpp_utils.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+namespace mozilla {
+
+class TransportLayerLogging : public TransportLayer {
+public:
+  TransportLayerLogging() {}
+
+  // Overrides for TransportLayer
+  virtual TransportResult SendPacket(const unsigned char *data, size_t len);
+
+  // Signals (forwarded to upper layer)
+  void StateChange(TransportLayer *layer, State state);
+  void PacketReceived(TransportLayer* layer, const unsigned char *data,
+                      size_t len);
+
+  TRANSPORT_LAYER_ID("log")
+
+protected:
+  virtual void WasInserted();
+
+private:
+  DISALLOW_COPY_ASSIGN(TransportLayerLogging);
+};
+
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayerloopback.cpp
@@ -0,0 +1,122 @@
+/* -*- 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 "nspr.h"
+#include "prlock.h"
+
+#include "nsNetCID.h"
+#include "nsIComponentManager.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIEventTarget.h"
+#include "nsIIOService.h"
+#include "nsIServiceManager.h"
+#include "nsISocketTransportService.h"
+#include "nsServiceManagerUtils.h"
+
+#include "logging.h"
+#include "transportflow.h"
+#include "transportlayerloopback.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport");
+
+nsresult TransportLayerLoopback::Init() {
+  timer_ = do_CreateInstance(NS_TIMER_CONTRACTID);
+  MOZ_ASSERT(timer_);
+  if (!timer_)
+    return NS_ERROR_FAILURE;
+
+  nsresult rv;
+  target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  if (!NS_SUCCEEDED(rv))
+    return rv;
+
+  timer_->SetTarget(target_);
+
+  packets_lock_ = PR_NewLock();
+  MOZ_ASSERT(packets_lock_);
+  if (!packets_lock_)
+    return NS_ERROR_FAILURE;
+
+  deliverer_ = new Deliverer(this);
+
+  timer_->InitWithCallback(deliverer_, 100, nsITimer::TYPE_REPEATING_SLACK);
+
+  return NS_OK;
+}
+
+// Connect to the other side
+void TransportLayerLoopback::Connect(TransportLayerLoopback* peer) {
+  peer_ = peer;
+
+  SetState(TS_OPEN);
+}
+
+TransportResult
+TransportLayerLoopback::SendPacket(const unsigned char *data, size_t len) {
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << "SendPacket(" << len << ")");
+
+  if (!peer_) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Discarding packet because peer not attached");
+    return TE_ERROR;
+  }
+
+  nsresult res = peer_->QueuePacket(data, len);
+  if (!NS_SUCCEEDED(res))
+    return TE_ERROR;
+
+  return static_cast<TransportResult>(len);
+}
+
+nsresult TransportLayerLoopback::QueuePacket(const unsigned char *data,
+                                         size_t len) {
+  MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << " Enqueuing packet of length " << len);
+  MOZ_ASSERT(packets_lock_);
+
+  PR_Lock(packets_lock_);
+
+  packets_.push(new QueuedPacket());
+  packets_.back()->Assign(data, len);
+
+  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();
+    packets_.pop();
+
+    MOZ_MTLOG(PR_LOG_DEBUG, LAYER_INFO << " Delivering packet of length " <<
+         packet->len());
+    SignalPacketReceived(this, packet->data(), packet->len());
+
+    delete packet;
+  }
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(TransportLayerLoopback::Deliverer,
+                              nsITimerCallback)
+
+NS_IMETHODIMP TransportLayerLoopback::Deliverer::Notify(nsITimer *timer) {
+  if (!layer_)
+    return NS_OK;
+
+  layer_->DeliverPackets();
+
+  return NS_OK;
+}
+}  // close namespace
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayerloopback.h
@@ -0,0 +1,140 @@
+/* -*- 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 transportlayerloopback_h__
+#define transportlayerloopback_h__
+
+#include "nspr.h"
+#include "prio.h"
+#include "prlock.h"
+
+#include <memory>
+#include <queue>
+
+
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsITimer.h"
+
+
+#include "m_cpp_utils.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+// A simple loopback transport layer that is used for testing.
+namespace mozilla {
+
+class TransportLayerLoopback : public TransportLayer {
+ public:
+  TransportLayerLoopback() :
+      peer_(nullptr),
+      timer_(nullptr),
+      target_(nullptr),
+      packets_(),
+      packets_lock_(nullptr),
+      deliverer_(nullptr) {}
+
+  ~TransportLayerLoopback() {
+    while (!packets_.empty()) {
+      QueuedPacket *packet = packets_.front();
+      packets_.pop();
+      delete packet;
+    }
+    if (packets_lock_) {
+      PR_DestroyLock(packets_lock_);
+    }
+    timer_->Cancel();
+    deliverer_->Detach();
+  }
+
+  // Init
+  nsresult Init();
+
+  // Connect to the other side
+  void Connect(TransportLayerLoopback* peer);
+
+  // Disconnect
+  void Disconnect() {
+    TransportLayerLoopback *peer = peer_;
+
+    peer_ = nullptr;
+    if (peer) {
+      peer->Disconnect();
+    }
+  }
+
+  // Overrides for TransportLayer
+  virtual TransportResult SendPacket(const unsigned char *data, size_t len);
+
+  // 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;