[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 Aug 1 13:58:17 CDT 2011


branch "master" has been updated
       via  1ac8012e89cb5412937c803dbb6b7451d08b297d (commit)
       via  319dee0a90edc3e89106b2ac39c464f294602516 (commit)
       via  685298fee308c586f5fb058740da50cf83c91847 (commit)
       via  713bee189b2ce86098341723895204c3633e32a9 (commit)
      from  736155bbeb65c6636154eb241939bdee6ad97947 (commit)

Summary of changes:
 config/SipConfigurator.py                          |    5 +-
 .../SipSessionManager/SipConfigurationIf.ice       |    2 +-
 .../SipSessionManager/SipStateReplicationIf.ice    |    2 +
 src/PJSipSessionModule.cpp                         |  180 +++--
 src/PJSipSessionModule.h                           |    1 +
 src/PJSipSessionModuleConstruction.cpp             |    6 +
 src/SipConfiguration.cpp                           |    3 +-
 src/SipEndpoint.cpp                                |   24 +-
 src/SipEndpoint.h                                  |    5 +-
 src/SipSession.cpp                                 |  877 +++++++++++++++++---
 src/SipSession.h                                   |   31 +-
 src/SipStateReplicatorListener.cpp                 |    2 +
 12 files changed, 915 insertions(+), 223 deletions(-)


- Log -----------------------------------------------------------------
commit 1ac8012e89cb5412937c803dbb6b7451d08b297d
Author: Joshua Colp <jcolp at digium.com>
Date:   Mon Aug 1 15:36:44 2011 -0300

    Throw exception instead of returning nothing.

diff --git a/src/SipSession.cpp b/src/SipSession.cpp
index 430b51a..b354d31 100755
--- a/src/SipSession.cpp
+++ b/src/SipSession.cpp
@@ -1111,7 +1111,8 @@ public:
             
             if (mImplPriv->mSessionController)
             {
-                mCb->ice_response(0);
+                mCb->ice_exception(AsteriskSCF::SessionCommunications::V1::ControllerAlreadySet());
+                return Complete;
             }
 
             mImplPriv->mSessionController = mController;

commit 319dee0a90edc3e89106b2ac39c464f294602516
Author: Joshua Colp <jcolp at digium.com>
Date:   Mon Aug 1 15:26:41 2011 -0300

    Remove a strange message.

diff --git a/src/SipConfiguration.cpp b/src/SipConfiguration.cpp
index 8dec889..65a40a2 100644
--- a/src/SipConfiguration.cpp
+++ b/src/SipConfiguration.cpp
@@ -1131,7 +1131,6 @@ private:
         {
             endpointGroup = i->second;
         }
-        std::cerr << "XXX wha" << std::endl;
         SipEndpointPtr endpoint = mEndpointFactory->findByName(group->name);
         if (!endpoint)
         {

commit 685298fee308c586f5fb058740da50cf83c91847
Author: Joshua Colp <jcolp at digium.com>
Date:   Mon Aug 1 15:26:03 2011 -0300

    Remove duplicate logic caused by merge.

diff --git a/config/SipConfigurator.py b/config/SipConfigurator.py
index 99dac9e..49a7b62 100755
--- a/config/SipConfigurator.py
+++ b/config/SipConfigurator.py
@@ -230,29 +230,6 @@ class SipSectionVisitors(Configurator.SectionVisitors):
 
         self.groups.append(group)
 
-        try:
-            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
-                item.formatSpecific = [ ]
-                if formatSpecific:
-                    item.formatSpecific.append(formatSpecific)
-
-                group.configurationItems[format] = item
-        except:
-            traceback.print_exc()
-            print 'No configured formats for endpoint ' + section
-
     def visit_unsupported(self, config, section):
         if config.get(section, 'type') == 'transport-udp':
             self.visit_transport_udp(config, section)

commit 713bee189b2ce86098341723895204c3633e32a9
Author: Joshua Colp <jcolp at digium.com>
Date:   Mon Aug 1 14:51:02 2011 -0300

    Add support for media streams and session controllers.

diff --git a/config/SipConfigurator.py b/config/SipConfigurator.py
index 2d61c7a..99dac9e 100755
--- a/config/SipConfigurator.py
+++ b/config/SipConfigurator.py
@@ -206,6 +206,28 @@ class SipSectionVisitors(Configurator.SectionVisitors):
             mapper.execute(group, section, option)
         mapper.finish(group)
 
+        try:
+            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
+                item.formatSpecific = [ ]
+                if formatSpecific:
+                    item.formatSpecific.append(formatSpecific)
+
+                group.configurationItems[format] = item
+        except:
+            print 'No configured formats for endpoint ' + section
+
         self.groups.append(group)
 
         try:
diff --git a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
index c0c85fb..715e039 100644
--- a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
+++ b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
@@ -429,7 +429,7 @@ class SipMediaFormatItem extends SipConfigurationItem
    int frameSize;
 
    /**
-    * Any format specific parameters
+    * Format specific attributes.
     */
    Ice::StringSeq formatSpecific;
 };
diff --git a/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice b/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice
index 59e121c..06e7f17 100644
--- a/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice
+++ b/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice
@@ -155,8 +155,10 @@ module V1
       string mEndpointName;
       Ice::Identity mSessionObjectId;
       Ice::Identity mMediaSessionObjectId;
+      Ice::Identity mSessionControllerObjectId;
       AsteriskSCF::Media::V1::StreamSourceSeq mSources;
       AsteriskSCF::Media::V1::StreamSinkSeq mSinks;
+      AsteriskSCF::Media::V1::StreamInformationDict mStreams;
       RTPMediaSessionSeq mRTPMediaSessions;
       SessionListenerSeq mListeners;
       AsteriskSCF::SessionCommunications::V1::Bridge *mBridge;
diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index 90da6e7..76d0d54 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -23,6 +23,8 @@
 
 #include <IceUtil/UUID.h>
 
+#include <boost/lexical_cast.hpp>
+
 #include <AsteriskSCF/Core/Endpoint/EndpointIf.h>
 #include <AsteriskSCF/Core/Routing/RoutingIf.h>
 #include <AsteriskSCF/SessionCommunications/SessionCommunicationsIf.h>
@@ -100,10 +102,12 @@ void PJSipSessionModInfo::updateSessionState(pjsip_inv_session *inv_session)
         mSessionState->mEndpointName = mSession->getEndpoint()->getName();
         mSessionState->mSessionObjectId = mSession->getSessionProxy()->ice_getIdentity();
         mSessionState->mMediaSessionObjectId = mSession->getMediaSessionProxy()->ice_getIdentity();
+	mSessionState->mSessionControllerObjectId = mSession->getOurSessionControllerProxy()->ice_getIdentity();
         mSessionState->mSources = mSession->getSources();
         mSessionState->mSinks = mSession->getSinks();
         mSessionState->mRTPMediaSessions = mSession->getRTPMediaSessions();
         mSessionState->mListeners = mSession->getListeners();
+	mSessionState->mStreams = mSession->getStreams();
         try
         {
             mSessionState->mBridge = mSession->getBridge();
@@ -461,16 +465,17 @@ protected:
         // Create an SDP offer or answer
 	const pjmedia_sdp_session *remote_sdp = NULL;
         pjmedia_sdp_session *sdp;
+        StreamInformationDict streams;
 
 	if (!mInv->neg || (pjmedia_sdp_neg_get_neg_remote(mInv->neg, &remote_sdp) != PJ_SUCCESS))
         {
             // No SDP was present in the INVITE so we need to create an offer
-            sdp = mSession->createSDPOffer();
+            sdp = mSession->createSDPOffer(StreamInformationDict(), streams);
         }
         else
         {
             // SDP was present in the INVITE so we need to create an answer using their offer
-            sdp = mSession->createSDPAnswer(remote_sdp);
+            sdp = mSession->createSDPAnswer(remote_sdp, streams);
         }
 
         if (!sdp)
@@ -1213,6 +1218,7 @@ protected:
         {
             lg(Error) << "Ice exception when attempting to indicate something or other";
         }
+
         return Complete;
     }
 private:
@@ -1498,13 +1504,9 @@ void PJSipSessionModule::invOnTsxStateChanged(pjsip_inv_session *inv, pjsip_tran
     }
 }
 
-/**
- * This is the only of the PJSIP callbacks that involves no queueing. 
- */
-void PJSipSessionModule::invOnRxOffer(pjsip_inv_session* inv, const pjmedia_sdp_session*)
+void PJSipSessionModule::invOnRxOffer(pjsip_inv_session*, const pjmedia_sdp_session*)
 {
-    PJSipSessionModInfo *session_mod_info = (PJSipSessionModInfo*)inv->mod_data[mModule.id];
-    pjsip_inv_set_sdp_answer(inv, session_mod_info->getSessionPtr()->createSDPOffer());
+    //stub
 }
 
 void PJSipSessionModule::invOnCreateOffer(pjsip_inv_session*, pjmedia_sdp_session**)
