iaxvoiplink.cpp 25.7 KB
Newer Older
yanmorin's avatar
 
yanmorin committed
1
/*
2
3
 *  Copyright (C) 2006-2007 Savoir-Faire Linux inc.
 *  Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
yanmorin's avatar
 
yanmorin committed
4
5
6
7
 *  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
8
 *  the Free Software Foundation; either version 3 of the License, or
yanmorin's avatar
 
yanmorin committed
9
10
11
12
13
14
15
16
17
18
19
 *  (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.
 */
yanmorin's avatar
   
yanmorin committed
20
#include "iaxvoiplink.h"
21
22
#include "iaxcall.h"
#include "eventthread.h"
Emmanuel Milou's avatar
Emmanuel Milou committed
23
#include "iaxaccount.h"
yanmorin's avatar
yanmorin committed
24
#include "manager.h"
25
#include "audio/audiolayer.h"
yanmorin's avatar
yanmorin committed
26

27
#include <math.h>
28
#include <dlfcn.h>
29
30
31

#define IAX_BLOCKING    1
#define IAX_NONBLOCKING 0
32

yanmorin's avatar
   
yanmorin committed
33
34
#define IAX_SUCCESS  0
#define IAX_FAILURE -1
yanmorin's avatar
 
yanmorin committed
35

36
37
#define RANDOM_IAX_PORT   rand() % 64000 + 1024

38
#define MUSIC_ONHOLD true
39

40
#define CHK_VALID_CALL   if (call == NULL) { _debug("IAX: Call doesn't exists\n"); \
41
    return false; }
42

43
    IAXVoIPLink::IAXVoIPLink(const AccountID& accountID)
44
: VoIPLink(accountID)
yanmorin's avatar
 
yanmorin committed
45
{
46
47
48
    _evThread = new EventThread(this);
    _regSession = NULL;
    _nextRefreshStamp = 0;
49

50
51
    // to get random number for RANDOM_PORT
    srand (time(NULL));
52

53
    audiolayer = NULL;
54

55
    converter = new SamplerateConverter();
56

57
    int nbSamplesMax = (int) ( converter->getFrequence() * converter->getFramesize() / 1000 );
58

59
60
61
    micData = new SFLDataFormat[nbSamplesMax];
    micDataConverted = new SFLDataFormat[nbSamplesMax];
    micDataEncoded = new unsigned char[nbSamplesMax];
62

63
64
    spkrDataConverted = new SFLDataFormat[nbSamplesMax];
    spkrDataDecoded = new SFLDataFormat[nbSamplesMax];
yanmorin's avatar
   
yanmorin committed
65
}
yanmorin's avatar
 
yanmorin committed
66
67


yanmorin's avatar
   
yanmorin committed
68
69
IAXVoIPLink::~IAXVoIPLink()
{
70
71
72
    delete _evThread; _evThread = NULL;
    _regSession = NULL; // shall not delete it
    terminate();
73

74
    audiolayer = NULL;
75

76
77
78
    delete [] micData;  micData = NULL;
    delete [] micDataConverted;  micDataConverted = NULL;
    delete [] micDataEncoded;  micDataEncoded = NULL;
79

80
81
    delete [] spkrDataDecoded; spkrDataDecoded = NULL;
    delete [] spkrDataConverted; spkrDataConverted = NULL;
yanmorin's avatar
   
yanmorin committed
82
}
yanmorin's avatar
 
yanmorin committed
83

84
    bool
yanmorin's avatar
   
yanmorin committed
85
86
IAXVoIPLink::init()
{
87
88
89
    // If it was done, don't do it again, until we call terminate()
    if (initDone())
        return false;
90

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
    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);
120
    }
121
122
    if (port == IAX_FAILURE || nbTry==0) {
        _debug("Fail to initialize iax\n");
123

124
125
        initDone(false);
    }
126

127
    return returnValue;
yanmorin's avatar
   
yanmorin committed
128
}
yanmorin's avatar
 
yanmorin committed
129

130
    void
yanmorin's avatar
   
yanmorin committed
131
132
IAXVoIPLink::terminate()
{
133
134
135
    // If it was done, don't do it again, until we call init()
    if (!initDone())
        return;
136

137
    // iaxc_shutdown();  
138

139
140
    // Hangup all calls
    terminateIAXCall();
141

142
    initDone(false);
yanmorin's avatar
   
yanmorin committed
143
}
144

