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

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Tue Jun 14 16:53:18 CDT 2011


branch "media" has been updated
       via  6eaf8baaf0ea97d672be92367676bf42b3cd450b (commit)
      from  d0ecc289997472e3662a7bb1add5e4c92c2ba683 (commit)

Summary of changes:
 src/PJSipSessionModule.cpp |   38 +++-
 src/SipEndpoint.cpp        |   89 ++++++++-
 src/SipEndpoint.h          |   23 ++
 src/SipSession.cpp         |  487 +++++++++++++++++++++++++++++++++++---------
 src/SipSession.h           |   10 +-
 5 files changed, 546 insertions(+), 101 deletions(-)


- Log -----------------------------------------------------------------
commit 6eaf8baaf0ea97d672be92367676bf42b3cd450b
Author: Joshua Colp <jcolp at digium.com>
Date:   Tue Jun 14 18:52:44 2011 -0300

    Make our SDP handling smarter! Not yet complete, but getting closer.
    
    Incoming offers:
    We will produce an answer that includes formats the endpoint is configured
    with across as many streams as are offered.
    
    Outgoing offers:
    We will produce an offer that has individual streams for each type of media.
    One for audio, one for video, etc. This will be improved.

diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index 958d96e..b688250 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -353,8 +353,37 @@ protected:
             pjsip_inv_send_msg(mInv, mTdata);
             return Complete;
         }
-        mSession->setInviteSession(mInv);
-        mSession->setDialog(mInv->dlg);
+
+	mSession->setInviteSession(mInv);
+	mSession->setDialog(mInv->dlg);
+
+        // Create an SDP offer or answer
+	const pjmedia_sdp_session *remote_sdp = NULL;
+        pjmedia_sdp_session *sdp;
+
+	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();
+        }
+        else
+        {
+            // SDP was present in the INVITE so we need to create an answer using their offer
+            sdp = mSession->createSDPAnswer(remote_sdp);
+        }
+
+        if (!sdp)
+        {
+            // If no SDP was created we can not accept this INVITE
+            lg(Error) << "No compatible formats found in offer\n";
+            pjsip_inv_end_session(mInv, 488, NULL, &mTdata);
+            pjsip_inv_send_msg(mInv, mTdata);
+            return Complete;
+        }
+
+	// Provide the produced SDP as our offer or answer
+	pjsip_inv_set_sdp_answer(mInv, sdp);
+
         PJSipDialogModInfo *dlg_mod_info =(PJSipDialogModInfo*)mInv->dlg->mod_data[mSessionModule->getModule().id];
         PJSipTransactionModInfo *tsx_mod_info = (PJSipTransactionModInfo *)mInv->invite_tsx->mod_data[mSessionModule->getModule().id];
         PJSipSessionModInfo *session_mod_info = (PJSipSessionModInfo*)mInv->mod_data[mSessionModule->getModule().id];
@@ -572,6 +601,8 @@ void PJSipSessionModule::handleNewInvite(pjsip_rx_data *rdata)
         destination = std::string(pj_strbuf(&sipRuri->user), pj_strlen(&sipRuri->user));
         lg(Debug) << "Call is destined for " << destination;
     }
+
+    // See if any SDP was present on this INVITE
     
     lg(Debug) << "Queueing a SessionCreationOperation";
     sessionWork->enqueueWork(new SessionCreationOperation(this, caller, mSessionRouter, inv_session, tdata, replaced_dlg, destination));
@@ -1386,6 +1417,7 @@ protected:
 	    return Complete;
         }
 
+#if 0
         const pjmedia_sdp_conn *remote_conn = remote_sdp->media[0]->conn ? remote_sdp->media[0]->conn : remote_sdp->conn;
 
         PJSipSessionModInfo *session_mod_info = (PJSipSessionModInfo*)mInv->mod_data[mModuleId];
@@ -1406,8 +1438,6 @@ protected:
             return Complete;
 	}
 
