audiortp.cpp 14.4 KB
Newer Older
jpbl's avatar
jpbl committed
1
/*
2
 *  Copyright (C) 2004-2008 Savoir-Faire Linux inc.
3
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
4
 *  Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
jpbl's avatar
jpbl committed
5
6
7
8
9
 *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
 *  Author: Laurielle Lea <laurielle.lea@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
10
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 *  (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 <cstdio>
#include <cstdlib>
#include <ccrtp/rtp.h>
#include <assert.h>
#include <string>
28
#include <cstring>
29
#include <math.h>
30
31
#include <dlfcn.h>
#include <iostream>
jpbl's avatar
jpbl committed
32
33
34
35
36
37
38
39
40
41
42
43
44

#include "../global.h"
#include "../manager.h"
#include "codecDescriptor.h"
#include "audiortp.h"
#include "audiolayer.h"
#include "ringbuffer.h"
#include "../user_cfg.h"
#include "../sipcall.h"

////////////////////////////////////////////////////////////////////////////////
// AudioRtp                                                          
////////////////////////////////////////////////////////////////////////////////
Yun Liu's avatar
Yun Liu committed
45
AudioRtp::AudioRtp() :_RTXThread(0), _symmetric(), _threadMutex()
jpbl's avatar
jpbl committed
46
47
48
49
{
}

AudioRtp::~AudioRtp (void) {
50
  delete _RTXThread; _RTXThread = 0;
jpbl's avatar
jpbl committed
51
52
53
}

int 
yanmorin's avatar
   
yanmorin committed
54
AudioRtp::createNewSession (SIPCall *ca) {
55
    ost::MutexLock m(_threadMutex);
56

57
58
59
60
61
62
63
    // something should stop the thread before...
    if ( _RTXThread != 0 ) { 
        _debug("**********************************************************\n");
        _debug("! ARTP Failure: Thread already exists..., stopping it\n");
        _debug("**********************************************************\n");
        delete _RTXThread; _RTXThread = 0;
    }
64
65
66
67
68
69

  // Start RTP Send/Receive threads
  _symmetric = Manager::instance().getConfigInt(SIGNALISATION,SYMMETRIC) ? true : false;
  _RTXThread = new AudioRtpRTX (ca, _symmetric);
  try {
    if (_RTXThread->start() != 0) {
Emmanuel Milou's avatar
Emmanuel Milou committed
70
     _debug("! ARTP Failure: unable to start RTX Thread\n");
71
72
73
74
75
76
77
      return -1;
    }
  } catch(...) {
    _debugException("! ARTP Failure: when trying to start a thread");
    throw;
  }
  return 0;
jpbl's avatar
jpbl committed
78
79
}

80

jpbl's avatar
jpbl committed
81
82
void
AudioRtp::closeRtpSession () {
83
  ost::MutexLock m(_threadMutex);
84
  // This will make RTP threads finish.
85
  _debug("Stopping AudioRTP\n");
86
87
88
89
90
91
  try {
    delete _RTXThread; _RTXThread = 0;
  } catch(...) {
    _debugException("! ARTP Exception: when stopping audiortp\n");
    throw;
  }
Emmanuel Milou's avatar
Emmanuel Milou committed
92
93
  AudioLayer* audiolayer = Manager::instance().getAudioDriver();
  audiolayer->stopStream();
jpbl's avatar
jpbl committed
94
95
96
97
98
}

////////////////////////////////////////////////////////////////////////////////
// AudioRtpRTX Class                                                          //
////////////////////////////////////////////////////////////////////////////////
Yun Liu's avatar
Yun Liu committed
99
100
101
AudioRtpRTX::AudioRtpRTX (SIPCall *sipcall, bool sym) : time(new ost::Time()), _ca(sipcall), _sessionSend(NULL), _sessionRecv(NULL), _session(NULL), _start(), 
		               _sym(sym), micData(NULL), micDataConverted(NULL), micDataEncoded(NULL), spkrDataDecoded(NULL), spkrDataConverted(NULL), 
		               converter(NULL), _layerSampleRate(),_codecSampleRate(), _layerFrameSize(), _audiocodec(NULL)
102
{
103
  setCancel(cancelDefault);
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  // AudioRtpRTX should be close if we change sample rate
  // TODO: Change bind address according to user settings.
  // TODO: this should be the local ip not the external (router) IP
  std::string localipConfig = _ca->getLocalIp(); // _ca->getLocalIp();
  ost::InetHostAddress local_ip(localipConfig.c_str());
  if (!_sym) {
    _sessionRecv = new ost::RTPSession(local_ip, _ca->getLocalAudioPort());
    _sessionSend = new ost::RTPSession(local_ip, _ca->getLocalAudioPort());
    _session = NULL;
  } else {
    _session = new ost::SymmetricRTPSession (local_ip, _ca->getLocalAudioPort());
    _sessionRecv = NULL;
    _sessionSend = NULL;
  }
jpbl's avatar
jpbl committed
118
119
120
}

AudioRtpRTX::~AudioRtpRTX () {
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  _start.wait();

  try {
    this->terminate();
  } catch(...) {
    _debugException("! ARTP: Thread destructor didn't terminate correctly");
    throw;
  }
  _ca = 0;
  if (!_sym) {
    delete _sessionRecv; _sessionRecv = NULL;
    delete _sessionSend; _sessionSend = NULL;
  } else {
    delete _session;     _session = NULL;
  }

137
138
139
  delete [] micData;  micData = NULL;
  delete [] micDataConverted;  micDataConverted = NULL;
  delete [] micDataEncoded;  micDataEncoded = NULL;
140

141
142
  delete [] spkrDataDecoded; spkrDataDecoded = NULL;
  delete [] spkrDataConverted; spkrDataConverted = NULL;
143
144

  delete time; time = NULL;
145
146

  delete converter; converter = NULL;
147
  
jpbl's avatar
jpbl committed
148
149
}

150
  void
151
AudioRtpRTX::initBuffers()
jpbl's avatar
jpbl committed
152
{
153
  converter = new SamplerateConverter( _layerSampleRate , _layerFrameSize );
154

155
  int nbSamplesMax = (int) (_layerSampleRate * _layerFrameSize /1000);
156
157
158
159
160
161
162

  micData = new SFLDataFormat[nbSamplesMax];
  micDataConverted = new SFLDataFormat[nbSamplesMax];
  micDataEncoded = new unsigned char[nbSamplesMax];

  spkrDataConverted = new SFLDataFormat[nbSamplesMax];
  spkrDataDecoded = new SFLDataFormat[nbSamplesMax];
jpbl's avatar
jpbl committed
163
164
}

165
  void
166
167
AudioRtpRTX::initAudioRtpSession (void) 
{
168
169
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
200
201
202
203
204
205
206
207
208
  try {
    if (_ca == 0) { return; }
    _audiocodec = Manager::instance().getCodecDescriptorMap().getCodec( _ca->getAudioCodec() );
    _codecSampleRate = _audiocodec->getClockRate();	

    _debug("Init audio RTP session\n");
    ost::InetHostAddress remote_ip(_ca->getRemoteIp().c_str());
    if (!remote_ip) {
      _debug("! ARTP Thread Error: Target IP address [%s] is not correct!\n", _ca->getRemoteIp().data());
      return;
    }

    // Initialization
    if (!_sym) {
      _sessionRecv->setSchedulingTimeout (10000);
      _sessionRecv->setExpireTimeout(1000000);

      _sessionSend->setSchedulingTimeout(10000);
      _sessionSend->setExpireTimeout(1000000);
    } else {
      _session->setSchedulingTimeout(10000);
      _session->setExpireTimeout(1000000);
    }

    if (!_sym) {
      if ( !_sessionRecv->addDestination(remote_ip, (unsigned short) _ca->getRemoteAudioPort()) ) {
	_debug("AudioRTP Thread Error: could not connect to port %d\n",  _ca->getRemoteAudioPort());
	return;
      }
      if (!_sessionSend->addDestination (remote_ip, (unsigned short) _ca->getRemoteAudioPort())) {
	_debug("! ARTP Thread Error: could not connect to port %d\n",  _ca->getRemoteAudioPort());
	return;
      }

      bool payloadIsSet = false;
      if (_audiocodec) {
	if (_audiocodec->hasDynamicPayload()) {
	  payloadIsSet = _sessionRecv->setPayloadFormat(ost::DynamicPayloadFormat((ost::PayloadType) _audiocodec->getPayload(), _audiocodec->getClockRate()));
	} else {
	  payloadIsSet= _sessionRecv->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) _audiocodec->getPayload()));
	  payloadIsSet = _sessionSend->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) _audiocodec->getPayload()));
209
	}
210
211
212
      }
      _sessionSend->setMark(true);
    } else {
213

214
      //_debug("AudioRTP Thread: Added session destination %s\n", remote_ip.getHostname() );
215

216
217
218
      if (!_session->addDestination (remote_ip, (unsigned short) _ca->getRemoteAudioPort())) {
	return;
      }
219

220
221
222
223
224
225
226
227
228
229
230
231
232
      bool payloadIsSet = false;
      if (_audiocodec) {
	if (_audiocodec->hasDynamicPayload()) {
	  payloadIsSet = _session->setPayloadFormat(ost::DynamicPayloadFormat((ost::PayloadType) _audiocodec->getPayload(), _audiocodec->getClockRate()));
	} else {
	  payloadIsSet = _session->setPayloadFormat(ost::StaticPayloadFormat((ost::StaticPayloadType) _audiocodec->getPayload()));
	}
      }
    }
  } catch(...) {
    _debugException("! ARTP Failure: initialisation failed");
    throw;
  }
Emmanuel Milou's avatar
Emmanuel Milou committed
233
234
}

235
  void
236
237
AudioRtpRTX::sendSessionFromMic(int timestamp)
{
238
239
240
241
242
  // STEP:
  //   1. get data from mic
  //   2. convert it to int16 - good sample, good rate
  //   3. encode it
  //   4. send it
Emmanuel Milou's avatar
Emmanuel Milou committed
243
  //try {
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259

    timestamp += time->getSecond();
    if (_ca==0) { _debug(" !ARTP: No call associated (mic)\n"); return; } // no call, so we do nothing
    AudioLayer* audiolayer = Manager::instance().getAudioDriver();
    if (!audiolayer) { _debug(" !ARTP: No audiolayer available for mic\n"); return; }

    if (!_audiocodec) { _debug(" !ARTP: No audiocodec available for mic\n"); return; }

    // we have to get 20ms of data from the mic *20/1000 = /50
    int maxBytesToGet = _layerSampleRate * _layerFrameSize * sizeof(SFLDataFormat) / 1000;
    // available bytes inside ringbuffer
    int availBytesFromMic = audiolayer->canGetMic();

    // take the lowest
    int bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet;
    // Get bytes from micRingBuffer to data_from_mic
260
261
    //_debug("get data from mic\n");
    int nbSample = audiolayer->getMic( micData , bytesAvail ) / sizeof(SFLDataFormat);
262
263
264
    int nb_sample_up = nbSample;
    int nbSamplesMax = _layerFrameSize * _audiocodec->getClockRate() / 1000;

Emmanuel Milou's avatar
Emmanuel Milou committed
265
    //_debug("resample data = %i\n", nb_sample_up);
266
267
268
269
    nbSample = reSampleData(_audiocodec->getClockRate(), nb_sample_up, DOWN_SAMPLING);	

    if ( nbSample < nbSamplesMax - 10 ) { // if only 10 is missing, it's ok
      // fill end with 0...
270
      memset( micDataConverted + nbSample, 0, (nbSamplesMax-nbSample)*sizeof(int16));
271
272
      nbSample = nbSamplesMax;
    }
273
    int compSize = _audiocodec->codecEncode( micDataEncoded , micDataConverted , nbSample*sizeof(int16));
274
275
276
277
    // encode divise by two
    // Send encoded audio sample over the network
    if (compSize > nbSamplesMax) { _debug("! ARTP: %d should be %d\n", compSize, nbSamplesMax);}
    if (!_sym) {
278
      _sessionSend->putData(timestamp, micDataEncoded, compSize);
279
    } else {
280
      _session->putData(timestamp, micDataEncoded, compSize);
281
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
282
  /*} catch(...) {
283
284
    _debugException("! ARTP: sending failed");
    throw;
Emmanuel Milou's avatar
Emmanuel Milou committed
285
  }*/
