Commit fb081475 authored by Pierre-Luc Beaudoin's avatar Pierre-Luc Beaudoin
Browse files

Merge commit 'origin/master' into dbus

Conflicts:

	.gitignore
	src/Makefile.am
	src/managerimpl.cpp
parents 96868436 4be69b42
......@@ -19,7 +19,7 @@
*/
#include "eventthread.h"
#include "voIPLink.h"
#include "voiplink.h"
EventThread::EventThread (VoIPLink* link) : Thread ()
{
......
......@@ -17,12 +17,14 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "iaxaccount.h"
#include "account.h"
#include "iaxvoiplink.h"
#include "manager.h"
#define IAX_HOST "IAX.host"
#define IAX_USER "IAX.user"
#define IAX_PASS "IAX.pass"
#define IAX_FULL_NAME "IAX.fullName"
#define IAX_HOST "IAX.host"
#define IAX_USER "IAX.user"
#define IAX_PASS "IAX.pass"
IAXAccount::IAXAccount(const AccountID& accountID)
: Account(accountID)
......@@ -50,9 +52,10 @@ IAXAccount::registerAccount()
{
if (_link && !_registered) {
init();
unregisterAccount();
//unregisterAccount(); No need to unregister first.
IAXVoIPLink* tmplink = dynamic_cast<IAXVoIPLink*> (_link);
if (tmplink) {
// Stuff needed for IAX registration
tmplink->setHost(Manager::instance().getConfigString(_accountID,IAX_HOST));
tmplink->setUser(Manager::instance().getConfigString(_accountID,IAX_USER));
tmplink->setPass(Manager::instance().getConfigString(_accountID,IAX_PASS));
......@@ -99,12 +102,13 @@ IAXAccount::initConfig(Conf::ConfigTree& config)
std::string section(_accountID);
std::string type_str("string");
std::string type_int("int");
// Account generic
Account::initConfig(config);
// IAX specific
config.addConfigTreeItem(section, Conf::ConfigTreeItem(CONFIG_ACCOUNT_TYPE, "IAX", type_str));
config.addConfigTreeItem(section, Conf::ConfigTreeItem(CONFIG_ACCOUNT_ENABLE,"1", type_int));
config.addConfigTreeItem(section, Conf::ConfigTreeItem(CONFIG_ACCOUNT_AUTO_REGISTER, "1", type_int));
config.addConfigTreeItem(section, Conf::ConfigTreeItem(CONFIG_ACCOUNT_ALIAS, _("My account"), type_str));
config.addConfigTreeItem(section, Conf::ConfigTreeItem(IAX_FULL_NAME, "", type_str));
config.addConfigTreeItem(section, Conf::ConfigTreeItem(IAX_HOST, "", type_str));
config.addConfigTreeItem(section, Conf::ConfigTreeItem(IAX_USER, "", type_str));
config.addConfigTreeItem(section, Conf::ConfigTreeItem(IAX_PASS, "", type_str));
......@@ -113,6 +117,9 @@ IAXAccount::initConfig(Conf::ConfigTree& config)
void
IAXAccount::loadConfig()
{
_shouldInitOnStart = Manager::instance().getConfigInt(_accountID, CONFIG_ACCOUNT_ENABLE) ? true : false;
_shouldRegisterOnStart = Manager::instance().getConfigInt(_accountID, CONFIG_ACCOUNT_AUTO_REGISTER) ? true : false;
// Account generic
Account::loadConfig();
// IAX specific
//none
}
......@@ -20,13 +20,93 @@
#include "iaxcall.h"
#include "global.h" // for _debug
IAXCall::IAXCall(const CallID& id, Call::CallType type) : Call(id, type), _session(0)
IAXCall::IAXCall(const CallID& id, Call::CallType type) : Call(id, type), _session(NULL)
{
}
IAXCall::~IAXCall()
{
_session = 0; // just to be sure to don't have unknown pointer, do not delete it!
_session = NULL; // just to be sure to don't have unknown pointer, do not delete it!
}
void
IAXCall::setFormat(int format)
{
_format = format;
switch(format) {
case AST_FORMAT_ULAW:
setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_ULAW)); break;
case AST_FORMAT_GSM:
setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_GSM)); break;
case AST_FORMAT_ALAW:
setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_ALAW)); break;
case AST_FORMAT_ILBC:
setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_ILBC)); break;
case AST_FORMAT_SPEEX:
setAudioCodec(_codecMap.getCodec(PAYLOAD_CODEC_SPEEX)); break;
default:
setAudioCodec(NULL);
break;
}
}
int
IAXCall::getSupportedFormat()
{
CodecMap map = getCodecMap().getMap();
int format = 0;
CodecMap::iterator iter = map.begin();
while(iter != map.end()) {
switch(iter->first) {
case PAYLOAD_CODEC_ULAW:
format |= AST_FORMAT_ULAW; break;
case PAYLOAD_CODEC_GSM:
format |= AST_FORMAT_GSM; break;
case PAYLOAD_CODEC_ALAW:
format |= AST_FORMAT_ALAW; break;
case PAYLOAD_CODEC_ILBC:
format |= AST_FORMAT_ILBC; break;
case PAYLOAD_CODEC_SPEEX:
format |= AST_FORMAT_SPEEX; break;
default:
break;
}
iter++;
}
return format;
}
int
IAXCall::getFirstMatchingFormat(int needles)
{
CodecMap map = getCodecMap().getMap();
int format = 0;
CodecMap::iterator iter = map.begin();
while(iter != map.end()) {
switch(iter->first) {
case PAYLOAD_CODEC_ULAW:
format = AST_FORMAT_ULAW; break;
case PAYLOAD_CODEC_GSM:
format = AST_FORMAT_GSM; break;
case PAYLOAD_CODEC_ALAW:
format = AST_FORMAT_ALAW; break;
case PAYLOAD_CODEC_ILBC:
format = AST_FORMAT_ILBC; break;
case PAYLOAD_CODEC_SPEEX:
format = AST_FORMAT_SPEEX; break;
default:
break;
}
// Return the first that matches
if (format & needles)
return format;
iter++;
}
return 0;
}
......@@ -20,7 +20,8 @@
#define IAXCALL_H
#include "call.h"
#include <iax-client.h>
#include <iax/iax-client.h>
#include <iax/frame.h>
/**
* IAXCall are IAX implementation of a normal Call
......@@ -33,7 +34,7 @@ public:
~IAXCall();
/** Get the session pointer or 0 */
/** Get the session pointer or NULL */
struct iax_session* getSession() { return _session; }
/** Set the session pointer
......@@ -41,12 +42,51 @@ public:
*/
void setSession(struct iax_session* session) { _session = session; }
void setFormat(int format) { _format = format; }
/**
* Set format (one single bit
*
* This function sets the _audioCodec variable with the correct
* codec.
*/
void setFormat(int format);
/**
* Get format for the voice codec used
*
* Bitmask for codecs defined in iax/frame.h
*/
int getFormat() { return _format; }
/**
* Get the bitwise list of supported formats
*/
int getSupportedFormat();
/**
* Return a format (int) with the first matching codec selected.
*
* This considers the order of the appearance in the CodecMap,
* thus, the order of preference.
*
* NOTE: Everything returned is bound to the content of the local
* CodecMap, so it won't return format values that aren't valid
* in this call context.
*
* @param needles The format(s) (bitwise) you are looking for to match
* @return The matching format, thus 0 if none matches
*/
int getFirstMatchingFormat(int needles);
private:
// each call is associate to a session
/** Each call is associated with an iax_session */
struct iax_session* _session;
/**
* Format currently in use in the conversation,
* sent in each outgoing voice packet.
*/
int _format;
};
......
......@@ -26,6 +26,17 @@
#include "audio/audiolayer.h"
#include <samplerate.h>
#include <iax/iax-client.h>
#include <math.h>
/** @todo Remove the fstream and iostream stuff */
//#include <fstream> // fstream + iostream pour fstream debugging..
//#include <iostream> // removeable...
#define IAX_BLOCKING 1
#define IAX_NONBLOCKING 0
#define IAX_SUCCESS 0
#define IAX_FAILURE -1
......@@ -34,44 +45,26 @@
// from IAXC : iaxclient.h
/* payload formats : WARNING: must match libiax values!!! */
/* Data formats for capabilities and frames alike */
#define IAX__FORMAT_G723_1 (1 << 0) /* G.723.1 compression */
#define IAX__FORMAT_GSM (1 << 1) /* GSM compression */
#define IAX__FORMAT_ULAW (1 << 2) /* Raw mu-law data (G.711) */
#define IAX__FORMAT_ALAW (1 << 3) /* Raw A-law data (G.711) */
#define IAX__FORMAT_G726 (1 << 4) /* ADPCM, 32kbps */
#define IAX__FORMAT_ADPCM (1 << 5) /* ADPCM IMA */
#define IAX__FORMAT_SLINEAR (1 << 6) /* Raw 16-bit Signed Linear (8000 Hz) PCM */
#define IAX__FORMAT_LPC10 (1 << 7) /* LPC10, 180 samples/frame */
#define IAX__FORMAT_G729A (1 << 8) /* G.729a Audio */
#define IAX__FORMAT_SPEEX (1 << 9) /* Speex Audio */
#define IAX__FORMAT_ILBC (1 << 10) /* iLBC Audio */
#define IAX__FORMAT_MAX_AUDIO (1 << 15) /* Maximum audio format */
#define IAX__FORMAT_JPEG (1 << 16) /* JPEG Images */
#define IAX__FORMAT_PNG (1 << 17) /* PNG Images */
#define IAX__FORMAT_H261 (1 << 18) /* H.261 Video */
#define IAX__FORMAT_H263 (1 << 19) /* H.263 Video */
#define IAX__FORMAT_H263_PLUS (1 << 20) /* H.263+ Video */
#define IAX__FORMAT_MPEG4 (1 << 21) /* MPEG4 Video */
#define IAX__FORMAT_H264 (1 << 23) /* H264 Video */
#define IAX__FORMAT_THEORA (1 << 24) /* Theora Video */
#define IAX__20S_8KHZ_MAX 320 // 320 samples
#define IAX__20S_48KHZ_MAX 1920 // 320*6 samples, 6 = 48000/8000
#define IAX__20S_8KHZ_MAX 160 // 320 //320 samples
#define IAX__20S_48KHZ_MAX 960 // 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)
: VoIPLink(accountID)
// , _fstream("/tmp/audio.dat", std::ofstream::binary) /** @todo Remove this */
{
_evThread = new EventThread(this);
_regSession = 0;
_regSession = NULL;
_nextRefreshStamp = 0;
// to get random number for RANDOM_PORT
srand (time(NULL));
audiocodec = 0;
audiolayer = 0;
audiolayer = NULL;
_receiveDataDecoded = new int16[IAX__20S_48KHZ_MAX];
_sendDataEncoded = new unsigned char[IAX__20S_8KHZ_MAX];
......@@ -81,24 +74,32 @@ IAXVoIPLink::IAXVoIPLink(const AccountID& accountID)
_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 = 0;
_regSession = 0; // shall not delete it
delete _evThread; _evThread = NULL;
_regSession = NULL; // shall not delete it
terminate();
audiocodec = 0;
audiolayer = 0;
delete [] _intBuffer8000; _intBuffer8000 = 0;
delete [] _floatBuffer48000; _floatBuffer48000 = 0;
delete [] _floatBuffer8000; _floatBuffer8000 = 0;
delete [] _dataAudioLayer; _dataAudioLayer = 0;
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;
delete [] _sendDataEncoded; _sendDataEncoded = 0;
delete [] _receiveDataDecoded; _receiveDataDecoded = 0;
// libsamplerate-related
_src_state_mic = src_delete(_src_state_mic);
_src_state_spkr = src_delete(_src_state_spkr);
}
bool
......@@ -128,8 +129,6 @@ IAXVoIPLink::init()
_evThread->start();
// audio stuff, not dynamic yet
audiocodec = Manager::instance().getCodecDescriptorMap().getCodec((CodecType)0);
audiolayer = Manager::instance().getAudioDriver();
break;
}
......@@ -162,8 +161,8 @@ IAXVoIPLink::terminateIAXCall()
_mutexIAX.enterMutex();
iax_hangup(call->getSession(),"Dumped Call");
_mutexIAX.leaveMutex();
call->setSession(0);
delete call; call = 0;
call->setSession(NULL);
delete call; call = NULL;
}
iter++;
}
......@@ -173,118 +172,279 @@ IAXVoIPLink::terminateIAXCall()
void
IAXVoIPLink::getEvent()
{
// mutex here
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);
iax_event* event = 0;
IAXCall* call = 0;
while ( (event = iax_get_event(0)) != 0 ) {
//_debug ("Receive IAX Event: %d\n", event->etype);
call = iaxFindCallBySession(event->session);
if (call!=0) {
if (call) {
// We know that call, deal with it
iaxHandleCallEvent(event, call);
} else if (event->session != 0 && event->session == _regSession) {
// in iaxclient, there is many session handling, here, only one
}
else if (event->session && event->session == _regSession) {
// This is a registration session, deal with it
iaxHandleRegReply(event);
} else {
switch(event->etype) {
case IAX_EVENT_REGACK:
case IAX_EVENT_REGREJ:
_debug("Unknown IAX Registration Event\n");
break;
}
else {
// We've got an event before it's associated with any call
iaxHandlePrecallEvent(event);
}
iax_event_free(event);
}
_mutexIAX.leaveMutex();
case IAX_EVENT_REGREQ:
_debug("Registration by a peer, don't allow it\n");
break;
case IAX_EVENT_CONNECT: // new call
// New incoming call!
break;
case IAX_EVENT_TIMEOUT: // timeout for an unknown session
// Do the doodle-moodle to send audio from the microphone to the IAX channel.
sendAudioFromMic();
break;
default:
_debug("Unknown event type: %d\n", event->etype);
}
// Refresh registration.
if (_nextRefreshStamp && _nextRefreshStamp - 2 < time(NULL)) {
setRegister();
}
// thread wait 5 millisecond
_evThread->sleep(3);
}
void
IAXVoIPLink::sendAudioFromMic(void)
{
IAXCall* currentCall = getIAXCall(Manager::instance().getCurrentCallId());
AudioCodec* audiocodec = NULL;
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 = currentCall->getAudioCodec();
if (!audiocodec) {
// Audio codec still not determined.
if (audiolayer) {
// To keep latency low..
audiolayer->flushMic();
}
iax_event_free(event);
return;
}
// send sound here
if(_currentCall != 0 && audiolayer != 0) {
int samples = audiolayer->canGetMic();
if (samples != 0) {
int datalen = audiolayer->getMic(_sendDataEncoded, samples);
_debug("iax_send_voice(%p, %d, ,%d, %d)\n", _currentCall->getSession(), _currentCall->getFormat(), datalen, samples);
//if ( iax_send_voice(_currentCall->getSession(), _currentCall->getFormat(), (char*)_sendDataEncoded, datalen, samples) == -1) {
// // error sending voice
//}
// 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) {
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; /* More data to come */
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();
}
}
// unlock mutex here
_mutexIAX.leaveMutex();
//iaxRefreshRegistrations();
// thread wait 5 millisecond
_evThread->sleep(5);
/*
void IAXVoIPLink::recvAudioForSpkr(void)
{
}
*/
IAXCall*
IAXVoIPLink::getIAXCall(const CallID& id)
{
Call* call = getCall(id);
if (call) {
return dynamic_cast<IAXCall*>(call);
}
return NULL;
}
bool
IAXVoIPLink::setRegister()
{
bool result = false;
if (_regSession==0) {