-#if 0
-
         // Each stream has its own set of formats, so go to that granularity
         for (unsigned int stream = 0; stream < remote_sdp->media_count; stream++)
         {
diff --git a/src/SipEndpoint.cpp b/src/SipEndpoint.cpp
index 6d8f5e9..bfe3908 100644
--- a/src/SipEndpoint.cpp
+++ b/src/SipEndpoint.cpp
@@ -94,6 +94,21 @@ public:
     {
     }
 
+    SDPDescriptorServicePrx getDescriptorService()
+    {
+        return mDescriptorService;
+    }
+
+    SDPDescriptorPtr getDescriptor()
+    {
+        return mDescriptor;
+    }
+
+    FormatPtr getFormat()
+    {
+        return mFormat;
+    }
+
 private:
     /**
      * Name of the media format.
@@ -390,7 +405,7 @@ AsteriskSCF::SessionCommunications::V1::SessionPrx SipEndpoint::createSession(co
     }
 
     SipSessionPtr session = new SipSession(mImplPriv->mAdapter, this, destination, listener, mImplPriv->mManager,
-        mImplPriv->mServiceLocator, mImplPriv->mReplica, mImplPriv->mConfig.sessionConfig.rtpOverIPv6, true);
+        mImplPriv->mServiceLocator, mImplPriv->mReplica, true);
     mImplPriv->mSessions.push_back(session);
     std::cout << "And now we're returing a session proxy..." << std::endl;
     return session->getSessionProxy();
@@ -399,7 +414,7 @@ AsteriskSCF::SessionCommunications::V1::SessionPrx SipEndpoint::createSession(co
 AsteriskSCF::SipSessionManager::SipSessionPtr SipEndpoint::createSession(const std::string& destination)
 {
     SipSessionPtr session = new SipSession(mImplPriv->mAdapter, this, destination, 0, mImplPriv->mManager,
-        mImplPriv->mServiceLocator, mImplPriv->mReplica, mImplPriv->mConfig.sessionConfig.rtpOverIPv6, false);
+        mImplPriv->mServiceLocator, mImplPriv->mReplica, false);
     mImplPriv->mSessions.push_back(session);
     return session;
 }
@@ -450,5 +465,75 @@ AsteriskSCF::SessionCommunications::V1::SessionEndpointPrx SipEndpoint::getEndpo
     return mImplPriv->mEndpointProxy;
 }
 
+SDPDescriptorServicePrx SipEndpoint::getDescriptorService(const SDPDescriptorPtr& descriptor)
+{
+    for (std::vector<ConfiguredFormatPtr>::const_iterator configuredFormat = mImplPriv->mFormats.begin();
+         configuredFormat != mImplPriv->mFormats.end();
+         ++configuredFormat)
+    {
+        // Dynamic payloads get searched via name while static payloads get searched by number
+        if (descriptor->payload < 96)
+        {
+            if ((*configuredFormat)->getDescriptor()->payload == descriptor->payload)
+            {
+                return (*configuredFormat)->getDescriptorService();
+            }
+        }
+        else
+        {
+            if ((*configuredFormat)->getDescriptor()->subtype == descriptor->subtype)
+            {
+                return (*configuredFormat)->getDescriptorService();
+            }
+        }
+    }
+
+    return 0;
+}
+
+SDPDescriptorPtr SipEndpoint::getDescriptor(const FormatPtr& format)
+{
+    for (std::vector<ConfiguredFormatPtr>::const_iterator configuredFormat = mImplPriv->mFormats.begin();
+         configuredFormat != mImplPriv->mFormats.end();
+         ++configuredFormat)
+    {
+        if ((*configuredFormat)->getFormat()->ice_id() == format->ice_id())
+        {
+            return (*configuredFormat)->getDescriptor();
+        }
+    }
+
+    return 0;
+}
+
+StreamTopologyMap SipEndpoint::getStreamTopology()
+{
+    StreamTopologyMap 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();
+         configuredFormat != mImplPriv->mFormats.end();
+         ++configuredFormat)
+    {
+        // See if a stream already exists for this type
+        StreamTopologyMap::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));
+        }
+        else
+        {
+            // If one does exist we can simply add the format to it
+            stream->second.push_back((*configuredFormat)->getFormat());
+        }
+    }
+
+    return topology;
+}
+
 }; // end SipSessionManager
 }; // end AsteriskSCF
diff --git a/src/SipEndpoint.h b/src/SipEndpoint.h
index c3bed5c..6327a09 100644
--- a/src/SipEndpoint.h
+++ b/src/SipEndpoint.h
@@ -32,6 +32,7 @@
 #include <AsteriskSCF/SessionCommunications/SessionCommunicationsIf.h>
 #include <AsteriskSCF/Media/MediaIf.h>
 #include <AsteriskSCF/Media/RTP/MediaRTPIf.h>
+#include <AsteriskSCF/Media/SDP/MediaSDPIf.h>
 
 #include "SipSession.h"
 
@@ -204,6 +205,12 @@ private:
 };
 
 /**
+ * A type def for a sequence of stream topology entries, each entry represents a stream and the contents are the formats
+ * carried over the stream.
+ */
+typedef std::map<std::string, AsteriskSCF::Media::V1::FormatSeq> StreamTopologyMap;
+
+/**
  * Private implementation details for SipEndpoint.
  */
 class SipEndpointImplPriv;
@@ -261,6 +268,22 @@ public:
     void setRTPOverIPv6(bool);
     void setFormats(const Ice::StringSeq&);
 
+    /**
+     * API call which returns a proxy to an SDP descriptor service given a descriptor.
+     */
+    AsteriskSCF::Media::SDP::V1::SDPDescriptorServicePrx getDescriptorService(
+        const AsteriskSCF::Media::SDP::V1::SDPDescriptorPtr&);
+    
+    /**
+     * API call which returns a descriptor given a media format.
+     */
+    AsteriskSCF::Media::SDP::V1::SDPDescriptorPtr getDescriptor(const AsteriskSCF::Media::V1::FormatPtr&);
+
+    /**
+     * API call which returns the stream topology to be used for an SDP offer.
+     */
+    StreamTopologyMap getStreamTopology();
+
 private:
     /**
      * Private implementation details.
diff --git a/src/SipSession.cpp b/src/SipSession.cpp
index 63df653..9817394 100644
--- a/src/SipSession.cpp
+++ b/src/SipSession.cpp
@@ -24,11 +24,18 @@
 
 #include <boost/thread.hpp>
 #include <boost/thread/shared_mutex.hpp>
+#include <boost/lexical_cast.hpp>
 
 #include <AsteriskSCF/System/WorkQueue/WorkQueueIf.h>
 #include <AsteriskSCF/logger.h>
+#include <AsteriskSCF/Media/MediaIf.h>
+#include <AsteriskSCF/Media/RTP/MediaRTPIf.h>
+#include <AsteriskSCF/Media/SDP/MediaSDPIf.h>
 
 using namespace AsteriskSCF::System::Logging;
+using namespace AsteriskSCF::Media::V1;
+using namespace AsteriskSCF::Media::RTP::V1;
+using namespace AsteriskSCF::Media::SDP::V1;
 
 namespace
 {
@@ -88,7 +95,7 @@ public:
         const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
         const AsteriskSCF::System::Component::V1::ReplicaPtr& replica)
         : mAdapter(adapter), mDialog(0), mInviteSession(0), mEndpoint(endpoint), mDestination(destination),
-          mManager(manager), mServiceLocator(serviceLocator), mReplica(replica) { };
+          mManager(manager), mServiceLocator(serviceLocator), mReplica(replica), mSDP(0) { };
 
     AsteriskSCF::SessionCommunications::V1::SessionInfoPtr getInfo()
     {
@@ -248,6 +255,11 @@ public:
     AsteriskSCF::System::Component::V1::ReplicaPtr mReplica;
 
     SessionWorkPtr mSessionWork;
+
+    /**
+     * SDP that has been produced for this session.
+     */
+    pjmedia_sdp_session *mSDP;
 };
 
 void SipSession::initializePJSIPStructs()
