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

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Sun Jul 3 12:47:14 CDT 2011


branch "sessioncontroller" has been updated
       via  44bf3fc63cd258cb15dc572727686e13de8d0c83 (commit)
      from  645a47f96f292aaf8c572feb8c126748d1eefb9c (commit)

Summary of changes:
 src/PJSipSessionModule.cpp             |  195 ++++++++++----------
 src/PJSipSessionModule.h               |    1 +
 src/PJSipSessionModuleConstruction.cpp |    6 +
 src/SipSession.cpp                     |  323 ++++++++++++++++++++------------
 src/SipSession.h                       |    5 +-
 5 files changed, 309 insertions(+), 221 deletions(-)


- Log -----------------------------------------------------------------
commit 44bf3fc63cd258cb15dc572727686e13de8d0c83
Author: Joshua Colp <jcolp at digium.com>
Date:   Sun Jul 3 14:46:08 2011 -0300

    Perform major refactoring to reduce code duplication. The code responsible for generating the SDP answer can now be used in all cases where an SDP answer is needed. Additionally add support for contacting the session controller when streams are added to determine which ones were permitted.

diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index d30c73e..59c0c9a 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -383,7 +383,8 @@ protected:
         else
         {
             // SDP was present in the INVITE so we need to create an answer using their offer
-            sdp = mSession->createSDPAnswer(remote_sdp);
+            StreamInformationDict streams;
+            sdp = mSession->createSDPAnswer(remote_sdp, streams);
         }
 
         if (!sdp)
@@ -1105,6 +1106,7 @@ protected:
     SuspendableWorkResult calledBack(const SuspendableWorkListenerPtr&)
     {
         assert(mAsyncResult);
+
         SessionListenerPrx listener = SessionListenerPrx::uncheckedCast(mAsyncResult->getProxy());
         try
         {
@@ -1114,6 +1116,7 @@ protected:
         {
             lg(Error) << "Ice exception when attempting to indicate something or other";
         }
+
         return Complete;
     }
 private:   
@@ -1398,13 +1401,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*)
 {
-    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**)
@@ -1436,100 +1435,8 @@ 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));
-
-        // Get the streams that exist on the session
-        StreamInformationDict streams = session->getStreams();
-
-        // Streams that have had their state changed
-        StreamStateDict streamsChanged;
-
-        // Each stream has its own set of formats, so go to that granularity
-        for (unsigned int stream = 0; stream < remote_sdp->media_count; stream++)
-        {
-            // Find the stream
-            StreamInformationDict::iterator ourStream = streams.find(boost::lexical_cast<std::string>(stream));
-
-            // If none exist than something is amiss... but go on
-            if (ourStream == streams.end())
-            {
-                continue;
-            }
-
-            // If the stream has already been removed just ignore it
-            if (ourStream->second->state == Removed)
-            {
-                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
-
-            // Determine what they say the current state of the stream is
-	    StreamState state = SendAndReceive;
-
-            if (pjmedia_sdp_media_find_attr2(remote_sdp->media[stream], "sendonly", NULL))
-            {
-                state = SendOnly;
-            }
-            else if (pjmedia_sdp_media_find_attr2(remote_sdp->media[stream], "recvonly", NULL))
-            {
-                state = ReceiveOnly;
-            }
-            else if (pjmedia_sdp_media_find_attr2(remote_sdp->media[stream], "inactive", NULL))
-            {
-                state = Inactive;
-            }
-
-            // Determine if the state actually changed and if so add ourselves to the dictionary so the
-            // SessionController gets notified
-            if (ourStream->second->state != state)
-            {
-                ourStream->second->state = state;
-                streamsChanged.insert(make_pair(ourStream->first, state));
-            }
-
-            // 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(ourStream->second->sinks.front());
-
-            // 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
-        }
-
-        // Notify the SessionController if any streams changed their state
-        if (!streamsChanged.empty())
-        {
-	    SessionControllerPrx controller = session->getSessionControllerProxy();
-	    if (controller)
-	    {
-		controller->changeStreamStates(streamsChanged);
-	    }
-        }
+        StreamInformationDict added;
+        session->createSDPAnswer(remote_sdp, added);
 
         return Complete;
     }