145
    void
146
147
IAXVoIPLink::terminateIAXCall()
{
148
149
150
151
152
153
154
155
156
157
158
159
160
161
    std::string reason = "Dumped Call";
    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(), (char*)reason.c_str());
            _mutexIAX.leaveMutex();
            call->setSession(NULL);
            delete call; call = NULL;
        }
        iter++;
162
    }
163
    _callMap.clear();
164
165
}

166
    void
167
168
IAXVoIPLink::getEvent() 
{
169
    IAXCall* call = NULL;
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
    // 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);
            //_audiocodec = Manager::instance().getCodecDescriptorMap().getCodec( call -> getAudioCodec() ); 
        }
        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);
200
    }
201
    _mutexIAX.leaveMutex();
202

203
204
    // Do the doodle-moodle to send audio from the microphone to the IAX channel.
    sendAudioFromMic();
205

206
207
208
    // Refresh registration.
    if (_nextRefreshStamp && _nextRefreshStamp - 2 < time(NULL)) {
        sendRegister("");
209
210
    }

211
212
213
    // thread wait 3 millisecond
    _evThread->sleep(3);
    free(event);
214
215
}

216
    void
217
218
IAXVoIPLink::sendAudioFromMic(void)
{
219
220
221
    int maxBytesToGet, availBytesFromMic, bytesAvail, nbSample, compSize;
    AudioCodec *ac;

Emmanuel Milou's avatar
Emmanuel Milou committed
222
223
224
225
    // We have to update the audio layer type in case we switched
    // TODO Find out a better way to do it
    updateAudiolayer();

226
    IAXCall* currentCall = getIAXCall(Manager::instance().getCurrentCallId());
227

228
229
230
231
    if (!currentCall) {
        // Let's mind our own business.
        return;
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
232

233
234
    if( currentCall -> getAudioCodec() < 0 )
        return;
235

236
237
238
239
    // 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());
240

241
242
243
244
    if (currentCall->getConnectionState() != Call::Connected ||
            currentCall->getState() != Call::Active) {
        return;
    }
245

246
247
248
249
250
    ac = currentCall->getCodecMap().getCodec( currentCall -> getAudioCodec() );
    if (!ac) {
        // Audio codec still not determined.
        if (audiolayer) {
            // To keep latency low..
Emmanuel Milou's avatar
Emmanuel Milou committed
251
            audiolayer->flushMic();
252
253
        }
        return;
254
    }
255

256
257
    // Send sound here
    if (audiolayer) {
258

259
260
261
        // we have to get 20ms of data from the mic *20/1000 = /50
        // rate/50 shall be lower than IAX__20S_48KHZ_MAX
        maxBytesToGet = audiolayer->getSampleRate()* audiolayer->getFrameSize() / 1000 * sizeof(SFLDataFormat);
262

263
264
        // available bytes inside ringbuffer
        availBytesFromMic = audiolayer->canGetMic();
265

266
267
268
269
        if (availBytesFromMic < maxBytesToGet) {
            // We need packets full!
            return;
        }
270

271
272
273
        // take the lowest
        bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet;
        //_debug("available = %d, maxBytesToGet = %d\n", availBytesFromMic, maxBytesToGet);
274

275
276
        // Get bytes from micRingBuffer to data_from_mic
        nbSample = audiolayer->getMic( micData, bytesAvail ) / sizeof(SFLDataFormat);
277

278
279
        // resample
        nbSample = converter->downsampleData( micData , micDataConverted , (int)ac ->getClockRate() ,  (int)audiolayer->getSampleRate() , nbSample );
280

281
282
283
284
285
286
287
        // for the mono: range = 0 to IAX_FRAME2SEND * sizeof(int16)
        compSize = ac->codecEncode( micDataEncoded, micDataConverted , nbSample*sizeof(int16));

        // Send it out!
        _mutexIAX.enterMutex();
        // Make sure the session and the call still exists.
        if (currentCall->getSession()) {
Emmanuel Milou's avatar
Emmanuel Milou committed
288
            if (iax_send_voice(currentCall->getSession(), currentCall->getFormat(), micDataEncoded, compSize, nbSample) == -1) {
289
290
291
292
                _debug("IAX: Error sending voice data.\n");
            }
        }
        _mutexIAX.leaveMutex();
293
    }
294
295
}

296

297
    IAXCall* 
298
299
IAXVoIPLink::getIAXCall(const CallID& id) 
{
300
301
302
303
304
    Call* call = getCall(id);
    if (call) {
        return dynamic_cast<IAXCall*>(call);
    }
    return NULL;
305
306
307
}


308
    int 
Emmanuel Milou's avatar
Emmanuel Milou committed
309
IAXVoIPLink::sendRegister(AccountID id) 
310
{
Emmanuel Milou's avatar
Emmanuel Milou committed
311
312
    IAXAccount *account;
    bool result;
yanmorin's avatar
yanmorin committed
313

Emmanuel Milou's avatar
Emmanuel Milou committed
314
    result = false;
315
316
    account = dynamic_cast<IAXAccount *> (getAccountPtr());
    if (account->getHostname().empty()) {
Emmanuel Milou's avatar
Emmanuel Milou committed
317
318
        return false;
    }
319
    if (account->getUsername().empty()) {
Emmanuel Milou's avatar
Emmanuel Milou committed
320
321
        return false;
    }
yanmorin's avatar
yanmorin committed
322

Emmanuel Milou's avatar
Emmanuel Milou committed
323
324
325
326
327
328
329
    // lock
    _mutexIAX.enterMutex();

    // Always use a brand new session
    if (_regSession) {
        iax_destroy(_regSession);
    }
330

Emmanuel Milou's avatar
Emmanuel Milou committed
331
    _regSession = iax_session_new();
332

Emmanuel Milou's avatar
Emmanuel Milou committed
333
334
335
    if (!_regSession) {
        _debug("Error when generating new session for register");
    } else {
336
337
        _debug("IAX Sending registration to %s with user %s\n", account->getHostname().c_str() , account->getUsername().c_str() );
        int val = iax_register(_regSession, account->getHostname().data(), account->getUsername().data(), account->getPassword().data(), 120);
Emmanuel Milou's avatar
Emmanuel Milou committed
338
339
340
341
342
343
344
        _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;

        account->setRegistrationState(Trying);
345
    }
346

347
348
    // unlock
    _mutexIAX.leaveMutex();
349

350
    return result;
351
352
}

353
    int 
Emmanuel Milou's avatar
Emmanuel Milou committed
354
IAXVoIPLink::sendUnregister(AccountID id)
355
{
356
    IAXAccount *account;
357

358
359
360
    account = dynamic_cast<IAXAccount*>(getAccountPtr());
    if(!account)
        return 1;
361

362
363
364
365
366
367
368
369
    _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();
370

371
    _nextRefreshStamp = 0;
372

373
374
    _debug("IAX2 send unregister\n");
    account->setRegistrationState(Unregistered);
375

376
    return SUCCESS;
377
378
}

379
    Call* 
380
381
IAXVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl)
{
382
383
    IAXCall* call = new IAXCall(id, Call::Outgoing);
    call->setCodecMap(Manager::instance().getCodecDescriptorMap());
384

385
386
387
388
389
390
391
392
393
394
    if (call) {
        call->setPeerNumber(toUrl);

        if ( iaxOutgoingInvite(call) ) {
            call->setConnectionState(Call::Progressing);
            call->setState(Call::Active);
            addCall(call);
        } else {
            delete call; call = NULL;
        }
395
    }
396
    return call;
397
398
}

