[asterisk-scf-commits] asterisk-scf/integration/sip.git branch "client-registration" updated.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Wed Oct 5 17:04:14 CDT 2011


branch "client-registration" has been updated
       via  0ca99918a255c76ca856ce1a1f4090b6d18834c2 (commit)
       via  929a1cff9ce65f6e5b18f3d0dc0cf761b3683f47 (commit)
       via  1094f862471a5cdc8955aecca1bbbeebba31b44c (commit)
       via  5e0ba080e24eaa73a77601f50733047fc4b4aff3 (commit)
       via  83d4e0737de244e6c1a64e8dac6dd12c04f8ec96 (commit)
       via  030ca07ce06155cc17518f07099650ab7b862d4a (commit)
       via  d686a273766e9ee293d9050c9dbfc3748b62eeaf (commit)
       via  d1c51b7c05833b65bffdc3599253dd6690580d34 (commit)
       via  bf97df459f0c054d095331534252ec3755abc1e2 (commit)
       via  74d1103527d97fa8cd5887ab510a748b20ceb8ff (commit)
       via  052d08061987f9d76a4bfa5784db4de1e003f672 (commit)
       via  ea9c1132ca848007552fa5763ec4a364e6e51e1b (commit)
       via  4558877dfa0be63ee1a06dad330c6a6dda84d147 (commit)
       via  1d5c2ca973d7e766cb128c1aa301757dbfc5c196 (commit)
       via  4a34134e7ea1733c0d76de33af0e7b7fdbecd4aa (commit)
       via  90925e5939f603e874103fd7731f5957febf7b90 (commit)
       via  8e0f48d19b059e7d26fcbd27ace7a0f34e759ef8 (commit)
       via  ee3a950ea3930a81c156217bf614f5b0512fc0aa (commit)
       via  b6345950d2a4f1c61c986232ca0b225eb84f4296 (commit)
       via  c18c1c9ca971b3b08c0224f059c45052e71f636a (commit)
       via  ec01c62e89fc764c104d4552891d366db52ad9b9 (commit)
       via  1c329a070da3354941507edd495c84d1cd0535ca (commit)
       via  8e37073a5318103b06dec8e241088244968a77fb (commit)
       via  712a66be46d2e2944440e097f88568182b405f4d (commit)
       via  57aae578b562047e1e8fa949a2ca597d73f36645 (commit)
      from  7cf2d5832e61940212efbd808a54a1135ace49a6 (commit)

Summary of changes:
 config/Sip.config                                  |   90 +
 config/SipConfigurator.py                          |   96 +-
 config/test_sip.conf                               |    4 +-
 .../SipSessionManager/SipConfigurationIf.ice       |   68 +-
 .../SipSessionManager/SipStateReplicationIf.ice    |    7 +-
 src/CMakeLists.txt                                 |    2 +-
 src/Component.cpp                                  |  118 +-
 src/NATOptions.h                                   |    6 +-
 src/PJSipLoggingModule.cpp                         |    2 +-
 src/PJSipManager.cpp                               |   16 +-
 src/PJSipManager.h                                 |   66 +-
 src/PJSipModule.cpp                                |    2 +-
 src/PJSipRegistrarModule.cpp                       |   12 +-
 src/PJSipSessionModule.cpp                         |   21 +-
 src/PJSipSessionModule.h                           |    7 +-
 src/PJSipSessionModuleConstruction.cpp             |    2 +-
 src/STUNModule.cpp                                 |    7 +-
 src/STUNModule.h                                   |    2 +-
 src/STUNTransport.cpp                              |    2 +-
 src/SipClientRegistration.cpp                      |    2 +-
 src/SipConfiguration.cpp                           |  247 +++-
 src/SipEndpoint.cpp                                |  316 +++-
 src/SipEndpoint.h                                  |   34 +-
 src/SipEndpointFactory.cpp                         |    8 +-
 src/SipEndpointFactory.h                           |    8 +-
 src/SipRegistrarListener.cpp                       |    2 +-
 src/SipSession.cpp                                 | 1865 ++++++++++++++++----
 src/SipSession.h                                   |  113 +-
 src/SipSessionManagerApp.cpp                       |  709 --------
 src/SipSessionManagerEndpointLocator.cpp           |    2 +-
 src/SipSessionManagerEventPublisher.cpp            |    2 +-
 src/SipStateReplicatorApp.cpp                      |   21 +-
 src/SipStateReplicatorListener.cpp                 |    3 +-
 src/SipTelephonyEventSource.cpp                    |   84 +-
 src/SipTelephonyEventSource.h                      |   24 +-
 src/SipTransfer.cpp                                |    6 +-
 36 files changed, 2586 insertions(+), 1390 deletions(-)
 delete mode 100644 src/SipSessionManagerApp.cpp


- Log -----------------------------------------------------------------
commit 0ca99918a255c76ca856ce1a1f4090b6d18834c2
Author: Mark Michelson <mmichelson at digium.com>
Date:   Wed Oct 5 17:02:55 2011 -0500

    Clarify documentation and change name of the method used to respond to an authentication challenge.

diff --git a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
index 6b74279..49f0d96 100644
--- a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
+++ b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
@@ -591,7 +591,7 @@ struct ContactInfo
 
 sequence<ContactInfo> ContactInfoSeq;
 
-const int DefaultExpiration = 3600;
+const int DefaultRegistrationExpirationSeconds = 3600;
 
 class SipClientRegistrationItem extends SipConfigurationItem
 {
@@ -606,8 +606,8 @@ class SipClientRegistrationItem extends SipConfigurationItem
     ContactInfoSeq contacts;
     /**
      * The expiration to use for any contacts
-     * with no explicit expiration set. Units
-     * for expiration are seconds.
+     * with no explicit expiration set (i.e. a 0 expiration).
+     * Units for expiration are seconds.
      */
     int defaultExpiration;
 };
diff --git a/src/SipClientRegistration.cpp b/src/SipClientRegistration.cpp
index ba624f7..a4e03d5 100644
--- a/src/SipClientRegistration.cpp
+++ b/src/SipClientRegistration.cpp
@@ -373,7 +373,7 @@ void SipRegistrationClient::authenticate(pjsip_rx_data *rdata)
         ClientAuthSeq auths;
         Ice::StringSeq realms = getRealms(rdata);
 
-        HookResult result = (*iter)->getCredentials(mEndpointName, realms, auths);
+        HookResult result = (*iter)->respondToChallenge(mEndpointName, realms, auths);
         if (result.status == Succeeded)
         {
             std::vector<pjsip_cred_info> creds;

commit 929a1cff9ce65f6e5b18f3d0dc0cf761b3683f47
Merge: 5e0ba08 1094f86
Author: Mark Michelson <mmichelson at digium.com>
Date:   Wed Oct 5 11:10:44 2011 -0500

    Merge branch 'master' into client-registration


commit 1094f862471a5cdc8955aecca1bbbeebba31b44c
Author: Joshua Colp <jcolp at digium.com>
Date:   Wed Oct 5 12:56:44 2011 -0300

    Only initialize pjlib once.

diff --git a/src/Component.cpp b/src/Component.cpp
index 37e6a7f..0e0a591 100644
--- a/src/Component.cpp
+++ b/src/Component.cpp
@@ -414,11 +414,6 @@ void Component::onPreInitialize()
 {
     try
     {
-        // Initialize PJSIP
-        // NOTE: Should use PJSipManager::create now.
-        mPJSipManager = PJSipManager::create(getName(), getCommunicator()->getProperties());
-        lg(Debug) << "Created PJSIP manager";
-
         //As nice as it is of IceBox to provide us with a communicator,
         //we're going to create our own so that we can provide it with a threadhook.
         //Yes, this could be done via a plugin, but this is easier. Go away.
@@ -426,6 +421,11 @@ void Component::onPreInitialize()
         id.threadHook = new AsteriskSCF::PJLib::ThreadHook("Ice");
         id.properties = getCommunicator()->getProperties();
 
+        // Initialize PJSIP
+        // NOTE: Should use PJSipManager::create now.
+        mPJSipManager = PJSipManager::create(getName(), getCommunicator()->getProperties());
+        lg(Debug) << "Created PJSIP manager";
+
         setCommunicator(Ice::initialize(id));
     }
     catch(const std::exception& e)
diff --git a/src/PJSipManager.cpp b/src/PJSipManager.cpp
index e4f1054..803edac 100644
--- a/src/PJSipManager.cpp
+++ b/src/PJSipManager.cpp
@@ -267,13 +267,6 @@ PJSipManager::PJSipManager() :
 {
     memset(&mCachingPool, 0, sizeof(mCachingPool));
 
-    pj_status_t status = pj_init();
-    if (fail(status))
-    {
-        const char* message = "Failed to Initialize PJSIP";
-        logger(Error) << message;
-        throw InternalInitializationException(message);
-    }
     // The third parameter is just copied from
     // example code from PJLIB. This can be adjusted
     // if necessary.
@@ -291,7 +284,7 @@ PJSipManager::PJSipManager() :
         throw InternalInitializationException(message);
     }
     
-    status = pj_thread_create(mMemoryPool, "SIP", (pj_thread_proc *) &monitorThread,
+    pj_status_t status = pj_thread_create(mMemoryPool, "SIP", (pj_thread_proc *) &monitorThread,
         this, PJ_THREAD_DEFAULT_STACK_SIZE * 2, 0, &mPjThread);
     if (fail(status))
     {

commit 5e0ba080e24eaa73a77601f50733047fc4b4aff3
Merge: 83d4e07 030ca07
Author: Mark Michelson <mmichelson at digium.com>
Date:   Tue Oct 4 18:25:42 2011 -0500

    Merge branch 'master' into client-registration
    
    Conflicts:
    	src/Component.cpp


commit 83d4e0737de244e6c1a64e8dac6dd12c04f8ec96
Author: Mark Michelson <mmichelson at digium.com>
Date:   Tue Oct 4 17:34:42 2011 -0500

    Set the replica proxy in the endpoint factory after it has actually been initialized.

diff --git a/src/Component.cpp b/src/Component.cpp
index 405ee50..53974e8 100644
--- a/src/Component.cpp
+++ b/src/Component.cpp
@@ -529,8 +529,7 @@ void Component::createPrimaryServices()
                     getBackplaneAdapter(),
                     mPJSipManager,
                     getServiceLocator(),
-                    sipReplicationContext,
-                    getReplicaProxy()));
+                    sipReplicationContext));
         lg(Debug) << "Created SIP endpoint factory";
 
 	    // Locate the Routing Service so that we can do routing. This is done here so it can be
@@ -575,6 +574,10 @@ void Component::createBackplaneServices()
             getBackplaneAdapter()->addWithUUID(mConfigurationService));
         lg(Debug) << "Created SIP Configuration Implementation";
 