@@ -1536,77 +1538,9 @@ protected:
         PJSipSessionModInfo *session_mod_info = (PJSipSessionModInfo*)mInv->mod_data[mModuleId];
         SipSessionPtr session = session_mod_info->getSessionPtr();
 
-        //
-        // In case there is no stream level connection details store this away
-        //
-        std::string destination(pj_strbuf(&remote_sdp->conn->addr), pj_strlen(&remote_sdp->conn->addr));
-	StreamSinkSeq sinks = session->getSinks();
-
-        // Each stream has its own set of formats, so go to that granularity
-        for (unsigned int stream = 0; stream < remote_sdp->media_count; stream++)
-        {
-	    // Do some sanity checking to confirm we have a stream setup for their answer stream
-            if (sinks.size() < (stream + 1))
-            {
-                continue;
-            }
-
-            // Assume no stream level details until proven otherwise
-            std::string connection = destination;
-            if (remote_sdp->media[stream]->conn)
-            {
-                connection = std::string(pj_strbuf(&remote_sdp->media[stream]->conn->addr),
-                                         pj_strlen(&remote_sdp->media[stream]->conn->addr));
-            }
-
-            // TODO: Add parsing of RTCP attribute
-
-            // The order of the media lines should match our offer so we can get the sink to update
-            // connection details on based on what stream number this is, easy!
-            StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(sinks[stream]);
-
-            // If this throws an exception then they answered with an address that is incompatible
-            // for the session, so we abort since our relationship with the other side seems to be
-            // broken
-            try
-            {
-                sink->setRemoteDetails(connection, remote_sdp->media[stream]->desc.port);
-            }
-            catch (const InvalidAddress&)
-            {
-                pjsip_tx_data *packet;
-                if (pjsip_inv_end_session(mInv, 488, NULL, &packet) == PJ_SUCCESS)
-                {
-                    pjsip_inv_send_msg(mInv, packet);
-                }
-                return Complete;
-            }
-
-            // TODO: Add format parsing stuff here so we can determine the formats actually negotiated
-        }
+        StreamInformationDict added;
+        session->createSDPAnswer(remote_sdp, added);
 
-        //
-        // Assuming all of the pre-setup for the sinks/sources went ok, then we can tell
-        // the session to start off any negotiated connections.
-        //
-	try
-        {
-            const pjmedia_sdp_session* local;
-            pj_status_t result = pjmedia_sdp_neg_get_active_local(mInv->neg, &local);
-            if (result == PJ_SUCCESS)
-            {
-                session->startMedia(local, remote_sdp);
-            }
-        }
-	catch (const AsteriskSCF::Media::RTP::V1::InvalidAddress&)
-	{
-	    pjsip_tx_data *packet;
-            if (pjsip_inv_end_session(mInv, 488, NULL, &packet) == PJ_SUCCESS)
-            {
-                pjsip_inv_send_msg(mInv, packet);
-            }
-            return Complete;
-	}
         return Complete;
     }
 
@@ -1646,6 +1580,96 @@ void PJSipSessionModule::authTimeout(pj_timer_heap_t *timer_heap, pj_timer_entry
     mAuthManager->authTimeout(timer_heap, entry);
 }
 