399

400
    bool 
401
IAXVoIPLink::answer(const CallID& id)
402
{
403
404
    IAXCall* call = getIAXCall(id);
    call->setCodecMap(Manager::instance().getCodecDescriptorMap());
405

406
    CHK_VALID_CALL;
407

408
409
410
    _mutexIAX.enterMutex();
    iax_answer(call->getSession());
    _mutexIAX.leaveMutex();
411

412
413
414
    call->setState(Call::Active);
    call->setConnectionState(Call::Connected);
    // Start audio
Emmanuel Milou's avatar
Emmanuel Milou committed
415
    audiolayer->flushMic();
416
    audiolayer->startStream();
417

418
    return true;
419
420
}

421
    bool 
yanmorin's avatar
yanmorin committed
422
423
IAXVoIPLink::hangup(const CallID& id)
{
424
425
426
427
428
429
430
431
432
433
434
435
436
437
    IAXCall* call = getIAXCall(id);
    std::string reason = "Dumped Call";
    CHK_VALID_CALL;

    _mutexIAX.enterMutex();
    iax_hangup(call->getSession(), (char*) reason.c_str());
    _mutexIAX.leaveMutex();
    call->setSession(NULL);
    if (Manager::instance().isCurrentCall(id)) {
        // stop audio
        audiolayer->stopStream();
    }
    removeCall(id);
    return true;	
yanmorin's avatar
yanmorin committed
438
}
439