jpbl's avatar
jpbl committed
286
287
}

288
  void
289
AudioRtpRTX::receiveSessionForSpkr (int& countTime)
jpbl's avatar
jpbl committed
290
{
291
292


293
  if (_ca == 0) { return; }
Emmanuel Milou's avatar
Emmanuel Milou committed
294
  //try {
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
    AudioLayer* audiolayer = Manager::instance().getAudioDriver();
    if (!audiolayer) { return; }

    const ost::AppDataUnit* adu = NULL;
    // Get audio data stream

    if (!_sym) {
      adu = _sessionRecv->getData(_sessionRecv->getFirstTimestamp());
    } else {
      adu = _session->getData(_session->getFirstTimestamp());
    }
    if (adu == NULL) {
      //_debug("No RTP audio stream\n");
      return;
    }

Emmanuel Milou's avatar
Emmanuel Milou committed
311
    //int payload = adu->getType(); // codec type
312
    unsigned char* spkrData  = (unsigned char*)adu->getData(); // data in char
313
314
315
    unsigned int size = adu->getSize(); // size in char

    // Decode data with relevant codec
Emmanuel Milou's avatar
Emmanuel Milou committed
316
    unsigned int max = (unsigned int)(_codecSampleRate * _layerFrameSize / 1000);
317
318

    if ( size > max ) {
Emmanuel Milou's avatar
Emmanuel Milou committed
319
      _debug("We have received from RTP a packet larger than expected: %d VS %d\n", size, max);
320
321
322
323
324
325
      _debug("The packet size has been cropped\n");
      size=max;
    }

    if (_audiocodec != NULL) {

326
      int expandedSize = _audiocodec->codecDecode( spkrDataDecoded , spkrData , size );
327
328
329
      //buffer _receiveDataDecoded ----> short int or int16, coded on 2 bytes
      int nbInt16 = expandedSize / sizeof(int16);
      //nbInt16 represents the number of samples we just decoded
Yun Liu's avatar
Yun Liu committed
330
      if ((unsigned int)nbInt16 > max) {
Emmanuel Milou's avatar
Emmanuel Milou committed
331
	_debug("We have decoded an RTP packet larger than expected: %d VS %d. Cropping.\n", nbInt16, max);
332
333
334
335
336
337
338
	nbInt16=max;
      }
      int nbSample = nbInt16;

      // Do sample rate conversion
      int nb_sample_down = nbSample;
      nbSample = reSampleData(_codecSampleRate , nb_sample_down, UP_SAMPLING);
339
340
341
#ifdef DATAFORMAT_IS_FLOAT
#else
#endif
Emmanuel Milou's avatar
Emmanuel Milou committed
342
      
343
344
    //audiolayer->playSamples( spkrDataConverted, nbSample * sizeof(SFLDataFormat), true);
    audiolayer->putMain (spkrDataConverted, nbSample * sizeof(SFLDataFormat));
Emmanuel Milou's avatar
Emmanuel Milou committed
345
      
346

347
348
349
350
351
352
      // Notify (with a beep) an incoming call when there is already a call 
      countTime += time->getSecond();
      if (Manager::instance().incomingCallWaiting() > 0) {
	countTime = countTime % 500; // more often...
	if (countTime == 0) {
	  Manager::instance().notificationIncomingCall();
353
	}
354
      }
355

356
357
358
359
    } else {
      countTime += time->getSecond();
    }
    delete adu; adu = NULL;
Emmanuel Milou's avatar
Emmanuel Milou committed
360
361
362
363
  //} catch(...) {
    //_debugException("! ARTP: receiving failed");
    //throw;
  //}
364

365
}
366