+class HandleSendReinviteResponse : public SipQueueableOperation
+{
+public:
+    HandleSendReinviteResponse(pjsip_inv_session *inv, const int moduleId, pjsip_tx_data *tdata)
+        : mInv(inv), mModuleId(moduleId), mResponse(tdata) { }
+
+protected:
+    SuspendableWorkResult initial(const SuspendableWorkListenerPtr&)
+    {
+        PJSipSessionModInfo *session_mod_info = (PJSipSessionModInfo*)mInv->mod_data[mModuleId];
+        SipSessionPtr session = session_mod_info->getSessionPtr();
+        SessionControllerPrx controller = session->getSessionControllerProxy();
+
+        // If we have no session controller respond to the reinvite with the same SDP that we previously had
+        if (!controller)
+        {
+            pjsip_inv_set_sdp_answer(mInv, session->modifySDP(StreamInformationDict()));
+            pjsip_inv_send_reinvite_response(mInv, mResponse);
+            return Complete;
+        }
+
+        const pjmedia_sdp_session *offer_sdp;
+        pj_status_t status;
+        if ((status = pjmedia_sdp_neg_get_neg_remote(mInv->neg, &offer_sdp)) != PJ_SUCCESS)
+        {
+            return Complete;
+        }
+
+        // Call into the session for the serious work
+	session->createSDPAnswer(offer_sdp, mStreamsAdded);
+
+        // If no streams were added we can respond to the offer with the SDP we had previously and move on
+        if (mStreamsAdded.empty())
+        {
+            pjsip_inv_set_sdp_answer(mInv, session->modifySDP(StreamInformationDict()));
+            pjsip_inv_send_reinvite_response(mInv, mResponse);
+            return Complete;
+        }
+
+        // If any streams were added we need to call into the session controller to see what ones
+        // we can actually accept
+        SipAMICallbackPtr cb(new SipAMICallback(0, session, this, false, true));
+        Ice::CallbackPtr d = Ice::newCallback(cb, &SipAMICallback::callback);
+        session->getSessionControllerProxy()->begin_addStreams(mStreamsAdded, d);
+
+        return Complete;
+    }
+
+    SuspendableWorkResult calledBack(const Ice::AsyncResultPtr& asyncResult)
+    {
+        SessionControllerPrx controller = SessionControllerPrx::uncheckedCast(asyncResult->getProxy());
+        StreamInformationDict accepted = controller->end_addStreams(asyncResult);
+
+        // Remove any streams that were accepted by the session controller, we don't need to do anything
+        // more with them since our initial call to createSDPAnswer will have created the proper SDP
+        for (StreamInformationDict::iterator stream = accepted.begin();
+             stream != accepted.end();
+             ++stream)
+        {
+            mStreamsAdded.erase(stream->first);
+        }
+
+        // We do have to modify the SDP though to remove the streams that were not accepted
+        PJSipSessionModInfo *session_mod_info = (PJSipSessionModInfo*)mInv->mod_data[mModuleId];
+        SipSessionPtr session = session_mod_info->getSessionPtr();
+
+        // Note that this is not a mistake, mStreamsAdded now contains all the streams that were
+        // actually not accepted so we are removing them from the SDP
+        const pjmedia_sdp_session *sdp = session->modifySDP(mStreamsAdded);
+
+        // Since that is now done we can set this new SDP as our answer and send a response to the reinvite
+        pjsip_inv_set_sdp_answer(mInv, sdp);
+	pjsip_inv_send_reinvite_response(mInv, mResponse);
+
+        return Complete;
+    }
+
+private:
+    pjsip_inv_session *mInv;
+    const int mModuleId;
+    pjsip_tx_data *mResponse;
+    StreamInformationDict mStreamsAdded;
+};
+
+void PJSipSessionModule::invOnSendReinviteResponse(pjsip_inv_session* inv, pjsip_tx_data* tdata)
+{
+    lg(Debug) << "Queuing HandleSendReinviteResponse";
+    enqueueSessionWork(new HandleSendReinviteResponse(inv, mModule.id, tdata), inv);
+}
+
 QueuePtr PJSipSessionModule::getThreadPoolQueue()
 {
     return mPoolQueue;
diff --git a/src/PJSipSessionModule.h b/src/PJSipSessionModule.h
index 79b0212..3f9ed89 100644
--- a/src/PJSipSessionModule.h
+++ b/src/PJSipSessionModule.h
@@ -126,6 +126,7 @@ public:
     void invOnMediaUpdate(pjsip_inv_session *inv, pj_status_t status);
     pjsip_redirect_op invOnRedirected(pjsip_inv_session *inv, const pjsip_uri *target, const pjsip_event *e);
     pjsip_dialog *uaOnDialogForked(pjsip_dialog *first_set, pjsip_rx_data *rdata);
+    void invOnSendReinviteResponse(pjsip_inv_session *inv, pjsip_tx_data *tdata);
     // Missing onsendack for now
     void authTimeout(pj_timer_heap_t *timer_heap, pj_timer_entry *entry);
     void replicateState(PJSipDialogModInfo *dlgInfo, PJSipTransactionModInfo *tsxInfo,
diff --git a/src/PJSipSessionModuleConstruction.cpp b/src/PJSipSessionModuleConstruction.cpp
index adcbb8a..7d86a95 100644
--- a/src/PJSipSessionModuleConstruction.cpp
+++ b/src/PJSipSessionModuleConstruction.cpp
@@ -113,6 +113,11 @@ void sessionAuthTimeout(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entr
     return sessionModule->authTimeout(timer_heap, entry);
 }
 
+static void invOnSendReinviteResponse(pjsip_inv_session *inv, pjsip_tx_data *tdata)
+{
+    return sessionModule->invOnSendReinviteResponse(inv, tdata);
+}
+
 PJSipSessionModule::PJSipSessionModule(pjsip_endpoint *endpt,
     const boost::shared_ptr<SipEndpointFactory>& endpointFactoryPtr,
     const AsteriskSCF::Discovery::SmartProxy<AsteriskSCF::SessionCommunications::V1::SessionRouterPrx>& sessionRouter,
@@ -169,6 +174,7 @@ PJSipSessionModule::PJSipSessionModule(pjsip_endpoint *endpt,
         mInvCallback.on_media_update = AsteriskSCF::SipSessionManager::invOnMediaUpdate;
         //mInvCallback.on_send_ack = invOnSendAck;
         mInvCallback.on_redirected = AsteriskSCF::SipSessionManager::invOnRedirected;
+	mInvCallback.on_send_reinvite_response = AsteriskSCF::SipSessionManager::invOnSendReinviteResponse;
     }
 
     pjsip_tsx_layer_init_module(endpt);
diff --git a/src/SipConfiguration.cpp b/src/SipConfiguration.cpp
index b710c35..8dec889 100644
--- a/src/SipConfiguration.cpp
+++ b/src/SipConfiguration.cpp
@@ -189,7 +189,7 @@ class EndpointConfigHelper : public boost::enable_shared_from_this<EndpointConfi
         {
             mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateDirection, mConfig, direction));
         }
-        
+
 	void visitSipMediaFormatItem(const SipMediaFormatItemPtr& format)
 	{
             mUpdates.push_back(boost::bind(&EndpointConfigHelper::addFormat, mConfig, format));
diff --git a/src/SipEndpoint.cpp b/src/SipEndpoint.cpp
index 8c5465a..624da0c 100644
--- a/src/SipEndpoint.cpp
+++ b/src/SipEndpoint.cpp
@@ -21,10 +21,11 @@
 
 #include <AsteriskSCF/Core/Discovery/ServiceLocatorIf.h>
 #include <AsteriskSCF/logger.h>
-#include "NATOptions.h"
 #include <AsteriskSCF/Media/MediaIf.h>
 #include <AsteriskSCF/Media/SDP/MediaSDPIf.h>
 
+#include "NATOptions.h"
+
 using namespace AsteriskSCF::System::Logging;
 using namespace AsteriskSCF::Media::V1;
 using namespace AsteriskSCF::Media::SDP::V1;
@@ -370,6 +371,7 @@ AsteriskSCF::SessionCommunications::V1::SessionPrx SipEndpoint::createSession(co
         mImplPriv->mServiceLocator, mImplPriv->mReplica, mImplPriv->mConfig.sessionConfig.rtpOverIPv6, true, 
         NATEndpointOptions(mImplPriv->mConfig.sessionConfig.rtpOverICE, mImplPriv->mConfig.sessionConfig.rtpICEIncludeTURN,
             mImplPriv->mConfig.transportConfig.enableNAT));
+
     mImplPriv->mSessions.push_back(session);
     std::cout << "And now we're returing a session proxy..." << std::endl;
     return session->getSessionProxy();
@@ -382,17 +384,19 @@ AsteriskSCF::SipSessionManager::SipSessionPtr SipEndpoint::createSession(const s
         NATEndpointOptions(mImplPriv->mConfig.sessionConfig.rtpOverICE, mImplPriv->mConfig.sessionConfig.rtpICEIncludeTURN,
             mImplPriv->mConfig.transportConfig.enableNAT)
         );
+
     mImplPriv->mSessions.push_back(session);
     return session;
 }
 
 AsteriskSCF::SipSessionManager::SipSessionPtr SipEndpoint::createSession(const std::string& destination,
-                                                                         const Ice::Identity& sessionid, const Ice::Identity& mediaid,
+                                                                         const Ice::Identity& sessionid, const Ice::Identity& controllerid,
+                                                                         const Ice::Identity& mediaid,
                                                                          const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq& mediasessions,
                                                                          const AsteriskSCF::Media::V1::StreamSourceSeq& sources,
                                                                          const AsteriskSCF::Media::V1::StreamSinkSeq& sinks)
 {
-    SipSessionPtr session = new SipSession(mImplPriv->mAdapter, this, destination, sessionid, mediaid, mediasessions,
+    SipSessionPtr session = new SipSession(mImplPriv->mAdapter, this, destination, sessionid, controllerid, mediaid, mediasessions,
             sources, sinks, mImplPriv->mManager, mImplPriv->mServiceLocator, mImplPriv->mReplica, false,
             NATEndpointOptions(mImplPriv->mConfig.sessionConfig.rtpOverICE, mImplPriv->mConfig.sessionConfig.rtpICEIncludeTURN,
                 mImplPriv->mConfig.transportConfig.enableNAT));
@@ -478,9 +482,9 @@ SDPDescriptorPtr SipEndpoint::getDescriptor(const FormatPtr& format)
     return 0;
 }
 
-StreamTopologyMap SipEndpoint::getStreamTopology()
+StreamInformationDict SipEndpoint::getStreamTopology()
 {
-    StreamTopologyMap topology;
+    StreamInformationDict topology;
 
     // Iterate through all the configured formats placing the same types on the same stream
     for (std::vector<ConfiguredFormatPtr>::const_iterator configuredFormat = mImplPriv->mFormats.begin();
@@ -488,19 +492,19 @@ StreamTopologyMap SipEndpoint::getStreamTopology()
          ++configuredFormat)
     {
         // See if a stream already exists for this type
-        StreamTopologyMap::iterator stream = topology.find((*configuredFormat)->getDescriptor()->type);
+        StreamInformationDict::iterator stream = topology.find((*configuredFormat)->getDescriptor()->type);
 
         if (stream == topology.end())
         {
             // If one does not exist we have to create it and add it to the map
-            FormatSeq formats;
-            formats.push_back((*configuredFormat)->getFormat());
-            topology.insert(make_pair((*configuredFormat)->getDescriptor()->type, formats));
+            StreamInformationPtr newStream = new StreamInformation();
+            newStream->formats.push_back((*configuredFormat)->getFormat());
+            topology.insert(make_pair((*configuredFormat)->getDescriptor()->type, newStream));
         }
         else
         {
             // If one does exist we can simply add the format to it
-            stream->second.push_back((*configuredFormat)->getFormat());
+            stream->second->formats.push_back((*configuredFormat)->getFormat());
         }
     }
 
diff --git a/src/SipEndpoint.h b/src/SipEndpoint.h
index 2ce13ff..a0657d5 100644
--- a/src/SipEndpoint.h
+++ b/src/SipEndpoint.h
@@ -298,7 +298,7 @@ public:
     // dependency insanity
     //
     AsteriskSCF::SipSessionManager::SipSessionPtr createSession(const std::string&);
-    AsteriskSCF::SipSessionManager::SipSessionPtr createSession(const std::string&, const Ice::Identity&,
+    AsteriskSCF::SipSessionManager::SipSessionPtr createSession(const std::string&, const Ice::Identity&, const Ice::Identity&,
                                                                 const Ice::Identity&, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq&,
                                                                 const AsteriskSCF::Media::V1::StreamSourceSeq&, const AsteriskSCF::Media::V1::StreamSinkSeq&);
 
@@ -330,11 +330,10 @@ public:
     /**
      * API call which returns the stream topology to be used for an SDP offer.
      */
-    StreamTopologyMap getStreamTopology();
-
     void setMediaNATOptions(bool useICE, bool useTURN);
     void setSRTPOptions(const SipEndpointMediaSRTPConfig& srtpConfig);
     void setSignalingNATOptions(bool enable);
+    AsteriskSCF::Media::V1::StreamInformationDict getStreamTopology();
 
 private:
     /**
diff --git a/src/SipSession.cpp b/src/SipSession.cpp
index fd5758e..430b51a 100755
--- a/src/SipSession.cpp
+++ b/src/SipSession.cpp
@@ -29,6 +29,8 @@
 #include <AsteriskSCF/System/WorkQueue/WorkQueueIf.h>
 #include <AsteriskSCF/logger.h>
 #include <AsteriskSCF/System/NAT/NATTraversalIf.h>
+#include <AsteriskSCF/Media/MediaIf.h>
+#include <AsteriskSCF/Media/RTP/MediaRTPIf.h>
 #include <AsteriskSCF/Media/SDP/MediaSDPIf.h>
 #include "NATOptions.h"
 
@@ -98,8 +100,7 @@ public:
         const AsteriskSCF::System::Component::V1::ReplicaPtr& replica,
         const NATEndpointOptions& natOptions)
         : mAdapter(adapter), mDialog(0), mInviteSession(0), mEndpoint(endpoint), mDestination(destination),
-          mManager(manager), mServiceLocator(serviceLocator), mReplica(replica), mNatOptions(natOptions), mSDP(0),
-          mSDPFinalized(false)
+          mManager(manager), mServiceLocator(serviceLocator), mReplica(replica), mNatOptions(natOptions), mSDP(0)
     { 
     }
 
@@ -197,6 +198,16 @@ public:
     AsteriskSCF::Media::V1::SessionPrx mMediaSessionProxy;
 
     /**
+     * An instance of a session controller.
+     */
+    AsteriskSCF::SessionCommunications::V1::SessionControllerPtr mOurSessionController;
+
+    /**
+     * A proxy to our own session controller.
+     */
+    AsteriskSCF::SessionCommunications::V1::SessionControllerPrx mOurSessionControllerProxy;
+
+    /**
      * A proxy to this communications session.
      */
     AsteriskSCF::SessionCommunications::V1::SessionPrx mSessionProxy;
@@ -282,9 +293,30 @@ public:
     pjmedia_sdp_session *mSDP;
 
     /**
-     * Whether the SDP has been finalized or not.
+     * Streams present on the session.
      */
-    bool mSDPFinalized;
+    StreamInformationDict mStreams;
+
+    /**
+     * Newly added streams that are awaiting a response from the remote party on acceptance.
+     */
+    StreamInformationDict mNewStreams;
+
+    /**
+     * A proxy to the SessionController we are controlling.
+     */
+    AsteriskSCF::SessionCommunications::V1::SessionControllerPrx mSessionController;
+
+    /**
+     * A pointer to the callback class used to send a response to the SessionController with which streams
+     * have been added.
+     */
+    AsteriskSCF::SessionCommunications::V1::AMD_SessionController_addStreamsPtr mAddStreamsCb;
+
+    /**
+     * A mapping for requested stream identifier to our stream identifier.
+     */
+    std::map<std::string, std::string> mAddStreamsMapping;
 };
 
 /**
@@ -296,10 +328,266 @@ inline T *allocate_from_pool(pj_pool_t *pool)
     return static_cast<T*>(pj_pool_zalloc(pool, sizeof(T)));
 }
 
+class ChangeStreamStatesOperation : public SuspendableWork
+{
+public:
+    ChangeStreamStatesOperation(
+        const AsteriskSCF::SessionCommunications::V1::AMD_SessionController_changeStreamStatesPtr& cb,
+        const AsteriskSCF::Media::V1::StreamStateDict& streams,
+        const boost::shared_ptr<SipSessionPriv>& sessionPriv)
+        : mCb(cb), mStreams(streams), mImplPriv(sessionPriv) { }
+    
+    SuspendableWorkResult execute(const SuspendableWorkListenerPtr&)
+    {
+        lg(Debug) << "Executing a changeStreamStates Operation";
+
+        // This boolean is set to true if at least one stream is actually changed. This is to prevent
+        // needless reinvites.
+        bool changed = false;
+            
+        // We iterate through each stream making sure we have one that matches it
+        for (AsteriskSCF::Media::V1::StreamStateDict::const_iterator stream = mStreams.begin();
+             stream != mStreams.end();
+             ++stream)
+        {
+            AsteriskSCF::Media::V1::StreamInformationDict::iterator ourStream = mImplPriv->mStreams.find(stream->first);
+                
+            // If we don't have a stream stored locally then they gave us a stream that we really know nothing about it
+            if (ourStream == mImplPriv->mStreams.end())
+            {
+                continue;
+            }
+
+            // If this doesn't actually alter the state of the stream do nothing, since it would just be silly
+            if (ourStream->second->state == stream->second)
+            {
+                continue;
+            }
+            
+            // The implementation of how we store streams and SDP are linked together, if we can find a stream
+            // in our dictionary of streams than it also exists in the SDP
+            pjmedia_sdp_media *media = mImplPriv->mSDP->media[boost::lexical_cast<int>(stream->first)];
+
+            // Depending on the current state go ahead and remove the current attribute
+            if (ourStream->second->state == SendAndReceive)
+            {
+                pjmedia_sdp_media_remove_all_attr(media, "sendrecv");
+            }
+            else if (ourStream->second->state == SendOnly)
+            {
+                pjmedia_sdp_media_remove_all_attr(media, "sendonly");
+            }
+            else if (ourStream->second->state == ReceiveOnly)
+            {
+                pjmedia_sdp_media_remove_all_attr(media, "recvonly");
+            }
+            else if (ourStream->second->state == Inactive)
+            {
+                pjmedia_sdp_media_remove_all_attr(media, "inactive");
+            }
+
+            // Now that the old attribute is removed we can go ahead and update our state
+            ourStream->second->state = stream->second;
+
+            // Now we can go ahead and add in the corret attribute
+            pjmedia_sdp_attr *attr = NULL;
+                
+            if (ourStream->second->state == SendAndReceive)
+            {
+                attr = pjmedia_sdp_attr_create(mImplPriv->mDialog->pool, "sendrecv", NULL);
+            }
+            else if (ourStream->second->state == SendOnly)
+            {
+                attr = pjmedia_sdp_attr_create(mImplPriv->mDialog->pool, "sendonly", NULL);
+            }
+            else if (ourStream->second->state == ReceiveOnly)
+            {
+                attr = pjmedia_sdp_attr_create(mImplPriv->mDialog->pool, "recvonly", NULL);
+            }
+            else if (ourStream->second->state == Inactive)
+            {
+                attr = pjmedia_sdp_attr_create(mImplPriv->mDialog->pool, "inactive", NULL);
+            }
+
+            if (attr)
+            {
+                pjmedia_sdp_media_add_attr(media, attr);
+                changed = true;
+            }
+        }
+
+        // If any streams were actually updated trigger a reinvite if need be
+        if (changed == true)
+        {
+            // TODO: Add code here to determine if we really do need to send the reinvite, if we haven't actually answered yet
+            // what we really want to do is just update our answer SDP
+            pjsip_tx_data *packet = NULL;
+
+            if ((pjsip_inv_reinvite(mImplPriv->mInviteSession, NULL, mImplPriv->mSDP, &packet)) == PJ_SUCCESS)
+            {
+                pjsip_inv_send_msg(mImplPriv->mInviteSession, packet);
+            }
+        }
+
+        mCb->ice_response();
+   
+        return Complete;
+    }
+    
+private:
+    AsteriskSCF::SessionCommunications::V1::AMD_SessionController_changeStreamStatesPtr mCb;
+    AsteriskSCF::Media::V1::StreamStateDict mStreams;
+    boost::shared_ptr<SipSessionPriv> mImplPriv;
+};
+
+class AddStreamsOperation : public SuspendableWork
+{
+public:
+    AddStreamsOperation(
+        const AsteriskSCF::SessionCommunications::V1::AMD_SessionController_addStreamsPtr& cb,
+        const AsteriskSCF::Media::V1::StreamInformationDict& streams,
+        const boost::shared_ptr<SipSessionPriv>& sessionPriv,
+        const SipSessionPtr& session)
+        : mCb(cb), mStreams(streams), mImplPriv(sessionPriv), mSession(session) { }
+
+    SuspendableWorkResult execute(const SuspendableWorkListenerPtr&)
+    {
+        lg(Debug) << "Executing an addStreams Operation";
+
+        // If there is an outstanding transaction then no streams can be added at this time
+        if (mImplPriv->mInviteSession->invite_tsx)
+        {
+            mCb->ice_response(StreamInformationDict());
+            return Complete;
+        }
+
+        // Create an offer adding in the requested streams
+        StreamInformationDict added;
+        pjmedia_sdp_session *sdp = mSession->createSDPOffer(mStreams, added);
+
+        // If no streams were actually added respond back appropriately
+        if (added.empty())
+        {
+            mCb->ice_response(StreamInformationDict());
+            return Complete;
+        }
+
+        // Store callback information so when the remote party responds with which streams were accepted we can
+        // communicate it to the controller
+        mImplPriv->mAddStreamsCb = mCb;
+
+        // Okay, create and send the reinvite!
+        pjsip_tx_data *packet = NULL;
+            
+        if ((pjsip_inv_reinvite(mImplPriv->mInviteSession, NULL, sdp, &packet)) == PJ_SUCCESS)
+        {
+            pjsip_inv_send_msg(mImplPriv->mInviteSession, packet);
+        }
+        else
+        {
+            // If we couldn't create the reinvite no streams were added
+            mCb->ice_response(StreamInformationDict());
+        }
+
+        return Complete;
+    }
+
+private:
+    AsteriskSCF::SessionCommunications::V1::AMD_SessionController_addStreamsPtr mCb;
+    AsteriskSCF::Media::V1::StreamInformationDict mStreams;
+    boost::shared_ptr<SipSessionPriv> mImplPriv;
+    SipSessionPtr mSession;
+};
+
+class RemoveStreamsOperation : public SuspendableWork
+{
+public:
+    RemoveStreamsOperation(
+        const AsteriskSCF::SessionCommunications::V1::AMD_SessionController_removeStreamsPtr& cb,
+        const AsteriskSCF::Media::V1::StreamInformationDict& streams,
+        const boost::shared_ptr<SipSessionPriv>& sessionPriv,
+        const SipSessionPtr& session)
+        : mCb(cb), mStreams(streams), mImplPriv(sessionPriv), mSession(session) { }
+
+    SuspendableWorkResult execute(const SuspendableWorkListenerPtr&)
+    {
+        lg(Debug) << "Executing a removeStreams Operation";
+
+        pjmedia_sdp_session *sdp = mSession->modifySDP(mStreams);
+
+        // If there is an outstanding transaction just set this as the answer SDP, otherwise trigger a reinvite
+        if (!mImplPriv->mInviteSession->invite_tsx)
+        {
+            pjsip_tx_data *packet = NULL;
+
+            if ((pjsip_inv_reinvite(mImplPriv->mInviteSession, NULL, mImplPriv->mSDP, &packet)) == PJ_SUCCESS)
+            {
+                pjsip_inv_send_msg(mImplPriv->mInviteSession, packet);
+            }
+        }
+        else
+        {
+            pjsip_inv_set_sdp_answer(mImplPriv->mInviteSession, sdp);
+        }
+
+        mCb->ice_response();
+
+        return Complete;
+    }
+
+private:
+    AsteriskSCF::SessionCommunications::V1::AMD_SessionController_removeStreamsPtr mCb;
+    AsteriskSCF::Media::V1::StreamInformationDict mStreams;
+    boost::shared_ptr<SipSessionPriv> mImplPriv;
+    SipSessionPtr mSession;
+};
+
+/**
+ * Implementation of a SessionController interface for the SipSession.
+ */
+class SipSessionController : public AsteriskSCF::SessionCommunications::V1::SessionController
+{
+public:
+    SipSessionController(boost::shared_ptr<SipSessionPriv> implPriv, const SipSessionPtr& session) :
+        mImplPriv(implPriv), mSession(session) { }
+    
+    void changeStreamStates_async(const AsteriskSCF::SessionCommunications::V1::AMD_SessionController_changeStreamStatesPtr& cb,
+                                  const AsteriskSCF::Media::V1::StreamStateDict& states, const Ice::Current&)
+    {
+        lg(Debug) << "Queueing changeStreamStates operation";
+        mSession->enqueueSessionWork(new ChangeStreamStatesOperation(cb, states, mImplPriv));
+    }
+    
+    void addStreams_async(const AsteriskSCF::SessionCommunications::V1::AMD_SessionController_addStreamsPtr& cb,
+                          const AsteriskSCF::Media::V1::StreamInformationDict& streams, const Ice::Current&)
+    {
+        lg(Debug) << "Queueing addStreams operation";
+        mSession->enqueueSessionWork(new AddStreamsOperation(cb, streams, mImplPriv, mSession));
+    }
+    
+    void removeStreams_async(const AsteriskSCF::SessionCommunications::V1::AMD_SessionController_removeStreamsPtr& cb,
+                             const AsteriskSCF::Media::V1::StreamInformationDict& streams, const Ice::Current&)
+    {
+        lg(Debug) << "Queueing removeStreams operation";
+        mSession->enqueueSessionWork(new RemoveStreamsOperation(cb, streams, mImplPriv, mSession));
+    }
+    
+private:
+    /**
+     * Private implementation details for SipSession.
+     */
+    boost::shared_ptr<SipSessionPriv> mImplPriv;
+    
+    /**
+     * A pointer to the communications session that created us.
+     */
+    SipSessionPtr mSession;
+};
+
 void SipSession::initializePJSIPStructs()
 {
     SipEndpointConfig& config = mImplPriv->mEndpoint->getConfig();
-    
+   
     std::string prefix;
     if (config.transportConfig.secureTransport == OUTBOUND || config.transportConfig.secureTransport == BOTH)
     {
@@ -399,7 +687,8 @@ void SipSession::initializePJSIPStructs()
     pjsip_inv_session *inviteSession;
 
     // Create an SDP offer based on what is configured
-    pjmedia_sdp_session *sdp = createSDPOffer();
+    StreamInformationDict added;
+    pjmedia_sdp_session *sdp = createSDPOffer(StreamInformationDict(), added);
 
     if ((pjsip_inv_create_uac(dialog, sdp, 0, &inviteSession)) != PJ_SUCCESS)
     {
@@ -476,6 +765,10 @@ SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter, const SipEndpointPt
     mImplPriv->mMediaSessionProxy =
         AsteriskSCF::Media::V1::SessionPrx::uncheckedCast(adapter->addWithUUID(mImplPriv->mMediaSession));
 
+    mImplPriv->mOurSessionController = new SipSessionController(mImplPriv, this);
+    mImplPriv->mOurSessionControllerProxy =
+        AsteriskSCF::SessionCommunications::V1::SessionControllerPrx::uncheckedCast(adapter->addWithUUID(mImplPriv->mOurSessionController));
+
     if (isUAC)
     {
         lg(Debug) << "New session is UAC, so we're creating the necessary PJSIP structures";
@@ -487,11 +780,12 @@ SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter, const SipEndpointPt
  * Replica constructor.
  */
 SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter, const SipEndpointPtr& endpoint,
-        const std::string& destination, const Ice::Identity& /* sessionid */,
-        const Ice::Identity& mediaid, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq& mediasessions,
-        const AsteriskSCF::Media::V1::StreamSourceSeq& sources, const AsteriskSCF::Media::V1::StreamSinkSeq& sinks,
-        const PJSipManagerPtr& manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
-        const AsteriskSCF::System::Component::V1::ReplicaPtr& replica, bool isUAC, const NATEndpointOptions& natOptions)
+                       const std::string& destination, const Ice::Identity& /* sessionid */,
+                       const Ice::Identity& controllerid,
+                       const Ice::Identity& mediaid, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq& mediasessions,
+                       const AsteriskSCF::Media::V1::StreamSourceSeq& sources, const AsteriskSCF::Media::V1::StreamSinkSeq& sinks,
+                       const PJSipManagerPtr& manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
+                       const AsteriskSCF::System::Component::V1::ReplicaPtr& replica, bool isUAC, const NATEndpointOptions& natOptions)
     : mImplPriv(new SipSessionPriv(adapter, endpoint, destination, manager, serviceLocator, replica, natOptions))
 {
     activateIceObjects(mImplPriv->mManager->getSessionModule()->getSessionCreationHooks());
@@ -500,6 +794,10 @@ SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter, const SipEndpointPt
     mImplPriv->mMediaSessionProxy =
         AsteriskSCF::Media::V1::SessionPrx::uncheckedCast(adapter->add(mImplPriv->mMediaSession, mediaid));
 
+    mImplPriv->mOurSessionController = new SipSessionController(mImplPriv, this);
+    mImplPriv->mOurSessionControllerProxy =
+        AsteriskSCF::SessionCommunications::V1::SessionControllerPrx::uncheckedCast(adapter->add(mImplPriv->mOurSessionController, controllerid));
+
     mImplPriv->mRTPSessions = mediasessions;
     mImplPriv->mSources = sources;
     mImplPriv->mSinks = sinks;
@@ -769,6 +1067,115 @@ void SipSession::getBridge_async(
     enqueueSessionWork(new GetBridgeOperation(cb, mImplPriv));
 }
 
+class GetStreamsOperation : public SuspendableWork
+{
+public:
+    GetStreamsOperation(const AsteriskSCF::SessionCommunications::V1::AMD_Session_getStreamsPtr& cb,
+                       const boost::shared_ptr<SipSessionPriv>& sessionPriv)
+        : mCb(cb), mImplPriv(sessionPriv) { }
+
+    SuspendableWorkResult execute(const SuspendableWorkListenerPtr&)
+    {
+        lg(Debug) << "Executing a GetStreams operation";
+        mCb->ice_response(mImplPriv->mStreams);
+        return Complete;
+    }
+
+private:
+    AsteriskSCF::SessionCommunications::V1::AMD_Session_getStreamsPtr mCb;
+    boost::shared_ptr<SipSessionPriv> mImplPriv;
+};
+
+/**
+ * An implementation of the getStreams method as defined in SessionCommunications.ice
+ */
+void SipSession::getStreams_async(
+    const AsteriskSCF::SessionCommunications::V1::AMD_Session_getStreamsPtr& cb,
+    const Ice::Current&)
+{
+    lg(Debug) << "queuing a getStreams operation";
+    enqueueSessionWork(new GetStreamsOperation(cb, mImplPriv));
+}
+
+class SetAndGetSessionControllerOperation : public SuspendableWork
+{
+public:
+    SetAndGetSessionControllerOperation(const AsteriskSCF::SessionCommunications::V1::AMD_Session_setAndGetSessionControllerPtr& cb,
+                                        const AsteriskSCF::SessionCommunications::V1::SessionControllerPrx& controller,
+                                        const boost::shared_ptr<SipSessionPriv>& sessionPriv)
+        : mCb(cb), mController(controller), mImplPriv(sessionPriv) { }
+
+    SuspendableWorkResult execute(const SuspendableWorkListenerPtr&)
+        {
+            lg(Debug) << "Executing a SetAndGetSessionController operation";
+            
+            if (mImplPriv->mSessionController)
+            {
+                mCb->ice_response(0);
+            }
+
+            mImplPriv->mSessionController = mController;
+            mCb->ice_response(mImplPriv->mOurSessionControllerProxy);
+            return Complete;
+        }
+
+private:
+    AsteriskSCF::SessionCommunications::V1::AMD_Session_setAndGetSessionControllerPtr mCb;
+    AsteriskSCF::SessionCommunications::V1::SessionControllerPrx mController;
+    boost::shared_ptr<SipSessionPriv> mImplPriv;
+};
+
+/**
+ * An implementation of the setAndGetSessionController method as defined in SessionCommunications.ice
+ */
+void SipSession::setAndGetSessionController_async(
+    const AsteriskSCF::SessionCommunications::V1::AMD_Session_setAndGetSessionControllerPtr& cb,
+    const AsteriskSCF::SessionCommunications::V1::SessionControllerPrx& controller,
+    const Ice::Current&)
+{
+    lg(Debug) << "queueing a setAndGetSessionController operation";
+    enqueueSessionWork(new SetAndGetSessionControllerOperation(cb, controller, mImplPriv));
+}
+
+class RemoveSessionControllerOperation : public SuspendableWork
+{
+public:
+    RemoveSessionControllerOperation(const AsteriskSCF::SessionCommunications::V1::AMD_Session_removeSessionControllerPtr& cb,
+                                     const AsteriskSCF::SessionCommunications::V1::SessionControllerPrx& controller,
+                                     const boost::shared_ptr<SipSessionPriv>& sessionPriv)
+        : mCb(cb), mController(controller), mImplPriv(sessionPriv) { }
+
+    SuspendableWorkResult execute(const SuspendableWorkListenerPtr&)
+        {
+            lg(Debug) << "Executing a RemoveSessionController operation";
+
+            if (mImplPriv->mSessionController == mController)
+            {
+                mImplPriv->mSessionController = 0;
+            }
+
+            mCb->ice_response();
+            return Complete;
+        }
+
+private:
+    AsteriskSCF::SessionCommunications::V1::AMD_Session_removeSessionControllerPtr mCb;
+    AsteriskSCF::SessionCommunications::V1::SessionControllerPrx mController;
+    boost::shared_ptr<SipSessionPriv> mImplPriv;
+};
+
+
+/**
+ * An implementation of the removeSessionController method as defined in SessionCommunications.ice
+ */
+void SipSession::removeSessionController_async(const AsteriskSCF::SessionCommunications::V1::AMD_Session_removeSessionControllerPtr& cb,
+                                               const AsteriskSCF::SessionCommunications::V1::SessionControllerPrx& controller,
+                                               const Ice::Current&)
+{
+    lg(Debug) << "queueing a removeSessionController operation";
+    enqueueSessionWork(new RemoveSessionControllerOperation(cb, controller, mImplPriv));
+}
+
 class SetBridgeOperation : public SuspendableWork
 {
 public:
@@ -1302,6 +1709,7 @@ pjmedia_sdp_session *SipSession::createSDP()
     }
 
     sdp->name = sdp->origin.user;
+
     return sdp;
 }
 
@@ -1381,21 +1789,21 @@ void SipSession::addFormatstoSDP(const FormatSeq& formats, pjmedia_sdp_media *me
 }
 
 /**
- * Internal function called to produce an SDP session structure using configuration.
+ * Internal function called to produce an SDP session structure using configuration and provided streams
  */
-pjmedia_sdp_session *SipSession::createSDPOffer()
+pjmedia_sdp_session *SipSession::createSDPOffer(const AsteriskSCF::Media::V1::StreamInformationDict& streams,
+                                                AsteriskSCF::Media::V1::StreamInformationDict& newStreams)
 {
-    // If SDP has already been produced then just return it, don't recreate it
-    if (mImplPriv->mSDPFinalized == true)
+    StreamInformationDict requestedStreams = streams;
+
+    // If no streams have been provided use a generate topology from the endpoint based on configured formats
+    if (requestedStreams.empty())
     {
-        return mImplPriv->mSDP;
+        requestedStreams = mImplPriv->mEndpoint->getStreamTopology();
     }
 
-    // Retrieve the stream topology from the endpoint
-    StreamTopologyMap streams = mImplPriv->mEndpoint->getStreamTopology();
-
-    // If there are no streams then we can not create an SDP offer
-    if (streams.empty())
+    // If we still have no streams we can not produce an SDP as it would be broken
+    if (requestedStreams.empty())
     {
         return 0;
     }
@@ -1406,30 +1814,41 @@ pjmedia_sdp_session *SipSession::createSDPOffer()
         mImplPriv->mSDP = createSDP();
     }
 
-    SipEndpointConfig config = mImplPriv->mEndpoint->getConfig();
-
-    // Iterate through each stream present in the topology
-    for (StreamTopologyMap::const_iterator stream = streams.begin();
-         stream != streams.end();
+    // Iterate through each requested stream
+    for (StreamInformationDict::const_iterator stream = requestedStreams.begin();
+         stream != requestedStreams.end();
          ++stream)
     {
-        RTPServiceLocatorParamsPtr params;
-        if (mImplPriv->mNatOptions.enableICE)
+        // Remove any provided values that we will overwrite
+        stream->second->sources.clear();
+        stream->second->sinks.clear();
+
+        // Determine the actual formats we can handle
+        FormatSeq formats;
+
+        for (FormatSeq::const_iterator format = stream->second->formats.begin();
+             format != stream->second->formats.end();
+             ++format)
         {
-            AsteriskSCF::Media::RTP::V1::RTPOverICEServiceLocatorParamsPtr iceParams = 
-                new AsteriskSCF::Media::RTP::V1::RTPOverICEServiceLocatorParams;
-            params = iceParams;
-            iceParams->enableRTPOverICE = true;
-            iceParams->enableTURN = mImplPriv->mNatOptions.enableTURN;
+            if (mImplPriv->mEndpoint->getDescriptor(*format) != 0)
+            {
+                formats.push_back(*format);
+            }
         }
-        else
+
+        // If no compatible formats were provided skip this stream
+        if (formats.empty())
         {
-            params = new AsteriskSCF::Media::RTP::V1::RTPServiceLocatorParams;
+            continue;
         }
+
+        // Update the stream with the actual formats
+        stream->second->formats = formats;
+
+        RTPServiceLocatorParamsPtr params = new RTPServiceLocatorParams();
         params->category = "rtp";
-        params->formats = stream->second;
-        params->ipv6 = config.sessionConfig.rtpOverIPv6;
-        params->srtpCapable = (!config.srtpConfig.cryptoKeys.empty());
+        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));
@@ -1453,27 +1872,27 @@ pjmedia_sdp_session *SipSession::createSDPOffer()
         // 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);
 
         // Update the SIP session with some RTP session details
         mImplPriv->mRTPSessions.push_back(session);
 
         // Add the stream to the SDP
         pjmedia_sdp_media *media = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
-        mImplPriv->mSDP->media[mImplPriv->mSDP->media_count++] = media;
-        pj_strdup2(mImplPriv->mDialog->pool, &media->desc.media, mImplPriv->mEndpoint->getDescriptor(stream->second.front())->type.c_str());
 
-        if (config.srtpConfig.cryptoKeys.empty())
-        {
-            pj_strdup2(mImplPriv->mDialog->pool, &media->desc.transport, "RTP/AVP");
-        }
-        else
-        {
-            pj_strdup2(mImplPriv->mDialog->pool, &media->desc.transport, "RTP/SAVP");
-        }
+        // 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());
+
+        // 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);
@@ -1502,31 +1921,33 @@ pjmedia_sdp_session *SipSession::createSDPOffer()
         PayloadMap payloads;
 
         // Add all of the formats to the SDP
-        addFormatstoSDP(stream->second, media, payloads);
+        addFormatstoSDP(stream->second->formats, media, payloads);
 
         // Push the payload mapping to the RTP session so it'll correctly map things
         session->associatePayloads(payloads);
 
-        addKeys(mImplPriv->mEndpoint->getConfig().srtpConfig, media);
+        // Make the caller aware of this new stream
+        newStreams.insert(make_pair(boost::lexical_cast<std::string>(mImplPriv->mSDP->media_count), stream->second));
+
+        // Since this is a newly added stream record it as such until the remote party accepts or declines it
+        mImplPriv->mNewStreams.insert(make_pair(boost::lexical_cast<std::string>(mImplPriv->mSDP->media_count), stream->second));
+
+        // Create a mapping so if we need to respond to an addStreams request they will get their own identifier back
+        mImplPriv->mAddStreamsMapping.insert(make_pair(boost::lexical_cast<std::string>(mImplPriv->mSDP->media_count), stream->first));
 
-        // The SDP has been finalized enough
-        mImplPriv->mSDPFinalized = true;
+	// Now that all is done we can add this stream in
+	mImplPriv->mStreams.insert(make_pair(boost::lexical_cast<std::string>(mImplPriv->mSDP->media_count++), stream->second));
     }
 
-    return mImplPriv->mSDPFinalized == true ? mImplPriv->mSDP : 0;
+    return mImplPriv->mSDP;
 }
 
 /**
  * Internal function called to produce an SDP session structure using configuration and an SDP offer.
  */
-pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offer)
+pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offer,
+                                                 AsteriskSCF::Media::V1::StreamInformationDict& newStreams)
 {
-    // If SDP has already been produced then just return it, don't recreate it
-    if (mImplPriv->mSDPFinalized == true)
-    {
-        return mImplPriv->mSDP;
-    }
-
     // Create the most common part of the SDP if not already done
     if (!mImplPriv->mSDP)
     {
@@ -1536,9 +1957,54 @@ pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offe
     // Get the non-stream level connection information in case there is no connection level one
     std::string destination(pj_strbuf(&offer->conn->addr), pj_strlen(&offer->conn->addr));
 
+    // A dictionary containing streams that have been added to the session
+    StreamInformationDict streamsAdded;
+
+    // A dictionary containing streams that have been removed from the session
+    StreamInformationDict streamsRemoved;
+
+    // A dictionary containing streams and their new state
+    StreamStateDict streamsChanged;
+
     // Iterate through each stream seeing if we are configured to support at least one format they have
     for (unsigned int stream = 0; stream < offer->media_count; stream++)
     {
+        // Determine if this is a new stream or an existing stream
+        StreamInformationDict::iterator existingStream = mImplPriv->mStreams.find(boost::lexical_cast<std::string>(stream));
+        StreamInformationPtr ourStream;
+
+        if (existingStream == mImplPriv->mStreams.end())
+        {
+	    // This follows an optimistic approach where it is believed that any offered stream will be accepted
+            ourStream = new StreamInformation();
+            mImplPriv->mStreams.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream));
+            newStreams.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream));
+        }
+        else
+        {
+            ourStream = existingStream->second;
+        }
+
+        // Examine the port number to determine if this stream has been removed
+        if (!offer->media[stream]->desc.port)
+        {
+            ourStream->state = Removed;
+            streamsRemoved.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream));
+            continue;
+        }
+
+        // If this is a newly added stream we may need to notify a session controller that it was accepted
+        if (mImplPriv->mNewStreams.find(boost::lexical_cast<std::string>(stream)) != mImplPriv->mNewStreams.end())
+        {
+            // We need to return a StreamInformationDict with their identifier so they can correctly associate it
+            std::map<std::string, std::string>::const_iterator mapping = mImplPriv->mAddStreamsMapping.find(
+                boost::lexical_cast<std::string>(stream));
+            if (mapping != mImplPriv->mAddStreamsMapping.end())
+            {
+                streamsAdded.insert(make_pair(mapping->second, ourStream));
+            }
+        }
+
         // If a format is actually found we add it to this sequence so we can create an RTP session
         FormatSeq formats;
 