440
    bool 
441
442
IAXVoIPLink::onhold(const CallID& id) 
{
443
    IAXCall* call = getIAXCall(id);
444

445
    CHK_VALID_CALL;
446

447
    //if (call->getState() == Call::Hold) { _debug("Call is already on hold\n"); return false; }
448

449
450
451
    _mutexIAX.enterMutex();
    iax_quelch_moh(call->getSession() , MUSIC_ONHOLD);
    _mutexIAX.leaveMutex();
452

453
454
    call->setState(Call::Hold);
    return true;
455
456
}

457
    bool 
458
459
IAXVoIPLink::offhold(const CallID& id)
{
460
    IAXCall* call = getIAXCall(id);
461

462
    CHK_VALID_CALL;
463

464
465
466
467
468
469
470
    //if (call->getState() == Call::Active) { _debug("Call is already active\n"); return false; }
    _mutexIAX.enterMutex();
    iax_unquelch(call->getSession());
    _mutexIAX.leaveMutex();
    audiolayer->startStream();
    call->setState(Call::Active);
    return true;
471
472
}

473
    bool 
474
475
IAXVoIPLink::transfer(const CallID& id, const std::string& to)
{
476
    IAXCall* call = getIAXCall(id);
477

478
    CHK_VALID_CALL;
479

480
481
    char callto[to.length()+1];
    strcpy(callto, to.c_str());
482

483
484
485
    _mutexIAX.enterMutex();
    iax_transfer(call->getSession(), callto); 
    _mutexIAX.leaveMutex();
486

487
    return true;
488

489
490
    // should we remove it?
    // removeCall(id);
491
492
}

493
    bool 
494
495
IAXVoIPLink::refuse(const CallID& id)
{
496
497
    IAXCall* call = getIAXCall(id);
    std::string reason = "Call rejected manually.";
498

499
    CHK_VALID_CALL;
500

501
502
503
504
    _mutexIAX.enterMutex();
    iax_reject(call->getSession(), (char*) reason.c_str());
    _mutexIAX.leaveMutex();
    removeCall(id);
505

506
    return true;
507
508
}

509
    bool
510
511
IAXVoIPLink::carryingDTMFdigits(const CallID& id, char code)
{
512
    IAXCall* call = getIAXCall(id);
513

514
    CHK_VALID_CALL;
515

516
517
518
    _mutexIAX.enterMutex();
    iax_send_dtmf(call->getSession(), code);
    _mutexIAX.leaveMutex();
519

520
    return true;
521
522
}

523
524


525
    bool
526
527
IAXVoIPLink::iaxOutgoingInvite(IAXCall* call) 
{
528
529
530
531
532
533
    struct iax_session *newsession;
    ost::MutexLock m(_mutexIAX);
    std::string username, strNum;
    char *lang=NULL;
    int wait, audio_format_preferred, audio_format_capability;
    IAXAccount *account;
yanmorin's avatar
yanmorin committed
534

535
536
537
538
539
540
    newsession = iax_session_new();
    if (!newsession) {
        _debug("IAX Error: Can't make new session for a new call\n");
        return false;
    }
    call->setSession(newsession);
541

542
543
544
    account = dynamic_cast<IAXAccount*> (getAccountPtr());
    username = account->getUsername();
    strNum = username + ":" + account->getPassword() + "@" + account->getHostname() + "/" + call->getPeerNumber();  
545

546
547
548
549
    wait = 0;
    /** @todo Make preference dynamic, and configurable */
    audio_format_preferred =  call->getFirstMatchingFormat(call->getSupportedFormat());
    audio_format_capability = call->getSupportedFormat();
550

Emmanuel Milou's avatar
Emmanuel Milou committed
551
552
    _debug("IAX New call: %s\n", strNum.c_str());
    iax_call(newsession, username.c_str(), username.c_str(), strNum.c_str(), lang, wait, audio_format_preferred, audio_format_capability);
553

554
    return true;
555
556
557
}


558
    IAXCall* 
