[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