mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1574 lines
40 KiB
1574 lines
40 KiB
/*
|
|
* Copyright (C) 2002-2003 Fhg Fokus
|
|
*
|
|
* 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. This program is released under
|
|
* the GPL with the additional exemption that compiling, linking,
|
|
* and/or using OpenSSL is allowed.
|
|
*
|
|
* 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 "AmSession.h"
|
|
#include "AmSdp.h"
|
|
#include "AmConfig.h"
|
|
#include "AmUtils.h"
|
|
#include "AmPlugIn.h"
|
|
#include "AmApi.h"
|
|
#include "AmSessionContainer.h"
|
|
#include "AmSessionProcessor.h"
|
|
#include "AmMediaProcessor.h"
|
|
#include "AmDtmfDetector.h"
|
|
#include "AmPlayoutBuffer.h"
|
|
#include "AmAppTimer.h"
|
|
#include "sip/msg_logger.h"
|
|
#include "sip/pcap_logger.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
#include <sys/time.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
using std::make_shared;
|
|
|
|
#define GET_CALL_ID() (dlg->getCallid().c_str())
|
|
|
|
unsigned int AmSession::session_num = 0;
|
|
AmMutex AmSession::session_num_mut;
|
|
unsigned int AmSession::max_session_num = 0;
|
|
unsigned long long AmSession::avg_session_num = 0;
|
|
|
|
struct timeval get_now() {
|
|
struct timeval res;
|
|
gettimeofday(&res, NULL);
|
|
return res;
|
|
}
|
|
struct timeval avg_last_timestamp = get_now();
|
|
struct timeval avg_first_timestamp = avg_last_timestamp;
|
|
|
|
// AmSession methods
|
|
|
|
AmSession::AmSession(AmSipDialog* p_dlg)
|
|
: AmEventQueue(this), dlg(p_dlg),
|
|
input(NULL), output(NULL),
|
|
sess_stopped(false),
|
|
m_dtmfDetector(this), m_dtmfEventQueue(&m_dtmfDetector),
|
|
m_dtmfDetectionEnabled(true),
|
|
accept_early_session(false),
|
|
rtp_interface(-1),
|
|
refresh_method(REFRESH_UPDATE_FB_REINV),
|
|
processing_status(SESSION_PROCESSING_EVENTS),
|
|
active_rtp_stream_i(false),
|
|
active_media_index(-1),
|
|
rtp_keepalive_freq(AmConfig::RtpKeepaliveFreq),
|
|
rtp_timeout(AmConfig::DeadRtpTime),
|
|
logger(NULL),
|
|
log_sip(false), log_rtp(false)
|
|
#ifdef SESSION_THREADPOOL
|
|
, _pid(this)
|
|
#endif
|
|
{
|
|
if(!dlg) dlg = new AmSipDialog(this);
|
|
else dlg->setEventhandler(this);
|
|
|
|
/* update snapshot */
|
|
if (!getCallID().empty())
|
|
{
|
|
session_snapshot.call_id = getCallID();
|
|
}
|
|
if (!getLocalTag().empty())
|
|
{
|
|
session_snapshot.local_tag = getLocalTag();
|
|
}
|
|
if (!getRemoteTag().empty())
|
|
{
|
|
session_snapshot.remote_tag = getRemoteTag();
|
|
}
|
|
if (!getFirstBranch().empty())
|
|
{
|
|
session_snapshot.first_branch = getFirstBranch();
|
|
}
|
|
if (!app_params.empty())
|
|
{
|
|
session_snapshot.app_params = app_params;
|
|
}
|
|
if (!dlg->getRemoteParty().empty())
|
|
{
|
|
session_snapshot.remote_party = dlg->getRemoteParty();
|
|
}
|
|
if (!dlg->getLocalParty().empty())
|
|
{
|
|
session_snapshot.local_party = dlg->getLocalParty();
|
|
}
|
|
|
|
ILOG_DLG(L_DBG, "dlg = %p",dlg);
|
|
}
|
|
|
|
AmSession::~AmSession()
|
|
{
|
|
for(vector<AmSessionEventHandler*>::iterator evh = ev_handlers.begin();
|
|
evh != ev_handlers.end(); evh++) {
|
|
|
|
if((*evh)->destroy)
|
|
delete *evh;
|
|
}
|
|
|
|
ILOG_DLG(L_DBG, "AmSession destructor finished\n");
|
|
|
|
delete dlg;
|
|
|
|
for (vector<AmRtpAudio*>::iterator it=rtp_streams.begin(); it != rtp_streams.end(); it++)
|
|
{
|
|
delete *it;
|
|
}
|
|
|
|
for (RtpTransportIterator it=rtp_transports.begin(); it != rtp_transports.end(); it++)
|
|
{
|
|
if (NULL != *it)
|
|
delete *it;
|
|
}
|
|
}
|
|
|
|
AmSipDialog* AmSession::createSipDialog()
|
|
{
|
|
return new AmSipDialog(this);
|
|
}
|
|
|
|
void AmSession::createRTPStreams() {
|
|
ILOG_DLG(L_DBG, "creating RTP stream instance(s) for session [%p]\n", this);
|
|
rtp_streams.push_back(new AmRtpAudio(this, rtp_interface));
|
|
}
|
|
|
|
void AmSession::setActiveRtpStream(int transport) {
|
|
for (vector<AmRtpAudio*>::iterator it = rtp_streams.begin(); it != rtp_streams.end(); it++)
|
|
{
|
|
if (transport == TP_RTPAVP || transport == TP_RTPSAVP || transport == TP_RTPSAVPF) {
|
|
active_rtp_stream = it;
|
|
active_rtp_stream_i = true;
|
|
ILOG_DLG(L_DBG, "set active RTP stream for transport '%d' to index %zd\n", transport, it - rtp_streams.begin());
|
|
return;
|
|
}
|
|
}
|
|
ILOG_DLG(L_ERR, "could not find RTP stream for transport '%d', using first stream\n", transport);
|
|
active_rtp_stream = rtp_streams.begin();
|
|
active_rtp_stream_i = true;
|
|
}
|
|
|
|
AmRtpAudio* AmSession::RTPStream() {
|
|
if (!active_rtp_stream_i) {
|
|
if (!rtp_streams.size()) {
|
|
createRTPStreams();
|
|
}
|
|
active_rtp_stream = rtp_streams.begin();
|
|
active_rtp_stream_i = true;
|
|
ILOG_DLG(L_DBG, "set active rtp stream to begin of %zd streams\n", rtp_streams.size());
|
|
}
|
|
return *active_rtp_stream;
|
|
}
|
|
|
|
|
|
void AmSession::setCallgroup(const string& cg) {
|
|
callgroup = cg;
|
|
}
|
|
|
|
string AmSession::getCallgroup() {
|
|
return callgroup;
|
|
}
|
|
|
|
void AmSession::changeCallgroup(const string& cg) {
|
|
callgroup = cg;
|
|
AmMediaProcessor::instance()->changeCallgroup(this, cg);
|
|
}
|
|
|
|
void AmSession::startMediaProcessing()
|
|
{
|
|
if(getStopped() || isProcessingMedia())
|
|
return;
|
|
|
|
if(isAudioSet()) {
|
|
AmMediaProcessor::instance()->addSession(this, callgroup);
|
|
}
|
|
else {
|
|
ILOG_DLG(L_DBG, "no audio input and output set. "
|
|
"Session will not be attached to MediaProcessor.\n");
|
|
}
|
|
}
|
|
|
|
void AmSession::stopMediaProcessing()
|
|
{
|
|
if(!isProcessingMedia())
|
|
return;
|
|
|
|
AmMediaProcessor::instance()->removeSession(this);
|
|
}
|
|
|
|
void AmSession::addHandler(AmSessionEventHandler* sess_evh)
|
|
{
|
|
if (sess_evh != NULL)
|
|
ev_handlers.push_back(sess_evh);
|
|
}
|
|
|
|
void AmSession::setInput(AmAudio* in)
|
|
{
|
|
lockAudio();
|
|
input = in;
|
|
unlockAudio();
|
|
}
|
|
|
|
void AmSession::setOutput(AmAudio* out)
|
|
{
|
|
lockAudio();
|
|
output = out;
|
|
unlockAudio();
|
|
}
|
|
|
|
void AmSession::setInOut(AmAudio* in,AmAudio* out)
|
|
{
|
|
lockAudio();
|
|
input = in;
|
|
output = out;
|
|
unlockAudio();
|
|
}
|
|
|
|
bool AmSession::isAudioSet()
|
|
{
|
|
lockAudio();
|
|
bool set = input || output;
|
|
unlockAudio();
|
|
return set;
|
|
}
|
|
|
|
void AmSession::lockAudio()
|
|
{
|
|
audio_mut.lock();
|
|
}
|
|
|
|
void AmSession::unlockAudio()
|
|
{
|
|
audio_mut.unlock();
|
|
}
|
|
|
|
const string& AmSession::getCallID() const
|
|
{
|
|
return dlg->getCallid();
|
|
}
|
|
|
|
const string& AmSession::getRemoteTag() const
|
|
{
|
|
return dlg->getRemoteTag();
|
|
}
|
|
|
|
const string& AmSession::getLocalTag() const
|
|
{
|
|
return dlg->getLocalTag();
|
|
}
|
|
|
|
const string& AmSession::getFirstBranch() const
|
|
{
|
|
return dlg->get1stBranch();
|
|
}
|
|
|
|
void AmSession::setUri(const string& uri)
|
|
{
|
|
ILOG_DLG(L_DBG, "AmSession::setUri(%s)\n",uri.c_str());
|
|
/* TODO: sdp.uri = uri;*/
|
|
}
|
|
|
|
void AmSession::setLocalTag()
|
|
{
|
|
if (dlg->getLocalTag().empty()) {
|
|
string new_id = getNewId();
|
|
dlg->setLocalTag(new_id);
|
|
/* update snapshot */
|
|
lock_guard<AmMutex> lock(snapshot_lock);
|
|
session_snapshot.local_tag = new_id;
|
|
ILOG_DLG(L_DBG, "AmSession::setLocalTag() - session id set to %s\n", new_id.c_str());
|
|
}
|
|
}
|
|
|
|
void AmSession::setLocalTag(const string& tag)
|
|
{
|
|
ILOG_DLG(L_DBG, "AmSession::setLocalTag(%s)\n",tag.c_str());
|
|
dlg->setLocalTag(tag);
|
|
/* update snapshot */
|
|
lock_guard<AmMutex> lock(snapshot_lock);
|
|
session_snapshot.local_tag = tag;
|
|
}
|
|
|
|
/**
|
|
* Used by remote unrelated sessions to get a snapshot of this session data primitives.
|
|
*/
|
|
void AmSession::snapshot(AmSessionSnapshot& copy)
|
|
{
|
|
lock_guard<AmMutex> lock(snapshot_lock);
|
|
copy = session_snapshot;
|
|
}
|
|
|
|
const vector<SdpPayload*>& AmSession::getPayloads()
|
|
{
|
|
return m_payloads;
|
|
}
|
|
|
|
int AmSession::getRemoteRtpPort()
|
|
{
|
|
return RTPStream()->getRemoteRtpPort();
|
|
}
|
|
|
|
#ifdef SESSION_THREADPOOL
|
|
void AmSession::start() {
|
|
AmSessionProcessorThread* processor_thread =
|
|
AmSessionProcessor::getProcessorThread();
|
|
if (NULL == processor_thread)
|
|
throw string("no processing thread available");
|
|
|
|
// have the thread register and start us
|
|
processor_thread->startSession(this);
|
|
}
|
|
|
|
bool AmSession::is_stopped() {
|
|
return processing_status == SESSION_ENDED_DISCONNECTED;
|
|
}
|
|
#else
|
|
// in this case every session has its own thread
|
|
// - this is the main processing loop
|
|
void AmSession::run() {
|
|
ILOG_DLG(L_DBG, "startup session\n");
|
|
if (!startup())
|
|
return;
|
|
|
|
ILOG_DLG(L_DBG, "running session event loop\n");
|
|
while (true)
|
|
{
|
|
waitForEvent();
|
|
if (!processingCycle())
|
|
break;
|
|
}
|
|
|
|
ILOG_DLG(L_DBG, "session event loop ended, finalizing session\n");
|
|
finalize();
|
|
}
|
|
#endif
|
|
|
|
bool AmSession::startup() {
|
|
session_started();
|
|
|
|
try {
|
|
try {
|
|
onStart();
|
|
}
|
|
catch(const AmSession::Exception& e){ throw e; }
|
|
catch(const string& str){
|
|
ILOG_DLG(L_ERR, "%s\n",str.c_str());
|
|
throw AmSession::Exception(500,"unexpected exception.");
|
|
}
|
|
catch(...) {
|
|
throw AmSession::Exception(500,"unexpected exception.");
|
|
}
|
|
|
|
} catch(const AmSession::Exception& e) {
|
|
ILOG_DLG(L_ERR, "%i %s\n",e.code,e.reason.c_str());
|
|
onBeforeDestroy();
|
|
destroy();
|
|
|
|
session_stopped();
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AmSession::processEventsCatchExceptions() {
|
|
try {
|
|
try {
|
|
processEvents();
|
|
}
|
|
catch(const AmSession::Exception& e){ throw e; }
|
|
catch(const string& str){
|
|
ILOG_DLG(L_ERR, "%s\n",str.c_str());
|
|
throw AmSession::Exception(500,"unexpected exception.");
|
|
}
|
|
catch(...) {
|
|
throw AmSession::Exception(500,"unexpected exception.");
|
|
}
|
|
} catch(const AmSession::Exception& e){
|
|
ILOG_DLG(L_ERR, "%i %s\n",e.code,e.reason.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** one cycle of the event processing loop.
|
|
this should be called until it returns false. */
|
|
bool AmSession::processingCycle() {
|
|
|
|
ILOG_DLG(L_DBG, "vv S [%s|%s] %s, %s, %i UACTransPending, %i usages vv\n",
|
|
dlg->getCallid().c_str(),getLocalTag().c_str(),
|
|
dlg->getStatusStr(),
|
|
sess_stopped.get()?"stopped":"running",
|
|
dlg->getUACTransPending(),
|
|
dlg->getUsages());
|
|
|
|
switch (processing_status) {
|
|
case SESSION_PROCESSING_EVENTS:
|
|
{
|
|
if (!processEventsCatchExceptions()) {
|
|
// exception occured, stop processing
|
|
processing_status = SESSION_ENDED_DISCONNECTED;
|
|
return false;
|
|
}
|
|
|
|
AmSipDialog::Status dlg_status = dlg->getStatus();
|
|
bool s_stopped = sess_stopped.get();
|
|
|
|
ILOG_DLG(L_DBG, "^^ S [%s|%s] %s, %s, %i UACTransPending, %i usages ^^\n",
|
|
dlg->getCallid().c_str(),getLocalTag().c_str(),
|
|
AmBasicSipDialog::getStatusStr(dlg_status),
|
|
s_stopped?"stopped":"running",
|
|
dlg->getUACTransPending(),
|
|
dlg->getUsages());
|
|
|
|
// session running?
|
|
if (!s_stopped || (dlg_status == AmSipDialog::Disconnecting) || dlg->getUsages())
|
|
return true;
|
|
|
|
// session stopped?
|
|
if (s_stopped && (dlg_status == AmSipDialog::Disconnected)) {
|
|
processing_status = SESSION_ENDED_DISCONNECTED;
|
|
return false;
|
|
}
|
|
|
|
// wait for session's status to be disconnected
|
|
// todo: set some timer to tear down the session anyway,
|
|
// or react properly on negative reply to BYE (e.g. timeout)
|
|
processing_status = SESSION_WAITING_DISCONNECTED;
|
|
|
|
if ((dlg_status != AmSipDialog::Disconnected) && (dlg_status != AmSipDialog::Cancelling)) {
|
|
ILOG_DLG(L_DBG, "app did not send BYE - do that for the app\n");
|
|
if (dlg->bye() != 0) {
|
|
processing_status = SESSION_ENDED_DISCONNECTED;
|
|
// BYE sending failed - don't wait for dlg status to go disconnected
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case SESSION_WAITING_DISCONNECTED: {
|
|
// processing events until dialog status is Disconnected
|
|
|
|
if (!processEventsCatchExceptions()) {
|
|
processing_status = SESSION_ENDED_DISCONNECTED;
|
|
return false; // exception occured, stop processing
|
|
}
|
|
|
|
bool res = dlg->getStatus() != AmSipDialog::Disconnected;
|
|
if (!res)
|
|
processing_status = SESSION_ENDED_DISCONNECTED;
|
|
|
|
ILOG_DLG(L_DBG, "^^ S [%s|%s] %s, %s, %i UACTransPending, %i usages ^^\n",
|
|
dlg->getCallid().c_str(),getLocalTag().c_str(),
|
|
dlg->getStatusStr(),
|
|
sess_stopped.get()?"stopped":"running",
|
|
dlg->getUACTransPending(),
|
|
dlg->getUsages());
|
|
|
|
return res;
|
|
}; break;
|
|
|
|
default: {
|
|
ILOG_DLG(L_ERR, "unknown session processing state\n");
|
|
return false; // stop processing
|
|
}
|
|
}
|
|
}
|
|
|
|
void AmSession::finalize()
|
|
{
|
|
ILOG_DLG(L_DBG, "running finalize sequence...\n");
|
|
dlg->finalize();
|
|
|
|
onBeforeDestroy();
|
|
destroy();
|
|
|
|
session_stopped();
|
|
|
|
ILOG_DLG(L_DBG, "session is stopped.\n");
|
|
}
|
|
#ifndef SESSION_THREADPOOL
|
|
void AmSession::on_stop()
|
|
#else
|
|
void AmSession::stop()
|
|
#endif
|
|
{
|
|
ILOG_DLG(L_DBG, "AmSession::stop()\n");
|
|
|
|
if (!isDetached())
|
|
AmMediaProcessor::instance()->clearSession(this);
|
|
else
|
|
clearAudio();
|
|
}
|
|
|
|
void AmSession::setStopped(bool wakeup) {
|
|
/* make snapshot safe */
|
|
lock_guard<AmMutex> lock(snapshot_lock);
|
|
|
|
if (!sess_stopped.get()) {
|
|
sess_stopped.set(true);
|
|
onStop();
|
|
}
|
|
if (wakeup)
|
|
AmSessionContainer::instance()->postEvent(getLocalTag(), new AmEvent(0));
|
|
}
|
|
|
|
string AmSession::getAppParam(const string& param_name) const
|
|
{
|
|
map<string,string>::const_iterator param_it;
|
|
param_it = app_params.find(param_name);
|
|
if(param_it != app_params.end())
|
|
return param_it->second;
|
|
else
|
|
return "";
|
|
}
|
|
|
|
void AmSession::destroy() {
|
|
ILOG_DLG(L_DBG, "AmSession::destroy()\n");
|
|
|
|
/* make snapshot safe */
|
|
lock_guard<AmMutex> lock(snapshot_lock);
|
|
|
|
AmSessionContainer::instance()->destroySession(this);
|
|
}
|
|
|
|
string AmSession::getNewId() {
|
|
struct timeval t;
|
|
gettimeofday(&t,NULL);
|
|
|
|
string id = "";
|
|
|
|
id += int2hex(get_random()) + "-";
|
|
id += int2hex(t.tv_sec) + int2hex(t.tv_usec) + "-";
|
|
id += int2hex((unsigned int)((unsigned long)pthread_self()));
|
|
|
|
return id;
|
|
}
|
|
/* bookkeeping functions - TODO: move to monitoring */
|
|
static inline void __update_session_count(unsigned int num) {
|
|
int fd = open("/proc/ngcp/check", O_RDONLY);
|
|
if (fd >= 0) {
|
|
// set calls usage
|
|
ioctl(fd, TCXONC, (int) num);
|
|
close(fd);
|
|
}
|
|
}
|
|
void AmSession::session_started() {
|
|
struct timeval now, delta;
|
|
|
|
session_num_mut.lock();
|
|
//avg session number
|
|
gettimeofday(&now, NULL);
|
|
timersub(&now, &avg_last_timestamp, &delta);
|
|
avg_session_num = avg_session_num + (session_num * (delta.tv_sec * 1000000ULL + delta.tv_usec));
|
|
avg_last_timestamp = now;
|
|
|
|
//current session number
|
|
session_num = session_num + 1;
|
|
|
|
//maximum session number
|
|
if(session_num > max_session_num) max_session_num = session_num;
|
|
|
|
session_num_mut.unlock();
|
|
|
|
__update_session_count(getSessionNum());
|
|
}
|
|
|
|
void AmSession::session_stopped() {
|
|
struct timeval now, delta;
|
|
session_num_mut.lock();
|
|
//avg session number
|
|
gettimeofday(&now, NULL);
|
|
timersub(&now, &avg_last_timestamp, &delta);
|
|
avg_session_num = avg_session_num + (session_num * (delta.tv_sec * 1000000ULL + delta.tv_usec));
|
|
avg_last_timestamp = now;
|
|
//current session number
|
|
session_num = session_num - 1;
|
|
session_num_mut.unlock();
|
|
|
|
__update_session_count(getSessionNum());
|
|
}
|
|
|
|
unsigned int AmSession::getSessionNum() {
|
|
unsigned int res = 0;
|
|
session_num_mut.lock();
|
|
res = session_num;
|
|
session_num_mut.unlock();
|
|
return res;
|
|
}
|
|
|
|
unsigned int AmSession::getMaxSessionNum() {
|
|
unsigned int res = 0;
|
|
session_num_mut.lock();
|
|
res = max_session_num;
|
|
max_session_num = session_num;
|
|
session_num_mut.unlock();
|
|
return res;
|
|
}
|
|
|
|
unsigned int AmSession::getAvgSessionNum() {
|
|
unsigned int res = 0;
|
|
struct timeval now, delta;
|
|
session_num_mut.lock();
|
|
gettimeofday(&now, NULL);
|
|
timersub(&now, &avg_last_timestamp, &delta);
|
|
avg_session_num = avg_session_num + (session_num * (delta.tv_sec * 1000000ULL + delta.tv_usec));
|
|
timersub(&now, &avg_first_timestamp, &delta);
|
|
unsigned long long d_usec = delta.tv_sec * 1000000ULL + delta.tv_usec;
|
|
if (!d_usec) {
|
|
res = 0;
|
|
WARN("zero delta!\n");
|
|
} else {
|
|
//Round up
|
|
res = (unsigned int)((avg_session_num + d_usec - 1) / d_usec);
|
|
}
|
|
avg_session_num = 0;
|
|
avg_last_timestamp = now;
|
|
avg_first_timestamp = now;
|
|
session_num_mut.unlock();
|
|
return res;
|
|
}
|
|
|
|
void AmSession::setInbandDetector(Dtmf::InbandDetectorType t)
|
|
{
|
|
m_dtmfDetector.setInbandDetector(t, RTPStream()->getSampleRate());
|
|
}
|
|
|
|
void AmSession::postDtmfEvent(AmDtmfEvent *evt)
|
|
{
|
|
if (m_dtmfDetectionEnabled)
|
|
{
|
|
if (dynamic_cast<AmSipDtmfEvent *>(evt) || dynamic_cast<AmRtpDtmfEvent *>(evt)) {
|
|
// this is a raw event from sip info or rtp
|
|
m_dtmfEventQueue.postEvent(evt);
|
|
}
|
|
else {
|
|
// this is an aggregated event,
|
|
// post it into our event queue
|
|
postEvent(evt);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AmSession::processDtmfEvents()
|
|
{
|
|
if (m_dtmfDetectionEnabled)
|
|
{
|
|
m_dtmfEventQueue.processEvents();
|
|
}
|
|
}
|
|
|
|
void AmSession::putDtmfAudio(const unsigned char *buf, int size, unsigned long long system_ts)
|
|
{
|
|
m_dtmfEventQueue.putDtmfAudio(buf, size, system_ts);
|
|
}
|
|
|
|
void AmSession::sendDtmf(int event, unsigned int duration_ms) {
|
|
RTPStream()->sendDtmf(event, duration_ms);
|
|
}
|
|
|
|
|
|
void AmSession::onDtmf(int event, int duration_msec)
|
|
{
|
|
ILOG_DLG(L_DBG, "AmSession::onDtmf(%i,%i)\n",event,duration_msec);
|
|
}
|
|
|
|
void AmSession::clearAudio()
|
|
{
|
|
lockAudio();
|
|
|
|
if (input) {
|
|
input->close();
|
|
input = NULL;
|
|
}
|
|
if (output) {
|
|
output->close();
|
|
output = NULL;
|
|
}
|
|
|
|
unlockAudio();
|
|
ILOG_DLG(L_DBG, "Audio cleared !!!\n");
|
|
postEvent(new AmAudioEvent(AmAudioEvent::cleared));
|
|
}
|
|
|
|
void AmSession::process(AmEvent* ev)
|
|
{
|
|
CALL_EVENT_H(process,ev);
|
|
|
|
ILOG_DLG(L_DBG, "AmSession processing event\n");
|
|
|
|
if (ev->event_id == E_SYSTEM) {
|
|
AmSystemEvent* sys_ev = dynamic_cast<AmSystemEvent*>(ev);
|
|
if(sys_ev){
|
|
ILOG_DLG(L_DBG, "Session received system Event\n");
|
|
onSystemEvent(sys_ev);
|
|
return;
|
|
}
|
|
}
|
|
|
|
AmSipEvent* sip_ev = dynamic_cast<AmSipEvent*>(ev);
|
|
if(sip_ev){
|
|
(*sip_ev)(dlg);
|
|
return;
|
|
}
|
|
|
|
AmAudioEvent* audio_ev = dynamic_cast<AmAudioEvent*>(ev);
|
|
if(audio_ev){
|
|
onAudioEvent(audio_ev);
|
|
return;
|
|
}
|
|
|
|
AmDtmfEvent* dtmf_ev = dynamic_cast<AmDtmfEvent*>(ev);
|
|
if (dtmf_ev) {
|
|
ILOG_DLG(L_DBG, "Session received DTMF, event = %d, duration = %d\n",
|
|
dtmf_ev->event(), dtmf_ev->duration());
|
|
onDtmf(dtmf_ev->event(), dtmf_ev->duration());
|
|
return;
|
|
}
|
|
|
|
AmRtpTimeoutEvent* timeout_ev = dynamic_cast<AmRtpTimeoutEvent*>(ev);
|
|
if(timeout_ev){
|
|
onRtpTimeout();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void AmSession::onSipRequest(const AmSipRequest& req)
|
|
{
|
|
CALL_EVENT_H(onSipRequest,req);
|
|
|
|
ILOG_DLG(L_DBG, "onSipRequest: method = %s\n",req.method.c_str());
|
|
|
|
updateRefreshMethod(req.hdrs);
|
|
|
|
if(req.method == SIP_METH_INVITE){
|
|
|
|
try {
|
|
onInvite(req);
|
|
}
|
|
catch(const string& s) {
|
|
ILOG_DLG(L_ERR, "%s\n",s.c_str());
|
|
setStopped();
|
|
dlg->reply(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR);
|
|
}
|
|
catch(const AmSession::Exception& e) {
|
|
ILOG_DLG(L_ERR, "%i %s\n",e.code,e.reason.c_str());
|
|
setStopped();
|
|
dlg->reply(req, e.code, e.reason, NULL, e.hdrs);
|
|
}
|
|
}
|
|
else if(req.method == SIP_METH_ACK){
|
|
return;
|
|
}
|
|
else if( req.method == SIP_METH_BYE ){
|
|
dlg->reply(req,200,"OK");
|
|
onBye(req);
|
|
}
|
|
else if( req.method == SIP_METH_CANCEL ){
|
|
onCancel(req);
|
|
}
|
|
else if( req.method == SIP_METH_INFO ){
|
|
|
|
const AmMimeBody* dtmf_body =
|
|
req.body.hasContentType("application/dtmf-relay");
|
|
|
|
if (dtmf_body) {
|
|
string dtmf_body_str(dtmf_body->getPayload());
|
|
postDtmfEvent(new AmSipDtmfEvent(dtmf_body_str));
|
|
dlg->reply(req, 200, "OK");
|
|
} else {
|
|
dlg->reply(req, 415, "Unsupported Media Type");
|
|
}
|
|
} else if (req.method == SIP_METH_PRACK) {
|
|
// TODO: SDP
|
|
dlg->reply(req, 200, "OK");
|
|
// TODO: WARN: only include latest SDP if req.rseq == dlg->rseq (latest 1xx)
|
|
}
|
|
else {
|
|
dlg->reply(req, 501, "Not implemented");
|
|
}
|
|
}
|
|
|
|
void AmSession::onSipReply(const AmSipRequest& req, const AmSipReply& reply,
|
|
AmBasicSipDialog::Status old_dlg_status)
|
|
{
|
|
CALL_EVENT_H(onSipReply, req, reply, old_dlg_status);
|
|
|
|
updateRefreshMethod(reply.hdrs);
|
|
|
|
if (dlg->getStatus() < AmSipDialog::Connected &&
|
|
reply.code == 180) {
|
|
onRinging(reply);
|
|
}
|
|
|
|
if (old_dlg_status != dlg->getStatus()) {
|
|
ILOG_DLG(L_DBG, "Dialog status changed %s -> %s (stopped=%s) \n",
|
|
AmBasicSipDialog::getStatusStr(old_dlg_status),
|
|
dlg->getStatusStr(),
|
|
sess_stopped.get() ? "true" : "false");
|
|
} else {
|
|
ILOG_DLG(L_DBG, "Dialog status stays %s (stopped=%s)\n",
|
|
AmBasicSipDialog::getStatusStr(old_dlg_status),
|
|
sess_stopped.get() ? "true" : "false");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void AmSession::onInvite2xx(const AmSipReply& reply)
|
|
{
|
|
dlg->send_200_ack(reply.cseq);
|
|
}
|
|
|
|
void AmSession::onRemoteDisappeared(const AmSipReply&) {
|
|
// see 3261 - 12.2.1.2: should end dialog on 408/481
|
|
ILOG_DLG(L_DBG, "Remote end unreachable - ending session\n");
|
|
dlg->bye();
|
|
setStopped();
|
|
}
|
|
|
|
void AmSession::onNoAck(unsigned int cseq)
|
|
{
|
|
if (dlg->getStatus() == AmSipDialog::Connected)
|
|
dlg->bye();
|
|
|
|
setStopped();
|
|
}
|
|
|
|
void AmSession::onNoPrack(const AmSipRequest &req, const AmSipReply &rpl)
|
|
{
|
|
dlg->reply(req, 504, "Server Time-out");
|
|
// TODO: handle forking case (when more PRACKs are sent, out of which some
|
|
// might time-out/fail).
|
|
if (dlg->getStatus() < AmSipDialog::Connected)
|
|
setStopped();
|
|
}
|
|
|
|
void AmSession::onAudioEvent(AmAudioEvent* audio_ev)
|
|
{
|
|
if (audio_ev->event_id == AmAudioEvent::cleared)
|
|
setStopped();
|
|
}
|
|
|
|
void AmSession::onInvite(const AmSipRequest& req)
|
|
{
|
|
dlg->reply(req,200,"OK");
|
|
}
|
|
|
|
void AmSession::onBye(const AmSipRequest& req)
|
|
{
|
|
setStopped();
|
|
}
|
|
|
|
void AmSession::onCancel(const AmSipRequest& cancel)
|
|
{
|
|
dlg->bye();
|
|
setStopped();
|
|
}
|
|
|
|
void AmSession::onSystemEvent(AmSystemEvent* ev) {
|
|
if (ev->sys_event == AmSystemEvent::ServerShutdown) {
|
|
setStopped();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void AmSession::onSendRequest(AmSipRequest& req, int& flags)
|
|
{
|
|
CALL_EVENT_H(onSendRequest,req,flags);
|
|
}
|
|
|
|
void AmSession::onSendReply(const AmSipRequest& req, AmSipReply& reply, int& flags)
|
|
{
|
|
CALL_EVENT_H(onSendReply,req,reply,flags);
|
|
}
|
|
|
|
/** Hook called when an SDP offer is required */
|
|
bool AmSession::getSdpOffer(AmSdp& offer)
|
|
{
|
|
ILOG_DLG(L_DBG, "AmSession::getSdpOffer(...) ...\n");
|
|
|
|
offer.version = 0;
|
|
offer.origin.user = "sems";
|
|
//offer.origin.sessId = 1;
|
|
//offer.origin.sessV = 1;
|
|
offer.sessionName = "sems";
|
|
offer.conn.network = NT_IN;
|
|
offer.conn.addrType = AT_V4;
|
|
offer.conn.address = advertisedIP();
|
|
|
|
bool streams_initialized = RTPStream()->getSdpMediaIndex() >= 0;
|
|
if (streams_initialized) {
|
|
ILOG_DLG(L_DBG, "updating offer from %zd already initialized RTP streams\n", rtp_streams.size());
|
|
for (vector<AmRtpAudio*>::iterator it = rtp_streams.begin();
|
|
it != rtp_streams.end();
|
|
it++)
|
|
{
|
|
int media_idx = (*it)->getSdpMediaIndex();
|
|
if (media_idx < 0) { // shouldn't happen - all streams should be initialized
|
|
ILOG_DLG(L_ERR, "stream %zd in rtp_streams does not have media index\n", it-rtp_streams.begin());
|
|
continue;
|
|
}
|
|
if ((unsigned)abs(media_idx) >= offer.media.size())
|
|
offer.media.resize(media_idx+1);
|
|
|
|
(*it)->getSdpOffer(media_idx, offer.media[media_idx]);
|
|
}
|
|
} else {
|
|
// streams were just newly created
|
|
ILOG_DLG(L_DBG, "creating offer from %zd new RTP streams\n", rtp_streams.size());
|
|
offer.media.clear(); // for safety
|
|
for (vector<AmRtpAudio*>::iterator it = rtp_streams.begin();
|
|
it != rtp_streams.end();
|
|
it++)
|
|
{
|
|
offer.media.push_back(SdpMedia());
|
|
offer.media.back().type=MT_AUDIO;
|
|
(*it)->getSdpOffer(it - rtp_streams.begin(), offer.media.back());
|
|
}
|
|
}
|
|
|
|
// // TODO: support mutiple media types (needs multiples RTP streams)
|
|
// // TODO: support update instead of clearing everything
|
|
// if (RTPStream()->getSdpMediaIndex() < 0) {
|
|
// offer.media.clear();
|
|
// }
|
|
|
|
// unsigned int media_idx = 0;
|
|
// if(!offer.media.size()) {
|
|
// offer.media.push_back(SdpMedia());
|
|
// offer.media.back().type=MT_AUDIO;
|
|
// } else {
|
|
// media_idx = RTPStream()->getSdpMediaIndex();
|
|
// }
|
|
|
|
// RTPStream()->setLocalIP(localMediaIP());
|
|
// RTPStream()->getSdpOffer(media_idx,offer.media.back());
|
|
|
|
return true;
|
|
}
|
|
|
|
struct codec_priority_cmp
|
|
{
|
|
public:
|
|
codec_priority_cmp() {}
|
|
|
|
bool operator()(const SdpPayload& left, const SdpPayload& right)
|
|
{
|
|
for (vector<string>::iterator it = AmConfig::CodecOrder.begin();
|
|
it != AmConfig::CodecOrder.end();
|
|
it++)
|
|
{
|
|
if (strcasecmp(left.encoding_name.c_str(),it->c_str())==0 && strcasecmp(right.encoding_name.c_str(), it->c_str())!=0)
|
|
return true;
|
|
if (strcasecmp(right.encoding_name.c_str(),it->c_str())==0)
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/** reject media in @param answer, taking parameters from @offer */
|
|
void rejectMedia(const SdpMedia& offer, SdpMedia& answer) {
|
|
// reject media
|
|
answer.type = offer.type;
|
|
answer.port = 0;
|
|
answer.nports = 0;
|
|
answer.transport = offer.transport;
|
|
answer.send = answer.recv = false;
|
|
answer.payloads.clear();
|
|
if (!offer.payloads.empty()) {
|
|
SdpPayload dummy_pl = offer.payloads.front();
|
|
dummy_pl.encoding_name.clear();
|
|
dummy_pl.sdp_format_parameters.clear();
|
|
answer.payloads.push_back(dummy_pl);
|
|
}
|
|
answer.attributes.clear();
|
|
}
|
|
|
|
/** Hook called when an SDP answer is required */
|
|
bool AmSession::getSdpAnswer(const AmSdp& offer, AmSdp& answer)
|
|
{
|
|
ILOG_DLG(L_DBG, "AmSession::getSdpAnswer(...) ...\n");
|
|
|
|
answer.version = 0;
|
|
answer.origin.user = "sems";
|
|
//answer.origin.sessId = 1;
|
|
//answer.origin.sessV = 1;
|
|
answer.sessionName = "sems";
|
|
answer.conn.network = NT_IN;
|
|
if (offer.conn.address.empty()) {
|
|
answer.conn.addrType = AT_V4; // or use first stream connection?
|
|
} else {
|
|
answer.conn.addrType = offer.conn.addrType;
|
|
}
|
|
answer.conn.address = advertisedIP(answer.conn.addrType);
|
|
answer.attributes.clear();
|
|
answer.media.clear();
|
|
|
|
if (offer.hasIce())
|
|
answer.addAttribute(SdpAttribute(string("ice-lite")));
|
|
|
|
// todo: optimize: if only one RTP stream required, create only one
|
|
if (!hasRtpStream()) {
|
|
createRTPStreams();
|
|
}
|
|
|
|
// find stream with matching type (MT_AUDIO), transport and codecs
|
|
bool audio_1st_stream = true; // still looking for first audio stream?
|
|
// did we encounter an audio stream already that didn't match, payload-wise?
|
|
bool have_audio_stream = false;
|
|
for(vector<SdpMedia>::const_iterator m_it = offer.media.begin();
|
|
m_it != offer.media.end();
|
|
m_it++)
|
|
{
|
|
unsigned int media_index = m_it - offer.media.begin();
|
|
bool rtcp;
|
|
|
|
answer.media.push_back(SdpMedia());
|
|
SdpMedia& answer_media = answer.media.back();
|
|
|
|
if (audio_1st_stream // still looking for compatible stream?
|
|
&& m_it->type == MT_AUDIO
|
|
&& m_it->transport == TP_RTPAVP
|
|
&& !m_it->isRejected())
|
|
{
|
|
// accept media
|
|
|
|
setActiveRtpStream(m_it->transport);
|
|
|
|
string ip = localMediaIP(answer.conn.addrType);
|
|
|
|
AmRtpTransport* rtp_transport = createRtpTransport(RTPStream(), ip,
|
|
m_it->rtcp_mux,
|
|
offer.hasIce(),
|
|
false);
|
|
|
|
if (offer.hasIce()) {
|
|
rtp_transport->setRemoteStunCredentials(m_it->ice_username,
|
|
m_it->ice_password);
|
|
}
|
|
|
|
// Set the remote SSRC if present in the offer
|
|
if (m_it->ssrc)
|
|
RTPStream()->setRemoteSSRC(m_it->ssrc);
|
|
|
|
// Save the transport into the stream
|
|
RTPStream()->setRtpTransport(rtp_transport);
|
|
|
|
RTPStream()->getSdpAnswer(media_index, *m_it, answer_media);
|
|
|
|
if(!answer_media.haveNonTelevPayload()) {
|
|
// no compatible codec found with this stream
|
|
have_audio_stream = true;
|
|
rejectMedia(*m_it, answer_media);
|
|
continue;
|
|
}
|
|
|
|
// sort payload type in the answer according to the priority given in the codec_order configuration key
|
|
std::stable_sort(answer_media.payloads.begin(),answer_media.payloads.end(),codec_priority_cmp());
|
|
// we have one accepted audio stream, reject the others
|
|
audio_1st_stream = false;
|
|
} else {
|
|
// reject media
|
|
rejectMedia(*m_it, answer_media);
|
|
}
|
|
}
|
|
|
|
if (audio_1st_stream && have_audio_stream) {
|
|
// no compatible codec found
|
|
throw Exception(488,"no compatible payload");
|
|
}
|
|
|
|
if (audio_1st_stream) {
|
|
// no compatible media/transport found
|
|
throw Exception(488,"No compatible media type and transport");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int AmSession::onSdpCompleted(const AmSdp& local_sdp, const AmSdp& remote_sdp)
|
|
{
|
|
ILOG_DLG(L_DBG, "AmSession::onSdpCompleted(...) ...\n");
|
|
|
|
if(local_sdp.media.empty() || remote_sdp.media.empty()) {
|
|
string sdp_str_local; string sdp_str_remote;
|
|
local_sdp.print(sdp_str_local); remote_sdp.print(sdp_str_remote);
|
|
ILOG_DLG(L_ERR, "Invalid SDP - Local SDP:<%s> Remote SDP: <%s>\n",
|
|
sdp_str_local.empty() ? "<empty>" : sdp_str_local.c_str(),
|
|
sdp_str_remote.empty() ? "<empty>" : sdp_str_remote.c_str());
|
|
return -1;
|
|
}
|
|
|
|
// bool set_on_hold = false;
|
|
// if (!remote_sdp.media.empty()) {
|
|
// vector<SdpAttribute>::const_iterator pos =
|
|
// std::find(remote_sdp.media[0].attributes.begin(), remote_sdp.media[0].attributes.end(), SdpAttribute("sendonly"));
|
|
// set_on_hold = pos != remote_sdp.media[0].attributes.end();
|
|
// }
|
|
|
|
lockAudio();
|
|
|
|
// set active RTP stream to the one with proper transport
|
|
for (vector<SdpMedia>::const_iterator m_it = remote_sdp.media.begin();
|
|
m_it != remote_sdp.media.end();
|
|
m_it++)
|
|
{
|
|
if (m_it->type == MT_AUDIO
|
|
&& !m_it->isRejected()
|
|
&& m_it->transport == TP_RTPAVP
|
|
) {
|
|
// accept media
|
|
setActiveRtpStream(m_it->transport);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// TODO:
|
|
// - get the right media ID
|
|
// - check if the stream coresponding to the media ID
|
|
// should be created or updated
|
|
//
|
|
int ret = 0;
|
|
|
|
try {
|
|
ret = RTPStream()->init(local_sdp, remote_sdp, AmConfig::ForceSymmetricRtp);
|
|
} catch (const std::exception& e) {
|
|
ILOG_DLG(L_ERR, "Error while initializing RTP stream: '%s'\n", e.what());
|
|
ret = -1;
|
|
} catch (...) {
|
|
ILOG_DLG(L_ERR, "Error while initializing RTP stream (unknown exception in AmRTPStream::init)\n");
|
|
ret = -1;
|
|
}
|
|
unlockAudio();
|
|
|
|
if (!isProcessingMedia()) {
|
|
setInbandDetector(AmConfig::DefaultDTMFDetector);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void AmSession::onEarlySessionStart()
|
|
{
|
|
startMediaProcessing();
|
|
}
|
|
|
|
void AmSession::onSessionStart()
|
|
{
|
|
startMediaProcessing();
|
|
}
|
|
|
|
void AmSession::onRtpTimeout()
|
|
{
|
|
ILOG_DLG(L_DBG, "RTP timeout, stopping Session\n");
|
|
dlg->bye();
|
|
setStopped();
|
|
}
|
|
|
|
void AmSession::onSessionTimeout() {
|
|
ILOG_DLG(L_DBG, "Session Timer: Timeout, ending session.\n");
|
|
dlg->bye();
|
|
setStopped();
|
|
}
|
|
|
|
void AmSession::updateRefreshMethod(const string& headers) {
|
|
if (refresh_method == REFRESH_UPDATE_FB_REINV) {
|
|
if (key_in_list(getHeader(headers, SIP_HDR_ALLOW), SIP_METH_UPDATE)) {
|
|
ILOG_DLG(L_DBG, "remote allows UPDATE, using UPDATE for session refresh.\n");
|
|
refresh_method = REFRESH_UPDATE;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AmSession::refresh(int flags) {
|
|
// no session refresh if not connected
|
|
if (dlg->getStatus() != AmSipDialog::Connected)
|
|
return false;
|
|
|
|
if (refresh_method == REFRESH_UPDATE) {
|
|
ILOG_DLG(L_DBG, "Refreshing session with UPDATE\n");
|
|
return sendUpdate( NULL, "") == 0;
|
|
} else {
|
|
|
|
if (dlg->getUACInvTransPending()) {
|
|
ILOG_DLG(L_DBG, "INVITE transaction pending - not refreshing now\n");
|
|
return false;
|
|
}
|
|
|
|
ILOG_DLG(L_DBG, "Refreshing session with re-INVITE\n");
|
|
return sendReinvite(true, "", flags) == 0;
|
|
}
|
|
}
|
|
|
|
int AmSession::sendUpdate(const AmMimeBody* body,
|
|
const string &hdrs)
|
|
{
|
|
return dlg->update(body, hdrs);
|
|
}
|
|
|
|
void AmSession::onInvite1xxRel(const AmSipReply &reply)
|
|
{
|
|
// TODO: SDP
|
|
if (dlg->prack(reply, NULL, /*headers*/"") < 0)
|
|
ILOG_DLG(L_ERR, "failed to send PRACK request in session '%s'.\n",sid4dbg().c_str());
|
|
}
|
|
|
|
void AmSession::onPrack2xx(const AmSipReply &reply)
|
|
{
|
|
/* TODO: SDP */
|
|
}
|
|
|
|
string AmSession::sid4dbg()
|
|
{
|
|
string dbg;
|
|
dbg = dlg->getCallid() + "/" + dlg->getLocalTag() + "/"
|
|
+ dlg->getRemoteTag() + "/"
|
|
+ int2str(RTPStream()->getLocalRtpPort()) + "/"
|
|
+ RTPStream()->getRemoteAddress() + ":" + int2str(RTPStream()->getRemoteRtpPort());
|
|
return dbg;
|
|
}
|
|
|
|
int AmSession::sendReinvite(bool updateSDP, const string& headers, int flags)
|
|
{
|
|
if(updateSDP){
|
|
// Forces SDP offer/answer
|
|
AmMimeBody sdp;
|
|
sdp.addPart(SIP_APPLICATION_SDP);
|
|
return dlg->reinvite(headers, &sdp, flags);
|
|
}
|
|
else {
|
|
return dlg->reinvite(headers, NULL, flags);
|
|
}
|
|
}
|
|
|
|
int AmSession::sendInvite(const string& headers)
|
|
{
|
|
onOutgoingInvite(headers);
|
|
|
|
// Forces SDP offer/answer
|
|
AmMimeBody sdp;
|
|
sdp.addPart(SIP_APPLICATION_SDP);
|
|
return dlg->invite(headers, &sdp);
|
|
}
|
|
|
|
void AmSession::setOnHold(bool hold)
|
|
{
|
|
lockAudio();
|
|
bool old_hold = RTPStream()->getOnHold();
|
|
RTPStream()->setOnHold(hold);
|
|
if (hold != old_hold)
|
|
sendReinvite();
|
|
unlockAudio();
|
|
}
|
|
|
|
void AmSession::onFailure()
|
|
{
|
|
// switch (cause) {
|
|
// case FAIL_REL100_421:
|
|
// case FAIL_REL100_420:
|
|
// if (rpl) {
|
|
// dlg.cancel();
|
|
// if (dlg.getStatus() < AmSipDialog::Connected)
|
|
// setStopped();
|
|
// } else if (req) {
|
|
// if (cause == FAIL_REL100_421) {
|
|
// dlg.reply(*req, 421, SIP_REPLY_EXTENSION_REQUIRED, NULL,
|
|
// SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF);
|
|
// } else {
|
|
// dlg.reply(*req, 420, SIP_REPLY_BAD_EXTENSION, NULL,
|
|
// SIP_HDR_COLSP(SIP_HDR_UNSUPPORTED) SIP_EXT_100REL CRLF);
|
|
// }
|
|
// /* finally, stop session if running */
|
|
// if (dlg.getStatus() < AmSipDialog::Connected)
|
|
// setStopped();
|
|
// }
|
|
// break;
|
|
// default:
|
|
// break;
|
|
// }
|
|
}
|
|
|
|
|
|
int AmSession::getRtpInterface()
|
|
{
|
|
if(rtp_interface < 0){
|
|
// TODO: get default media interface for signaling IF instead
|
|
rtp_interface = AmConfig::SIP_Ifs[dlg->getOutboundIf()].RtpInterface;
|
|
if(rtp_interface < 0) {
|
|
ILOG_DLG(L_DBG, "No media interface for signaling interface: Using default media interface (0) instead.\n");
|
|
rtp_interface = 0;
|
|
}
|
|
}
|
|
return rtp_interface;
|
|
}
|
|
|
|
string AmSession::localMediaIP(int addrType)
|
|
{
|
|
// sets rtp_interface if not initialized
|
|
getRtpInterface();
|
|
|
|
assert(rtp_interface >= 0);
|
|
assert((unsigned int)rtp_interface < AmConfig::RTP_Ifs.size());
|
|
|
|
string set_ip = "";
|
|
for (size_t i = rtp_interface;
|
|
i < AmConfig::RTP_Ifs.size();
|
|
i++)
|
|
{
|
|
// TODO: AmConfig::RTP_Ifs[i].lock_shared();
|
|
set_ip = AmConfig::RTP_Ifs[i].LocalIP; // "media_ip" parameter.
|
|
// TODO: AmConfig::RTP_Ifs[i].unlock_shared();
|
|
if ((addrType == AT_NONE) ||
|
|
((addrType == AT_V4) && (set_ip.find(".") != std::string::npos)) ||
|
|
((addrType == AT_V6) && (set_ip.find(":") != std::string::npos)))
|
|
{
|
|
return set_ip;
|
|
}
|
|
}
|
|
return set_ip;
|
|
}
|
|
|
|
// Utility for basic NAT handling: allow the config file to specify the IP
|
|
// address to use in SDP bodies
|
|
string AmSession::advertisedIP(int addrType)
|
|
{
|
|
// sets rtp_interface if not initialized
|
|
getRtpInterface();
|
|
|
|
assert(rtp_interface >= 0);
|
|
assert((unsigned int)rtp_interface < AmConfig::RTP_Ifs.size());
|
|
|
|
string set_ip = "";
|
|
for (size_t i = rtp_interface; i < AmConfig::RTP_Ifs.size(); i++)
|
|
{
|
|
// TODO: AmConfig::RTP_Ifs[i].lock_shared();
|
|
set_ip = AmConfig::RTP_Ifs[i].getIP(); // "media_ip" parameter.
|
|
// TODO: AmConfig::RTP_Ifs[i].unlock_shared();
|
|
if ((addrType == AT_NONE) ||
|
|
((addrType == AT_V4) && (set_ip.find(".") != std::string::npos)) ||
|
|
((addrType == AT_V6) && (set_ip.find(":") != std::string::npos)))
|
|
{
|
|
return set_ip;
|
|
}
|
|
}
|
|
return set_ip;
|
|
}
|
|
|
|
bool AmSession::timersSupported() {
|
|
WARN("this function is deprecated; application timers are always supported\n");
|
|
return true;
|
|
}
|
|
|
|
bool AmSession::setTimer(int timer_id, double timeout) {
|
|
if (timeout <= 0.005) {
|
|
ILOG_DLG(L_DBG, "setting timer %d with immediate timeout - posting Event\n", timer_id);
|
|
AmTimeoutEvent* ev = new AmTimeoutEvent(timer_id);
|
|
postEvent(ev);
|
|
return true;
|
|
}
|
|
|
|
ILOG_DLG(L_DBG, "setting timer %d with timeout %f\n", timer_id, timeout);
|
|
AmAppTimer::instance()->setTimer(getLocalTag(), timer_id, timeout);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AmSession::removeTimer(int timer_id) {
|
|
|
|
ILOG_DLG(L_DBG, "removing timer %d\n", timer_id);
|
|
AmAppTimer::instance()->removeTimer(getLocalTag(), timer_id);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AmSession::removeTimers() {
|
|
|
|
ILOG_DLG(L_DBG, "removing timers\n");
|
|
AmAppTimer::instance()->removeTimers(getLocalTag());
|
|
|
|
return true;
|
|
}
|
|
|
|
int AmSession::readStreams(unsigned long long ts, unsigned char *buffer)
|
|
{
|
|
int res = 0;
|
|
lockAudio();
|
|
|
|
AmRtpAudio *stream = RTPStream();
|
|
unsigned int f_size = stream->getFrameSize();
|
|
if (stream->checkInterval(ts)) {
|
|
int got = stream->get(ts, buffer, stream->getSampleRate(), f_size);
|
|
|
|
if (got < 0)
|
|
res = -1;
|
|
|
|
if (got > 0) {
|
|
if (isDtmfDetectionEnabled())
|
|
putDtmfAudio(buffer, got, ts);
|
|
|
|
if (input)
|
|
res = input->put(ts, buffer, stream->getSampleRate(), got);
|
|
}
|
|
}
|
|
|
|
unlockAudio();
|
|
return res;
|
|
}
|
|
|
|
int AmSession::writeStreams(unsigned long long ts, unsigned char *buffer)
|
|
{
|
|
int res = 0;
|
|
lockAudio();
|
|
|
|
AmRtpAudio *stream = RTPStream();
|
|
if (stream->sendIntReached()) { // FIXME: shouldn't depend on checkInterval call before!
|
|
unsigned int f_size = stream->getFrameSize();
|
|
int got = 0;
|
|
if (output)
|
|
got = output->get(ts, buffer, stream->getSampleRate(), f_size);
|
|
if (got < 0)
|
|
res = -1;
|
|
if (got > 0)
|
|
res = stream->put(ts, buffer, stream->getSampleRate(), got);
|
|
}
|
|
|
|
unlockAudio();
|
|
return res;
|
|
|
|
}
|
|
|
|
/* \brief Check whether the given stream is already present */
|
|
bool AmSession::hasStream(AmRtpStream* stream)
|
|
{
|
|
for (RtpTransportIterator it=rtp_transports.begin();
|
|
it != rtp_transports.end();
|
|
it++)
|
|
{
|
|
if ((NULL != *it) && (*it)->hasStream(stream))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* \brief Get the transport holding the given rtp stream */
|
|
AmRtpTransport* AmSession::getRtpTransport(AmRtpStream* stream) {
|
|
for (RtpTransportIterator it=rtp_transports.begin(); it != rtp_transports.end(); it++)
|
|
{
|
|
if ((NULL != *it) && (*it) == stream->rtp_transport)
|
|
return *it;
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* \brief Process the given SDP Media Stream description
|
|
* Check if the corresponding rtp stream is new or is already present
|
|
* If present, reset it and check whether the RTP transport needs renegotiation
|
|
* If not present, get the corresponding transport, or create a new one if needed, and
|
|
* attach the new stream to it.
|
|
*/
|
|
AmRtpTransport* AmSession::createRtpTransport(AmRtpStream* stream, const string& ip,
|
|
bool rtcp_mux, bool ice, bool srtp)
|
|
{
|
|
/* If no transport exist for the stream
|
|
* Create one and attach the stream to it
|
|
*/
|
|
if (!hasStream(stream)) {
|
|
|
|
rtp_transports.push_back(new AmRtpTransport(rtp_interface, ip, !rtcp_mux,
|
|
ice, srtp));
|
|
|
|
stream->setRtpTransport(rtp_transports.back());
|
|
|
|
if (logger && log_rtp)
|
|
rtp_transports.back()->setLogger(logger);
|
|
|
|
return rtp_transports.back();
|
|
} else {
|
|
return getRtpTransport(stream);
|
|
}
|
|
}
|
|
|
|
bool AmSession::openLogger(const std::string &path)
|
|
{
|
|
auto log = make_shared<pcap_logger>();
|
|
|
|
if(log->open(path.c_str()) != 0) {
|
|
// open error
|
|
return false;
|
|
}
|
|
|
|
// opened successfully
|
|
setLogger(log);
|
|
return true;
|
|
}
|
|
|
|
void AmSession::setLogger(const shared_ptr<msg_logger>& _logger)
|
|
{
|
|
// Set logger
|
|
logger = _logger;
|
|
|
|
if (!logger) {
|
|
return;
|
|
}
|
|
|
|
if (log_sip)
|
|
dlg->setMsgLogger(logger);
|
|
else
|
|
dlg->setMsgLogger(NULL);
|
|
|
|
shared_ptr<msg_logger> rtp_logger;
|
|
if (log_rtp) rtp_logger = logger;
|
|
|
|
for (RtpTransportIterator it=rtp_transports.begin(); it != rtp_transports.end(); it++)
|
|
{
|
|
if (NULL != *it) {
|
|
(*it)->setLogger(rtp_logger);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AmSession::setLogger(const shared_ptr<msg_logger>& _logger, bool _log_sip, bool _log_rtp)
|
|
{
|
|
log_sip = _log_sip;
|
|
log_rtp = _log_rtp;
|
|
setLogger(_logger);
|
|
}
|