+        // We have to wait until this point to add the replica proxy to
+        // the endpoint factory because prior to this point the replica hasn't
+        // been created.
+        mEndpointFactory->setReplicaProxy(getReplicaProxy());
     }
     catch(const Ice::Exception& e)
     {
diff --git a/src/SipConfiguration.cpp b/src/SipConfiguration.cpp
index ab28759..a505fff 100644
--- a/src/SipConfiguration.cpp
+++ b/src/SipConfiguration.cpp
@@ -1888,9 +1888,7 @@ void ConfigurationServiceImpl::setConfiguration(const AsteriskSCF::System::Confi
 
         void visitSipRegistrationGroup(const SipRegistrationGroupPtr& group)
         {
-            std::cout << "!!!!!!!!!!!!!! How's it going? !!!!!!!!!!!!" << std::endl;
             genericSet<SipRegistrationGroupPtr>(mImpl->getData(), group);
-            std::cout << "!!!!!!!!!!!!!! FINE? !!!!!!!!!!!!" << std::endl;
         }
         void visitIdentityGroup(const IdentityGroupPtr& group)
         {
diff --git a/src/SipEndpointFactory.cpp b/src/SipEndpointFactory.cpp
index 2be256d..4b8682c 100644
--- a/src/SipEndpointFactory.cpp
+++ b/src/SipEndpointFactory.cpp
@@ -20,6 +20,7 @@
 #include "SipEndpointFactory.h"
 
 using namespace AsteriskSCF::System::Logging;
+using namespace AsteriskSCF::System::Component::V1;
 
 namespace
 {
@@ -77,5 +78,10 @@ void SipEndpointFactory::generateRoutingDestinations(AsteriskSCF::Core::Routing:
     }
 }
 
+void SipEndpointFactory::setReplicaProxy(const ReplicaPrx& replica)
+{
+    mReplica = replica;
+}
+
 }; // end SipSessionManager
 }; // end AsteriskSCF
diff --git a/src/SipEndpointFactory.h b/src/SipEndpointFactory.h
index fbefdd2..7d792b5 100644
--- a/src/SipEndpointFactory.h
+++ b/src/SipEndpointFactory.h
@@ -43,14 +43,12 @@ public:
             const Ice::ObjectAdapterPtr& backplaneAdapter,
             const PJSipManagerPtr& manager,
             const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
-            const SipReplicationContextPtr& replicationContext,
-            const AsteriskSCF::System::Component::V1::ReplicaPrx& replica) :
+            const SipReplicationContextPtr& replicationContext) :
         mAdapter(adapter),
         mBackplaneAdapter(backplaneAdapter),
         mManager(manager),
         mServiceLocator(serviceLocator),
-        mReplicationContext(replicationContext),
-        mReplica(replica){};
+        mReplicationContext(replicationContext) { }
 
     SipEndpointPtr createEndpoint(std::string);
 
@@ -60,6 +58,8 @@ public:
 
     void generateRoutingDestinations(AsteriskSCF::Core::Routing::V1::RegExSeq&);
 
+    void setReplicaProxy(const AsteriskSCF::System::Component::V1::ReplicaPrx& replica);
+
 private:
     /**
      * A pointer to the object adapter that endpoints will be added to.

commit 030ca07ce06155cc17518f07099650ab7b862d4a
Author: Ken Hunt <ken.hunt at digium.com>
Date:   Tue Oct 4 12:07:29 2011 -0500

    Passing the component's ComponentService discovery category to the base Component (in constructor), namespace flattening was causing it to select the wrong category.

diff --git a/src/Component.cpp b/src/Component.cpp
index 1d63c04..37e6a7f 100644
--- a/src/Component.cpp
+++ b/src/Component.cpp
@@ -112,7 +112,7 @@ class Component : public AsteriskSCF::Component::Component
 {
 public:
     Component()
-        :  AsteriskSCF::Component::Component(lg, ComponentServiceDiscoveryCategory),
+        :  AsteriskSCF::Component::Component(lg, AsteriskSCF::SIP::V1::ComponentServiceDiscoveryCategory),
            mListeningToReplicator(false)
     {
     }

commit d686a273766e9ee293d9050c9dbfc3748b62eeaf
Merge: b634595 ee3a950
Author: Mark Michelson <mmichelson at digium.com>
Date:   Tue Oct 4 10:55:45 2011 -0500

    Merge branch 'master' into client-registration

diff --cc src/SipEndpoint.cpp
index 3371038,2e59ea0..822fbe7
--- a/src/SipEndpoint.cpp
+++ b/src/SipEndpoint.cpp
@@@ -327,75 -319,19 +338,86 @@@ void SipEndpoint::setSecureTransport(en
  void SipEndpoint::setDTMFMethod(AsteriskSCF::Configuration::SipSessionManager::V1::SipDTMFOption dtmf)
  {
      mImplPriv->mConfig.sessionConfig.dtmf = dtmf;
+     if (mImplPriv->mConfig.sessionConfig.dtmf ==
+             AsteriskSCF::Configuration::SipSessionManager::V1::RFC4733)
+     {
+         //For RFC 4733, we need to include information in the SDP about
+         //the format, so we need to add a new format to the endpoint's
+         //configured formats.
+ 
+         //XXX For now, we're just adding some hard-coded values. We should probably
+         //be a bit more flexible here in the face of changes or something.
+         addFormat("rfc4733", 8000, 0, Ice::StringSeq());
+     }
  }
  
 +class MatchRegistration
 +{
 +public:
 +    MatchRegistration(const std::string& aor)
 +        : mAOR(aor) { }
 +
 +    bool operator()(const SipClientRegistrationItemPtr& item)
 +    {
 +        return item->aor == mAOR;
 +    }
 +private:
 +    const std::string mAOR;
 +};
 +
 +void SipEndpoint::updateClientRegistrations(SipClientRegistrationItemSeq& items)
 +{
 +    lg(Debug) << "Updating client registrations for endpoint " << mImplPriv->mName;
 +
 +    //First step is to find registrations that exist for this endpoint but
 +    //that no longer appear in configuration. These must be KILLED!!!!!
 +    
 +    for (std::map<std::string, SipRegistrationClientPtr>::iterator iter = mImplPriv->mClientRegistrations.begin(); 
 +            iter != mImplPriv->mClientRegistrations.end(); )
 +    {
 +        SipClientRegistrationItemSeq::iterator toRemove = std::find_if(items.begin(), items.end(), MatchRegistration(iter->first));
 +        if (toRemove != items.end())
 +        {
 +            iter->second->destroy();
 +            mImplPriv->mClientRegistrations.erase(iter++);
 +        }
 +        else
 +        {
 +            ++iter;
 +        }
 +    }
 +
 +    //Next we go through the configured registrations and either update existing
 +    //client registrations or create new ones.
 +    for (SipClientRegistrationItemSeq::iterator item = items.begin();
 +            item != items.end(); ++item)
 +    {
 +        std::map<std::string, SipRegistrationClientPtr>::iterator iter =
 +            mImplPriv->mClientRegistrations.find((*item)->aor);
 +
 +        if (iter != mImplPriv->mClientRegistrations.end())
 +        {
 +            //Updating one that exists
 +            iter->second->updateRegistration(*item, mImplPriv->mManager->getEndpoint(), this);
 +        }
 +        else
 +        {
 +            //New one!
 +            mImplPriv->mClientRegistrations[(*item)->aor] =
 +                new SipRegistrationClient(
 +                        *item,
 +                        mImplPriv->mManager->getEndpoint(),
 +                        this,
 +                        mImplPriv->mManager->getClientRegistrationManager(),
 +                        mImplPriv->mReplicationContext,
 +                        mImplPriv->mBackplaneAdapter,
 +                        mImplPriv->mReplica);
 +
 +            mImplPriv->mClientRegistrations[(*item)->aor]->activate();
 +        }
 +    }
 +}
 +
  void SipEndpoint::setRTPOverIPv6(bool enabled)
  {
      mImplPriv->mConfig.sessionConfig.rtpOverIPv6 = enabled;

commit d1c51b7c05833b65bffdc3599253dd6690580d34
Author: Joshua Colp <jcolp at digium.com>
Date:   Mon Oct 3 20:04:29 2011 -0300

    Add T.38 UDPTL support.

diff --git a/config/Sip.config b/config/Sip.config
index 7946dba..e72b415 100644
--- a/config/Sip.config
+++ b/config/Sip.config
@@ -89,8 +89,32 @@ direction=both
 securetransport=none
 # Whether to use IPv6 for media transport or not
 rtpoveripv6=no
+# Whether to use IPv6 for UDPTL transport or not
+udptloveripv6=no
+
+#
+# Configure endpoint to create ICE enabled UDPTL streams. Disabled by default.
+#
+# udptloverice=false
+
+#
+# Configure endpoint to enable TURN support for ICE enabled UDPTL streams. ICE must be enabled
+# for this to have an effect. Disable by default.
+#
+# udptlwithturn=false
+
 # Allowable media formats for the endpoint. Each format is separated using , and follows the format
 # <name>/<sample rate>@<frame size>;<format specific parameters>
+#
+# If you would like to enable T.38 you can specify t38udptl here. This will enable T.38 with no overridden
+# datagram and no error correction scheme. To override the datagram add maxdatagram with a value in the format
+# specific parameters section. To enable error correction add errorcorrect with either fec, redundancy, or none
+# as the value.
+#
+# Example of T.38 with an overridden max datagram of 400: t38udptl;maxdatagram=400
+# Example of T.38 with fec error correction: t38udptl;errorcorrect=fec
+# Example of T.38 with overridden max datagram and fec error correction: t38udptl;maxdatagram=400&errorcorrect=fec
+#
 formats=ulaw/8000,alaw/8000
 
 # Whether media should be allowed to flow directly between endpoints or not
diff --git a/config/SipConfigurator.py b/config/SipConfigurator.py
index 5505f2a..f6c09b7 100755
--- a/config/SipConfigurator.py
+++ b/config/SipConfigurator.py
@@ -206,6 +206,11 @@ class SipSectionVisitors(Configurator.SectionVisitors):
         item = AsteriskSCF.Configuration.SipSessionManager.V1.DirectMediaItem()
         mapper.map('directmedia', item, 'enabled', 'directmedia', config.getboolean, None)
 
+        item = AsteriskSCF.Configuration.SipSessionManager.V1.SipUDPTLMediaServiceItem()
+        mapper.map('udptloveripv6', item, 'requireIPv6', 'udptlmediaservice', config.getboolean, None)
+        mapper.map('udptloverice', item, 'enableICE', 'udptlmediaservice', config.getboolean, None)
+        mapper.map('udptlwithturn', item, 'enableTURN', 'udptlmediaservice', config.getboolean, None)
+
         item = AsteriskSCF.Configuration.SipSessionManager.V1.SipCryptoCertificateItem()
         mapper.map('certificateauthorityfile', item, 'certificateAuthority', 'cryptocert', config.get, None)
         mapper.map('certificatefile', item, 'certificate', 'cryptocert', config.get, None)
@@ -263,21 +268,43 @@ class SipSectionVisitors(Configurator.SectionVisitors):
             formats = config.get(section, 'formats')
             configuredFormats = formats.split(',')
             for format in configuredFormats:
-                name, found, rest = format.partition('/')
-                sampleRate, found, rest = rest.partition('@')
-                frameSize, found, formatSpecific = rest.partition(';')
-
                 item = AsteriskSCF.Configuration.SipSessionManager.V1.SipMediaFormatItem()
-                item.name = name
-                if sampleRate:
-                    item.sampleRate = sampleRate
-                if frameSize:
-                    item.frameSize = frameSize
+
+                front, found, rest = format.partition('/')
+                if found:
+                    item.name = front
+                else:
+                    rest = front
+
+                front, found, rest = rest.partition('@')
+                if found:
+                    if item.name:
+                        item.sampleRate = front
+                    else:
+                        item.name = front
+                else:
+                    rest = front
+
+                front, found, rest = rest.partition(';')
+                if found:
+                    if item.name:
+                        item.frameSize = front
+                    else:
+                        item.name = front
+                else:
+                    rest = front
+
                 item.formatSpecific = [ ]
-                if formatSpecific:
-                    item.formatSpecific.append(formatSpecific)
 
-                group.configurationItems[format] = item
+                if not item.name:
+                    item.name = format
+
+                if item.name:
+                    while rest:
+                        front, found, rest = rest.partition('&')
+                        item.formatSpecific.append(front)
+
+                    group.configurationItems[format] = item
         except:
             print 'No configured formats for endpoint ' + section
 
diff --git a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
index c22c91b..3641291 100644
--- a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
+++ b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
@@ -334,6 +334,32 @@ class SipRTPMediaServiceItem extends SipConfigurationItem
 };
 
 /**
+ * UDPTL Media service configuration item
+ */
+class SipUDPTLMediaServiceItem extends SipConfigurationItem
+{
+    /**
+     * Name of the UDPTL media service to use
+     */
+    string mediaServiceName;
+
+    /**
+     * Whether to choose an IPv6 UDPTL media service or not
+     */
+    bool requireIPv6 = false;
+
+    /**
+     * Boolean for whether ICE is enabled
+     */
+    bool enableICE = false;
+
+    /**
+     * Boolean for whether TURN is enabled
+     */
+    bool enableTURN = false;
+};
+
+/**
  * Signaling NAT configuration item
  */
 class SipSignalingNATItem extends SipConfigurationItem
