[asterisk-scf-commits] asterisk-scf/release/sip.git branch "master" updated.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Mon Oct 3 18:04:24 CDT 2011


branch "master" has been updated
       via  d1c51b7c05833b65bffdc3599253dd6690580d34 (commit)
      from  bf97df459f0c054d095331534252ec3755abc1e2 (commit)

Summary of changes:
 config/Sip.config                                  |   24 +
 config/SipConfigurator.py                          |   51 +-
 .../SipSessionManager/SipConfigurationIf.ice       |   26 +
 .../SipSessionManager/SipStateReplicationIf.ice    |    3 +
 src/NATOptions.h                                   |    6 +-
 src/PJSipSessionModule.cpp                         |    9 +-
 src/SipConfiguration.cpp                           |   12 +
 src/SipEndpoint.cpp                                |  102 ++-
 src/SipEndpoint.h                                  |   22 +-
 src/SipSession.cpp                                 |  863 ++++++++++++++------
 src/SipSession.h                                   |   14 +-
 src/SipStateReplicatorListener.cpp                 |    3 +-
 12 files changed, 830 insertions(+), 305 deletions(-)


- Log -----------------------------------------------------------------
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

-----------------------------------------------------------------------


-- 
asterisk-scf/release/sip.git



More information about the asterisk-scf-commits mailing list