@@ -1565,6 +1472,94 @@ pjsip_dialog *PJSipSessionModule::uaOnDialogForked(pjsip_dialog*, pjsip_rx_data*
     return NULL;
 }
 
+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_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_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 SuspendableWorkListenerPtr&)
+    {
+        SessionControllerPrx controller = SessionControllerPrx::uncheckedCast(mAsyncResult->getProxy());
+        StreamInformationDict accepted = controller->end_addStreams(mAsyncResult);
+
+        // 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 7b4cd30..78dd2f9 100644
--- a/src/PJSipSessionModule.h
+++ b/src/PJSipSessionModule.h
@@ -109,6 +109,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 replicateState(PJSipDialogModInfo *dlgInfo, PJSipTransactionModInfo *tsxInfo,
         PJSipSessionModInfo *sessionInfo);
diff --git a/src/PJSipSessionModuleConstruction.cpp b/src/PJSipSessionModuleConstruction.cpp
index 56a0beb..c378a23 100644
--- a/src/PJSipSessionModuleConstruction.cpp
+++ b/src/PJSipSessionModuleConstruction.cpp
@@ -107,6 +107,11 @@ static pjsip_dialog *uaOnDialogForked(pjsip_dialog *first_set, pjsip_rx_data *rd
     return sessionModule->uaOnDialogForked(first_set, rdata);
 }
 
+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,
@@ -153,6 +158,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/SipSession.cpp b/src/SipSession.cpp
index 7bb5890..947ee72 100644
--- a/src/SipSession.cpp
+++ b/src/SipSession.cpp
@@ -414,51 +414,19 @@ class RemoveStreamsOperation : public SuspendableWork
 public:
     RemoveStreamsOperation(
         const AsteriskSCF::Media::V1::StreamInformationDict& streams,
-        const boost::shared_ptr<SipSessionPriv>& sessionPriv)
-        : mStreams(streams), mImplPriv(sessionPriv) { }
+        const boost::shared_ptr<SipSessionPriv>& sessionPriv,
+        const SipSessionPtr& session)
+        : mStreams(streams), mImplPriv(sessionPriv), mSession(session) { }
 
     SuspendableWorkResult execute(const SuspendableWorkListenerPtr&)
     {
         lg(Debug) << "Executing a removeStreams Operation";
 
-        // This gets set to true if at least one stream was actually removed
-        bool removed = false;
-
-        // We iterate through each stream making sure we have one that matches it
-        for (AsteriskSCF::Media::V1::StreamInformationDict::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 the stream has already been removed doing it again is silllly
-            if (ourStream->second->state == Removed)
-            {
-                continue;
-            }
-
-            ourStream->second->state = Removed;
+        pjmedia_sdp_session *sdp = mSession->modifySDP(mStreams);
 
-            pjmedia_sdp_media *media = mImplPriv->mSDP->media[boost::lexical_cast<int>(stream->first)];
-
-            // Per the RFC removing the stream is accomplished by setting the port to 0
-	    media->desc.port = 0;
-	    media->desc.port_count = 0;
-
-	    // 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
-        }
-
-        if (removed == true)
+        // If there is an outstanding transaction just set this as the answer SDP, otherwise trigger a reinvite
+        if (!mImplPriv->mInviteSession->invite_tsx)
         {
-            // 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)
@@ -466,6 +434,10 @@ public:
                 pjsip_inv_send_msg(mImplPriv->mInviteSession, packet);
             }
         }
+        else
+        {
+            pjsip_inv_set_sdp_answer(mImplPriv->mInviteSession, sdp);
+        }
 
         return Complete;
     }
@@ -473,6 +445,7 @@ public:
 private:
     AsteriskSCF::Media::V1::StreamInformationDict mStreams;
     boost::shared_ptr<SipSessionPriv> mImplPriv;
