[asterisk-scf-commits] asterisk-scf/release/sip.git branch "master" updated.
Commits to the Asterisk SCF project code repositories
asterisk-scf-commits at lists.digium.com
Sun Jul 10 09:49:38 CDT 2011
branch "master" has been updated
via fc8f71159530653f06e3217ec733058ce033d4fb (commit)
from 7d43289b0a830b2c545b4509ca3df7d8dd510186 (commit)
Summary of changes:
config/Sip.config | 4 +-
config/SipConfigurator.py | 22 +
.../SipSessionManager/SipConfigurationIf.ice | 30 +
.../SipSessionManager/SipStateReplicationIf.ice | 6 +-
src/PJSipSessionModule.cpp | 171 +++----
src/SipConfiguration.cpp | 10 +
src/SipEndpoint.cpp | 222 ++++++++-
src/SipEndpoint.h | 28 +-
src/SipSession.cpp | 555 ++++++++++++++++----
src/SipSession.h | 22 +-
src/SipStateReplicatorListener.cpp | 2 +-
11 files changed, 843 insertions(+), 229 deletions(-)
- Log -----------------------------------------------------------------
commit fc8f71159530653f06e3217ec733058ce033d4fb
Author: Joshua Colp <jcolp at digium.com>
Date: Sun Jul 10 11:50:10 2011 -0300
Merge revised media design implementation.
diff --git a/config/Sip.config b/config/Sip.config
index 176d796..ac8e349 100644
--- a/config/Sip.config
+++ b/config/Sip.config
@@ -79,4 +79,6 @@ direction=both
securetransport=none
# Whether to use IPv6 for media transport or not
rtpoveripv6=no
-
+# Allowable media formats for the endpoint. Each format is separated using , and follows the format
+# <name>/<sample rate>@<frame size>;<format specific parameters>
+formats=ulaw/8000,alaw/8000
diff --git a/config/SipConfigurator.py b/config/SipConfigurator.py
index 7db7217..1d34378 100755
--- a/config/SipConfigurator.py
+++ b/config/SipConfigurator.py
@@ -157,6 +157,28 @@ class SipSectionVisitors(Configurator.SectionVisitors):
mapper.execute(group, section, option)
mapper.finish(group)
+ try:
+ formats = config.get(section, 'formats')
+ configuredFormats = formats.split(',')
+ for format in configuredFormats:
+ name, found, rest = format.partition('/')
+ sampleRate, found, rest = rest.partition('@')
+ frameSize, found, formatSpecific = rest.partition(';')
+
+ item = AsteriskSCF.Configuration.SipSessionManager.V1.SipMediaFormatItem()
+ item.name = name
+ if sampleRate:
+ item.sampleRate = sampleRate
+ if frameSize:
+ item.frameSize = frameSize
+ item.formatSpecific = [ ]
+ if formatSpecific:
+ item.formatSpecific.append(formatSpecific)
+
+ group.configurationItems[format] = item
+ except:
+ print 'No configured formats for endpoint ' + section
+
self.groups.append(group)
def visit_unsupported(self, config, section):
diff --git a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
index ed2dbe3..f37030d 100644
--- a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
+++ b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
@@ -362,6 +362,36 @@ module V1
};
/**
+ * Allowable media format item
+ */
+ class SipMediaFormatItem extends SipConfigurationItem
+ {
+ /**
+ * Name of the media format
+ */
+ string name;
+
+ /**
+ * Sample rate for frames
+ *
+ * This is specified in Hz and has a default value of 8000.
+ */
+ int sampleRate = 8000;
+
+ /**
+ * Amount of audio in frames
+ *
+ * This is specified in
+ */
+ int frameSize;
+
+ /**
+ * Any format specific parameters
+ */
+ Ice::StringSeq formatSpecific;
+ };
+
+ /**
* User agent presentation configuration item
*/
class SipUserAgentItem extends SipConfigurationItem
diff --git a/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice b/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice
index 5e613db..605b249 100644
--- a/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice
+++ b/slice/AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice
@@ -18,6 +18,7 @@
#include <Ice/BuiltinSequences.ice>
#include <Ice/Identity.ice>
#include <AsteriskSCF/Media/MediaIf.ice>
+#include <AsteriskSCF/Media/RTP/MediaRTPIf.ice>
#include <AsteriskSCF/SessionCommunications/SessionCommunicationsIf.ice>
#include <AsteriskSCF/Core/Discovery/ServiceLocatorIf.ice>
#include <AsteriskSCF/System/Component/ConfigurationIf.ice>
@@ -142,8 +143,7 @@ module V1
sequence<AsteriskSCF::SessionCommunications::V1::SessionListener*> SessionListenerSeq;
- // Should this exist within MediaIf.ice? Are we going to pass around a sequence of sessions elsewhere?
- //sequence<AsteriskSCF::Media::V1::Session*> MediaSessionSeq;
+ sequence<AsteriskSCF::Media::RTP::V1::RTPSession*> RTPMediaSessionSeq;
class SipSessionStateItem extends SipStateItem
{
@@ -154,7 +154,7 @@ module V1
Ice::Identity mMediaSessionObjectId;
AsteriskSCF::Media::V1::StreamSourceSeq mSources;
AsteriskSCF::Media::V1::StreamSinkSeq mSinks;
- AsteriskSCF::Media::V1::Session *mMediaSession;
+ RTPMediaSessionSeq mRTPMediaSessions;
SessionListenerSeq mListeners;
AsteriskSCF::SessionCommunications::V1::Bridge *mBridge;
AsteriskSCF::SessionCommunications::V1::SessionCookieDict mCookies;
diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index b6719d5..7128453 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -27,6 +27,8 @@
#include <AsteriskSCF/Core/Routing/RoutingIf.h>
#include <AsteriskSCF/SessionCommunications/SessionCommunicationsIf.h>
#include <AsteriskSCF/Media/MediaIf.h>
+#include <AsteriskSCF/Media/RTP/MediaRTPIf.h>
+#include <AsteriskSCF/Media/SDP/MediaSDPIf.h>
#include <AsteriskSCF/logger.h>
#include <AsteriskSCF/WorkQueue/WorkQueue.h>
#include <AsteriskSCF/WorkQueue/SuspendableWorkQueue.h>
@@ -53,6 +55,8 @@ using namespace AsteriskSCF::Core::Routing::V1;
using namespace AsteriskSCF::Core::Endpoint::V1;
using namespace AsteriskSCF::SessionCommunications::V1;
using namespace AsteriskSCF::Media::V1;
+using namespace AsteriskSCF::Media::RTP::V1;
+using namespace AsteriskSCF::Media::SDP::V1;
using namespace AsteriskSCF::Replication::SipSessionManager::V1;
using namespace AsteriskSCF::System::ThreadPool::V1;
using namespace AsteriskSCF::System::WorkQueue::V1;
@@ -94,7 +98,7 @@ void PJSipSessionModInfo::updateSessionState(pjsip_inv_session *inv_session)
mSessionState->mMediaSessionObjectId = mSession->getMediaSessionProxy()->ice_getIdentity();
mSessionState->mSources = mSession->getSources();
mSessionState->mSinks = mSession->getSinks();
- mSessionState->mMediaSession = mSession->getHiddenMediaSession();
+ mSessionState->mRTPMediaSessions = mSession->getRTPMediaSessions();
mSessionState->mListeners = mSession->getListeners();
try
{
@@ -214,7 +218,14 @@ void PJSipSessionModule::replicateState(PJSipDialogModInfo *dlgInfo, PJSipTransa
lg(Debug) << "Endpoint name: " << sessionInfo->mSessionState->mEndpointName;
lg(Debug) << "Session object identity: " << sessionInfo->mSessionState->mSessionObjectId.name;
lg(Debug) << "Media session object identity: " << sessionInfo->mSessionState->mMediaSessionObjectId.name;
- lg(Debug) << "Media session: " << sessionInfo->mSessionState->mMediaSession;
+
+ for (RTPMediaSessionSeq::const_iterator mediaSession = sessionInfo->mSessionState->mRTPMediaSessions.begin();
+ mediaSession != sessionInfo->mSessionState->mRTPMediaSessions.end();
+ ++mediaSession)
+ {
+ lg(Debug) << "Media session: " << (*mediaSession);
+ }
+
lg(Debug) << "Bridge: " << sessionInfo->mSessionState->mBridge;
lg(Debug) << "--- Begin Invite Session " << sessionInfo->mInviteState->key;
lg(Debug) << "Current state: " << sessionInfo->mInviteState->mCurrentState;
@@ -352,8 +363,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];
@@ -571,6 +611,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));
@@ -1387,108 +1429,55 @@ protected:
return Complete;
}
- 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];
SipSessionPtr session = session_mod_info->getSessionPtr();
- std::string destination(pj_strbuf(&remote_conn->addr), pj_strlen(&remote_conn->addr));
- try
- {
- session->setRemoteDetails(destination, remote_sdp->media[0]->desc.port);
- }
- catch (const AsteriskSCF::Media::RTP::V1::InvalidAddress&)
- {
- pjsip_tx_data *packet;
- if (pjsip_inv_end_session(mInv, 488, NULL, &packet) == PJ_SUCCESS)
- {
- pjsip_inv_send_msg(mInv, packet);
- }
- return Complete;
- }
+ // In case there is no stream level connection details store this away
+ std::string destination(pj_strbuf(&remote_sdp->conn->addr), pj_strlen(&remote_sdp->conn->addr));
+
+ StreamSinkSeq sinks = session->getSinks();
// Each stream has its own set of formats, so go to that granularity
for (unsigned int stream = 0; stream < remote_sdp->media_count; stream++)
{
- // We should have the parsing of the connection information here
-
- // We should have the parsing of the rtcp attribute here
-
- FormatSeq formats;
-
- // Next step is to see what formats exist on this stream
- for (unsigned int format = 0; format < remote_sdp->media[stream]->desc.fmt_count; format++)
+ // Do some sanity checking to confirm we have a stream setup for their answer stream
+ if (sinks.size() < (stream + 1))
{
- FormatDiscoverySDPPtr params = new FormatDiscoverySDP();
- params->category = "media_format";
- std::stringstream(pj_strbuf(&remote_sdp->media[stream]->desc.fmt[format])) >> params->payload;
- params->type = std::string(pj_strbuf(&remote_sdp->media[stream]->desc.media),
- pj_strlen(&remote_sdp->media[stream]->desc.media));
-
- // Some devices rely solely on the payload for known formats (such as PCMU) so the following format
- // parameters are optional
- const pjmedia_sdp_attr *attr;
- if ((attr = pjmedia_sdp_media_find_attr2(remote_sdp->media[stream], "rtpmap",
- &remote_sdp->media[stream]->desc.fmt[format])))
- {
- pjmedia_sdp_rtpmap *rtpmap;
- if ((pjmedia_sdp_attr_to_rtpmap(mInv->pool_active, attr, &rtpmap)) == PJ_SUCCESS)
- {
- params->subtype = std::string(pj_strbuf(&rtpmap->enc_name), pj_strlen(&rtpmap->enc_name));
- params->samplerate = rtpmap->clock_rate;
- }
- }
-
- // Next we move on to the format specific parameters
- if ((attr = pjmedia_sdp_media_find_attr2(remote_sdp->media[stream], "fmtp",
- &remote_sdp->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));
- params->parameters.push_back(parameter);
- }
- }
+ continue;
+ }
- // Next up are attributes that are not specific to the format, such as ptime
- for (unsigned int attribute = 0; attribute < remote_sdp->media[stream]->attr_count; attribute++)
- {
- // Attributes we already touch above OR know aren't helpful to the media format component don't need to
- // be given to it, obviously
- if (!pj_strcmp2(&remote_sdp->media[stream]->attr[attribute]->name, "rtpmap") ||
- !pj_strcmp2(&remote_sdp->media[stream]->attr[attribute]->name, "fmtp") ||
- !pj_strcmp2(&remote_sdp->media[stream]->attr[attribute]->name, "rtcp") ||
- !pj_strcmp2(&remote_sdp->media[stream]->attr[attribute]->name, "sendrecv") ||
- !pj_strcmp2(&remote_sdp->media[stream]->attr[attribute]->name, "sendonly") ||
- !pj_strcmp2(&remote_sdp->media[stream]->attr[attribute]->name, "recvonly"))
- {
- 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));
+ }
- std::string parameter = std::string(pj_strbuf(&remote_sdp->media[stream]->attr[attribute]->name),
- pj_strlen(&remote_sdp->media[stream]->attr[attribute]->name)) + ':' +
- std::string(pj_strbuf(&remote_sdp->media[stream]->attr[attribute]->value),
- pj_strlen(&remote_sdp->media[stream]->attr[attribute]->value));
- params->parameters.push_back(parameter);
- }
+ // TODO: Add parsing of RTCP attribute
- try
- {
- MediaFormatServicePrx service = MediaFormatServicePrx::uncheckedCast(mServiceLocator->locate(params));
+ // The order of the media lines should match our offer so we can get the sink to update
+ // connection details on based on what stream number this is, easy!
+ StreamSinkRTPPrx sink = StreamSinkRTPPrx::uncheckedCast(sinks[stream]);
- // It is entirely possible for the service locator to not find a service that knows about this media
- // format
- if (service != 0)
- {
- formats.push_back(FormatPtr::dynamicCast(service->getFormat(params)));
- }
- }
- catch (...)
+ // 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)
{
- // If we get here the format just isn't supported...
+ pjsip_inv_send_msg(mInv, packet);
}
+ return Complete;
}
+
+ // TODO: Add format parsing stuff here so we can determine the formats actually negotiated
}
return Complete;
}
diff --git a/src/SipConfiguration.cpp b/src/SipConfiguration.cpp
index a9ed619..d4077a8 100644
--- a/src/SipConfiguration.cpp
+++ b/src/SipConfiguration.cpp
@@ -183,6 +183,11 @@ class EndpointConfigHelper : public boost::enable_shared_from_this<EndpointConfi
{
mUpdates.push_back(boost::bind(&EndpointConfigHelper::updateDirection, mConfig, direction));
}
+
+ void visitSipMediaFormatItem(const SipMediaFormatItemPtr& format)
+ {
+ mUpdates.push_back(boost::bind(&EndpointConfigHelper::addFormat, mConfig, format));
+ }
void visitSipSourceTransportAddressItem(const SipSourceTransportAddressItemPtr& source)
{
@@ -258,6 +263,11 @@ public:
mEndpoint->setCallDirection(translateCallDirection(direction->callDirection));
}
+ void addFormat(const SipMediaFormatItemPtr& format)
+ {
+ mEndpoint->addFormat(format->name, format->sampleRate, format->frameSize, format->formatSpecific);
+ }
+
void updateSource(const SipSourceTransportAddressItemPtr& source)
{
mEndpoint->setSourceAddress(source->host, source->port);
diff --git a/src/SipEndpoint.cpp b/src/SipEndpoint.cpp
index 2a6af1a..ac3518a 100644
--- a/src/SipEndpoint.cpp
+++ b/src/SipEndpoint.cpp
@@ -21,8 +21,13 @@
#include <AsteriskSCF/Core/Discovery/ServiceLocatorIf.h>
#include <AsteriskSCF/logger.h>
+#include <AsteriskSCF/Media/MediaIf.h>
+#include <AsteriskSCF/Media/SDP/MediaSDPIf.h>
using namespace AsteriskSCF::System::Logging;
+using namespace AsteriskSCF::Media::V1;
+using namespace AsteriskSCF::Media::SDP::V1;
+using namespace AsteriskSCF::Core::Discovery::V1;
namespace
{
@@ -35,6 +40,121 @@ namespace SipSessionManager
{
/**
+ * Class used to store information about the configured formats.
+ */
+class ConfiguredFormat : public IceUtil::Shared
+{
+public:
+ ConfiguredFormat(const std::string& name, int sampleRate, int frameSize, const Ice::StringSeq& formatSpecific) :
+ mName(name), mSampleRate(sampleRate), mFrameSize(frameSize), mFormatSpecific(formatSpecific), mAvailable(false) { }
+
+ void locateCB(const Ice::ObjectPrx& service)
+ {
+ // Store away the descriptor service proxy for future use
+ mDescriptorService = SDPDescriptorServicePrx::uncheckedCast(service);
+
+ // Setup an AMI request to get the named format
+ Callback_SDPDescriptorService_getNamedFormatPtr descriptorCB = newCallback_SDPDescriptorService_getNamedFormat(
+ this, &ConfiguredFormat::getNamedFormatCB, &ConfiguredFormat::getNamedFormatFailureCB);
+
+ // Send it off
+ mDescriptorService->begin_getNamedFormat(mName, mSampleRate, mFrameSize, mFormatSpecific, descriptorCB);
+ }
+
+ void locateFailureCB(const Ice::Exception&)
+ {
+ }
+
+ void getNamedFormatCB(const FormatPtr& format)
+ {
+ // Store away the format
+ mFormat = format;
+
+ // Setup an AMI request to get the SDP descriptor
+ Callback_SDPDescriptorService_getDescriptorPtr descriptorCB = newCallback_SDPDescriptorService_getDescriptor(
+ this, &ConfiguredFormat::getDescriptorCB, &ConfiguredFormat::getDescriptorFailureCB);
+
+ // Send it off
+ mDescriptorService->begin_getDescriptor(mFormat, descriptorCB);
+ }
+
+ void getNamedFormatFailureCB(const Ice::Exception&)
+ {
+ }
+
+ void getDescriptorCB(const SDPDescriptorPtr& descriptor)
+ {
+ // Store it away
+ mDescriptor = descriptor;
+
+ // This configured format is now available for use
+ mAvailable = true;
+ }
+
+ void getDescriptorFailureCB(const Ice::Exception&)
+ {
+ }
+
+ SDPDescriptorServicePrx getDescriptorService()
+ {
+ return mDescriptorService;
+ }
+
+ SDPDescriptorPtr getDescriptor()
+ {
+ return mDescriptor;
+ }
+
+ FormatPtr getFormat()
+ {
+ return mFormat;
+ }
+
+private:
+ /**
+ * Name of the media format.
+ */
+ std::string mName;
+
+ /**
+ * Sample rate of the media.
+ */
+ int mSampleRate;
+
+ /**
+ * Size of the media frames.
+ */
+ int mFrameSize;
+
+ /**
+ * Format specific parameters.
+ */
+ Ice::StringSeq mFormatSpecific;
+
+ /**
+ * Whether this configured format is available or not.
+ */
+ bool mAvailable;
+
+ /**
+ * SDP descriptor service for this media format.
+ */
+ SDPDescriptorServicePrx mDescriptorService;
+
+ /**
+ * Media format concrete class, retrieved from SDP descriptor service.
+ */
+ FormatPtr mFormat;
+
+ /**
+ * SDP descriptor class, retrieved from the SDP descriptor service.
+ */
+ SDPDescriptorPtr mDescriptor;
+};
+
+typedef IceUtil::Handle<ConfiguredFormat> ConfiguredFormatPtr;
+
+/**
* Private implementation details for SipEndpoint.
*/
class SipEndpointImplPriv
@@ -78,6 +198,11 @@ public:
std::vector<SipSessionPtr> mSessions;
/**
+ * A vector of configured formats for this endpoint.
+ */
+ std::vector<ConfiguredFormatPtr> mFormats;
+
+ /**
* A proxy to our endpoint.
*/
AsteriskSCF::SessionCommunications::V1::SessionEndpointPrx mEndpointProxy;
@@ -141,6 +266,20 @@ void SipEndpoint::setRTPOverIPv6(bool enabled)
mImplPriv->mConfig.sessionConfig.rtpOverIPv6 = enabled;
}
+void SipEndpoint::addFormat(const std::string& name, int sampleRate, int frameSize, const Ice::StringSeq& formatSpecific)
+{
+ SDPDescriptorServiceLocatorParamsPtr params = new SDPDescriptorServiceLocatorParams();
+ params->category = "Media/SDP_Descriptor";
+ params->name = name;
+
+ ConfiguredFormatPtr configuredFormat = new ConfiguredFormat(name, sampleRate, frameSize, formatSpecific);
+ Callback_ServiceLocator_locatePtr descriptorCB = newCallback_ServiceLocator_locate(
+ configuredFormat, &ConfiguredFormat::locateCB, &ConfiguredFormat::locateFailureCB);
+
+ mImplPriv->mFormats.push_back(configuredFormat);
+ mImplPriv->mServiceLocator->begin_locate(params, descriptorCB);
+}
+
Direction SipEndpointConfig::stringToDirection(const std::string& directionString)
{
if (directionString == "Both")
@@ -189,7 +328,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();
@@ -198,17 +337,18 @@ 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;
}
AsteriskSCF::SipSessionManager::SipSessionPtr SipEndpoint::createSession(const std::string& destination,
- const Ice::Identity& sessionid, const Ice::Identity& mediaid,
- const AsteriskSCF::Media::V1::SessionPrx& mediasession, const AsteriskSCF::Media::V1::StreamSourceSeq& sources,
- const AsteriskSCF::Media::V1::StreamSinkSeq& sinks)
+ const Ice::Identity& sessionid, const Ice::Identity& mediaid,
+ const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq& mediasessions,
+ const AsteriskSCF::Media::V1::StreamSourceSeq& sources,
+ const AsteriskSCF::Media::V1::StreamSinkSeq& sinks)
{
- SipSessionPtr session = new SipSession(mImplPriv->mAdapter, this, destination, sessionid, mediaid, mediasession,
+ SipSessionPtr session = new SipSession(mImplPriv->mAdapter, this, destination, sessionid, mediaid, mediasessions,
sources, sinks, mImplPriv->mManager, mImplPriv->mServiceLocator, mImplPriv->mReplica, false);
mImplPriv->mSessions.push_back(session);
return session;
@@ -249,5 +389,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()->name == format->name)
+ {
+ 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 cb91a66..ebf4d87 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;
@@ -239,8 +246,8 @@ public:
//
AsteriskSCF::SipSessionManager::SipSessionPtr createSession(const std::string&);
AsteriskSCF::SipSessionManager::SipSessionPtr createSession(const std::string&, const Ice::Identity&,
- const Ice::Identity&, const AsteriskSCF::Media::V1::SessionPrx&,
- const AsteriskSCF::Media::V1::StreamSourceSeq&, const AsteriskSCF::Media::V1::StreamSinkSeq&);
+ const Ice::Identity&, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq&,
+ const AsteriskSCF::Media::V1::StreamSourceSeq&, const AsteriskSCF::Media::V1::StreamSinkSeq&);
void removeSession(const AsteriskSCF::SessionCommunications::V1::SessionPtr&);
@@ -254,6 +261,23 @@ public:
void setCallDirection(enum Direction);
void setSecureTransport(enum Direction);
void setRTPOverIPv6(bool);
+ void addFormat(const std::string& name, int sampleRate, int frameSize, const Ice::StringSeq& formatSpecific);
+
+ /**
+ * 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:
/**
diff --git a/src/SipSession.cpp b/src/SipSession.cpp
index 972b5b0..6210963 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), mSDPFinalized(false) { };
AsteriskSCF::SessionCommunications::V1::SessionInfoPtr getInfo()
{
@@ -206,7 +213,7 @@ public:
/**
* A vector of RTP media sessions belonging to this endpoint.
*/
- std::vector<AsteriskSCF::Media::RTP::V1::RTPSessionPrx> mRTPSessions;
+ RTPMediaSessionSeq mRTPSessions;
/**
* A vector of media sources associated with this endpoint.
@@ -253,8 +260,27 @@ public:
AsteriskSCF::System::Component::V1::ReplicaPtr mReplica;
SessionWorkPtr mSessionWork;
+
+ /**
+ * SDP that has been produced for this session.
+ */
+ pjmedia_sdp_session *mSDP;
+
+ /**
+ * Whether the SDP has been finalized or not.
+ */
+ bool mSDPFinalized;
};
+/**
+ * Template for allocating from a pool.
+ */
+template<typename T>
+inline T *allocate_from_pool(pj_pool_t *pool)
+{
+ return static_cast<T*>(pj_pool_zalloc(pool, sizeof(T)));
+}
+
void SipSession::initializePJSIPStructs()
{
pj_str_t local_uri, remote_uri;
@@ -294,7 +320,10 @@ 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)
{
lg(Error) << "Failed to create a UAC INVITE session!";
@@ -318,9 +347,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)
@@ -335,10 +364,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)
{
lg(Debug) << "New session is UAC, so we're creating the necessary PJSIP structures";
@@ -350,11 +375,11 @@ SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter, const SipEndpointPt
* Replica constructor.
*/
SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter, const SipEndpointPtr& endpoint,
- const std::string& destination, const Ice::Identity& sessionid,
- const Ice::Identity& mediaid, const AsteriskSCF::Media::V1::SessionPrx& mediasession,
- const AsteriskSCF::Media::V1::StreamSourceSeq& sources, const AsteriskSCF::Media::V1::StreamSinkSeq& sinks,
- PJSipManager *manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
- const AsteriskSCF::System::Component::V1::ReplicaPtr& replica, bool isUAC)
+ const std::string& destination, const Ice::Identity& sessionid,
+ const Ice::Identity& mediaid, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq& mediasessions,
+ const AsteriskSCF::Media::V1::StreamSourceSeq& sources, const AsteriskSCF::Media::V1::StreamSinkSeq& sinks,
+ 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))
{
mImplPriv->mSessionProxy =
@@ -364,10 +389,7 @@ SipSession::SipSession(const Ice::ObjectAdapterPtr& adapter, const SipEndpointPt
mImplPriv->mMediaSessionProxy =
AsteriskSCF::Media::V1::SessionPrx::uncheckedCast(adapter->add(mImplPriv->mMediaSession, mediaid));
- AsteriskSCF::Media::RTP::V1::RTPSessionPrx rtpsession =
- AsteriskSCF::Media::RTP::V1::RTPSessionPrx::uncheckedCast(mediasession);
- mImplPriv->mRTPSessions.push_back(rtpsession);
-
+ mImplPriv->mRTPSessions = mediasessions;
mImplPriv->mSources = sources;
mImplPriv->mSinks = sinks;
@@ -435,8 +457,7 @@ public:
if ((Connect = AsteriskSCF::SessionCommunications::V1::ConnectIndicationPtr::dynamicCast(mIndication)))
{
lg(Debug) << "Processing a Connect indication";
- 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)))
{
@@ -453,8 +474,7 @@ public:
else if ((Progress = AsteriskSCF::SessionCommunications::V1::ProgressIndicationPtr::dynamicCast(mIndication)))
{
lg(Debug) << "Processing a Progress indication";
- 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)))
{
@@ -1019,8 +1039,8 @@ public:
{
//XXX This loop may be a candidate for making AMI-ified and returning "Suspended"
// Release all the RTP sessions we are using
- for (std::vector<AsteriskSCF::Media::RTP::V1::RTPSessionPrx>::const_iterator i =
- mSessionPriv->mRTPSessions.begin(); i != mSessionPriv->mRTPSessions.end(); ++i)
+ for (RTPMediaSessionSeq::const_iterator i = mSessionPriv->mRTPSessions.begin();
+ i != mSessionPriv->mRTPSessions.end(); ++i)
{
try
{
@@ -1051,12 +1071,11 @@ 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 = allocate_from_pool<pjmedia_sdp_session>(mImplPriv->mDialog->pool);
pj_strdup2(mImplPriv->mDialog->pool, &sdp->origin.user, "AsteriskSCF");
pj_time_val tv;
@@ -1064,113 +1083,418 @@ 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));
lg(Debug) << "The local address we are placing in our SDP is " << address;
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::requestRTPSessions(AsteriskSCF::Media::V1::FormatSeq& formats, bool ipv6)
+void SipSession::addFormatstoSDP(const FormatSeq& formats, pjmedia_sdp_media *media, PayloadMap& payloads)
{
- // 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);
+ // Start dynamic payload assignment at 96
+ int dynamicPayload = 96;
+
+ // 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;
+ }
+
+ int payload = ourDescriptor->payload;
+
+ // If we have been told to assign a dynamic payload ourselves do so
+ if (payload == -1)
+ {
+ // Ensure we don't assign a payload that has been specified by checking the payload -> format mapping
+ for (; dynamicPayload < 130; dynamicPayload++)
+ {
+ if (payloads.find(dynamicPayload) == payloads.end())
+ {
+ payload = dynamicPayload;
+ break;
+ }
+ }
+
+ // If the payload still has not been decided we have no more room in the dynamic payload range
+ if (payload == -1)
+ {
+ 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>(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 = allocate_from_pool<pjmedia_sdp_attr>(mImplPriv->mDialog->pool);
+ std::string parameters = "fmtp:" + boost::lexical_cast<std::string>(payload) + " " + (*parameter);
+ pj_strdup2(mImplPriv->mDialog->pool, &attr->name, parameters.c_str());
+ media->attr[media->attr_count++] = attr;
+ }
+
+ payloads.insert(std::make_pair(payload, (*mediaformat)));
+ }
}
/**
- * Internal function called to set the remote details for RTP.
+ * Internal function called to produce an SDP session structure using configuration.
*/
-void SipSession::setRemoteDetails(const std::string& destination, int port)
+pjmedia_sdp_session *SipSession::createSDPOffer()
{
- AsteriskSCF::Media::RTP::V1::StreamSinkRTPPrx sink =
- AsteriskSCF::Media::RTP::V1::StreamSinkRTPPrx::uncheckedCast(mImplPriv->mSinks.front());
- sink->setRemoteDetails(destination, port);
+ // If SDP has already been produced then just return it, don't recreate it
+ if (mImplPriv->mSDPFinalized == true)
+ {
+ 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 if not already done
+ if (!mImplPriv->mSDP)
+ {
+ 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)
+ {
+ 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());
+ 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 = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
+ mImplPriv->mSDP->media[mImplPriv->mSDP->media_count++] = media;
+ pj_strdup2(mImplPriv->mDialog->pool, &media->desc.media, mImplPriv->mEndpoint->getDescriptor(stream->second.front())->type.c_str());
+
+ // TODO: This should not be hardcoded
+ pj_strdup2(mImplPriv->mDialog->pool, &media->desc.transport, "RTP/AVP");
+
+ // Add connection level details
+ media->conn = allocate_from_pool<pjmedia_sdp_conn>(mImplPriv->mDialog->pool);
+ pj_strdup2(mImplPriv->mDialog->pool, &media->conn->net_type, "IN");
+
+ 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);
+
+ // The SDP has been finalized enough
+ mImplPriv->mSDPFinalized = true;
+ }
+
+ return mImplPriv->mSDPFinalized == true ? mImplPriv->mSDP : 0;
+}
+
+/**
+ * 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)
+{
+ // 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)
+ {
+ 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]));
+ descriptor->payload = boost::lexical_cast<int>(payload);
+ descriptor->type = std::string(pj_strbuf(&offer->media[stream]->desc.media),
+ pj_strlen(&offer->media[stream]->desc.media));
+
+ // 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 = allocate_from_pool<pjmedia_sdp_media>(mImplPriv->mDialog->pool);
+ mImplPriv->mSDP->media[mImplPriv->mSDP->media_count++] = media;
+
+ // Since our stream should be like the offering stream we can just use some values from there
+ media->desc.media = offer->media[stream]->desc.media;
+ media->desc.transport = offer->media[stream]->desc.transport;
+
+ // Add connection level information so they know our IP address
+ media->conn = allocate_from_pool<pjmedia_sdp_conn>(mImplPriv->mDialog->pool);
+ media->conn->net_type = offer->origin.net_type;
+ media->conn->addr_type = offer->origin.addr_type;
+ pj_strdup2(mImplPriv->mDialog->pool, &media->conn->addr, source->getLocalAddress().c_str());
+
+ // If session level connection information has not yet been set then set it to us
+ if (!mImplPriv->mSDP->conn)
+ {
+ mImplPriv->mSDP->conn = media->conn;
+ }
+
+ // Add port information so they can talk to us
+ media->desc.port = (pj_uint16_t) source->getLocalPort();
+ media->desc.port_count = 1;
+
+ PayloadMap payloads;
+
+ addFormatstoSDP(formats, media, payloads);
+
+ // Push the payload mapping to the RTP session so it'll correctly map things
+ session->associatePayloads(payloads);
+
+ // The SDP has been finalized enough
+ mImplPriv->mSDPFinalized = true;
+ }
+
+ return mImplPriv->mSDPFinalized == true ? mImplPriv->mSDP : 0;
}
/**
@@ -1276,12 +1600,11 @@ void SipSession::setListeners(const SessionListenerSeq& listeners)
}
/**
- * Internal function which returns the media session that is hidden inside the SIP session.
+ * Internal function which returns the RTP media sessions that are hidden inside the SIP session.
*/
-AsteriskSCF::Media::V1::SessionPrx SipSession::getHiddenMediaSession()
+RTPMediaSessionSeq SipSession::getRTPMediaSessions()
{
- // TODO: This should return a sequence
- return mImplPriv->mRTPSessions.front();
+ return mImplPriv->mRTPSessions;
}
bool SipSession::operator==(const SipSession &other) const {
diff --git a/src/SipSession.h b/src/SipSession.h
index f670454..ce5dd43 100644
--- a/src/SipSession.h
+++ b/src/SipSession.h
@@ -98,14 +98,14 @@ 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&,
- const AsteriskSCF::Media::V1::StreamSourceSeq&, const AsteriskSCF::Media::V1::StreamSinkSeq&,
- PJSipManager *manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
- const AsteriskSCF::System::Component::V1::ReplicaPtr& replica,
- bool isUAC);
+ const Ice::Identity&, const AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq&,
+ const AsteriskSCF::Media::V1::StreamSourceSeq&, const AsteriskSCF::Media::V1::StreamSinkSeq&,
+ PJSipManager *manager, const AsteriskSCF::Core::Discovery::V1::ServiceLocatorPrx& serviceLocator,
+ const AsteriskSCF::System::Component::V1::ReplicaPtr& replica,
+ bool isUAC);
bool operator==(const SipSession &other) const;
@@ -176,7 +176,7 @@ public:
pjmedia_sdp_session *createSDPOffer();
- void setRemoteDetails(const std::string&, int);
+ pjmedia_sdp_session *createSDPAnswer(const pjmedia_sdp_session*);
void setDialog(pjsip_dialog *dialog);
@@ -203,14 +203,18 @@ public:
void setListeners(const AsteriskSCF::Replication::SipSessionManager::V1::SessionListenerSeq&);
- AsteriskSCF::Media::V1::SessionPrx getHiddenMediaSession();
+ AsteriskSCF::Replication::SipSessionManager::V1::RTPMediaSessionSeq getRTPMediaSessions();
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.
*/
diff --git a/src/SipStateReplicatorListener.cpp b/src/SipStateReplicatorListener.cpp
index 6949339..c80d019 100644
--- a/src/SipStateReplicatorListener.cpp
+++ b/src/SipStateReplicatorListener.cpp
@@ -102,7 +102,7 @@ public:
// Now that all is well we can create an actual session
SipSessionPtr localSession = endpoint->createSession("", session->mSessionObjectId,
- session->mMediaSessionObjectId, session->mMediaSession, session->mSources, session->mSinks);
+ session->mMediaSessionObjectId, session->mRTPMediaSessions, session->mSources, session->mSinks);
localitem->setSession(localSession);
}
else
-----------------------------------------------------------------------
--
asterisk-scf/release/sip.git
More information about the asterisk-scf-commits
mailing list