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 109578 a8fac69129b0bac8b55e33574f4c4338d494b150
parent 109577 da39ec3b97783342c892e6a9b8abaedeb372d8fc
child 109579 2b08abdcc8a9de9e9b03d5e6f8961d1103376225
push id1145
push userpastithas@mozilla.com
push dateMon, 08 Oct 2012 14:21:16 +0000
treeherderfx-team@e7f2e2c944b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, bsmith, mcmanus
bugs790517
milestone18.0a1
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;
+    }
+
+    const unsigned char *data() const { return data_; }
+    size_t len() const { return len_; }
+
+   private:
+    DISALLOW_COPY_ASSIGN(QueuedPacket);
+
+    unsigned char *data_;
+    size_t len_;
+  };
+
+  // A timer to deliver packets if some are available
+  // Fires every 100 ms
+  class Deliverer : public nsITimerCallback {
+   public:
+    Deliverer(TransportLayerLoopback *layer) :
+        layer_(layer) {}
+    virtual ~Deliverer() {
+    }
+    void Detach() {
+      layer_ = nullptr;
+    }
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSITIMERCALLBACK
+
+ private:
+    DISALLOW_COPY_ASSIGN(Deliverer);
+
+    TransportLayerLoopback *layer_;
+  };
+
+  // Queue a packet for delivery
+  nsresult QueuePacket(const unsigned char *data, size_t len);
+
+  TransportLayerLoopback* peer_;
+  nsCOMPtr<nsITimer> timer_;
+  nsCOMPtr<nsIEventTarget> target_;
+  std::queue<QueuedPacket *> packets_;
+  PRLock *packets_lock_;
+  nsRefPtr<Deliverer> deliverer_;
+};
+
+}  // close namespace
+#endif
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayerprsock.cpp
@@ -0,0 +1,116 @@
+/* -*- 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 "prerror.h"
+#include "prio.h"
+
+#include "nsCOMPtr.h"
+#include