@@ -1629,9 +2095,16 @@ pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offe
         // If no formats were found on this stream that we are configured with then move on to the next one
         if (formats.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;
         }
 
+	// Update the stream with the formats
+	ourStream->formats = formats;
+
         // Assume that no connection level details exist until proven otherwise
         std::string connection = destination;
         if (offer->media[stream]->conn)
@@ -1640,100 +2113,225 @@ pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offe
                                      pj_strlen(&offer->media[stream]->conn->addr));
         }
 
-        RTPServiceLocatorParamsPtr params;
-        if (mImplPriv->mNatOptions.enableICE)
+	// 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
+	    {
+		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
+            RTPSessionPrx session = factory->allocate(params);
+
+            // 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;
+            }
+
+            // 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.push_back(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;
+
+            // 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);
+        }
+
+        // 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))
         {
-            AsteriskSCF::Media::RTP::V1::RTPOverICEServiceLocatorParamsPtr iceParams = 
-                new AsteriskSCF::Media::RTP::V1::RTPOverICEServiceLocatorParams;
-            params = iceParams;
-            iceParams->enableRTPOverICE = true;
-            iceParams->enableTURN = mImplPriv->mNatOptions.enableTURN;
+            ourStream->state = SendOnly;
         }
-        else
+        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "recvonly", NULL))
         {
-            params = new AsteriskSCF::Media::RTP::V1::RTPServiceLocatorParams;
+            ourStream->state = ReceiveOnly;
         }
