iaxvoiplink.cpp 23.7 KB
Newer Older
yanmorin's avatar
 
yanmorin committed
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc.
3
 *  Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
yanmorin's avatar
 
yanmorin committed
4
 *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
5
 *
yanmorin's avatar
 
yanmorin committed
6
7
 *  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
8
 *  the Free Software Foundation; either version 3 of the License, or
yanmorin's avatar
 
yanmorin committed
9
 *  (at your option) any later version.
10
 *
yanmorin's avatar
 
yanmorin committed
11
12
13
14
 *  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.
15
 *
yanmorin's avatar
 
yanmorin committed
16
17
18
 *  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.
19
20
21
22
23
24
25
26
27
28
29
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
yanmorin's avatar
 
yanmorin committed
30
 */
yanmorin's avatar
   
yanmorin committed
31
#include "iaxvoiplink.h"
32
33
#include "iaxcall.h"
#include "eventthread.h"
34
#include "im/InstantMessaging.h"
Emmanuel Milou's avatar
Emmanuel Milou committed
35
#include "iaxaccount.h"
yanmorin's avatar
yanmorin committed
36
#include "manager.h"
37
#include "hooks/urlhook.h"
38
#include "audio/audiolayer.h"
39
#include "audio/samplerateconverter.h"
yanmorin's avatar
yanmorin committed
40

41
#include <cmath>
42
#include <dlfcn.h>
43
44
45

#define IAX_BLOCKING    1
#define IAX_NONBLOCKING 0
46

yanmorin's avatar
   
yanmorin committed
47
48
#define IAX_SUCCESS  0
#define IAX_FAILURE -1
yanmorin's avatar
 
yanmorin committed
49

50
51
#define RANDOM_IAX_PORT   rand() % 64000 + 1024

52
#define MUSIC_ONHOLD true
53

Julien Bonjean's avatar
Julien Bonjean committed
54
#define CHK_VALID_CALL   if (call == NULL) { _debug("IAX: Call doesn't exists"); \
55
	return false; }
56

57
58
59
60
61
62
namespace {
    const char * const HOOKS = "Hooks"; /** Hooks section */
    const char * const URLHOOK_IAX2_ENABLED = "Hooks.iax2_enabled";
    const char * const URLHOOK_COMMAND = "Hooks.url_command";
} // end anonymous namespace

63
64
65
66
67
68
69
70
71
IAXVoIPLink::IAXVoIPLink (const std::string& accountID) :
    evThread_(new EventThread(this))
    , regSession_(NULL)
    , nextRefreshStamp_(0)
    , audiolayer_(NULL)
    , converterSamplingRate_(0)
    , converter_(new SamplerateConverter (44100))
    , initDone_(false)
	, accountID_(accountID)
yanmorin's avatar
 
yanmorin committed
72
{
Emmanuel Milou's avatar
Emmanuel Milou committed
73
    // to get random number for RANDOM_PORT
74
    srand(time(NULL));
yanmorin's avatar
   
yanmorin committed
75
}
yanmorin's avatar
 
yanmorin committed
76
77


yanmorin's avatar
   
yanmorin committed
78
79
IAXVoIPLink::~IAXVoIPLink()
{
80
81
	delete converter_;
	delete evThread_;
82

83
    regSession_ = NULL; // shall not delete it
Emmanuel Milou's avatar
Emmanuel Milou committed
84
    terminate();
yanmorin's avatar
   
yanmorin committed
85
}
yanmorin's avatar
 
yanmorin committed
86

87
void
yanmorin's avatar
   