diff --git a/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice b/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice
index f982668..f9eab87 100644
--- a/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice
+++ b/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice
@@ -19,6 +19,7 @@
 #include <Ice/Identity.ice>
 #include <AsteriskSCF/Media/MediaIf.ice>
 #include <AsteriskSCF/Media/RTP/MediaRTPIf.ice>
+#include <AsteriskSCF/Media/UDPTL/MediaUDPTLIf.ice>
 #include <AsteriskSCF/SessionCommunications/SessionCommunicationsIf.ice>
 #include <AsteriskSCF/Core/Discovery/ServiceLocatorIf.ice>
 #include <AsteriskSCF/System/Component/ConfigurationIf.ice>
@@ -140,6 +141,7 @@ module V1
    };
 
    dictionary<string, AsteriskSCF::Media::RTP::V1::RTPSession*> RTPMediaSessionDict;
+   sequence<AsteriskSCF::Media::UDPTL::V1::UDPTLSession*> UDPTLMediaSessionSeq;
 
    class SipSessionStateItem extends SipStateItem
    {
@@ -153,6 +155,7 @@ module V1
       AsteriskSCF::Media::V1::StreamSinkSeq sinks;
       AsteriskSCF::Media::V1::StreamInformationDict streams;
       RTPMediaSessionDict rtpMediaSessions;
+      UDPTLMediaSessionSeq udptlMediaSessions;
       AsteriskSCF::SessionCommunications::V1::SessionListenerSeq listeners;
       AsteriskSCF::SessionCommunications::V1::Bridge *bridge;
       AsteriskSCF::SessionCommunications::V1::SessionCookieDict cookies;
diff --git a/src/NATOptions.h b/src/NATOptions.h
index 2319c50..5c71700 100644
--- a/src/NATOptions.h
+++ b/src/NATOptions.h
@@ -27,9 +27,11 @@ struct NATEndpointOptions
     bool enableICE;
     bool enableTURN;
     bool enableSIPSTUN;
+    bool enableUDPTLICE;
+    bool enableUDPTLTURN;
 
-    NATEndpointOptions(bool ice, bool turn, bool sip) :
-        enableICE(ice), enableTURN(turn), enableSIPSTUN(sip) 
+    NATEndpointOptions(bool ice, bool turn, bool sip, bool udptlice, bool udptlturn) :
+        enableICE(ice), enableTURN(turn), enableSIPSTUN(sip), enableUDPTLICE(udptlice), enableUDPTLTURN(udptlturn)
     {
     }
 };
diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index 3b97fd4..9504d47 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -108,6 +108,7 @@ void PJSipSessionModInfo::updateSessionState(pjsip_inv_session *inv_session)
         mSessionState->sources = mSession->getMediaSources();
         mSessionState->sinks = mSession->getMediaSinks();
         mSessionState->rtpMediaSessions = mSession->getRTPMediaSessions();
+        mSessionState->udptlMediaSessions = mSession->getUDPTLMediaSessions();
         mSessionState->listeners = mSession->getListeners();
 	    mSessionState->streams = mSession->getStreams();
         try
@@ -1508,14 +1509,8 @@ private:
     ServiceLocatorPrx mServiceLocator;
 };
 
-void PJSipSessionModule::invOnMediaUpdate(pjsip_inv_session *inv, pj_status_t status)
+void PJSipSessionModule::invOnMediaUpdate(pjsip_inv_session *inv, pj_status_t)
 {
-    if (status != PJ_SUCCESS)
-    {
-        // We have nothing, zip, nada, kablamo, in common.
-        return;
-    }
-
     lg(Debug) << "Queuing HandleMediaUpdate";
     enqueueSessionWork(new HandleMediaUpdate(inv, mModule.id, mServiceLocator), inv);
 }
diff --git a/src/SipConfiguration.cpp b/src/SipConfiguration.cpp
index f4c07ef..cd00465 100644
--- a/src/SipConfiguration.cpp
+++ b/src/SipConfiguration.cpp
@@ -310,6 +310,11 @@ class EndpointConfigHelper : public boost::enable_shared_from_this<EndpointConfi
 	    mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateMediaService, mConfig, service));
 	};
 
+        void visitSipUDPTLMediaServiceItem(const SipUDPTLMediaServiceItemPtr& service)
+        {
+            mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateUDPTLMediaService, mConfig, service));
+        };
+
 	void visitSipEndpointTransportItem(const SipEndpointTransportItemPtr& transport)
 	{
 	    mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateTransport, mConfig, transport));
@@ -460,6 +465,13 @@ public:
 	mEndpoint->setRTPOverIPv6(service->requireIPv6);
     }
 
+    void updateUDPTLMediaService(const SipUDPTLMediaServiceItemPtr& service)
+    {
+        mEndpoint->setUDPTLOverIPv6(service->requireIPv6);
+        mEndpoint->enableUDPTLICE(service->enableICE);
+        mEndpoint->enableUDPTLTURN(service->enableTURN);
+    }
+
     void updateTransport(const SipEndpointTransportItemPtr& transport)
     {
 	mEndpoint->setSecureTransport(translateCallDirection(transport->secureTransport));
diff --git a/src/SipEndpoint.cpp b/src/SipEndpoint.cpp
index d017a06..f03e3ac 100644
--- a/src/SipEndpoint.cpp
+++ b/src/SipEndpoint.cpp
@@ -337,12 +337,26 @@ void SipEndpoint::setRTPOverIPv6(bool enabled)
     mImplPriv->mConfig.sessionConfig.rtpOverIPv6 = enabled;
 }
 
-
 void SipEndpoint::setDirectMedia(bool enabled)
 {
     mImplPriv->mConfig.sessionConfig.directMedia = enabled;
 }
 
+void SipEndpoint::setUDPTLOverIPv6(bool enabled)
+{
+    mImplPriv->mConfig.sessionConfig.udptlOverIPv6 = enabled;
+}
+
+void SipEndpoint::enableUDPTLICE(bool enabled)
+{
+    mImplPriv->mConfig.sessionConfig.udptlOverICE = enabled;
+}
+
+void SipEndpoint::enableUDPTLTURN(bool enabled)
+{
+    mImplPriv->mConfig.sessionConfig.udptlWithTURN = enabled;
+}
+
 void SipEndpoint::setMediaNATOptions(bool useICE, bool useTURN)
 {
     mImplPriv->mConfig.sessionConfig.rtpOverICE = useICE;
@@ -446,8 +460,10 @@ AsteriskSCF::SessionCommunications::V1::SessionPrx SipEndpoint::createSession(
 	    true, 
 	    mImplPriv->mConfig, 
             NATEndpointOptions(mImplPriv->mConfig.sessionConfig.rtpOverICE, 
-                 mImplPriv->mConfig.sessionConfig.rtpICEIncludeTURN,
-                 mImplPriv->mConfig.transportConfig.enableNAT));
+                               mImplPriv->mConfig.sessionConfig.rtpICEIncludeTURN,
+                               mImplPriv->mConfig.transportConfig.enableNAT,
+                               mImplPriv->mConfig.sessionConfig.udptlOverICE,
+                               mImplPriv->mConfig.sessionConfig.udptlWithTURN));
 
     mImplPriv->mSessions.push_back(session);
 
@@ -475,7 +491,9 @@ AsteriskSCF::SipSessionManager::SipSessionPtr SipEndpoint::createSession(const s
 	     mImplPriv->mConfig, 
              NATEndpointOptions(mImplPriv->mConfig.sessionConfig.rtpOverICE, 
                                 mImplPriv->mConfig.sessionConfig.rtpICEIncludeTURN,
-                                mImplPriv->mConfig.transportConfig.enableNAT)
+                                mImplPriv->mConfig.transportConfig.enableNAT,
+                                mImplPriv->mConfig.sessionConfig.udptlOverICE,
+                                mImplPriv->mConfig.sessionConfig.udptlWithTURN)
 	     );
 
     mImplPriv->mSessions.push_back(session);
@@ -493,28 +511,32 @@ SipSessionPtr SipEndpoint::createSession
 		const Ice::Identity& controllerid,
                 const Ice::Identity& mediaid,
                 const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionDict& mediasessions,