-        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)
+        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "inactive", NULL))
         {
-            params->ipv6 = true;
+            ourStream->state = Inactive;
         }
-        else
+        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "sendrecv", NULL))
         {
-            params->ipv6 = false;
+            ourStream->state = SendAndReceive;
         }
 
-        // 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)
+        // If the state changed we need to notify the controller if one is around
+        if (oldState != ourStream->state)
         {
-            continue;
+            streamsChanged.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream->state));
         }
 
-        // Allocate a new RTP session to carry the media formats we have in common
-        RTPSessionPrx session = factory->allocate(params);
+        // 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
         {
-            continue;
+            sink->setRemoteDetails(connection, offer->media[stream]->desc.port);
+        }
+        catch (const 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;
         }
+    }
 
-        // 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());
-        sink->setRemoteDetails(connection, offer->media[stream]->desc.port);
-        mImplPriv->mSinks.push_back(sink);
+    if (mImplPriv->mSessionController)
+    {
+        if (!streamsRemoved.empty())
+        {
+            mImplPriv->mSessionController->removeStreams(streamsRemoved);
+        }
+        if (!streamsChanged.empty())
+        {
+            mImplPriv->mSessionController->changeStreamStates(streamsChanged);
+        }
+    }
 
-        // Ditto goes for source
-        StreamSourceRTPPrx source = StreamSourceRTPPrx::uncheckedCast(session->getSources().front());
-        mImplPriv->mSources.push_back(source);
+    if (mImplPriv->mAddStreamsCb)
+    {
+        mImplPriv->mAddStreamsCb->ice_response(streamsAdded);
+        mImplPriv->mAddStreamsCb = 0;
+    }
 
