signalling and media B2BUA class implementation.

Can be used the same way as B2BSession, but SEMS stays in the media path as well.
Not very efficient if used for relaying (it decodes and encodes the media). 



git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@286 8eb893ce-cfd4-0310-b710-fb5ebe64c474
sayer/1.4-spce2.6
Stefan Sayer 20 years ago
parent 445db2035f
commit d60aee543a

@ -0,0 +1,367 @@
/*
* $Id: AmB2ABSession.cpp 145 2006-11-26 00:01:18Z sayer $
*
* Copyright (C) 2006-2007 iptego GmbH
*
* This file is part of sems, a free SIP media server.
*
* sems is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* For a license to use the sems software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* sems is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "AmB2ABSession.h"
#include "AmSessionContainer.h"
#include "AmConfig.h"
#include "AmMediaProcessor.h"
#include <assert.h>
AmB2ABSession::AmB2ABSession()
: AmSession(),
connector(NULL)
{
}
AmB2ABSession::AmB2ABSession(const string& other_local_tag)
: other_id(other_local_tag),
AmSession()
{}
AmB2ABSession::~AmB2ABSession()
{
}
void AmB2ABSession::clear_other()
{
#if __GNUC__ < 3
string cleared ("");
other_id.assign (cleared, 0, 0);
#else
other_id.clear();
#endif
}
void AmB2ABSession::process(AmEvent* event)
{
B2ABEvent* b2b_e = dynamic_cast<B2ABEvent*>(event);
if(b2b_e){
onB2ABEvent(b2b_e);
return;
}
AmSession::process(event);
}
void AmB2ABSession::onB2ABEvent(B2ABEvent* ev)
{
switch(ev->event_id){
case B2ABTerminateLeg:
terminateLeg();
break;
}
}
void AmB2ABSession::relayEvent(AmEvent* ev)
{
DBG("AmB2ABSession::relayEvent: id=%s\n",
other_id.c_str());
if(!other_id.empty())
AmSessionContainer::instance()->postEvent(other_id,ev);
}
void AmB2ABSession::releaseAudio() {
if (connector)
connector->disconnectSession(this);
connector=NULL;
}
void AmB2ABSession::onBye(const AmSipRequest& req) {
terminateOtherLeg();
releaseAudio();
setStopped();
}
void AmB2ABSession::terminateLeg()
{
dlg.bye();
releaseAudio();
setStopped();
}
void AmB2ABSession::terminateOtherLeg()
{
relayEvent(new B2ABEvent(B2ABTerminateLeg));
clear_other();
}
AmB2ABCallerSession::AmB2ABCallerSession()
: AmB2ABSession(),
callee_status(None)
{
}
AmB2ABCallerSession::~AmB2ABCallerSession()
{ }
void AmB2ABCallerSession::terminateOtherLeg()
{
if (callee_status != None)
AmB2ABSession::terminateOtherLeg();
callee_status = None;
}
void AmB2ABCallerSession::onB2ABEvent(B2ABEvent* ev)
{
switch(ev->event_id) {
case B2ABConnectAudio: {
callee_status = Connected;
DBG("ConnectAudio event received from other leg\n");
B2ABConnectAudioEvent* ca = dynamic_cast<B2ABConnectAudioEvent*>(ev);
assert(ca);
connector = ca->connector;
connector->connectSession(this, ca->first);
AmMediaProcessor::instance()->addSession(this, callgroup);
return;
} break;
case B2ABConnectOtherLegException:
case B2ABConnectOtherLegFailed: {
DBG("looks like callee leg could not be created. terminating our leg.\n");
terminateLeg();
callee_status = None;
return;
} break;
case B2ABOtherLegRinging: {
DBG("callee_status set to Ringing.\n");
callee_status = Ringing;
return;
} break;
}
AmB2ABSession::onB2ABEvent(ev);
}
void AmB2ABCallerSession::connectCallee(const string& remote_party,
const string& remote_uri,
const string& local_party,
const string& local_uri)
{
if(callee_status != None)
terminateOtherLeg();
B2ABConnectLegEvent* ev = new B2ABConnectLegEvent(remote_party,remote_uri,
local_party,local_uri,
getLocalTag());
relayEvent(ev);
callee_status = NoReply;
}
void AmB2ABCallerSession::relayEvent(AmEvent* ev)
{
if(other_id.empty()){
B2ABConnectLegEvent* co_ev = dynamic_cast<B2ABConnectLegEvent*>(ev);
if( co_ev ) {
setupCalleeSession(createCalleeSession(), co_ev);
}
}
AmB2ABSession::relayEvent(ev);
}
void AmB2ABCallerSession::setupCalleeSession(AmB2ABCalleeSession* callee_session,
B2ABConnectLegEvent* ev) {
other_id = AmSession::getNewId();
AmSipDialog& callee_dlg = callee_session->dlg;
callee_dlg.callid = AmSession::getNewId() + "@" + AmConfig::LocalIP;
callee_dlg.local_tag = other_id;
callee_session->start();
AmSessionContainer* sess_cont = AmSessionContainer::instance();
sess_cont->addSession(other_id,callee_session);
}
AmB2ABCalleeSession* AmB2ABCallerSession::createCalleeSession()
{
return new AmB2ABCalleeSession(getLocalTag());
}
AmB2ABCalleeSession::AmB2ABCalleeSession(const string& other_local_tag)
: AmB2ABSession(other_local_tag)
{ }
AmB2ABCalleeSession::~AmB2ABCalleeSession()
{ }
void AmB2ABCalleeSession::onB2ABEvent(B2ABEvent* ev)
{
if(ev->event_id == B2ABConnectLeg){
try {
B2ABConnectLegEvent* co_ev = dynamic_cast<B2ABConnectLegEvent*>(ev);
assert(co_ev);
dlg.local_party = co_ev->local_party;
dlg.local_uri = co_ev->local_uri;
dlg.remote_party = co_ev->remote_party;
dlg.remote_uri = co_ev->remote_uri;
setCallgroup(co_ev->callgroup);
setNegotiateOnReply(true);
sendInvite();
return;
}
catch(const AmSession::Exception& e){
ERROR("%i %s\n",e.code,e.reason.c_str());
relayEvent(new B2ABConnectOtherLegExceptionEvent(e.code,e.reason));
setStopped();
}
catch(const string& err){
ERROR("startSession: %s\n",err.c_str());
relayEvent(new B2ABConnectOtherLegExceptionEvent(500,err));
setStopped();
}
catch(...){
ERROR("unexpected exception\n");
relayEvent(new B2ABConnectOtherLegExceptionEvent(500,"unexpected exception"));
setStopped();
}
}
AmB2ABSession::onB2ABEvent(ev);
}
void AmB2ABCalleeSession::onSessionStart(const AmSipReply& rep) {
DBG("onSessionStart of callee session\n");
// connect our audio
connector = new AmSessionAudioConnector(this, true);
// acc to mediaprocessor
AmMediaProcessor::instance()->addSession(this, callgroup);
relayEvent(new B2ABConnectAudioEvent(connector, false));
}
void AmB2ABCalleeSession::reconnectAudio() {
if (connector) {
connector->connectSession(this, true);
AmMediaProcessor::instance()->addSession(this, callgroup);
} else {
ERROR("can not connect audio of not connected session.\n");
}
}
void AmB2ABCallerSession::reconnectAudio() {
if (connector) {
connector->connectSession(this, false);
AmMediaProcessor::instance()->addSession(this, callgroup);
} else {
ERROR("can not connect audio of not connected session.\n");
}
}
void AmB2ABCalleeSession::onSipReply(const AmSipReply& rep) {
int status_before = dlg.getStatus();
AmB2ABSession::onSipReply(rep);
int status = dlg.getStatus();
if ((status_before == AmSipDialog::Pending)&&
(status == AmSipDialog::Disconnected)) {
DBG("callee session creation failed. notifying callersession.\n");
relayEvent(new B2ABConnectOtherLegFailedEvent(rep.code, rep.reason));
} else if ((status == AmSipDialog::Pending) && (rep.code == 180)) {
relayEvent(new B2ABOtherLegRingingEvent());
}
}
// ----------------------- SessionAudioConnector -----------------
AmSessionAudioConnector::AmSessionAudioConnector(AmSession* sess, bool first) {
connectSession(sess, first);
}
AmSessionAudioConnector::~AmSessionAudioConnector() {
}
void AmSessionAudioConnector::connectSession(AmSession* sess, bool first) {
if (first) {
tag_sess1 = sess->getLocalTag();
sess->setInOut(&audio_connectors[0],&audio_connectors[1]);
} else {
tag_sess2 = sess->getLocalTag();
sess->setInOut(&audio_connectors[1],&audio_connectors[0]);
}
}
void AmSessionAudioConnector::disconnectSession(AmSession* sess) {
if (tag_sess1 == sess->getLocalTag()) {
tag_sess1.clear();
sess->setInOut(NULL, NULL);
if ((!tag_sess1.length())&& (!tag_sess2.length())) {
delete this; // both are disconnected -> destroy self
}
} else if (tag_sess2 == sess->getLocalTag()) {
tag_sess2.clear();
sess->setInOut(NULL, NULL);
if ((!tag_sess1.length())&& (!tag_sess2.length())){
delete this; // both are disconnected -> destroy self
}
} else {
ERROR("disconnecting from wrong AmSessionAudioConnector\n");
}
}
// ----------------------- AudioDelayBridge -----------------
/** BRIDGE_DELAY is needed because of possible different packet sizes */
#define BRIDGE_DELAY 320 // 30ms
/* AudioBridge */
AmAudioDelayBridge::AmAudioDelayBridge()
: AmAudio(new AmAudioSimpleFormat(CODEC_PCM16))
{
sarr.clear_all();
}
AmAudioDelayBridge::~AmAudioDelayBridge() {
}
int AmAudioDelayBridge::write(unsigned int user_ts, unsigned int size) {
// DBG("bridge write %u - this = %lu\n", user_ts + BRIDGE_DELAY, (unsigned long) this);
sarr.write(user_ts + BRIDGE_DELAY, (short*) ((unsigned char*) samples), size >> 1);
return size;
}
int AmAudioDelayBridge::read(unsigned int user_ts, unsigned int size) {
// DBG("bridge read %u - this = %lu\n", user_ts, (unsigned long) this);
sarr.read(user_ts, (short*) ((unsigned char*) samples), size >> 1);
return size;
}