+                const AsteriskSCF::Replication::SipSessionManager::V1::UDPTLMediaSessionSeq& udptlMediaSessions,
                 const AsteriskSCF::Media::V1::StreamSourceSeq& sources,
                 const AsteriskSCF::Media::V1::StreamSinkSeq& sinks)
 {
     SipSessionPtr session = SipSession::create
-	    (mImplPriv->mAdapter, 
-	     this, 
-	     destination, 
-	     sessionid, 
-             controllerid, 
-	     mediaid, 
-	     mediasessions, 
-	     sources, 
-	     sinks, 
-	     mImplPriv->mManager, 
-             mImplPriv->mServiceLocator, 
-	     mImplPriv->mReplicationContext, 
-	     0,
-	     false, 
-	     mImplPriv->mConfig,
-             NATEndpointOptions(mImplPriv->mConfig.sessionConfig.rtpOverICE, 
-                                mImplPriv->mConfig.sessionConfig.rtpICEIncludeTURN,
-                                mImplPriv->mConfig.transportConfig.enableNAT));
+        (mImplPriv->mAdapter, 
+         this, 
+         destination, 
+         sessionid, 
+         controllerid, 
+         mediaid, 
+         mediasessions, 
+         udptlMediaSessions,
+         sources, 
+         sinks, 
+         mImplPriv->mManager, 
+         mImplPriv->mServiceLocator, 
+         mImplPriv->mReplicationContext, 
+         0,
+         false, 
+         mImplPriv->mConfig,
+         NATEndpointOptions(mImplPriv->mConfig.sessionConfig.rtpOverICE, 
+                            mImplPriv->mConfig.sessionConfig.rtpICEIncludeTURN,
+                            mImplPriv->mConfig.transportConfig.enableNAT,
+                            mImplPriv->mConfig.sessionConfig.udptlOverICE,
+                            mImplPriv->mConfig.sessionConfig.udptlWithTURN));
 
     mImplPriv->mSessions.push_back(session);
     return session;
@@ -767,6 +789,36 @@ SDPDescriptorPtr SipEndpoint::getDescriptor(const FormatPtr& format)
     return 0;
 }
 
+SDPDescriptorPtr SipEndpoint::getInterpretedDescriptor(const FormatPtr& format)
+{
+    for (std::vector<ConfiguredFormatPtr>::const_iterator configuredFormat = mImplPriv->mFormats.begin();
+         configuredFormat != mImplPriv->mFormats.end();
+         ++configuredFormat)
+    {
+        if ((*configuredFormat)->getFormat()->name == format->name)
+        {
+            return (*configuredFormat)->getDescriptorService()->getDescriptor(format);
+        }
+    }
+
+    return 0;
+}
+
+FormatPtr SipEndpoint::getFormat(const FormatPtr& format)
+{
+    for (std::vector<ConfiguredFormatPtr>::const_iterator configuredFormat = mImplPriv->mFormats.begin();
+         configuredFormat != mImplPriv->mFormats.end();
+         ++configuredFormat)
+    {
+        if ((*configuredFormat)->getFormat()->name == format->name)
+        {
+            return (*configuredFormat)->getFormat();
+        }
+    }
+
+    return 0;
+}
+
 StreamInformationDict SipEndpoint::getStreamTopology()
 {
     StreamInformationDict topology;
@@ -776,6 +828,12 @@ StreamInformationDict SipEndpoint::getStreamTopology()
          configuredFormat != mImplPriv->mFormats.end();
          ++configuredFormat)
     {
+        // Initial stream topology does not support T.38 streams
+        if ((*configuredFormat)->getDescriptor()->type == "image")
+        {
+            continue;
+        }
+
         // See if a stream already exists for this type
         StreamInformationDict::iterator stream = topology.find((*configuredFormat)->getDescriptor()->type);
 
diff --git a/src/SipEndpoint.h b/src/SipEndpoint.h
index 0a07081..037b1b3 100644
--- a/src/SipEndpoint.h
+++ b/src/SipEndpoint.h
@@ -161,6 +161,12 @@ public:
     bool rtpOverIPv6;
     // Whether we are allowing direct media or not
     bool directMedia;
+    // Whether we are using IPv6 for UDPTL transport or not.
+    bool udptlOverIPv6;
+    // Whether ICE is enabled for UDPTL.
+    bool udptlOverICE;
+    // Whether TURN is enabled for UDPTL.
+    bool udptlWithTURN;
     // The method by which we will transmit
     // DTMF to an endpoint
     AsteriskSCF::Configuration::SipSessionManager::V1::SipDTMFOption dtmf;
@@ -321,6 +327,7 @@ public:
     AsteriskSCF::SipSessionManager::SipSessionPtr createSession(const std::string&);
     AsteriskSCF::SipSessionManager::SipSessionPtr createSession(const std::string&, const Ice::Identity&, const Ice::Identity&,
                                                                 const Ice::Identity&, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionDict&,
+                                                                const AsteriskSCF::Replication::SipSessionManager::V1::UDPTLMediaSessionSeq&,
                                                                 const AsteriskSCF::Media::V1::StreamSourceSeq&, const AsteriskSCF::Media::V1::StreamSinkSeq&);
 
     void removeSession(const AsteriskSCF::SessionCommunications::V1::SessionPtr&);
@@ -336,6 +343,9 @@ public:
     void setSecureTransport(enum Direction);
     void setRTPOverIPv6(bool);
     void setDirectMedia(bool);
+    void setUDPTLOverIPv6(bool);
+    void enableUDPTLICE(bool);
+    void enableUDPTLTURN(bool);
     void addFormat(const std::string& name, int sampleRate, int frameSize, const Ice::StringSeq& formatSpecific);
 
     /**
@@ -345,11 +355,21 @@ public:
         const AsteriskSCF::Media::SDP::V1::SDPDescriptorPtr&);
     
     /**
-     * API call which returns a descriptor given a media format.
+     * API call which returns a locally cached descriptor given a media format.
      */
     AsteriskSCF::Media::SDP::V1::SDPDescriptorPtr getDescriptor(const AsteriskSCF::Media::V1::FormatPtr&);
 
     /**
+     * API call which returns a descriptor given a media format.
+     */
+    AsteriskSCF::Media::SDP::V1::SDPDescriptorPtr getInterpretedDescriptor(const AsteriskSCF::Media::V1::FormatPtr&);
+
+    /**
+     * API call which returns the local format given a media format.
+     */
+    AsteriskSCF::Media::V1::FormatPtr getFormat(const AsteriskSCF::Media::V1::FormatPtr&);
+
+    /**
      * API call which returns the stream topology to be used for an SDP offer.
      */
 
diff --git a/src/SipSession.cpp b/src/SipSession.cpp
index dce14ab..3182731 100755
--- a/src/SipSession.cpp
+++ b/src/SipSession.cpp
@@ -40,6 +40,9 @@
 #include <AsteriskSCF/Media/RTP/MediaRTPIf.h>
 #include <AsteriskSCF/Media/SDP/MediaSDPIf.h>
 #include <AsteriskSCF/Media/RTP/MediaRTCPIf.h>
+#include <AsteriskSCF/Media/UDPTL/MediaUDPTLIf.h>
+#include <AsteriskSCF/Media/Formats/T38UdptlFormat.h>
+#include <AsteriskSCF/Media/NetworkIf.h>
 #include <AsteriskSCF/SessionCookies/SipSessionManager/SipSessionCookiesIf.h>
 #include <AsteriskSCF/Collections/HandleSet.h>
 #include "NATOptions.h"
@@ -52,6 +55,9 @@ using namespace AsteriskSCF::Media::RTP::V1;
 using namespace AsteriskSCF::Media::V1;
 using namespace AsteriskSCF::Media;
 using namespace AsteriskSCF::Media::SDP::V1;
+using namespace AsteriskSCF::Media::UDPTL::V1;
+using namespace AsteriskSCF::Media::Formats::T38Udptl::V1;
+using namespace AsteriskSCF::Network::V1;
 using namespace AsteriskSCF::System::V1;
 using namespace AsteriskSCF::Helpers;
 using namespace std;
@@ -284,6 +290,11 @@ public:
     RTPMediaSessionDict mRTPSessions;
 
     /**
+     * A vector of UDPTL media sessions belonging to this session.
+     */
+    UDPTLMediaSessionSeq mUDPTLSessions;
+
+    /**
      * A vector of media sources associated with this endpoint.
      */
     AsteriskSCF::Media::V1::StreamSourceSeq mSources;
@@ -1276,37 +1287,39 @@ SipSessionPtr SipSession::create(const Ice::ObjectAdapterPtr& adapter,
  * Factory used by a standby component to create replicas. 
  */
 SipSessionPtr SipSession::create(const Ice::ObjectAdapterPtr& adapter, 
-                       const SipEndpointPtr& endpoint,
-                       const std::string& destination, 
-                       const Ice::Identity& sessionid,
-                       const Ice::Identity& controllerid,
-                       const Ice::Identity& mediaid, 
-                       const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionDict& mediasessions,
-                       const AsteriskSCF::Media::V1::StreamSourceSeq& sources, 
-                       const AsteriskSCF::Media::V1::StreamSinkSeq& sinks,
-                       const PJSipManagerPtr& manager, 
-                       const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
-                       const SipReplicationContextPtr& replicationContext, 
-                       const AsteriskSCF::SessionCommunications::ExtensionPoints::V1::SessionCreationHookPrx& oneShotHook,
-                       bool isUAC, 
-                       const SipEndpointConfig &config,
-                       const NATEndpointOptions& natOptions)
+                                 const SipEndpointPtr& endpoint,
+                                 const std::string& destination, 
+                                 const Ice::Identity& sessionid,
+                                 const Ice::Identity& controllerid,
+                                 const Ice::Identity& mediaid, 
+                                 const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionDict& mediasessions,
+                                 const AsteriskSCF::Replication::SipSessionManager::V1::UDPTLMediaSessionSeq& udptlMediaSessions,
+                                 const AsteriskSCF::Media::V1::StreamSourceSeq& sources, 
+                                 const AsteriskSCF::Media::V1::StreamSinkSeq& sinks,
+                                 const PJSipManagerPtr& manager, 
+                                 const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
+                                 const SipReplicationContextPtr& replicationContext, 
+                                 const AsteriskSCF::SessionCommunications::ExtensionPoints::V1::SessionCreationHookPrx& oneShotHook,
+                                 bool isUAC, 
+                                 const SipEndpointConfig &config,
+                                 const NATEndpointOptions& natOptions)
 {
     SipSessionPtr newSession = new SipSession(adapter, 
-                       endpoint,
-                       destination, 
-                       sessionid,
-                       controllerid,
-                       mediaid, 
-                       mediasessions,
-                       sources, 
-                       sinks,
-                       manager, 
-                       serviceLocator,
-                       replicationContext, 
-                       isUAC, 
-                       config,
-                       natOptions);
+                                              endpoint,
+                                              destination, 
+                                              sessionid,
+                                              controllerid,
+                                              mediaid, 
+                                              mediasessions,
+                                              udptlMediaSessions,
+                                              sources, 
+                                              sinks,
+                                              manager, 
+                                              serviceLocator,
+                                              replicationContext, 
+                                              isUAC, 
+                                              config,
+                                              natOptions);
 
     AsteriskSCF::SessionCommunications::ExtensionPoints::V1::SessionCreationHookSeq hooks =
         newSession->mImplPriv->mManager->getSessionModule()->getSessionCreationHooks();
@@ -1376,6 +1389,7 @@ SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter,
                        const Ice::Identity& controllerid,
                        const Ice::Identity& mediaid,
                        const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionDict& mediasessions,
+                       const AsteriskSCF::Replication::SipSessionManager::V1::UDPTLMediaSessionSeq& udptlMediaSessions,
                        const AsteriskSCF::Media::V1::StreamSourceSeq& sources,
                        const AsteriskSCF::Media::V1::StreamSinkSeq& sinks,
                        const PJSipManagerPtr& manager,
@@ -1401,6 +1415,7 @@ SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter,
     adapter->addFacet(directMedia, sessionid, directMediaConnectionFacet);
 
     mImplPriv->mRTPSessions = mediasessions;
+    mImplPriv->mUDPTLSessions = udptlMediaSessions;
     mImplPriv->mSources = sources;
     mImplPriv->mSinks = sinks;
 
@@ -2434,6 +2449,19 @@ public:
                     lg(Error) << "Exception caught while trying to release a media session\n" << ex.what();
                 }
            }