559
560
IAXVoIPLink::iaxFindCallBySession(struct iax_session* session) 
{
561
562
563
564
565
566
567
568
569
570
571
    // 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++;
572
    }
573
    return NULL; // not found
574
575
}

576
    void
577
578
IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) 
{
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
    // call should not be 0
    // note activity?
    //
    CallID id = call->getCallId();

    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:
            //Manager::instance().peerHungupCall(id); 
            if (Manager::instance().isCurrentCall(id)) {
                // stop audio
                audiolayer->stopStream();
            }
            call->setConnectionState(Call::Connected);
            call->setState(Call::Error);
            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);
Emmanuel Milou's avatar
Emmanuel Milou committed
624
                audiolayer->flushMic();
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
                audiolayer->startStream();
                // start audio here?
            } else {
                // deja connecté ?
            }
            break;

        case IAX_EVENT_BUSY:
            call->setConnectionState(Call::Connected);
            call->setState(Call::Busy);
            Manager::instance().callBusy(id);
            removeCall(id);
            break;

        case IAX_EVENT_VOICE:
Emmanuel Milou's avatar
Emmanuel Milou committed
640
641
            //if (!audiolayer->isCaptureActive ())
              //  audiolayer->startStream ();
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
            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_IE_MSGCOUNT:	
            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);

    }
674
675
676
677
}


/* Handle audio event, VOICE packet received */
678
    void
679
IAXVoIPLink::iaxHandleVoiceEvent(iax_event* event, IAXCall* call)
680
{ 
681
682
683
684
685
686

    unsigned char *data;
    unsigned int size, max, nbInt16;
    int expandedSize, nbSample;    
    AudioCodec *ac;

687
688
689
690
691
692
    // 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;
693
    }
694

695
696
697
698
699
700
701
702
703
    if (audiolayer) {
        // 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);
        }
        //_debug("Receive: len=%d, format=%d, _receiveDataDecoded=%p\n", event->datalen, call->getFormat(), _receiveDataDecoded);
        ac = call->getCodecMap().getCodec( call -> getAudioCodec() );
Emmanuel Milou's avatar
Emmanuel Milou committed
704

705
706
        data = (unsigned char*)event->data;
        size   = event->datalen;
707

708
709
        // Decode data with relevant codec
        max = (int)( ac->getClockRate() * audiolayer->getFrameSize() / 1000 );
710

711
712
713
714
        if (size > max) {
            _debug("The size %d is bigger than expected %d. Packet cropped. Ouch!\n", size, max);
            size = max;
        }
715

716
717
        expandedSize = ac->codecDecode( spkrDataDecoded , data , size );
        nbInt16      = expandedSize/sizeof(int16);
718

719
720
721
722
        if (nbInt16 > max) {
            _debug("We have decoded an IAX VOICE packet larger than expected: %i VS %i. Cropping.\n", nbInt16, max);
            nbInt16 = max;
        }
723

724
725
726
        nbSample = nbInt16;
        // resample
        nbInt16 = converter->upsampleData( spkrDataDecoded , spkrDataConverted , ac->getClockRate() , audiolayer->getSampleRate() , nbSample);
727

728
729
        //audiolayer->playSamples( spkrDataConverted , nbInt16 * sizeof(SFLDataFormat), true);
        audiolayer->putMain (spkrDataConverted , nbInt16 * sizeof(SFLDataFormat));
730
731
732
733

    } else {
        _debug("IAX: incoming audio, but no sound card open");
    }
734

yanmorin's avatar
yanmorin committed
735
736
}

737
738
739
/**
 * Handle the registration process
 */
740
    void
yanmorin's avatar
yanmorin committed
741
742
IAXVoIPLink::iaxHandleRegReply(iax_event* event) 
{
743

744
    int new_voicemails;
745
    std::string account_id;
746
747
748
749
750
    IAXAccount *account;
    
    account_id = getAccountID();
    account = dynamic_cast<IAXAccount *>(Manager::instance().getAccount(account_id));

751
752
753
754
755
756
    if (event->etype == IAX_EVENT_REGREJ) {
        /* Authentication failed! */
        _mutexIAX.enterMutex();
        iax_destroy(_regSession);
        _mutexIAX.leaveMutex();
        _regSession = NULL;
757
758
        // Update the account registration state
        account->setRegistrationState(ErrorAuth);
759
    }
760

761
762
763
764
765
766
    else if (event->etype == IAX_EVENT_REGACK) {
        /* Authentication succeeded */
        _mutexIAX.enterMutex();

        // Looking for the voicemail information
        //if( event->ies != 0 )        
767
768
        //new_voicemails = processIAXMsgCount(event->ies.msgcount);
        //_debug("iax voicemail number notification: %i\n", new_voicemails);
769
        // Notify the client if new voicemail waiting for the current account
770
771
        //account_id = getAccountID();
        //Manager::instance().startVoiceMessageNotification(account_id.c_str(), new_voicemails);
772
773
774
775
776
777
778
779

        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);
780
        account->setRegistrationState(Registered);
781
    }