-        // Update the SIP session with some RTP session details
-        mImplPriv->mRTPSessions.push_back(session);
+    startMedia(mImplPriv->mSDP, offer);
 
-        // Add a new stream to the answer SDP
-        pjmedia_sdp_media *media = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
-        mImplPriv->mSDP->media[mImplPriv->mSDP->media_count++] = media;
+    mImplPriv->mNewStreams.clear();
+    mImplPriv->mAddStreamsMapping.clear();
 
-        // 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;
+    return mImplPriv->mSDP;
+}
 
-        // 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());
+/**
+ * Internal function which modifies our SDP.
+ */
+pjmedia_sdp_session *SipSession::modifySDP(const AsteriskSCF::Media::V1::StreamInformationDict& toRemove)
+{
+    // In order to modify SDP you have to have SDP
+    if (!mImplPriv->mSDP)
+    {
+        return 0;
+    }
 
-        // If session level connection information has not yet been set then set it to us
-        if (!mImplPriv->mSDP->conn)
+    // Iterate through each stream to remove so we can change the port number and make it go bye bye
+    for (AsteriskSCF::Media::V1::StreamInformationDict::const_iterator stream = toRemove.begin();
+         stream != toRemove.end();
+         ++stream)
+    {
+        AsteriskSCF::Media::V1::StreamInformationDict::iterator ourStream = mImplPriv->mStreams.find(stream->first);
+
+        // If we don't have a stream stored locally then they gave us a stream that we really know nothing about it
+        if (ourStream == mImplPriv->mStreams.end())
         {
-            mImplPriv->mSDP->conn = media->conn;
+            continue;
         }
 
-        // Add port information so they can talk to us
-        media->desc.port = (pj_uint16_t) source->getLocalPort();
-        media->desc.port_count = 1;
+        // If the stream has already been removed doing it again is silllly
+        if (ourStream->second->state == Removed)
+        {
+            continue;
+        }
 
-        PayloadMap payloads;
+        ourStream->second->state = Removed;
 
-        addFormatstoSDP(formats, media, payloads);
-        addKeys(mImplPriv->mEndpoint->getConfig().srtpConfig, media);
+        // Due to our usage of the pjmedia SDP API if we have a stream on our side there *will* be a media
+        // line in the SDP
+        pjmedia_sdp_media *media = mImplPriv->mSDP->media[boost::lexical_cast<int>(stream->first)];
 
-        // Push the payload mapping to the RTP session so it'll correctly map things
-        session->associatePayloads(payloads);
+        // Per the RFC removing the stream is accomplished by setting the port to 0
+        media->desc.port = 0;
+        media->desc.port_count = 0;
 
-        // The SDP has been finalized enough
-        mImplPriv->mSDPFinalized = true;
+        // The RFC states that an implementation MAY choose to remove all formats but one and all attributes
+        // but at this point in time we choose not to do this
     }
 
-    return mImplPriv->mSDPFinalized == true ? mImplPriv->mSDP : 0;
+    return mImplPriv->mSDP;
 }
 
 /**
@@ -1881,6 +2479,14 @@ void SipSession::startMedia(const pjmedia_sdp_session*, const pjmedia_sdp_sessio
 }
 
 /**
+ * Internal function which gets the streams on this session.
+ */
+AsteriskSCF::Media::V1::StreamInformationDict& SipSession::getStreams()
+{
+    return mImplPriv->mStreams;
+}
+
+/**
  * Internal function which gets the endpoint associated with the session.
  */
 SipEndpointPtr SipSession::getEndpoint()