+
+            for (UDPTLMediaSessionSeq::const_iterator i = mSessionPriv->mUDPTLSessions.begin();
+                 i != mSessionPriv->mUDPTLSessions.end(); ++i)
+            {
+                try
+                {
+                    (*i)->release();
+                }
+                catch (const Ice::Exception& ex)
+                {
+                    lg(Error) << "Exception caught while trying to release a udptl session\n" << ex.what();
+                }
+            }
         }
         mSessionPriv->mEndpoint->removeSession(mSession);
         return Complete;
@@ -2710,111 +2738,224 @@ pjmedia_sdp_session *SipSession::createSDPOffer(const AsteriskSCF::Media::V1::St
         // Update the stream with the actual formats
         stream->second->formats = formats;
 
-        RTPServiceLocatorParamsPtr params = new RTPServiceLocatorParams();
-        params->category = "rtp";
-        params->formats = stream->second->formats;
-        params->ipv6 = mImplPriv->mEndpoint->getConfig().sessionConfig.rtpOverIPv6;
+        // Determine what media this stream is carrying
+        AudioFormatPtr audio;
+        VideoFormatPtr video;
+	T38UdptlFormatPtr t38;
 
-        // Try to find a factory for RTP sessions matching what we need
-        RTPMediaServicePrx factory = RTPMediaServicePrx::uncheckedCast(mImplPriv->mServiceLocator->locate(params));
-
-        // If none exist we can't provide the stream
-        if (factory == 0)
+        // If this stream contains an audio or video format the stream is transported using RTP
+        if ((audio = AudioFormatPtr::dynamicCast(formats.front())) || (video = VideoFormatPtr::dynamicCast(formats.front())))
         {
-            continue;
-        }
+            RTPServiceLocatorParamsPtr params = new RTPServiceLocatorParams();
+            params->category = "rtp";
+            params->formats = stream->second->formats;
+            params->ipv6 = mImplPriv->mEndpoint->getConfig().sessionConfig.rtpOverIPv6;
+
+            // Try to find a factory for RTP sessions matching what we need
+            RTPMediaServicePrx factory = RTPMediaServicePrx::uncheckedCast(mImplPriv->mServiceLocator->locate(params));
+
+            if (factory == 0)
+            {
+                continue;
+            }
 
-        // Allocate a new RTP session to carry the media formats we have in common
+            // Allocate a new RTP session to carry the media formats we have in common
         
-        RTPOptionsPtr options(new RTPOptions());
-        RTPAllocationOutputsPtr outputs;
+            RTPOptionsPtr options(new RTPOptions());
+            RTPAllocationOutputsPtr outputs;
 
-        SipEndpointConfig& config = mImplPriv->mEndpoint->getConfig();
-        if (config.sessionConfig.dtmf == AsteriskSCF::Configuration::SipSessionManager::V1::RFC4733)
-        {
-            options->handleTelephonyEvents = true;
-        }
+            SipEndpointConfig& config = mImplPriv->mEndpoint->getConfig();
+            if (config.sessionConfig.dtmf == AsteriskSCF::Configuration::SipSessionManager::V1::RFC4733)
+            {
+                options->handleTelephonyEvents = true;
+            }
 
-        RTPSessionPrx session = factory->allocate(params, options, outputs);
+            RTPSessionPrx session = factory->allocate(params, options, outputs);
 
-        // Double check to make sure they actually gave us a sesson back... they could have had a problem
-        if (session == 0)
-        {
-            continue;
-        }
+            // Double check to make sure they actually gave us a sesson back... they could have had a problem
+            if (session == 0)
+            {
+                continue;
+            }
 
-        if (outputs)
-        {
-            mImplPriv->mExternalEventSources = outputs->eventSources;
-            mImplPriv->mExternalEventSinks = outputs->eventSinks;
-        }
+            if (outputs)
+            {
+                mImplPriv->mExternalEventSources = outputs->eventSources;
+                mImplPriv->mExternalEventSinks = outputs->eventSinks;
+            }
 
-        // RTP sessions should only provide a single sink, so grab it and update the connection details with that
-        // of the remote party
-        StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(session->getSinks().front());
-        mImplPriv->mSinks.push_back(sink);
-    stream->second->sinks.push_back(sink);
+            // RTP sessions should only provide a single sink, so grab it and update the connection details with that
+            // of the remote party
+            StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(session->getSinks().front());
+            mImplPriv->mSinks.push_back(sink);
+            stream->second->sinks.push_back(sink);
 
-        // Ditto goes for source
-        StreamSourceRTPPrx source = StreamSourceRTPPrx::uncheckedCast(session->getSources().front());
-        mImplPriv->mSources.push_back(source);
-    stream->second->sources.push_back(source);
+            // Ditto goes for source
+            StreamSourceRTPPrx source = StreamSourceRTPPrx::uncheckedCast(session->getSources().front());
+            mImplPriv->mSources.push_back(source);
+            stream->second->sources.push_back(source);
 
-        // Update the SIP session with some RTP session details
-        mImplPriv->mRTPSessions.insert(make_pair(boost::lexical_cast<std::string>(mImplPriv->mSDP->media_count), session));
+            // Update the SIP session with some RTP session details
+            mImplPriv->mRTPSessions.insert(make_pair(boost::lexical_cast<std::string>(mImplPriv->mSDP->media_count), session));
+         
+	    // Add the stream to the SDP
+	    pjmedia_sdp_media *media = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
 
-        // Add the stream to the SDP
-        pjmedia_sdp_media *media = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
+	    // The media count is purposely not incremented here since it is done when the stream is added to the sequence
+	    // of streams
+	    mImplPriv->mSDP->media[mImplPriv->mSDP->media_count] = media;
+	    pj_strdup2(mImplPriv->mDialog->pool, &media->desc.media, mImplPriv->mEndpoint->getDescriptor(
+                           stream->second->formats.front())->type.c_str());
 
-        // The media count is purposely not incremented here since it is done when the stream is added to the sequence
-        // of streams
-        mImplPriv->mSDP->media[mImplPriv->mSDP->media_count] = media;
-        pj_strdup2(mImplPriv->mDialog->pool, &media->desc.media, mImplPriv->mEndpoint->getDescriptor(
-                       stream->second->formats.front())->type.c_str());
+	    pj_strdup2(mImplPriv->mDialog->pool, &media->desc.transport, "RTP/AVP");
 
-        // TODO: This should not be hardcoded
-        pj_strdup2(mImplPriv->mDialog->pool, &media->desc.transport, "RTP/AVP");
+            // Add connection level details
+            media->conn = allocate_from_pool<pjmedia_sdp_conn>(mImplPriv->mDialog->pool);
+            pj_strdup2(mImplPriv->mDialog->pool, &media->conn->net_type, "IN");
 
-        // Add connection level details
-        media->conn = allocate_from_pool<pjmedia_sdp_conn>(mImplPriv->mDialog->pool);
-        pj_strdup2(mImplPriv->mDialog->pool, &media->conn->net_type, "IN");
+            if (params->ipv6 == true)
+            {
+                pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr_type, "IP6");
+            }
+            else
+            {
+                pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr_type, "IP4");
+            }
 
-        if (params->ipv6 == true)
-        {
-            pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr_type, "IP6");
+            pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr, source->getLocalAddress().c_str());
+
+            // If session level connection information has not yet been set then set it to us
+            if (!mImplPriv->mSDP->conn)
+            {
+                mImplPriv->mSDP->conn = media->conn;
+            }
+
+            media->desc.port = (pj_uint16_t) source->getLocalPort();
+            media->desc.port_count = 1;
+
+            RTCP::V1::RTCPSessionPrx rtcpSession;
+            if ((rtcpSession = RTCP::V1::RTCPSessionPrx::checkedCast(session, RTCP::V1::SessionFacet)))
+            {
+                pjmedia_sdp_attr *attr = allocate_from_pool<pjmedia_sdp_attr>(mImplPriv->mDialog->pool);
+                pj_strdup2(mImplPriv->mDialog->pool, &attr->name, "rtcp");
+                pj_strdup2(mImplPriv->mDialog->pool, &attr->value, boost::lexical_cast<std::string>(rtcpSession->getLocalPort()).c_str());
+                media->attr[media->attr_count++] = attr;
+            }
+
+            PayloadMap payloads;
+
+            // Add all of the formats to the SDP
+            addFormatstoSDP(stream->second->formats, media, payloads);
+
+            // Push the payload mapping to the RTP session so it'll correctly map things
+            session->associatePayloads(payloads);
         }
-        else
+        else if ((t38 = T38UdptlFormatPtr::dynamicCast(formats.front())))
         {
-            pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr_type, "IP4");
-        }
+            UDPTLServiceLocatorParamsPtr params;
 
-        pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr, source->getLocalAddress().c_str());
+            if (mImplPriv->mNatOptions.enableUDPTLICE)
+            {
+                AsteriskSCF::Media::UDPTL::V1::UDPTLOverICEServiceLocatorParamsPtr iceParams =
+                    new AsteriskSCF::Media::UDPTL::V1::UDPTLOverICEServiceLocatorParams;
+                params = iceParams;
+                iceParams->enableICE = true;
+                iceParams->enableTURN = mImplPriv->mNatOptions.enableUDPTLTURN;
+            }
+            else
+            {
+                params = new AsteriskSCF::Media::UDPTL::V1::UDPTLServiceLocatorParams;
+            }
 
-        // If session level connection information has not yet been set then set it to us
-        if (!mImplPriv->mSDP->conn)
-        {
-            mImplPriv->mSDP->conn = media->conn;
-        }
+            params->category = "udptl";
+            params->ipv6 = mImplPriv->mEndpoint->getConfig().sessionConfig.udptlOverIPv6;
 
-        media->desc.port = (pj_uint16_t) source->getLocalPort();
-        media->desc.port_count = 1;
+            UDPTLMediaServicePrx factory = UDPTLMediaServicePrx::uncheckedCast(mImplPriv->mServiceLocator->locate(params));
+            if (factory == 0)
+            {
+                continue;
+            }
 
-        RTCP::V1::RTCPSessionPrx rtcpSession;
-        if ((rtcpSession = RTCP::V1::RTCPSessionPrx::checkedCast(session, RTCP::V1::SessionFacet)))
-        {
-            pjmedia_sdp_attr *attr = allocate_from_pool<pjmedia_sdp_attr>(mImplPriv->mDialog->pool);
-            pj_strdup2(mImplPriv->mDialog->pool, &attr->name, "rtcp");
-            pj_strdup2(mImplPriv->mDialog->pool, &attr->value, boost::lexical_cast<std::string>(rtcpSession->getLocalPort()).c_str());
-            media->attr[media->attr_count++] = attr;
-        }
+            UDPTLSessionPrx session = factory->allocate(params);
+            if (session == 0)
+            {
+                continue;
+            }
 