yanmorin committed
88
89
IAXVoIPLink::init()
{
Emmanuel Milou's avatar
Emmanuel Milou committed
90
    // If it was done, don't do it again, until we call terminate()
91
    if (initDone_)
92
        return;
93

Emmanuel Milou's avatar
Emmanuel Milou committed
94
    int port = IAX_DEFAULT_PORTNO;
95

Emmanuel Milou's avatar
Emmanuel Milou committed
96
    int last_port = 0;
97

Emmanuel Milou's avatar
Emmanuel Milou committed
98
    int nbTry = 3;
99

Emmanuel Milou's avatar
Emmanuel Milou committed
100
101
102
    while (port != IAX_FAILURE && nbTry) {
        last_port = port;
        port = iax_init (port);
103

Emmanuel Milou's avatar
Emmanuel Milou committed
104
        if (port < 0) {
Julien Bonjean's avatar
Julien Bonjean committed
105
            _debug ("IAX Warning: already initialize on port %d", last_port);
Emmanuel Milou's avatar
Emmanuel Milou committed
106
107
108
109
110
            port = RANDOM_IAX_PORT;
        } else if (port == IAX_FAILURE) {
            _debug ("IAX Fail to start on port %d", last_port);
            port = RANDOM_IAX_PORT;
        } else {
Julien Bonjean's avatar
Julien Bonjean committed
111
            _debug ("IAX Info: listening on port %d", last_port);
112
            evThread_->start();
113

114
            audiolayer_ = Manager::instance().getAudioDriver();
115

Emmanuel Milou's avatar
Emmanuel Milou committed
116
            // may be different than one already setted
117
            converterSamplingRate_ = Manager::instance().getMainBuffer()->getInternalSamplingRate();
118

Emmanuel Milou's avatar
Emmanuel Milou committed
119
120
            break;
        }
121

Emmanuel Milou's avatar
Emmanuel Milou committed
122
        nbTry--;
123

124
        initDone_ = true;
Emmanuel Milou's avatar
Emmanuel Milou committed
125
    }
126

127
    if (port == IAX_FAILURE or nbTry == 0) {
Julien Bonjean's avatar
Julien Bonjean committed
128
        _debug ("Fail to initialize iax");
129
        initDone_ = false;
Emmanuel Milou's avatar
Emmanuel Milou committed
130
    }
yanmorin's avatar
   
yanmorin committed
131
}
yanmorin's avatar
 
yanmorin committed
132

Emmanuel Milou's avatar
Emmanuel Milou committed
133
void
yanmorin's avatar
   
yanmorin committed
134
135
IAXVoIPLink::terminate()
{
Emmanuel Milou's avatar
Emmanuel Milou committed
136
    // If it was done, don't do it again, until we call init()
137
    if (!initDone_)
Emmanuel Milou's avatar
Emmanuel Milou committed
138
        return;
139

Emmanuel Milou's avatar
Emmanuel Milou committed
140
141
    // Hangup all calls
    terminateIAXCall();
142

143
    initDone_ = false;
yanmorin's avatar
   
yanmorin committed
144
}
145

Emmanuel Milou's avatar
Emmanuel Milou committed
146
void
147
148
IAXVoIPLink::terminateIAXCall()
{
149
150
    ost::MutexLock m(_callMapMutex);
    for (CallMap::iterator iter = _callMap.begin(); iter != _callMap.end(); ++iter) {
Rafaël Carré's avatar
Rafaël Carré committed
151
        IAXCall *call = dynamic_cast<IAXCall*> (iter->second);
Emmanuel Milou's avatar
Emmanuel Milou committed
152
        if (call) {
153
154
155
156
            {
                ost::MutexLock m(mutexIAX_);
                iax_hangup (call->getSession(), (char*) "Dumped Call");
            }
Emmanuel Milou's avatar
Emmanuel Milou committed
157
158
159
160
161
162
            call->setSession (NULL);
            delete call;
        }
    }

    _callMap.clear();
163
164
}