@@ -288,6 +300,8 @@ void SipSession::initializePJSIPStructs()
     mImplPriv->mDialog = dialog;
     
     pjsip_inv_session *inviteSession;
+
+    // Create an SDP offer based on what is configured
     pjmedia_sdp_session *sdp = createSDPOffer();
     if ((pjsip_inv_create_uac(dialog, sdp, 0, &inviteSession)) != PJ_SUCCESS)
     {
@@ -311,9 +325,9 @@ void SipSession::initializePJSIPStructs()
  * Default constructor.
  */
 SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter, const SipEndpointPtr& endpoint,
-        const std::string& destination,  const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener,
-        PJSipManager *manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
-        const AsteriskSCF::System::Component::V1::ReplicaPtr& replica, bool ipv6, bool isUAC)
+                       const std::string& destination,  const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener,
+                       PJSipManager *manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
+                       const AsteriskSCF::System::Component::V1::ReplicaPtr& replica, bool isUAC)
     : mImplPriv(new SipSessionPriv(adapter, endpoint, destination, manager, serviceLocator, replica))
 {
     if (listener != 0)
@@ -328,10 +342,6 @@ SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter, const SipEndpointPt
     mImplPriv->mMediaSessionProxy =
         AsteriskSCF::Media::V1::SessionPrx::uncheckedCast(adapter->addWithUUID(mImplPriv->mMediaSession));
 
-    // Get an RTP session capable of handling the formats we are going to offer
-    AsteriskSCF::Media::V1::FormatSeq formats;
-    requestRTPSessions(formats, ipv6);
-
     if (isUAC)
     {
         initializePJSIPStructs();
@@ -426,8 +436,7 @@ public:
 
         if ((Connect = AsteriskSCF::SessionCommunications::V1::ConnectIndicationPtr::dynamicCast(mIndication)))
         {
-            pjmedia_sdp_session *sdp = mSession->createSDPOffer();
-            status = pjsip_inv_answer(mImplPriv->mInviteSession, 200, NULL, sdp, &packet);
+            status = pjsip_inv_answer(mImplPriv->mInviteSession, 200, NULL, NULL, &packet);
         }
         else if ((Flash = AsteriskSCF::SessionCommunications::V1::FlashIndicationPtr::dynamicCast(mIndication)))
         {
@@ -441,8 +450,7 @@ public:
         }
         else if ((Progress = AsteriskSCF::SessionCommunications::V1::ProgressIndicationPtr::dynamicCast(mIndication)))
         {
-            pjmedia_sdp_session *sdp = mSession->createSDPOffer();
-            status = pjsip_inv_answer(mImplPriv->mInviteSession, 183, NULL, sdp, &packet);
+            status = pjsip_inv_answer(mImplPriv->mInviteSession, 183, NULL, NULL, &packet);
         }
         else if ((Ring = AsteriskSCF::SessionCommunications::V1::RingIndicationPtr::dynamicCast(mIndication)))
         {
@@ -889,12 +897,12 @@ void SipSession::destroy()
 }
 
 /**
- * Internal function called to produce an SDP session structure.
+ * Internal function called to produce an SDP session structure without any streams.
  */
-pjmedia_sdp_session *SipSession::createSDPOffer()
+pjmedia_sdp_session *SipSession::createSDP()
 {
-    pjmedia_sdp_session *sdp = static_cast<pjmedia_sdp_session*>(
-        pj_pool_zalloc(mImplPriv->mDialog->pool, sizeof(pjmedia_sdp_session)));
+    pjmedia_sdp_session *sdp = static_cast<pjmedia_sdp_session*>(pj_pool_zalloc(mImplPriv->mDialog->pool,
+                                                                                sizeof(pjmedia_sdp_session)));
 
     pj_strdup2(mImplPriv->mDialog->pool, &sdp->origin.user, "AsteriskSCF");
     pj_time_val tv;
@@ -902,102 +910,395 @@ pjmedia_sdp_session *SipSession::createSDPOffer()
     sdp->origin.version = sdp->origin.id = (pj_uint32_t) (tv.sec + 2208988800UL);
     pj_strdup2(mImplPriv->mDialog->pool, &sdp->origin.net_type, "IN");
 
-    // Right now we only support a single stream so go and get it
-    AsteriskSCF::Media::RTP::V1::StreamSourceRTPPrx stream =
-        AsteriskSCF::Media::RTP::V1::StreamSourceRTPPrx::uncheckedCast(mImplPriv->mSources.front());
+    sdp->origin.addr = *pj_gethostname();
 
-    std::string address = stream->getLocalAddress();
+    std::string address = std::string(pj_strbuf(&sdp->origin.addr), pj_strlen(&sdp->origin.addr));
 
     if (address.find(":") != std::string::npos)
     {
-	pj_strdup2(mImplPriv->mDialog->pool, &sdp->origin.addr_type, "IP6");
+        pj_strdup2(mImplPriv->mDialog->pool, &sdp->origin.addr_type, "IP6");
     }
     else
     {
-	pj_strdup2(mImplPriv->mDialog->pool, &sdp->origin.addr_type, "IP4");
+        pj_strdup2(mImplPriv->mDialog->pool, &sdp->origin.addr_type, "IP4");
     }
 
-    sdp->origin.addr = *pj_gethostname();
     sdp->name = sdp->origin.user;
     sdp->time.start = 0;
     sdp->time.stop = 0;
     sdp->attr_count = 0;
 
-    // Add connection details at the session level since we currently only support one media stream.
-    sdp->conn = static_cast<pjmedia_sdp_conn*>(pj_pool_zalloc(mImplPriv->mDialog->pool, sizeof(pjmedia_sdp_conn)));
-    sdp->conn->net_type = sdp->origin.net_type;
-    sdp->conn->addr_type = sdp->origin.addr_type;
-    pj_strdup2(mImplPriv->mDialog->pool, &sdp->conn->addr, address.c_str());
-
-    // Add a single media stream
-    sdp->media_count = 1;
-    pjmedia_sdp_media* media =
-        static_cast<pjmedia_sdp_media*>(pj_pool_zalloc(mImplPriv->mDialog->pool, sizeof(pjmedia_sdp_media)));
-    sdp->media[0] = media;
-    pj_strdup2(mImplPriv->mDialog->pool, &media->desc.media, "audio");
-    media->desc.port = (pj_uint16_t) stream->getLocalPort();
-    media->desc.port_count = 1;
-    pj_strdup2(mImplPriv->mDialog->pool, &media->desc.transport, "RTP/AVP");
-
-    // Populate the stream with codec details
-    sdp->media[0]->desc.fmt_count = 1;
-    sdp->media[0]->attr_count = 0;
-
-    // TODO: We should iterate over the formats to produce this instead of hardcoding
-    pjmedia_sdp_rtpmap rtpmap;
-    pjmedia_sdp_attr *attr;
-
-    // This is hardcoded value for ULAW for now
-    pj_strdup2(mImplPriv->mDialog->pool, &media->desc.fmt[0], "0");
-    rtpmap.pt = media->desc.fmt[0];
-    rtpmap.clock_rate = 8000;
-    pj_strdup2(mImplPriv->mDialog->pool, &rtpmap.enc_name, "PCMU");
-    rtpmap.param.slen = 0;
-    pjmedia_sdp_rtpmap_to_attr(mImplPriv->mDialog->pool, &rtpmap, &attr);
-    sdp->media[0]->attr[sdp->media[0]->attr_count++] = attr;
-
-    // Might as well add sendrecv
-    attr = static_cast<pjmedia_sdp_attr*>(pj_pool_zalloc(mImplPriv->mDialog->pool, sizeof(pjmedia_sdp_attr)));
-    pj_strdup2(mImplPriv->mDialog->pool, &attr->name, "sendrecv");
-    sdp->media[0]->attr[sdp->media[0]->attr_count++] = attr;
-
     return sdp;
 }
 
 /**
- * Internal function called to request needed RTP sessions.
+ * Internal function called to add formats to an SDP media stream
+ */
+void SipSession::addFormatstoSDP(const FormatSeq& formats, pjmedia_sdp_media *media, PayloadMap& payloads)
+{
+    // Iterate through each format adding it to the SDP
+    for (FormatSeq::const_iterator mediaformat = formats.begin();
+         mediaformat != formats.end();
+         ++mediaformat)
+    {
+        // Attempt to get our configured descriptor
+        SDPDescriptorPtr ourDescriptor = mImplPriv->mEndpoint->getDescriptor((*mediaformat));
+
+        // This should not happen... if we can not get our descriptor then there is a disassociation
+        // between their SDP and our SDP
+        if (!ourDescriptor)
+        {
+            continue;
+        }
+
+        // Add the format to the 'm' line
+        pj_strdup2(mImplPriv->mDialog->pool, &media->desc.fmt[media->desc.fmt_count++],
+                   boost::lexical_cast<std::string>(ourDescriptor->payload).c_str());
+
+        // Add an rtpmap attribute line for the format
+        pjmedia_sdp_rtpmap rtpmap;
+        pjmedia_sdp_attr *attr;
+
+        rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1];
+        rtpmap.clock_rate = ourDescriptor->samplerate;
+        pj_strdup2(mImplPriv->mDialog->pool, &rtpmap.enc_name, ourDescriptor->subtype.c_str());
+        rtpmap.param.slen = 0;
+        pjmedia_sdp_rtpmap_to_attr(mImplPriv->mDialog->pool, &rtpmap, &attr);
+        media->attr[media->attr_count++] = attr;
+
+        // Add any fmtp attributes for the format
+        for (SDPFormatParameterSeq::const_iterator parameter = ourDescriptor->parameters.begin();
+             parameter != ourDescriptor->parameters.end();
+             ++parameter)
+        {
+            attr = static_cast<pjmedia_sdp_attr*>(pj_pool_zalloc(mImplPriv->mDialog->pool, sizeof(pjmedia_sdp_attr)));
+            pj_strdup2(mImplPriv->mDialog->pool, &attr->name, (*parameter).c_str());
+            media->attr[media->attr_count++] = attr;
+        }
+
+        payloads.insert(std::make_pair(ourDescriptor->payload, (*mediaformat)));
+    }
+}
+
+/**
+ * Internal function called to produce an SDP session structure using configuration.
+ */
+pjmedia_sdp_session *SipSession::createSDPOffer()
+{
+    // If SDP has already been produced then just return it, don't recreate it
+    if (mImplPriv->mSDP)
+    {
+        return mImplPriv->mSDP;
+    }
+
+    // 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())
+    {
+        return 0;
+    }
+
+    // Create the most common part of the SDP
+    mImplPriv->mSDP = createSDP();
+
+    // Iterate through each stream present in the topology
+    for (StreamTopologyMap::const_iterator stream = streams.begin();
+         stream != streams.end();
+         ++stream)
+    {
+        RTPServiceLocatorParamsPtr params = new RTPServiceLocatorParams();
+        params->category = "rtp";
+        params->formats = stream->second;
+        params->ipv6 = mImplPriv->mEndpoint->getConfig().sessionConfig.rtpOverIPv6;
+
+        // Try to find a factory for RTP sessions matching what we need
+        RTPMediaServicePrx factory = RTPMediaServicePrx::uncheckedCast(mImplPriv->mServiceLocator->locate(params));
+
+        // If none exist we can't provide the stream
+        if (factory == 0)
+        {
+            std::cout << "No factory for what we need" << std::endl;
+            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)
+        {
+            std::cout << "No session created" << std::endl;
+            continue;
+        }
+
+        // RTP sessions should only provide a single sink, so grab it and update the connection details with that
+        // of the remote party
+        StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(session->getSinks().front());
+        mImplPriv->mSinks.push_back(sink);
+
+        // Ditto goes for source
+        StreamSourceRTPPrx source = StreamSourceRTPPrx::uncheckedCast(session->getSources().front());
+        mImplPriv->mSources.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 = static_cast<pjmedia_sdp_media*>(pj_pool_zalloc(mImplPriv->mDialog->pool, sizeof(pjmedia_sdp_media)));
+        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());
+
+        // TODO: This should not be hardcoded
+        pj_strdup2(mImplPriv->mDialog->pool, &media->desc.transport, "RTP/AVP");
+
+        // Add connection level details
+        media->conn = static_cast<pjmedia_sdp_conn*>(pj_pool_zalloc(mImplPriv->mDialog->pool, sizeof(pjmedia_sdp_conn)));
+        pj_strdup2(mImplPriv->mDialog->pool, &media->conn->net_type, "IN");
+
+        if (params->ipv6 == true)
+        {
+            pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr_type, "IP6");
+        }
+        else
+        {
+            pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr_type, "IP4");
+        }
+
+        pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr, source->getLocalAddress().c_str());
+
+        // If session level connection information has not yet been set then set it to us
+        if (!mImplPriv->mSDP->conn)
+        {
+            mImplPriv->mSDP->conn = media->conn;
+        }
+
+        media->desc.port = (pj_uint16_t) source->getLocalPort();
+        media->desc.port_count = 1;
+
+        PayloadMap payloads;
+
+        // Add all of the formats to the SDP
+        addFormatstoSDP(stream->second, media, payloads);
+
+        // Push the payload mapping to the RTP session so it'll correctly map things
+        session->associatePayloads(payloads);
+    }
+
+    char createdSDP[1024] = "";
+
+    pjmedia_sdp_print(mImplPriv->mSDP, createdSDP, sizeof(createdSDP));
+
+    std::cout << createdSDP << std::endl;
+
+    return mImplPriv->mSDP;
+}
+
+/**
+ * Internal function called to produce an SDP session structure using configuration and an SDP offer.
  */