-        PayloadMap payloads;
+            StreamSinkUDPTLPrx sink = StreamSinkUDPTLPrx::uncheckedCast(session->getSinks().front());
+            mImplPriv->mSinks.push_back(sink);
+            stream->second->sinks.push_back(sink);
+
+            StreamSourceUDPTLPrx source = StreamSourceUDPTLPrx::uncheckedCast(session->getSources().front());
+            mImplPriv->mSources.push_back(source);
+            stream->second->sources.push_back(source);
+
+            mImplPriv->mUDPTLSessions.push_back(session);
+
+            // Add the stream to the SDP
+            mImplPriv->mSDP->media_count = 0;
 
-        // Add all of the formats to the SDP
-        addFormatstoSDP(stream->second->formats, media, payloads);
+            // Since we may be replacing an existing stream go ahead and remove it
+            mImplPriv->mStreams.erase(boost::lexical_cast<std::string>(mImplPriv->mSDP->media_count));
+
+            pjmedia_sdp_media *media = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
+            // The media count is purposely not incremented here since it is done when the stream is added to the sequence
+            // of streams
+            mImplPriv->mSDP->media[mImplPriv->mSDP->media_count] = media;
+
+            pj_strdup2(mImplPriv->mDialog->pool, &media->desc.media, mImplPriv->mEndpoint->getDescriptor(
+                           stream->second->formats.front())->type.c_str());
+
+            pj_strdup2(mImplPriv->mDialog->pool, &media->desc.transport, "UDPTL");
+
+	    pj_strdup2(mImplPriv->mDialog->pool, &media->desc.fmt[media->desc.fmt_count++], "t38");
+
+            // Add connection level details
+            media->conn = allocate_from_pool<pjmedia_sdp_conn>(mImplPriv->mDialog->pool);
+            pj_strdup2(mImplPriv->mDialog->pool, &media->conn->net_type, "IN");
+
+            if (params->ipv6 == true)
+            {
+                pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr_type, "IP6");
+            }
+            else
+            {
+                pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr_type, "IP4");
+            }
+
+            AddressInformation info = source->getLocalDetails();
+
+            pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr, info.ipAddress.c_str());
+
+            // If session level connection information has not yet been set then set it to us
+            if (!mImplPriv->mSDP->conn)
+            {
+                mImplPriv->mSDP->conn = media->conn;
+            }
+
+            media->desc.port = (pj_uint16_t) info.port;
+            media->desc.port_count = 1;
 
-        // Push the payload mapping to the RTP session so it'll correctly map things
-        session->associatePayloads(payloads);
+            // Use the configured format to set the error correction
+            T38UdptlFormatPtr t38Configuration = T38UdptlFormatPtr::dynamicCast(mImplPriv->mEndpoint->getFormat(t38));
+
+            t38->errorCorrection = t38Configuration->errorCorrection;
+
+            SDPDescriptorPtr ourDescriptor = mImplPriv->mEndpoint->getInterpretedDescriptor(t38);
+            for (SDPFormatParameterSeq::const_iterator parameter = ourDescriptor->parameters.begin();
+                 parameter != ourDescriptor->parameters.end();
+                 ++parameter)
+            {
+                pjmedia_sdp_attr *attr = allocate_from_pool<pjmedia_sdp_attr>(mImplPriv->mDialog->pool);
+                pj_strdup2(mImplPriv->mDialog->pool, &attr->name, (*parameter).c_str());
+                media->attr[media->attr_count++] = attr;
+            }
+        }
+        else
+        {
+            // The media format type is just unknown to us, so we can not create a stream for this
+            continue;
+        }
 
         // Make the caller aware of this new stream
         newStreams.insert(make_pair(boost::lexical_cast<std::string>(mImplPriv->mSDP->media_count), stream->second));
@@ -2903,11 +3044,21 @@ pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offe
         {
             SDPDescriptorPtr descriptor = new SDPDescriptor();
 
-        std::string payload = std::string(pj_strbuf(&offer->media[stream]->desc.fmt[format]),
-                pj_strlen(&offer->media[stream]->desc.fmt[format]));
-        descriptor->payload = boost::lexical_cast<int>(payload);
             descriptor->type = std::string(pj_strbuf(&offer->media[stream]->desc.media),
                                            pj_strlen(&offer->media[stream]->desc.media));
+	    std::string payload = std::string(pj_strbuf(&offer->media[stream]->desc.fmt[format]),
+                pj_strlen(&offer->media[stream]->desc.fmt[format]));
+
+            // If this is for T.38 it is actually special
+            if (descriptor->type == "image" && (payload == "T38" || payload == "t38"))
+            {
+                descriptor->payload = 96;
+                descriptor->subtype = "UDPTL";
+            }
+            else
+            {
+                descriptor->payload = boost::lexical_cast<int>(payload);
+            }
 
             // Some devices rely solely on the payload for known formats (such as PCMU) so the following format
             // parameters are completely optional
@@ -2992,8 +3143,29 @@ pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offe
             continue;
         }
 
-    // Update the stream with the formats
-    ourStream->formats = formats;
+        // Update the stream with the formats
+        ourStream->formats = formats;
+
+        // Record the old state so we can relay the state change to the controller if needed
+        StreamState oldState = ourStream->state;
+
+        // Determine the state of the stream and update it
+        if (pjmedia_sdp_media_find_attr2(offer->media[stream], "sendonly", NULL))
+        {
+            ourStream->state = SendOnly;
+        }
+        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "recvonly", NULL))
+        {
+            ourStream->state = ReceiveOnly;
+        }
+        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "inactive", NULL))
+        {
+            ourStream->state = Inactive;
+        }
+        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "sendrecv", NULL))
+        {
+            ourStream->state = SendAndReceive;
+        }
 
         // Assume that no connection level details exist until proven otherwise
         std::string connection = destination;
@@ -3003,193 +3175,366 @@ pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offe
                                      pj_strlen(&offer->media[stream]->conn->addr));
         }
 
-        RTPSessionPrx session;
+        AudioFormatPtr audio;
+        VideoFormatPtr video;
+        T38UdptlFormatPtr t38;
 
-    // If no sink and no source is present then this stream has no RTP session yet so find one
-    if (ourStream->sinks.empty() && ourStream->sources.empty())
-    {
-        RTPServiceLocatorParamsPtr params;
-        if (mImplPriv->mNatOptions.enableICE)
-        {
-        AsteriskSCF::Media::RTP::V1::RTPOverICEServiceLocatorParamsPtr iceParams =
-            new AsteriskSCF::Media::RTP::V1::RTPOverICEServiceLocatorParams;
-        params = iceParams;
-        iceParams->enableRTPOverICE = true;
-        iceParams->enableTURN = mImplPriv->mNatOptions.enableTURN;
-        }
-        else
+        if ((audio = AudioFormatPtr::dynamicCast(formats.front())) || (video = VideoFormatPtr::dynamicCast(formats.front())))
         {
-        params = new AsteriskSCF::Media::RTP::V1::RTPServiceLocatorParams;
-        }
-
-            params->category = "rtp";
-            params->formats = formats;
-
-            // See what address family the connection address is in so we can request the right RTP service
-            if (connection.find(":") != std::string::npos)
+            // If this is a modified stream make sure the transport is RTP, if not clear things out
+            if (!ourStream->sinks.empty())
             {
-                params->ipv6 = true;
+                StreamSinkRTPPrx rtpSink = StreamSinkRTPPrx::checkedCast(ourStream->sinks.front());
+                if (!rtpSink)
+                {
+                    ourStream->sinks.clear();
+                    newStreams.erase(boost::lexical_cast<std::string>(stream));
+                    newStreams.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream));
+                }
             }
-            else
+
+            if (!ourStream->sources.empty())
             {
-                params->ipv6 = false;
+                StreamSourceRTPPrx rtpSource = StreamSourceRTPPrx::checkedCast(ourStream->sources.front());
+                if (!rtpSource)
+                {
+                    ourStream->sources.clear();
+                    newStreams.erase(boost::lexical_cast<std::string>(stream));
+                    newStreams.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream));
+                }
             }
 
-            // Try to find a factory for RTP sessions matching what we need
-            RTPMediaServicePrx factory = RTPMediaServicePrx::uncheckedCast(mImplPriv->mServiceLocator->locate(params));
+            RTPSessionPrx session;
 