Emmanuel Milou's avatar
Emmanuel Milou committed
165
void
166
IAXVoIPLink::getEvent()
167
{
168
169
    {
        ost::MutexLock m(mutexIAX_);
170
        iax_event *event;
171
        while ((event = iax_get_event (IAX_NONBLOCKING)) != NULL) {
172
        	// If we received an 'ACK', libiax2 tells apps to ignore them.
173
174
            if (event->etype == IAX_EVENT_NULL)
                continue;
175

176
            IAXCall *call = iaxFindCallBySession (event->session);
177

178
179
            if (call)
                iaxHandleCallEvent (event, call);
180
            else if (event->session && event->session == regSession_) // This is a registration session, deal with it
181
                iaxHandleRegReply (event);
182
            else // We've got an event before it's associated with any call
183
                iaxHandlePrecallEvent (event);
184

185
            iax_event_free (event);
Emmanuel Milou's avatar
Emmanuel Milou committed
186
        }
187
        free(event);
Emmanuel Milou's avatar
Emmanuel Milou committed
188
    }
189

Emmanuel Milou's avatar
Emmanuel Milou committed
190
    sendAudioFromMic();
191

Emmanuel Milou's avatar
Emmanuel Milou committed
192
    // thread wait 3 millisecond
193
    evThread_->sleep(3);
194
195
}

Emmanuel Milou's avatar
Emmanuel Milou committed
196
void
197
IAXVoIPLink::sendAudioFromMic (void)
198
{
199
200
201
    // We have to update the audio layer type in case we switched
    // TODO Find out a better way to do it
    updateAudiolayer();
202

203
204
205
    for (CallMap::const_iterator iter = _callMap.begin(); iter != _callMap.end() ; ++iter) {
        IAXCall *currentCall = dynamic_cast<IAXCall*>(iter->second);
        if (!currentCall or currentCall->getState() != Call::Active)
Rafaël Carré's avatar
Rafaël Carré committed
206
			continue;
207

208
209
		int codecType = currentCall->getAudioCodec();
	    sfl::AudioCodec *audioCodec = static_cast<sfl::AudioCodec *>(Manager::instance().audioCodecFactory.getCodec(codecType));
210

211
		if (!audioCodec or !audiolayer_)
Rafaël Carré's avatar
Rafaël Carré committed
212
			continue;
213

Rafaël Carré's avatar
Rafaël Carré committed
214
		Manager::instance().getMainBuffer()->setInternalSamplingRate (audioCodec->getClockRate());
215

216
		unsigned int mainBufferSampleRate = audiolayer_->getMainBuffer()->getInternalSamplingRate();
217

Rafaël Carré's avatar
Rafaël Carré committed
218
219
		// we have to get 20ms of data from the mic *20/1000 = /50
		// rate/50 shall be lower than IAX__20S_48KHZ_MAX
220
		int bytesNeeded = mainBufferSampleRate * 20 / 1000 * sizeof (SFLDataFormat);
221
		if (audiolayer_->getMainBuffer()->availForGet (currentCall->getCallId()) < bytesNeeded)
Rafaël Carré's avatar
Rafaël Carré committed
222
			continue;
223

Rafaël Carré's avatar
Rafaël Carré committed
224
		// Get bytes from micRingBuffer to data_from_mic
225
        int bytes = audiolayer_->getMainBuffer()->getData (decData, bytesNeeded, currentCall->getCallId());
226
        int samples = bytes / sizeof(SFLDataFormat);
227

Rafaël Carré's avatar
Rafaël Carré committed
228
		int compSize;
229
230
		unsigned int audioRate = audioCodec->getClockRate();
		int outSamples;
231
		SFLDataFormat *in;
232
		if (audioRate != mainBufferSampleRate) {
233
			converter_->resample (decData, resampledData, audioRate, mainBufferSampleRate, samples);
234
			in = resampledData;
235
            outSamples = 0;
Rafaël Carré's avatar
Rafaël Carré committed
236
		} else {
237
			outSamples = samples;
238
			in = decData;
Rafaël Carré's avatar
Rafaël Carré committed
239
		}
240
		compSize = audioCodec->encode (encodedData, in, DEC_BUFFER_SIZE);
241

Rafaël Carré's avatar
Rafaël Carré committed
242
		// Send it out!
243
244
        {
            ost::MutexLock m(mutexIAX_);
245

246
247
248
249
250
251
            // Make sure the session and the call still exists.
            if (currentCall->getSession() and (bytes > 0)) {
                if (iax_send_voice (currentCall->getSession(), currentCall->getFormat(), encodedData, compSize, outSamples) == -1)
                    _error ("IAX: Error sending voice data.");
            }
        }
252
    }
253
254
}