-void SipSession::requestRTPSessions(AsteriskSCF::Media::V1::FormatSeq& formats, bool ipv6)
+pjmedia_sdp_session *SipSession::createSDPAnswer(const pjmedia_sdp_session* offer)
 {
-    // TODO: This needs to be improved for multiple streams
-    AsteriskSCF::Media::RTP::V1::RTPServiceLocatorParamsPtr params =
-        new AsteriskSCF::Media::RTP::V1::RTPServiceLocatorParams();
-    params->category = "rtp";
-    params->formats = formats;
-    params->ipv6 = ipv6;
-
-    AsteriskSCF::Media::RTP::V1::RTPMediaServicePrx factory =
-        AsteriskSCF::Media::RTP::V1::RTPMediaServicePrx::uncheckedCast(mImplPriv->mServiceLocator->locate(params));
-    AsteriskSCF::Media::RTP::V1::RTPSessionPrx session = factory->allocate(params);
-    mImplPriv->mRTPSessions.push_back(session);
-
-    // Create a local copy of the sources, this won't get changed by the RTP session so it's all good
-    mImplPriv->mSources = session->getSources();
-
-    // Create a local copy of the sinks, this won't get changed by the RTP session so it's all good
-    mImplPriv->mSinks = session->getSinks();
-
-    // For testing push a static payload mapping for ULAW into the RTP session
-    AsteriskSCF::Media::RTP::V1::PayloadMap payloads;
-    AsteriskSCF::Media::V1::AudioFormatPtr format = new AsteriskSCF::Media::V1::AudioFormat();
-    format->name = "ulaw";
-    format->sampleRate = 8000;
-    format->frameSize = 20;
-    format->maximumFrameSize = 20;
-    format->minimumFrameSize = 20;
-    payloads.insert(std::make_pair(0, format));
-
-    session->associatePayloads(payloads);
+    // If SDP has already been produced then just return it, don't recreate it
+    if (mImplPriv->mSDP)
+    {
+        return mImplPriv->mSDP;
+    }
+
+    // Create the most common part of the SDP
+    mImplPriv->mSDP = createSDP();
+
+    // 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));
+
+    // 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++)
+    {
+        // If a format is actually found we add it to this sequence so we can create an RTP session
+        FormatSeq formats;
+
+        // Iterate through formats that exist on this stream, creating an SDP descriptor class
+        for (unsigned int format = 0; format < offer->media[stream]->desc.fmt_count; format++)
+        {
+            SDPDescriptorPtr descriptor = new SDPDescriptor();
+
+	    std::string payload = std::string(pj_strbuf(&offer->media[stream]->desc.fmt[format]),
+                pj_strlen(&offer->media[stream]->desc.fmt[format]));
+            std::stringstream(payload) >> descriptor->payload;
+            descriptor->type = std::string(pj_strbuf(&offer->media[stream]->desc.media),
+                                           pj_strlen(&offer->media[stream]->desc.media));
+
+            // Some devices rely solely on the payload for known formats (such as PCMU) so the following format
+            // parameters are completely optional
+            const pjmedia_sdp_attr *attr;
+            if ((attr = pjmedia_sdp_media_find_attr2(offer->media[stream], "rtpmap",
+                                                     &offer->media[stream]->desc.fmt[format])))
+            {
+                pjmedia_sdp_rtpmap *rtpmap;
+                if ((pjmedia_sdp_attr_to_rtpmap(mImplPriv->mInviteSession->pool_active, attr, &rtpmap)) == PJ_SUCCESS)
+                {
+                    descriptor->subtype = std::string(pj_strbuf(&rtpmap->enc_name), pj_strlen(&rtpmap->enc_name));
+                    descriptor->samplerate = rtpmap->clock_rate;
+                }
+            }
+
+            // Next we move on to format specific parameters
+            if ((attr = pjmedia_sdp_media_find_attr2(offer->media[stream], "fmtp",
+                                                     &offer->media[stream]->desc.fmt[format])))
+            {
+                pjmedia_sdp_fmtp fmtp;
+                if ((pjmedia_sdp_attr_get_fmtp(attr, &fmtp)) == PJ_SUCCESS)
+                {
+                    std::string parameter = std::string(pj_strbuf(&fmtp.fmt_param), pj_strlen(&fmtp.fmt_param));
+                    descriptor->parameters.push_back(parameter);
+                }
+            }
+
+            // Next up are attributes that are not specific to the format, such as ptime
+            for (unsigned int attribute = 0; attribute < offer->media[stream]->attr_count; attribute++)
+            {
+                // Attributes we already touch above OR know aren't helpful to the SDP descriptor service don't need to
+                // be given to it, obviously
+                if (!pj_strcmp2(&offer->media[stream]->attr[attribute]->name, "rtpmap") ||
+                    !pj_strcmp2(&offer->media[stream]->attr[attribute]->name, "fmtp") ||
+                    !pj_strcmp2(&offer->media[stream]->attr[attribute]->name, "rtcp") ||
+                    !pj_strcmp2(&offer->media[stream]->attr[attribute]->name, "sendrecv") ||
+                    !pj_strcmp2(&offer->media[stream]->attr[attribute]->name, "sendonly") ||
+                    !pj_strcmp2(&offer->media[stream]->attr[attribute]->name, "recvonly"))
+                {
+                    continue;
+                }
+
+                std::string parameter = std::string(pj_strbuf(&offer->media[stream]->attr[attribute]->name),
+                                                    pj_strlen(&offer->media[stream]->attr[attribute]->name)) + ':' +
+                    std::string(pj_strbuf(&offer->media[stream]->attr[attribute]->value),
+                                pj_strlen(&offer->media[stream]->attr[attribute]->value));
+                descriptor->parameters.push_back(parameter);
+            }
+
+            // Use the configured formats to find what SDP descriptor service to use to turn this into
+            // a media format concrete class
+            SDPDescriptorServicePrx descriptorService = mImplPriv->mEndpoint->getDescriptorService(descriptor);
+
+            // If no descriptor service was found then they offered something that we are not configured to
+            // accept
+            if (descriptorService == 0)
+            {
+                continue;
+            }
+
+            // Try to turn the SDP descriptor class into a media format class, if this fails then whatever
+            // they have in their SDP is something the service is unwilling to accept and we have to consider
+            // it unsupported
+            FormatPtr mediaformat = descriptorService->getDescribedFormat(descriptor);
+
+            if (mediaformat == 0)
+            {
+                continue;
+            }
+
+            // This is a supported format so add it to the sequence
+            formats.push_back(mediaformat);
+        }
+
+        // If no formats were found on this stream that we are configured with then move on to the next one
+        if (formats.empty())
+        {
+            continue;
+        }
+
+        // Assume that no connection level details exist until proven otherwise
+        std::string connection = destination;
+        if (offer->media[stream]->conn)
+        {
+            connection = std::string(pj_strbuf(&offer->media[stream]->conn->addr),
+                                     pj_strlen(&offer->media[stream]->conn->addr));
+        }
+
+        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;
+        }
+
+        // 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;
+        }
+
+        // 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;
+        }
+
+        // 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);
+
+        // Ditto goes for source
+        StreamSourceRTPPrx source = StreamSourceRTPPrx::uncheckedCast(session->getSources().front());
+        mImplPriv->mSources.push_back(source);
+
+        // Update the SIP session with some RTP session details
+        mImplPriv->mRTPSessions.push_back(session);
+
+        // Add a new stream to the answer SDP
+        pjmedia_sdp_media *media = static_cast<pjmedia_sdp_media*>(pj_pool_zalloc(mImplPriv->mDialog->pool,
+                                                                                  sizeof(pjmedia_sdp_media)));
+        mImplPriv->mSDP->media[mImplPriv->mSDP->media_count++] = media;
+
+        // Initialize some parts to sane values
+        media->desc.fmt_count = 0;
+        media->attr_count = 0;
+
+        // 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 = static_cast<pjmedia_sdp_conn*>(pj_pool_zalloc(mImplPriv->mDialog->pool, sizeof(pjmedia_sdp_conn)));
+        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);
+    }
+
+    return mImplPriv->mSDP;
 }
 
 /**
diff --git a/src/SipSession.h b/src/SipSession.h
index 2a60c76..2ca987b 100644
--- a/src/SipSession.h
+++ b/src/SipSession.h
@@ -97,7 +97,7 @@ public:
         const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx&, PJSipManager *manager,
         const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
         const AsteriskSCF::System::Component::V1::ReplicaPtr& replica,
-        bool ipv6, bool isUAC);
+        bool isUAC);
 
     SipSession(const Ice::ObjectAdapterPtr&, const SipEndpointPtr&, const std::string&, const Ice::Identity&,
         const Ice::Identity&, const AsteriskSCF::Media::V1::SessionPrx&,
@@ -169,6 +169,8 @@ public:
 
     pjmedia_sdp_session *createSDPOffer();
 
+    pjmedia_sdp_session *createSDPAnswer(const pjmedia_sdp_session*);
+
     void setRemoteDetails(const std::string&, int);
 
     void setDialog(pjsip_dialog *dialog);
@@ -200,10 +202,14 @@ public:
 
     void enqueueSessionWork(const AsteriskSCF::System::WorkQueue::V1::SuspendableWorkPtr&);
 private:
-    void requestRTPSessions(AsteriskSCF::Media::V1::FormatSeq& formats, bool ipv6);
 
     void initializePJSIPStructs();
 
+    pjmedia_sdp_session *createSDP();
+
+    void addFormatstoSDP(const AsteriskSCF::Media::V1::FormatSeq& formats, pjmedia_sdp_media *media,
+                         AsteriskSCF::Media::RTP::V1::PayloadMap& payloads);
+
     /**
      * Private implementation details.
      */

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


-- 
asterisk-scf/integration/sip.git



More information about the asterisk-scf-commits mailing list