-            // If none exist we can't provide accept the stream
-            if (factory == 0)
+            // If no sink and no source is present then this stream has no RTP session yet so find one
+            if (ourStream->sinks.empty() && ourStream->sources.empty())
             {
-                // The only time this should ever occur is on the initial INVITE so removing the stream from everywhere
-                // is perfectly acceptable since nothing has seen it
-                mImplPriv->mStreams.erase(boost::lexical_cast<std::string>(stream));
-                newStreams.erase(boost::lexical_cast<std::string>(stream));
-                continue;
+                RTPServiceLocatorParamsPtr params;
+                if (mImplPriv->mNatOptions.enableICE)
+                {
+                    AsteriskSCF::Media::RTP::V1::RTPOverICEServiceLocatorParamsPtr iceParams =
+                        new AsteriskSCF::Media::RTP::V1::RTPOverICEServiceLocatorParams;
+                    params = iceParams;
+                    iceParams->enableRTPOverICE = true;
+                    iceParams->enableTURN = mImplPriv->mNatOptions.enableTURN;
+                }
+                else
+                {
+                    params = new AsteriskSCF::Media::RTP::V1::RTPServiceLocatorParams;
+                }
+
+                params->category = "rtp";
+                params->formats = formats;
+
+                // See what address family the connection address is in so we can request the right RTP service
+                if (connection.find(":") != std::string::npos)
+                {
+                    params->ipv6 = true;
+                }
+                else
+                {
+                    params->ipv6 = false;
+                }
+
+                // Try to find a factory for RTP sessions matching what we need
+                RTPMediaServicePrx factory = RTPMediaServicePrx::uncheckedCast(mImplPriv->mServiceLocator->locate(params));
+
+                // If none exist we can't provide accept the stream
+                if (factory == 0)
+                {
+                    // The only time this should ever occur is on the initial INVITE so removing the stream from everywhere
+                    // is perfectly acceptable since nothing has seen it
+                    mImplPriv->mStreams.erase(boost::lexical_cast<std::string>(stream));
+                    newStreams.erase(boost::lexical_cast<std::string>(stream));
+                    continue;
+                }
+
+                // Allocate a new RTP session to carry the media formats we have in common
+
+                RTPOptionsPtr options(new RTPOptions());
+                RTPAllocationOutputsPtr outputs;
+
+                SipEndpointConfig& config = mImplPriv->mEndpoint->getConfig();
+                if (config.sessionConfig.dtmf == AsteriskSCF::Configuration::SipSessionManager::V1::RFC4733)
+                {
+                    options->handleTelephonyEvents = true;
+                }
+
+                session = factory->allocate(params, options, outputs);
+
+                // Double check to make sure they actually gave us a sesson back... they could have had a problem
+                if (session == 0)
+                {
+                    // The only time this should ever occur is on the initial INVITE so removing the stream from everywhere
+                    // is perfectly acceptable since nothing has seen it
+                    mImplPriv->mStreams.erase(boost::lexical_cast<std::string>(stream));
+                    newStreams.erase(boost::lexical_cast<std::string>(stream));
+                    continue;
+                }
+
+                if (outputs)
+                {
+                    mImplPriv->mExternalEventSources = outputs->eventSources;
+                    mImplPriv->mExternalEventSinks = outputs->eventSinks;
+                }
+
+                // RTP sessions only provide a single sink and source so that makes this easy
+                StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(session->getSinks().front());
+                mImplPriv->mSinks.push_back(sink);
+                ourStream->sinks.push_back(sink);
+
+                StreamSourceRTPPrx source = StreamSourceRTPPrx::uncheckedCast(session->getSources().front());
+                mImplPriv->mSources.push_back(source);
+                ourStream->sources.push_back(source);
+
+                // Update the SIP session with some RTP session details
+                mImplPriv->mRTPSessions.insert(make_pair(boost::lexical_cast<std::string>(mImplPriv->mSDP->media_count), session));
+
+                // Just by common sense alone since we just got an RTP session for this stream we obviously
+                // have not added it to the answer SDP either, so do it
+                pjmedia_sdp_media *media = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
+                mImplPriv->mSDP->media[mImplPriv->mSDP->media_count++] = media;
+
+                // Since our stream should be like the offering stream we can just use some values from there
+                media->desc.media = offer->media[stream]->desc.media;
+                media->desc.transport = offer->media[stream]->desc.transport;
+		media->desc.fmt[0] = offer->media[stream]->desc.fmt[0];
+
+                // Add connection level information so they know our IP address
+                media->conn = allocate_from_pool<pjmedia_sdp_conn>(mImplPriv->mDialog->pool);
+                media->conn->net_type = offer->origin.net_type;
+                media->conn->addr_type = offer->origin.addr_type;
+                pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr, source->getLocalAddress().c_str());
+
+                // If session level connection information has not yet been set then set it to us
+                if (!mImplPriv->mSDP->conn)
+                {
+                    mImplPriv->mSDP->conn = media->conn;
+                }
+
+                // Add port information so they can talk to us
+                media->desc.port = (pj_uint16_t) source->getLocalPort();
+                media->desc.port_count = 1;
+
+                PayloadMap payloads;
+
+                addFormatstoSDP(formats, media, payloads);
+
+                // Push the payload mapping to the RTP session so it'll correctly map things
+                session->associatePayloads(payloads);
             }
-        
-            RTPOptionsPtr options(new RTPOptions());
-            RTPAllocationOutputsPtr outputs;
 
-            SipEndpointConfig& config = mImplPriv->mEndpoint->getConfig();
-            if (config.sessionConfig.dtmf == AsteriskSCF::Configuration::SipSessionManager::V1::RFC4733)
+            // If the RTP session supports RTCP determine the connection details for it
+            RTCP::V1::RTCPSessionPrx rtcpSession;
+            if ((rtcpSession = RTCP::V1::RTCPSessionPrx::checkedCast(session, RTCP::V1::SessionFacet)))
             {
-                options->handleTelephonyEvents = true;
+                // Assume RTCP is destined for the same address and the RTP port + 1 as it probably is
+                std::string rtcpConnection = connection;
+                int rtcpPort = offer->media[stream]->desc.port + 1;
+
+                pjmedia_sdp_attr *attr;
+                pjmedia_sdp_rtcp_attr rtcpAttr;
+
+                if ((attr = pjmedia_sdp_media_find_attr2(offer->media[stream], "rtcp", NULL)) &&
+                    (pjmedia_sdp_attr_get_rtcp(attr, &rtcpAttr) == PJ_SUCCESS))
+                {
+                    rtcpPort = rtcpAttr.port;
+
+                    if (rtcpAttr.addr.slen)
+                    {
+                        rtcpConnection = std::string(pj_strbuf(&rtcpAttr.addr), pj_strlen(&rtcpAttr.addr));
+                    }
+                }
+
+                rtcpSession->setRemoteDetails(rtcpConnection, rtcpPort);
             }
 
-            // Allocate a new RTP session to carry the media formats we have in common
-            session = factory->allocate(params, options, outputs);
+            // Update connection information
+            StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(ourStream->sinks.front());
 
-            // Double check to make sure they actually gave us a sesson back... they could have had a problem
-            if (session == 0)
+            // If the remote party attempts to use an invalid address bring down the session since obviously
+            // something is critically wrong and stuff will most likely fall apart quickly
+            try
             {
-                // The only time this should ever occur is on the initial INVITE so removing the stream from everywhere
-                // is perfectly acceptable since nothing has seen it
-                mImplPriv->mStreams.erase(boost::lexical_cast<std::string>(stream));
-                newStreams.erase(boost::lexical_cast<std::string>(stream));
-                continue;
+                sink->setRemoteDetails(connection, offer->media[stream]->desc.port);
+            }
+            catch (const AsteriskSCF::Media::RTP::V1::InvalidAddress&)
+            {
+                pjsip_tx_data *packet;
+                if (pjsip_inv_end_session(mImplPriv->mInviteSession, 488, NULL, &packet) == PJ_SUCCESS)
+                {
+                    pjsip_inv_send_msg(mImplPriv->mInviteSession, packet);
+                }
+                return 0;
             }
+        }
+        else if ((t38 = T38UdptlFormatPtr::dynamicCast(formats.front())))
+        {
+            // Ensure the offer is valid
+	    if (t38->faxRateManagement == UNSPECIFIED_TCF)
+	    {
+		continue;
+	    }
 
-            if (outputs)
+            // If this is a modified stream make sure the transport is UDPTL, if not clear things out
+            if (!ourStream->sinks.empty())
             {
-                mImplPriv->mExternalEventSources = outputs->eventSources;
-                mImplPriv->mExternalEventSinks = outputs->eventSinks;
+                StreamSinkUDPTLPrx udptlSink = StreamSinkUDPTLPrx::checkedCast(ourStream->sinks.front());
+                if (!udptlSink)
+                {
+                    ourStream->sinks.clear();
+                    newStreams.erase(boost::lexical_cast<std::string>(stream));
+                    newStreams.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream));
+                }
             }
 
-            // RTP sessions only provide a single sink and source so that makes this easy
-            StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(session->getSinks().front());
-            mImplPriv->mSinks.push_back(sink);
-            ourStream->sinks.push_back(sink);
+            if (!ourStream->sources.empty())
+            {
+                StreamSourceUDPTLPrx udptlSource = StreamSourceUDPTLPrx::checkedCast(ourStream->sources.front());
+                if (!udptlSource)
+                {
+                    ourStream->sources.clear();
+                    newStreams.erase(boost::lexical_cast<std::string>(stream));
+                    newStreams.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream));
+                }
+            }
 
-            StreamSourceRTPPrx source = StreamSourceRTPPrx::uncheckedCast(session->getSources().front());
-            mImplPriv->mSources.push_back(source);
-            ourStream->sources.push_back(source);
+            if (ourStream->sinks.empty() && ourStream->sources.empty())
+            {
+                UDPTLServiceLocatorParamsPtr params;
 
-            // Update the SIP session with some RTP session details
-            mImplPriv->mRTPSessions.insert(make_pair(boost::lexical_cast<std::string>(stream), session));
+                if (mImplPriv->mNatOptions.enableUDPTLICE)
+                {
+                    AsteriskSCF::Media::UDPTL::V1::UDPTLOverICEServiceLocatorParamsPtr iceParams =
+                        new AsteriskSCF::Media::UDPTL::V1::UDPTLOverICEServiceLocatorParams;
+                    params = iceParams;
+                    iceParams->enableICE = true;
+                    iceParams->enableTURN = mImplPriv->mNatOptions.enableUDPTLTURN;
+                }
+                else
+                {
+                    params = new AsteriskSCF::Media::UDPTL::V1::UDPTLServiceLocatorParams;
+                }
 
-            // Just by common sense alone since we just got an RTP session for this stream we obviously
-            // have not added it to the answer SDP either, so do it
-            pjmedia_sdp_media *media = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
-            mImplPriv->mSDP->media[mImplPriv->mSDP->media_count++] = media;
+                params->category = "udptl";
 
-            // Since our stream should be like the offering stream we can just use some values from there
-            media->desc.media = offer->media[stream]->desc.media;
-            media->desc.transport = offer->media[stream]->desc.transport;
+                if (connection.find(":") != std::string::npos)
+                {
+                    params->ipv6 = true;
+                }
+                else
+                {
+                    params->ipv6 = false;
+                }
 
-            // Add connection level information so they know our IP address
-            media->conn = allocate_from_pool<pjmedia_sdp_conn>(mImplPriv->mDialog->pool);
-            media->conn->net_type = offer->origin.net_type;
-            media->conn->addr_type = offer->origin.addr_type;
-            pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr, source->getLocalAddress().c_str());
+                UDPTLMediaServicePrx factory = UDPTLMediaServicePrx::uncheckedCast(mImplPriv->mServiceLocator->locate(params));
 
-            // If session level connection information has not yet been set then set it to us
-            if (!mImplPriv->mSDP->conn)
-            {
-                mImplPriv->mSDP->conn = media->conn;
-            }
+                if (factory == 0)
+                {
+                    mImplPriv->mStreams.erase(boost::lexical_cast<std::string>(stream));
+                    newStreams.erase(boost::lexical_cast<std::string>(stream));
+                    continue;
+                }
 
-            // Add port information so they can talk to us
-            media->desc.port = (pj_uint16_t) source->getLocalPort();
-            media->desc.port_count = 1;
+                UDPTLSessionPrx session = factory->allocate(params);
 
-            PayloadMap payloads;
+                if (session == 0)
+                {
+                    mImplPriv->mStreams.erase(boost::lexical_cast<std::string>(stream));
+                    newStreams.erase(boost::lexical_cast<std::string>(stream));
+                    continue;
+                }
 
-            addFormatstoSDP(formats, media, payloads);
+                StreamSinkUDPTLPrx sink = StreamSinkUDPTLPrx::uncheckedCast(session->getSinks().front());
+                mImplPriv->mSinks.push_back(sink);
+                ourStream->sinks.push_back(sink);
 
-            // Push the payload mapping to the RTP session so it'll correctly map things
-            session->associatePayloads(payloads);
-        }
+                StreamSourceUDPTLPrx source = StreamSourceUDPTLPrx::uncheckedCast(session->getSources().front());
+                mImplPriv->mSources.push_back(source);
+                ourStream->sources.push_back(source);
 