255

Emmanuel Milou's avatar
Emmanuel Milou committed
256
IAXCall*
257
IAXVoIPLink::getIAXCall (const std::string& id)
258
{
259
    return dynamic_cast<IAXCall*>(getCall(id));
260
261
}

262
void
263
IAXVoIPLink::sendRegister (Account *a)
264
{
265
    IAXAccount *account = dynamic_cast<IAXAccount*>(a);
266

267
    if (account->getHostname().empty())
268
    	throw VoipLinkException("Account hostname is empty");
yanmorin's avatar
yanmorin committed
269

270
    if (account->getUsername().empty())
271
    	throw VoipLinkException("Account username is empty");
272

273
    ost::MutexLock m(mutexIAX_);
274

275
276
    if (regSession_)
        iax_destroy(regSession_);
277

278
    regSession_ = iax_session_new();
Emmanuel Milou's avatar
Emmanuel Milou committed
279

280
281
    if (regSession_) {
        iax_register (regSession_, account->getHostname().data(), account->getUsername().data(), account->getPassword().data(), 120);
282
        nextRefreshStamp_ = time (NULL) + 10;
283
        account->setRegistrationState(Trying);
Emmanuel Milou's avatar
Emmanuel Milou committed
284
    }
285
286
}

287
void
288
IAXVoIPLink::sendUnregister (Account *a)
289
{
290
291
292
293
294
	ost::MutexLock m(mutexIAX_);
	if (regSession_) {
		iax_destroy (regSession_);
		regSession_ = NULL;
	}
295

296
    nextRefreshStamp_ = 0;
297

298
    IAXAccount *account = dynamic_cast<IAXAccount*>(a);
Emmanuel Milou's avatar
Emmanuel Milou committed
299
    account->setRegistrationState (Unregistered);
300
301
}

Emmanuel Milou's avatar
Emmanuel Milou committed
302
Call*
303
IAXVoIPLink::newOutgoingCall (const std::string& id, const std::string& toUrl)
304
{
Emmanuel Milou's avatar
Emmanuel Milou committed
305
306
    IAXCall* call = new IAXCall (id, Call::Outgoing);

307
308
    call->setPeerNumber (toUrl);
    call->initRecFileName (toUrl);
Emmanuel Milou's avatar
Emmanuel Milou committed
309

310
311
312
313
314
315
316
    if (iaxOutgoingInvite (call)) {
        call->setConnectionState (Call::Progressing);
        call->setState (Call::Active);
        addCall (call);
    } else {
        delete call;
        call = NULL;
Emmanuel Milou's avatar
Emmanuel Milou committed
317
318
319
    }

    return call;
320
321
}

322

323
void
324
IAXVoIPLink::answer (Call *c)
325
{
326
    IAXCall* call = dynamic_cast<IAXCall*>(c);
327

Emmanuel Milou's avatar
Emmanuel Milou committed
328
    Manager::instance().addStream (call->getCallId());
329

330
331
332
333
    {
        ost::MutexLock m(mutexIAX_);
        iax_answer(call->getSession());
    }
334

335
336
    call->setState(Call::Active);
    call->setConnectionState(Call::Connected);
337

Emmanuel Milou's avatar
Emmanuel Milou committed
338
    // Flush main buffer
339
    audiolayer_->flushMain();
340
341
}

342
void
343
IAXVoIPLink::hangup (const std::string& id)
yanmorin's avatar
yanmorin committed
344
{
345
    _debug ("IAXVoIPLink: Hangup");
346

347
    IAXCall* call = getIAXCall(id);
348
    if (call == NULL)
349
350
    	throw VoipLinkException("Could not find call");

351
    Manager::instance().getMainBuffer()->unBindAll(call->getCallId());
352

353
    mutexIAX_.enterMutex();
354
    iax_hangup (call->getSession(), (char*) "Dumped Call");
355
    mutexIAX_.leaveMutex();
356

357
    call->setSession(NULL);
358

359
    removeCall(id);
360
361
362
}