@ -0,0 +1,272 @@
/*
* $Id: AmB2ABSession.h 145 2006-11-26 00:01:18Z sayer $
*
* Copyright (C) 2006-2007 iptego GmbH
*
* This file is part of sems, a free SIP media server.
*
* sems is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* For a license to use the sems software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* sems is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef B2ABSession_H
#define B2ABSession_H
#include "AmSession.h"
#include "AmSipDialog.h"
#include "AmAdvancedAudio.h"
#include "SampleArray.h"
#include <string>
using std::string;
class AmAudioDelayBridge;
class AmSessionAudioConnector;
enum { B2ABTerminateLeg,
B2ABConnectLeg,
B2ABConnectAudio,
B2ABConnectOtherLegFailed,
B2ABConnectOtherLegException,
B2ABOtherLegRinging
};
/** \brief base class for event in B2AB session */
struct B2ABEvent: public AmEvent
{
B2ABEvent(int ev_id)
: AmEvent(ev_id)
{}
};
struct B2ABOtherLegRingingEvent: public B2ABEvent
{
B2ABOtherLegRingingEvent()
: B2ABEvent(B2ABOtherLegRinging)
{}
};
/** \brief trigger connecting the audio in B2AB session */
struct B2ABConnectAudioEvent: public B2ABEvent
{
AmSessionAudioConnector* connector;
bool first;
B2ABConnectAudioEvent(AmSessionAudioConnector* connector,
bool first)
: B2ABEvent(B2ABConnectAudio),
connector(connector),
first(first)
{}
};
/** \brief trigger connecting the callee leg in B2AB session */
struct B2ABConnectLegEvent: public B2ABEvent
{
string remote_party;
string remote_uri;
string local_party;
string local_uri;
string callgroup;
B2ABConnectLegEvent(const string& remote_party,
const string& remote_uri,
const string& local_party,
const string& local_uri,
const string& callgroup)
: B2ABEvent(B2ABConnectLeg),
remote_party(remote_party),
remote_uri(remote_uri),
local_party(local_party),
local_uri(local_uri),
callgroup(callgroup)
{}
};
struct B2ABConnectOtherLegExceptionEvent: public B2ABEvent {
unsigned int code;
string reason;
B2ABConnectOtherLegExceptionEvent(unsigned int code,
const string& reason)
: B2ABEvent(B2ABConnectOtherLegException),
code(code), reason(reason)
{}
};
struct B2ABConnectOtherLegFailedEvent: public B2ABEvent {
unsigned int code;
string reason;
B2ABConnectOtherLegFailedEvent(unsigned int code,
const string& reason)
: B2ABEvent(B2ABConnectOtherLegFailed),
code(code), reason(reason)
{}
};
/**
* \brief Base class for Sessions in B2ABUA mode.
*
* It has two legs: Callee- and caller-leg.
*/
class AmB2ABSession: public AmSession
{
protected:
/** local tag of the other leg */
string other_id;
/** Requests received for relaying */
map<int,AmSipRequest> recvd_req;
void clear_other();
/** Relay one event to the other side. */
virtual void relayEvent(AmEvent* ev);
/** Terminate our leg and forget the other. */
virtual void terminateLeg();
/** Terminate the other leg and forget it.*/
virtual void terminateOtherLeg();
/** B2ABEvent handler */
virtual void onB2ABEvent(B2ABEvent* ev);
/* // Other leg received a BYE */
/* virtual void onOtherBye(const AmSipRequest& req); */
/* /\** INVITE from other leg has been replied *\/ */
/* virtual void onOtherReply(const AmSipReply& reply); */
/** @see AmEventQueue */
void process(AmEvent* event);
/** reference to the audio connector - caution: you may not delete this! */
AmSessionAudioConnector* connector;
/** set in&out to NULL and release connector */
void releaseAudio();
public:
AmB2ABSession();
AmB2ABSession(const string& other_local_tag);
~AmB2ABSession();
void onBye(const AmSipRequest& req);
};
class AmB2ABCalleeSession;
/** \brief Caller leg of a B2AB session */
class AmB2ABCallerSession: public AmB2ABSession
{
public:
enum CalleeStatus {
None=0,
NoReply,
Ringing,
Connected
};
private:
// Callee Status
CalleeStatus callee_status;
virtual void setupCalleeSession(AmB2ABCalleeSession* callee_session,
B2ABConnectLegEvent* ev);
protected:
virtual AmB2ABCalleeSession* createCalleeSession();
void relayEvent(AmEvent* ev);
public:
AmB2ABCallerSession();
~AmB2ABCallerSession();
CalleeStatus getCalleeStatus() { return callee_status; }
void connectCallee(const string& remote_party,
const string& remote_uri,
const string& local_party,
const string& local_uri);
void reconnectAudio();
// @see AmB2ABSession
void terminateOtherLeg();
protected:
void onB2ABEvent(B2ABEvent* ev);
};
/** \brief Callee leg of a B2AB session */
class AmB2ABCalleeSession: public AmB2ABSession
{
public:
AmB2ABCalleeSession(const string& other_local_tag);
~AmB2ABCalleeSession();
void onB2ABEvent(B2ABEvent* ev);
void onSessionStart(const AmSipReply& rep);
void onSipReply(const AmSipReply& rep);
void reconnectAudio();
};
/**
* \brief \ref AmAudio that connects input and output with delay
*
* AmAudioDelayBridge simply connects input and output
* with a fixed packet size delay.
*/
class AmAudioDelayBridge : public AmAudio {
SampleArrayShort sarr;
public:
AmAudioDelayBridge();
~AmAudioDelayBridge();
protected:
int write(unsigned int user_ts, unsigned int size);
int read(unsigned int user_ts, unsigned int size);
};
/**
* \brief connects the audio of two sessions together
*/
class AmSessionAudioConnector {
private:
AmAudioDelayBridge audio_connectors[2];
int refcnt;
~AmSessionAudioConnector();
string tag_sess1;
string tag_sess2;
public:
/** create a connector, connect audio to sess */
AmSessionAudioConnector(AmSession* sess, bool first);
/** connect audio to sess */
void connectSession(AmSession* sess, bool first);
/** disconnect session */
void disconnectSession(AmSession* sess);
};
#endif
Loading…
Cancel
Save