782
}
yanmorin's avatar
yanmorin committed
783

784
785
int IAXVoIPLink::processIAXMsgCount( int msgcount )
{
786

787
788
789
790
791
792
793
794
795
796
797
798
799
    // IAX sends the message count under a specific format:
    //                       1
    //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //  |      0x18     |      0x02     |
    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //  |  Old messages |  New messages |
    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    // For now we just need the new messages informations.
    // Thus: 
    // 0 <= msgcount <= 255   => msgcount new messages, 0 old messages
    // msgcount >= 256        => msgcount/256 old messages , msgcount%256 new messages (RULES)
800

801
802
803
804
805
    return msgcount%256;

}


806
    void
807
IAXVoIPLink::iaxHandlePrecallEvent(iax_event* event)
yanmorin's avatar
yanmorin committed
808
{
809
810
811
    IAXCall* call = NULL;
    CallID   id;
    std::string reason = "Error ringing user.";
812

813
814
815
816
817
    switch(event->etype) {
        case IAX_EVENT_REGACK:
        case IAX_EVENT_REGREJ:
            _debug("IAX Registration Event in a pre-call setup\n");
            break;
818

819
820
821
822
823
        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;
824

825
826
827
        case IAX_EVENT_CONNECT:
            // We've got an incoming call! Yikes!
            _debug("> IAX_EVENT_CONNECT (receive)\n");
828

829
            id = Manager::instance().getNewCallID();
830

831
            call = new IAXCall(id, Call::Incoming);
832

833
834
835
836
            if (!call) {
                _debug("! IAX Failure: unable to create an incoming call");
                return;
            }
837

838
839
840
            // Setup the new IAXCall
            // Associate the call to the session.
            call->setSession(event->session);
841

842
843
844
            // setCallAudioLocal(call);
            call->setCodecMap(Manager::instance().getCodecDescriptorMap());
            call->setConnectionState(Call::Progressing);
845
846


847
848
849
850
            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));
851

852
853
854
            if (Manager::instance().incomingCall(call, getAccountID())) {
                /** @todo Faudra considérer éventuellement le champ CODEC PREFS pour
                 * l'établissement du codec de transmission */
855

856
857
858
859
                // 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);
860

861
862
863
864
                // 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;
865

866
867
                iax_accept(event->session, format);
                iax_ring_announce(event->session);
868

869
870
871
872
                addCall(call);
            } else {
                // reject call, unable to add it
                iax_reject(event->session, (char*)reason.c_str());
873

874
875
                delete call; call = NULL;
            }
876

877
            break;
878

879
880
881
882
        case IAX_EVENT_HANGUP:
            // Remote peer hung up
            call = iaxFindCallBySession(event->session);
            id = call->getCallId();
883

884
885
886
            Manager::instance().peerHungupCall(id);
            removeCall(id);
            break;
887

888
        case IAX_EVENT_TIMEOUT: // timeout for an unknown session
889

890
            break;
891

892
893
894
        case IAX_IE_MSGCOUNT:	
            //_debug("messssssssssssssssssssssssssssssssssssssssssssssssages\n");
            break;
895

896
897
898
        default:
            _debug("Unknown event type (in precall): %d\n", event->etype);
    }
899

900
901
}

Emmanuel Milou's avatar
Emmanuel Milou committed
902
903
void IAXVoIPLink::updateAudiolayer( void )
{
904
    _mutexIAX.enterMutex();
Emmanuel Milou's avatar
Emmanuel Milou committed
905
906
    audiolayer = NULL;
    audiolayer = Manager::instance().getAudioDriver();
907
    _mutexIAX.leaveMutex();
Emmanuel Milou's avatar
Emmanuel Milou committed
908
}