363
void
364
IAXVoIPLink::peerHungup (const std::string& id)
365
{
366
    _debug ("IAXVoIPLink: Peer hung up");
367

Emmanuel Milou's avatar
Emmanuel Milou committed
368
    IAXCall* call = getIAXCall (id);
369
    if (call == NULL)
370
371
    	throw VoipLinkException("Could not find call");

372
    Manager::instance().getMainBuffer()->unBindAll (call->getCallId());
373

Emmanuel Milou's avatar
Emmanuel Milou committed
374
    call->setSession (NULL);
375

Emmanuel Milou's avatar
Emmanuel Milou committed
376
    removeCall (id);
yanmorin's avatar
yanmorin committed
377
}
378

379
380


381
void
382
IAXVoIPLink::onhold (const std::string& id)
383
{
Emmanuel Milou's avatar
Emmanuel Milou committed
384
    IAXCall* call = getIAXCall (id);
385
    if (call == NULL)
386
    	throw VoipLinkException("Call does not exist");
387

388
    Manager::instance().getMainBuffer()->unBindAll (call->getCallId());
389

390
    mutexIAX_.enterMutex();
391
    iax_quelch_moh (call->getSession(), MUSIC_ONHOLD);
392
    mutexIAX_.leaveMutex();
393

Emmanuel Milou's avatar
Emmanuel Milou committed
394
    call->setState (Call::Hold);
395
396
}

397
void
398
IAXVoIPLink::offhold (const std::string& id)
399
{
Emmanuel Milou's avatar
Emmanuel Milou committed
400
    IAXCall* call = getIAXCall (id);
401
402
    if (call == NULL)
    	throw VoipLinkException("Call does not exist");
403

Emmanuel Milou's avatar
Emmanuel Milou committed
404
    Manager::instance().addStream (call->getCallId());
405

406
    mutexIAX_.enterMutex();
Emmanuel Milou's avatar
Emmanuel Milou committed
407
    iax_unquelch (call->getSession());
408
409
    mutexIAX_.leaveMutex();
    audiolayer_->startStream();
Emmanuel Milou's avatar
Emmanuel Milou committed
410
    call->setState (Call::Active);
411
412
}

413
void
414
IAXVoIPLink::transfer (const std::string& id, const std::string& to)
415
{
Emmanuel Milou's avatar
Emmanuel Milou committed
416
    IAXCall* call = getIAXCall (id);
417
418
    if (!call)
    	return;
419

Emmanuel Milou's avatar
Emmanuel Milou committed
420
421
    char callto[to.length() +1];
    strcpy (callto, to.c_str());
422

423
    mutexIAX_.enterMutex();
Emmanuel Milou's avatar
Emmanuel Milou committed
424
    iax_transfer (call->getSession(), callto);
425
    mutexIAX_.leaveMutex();
426
427
}

428
bool
429
IAXVoIPLink::attendedTransfer(const std::string& /*transferID*/, const std::string& /*targetID*/)
430
431
432
433
434
{
	// TODO implement attended transfer for IAX
	return false;
}

Emmanuel Milou's avatar
Emmanuel Milou committed
435
bool
436
IAXVoIPLink::refuse (const std::string& id)
437
{
Emmanuel Milou's avatar
Emmanuel Milou committed
438
439
    IAXCall* call = getIAXCall (id);
    CHK_VALID_CALL;
440

441
    mutexIAX_.enterMutex();
442
    iax_reject (call->getSession(), (char*) "Call rejected manually.");
443
    mutexIAX_.leaveMutex();
alexandresavard's avatar
alexandresavard committed
444

Emmanuel Milou's avatar
Emmanuel Milou committed
445
    removeCall (id);
446

Emmanuel Milou's avatar
Emmanuel Milou committed
447
    return true;
448
449
}

alexandresavard's avatar
alexandresavard committed
450