-        // Record the old state so we can relay the state change to the controller if needed
-        StreamState oldState = ourStream->state;
-        
-        // Determine the state of the stream and update it
-    if (pjmedia_sdp_media_find_attr2(offer->media[stream], "sendonly", NULL))
-        {
-            ourStream->state = SendOnly;
-        }
-        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "recvonly", NULL))
-        {
-            ourStream->state = ReceiveOnly;
-        }
-        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "inactive", NULL))
-        {
-            ourStream->state = Inactive;
-        }
-        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "sendrecv", NULL))
-        {
-            ourStream->state = SendAndReceive;
-        }
+                mImplPriv->mUDPTLSessions.push_back(session);
 
-        // If the RTP session supports RTCP determine the connection details for it
-        RTCP::V1::RTCPSessionPrx rtcpSession;
-        if ((rtcpSession = RTCP::V1::RTCPSessionPrx::checkedCast(session, RTCP::V1::SessionFacet)))
-        {
-            // Assume RTCP is destined for the same address and the RTP port + 1 as it probably is
-            std::string rtcpConnection = connection;
-            int rtcpPort = offer->media[stream]->desc.port + 1;
+                mImplPriv->mSDP->media_count = 0;
 
-            pjmedia_sdp_attr *attr;
-            pjmedia_sdp_rtcp_attr rtcpAttr;
+                pjmedia_sdp_media *media = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
+                mImplPriv->mSDP->media[mImplPriv->mSDP->media_count++] = media;
 
-            if ((attr = pjmedia_sdp_media_find_attr2(offer->media[stream], "rtcp", NULL)) &&
-                (pjmedia_sdp_attr_get_rtcp(attr, &rtcpAttr) == PJ_SUCCESS))
-            {
-                rtcpPort = rtcpAttr.port;
+                media->desc.media = offer->media[stream]->desc.media;
+                media->desc.transport = offer->media[stream]->desc.transport;
+		media->desc.fmt[0] = offer->media[stream]->desc.fmt[0];
+		media->desc.fmt_count++;
+
+                media->conn = allocate_from_pool<pjmedia_sdp_conn>(mImplPriv->mDialog->pool);
+                media->conn->net_type = offer->origin.net_type;
+                media->conn->addr_type = offer->origin.addr_type;
+
+                AddressInformation info = source->getLocalDetails();
+
+                pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr, info.ipAddress.c_str());
+
+                if (!mImplPriv->mSDP->conn)
+                {
+                    mImplPriv->mSDP->conn = media->conn;
+                }
+
+                media->desc.port = (pj_uint16_t) info.port;
+                media->desc.port_count = 1;
 
-                if (rtcpAttr.addr.slen)
+                SDPDescriptorPtr ourDescriptor = mImplPriv->mEndpoint->getInterpretedDescriptor(t38);
+                for (SDPFormatParameterSeq::const_iterator parameter = ourDescriptor->parameters.begin();
+                     parameter != ourDescriptor->parameters.end();
+                     ++parameter)
                 {
-                    rtcpConnection = std::string(pj_strbuf(&rtcpAttr.addr), pj_strlen(&rtcpAttr.addr));
+                    pjmedia_sdp_attr *attr = allocate_from_pool<pjmedia_sdp_attr>(mImplPriv->mDialog->pool);
+                    pj_strdup2(mImplPriv->mDialog->pool, &attr->name, (*parameter).c_str());
+                    media->attr[media->attr_count++] = attr;
                 }
             }
 
-            rtcpSession->setRemoteDetails(rtcpConnection, rtcpPort);
-        }
+            StreamSinkUDPTLPrx sink = StreamSinkUDPTLPrx::uncheckedCast(ourStream->sinks.front());
+            try
+            {
+                sink->setRemoteDetails(connection, offer->media[stream]->desc.port);
+            }
+            catch (const AsteriskSCF::Network::V1::InvalidAddress&)
+            {
+                pjsip_tx_data *packet;
+                if (pjsip_inv_end_session(mImplPriv->mInviteSession, 488, NULL, &packet) == PJ_SUCCESS)
+                {
+                    pjsip_inv_send_msg(mImplPriv->mInviteSession, packet);
+                }
+                return 0;
+            }
 
-        // If the state changed we need to notify the controller if one is around
-        if (oldState != ourStream->state)
-        {
-            streamsChanged.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream->state));
-        }
+            T38UdptlFormatPtr t38Configuration = T38UdptlFormatPtr::dynamicCast(mImplPriv->mEndpoint->getFormat(t38));
+
+            // If the max datagram has been overridden then use that instead of what they offered
+            if (t38Configuration->maxDatagram)
+            {
+                sink->setFarMaxDatagram(t38Configuration->maxDatagram);
+            }
+            else
+            {
+                sink->setFarMaxDatagram(t38->maxDatagram);
+            }
 
-        // Update connection information
-        StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(ourStream->sinks.front());
+            sink->setErrorCorrectionScheme(t38->errorCorrection);
 
-        // If the remote party attempts to use an invalid address bring down the session since obviously
-        // something is critically wrong and stuff will most likely fall apart quickly
-        try
+	    break;
+        }
+        else
         {
-            sink->setRemoteDetails(connection, offer->media[stream]->desc.port);
+            // The type of this stream is just unknown to us, we can not accept it
+            mImplPriv->mStreams.erase(boost::lexical_cast<std::string>(stream));
+            newStreams.erase(boost::lexical_cast<std::string>(stream));
+            continue;
         }
-        catch (const InvalidAddress&)
+
+        // If the state changed we need to notify the controller if one is around
+        if (oldState != ourStream->state)
         {
-            pjsip_tx_data *packet;
-            if (pjsip_inv_end_session(mImplPriv->mInviteSession, 488, NULL, &packet) == PJ_SUCCESS)
-            {
-                pjsip_inv_send_msg(mImplPriv->mInviteSession, packet);
-            }
-            return 0;
+            streamsChanged.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream->state));
         }
     }
 
@@ -3571,6 +3916,14 @@ RTPMediaSessionDict SipSession::getRTPMediaSessions()
 }
 
 /**
+ * Internal function which returns the UDPTL media sessions that are hidden inside the SIP session.
+ */
+UDPTLMediaSessionSeq SipSession::getUDPTLMediaSessions()
+{
+    return mImplPriv->mUDPTLSessions;
+}
+
+/**
  * Internal function which sets the streams explicitly.
  */
 void SipSession::setStreams(const AsteriskSCF::Media::V1::StreamInformationDict& streams)
diff --git a/src/SipSession.h b/src/SipSession.h
index bb36ff5..50da5f9 100644
--- a/src/SipSession.h
+++ b/src/SipSession.h
@@ -141,6 +141,7 @@ public:
         const Ice::Identity& controllerid,
         const Ice::Identity& mediaid, 
 	const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionDict& mediasessions,
+        const AsteriskSCF::Replication::SipSessionManager::V1::UDPTLMediaSessionSeq& udptlMediaSessions,
         const AsteriskSCF::Media::V1::StreamSourceSeq& sources, 
 	const AsteriskSCF::Media::V1::StreamSinkSeq& sinks,
         const PJSipManagerPtr& manager, 
@@ -331,6 +332,8 @@ public:
 
     AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionDict getRTPMediaSessions();
 
+    AsteriskSCF::Replication::SipSessionManager::V1::UDPTLMediaSessionSeq getUDPTLMediaSessions();
+
     void enqueueSessionWork(const AsteriskSCF::System::WorkQueue::V1::SuspendableWorkPtr&);
 
     void setTelephonyEventSourcesAndSinks(const SipEndpointConfig& config);
@@ -355,11 +358,12 @@ private:
         bool ipv6, bool isUAC, const SipEndpointConfig& config, const NATEndpointOptions& natOptions);
 
     SipSession(const Ice::ObjectAdapterPtr&, const SipEndpointPtr&, const std::string&, const Ice::Identity&, const Ice::Identity&,
-        const Ice::Identity&, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionDict&,
-        const AsteriskSCF::Media::V1::StreamSourceSeq&, const AsteriskSCF::Media::V1::StreamSinkSeq&,
-        const PJSipManagerPtr& manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
-        const SipReplicationContextPtr& replicationContext,
-        bool isUAC, const SipEndpointConfig& config, const NATEndpointOptions& natOptions);
+               const Ice::Identity&, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionDict&,
+               const AsteriskSCF::Replication::SipSessionManager::V1::UDPTLMediaSessionSeq&,
+               const AsteriskSCF::Media::V1::StreamSourceSeq&, const AsteriskSCF::Media::V1::StreamSinkSeq&,
+               const PJSipManagerPtr& manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
+               const SipReplicationContextPtr& replicationContext,
+               bool isUAC, const SipEndpointConfig& config, const NATEndpointOptions& natOptions);
 
 
     void initializePJSIPStructs();
diff --git a/src/SipStateReplicatorListener.cpp b/src/SipStateReplicatorListener.cpp
index 168b25d..689785a 100644
--- a/src/SipStateReplicatorListener.cpp
+++ b/src/SipStateReplicatorListener.cpp
@@ -326,7 +326,8 @@ public:
                     // Now that all is well we can create an actual session
                     SipSessionPtr localSession = endpoint->createSession("", session->sessionObjectId,
                                                                          session->sessionControllerObjectId,
-                        session->mediaSessionObjectId, session->rtpMediaSessions, session->sources, session->sinks);
+                                                                         session->mediaSessionObjectId, session->rtpMediaSessions,
+                                                                         session->udptlMediaSessions, session->sources, session->sinks);
                     localitem->setSession(localSession);
                 }
                 else

commit bf97df459f0c054d095331534252ec3755abc1e2
Author: Joshua Colp <jcolp at digium.com>
Date:   Mon Oct 3 19:07:36 2011 -0300

    Merge direct media connection support.

diff --git a/config/Sip.config b/config/Sip.config
index 5646d97..7946dba 100644
--- a/config/Sip.config
+++ b/config/Sip.config
@@ -92,6 +92,10 @@ rtpoveripv6=no
 # Allowable media formats for the endpoint. Each format is separated using , and follows the format
 # <name>/<sample rate>@<frame size>;<format specific parameters>
 formats=ulaw/8000,alaw/8000
+
+# Whether media should be allowed to flow directly between endpoints or not
+directmedia=no
+
 #
 # Simple fields for a single identity record
 name='robert foo bar'
@@ -143,3 +147,4 @@ ids=bob-bar-office,bob-bar-cell
 #
 # baseport=[pick a number]
 
+
diff --git a/config/SipConfigurator.py b/config/SipConfigurator.py
index bd9a383..5505f2a 100755
--- a/config/SipConfigurator.py
+++ b/config/SipConfigurator.py
@@ -203,6 +203,9 @@ class SipSectionVisitors(Configurator.SectionVisitors):
         item = AsteriskSCF.Configuration.SipSessionManager.V1.SipRTPMediaServiceItem()
         mapper.map('rtpoveripv6', item, 'requireIPv6', 'mediaservice', config.getboolean, None)
 
+        item = AsteriskSCF.Configuration.SipSessionManager.V1.DirectMediaItem()
+        mapper.map('directmedia', item, 'enabled', 'directmedia', config.getboolean, None)
+
... 3616 lines suppressed ...


-- 
asterisk-scf/integration/sip.git



More information about the asterisk-scf-commits mailing list