Select Git revision
audiortp.cpp
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
audiortp.cpp 17.29 KiB
/*
*
* Copyright (C) 2004-2007 Savoir-Faire Linux inc.
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
* Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <cstdio>
#include <cstdlib>
#include <ccrtp/rtp.h>
#include <assert.h>
#include <string>
#include <cstring>
#include <math.h>
#include <dlfcn.h>
#include <iostream>
#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"
#include <samplerate.h>
////////////////////////////////////////////////////////////////////////////////
// AudioRtp
////////////////////////////////////////////////////////////////////////////////
AudioRtp::AudioRtp ()
{
_RTXThread = 0;
}
AudioRtp::~AudioRtp (void) {
delete _RTXThread; _RTXThread = 0;
}
int
AudioRtp::createNewSession (SIPCall *ca) {
ost::MutexLock m(_threadMutex);
// something should stop the thread before...
if ( _RTXThread != 0 ) {
_debug("! ARTP Failure: Thread already exists..., stopping it\n");
delete _RTXThread; _RTXThread = 0;
//return -1;
}
// Start RTP Send/Receive threads
_symmetric = Manager::instance().getConfigInt(SIGNALISATION,SYMMETRIC) ? true : false;
_RTXThread = new AudioRtpRTX (ca, _symmetric);
try {
if (_RTXThread->start() != 0) {
_debug("! ARTP Failure: unable to start RTX Thread\n");
return -1;
}
} catch(...) {
_debugException("! ARTP Failure: when trying to start a thread");
throw;
}
return 0;
}
void
AudioRtp::closeRtpSession () {
ost::MutexLock m(_threadMutex);
// This will make RTP threads finish.
// _debug("Stopping AudioRTP\n");
try {
delete _RTXThread; _RTXThread = 0;
} catch(...) {
_debugException("! ARTP Exception: when stopping audiortp\n");
throw;
}
}
////////////////////////////////////////////////////////////////////////////////
// AudioRtpRTX Class //
////////////////////////////////////////////////////////////////////////////////
AudioRtpRTX::AudioRtpRTX (SIPCall *sipcall, bool sym)
: _fstream("/tmp/audio.gsm", std::ofstream::binary|std::ios::out|std::ios::app)
{
setCancel(cancelDeferred);
time = new ost::Time();
_ca = sipcall;
_sym = sym;
// AudioRtpRTX should be close if we change sample rate
//_codecSampleRate = _ca->getAudioCodec()->getClockRate();
// 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;
}
// libsamplerate-related
// Set the converter type for the upsampling and the downsampling
// interpolator SRC_SINC_BEST_QUALITY
_src_state_mic = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err);
_src_state_spkr = src_new(SRC_SINC_BEST_QUALITY, 1, &_src_err);
}
AudioRtpRTX::~AudioRtpRTX () {
_start.wait();
try {
this->terminate();
} catch(...) {
_debugException("! ARTP: Thread destructor didn't terminate correctly");
throw;
}
//_debug("terminate audiortprtx ended...\n");
_ca = 0;
if (!_sym) {
delete _sessionRecv; _sessionRecv = NULL;
delete _sessionSend; _sessionSend = NULL;
} else {
delete _session; _session = NULL;
}
delete [] _intBufferDown; _intBufferDown = NULL;
delete [] _floatBufferUp; _floatBufferUp = NULL;
delete [] _floatBufferDown; _floatBufferDown = NULL;
delete [] _dataAudioLayer; _dataAudioLayer = NULL;
delete [] _sendDataEncoded; _sendDataEncoded = NULL;
delete [] _receiveDataDecoded; _receiveDataDecoded = NULL;
delete time; time = NULL;
// libsamplerate-related
_src_state_mic = src_delete(_src_state_mic);
_src_state_spkr = src_delete(_src_state_spkr);
}
void
AudioRtpRTX::initBuffers()
{
int nbSamplesMax = (int) (_layerSampleRate * _layerFrameSize /1000);
_dataAudioLayer = new SFLDataFormat[nbSamplesMax];
_receiveDataDecoded = new int16[nbSamplesMax];
_floatBufferDown = new float32[nbSamplesMax];
_floatBufferUp = new float32[nbSamplesMax];
_sendDataEncoded = new unsigned char[nbSamplesMax];
_intBufferDown = new int16[nbSamplesMax];
}
void
AudioRtpRTX::initAudioRtpSession (void)
{
try {
if (_ca == 0) { return; }
_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()));
}
}
_sessionSend->setMark(true);
} else {
//_debug("AudioRTP Thread: Added session destination %s\n", remote_ip.getHostname() );
if (!_session->addDestination (remote_ip, (unsigned short) _ca->getRemoteAudioPort())) {
return;
}
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;
}
}
void
AudioRtpRTX::loadCodec(int payload)
{
using std::cerr;
switch(payload){
case 0:
handle_codec = dlopen( CODECS_DIR "/libcodec_ulaw.so", RTLD_LAZY);
break;
case 3:
handle_codec = dlopen(CODECS_DIR "/libcodec_gsm.so", RTLD_LAZY);
break;
case 8:
handle_codec = dlopen(CODECS_DIR "/libcodec_alaw.so", RTLD_LAZY);
break;
case 97:
handle_codec = dlopen(CODECS_DIR "/libcodec_ilbc.so", RTLD_LAZY);
break;
case 110:
handle_codec = dlopen(CODECS_DIR "/libcodec_speex.so", RTLD_LAZY);
break;
}
if(!handle_codec){
cerr<<"cannot load library: "<< dlerror() <<'\n';
}
dlerror();
create_t* create_codec = (create_t*)dlsym(handle_codec, "create");
const char* dlsym_error = dlerror();
if(dlsym_error){
cerr << "Cannot load symbol create: " << dlsym_error << '\n';
}
_audiocodec = create_codec();
}
void
AudioRtpRTX::unloadCodec()
{
using std::cerr;
destroy_t* destroy_codec = (destroy_t*)dlsym(handle_codec, "destroy");
const char* dlsym_error = dlerror();
if(dlsym_error){
cerr << "Cannot load symbol destroy" << dlsym_error << '\n';
}
destroy_codec(_audiocodec);
dlclose(handle_codec);
}
void
AudioRtpRTX::sendSessionFromMic(int timestamp)
{
// STEP:
// 1. get data from mic
// 2. convert it to int16 - good sample, good rate
// 3. encode it
// 4. send it
try {
int16* toSIP = NULL;
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
int nbSample = audiolayer->getMic(_dataAudioLayer, bytesAvail) / sizeof(SFLDataFormat);
int nb_sample_up = nbSample;
int nbSamplesMax = _layerFrameSize * _audiocodec->getClockRate() / 1000;
nbSample = reSampleData(_audiocodec->getClockRate(), nb_sample_up, DOWN_SAMPLING);
toSIP = _intBufferDown;
if ( nbSample < nbSamplesMax - 10 ) { // if only 10 is missing, it's ok
// fill end with 0...
//_debug("begin: %p, nbSample: %d\n", toSIP, nbSample);
memset(toSIP + nbSample, 0, (nbSamplesMax-nbSample)*sizeof(int16));
nbSample = nbSamplesMax;
}
// debug - dump sound in a file
//_debug("AR: Nb sample: %d int, [0]=%d [1]=%d [2]=%d\n", nbSample, toSIP[0], toSIP[1], toSIP[2]);
// for the mono: range = 0 to RTP_FRAME2SEND * sizeof(int16)
// codecEncode(char *dest, int16* src, size in bytes of the src)
int compSize = _audiocodec->codecEncode(_sendDataEncoded, toSIP, nbSample*sizeof(int16));
//printf("jusqu'ici tout vas bien\n");
// 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) {
_sessionSend->putData(timestamp, _sendDataEncoded, compSize);
} else {
_session->putData(timestamp, _sendDataEncoded, compSize);
}
toSIP = NULL;
} catch(...) {
_debugException("! ARTP: sending failed");
throw;
}
}
void
AudioRtpRTX::receiveSessionForSpkr (int& countTime)
{
if (_ca == 0) { return; }
try {
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;
}
int payload = adu->getType(); // codec type
unsigned char* data = (unsigned char*)adu->getData(); // data in char
unsigned int size = adu->getSize(); // size in char
//_fstream.write((char*) data, size);
// Decode data with relevant codec
int max = (int)(_codecSampleRate * _layerFrameSize / 1000);
if ( size > max ) {
_debug("We have received from RTP a packet larger than expected: %s VS %s\n", size, max);
_debug("The packet size has been cropped\n");
size=max;
}
//printf("size = %i\n", size);
if (_audiocodec != NULL) {
int expandedSize = _audiocodec->codecDecode(_receiveDataDecoded, data, size);
// printf("%i\n", expandedSize);
//_fstream.write((char*) _receiveDataDecoded, );
//buffer _receiveDataDecoded ----> short int or int16, coded on 2 bytes
int nbInt16 = expandedSize / sizeof(int16);
//nbInt16 represents the number of samples we just decoded
if (nbInt16 > max) {
_debug("We have decoded an RTP packet larger than expected: %s VS %s. Cropping.\n", nbInt16, max);
nbInt16=max;
}
SFLDataFormat* toAudioLayer;
int nbSample = nbInt16;
// Do sample rate conversion
int nb_sample_down = nbSample;
nbSample = reSampleData(_codecSampleRate , nb_sample_down, UP_SAMPLING);
#ifdef DATAFORMAT_IS_FLOAT
toAudioLayer = _floatBufferUp;
#else
toAudioLayer = _dataAudioLayer;
#endif
audiolayer->putMain(toAudioLayer, nbSample * sizeof(SFLDataFormat));
// 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();
}
}
} else {
countTime += time->getSecond();
}
delete adu; adu = NULL;
} catch(...) {
_debugException("! ARTP: receiving failed");
throw;
}
}
int
AudioRtpRTX::reSampleData(int sampleRate_codec, int nbSamples, int status)
{
if(status==UP_SAMPLING)
return upSampleData(sampleRate_codec, nbSamples);
else if(status==DOWN_SAMPLING)
return downSampleData(sampleRate_codec, nbSamples);
else
return 0;
}
////////////////////////////////////////////////////////////////////
//////////// RESAMPLING FUNCTIONS /////////////////////////////////
//////////////////////////////////////////////////////////////////
int
AudioRtpRTX::upSampleData(int sampleRate_codec, int nbSamples)
{
double upsampleFactor = (double) _layerSampleRate / sampleRate_codec;
int nbSamplesMax = (int) (_layerSampleRate * _layerFrameSize /1000);
if( upsampleFactor != 1 )
{
SRC_DATA src_data;
src_data.data_in = _floatBufferDown;
src_data.data_out = _floatBufferUp;
src_data.input_frames = nbSamples;
src_data.output_frames = (int) floor(upsampleFactor * nbSamples);
src_data.src_ratio = upsampleFactor;
src_data.end_of_input = 0; // More data will come
src_short_to_float_array(_receiveDataDecoded, _floatBufferDown, nbSamples);
src_process(_src_state_spkr, &src_data);
nbSamples = ( src_data.output_frames_gen > nbSamplesMax) ? nbSamplesMax : src_data.output_frames_gen;
src_float_to_short_array(_floatBufferUp, _dataAudioLayer, nbSamples);
}
return nbSamples;
}
int
AudioRtpRTX::downSampleData(int sampleRate_codec, int nbSamples)
{
double downsampleFactor = (double) sampleRate_codec / _layerSampleRate;
int nbSamplesMax = (int) (sampleRate_codec * _layerFrameSize / 1000);
if ( downsampleFactor != 1)
{
SRC_DATA src_data;
src_data.data_in = _floatBufferUp;
src_data.data_out = _floatBufferDown;
src_data.input_frames = nbSamples;
src_data.output_frames = (int) floor(downsampleFactor * nbSamples);
src_data.src_ratio = downsampleFactor;
src_data.end_of_input = 0; // More data will come
src_short_to_float_array(_dataAudioLayer, _floatBufferUp, nbSamples);
src_process(_src_state_mic, &src_data);
nbSamples = ( src_data.output_frames_gen > nbSamplesMax) ? nbSamplesMax : src_data.output_frames_gen;
src_float_to_short_array(_floatBufferDown, _intBufferDown, nbSamples);
}
return nbSamples;
}
//////////////////////// END RESAMPLING //////////////////////////////////////////////////////
void
AudioRtpRTX::run () {
//mic, we receive from soundcard in stereo, and we send encoded
//encoding before sending
AudioLayer *audiolayer = Manager::instance().getAudioDriver();
loadCodec(_ca->getAudioCodec());
_layerFrameSize = audiolayer->getFrameSize(); // en ms
_layerSampleRate = audiolayer->getSampleRate();
initBuffers();
int step;
try {
// 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->flushMic();
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
}
_fstream.close();
unloadCodec();
//_debug("stop stream for audiortp loop\n");
audiolayer->stopStream();
} catch(std::exception &e) {
_start.post();
_debug("! ARTP: Stop %s\n", e.what());
throw;
} catch(...) {
_start.post();
_debugException("* ARTP Action: Stop");
throw;
}
}
// EOF