Rafaël Carré's avatar
Rafaël Carré committed
451
void
452
IAXVoIPLink::carryingDTMFdigits (const std::string& id, char code)
453
{
Emmanuel Milou's avatar
Emmanuel Milou committed
454
    IAXCall* call = getIAXCall (id);
Rafaël Carré's avatar
Rafaël Carré committed
455
456
457
458
459
    if (call) {
		mutexIAX_.enterMutex();
		iax_send_dtmf (call->getSession(), code);
		mutexIAX_.leaveMutex();
    }
460
461
}

Rafaël Carré's avatar
Rafaël Carré committed
462
void
463
464
465
IAXVoIPLink::sendTextMessage (sfl::InstantMessaging *module,
        const std::string& callID, const std::string& message,
        const std::string& /*from*/)
466
467
{
    IAXCall* call = getIAXCall (callID);
Rafaël Carré's avatar
Rafaël Carré committed
468
469
    if (!call)
    	return;
470

471
    mutexIAX_.enterMutex();
472
    module->send_iax_message (call->getSession(), callID, message.c_str());
473
    mutexIAX_.leaveMutex();
474
475
}

476

Emmanuel Milou's avatar
Emmanuel Milou committed
477
std::string
478
IAXVoIPLink::getCurrentCodecName(Call *c) const
Alexandre Savard's avatar
Alexandre Savard committed
479
{
480
    IAXCall *call = dynamic_cast<IAXCall*>(c);
481
    sfl::Codec *audioCodec = Manager::instance().audioCodecFactory.getCodec(call->getAudioCodec());
Rafaël Carré's avatar
Rafaël Carré committed
482
    return audioCodec ? audioCodec->getMimeSubtype() : "";
Alexandre Savard's avatar
Alexandre Savard committed
483
484
}

485

Emmanuel Milou's avatar
Emmanuel Milou committed
486
bool
487
IAXVoIPLink::iaxOutgoingInvite (IAXCall* call)
488
{
489
    ost::MutexLock m(mutexIAX_);
yanmorin's avatar
yanmorin committed
490

491
    iax_session *newsession = iax_session_new();
492

Emmanuel Milou's avatar
Emmanuel Milou committed
493
    if (!newsession) {
Julien Bonjean's avatar
Julien Bonjean committed
494
        _debug ("IAX Error: Can't make new session for a new call");
Emmanuel Milou's avatar
Emmanuel Milou committed
495
496
        return false;
    }
497

498
    call->setSession(newsession);
499

500
    IAXAccount *account = dynamic_cast<IAXAccount *>(Manager::instance().getAccount(accountID_));
501
502
    std::string username(account->getUsername());
    std::string strNum(username + ":" + account->getPassword() + "@" + account->getHostname() + "/" + call->getPeerNumber());
503

Emmanuel Milou's avatar
Emmanuel Milou committed
504
    /** @todo Make preference dynamic, and configurable */
505
506
    int audio_format_preferred = call->getFirstMatchingFormat(call->getSupportedFormat(accountID_), accountID_);
    int audio_format_capability = call->getSupportedFormat(accountID_);
507

Julien Bonjean's avatar
Julien Bonjean committed
508
    _debug ("IAX New call: %s", strNum.c_str());
509
510
    iax_call(newsession, username.c_str(), username.c_str(), strNum.c_str(),
              NULL, 0, audio_format_preferred, audio_format_capability);
511

Emmanuel Milou's avatar
Emmanuel Milou committed
512
    return true;
513
514
515
}


Emmanuel Milou's avatar
Emmanuel Milou committed
516
IAXCall*
517
IAXVoIPLink::iaxFindCallBySession (struct iax_session* session)
518
{
Emmanuel Milou's avatar
Emmanuel Milou committed
519
520
    // access to callMap shoud use that
    // the code below is like findSIPCallWithCid()
521
522
    ost::MutexLock m(_callMapMutex);
    for (CallMap::const_iterator iter = _callMap.begin(); iter != _callMap.end(); ++iter) {
523
        IAXCall* call = dynamic_cast<IAXCall*> (iter->second);
524
        if (call and call->getSession() == session)
Emmanuel Milou's avatar
Emmanuel Milou committed
525
526
            return call;
    }
527

Emmanuel Milou's avatar
Emmanuel Milou committed
528
    return NULL; // not found
529
530
}