367
  int 
368
369
AudioRtpRTX::reSampleData(int sampleRate_codec, int nbSamples, int status)
{
370
  if(status==UP_SAMPLING){
371
    return converter->upsampleData( spkrDataDecoded , spkrDataConverted , sampleRate_codec , _layerSampleRate , nbSamples );
372
  }
373
  else if(status==DOWN_SAMPLING){
374
    return converter->downsampleData( micData , micDataConverted , sampleRate_codec , _layerSampleRate , nbSamples );
375
376
377
  }
  else
    return 0;
378
379
}

jpbl's avatar
jpbl committed
380
381
void
AudioRtpRTX::run () {
382
383
384
385
386
387
388
389
  //mic, we receive from soundcard in stereo, and we send encoded
  //encoding before sending
  AudioLayer *audiolayer = Manager::instance().getAudioDriver();
  _layerFrameSize = audiolayer->getFrameSize(); // en ms
  _layerSampleRate = audiolayer->getSampleRate();	
  initBuffers();
  int step; 

Emmanuel Milou's avatar
Emmanuel Milou committed
390
  //try {
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
    // Init the session
    initAudioRtpSession();
    step = (int) (_layerFrameSize * _codecSampleRate / 1000);
    // start running the packet queue scheduler.
    //_debug("AudioRTP Thread started\n");
    if (!_sym) {
      _sessionRecv->startRunning();
      _sessionSend->startRunning();
    } else {
      _session->startRunning();
      //_debug("Session is now: %d active\n", _session->isActive());
    }

    int timestamp = 0; // for mic
    int countTime = 0; // for receive
    TimerPort::setTimer(_layerFrameSize);

    audiolayer->startStream();
    _start.post();
    _debug("- ARTP Action: Start\n");
    while (!testCancel()) {
      ////////////////////////////
      // Send session
      ////////////////////////////
      sendSessionFromMic(timestamp);
      timestamp += step;
      ////////////////////////////
      // Recv session
      ////////////////////////////
      receiveSessionForSpkr(countTime);
      // Let's wait for the next transmit cycle
      Thread::sleep(TimerPort::getTimer());
      TimerPort::incTimer(_layerFrameSize); // 'frameSize' ms
    }
    //_debug("stop stream for audiortp loop\n");
    audiolayer->stopStream();
Emmanuel Milou's avatar
Emmanuel Milou committed
427
428
429
430
431
432
433
434
435
436
    _debug("- ARTP Action: Stop\n");
  //} catch(std::exception &e) {
    //_start.post();
    //_debug("! ARTP: Stop %s\n", e.what());
    //throw;
  //} catch(...) {
    //_start.post();
    //_debugException("* ARTP Action: Stop");
    //throw;
  //}
jpbl's avatar
jpbl committed
437
438
439
440
}


// EOF