+    SipSessionPtr mSession;
 };
 
 /**
@@ -497,7 +470,7 @@ public:
     void removeStreams(const AsteriskSCF::Media::V1::StreamInformationDict& streams, const Ice::Current&)
     {
         lg(Debug) << "Queueing removeStreams operation";
-        mSession->enqueueSessionWork(new RemoveStreamsOperation(streams, mImplPriv));
+        mSession->enqueueSessionWork(new RemoveStreamsOperation(streams, mImplPriv, mSession));
     }
     
 private:
@@ -1658,14 +1631,9 @@ pjmedia_sdp_session *SipSession::createSDPOffer()
 /**
  * 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)
     {
@@ -1675,9 +1643,39 @@ 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 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 a format is actually found we add it to this sequence so we can create an RTP session
         FormatSeq formats;
 
@@ -1768,9 +1766,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)
@@ -1779,45 +1784,98 @@ pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offe
                                      pj_strlen(&offer->media[stream]->conn->addr));
         }
 
-        RTPServiceLocatorParamsPtr params = new RTPServiceLocatorParams();
-        params->category = "rtp";
-        params->formats = formats;
+	// 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 = new 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;
-        }
+            // 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));
+            // 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)
-        {
-            continue;
-        }
+            // 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);
+            // 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)
-        {
-            continue;
-        }
+            // 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;
 
-        // Instantiate a stream information class so we can keep track of this stream
-        StreamInformationPtr ourStream = new StreamInformation();
+            // 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;
 
-        // Formats actually accepted by us were determined above so we can just copy them
-        ourStream->formats = formats;
+            // 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());
 
-        // Determine the state of the stream
+            // 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))
         {
             ourStream->state = SendOnly;
@@ -1830,64 +1888,89 @@ pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offe
         {
             ourStream->state = Inactive;
         }
+        else if (pjmedia_sdp_media_find_attr2(offer->media[stream], "sendrecv", NULL))
+        {
+            ourStream->state = SendAndReceive;
+        }
 
-	// We don't explicitly look for sendrecv since the default state of a newly created StreamInformation
-	// is SendAndReceive, which is also implied if none of the above are found in the SDP
+        // 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));
+        }
 
-        // 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);
-        ourStream->sinks.push_back(sink);
+        // Update connection information
+        StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(ourStream->sinks.front());
 
-        // Ditto goes for source
-        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);
+        // 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
+        {
+            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;
+        }
+    }
 
-        // 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;
+    if (mImplPriv->mSessionController)
+    {
+        if (!streamsRemoved.empty())
+        {
+            mImplPriv->mSessionController->removeStreams(streamsRemoved);
+        }
+        if (!streamsChanged.empty())
+        {
+            mImplPriv->mSessionController->changeStreamStates(streamsChanged);
+        }
+    }
 
-        // 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)
+{
+    // 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 session level connection information has not yet been set then set it to us
-        if (!mImplPriv->mSDP->conn)
+        // 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;
-
-        PayloadMap payloads;
+        // If the stream has already been removed doing it again is silllly
+        if (ourStream->second->state == Removed)
+        {
+            continue;
+        }
 
-        addFormatstoSDP(formats, media, payloads);
+        ourStream->second->state = Removed;
 
-        // Push the payload mapping to the RTP session so it'll correctly map things
-        session->associatePayloads(payloads);
+        // 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)];
 
-        // The SDP has been finalized enough
-        mImplPriv->mSDPFinalized = true;
+        // Per the RFC removing the stream is accomplished by setting the port to 0
+        media->desc.port = 0;
+        media->desc.port_count = 0;
 
-        // Add this as a stream to ourselves, yay!
-        mImplPriv->mStreams.insert(make_pair(boost::lexical_cast<std::string>(stream), ourStream));
+        // 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;
 }
 
 /**
diff --git a/src/SipSession.h b/src/SipSession.h
index b31ac5b..b4b5c48 100644
--- a/src/SipSession.h
+++ b/src/SipSession.h
@@ -190,7 +190,10 @@ public:
 
     pjmedia_sdp_session *createSDPOffer();
 
-    pjmedia_sdp_session *createSDPAnswer(const pjmedia_sdp_session*);
+    pjmedia_sdp_session *createSDPAnswer(const pjmedia_sdp_session*,
+                                         AsteriskSCF::Media::V1::StreamInformationDict&);
+
+    pjmedia_sdp_session *modifySDP(const AsteriskSCF::Media::V1::StreamInformationDict&);
 
     void setDialog(pjsip_dialog *dialog);
 

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


-- 
asterisk-scf/integration/sip.git



More information about the asterisk-scf-commits mailing list