Emmanuel Milou's avatar
Emmanuel Milou committed
531
void
532
IAXVoIPLink::iaxHandleCallEvent (iax_event* event, IAXCall* call)
533
{
Emmanuel Milou's avatar
Emmanuel Milou committed
534
535
536
    // call should not be 0
    // note activity?
    //
537
    std::string id = call->getCallId();
Emmanuel Milou's avatar
Emmanuel Milou committed
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559

    switch (event->etype) {

        case IAX_EVENT_HANGUP:

            if (Manager::instance().isCurrentCall (id)) {
                // stop audio
                // audiolayer->stopStream();
            }

            Manager::instance().peerHungupCall (id);

            removeCall (id);
            break;

        case IAX_EVENT_REJECT:
            call->setConnectionState (Call::Connected);

            call->setState (Call::Error);
            Manager::instance().callFailure (id);
            removeCall (id);
            break;
560

Emmanuel Milou's avatar
Emmanuel Milou committed
561
562
        case IAX_EVENT_ACCEPT:
            // Call accepted over there by the computer, not the user yet.
563
            _debug ("IAX_EVENT_ACCEPT: codec format: %d", event->ies.format);
564

565
            if (event->ies.format)
Emmanuel Milou's avatar
Emmanuel Milou committed
566
                call->setFormat (event->ies.format);
567

Emmanuel Milou's avatar
Emmanuel Milou committed
568
            break;
569

Emmanuel Milou's avatar
Emmanuel Milou committed
570
        case IAX_EVENT_ANSWER:
571
572
573
        case IAX_EVENT_TRANSFER:
            if (call->getConnectionState() == Call::Connected)
                break;
574

575
            Manager::instance().addStream (call->getCallId());
576

577
578
579
            call->setConnectionState (Call::Connected);
            call->setState (Call::Active);
            // audiolayer->startStream();
580

581
            _debug ("IAX_EVENT_ANSWER: codec format: %d", event->ies.format);
582

583
584
585
            // Should not get here, should have been set in EVENT_ACCEPT
            if (event->ies.format)
                call->setFormat (event->ies.format);
586

587
            Manager::instance().peerAnsweredCall (id);
588

589
            // start audio here?
590
591
            audiolayer_->startStream();
            audiolayer_->flushMain();
592

Emmanuel Milou's avatar
Emmanuel Milou committed
593
            break;
594

Emmanuel Milou's avatar
Emmanuel Milou committed
595
596
597
598
599
600
        case IAX_EVENT_BUSY:
            call->setConnectionState (Call::Connected);
            call->setState (Call::Busy);
            Manager::instance().callBusy (id);
            removeCall (id);
            break;
601

Emmanuel Milou's avatar
Emmanuel Milou committed
602
603
604
        case IAX_EVENT_VOICE:
            //if (!audiolayer->isCaptureActive ())
            //  audiolayer->startStream ();
Julien Bonjean's avatar
Julien Bonjean committed
605
            // _debug("IAX_EVENT_VOICE: ");
Emmanuel Milou's avatar
Emmanuel Milou committed
606
607
            iaxHandleVoiceEvent (event, call);
            break;
608

Emmanuel Milou's avatar
Emmanuel Milou committed
609
        case IAX_EVENT_TEXT:
610
            Manager::instance ().incomingMessage (call->getCallId (), call->getPeerNumber(), std::string ( (const char*) event->data));
Emmanuel Milou's avatar
Emmanuel Milou committed
611
            break;
612

Emmanuel Milou's avatar
Emmanuel Milou committed
613
614
615
616
        case IAX_EVENT_RINGA:
            call->setConnectionState (Call::Ringing);
            Manager::instance().peerRingingCall (call->getCallId());
            break;
617

Emmanuel Milou's avatar
Emmanuel Milou committed
618
        case IAX_IE_MSGCOUNT:
Rafaël Carré's avatar
Rafaël Carré committed
619
        case IAX_EVENT_TIMEOUT:
Emmanuel Milou's avatar
Emmanuel Milou committed
620
        case IAX_EVENT_PONG:
Rafaël Carré's avatar
Rafaël Carré committed
621
        default:
Emmanuel Milou's avatar
Emmanuel Milou committed
622
            break;
623

Emmanuel Milou's avatar
Emmanuel Milou committed
624
        case IAX_EVENT_URL:
625

Rafaël Carré's avatar
Rafaël Carré committed
626
627
            if (Manager::instance().getConfigString (HOOKS, URLHOOK_IAX2_ENABLED) == "1")
				UrlHook::runAction (Manager::instance().getConfigString (HOOKS, URLHOOK_COMMAND), (char*) event->data);
628

Emmanuel Milou's avatar
Emmanuel Milou committed
629
630
            break;
    }
631
}
632