@@ -1909,6 +2515,22 @@ AsteriskSCF::Media::V1::SessionPrx& SipSession::getMediaSessionProxy()
 }
 
 /**
+ * Internal function which gets the proxy to our session controller.
+ */
+AsteriskSCF::SessionCommunications::V1::SessionControllerPrx& SipSession::getOurSessionControllerProxy()
+{
+    return mImplPriv->mOurSessionControllerProxy;
+}
+
+/**
+ * Internal function which gets the proxy to the remote session controller.
+ */
+AsteriskSCF::SessionCommunications::V1::SessionControllerPrx& SipSession::getSessionControllerProxy()
+{
+    return mImplPriv->mSessionController;
+}
+
+/**
  * Internal function which sets the listeners explicitly.
  */
 void SipSession::setListeners(const SessionListenerSeq& listeners)
@@ -1924,6 +2546,14 @@ RTPMediaSessionSeq SipSession::getRTPMediaSessions()
     return mImplPriv->mRTPSessions;
 }
 
+/**
+ * Internal function which sets the streams explicitly.
+ */
+void SipSession::setStreams(const AsteriskSCF::Media::V1::StreamInformationDict& streams)
+{
+    mImplPriv->mStreams = streams;
+}
+
 bool SipSession::operator==(const SipSession &other) const {
     return (this->mImplPriv->mInviteSession == other.mImplPriv->mInviteSession);
 }
