Project 'savoirfairelinux/ring-project' was moved to 'savoirfairelinux/jami-project'. Please update any links and bookmarks that may still have the old path.
Select Git revision
calladapter.cpp
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
iaxvoiplink.cpp 25.35 KiB
/*
* Copyright (C) 2006-2007 Savoir-Faire Linux inc.
* Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
* Author: Yan Morin <yan.morin@savoirfairelinux.com>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "iaxvoiplink.h"
#include "global.h" // for _debug
#include "iaxcall.h"
#include "eventthread.h"
#include "manager.h"
#include "audio/audiolayer.h"
#include <samplerate.h>
#include <iax/iax-client.h>
#include <math.h>
#include <dlfcn.h>
#define IAX_BLOCKING 1
#define IAX_NONBLOCKING 0
#define IAX_SUCCESS 0
#define IAX_FAILURE -1
#define RANDOM_IAX_PORT rand() % 64000 + 1024
// from IAXC : iaxclient.h
#define IAX__20S_8KHZ_MAX 320 //320 samples, IAX packets can have more than 20ms.
#define IAX__20S_48KHZ_MAX 1920 // 320*6 samples = 1920, 6 = 48000/8000
#define CHK_VALID_CALL if (call == NULL) { _debug("IAX: Call doesn't exists\n"); \
return false; }
IAXVoIPLink::IAXVoIPLink(const AccountID& accountID)
: VoIPLink(accountID)
{
_evThread = new EventThread(this);
_regSession = NULL;
_nextRefreshStamp = 0;
// to get random number for RANDOM_PORT
srand (time(NULL));
audiolayer = NULL;
_receiveDataDecoded = new int16[IAX__20S_48KHZ_MAX];
_sendDataEncoded = new unsigned char[IAX__20S_8KHZ_MAX];
// we estimate that the number of format after a conversion 8000->48000 is expanded to 6 times
_dataAudioLayer = new SFLDataFormat[IAX__20S_48KHZ_MAX];
_floatBuffer8000 = new float32[IAX__20S_8KHZ_MAX];
_floatBuffer48000 = new float32[IAX__20S_48KHZ_MAX];
_intBuffer8000 = new int16[IAX__20S_8KHZ_MAX];
// libsamplerate-related
_src_state_mic = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err);
_src_state_spkr = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err);
}
IAXVoIPLink::~IAXVoIPLink()
{
delete _evThread; _evThread = NULL;
_regSession = NULL; // shall not delete it
terminate();
audiolayer = NULL;
delete [] _intBuffer8000; _intBuffer8000 = NULL;
delete [] _floatBuffer48000; _floatBuffer48000 = NULL;
delete [] _floatBuffer8000; _floatBuffer8000 = NULL;
delete [] _dataAudioLayer; _dataAudioLayer = NULL;
delete [] _sendDataEncoded; _sendDataEncoded = NULL;
delete [] _receiveDataDecoded; _receiveDataDecoded = NULL;
// libsamplerate-related
_src_state_mic = src_delete(_src_state_mic);
_src_state_spkr = src_delete(_src_state_spkr);
}
bool
IAXVoIPLink::init()
{
// If it was done, don't do it again, until we call terminate()
if (_initDone)
return false;
bool returnValue = false;
//_localAddress = "127.0.0.1";
// port 0 is default
// iax_enable_debug(); have to enable debug when compiling iax...
int port = IAX_DEFAULT_PORTNO;
int last_port = 0;
int nbTry = 3;
while (port != IAX_FAILURE && nbTry) {
last_port = port;
port = iax_init(port);
if ( port < 0 ) {
_debug("IAX Warning: already initialize on port %d\n", last_port);
port = RANDOM_IAX_PORT;
} else if (port == IAX_FAILURE) {
_debug("IAX Fail to start on port %d", last_port);
port = RANDOM_IAX_PORT;
} else {
_debug("IAX Info: listening on port %d\n", last_port);
_localPort = last_port;
returnValue = true;
_evThread->start();
audiolayer = Manager::instance().getAudioDriver();
break;
}
nbTry--;
_initDone = true;
}
if (port == IAX_FAILURE || nbTry==0) {
_debug("Fail to initialize iax\n");
_initDone = false;
}
return returnValue;
}
void
IAXVoIPLink::terminate()
{
// If it was done, don't do it again, until we call init()
if (!_initDone)
return;
// iaxc_shutdown();
// Hangup all calls
terminateIAXCall();
_initDone = false;
}
void
IAXVoIPLink::terminateIAXCall()
{
ost::MutexLock m(_callMapMutex);
CallMap::iterator iter = _callMap.begin();
IAXCall *call;
while( iter != _callMap.end() ) {
call = dynamic_cast<IAXCall*>(iter->second);
if (call) {
_mutexIAX.enterMutex();
iax_hangup(call->getSession(), "Dumped Call");
_mutexIAX.leaveMutex();
call->setSession(NULL);
delete call; call = NULL;
}
iter++;
}
_callMap.clear();
delete _audiocodec;
}
void
IAXVoIPLink::getEvent()
{
IAXCall* call = NULL;
// lock iax_ stuff..
_mutexIAX.enterMutex();
iax_event* event = NULL;
while ( (event = iax_get_event(IAX_NONBLOCKING)) != NULL ) {
// If we received an 'ACK', libiax2 tells apps to ignore them.
if (event->etype == IAX_EVENT_NULL) {
continue;
}
//_debug ("Receive IAX Event: %d (0x%x)\n", event->etype, event->etype);
call = iaxFindCallBySession(event->session);
if (call) {
// We know that call, deal with it
iaxHandleCallEvent(event, call);
}
else if (event->session && event->session == _regSession) {
// This is a registration session, deal with it
iaxHandleRegReply(event);
}
else {
// We've got an event before it's associated with any call
iaxHandlePrecallEvent(event);
}
iax_event_free(event);
}
_mutexIAX.leaveMutex();
// Do the doodle-moodle to send audio from the microphone to the IAX channel.
sendAudioFromMic();
// Refresh registration.
if (_nextRefreshStamp && _nextRefreshStamp - 2 < time(NULL)) {
sendRegister();
}
// thread wait 3 millisecond
_evThread->sleep(3);
}
void
IAXVoIPLink::sendAudioFromMic(void)
{
IAXCall* currentCall = getIAXCall(Manager::instance().getCurrentCallId());
if (!currentCall) {
// Let's mind our own business.
return;
}
// Just make sure the currentCall is in state to receive audio right now.
//_debug("Here we get: connectionState: %d state: %d \n",
// currentCall->getConnectionState(),
// currentCall->getState());
if (currentCall->getConnectionState() != Call::Connected ||
currentCall->getState() != Call::Active) {
return;
}
_audiocodec = Manager::instance().getCodecDescriptorMap().getCodec( currentCall -> getAudioCodec() );
if (!_audiocodec) {
// Audio codec still not determined.
if (audiolayer) {
// To keep latency low..
//audiolayer->flushMic();
}
return;
}
// Send sound here
if (audiolayer) {
// we have to get 20ms of data from the mic *20/1000 = /50
// rate/50 shall be lower than IAX__20S_48KHZ_MAX
int maxBytesToGet = audiolayer->getSampleRate()/50*sizeof(SFLDataFormat);
// available bytes inside ringbuffer
int availBytesFromMic = audiolayer->canGetMic();
if (availBytesFromMic < maxBytesToGet) {
// We need packets full!
return;
}
// take the lowest
int bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet;
//_debug("available = %d, maxBytesToGet = %d\n", availBytesFromMic, maxBytesToGet);
// Get bytes from micRingBuffer to data_from_mic
int nbSample = audiolayer->getMic(_dataAudioLayer, bytesAvail) / sizeof(SFLDataFormat);
// Audio ici est PARFAIT
int16* toIAX = NULL;
//if (audiolayer->getSampleRate() != audiocodec->getClockRate() && nbSample) {
if (audiolayer->getSampleRate() != _audiocodec->getClockRate() && nbSample) {
SRC_DATA src_data;
#ifdef DATAFORMAT_IS_FLOAT
src_data.data_in = _dataAudioLayer;
#else
src_short_to_float_array(_dataAudioLayer, _floatBuffer48000, nbSample);
src_data.data_in = _floatBuffer48000;
#endif
// Audio parfait à ce point.
double factord = (double) _audiocodec->getClockRate() / audiolayer->getSampleRate();
src_data.src_ratio = factord;
src_data.input_frames = nbSample;
src_data.output_frames = (int) floor(factord * nbSample);
src_data.data_out = _floatBuffer8000;
src_data.end_of_input = 0;
src_process(_src_state_mic, &src_data);
nbSample = src_data.output_frames_gen;
// Bon, l'audio en float 8000 est laid mais yé consistant.
src_float_to_short_array (_floatBuffer8000, _intBuffer8000, nbSample);
toIAX = _intBuffer8000;
// Audio bon ici aussi..
} else {
#ifdef DATAFORMAT_IS_FLOAT
// convert _receiveDataDecoded to float inside _receiveData
src_float_to_short_array(_dataAudioLayer, _intBuffer8000, nbSample);
toIAX = _intBuffer8000;
//if (nbSample > IAX__20S_8KHZ_MAX) { _debug("Alert from mic, nbSample %d is bigger than expected %d\n", nbSample, IAX__20S_8KHZ_MAX); }
#else
toIAX = _dataAudioLayer; // int to int
#endif
}
// NOTE: L'audio ici est bon.
//
// LE PROBLÈME est dans cette snippet de fonction:
// C'est une fonction destructrice ! On n'en veut pas!
//if ( nbSample < (IAX__20S_8KHZ_MAX - 10) ) { // if only 10 is missing, it's ok
// fill end with 0...
//_debug("begin: %p, nbSample: %d\n", toIAX, nbSample);
//_debug("has to fill: %d chars at %p\n", (IAX__20S_8KHZ_MAX-nbSample)*sizeof(int16), toIAX + nbSample);
//memset(toIAX + nbSample, 0, (IAX__20S_8KHZ_MAX-nbSample)*sizeof(int16));
//nbSample = IAX__20S_8KHZ_MAX;
//}
//_debug("AR: Nb sample: %d int, [0]=%d [1]=%d [2]=%d\n", nbSample, toIAX[0], toIAX[1], toIAX[2]);
// NOTE: Le son dans toIAX (nbSamle*sizeof(int16)) est mauvais,
// s'il passe par le snippet précédent.
// DEBUG
//_fstream.write((char *) toIAX, nbSample*sizeof(int16));
//_fstream.flush();
// for the mono: range = 0 to IAX_FRAME2SEND * sizeof(int16)
int compSize = _audiocodec->codecEncode(_sendDataEncoded, toIAX, nbSample*sizeof(int16));
// Send it out!
_mutexIAX.enterMutex();
// Make sure the session and the call still exists.
if (currentCall->getSession()) {
if ( iax_send_voice(currentCall->getSession(), currentCall->getFormat(), (unsigned char*)_sendDataEncoded, compSize, nbSample) == -1) {
_debug("IAX: Error sending voice data.\n");
}
}
_mutexIAX.leaveMutex();
}
//unloadCodec(audiocodec);
}
IAXCall*
IAXVoIPLink::getIAXCall(const CallID& id)
{
Call* call = getCall(id);
if (call) {
return dynamic_cast<IAXCall*>(call);
}
return NULL;
}
bool
IAXVoIPLink::sendRegister()
{
bool result = false;
if (_host.empty()) {
Manager::instance().displayConfigError("Fill host field for IAX Account");
return false;
}
if (_user.empty()) {
Manager::instance().displayConfigError("Fill user field for IAX Account");
return false;
}
// lock
_mutexIAX.enterMutex();
// Always use a brand new session
if (_regSession) {
iax_destroy(_regSession);
}
_regSession = iax_session_new();
if (!_regSession) {
_debug("Error when generating new session for register");
} else {
// refresh
// last reg
char host[_host.length()+1];
strcpy(host, _host.c_str());
char user[_user.length()+1];
strcpy(user, _user.c_str());
char pass[_pass.length()+1];
strcpy(pass, _pass.c_str());
// iax_register doesn't use const char*
_debug("IAX Sending registration to %s with user %s\n", host, user);
int val = iax_register(_regSession, host, user, pass, 120);
_debug ("Return value: %d\n", val);
// set the time-out to 15 seconds, after that, resend a registration request.
// until we unregister.
_nextRefreshStamp = time(NULL) + 10;
result = true;
setRegistrationState(Trying);
}
// unlock
_mutexIAX.leaveMutex();
return result;
}
bool
IAXVoIPLink::sendUnregister()
{
_mutexIAX.enterMutex();
if (_regSession) {
/** @todo Should send a REGREL in sendUnregister()... */
//iax_send_regrel(); doesn't exist yet :)
iax_destroy(_regSession);
_regSession = NULL;
}
_mutexIAX.leaveMutex();
_nextRefreshStamp = 0;
setRegistrationState(Unregistered);
return false;
}
Call*
IAXVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl)
{
IAXCall* call = new IAXCall(id, Call::Outgoing);
call->setCodecMap(Manager::instance().getCodecDescriptorMap());
if (call) {
call->setPeerNumber(toUrl);
if ( iaxOutgoingInvite(call) ) {
call->setConnectionState(Call::Progressing);
call->setState(Call::Active);
addCall(call);
} else {
delete call; call = NULL;
}
}
return call;
}
bool
IAXVoIPLink::answer(const CallID& id)
{
IAXCall* call = getIAXCall(id);
CHK_VALID_CALL;
_mutexIAX.enterMutex();
iax_answer(call->getSession());
_mutexIAX.leaveMutex();
call->setState(Call::Active);
call->setConnectionState(Call::Connected);
// Start audio
audiolayer->startStream();
//audiolayer->flushMic();
return true;
}
bool
IAXVoIPLink::hangup(const CallID& id)
{
IAXCall* call = getIAXCall(id);
CHK_VALID_CALL;
_mutexIAX.enterMutex();
iax_hangup(call->getSession(), "Dumped Call");
_mutexIAX.leaveMutex();
call->setSession(NULL);
if (Manager::instance().isCurrentCall(id)) {
// stop audio
}
removeCall(id);
return true;
}
bool
IAXVoIPLink::onhold(const CallID& id)
{
IAXCall* call = getIAXCall(id);
CHK_VALID_CALL;
//if (call->getState() == Call::Hold) { _debug("Call is already on hold\n"); return false; }
_mutexIAX.enterMutex();
iax_quelch(call->getSession());
_mutexIAX.leaveMutex();
call->setState(Call::Hold);
return true;
}
bool
IAXVoIPLink::offhold(const CallID& id)
{
IAXCall* call = getIAXCall(id);
CHK_VALID_CALL;
//if (call->getState() == Call::Active) { _debug("Call is already active\n"); return false; }
_mutexIAX.enterMutex();
iax_unquelch(call->getSession());
_mutexIAX.leaveMutex();
call->setState(Call::Active);
return true;
}
bool
IAXVoIPLink::transfer(const CallID& id, const std::string& to)
{
IAXCall* call = getIAXCall(id);
CHK_VALID_CALL;
char callto[to.length()+1];
strcpy(callto, to.c_str());
_mutexIAX.enterMutex();
iax_transfer(call->getSession(), callto);
_mutexIAX.leaveMutex();
// should we remove it?
// removeCall(id);
}
bool
IAXVoIPLink::refuse(const CallID& id)
{
IAXCall* call = getIAXCall(id);
CHK_VALID_CALL;
_mutexIAX.enterMutex();
iax_reject(call->getSession(), "Call rejected manually.");
_mutexIAX.leaveMutex();
removeCall(id);
}
bool
IAXVoIPLink::carryingDTMFdigits(const CallID& id, char code)
{
IAXCall* call = getIAXCall(id);
CHK_VALID_CALL;
_mutexIAX.enterMutex();
iax_send_dtmf(call->getSession(), code);
_mutexIAX.leaveMutex();
}
bool
IAXVoIPLink::iaxOutgoingInvite(IAXCall* call)
{
struct iax_session *newsession;
ost::MutexLock m(_mutexIAX);
newsession = iax_session_new();
if (!newsession) {
_debug("IAX Error: Can't make new session for a new call\n");
return false;
}
call->setSession(newsession);
/* reset activity and ping "timers" */
// iaxc_note_activity(callNo);
std::string strNum = _user + ":" + _pass + "@" + _host + "/" + call->getPeerNumber();
char user[_user.length()+1];
strcpy(user, _user.c_str());
char num[strNum.length()+1];
strcpy(num, strNum.c_str());
char* lang = NULL;
int wait = 0;
/** @todo Make preference dynamic, and configurable */
int audio_format_preferred = call->getFirstMatchingFormat(call->getSupportedFormat());
int audio_format_capability = call->getSupportedFormat();
_debug("IAX New call: %s\n", num);
iax_call(newsession, user, user, num, lang, wait, audio_format_preferred, audio_format_capability);
return true;
}
IAXCall*
IAXVoIPLink::iaxFindCallBySession(struct iax_session* session)
{
// access to callMap shoud use that
// the code below is like findSIPCallWithCid()
ost::MutexLock m(_callMapMutex);
IAXCall* call = NULL;
CallMap::iterator iter = _callMap.begin();
while(iter != _callMap.end()) {
call = dynamic_cast<IAXCall*>(iter->second);
if (call && call->getSession() == session) {
return call;
}
iter++;
}
return NULL; // not found
}
void
IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call)
{
// call should not be 0
// note activity?
//
CallID id = call->getCallId();
int16* output = 0; // for audio output
switch(event->etype) {
case IAX_EVENT_HANGUP:
Manager::instance().peerHungupCall(id);
if (Manager::instance().isCurrentCall(id)) {
audiolayer->stopStream();
// stop audio
}
removeCall(id);
break;
case IAX_EVENT_REJECT:
//Manager::instance().peerHungupCall(id);
if (Manager::instance().isCurrentCall(id)) {
// stop audio
audiolayer->stopStream();
}
call->setConnectionState(Call::Connected);
call->setState(Call::Error);
Manager::instance().displayErrorText(id, "Failure");
Manager::instance().callFailure(id);
removeCall(id);
break;
case IAX_EVENT_ACCEPT:
// Call accepted over there by the computer, not the user yet.
if (event->ies.format) {
call->setFormat(event->ies.format);
}
break;
case IAX_EVENT_ANSWER:
if (call->getConnectionState() != Call::Connected){
call->setConnectionState(Call::Connected);
call->setState(Call::Active);
if (event->ies.format) {
// Should not get here, should have been set in EVENT_ACCEPT
call->setFormat(event->ies.format);
}
Manager::instance().peerAnsweredCall(id);
//audiolayer->flushMic();
audiolayer->startStream();
// start audio here?
} else {
// deja connecté ?
}
break;
case IAX_EVENT_BUSY:
call->setConnectionState(Call::Connected);
call->setState(Call::Busy);
Manager::instance().displayErrorText(id, "Busy");
Manager::instance().callBusy(id);
removeCall(id);
break;
case IAX_EVENT_VOICE:
iaxHandleVoiceEvent(event, call);
break;
case IAX_EVENT_TEXT:
break;
case IAX_EVENT_RINGA:
call->setConnectionState(Call::Ringing);
Manager::instance().peerRingingCall(call->getCallId());
break;
case IAX_EVENT_PONG:
break;
case IAX_EVENT_URL:
break;
// case IAX_EVENT_CNG: ??
// break;
case IAX_EVENT_TIMEOUT:
break;
case IAX_EVENT_TRANSFER:
break;
default:
_debug("Unknown event type (in call event): %d\n", event->etype);
}
}
/* Handle audio event, VOICE packet received */
void
IAXVoIPLink::iaxHandleVoiceEvent(iax_event* event, IAXCall* call)
{
// If we receive datalen == 0, some things of the jitter buffer in libiax2/iax.c
// were triggered
if (!event->datalen) {
// Skip this empty packet.
//_debug("IAX: Skipping empty jitter-buffer interpolated packet\n");
return;
}
if (audiolayer) {
_audiocodec = Manager::instance().getCodecDescriptorMap().getCodec( call -> getAudioCodec() );
// On-the-fly codec changing (normally, when we receive a full packet)
// as per http://tools.ietf.org/id/draft-guy-iax-03.txt
// - subclass holds the voiceformat property.
if (event->subclass && event->subclass != call->getFormat()) {
call->setFormat(event->subclass);
}
//audiocodec = loadCodec(call->getAudioCodec());
//_debug("Receive: len=%d, format=%d, _receiveDataDecoded=%p\n", event->datalen, call->getFormat(), _receiveDataDecoded);
unsigned char* data = (unsigned char*)event->data;
unsigned int size = event->datalen;
if (size > IAX__20S_8KHZ_MAX) {
_debug("The size %d is bigger than expected %d. Packet cropped. Ouch!\n", size, IAX__20S_8KHZ_MAX);
size = IAX__20S_8KHZ_MAX;
}
int expandedSize = _audiocodec->codecDecode(_receiveDataDecoded, data, size);
int nbInt16 = expandedSize/sizeof(int16);
if (nbInt16 > IAX__20S_8KHZ_MAX) {
_debug("We have decoded an IAX VOICE packet larger than expected: %s VS %s. Cropping.\n", nbInt16, IAX__20S_8KHZ_MAX);
nbInt16 = IAX__20S_8KHZ_MAX;
}
SFLDataFormat* toAudioLayer;
int nbSample = nbInt16;
int nbSampleMaxRate = nbInt16 * 6;
if ( audiolayer->getSampleRate() != _audiocodec->getClockRate() && nbSample ) {
// Do sample rate conversion
double factord = (double) audiolayer->getSampleRate() / _audiocodec->getClockRate();
// SRC_DATA from samplerate.h
SRC_DATA src_data;
src_data.data_in = _floatBuffer8000;
src_data.data_out = _floatBuffer48000;
src_data.input_frames = nbSample;
src_data.output_frames = (int) floor(factord * nbSample);
src_data.src_ratio = factord;
src_data.end_of_input = 0;
src_short_to_float_array(_receiveDataDecoded, _floatBuffer8000, nbSample);
// samplerate convert, go!
src_process(_src_state_spkr, &src_data);
nbSample = ( src_data.output_frames_gen > IAX__20S_48KHZ_MAX) ? IAX__20S_48KHZ_MAX : src_data.output_frames_gen;
#ifdef DATAFORMAT_IS_FLOAT
toAudioLayer = _floatBuffer48000;
#else
src_float_to_short_array(_floatBuffer48000, _dataAudioLayer, nbSample);
toAudioLayer = _dataAudioLayer;
#endif
} else {
nbSample = nbInt16;
#ifdef DATAFORMAT_IS_FLOAT
// convert _receiveDataDecoded to float inside _receiveData
src_short_to_float_array(_receiveDataDecoded, _floatBuffer8000, nbSample);
toAudioLayer = _floatBuffer8000;
#else
toAudioLayer = _receiveDataDecoded; // int to int
#endif
}
audiolayer->playSamples(toAudioLayer, nbSample * sizeof(SFLDataFormat), true);
} else {
_debug("IAX: incoming audio, but no sound card open");
}
//unloadCodec(audiocodec);
}
/**
* Handle the registration process
*/
void
IAXVoIPLink::iaxHandleRegReply(iax_event* event)
{
if (event->etype == IAX_EVENT_REGREJ) {
/* Authentication failed! */
_mutexIAX.enterMutex();
iax_destroy(_regSession);
_mutexIAX.leaveMutex();
_regSession = NULL;
setRegistrationState(Error, "Registration failed");
//Manager::instance().registrationFailed(getAccountID());
}
else if (event->etype == IAX_EVENT_REGACK) {
/* Authentication succeeded */
_mutexIAX.enterMutex();
iax_destroy(_regSession);
_mutexIAX.leaveMutex();
_regSession = NULL;
// I mean, save the timestamp, so that we re-register again in the REFRESH time.
// Defaults to 60, as per draft-guy-iax-03.
_nextRefreshStamp = time(NULL) + (event->ies.refresh ? event->ies.refresh : 60);
setRegistrationState(Registered);
//Manager::instance().registrationSucceed(getAccountID());
}
}
void
IAXVoIPLink::iaxHandlePrecallEvent(iax_event* event)
{
IAXCall* call = NULL;
CallID id;
switch(event->etype) {
case IAX_EVENT_REGACK:
case IAX_EVENT_REGREJ:
_debug("IAX Registration Event in a pre-call setup\n");
break;
case IAX_EVENT_REGREQ:
// Received when someone wants to register to us!?!
// Asterisk receives and answers to that, not us, we're a phone.
_debug("Registration by a peer, don't allow it\n");
break;
case IAX_EVENT_CONNECT:
// We've got an incoming call! Yikes!
_debug("> IAX_EVENT_CONNECT (receive)\n");
id = Manager::instance().getNewCallID();
call = new IAXCall(id, Call::Incoming);
if (!call) {
_debug("! IAX Failure: unable to create an incoming call");
return;
}
// Setup the new IAXCall
// Associate the call to the session.
call->setSession(event->session);
// setCallAudioLocal(call);
call->setCodecMap(Manager::instance().getCodecDescriptorMap());
call->setConnectionState(Call::Progressing);
if (event->ies.calling_number)
call->setPeerNumber(std::string(event->ies.calling_number));
if (event->ies.calling_name)
call->setPeerName(std::string(event->ies.calling_name));
if (Manager::instance().incomingCall(call, getAccountID())) {
/** @todo Faudra considérer éventuellement le champ CODEC PREFS pour
* l'établissement du codec de transmission */
// Remote lists its capabilities
int format = call->getFirstMatchingFormat(event->ies.capability);
// Remote asks for preferred codec voiceformat
int pref_format = call->getFirstMatchingFormat(event->ies.format);
// Priority to remote's suggestion. In case it's a forwarding, no transcoding
// will be needed from the server, thus less latency.
if (pref_format)
format = pref_format;
iax_accept(event->session, format);
iax_ring_announce(event->session);
addCall(call);
} else {
// reject call, unable to add it
iax_reject(event->session, "Error ringing user.");
delete call; call = NULL;
}
break;
case IAX_EVENT_HANGUP:
// Remote peer hung up
call = iaxFindCallBySession(event->session);
id = call->getCallId();
Manager::instance().peerHungupCall(id);
removeCall(id);
break;
case IAX_EVENT_TIMEOUT: // timeout for an unknown session
break;
default:
_debug("Unknown event type (in precall): %d\n", event->etype);
}
}
int
IAXVoIPLink::iaxCodecMapToFormat(IAXCall* call)
{
CodecOrder map = call->getCodecMap().getActiveCodecs();
printf("taytciatcia = %i\n", map.size());
return 0;
}