633

634
/* Handle audio event, VOICE packet received */
Emmanuel Milou's avatar
Emmanuel Milou committed
635
void
636
637
IAXVoIPLink::iaxHandleVoiceEvent (iax_event* event, IAXCall* call)
{
638
639
    // Skip this empty packet.
    if (!event->datalen)
Emmanuel Milou's avatar
Emmanuel Milou committed
640
641
        return;

642
    if (audiolayer_) {
Rafaël Carré's avatar
Rafaël Carré committed
643
644
645
        _debug ("IAX: incoming audio, but no sound card open");
        return;
    }
646

647
    sfl::AudioCodec *audioCodec = static_cast<sfl::AudioCodec *>(Manager::instance().audioCodecFactory.getCodec(call->getAudioCodec()));
648
649
    if (!audioCodec)
        return;
650

651
	Manager::instance().getMainBuffer()->setInternalSamplingRate(audioCodec->getClockRate());
652
	unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer()->getInternalSamplingRate();
653

Rafaël Carré's avatar
Rafaël Carré committed
654
655
656
	// 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.
657

Rafaël Carré's avatar
Rafaël Carré committed
658
659
660
661
	if (event->subclass && event->subclass != call->getFormat()) {
		_debug ("iaxHandleVoiceEvent: no format found in call setting it to %i", event->subclass);
		call->setFormat (event->subclass);
	}
662

663
664
	unsigned char *data = (unsigned char*) event->data;
	unsigned int size = event->datalen;
665

Rafaël Carré's avatar
Rafaël Carré committed
666
	// Decode data with relevant codec
667
	unsigned int max = audioCodec->getClockRate() * 20 / 1000;
668

Rafaël Carré's avatar
Rafaël Carré committed
669
670
671
672
	if (size > max) {
		_debug ("The size %d is bigger than expected %d. Packet cropped. Ouch!", size, max);
		size = max;
	}
673

674
675
	int samples = audioCodec->decode (decData, data , size);
	int outSize = samples * sizeof(SFLDataFormat);
676
677
	unsigned int audioRate = audioCodec->getClockRate();
	if (audioRate != mainBufferSampleRate) {
678
		outSize = (double)outSize * (mainBufferSampleRate / audioRate);
679
680
		converter_->resample (decData, resampledData, mainBufferSampleRate, audioRate, samples);
		audiolayer_->getMainBuffer()->putData (resampledData, outSize, call->getCallId());
681
	} else
682
		audiolayer_->getMainBuffer()->putData (decData, outSize, call->getCallId());
yanmorin's avatar
yanmorin committed
683
684
}

685
686
687
/**
 * Handle the registration process
 */
Emmanuel Milou's avatar
Emmanuel Milou committed
688
void
689
IAXVoIPLink::iaxHandleRegReply (iax_event* event)
yanmorin's avatar
yanmorin committed
690
{
691
    IAXAccount *account = dynamic_cast<IAXAccount *>(Manager::instance().getAccount(accountID_));
Emmanuel Milou's avatar
Emmanuel Milou committed
692
693
694

    if (event->etype == IAX_EVENT_REGREJ) {
        /* Authentication failed! */
695
696
697
698
699
        {
            ost::MutexLock m(mutexIAX_);
            iax_destroy</