diff --git a/src/SipSession.h b/src/SipSession.h
index 165c411..fa89ad0 100644
--- a/src/SipSession.h
+++ b/src/SipSession.h
@@ -111,7 +111,7 @@ public:
         const AsteriskSCF::System::Component::V1::ReplicaPtr& replica,
         bool ipv6, bool isUAC, const NATEndpointOptions& natOptions);
 
-    SipSession(const Ice::ObjectAdapterPtr&, const SipEndpointPtr&, const std::string&, const Ice::Identity&,
+    SipSession(const Ice::ObjectAdapterPtr&, const SipEndpointPtr&, const std::string&, const Ice::Identity&, const Ice::Identity&,
         const Ice::Identity&, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq&,
         const AsteriskSCF::Media::V1::StreamSourceSeq&, const AsteriskSCF::Media::V1::StreamSinkSeq&,
         const PJSipManagerPtr& manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
@@ -182,6 +182,19 @@ public:
     AsteriskSCF::SessionCommunications::V1::SessionCookies getCookies();
     AsteriskSCF::SessionCommunications::V1::SessionCookieDict getAllCookies();
 
+    void getStreams_async(
+        const AsteriskSCF::SessionCommunications::V1::AMD_Session_getStreamsPtr&,
+        const Ice::Current&);
+
+    void setAndGetSessionController_async(
+        const AsteriskSCF::SessionCommunications::V1::AMD_Session_setAndGetSessionControllerPtr&,
+        const AsteriskSCF::SessionCommunications::V1::SessionControllerPrx&,
+        const Ice::Current&);
+
+    void removeSessionController_async(const AsteriskSCF::SessionCommunications::V1::AMD_Session_removeSessionControllerPtr&,
+                                       const AsteriskSCF::SessionCommunications::V1::SessionControllerPrx&,
+                                       const Ice::Current&);
+
     /**
      * Implementation specific functions.
      */
@@ -197,9 +210,13 @@ public:
 
     void addKeys(const SipEndpointMediaSRTPConfig& config, pjmedia_sdp_media* mediaSDP);
 
-    pjmedia_sdp_session *createSDPOffer();
+    pjmedia_sdp_session *createSDPOffer(const AsteriskSCF::Media::V1::StreamInformationDict&,
+                                        AsteriskSCF::Media::V1::StreamInformationDict&);
+
+    pjmedia_sdp_session *createSDPAnswer(const pjmedia_sdp_session*,
+                                         AsteriskSCF::Media::V1::StreamInformationDict&);
 
-    pjmedia_sdp_session *createSDPAnswer(const pjmedia_sdp_session*);
+    pjmedia_sdp_session *modifySDP(const AsteriskSCF::Media::V1::StreamInformationDict&);
 
     void setDialog(pjsip_dialog *dialog);
 
@@ -224,14 +241,22 @@ public:
     //
     void startMedia(const pjmedia_sdp_session* local, const pjmedia_sdp_session* remote);
 
+    AsteriskSCF::Media::V1::StreamInformationDict& getStreams();
+
     SipEndpointPtr getEndpoint();
 
     AsteriskSCF::SessionCommunications::V1::SessionPrx& getSessionProxy();
 
     AsteriskSCF::Media::V1::SessionPrx& getMediaSessionProxy();
 
+    AsteriskSCF::SessionCommunications::V1::SessionControllerPrx& getOurSessionControllerProxy();
+
+    AsteriskSCF::SessionCommunications::V1::SessionControllerPrx& getSessionControllerProxy();
+
     void setListeners(const AsteriskSCF::Replication::SipSessionManager::V1::SessionListenerSeq&);
 
+    void setStreams(const AsteriskSCF::Media::V1::StreamInformationDict& streams);
+
     AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq getRTPMediaSessions();
 
     void enqueueSessionWork(const AsteriskSCF::System::WorkQueue::V1::SuspendableWorkPtr&);
diff --git a/src/SipStateReplicatorListener.cpp b/src/SipStateReplicatorListener.cpp
index ed9fd83..d2097f2 100644
--- a/src/SipStateReplicatorListener.cpp
+++ b/src/SipStateReplicatorListener.cpp
@@ -261,6 +261,7 @@ public:
 
                     // Now that all is well we can create an actual session
                     SipSessionPtr localSession = endpoint->createSession("", session->mSessionObjectId,
+                                                                         session->mSessionControllerObjectId,
                         session->mMediaSessionObjectId, session->mRTPMediaSessions, session->mSources, session->mSinks);
                     localitem->setSession(localSession);
                 }
@@ -272,6 +273,7 @@ public:
                 localitem->getSession()->setListeners(session->mListeners);
                 localitem->getSession()->setBridge(session->mBridge);
 		localitem->getSession()->setCookies(session->mCookies);
+                localitem->getSession()->setStreams(session->mStreams);
             }
             else if ((dialog = SipDialogStateItemPtr::dynamicCast((*item))))
             {

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


-- 
asterisk-scf/release/sip.git



More information about the asterisk-scf-commits mailing list