diff --git a/sflphone-common/src/audio/audiortp/AudioRtpFactory.cpp b/sflphone-common/src/audio/audiortp/AudioRtpFactory.cpp index 41f572972ad7f06aa09f9aeacb2d7ca50bdc0948..d4e649ca77b6c137ef5a5f2674bf0ee0fbb9535b 100644 --- a/sflphone-common/src/audio/audiortp/AudioRtpFactory.cpp +++ b/sflphone-common/src/audio/audiortp/AudioRtpFactory.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc. * Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@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 diff --git a/sflphone-common/src/audio/audiortp/AudioRtpFactory.h b/sflphone-common/src/audio/audiortp/AudioRtpFactory.h index b5183d67b2a509957423d08a41549e6af32ae672..a88cfe2a3a128637b56a36d22535f854f4ed68c1 100644 --- a/sflphone-common/src/audio/audiortp/AudioRtpFactory.h +++ b/sflphone-common/src/audio/audiortp/AudioRtpFactory.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc. * Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@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 diff --git a/sflphone-common/src/audio/audiortp/AudioRtpRecordHandler.cpp b/sflphone-common/src/audio/audiortp/AudioRtpRecordHandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d783b894e0f7f9850635c3454ad688ec896a7516 --- /dev/null +++ b/sflphone-common/src/audio/audiortp/AudioRtpRecordHandler.cpp @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc. + * Author: Alexandre Savard <alexandre.savard@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. + * + * 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. + */ + +#include "AudioRtpRecordHandler.h" + +namespace sfl +{ + +static const SFLDataFormat initFadeinFactor = 32000; + +AudioRtpRecord::AudioRtpRecord() : _audioCodec(NULL) + , _hasDynamicPayloadType(false) + , _micData(NULL) + , _micDataConverted(NULL) + , _micDataEncoded(NULL) + , _spkrDataDecoded(NULL) + , _spkrDataConverted(NULL) + , _converter(NULL) + , _audioLayerSampleRate(0) + , _codecSampleRate(0) + , _audioLayerFrameSize(0) + , _codecFrameSize(0) + , _converterSamplingRate(0) + , _micFadeInComplete(false) + , _spkrFadeInComplete(false) + , _micAmplFactor(initFadeinFactor) + , _spkrAmplFactor(initFadeinFactor) + , _audioProcess(NULL) + , _noiseSuppress(NULL) +{ + _audioLayerFrameSize = _manager->getAudioDriver()->getFrameSize(); // in ms + _audioLayerSampleRate = _manager->getAudioDriver()->getSampleRate(); +} + + +AudioRtpRecord::~AudioRtpRecord() +{ + if (_micData) + delete [] _micData; + _micData = NULL; + + if (_micDataConverted) + delete [] _micDataConverted; + _micDataConverted = NULL; + + if (_micDataEncoded) + delete [] _micDataEncoded; + _micDataEncoded = NULL; + + if (_spkrDataDecoded) + delete [] _spkrDataDecoded; + _spkrDataDecoded = NULL; + + if (_spkrDataConverted) + delete [] _spkrDataConverted; + _spkrDataConverted = NULL; + + + if(_converter) + delete _converter; + _converter = NULL; + + if (_audioCodec) { + delete _audioCodec; + _audioCodec = NULL; + } + + if (_audioProcess) { + delete _audioProcess; + _audioProcess = NULL; + } + + if (_noiseSuppress) { + delete _noiseSuppress; + _noiseSuppress = NULL; + } +} + +AudioCodec *AudioRtpRecord::getAudiocodec() const +{ + return _audioCodec; +} + +int AudioRtpRecord::getCodecPayloadType() const +{ + return _codecPayloadType; +} + +bool AudioRtpRecord::getHasDynamicPayload() const +{ + return _hasDynamicPayloadType; +} + +int AudioRtpRecord::getAudioLayerFrameSize() const +{ + return _audioLayerFrameSize; +} + +int AudioRtpRecord::getAudioLayerSampleRate() const +{ + return _audioLayerSampleRate; +} + +int AudioRtpRecord::getCodecFrameSize() const +{ + return _codecFrameSize; +} + +int AudioRtpRecord::getCodecSampleRate() const +{ + return _codecSampleRate; +} + +SamplerateConverter *AudioRtpRecord::getConverter() const +{ + return _converter; +} + +int AudioRtpRecord::getConverterSamplingRate() const +{ + return _converterSamplingRate; +} + +EventQueue *AudioRtpRecord::getEventQueue() const +{ + return &_eventQueue; +} + +int AudioRtpRecord::getEventQueueSize() const +{ + return _eventQueue.size(); +} + +unsigned char *getEncodedData() const +{ + return _micDataEncoded; +} + +void AudioRtpRecord::setAudiocodec(AudioCodec *audiocodec) +{ + this->_audiocodec = audiocodec; +} + +void AudioRtpRecord::setCodecPayloadType(int codecPayloadType) +{ + this->codecPayloadType = codecPayloadType; +} + +bool AudioRtpRecord::setHasDynamicPayload(bool hasDynamicPayload) +{ + _hasDynamicPayloadType = hasDynamicPayload; +} + +void AudioRtpRecord::setAudioLayerFrameSize(int _audioLayerFrameSize) +{ + this->_audioLayerFrameSize = _audioLayerFrameSize; +} + +void AudioRtpRecord::setAudioLayerSampleRate(int _audioLayerSampleRate) +{ + this->_audioLayerSampleRate = _audioLayerSampleRate; +} + +void AudioRtpRecord::setCodecFrameSize(int _codecFrameSize) +{ + this->_codecFrameSize = _codecFrameSize; +} + +void AudioRtpRecord::setCodecSampleRate(int _codecSampleRate) +{ + this->_codecSampleRate = _codecSampleRate; +} + +void AudioRtpRecord::setConverter(SamplerateConverter *_converter) +{ + this->_converter = _converter; +} + +void AudioRtpRecord::setConverterSamplingRate(int _converterSamplingRate) +{ + this->_converterSamplingRate = _converterSamplingRate; +} + +void AudioRtpRecord::setEventQueue(EventQueue _eventQueue) +{ + this->_eventQueue = _eventQueue; +} + + +AudioRtpRecordHandler::AudioRtpRecordHandler() { + // TODO Auto-generated constructor stub + +} + + +AudioRtpRecordHandler::~AudioRtpRecordHandler() { + // TODO Auto-generated destructor stub +} + +void AudioRtpRecord::SetRtpMedia(AudioCodec* audioCodec) { + + // Set varios codec info to reduce indirection + _audioRtpRecord.setAudioCodec(audioCodec); + _audioRtpRecord.setCodecPayloadType(audioCodec->getPayloadType()); + _audioRtpRecord.setCodecSampleRate(audioCodec->getClockRate()); + _audioRtpRecord.setCodecFrameSize(audioCodec->getFrameSize()); + _audioRtpRecord.setHasDynamicPayload(audioCodec->hasDynamicPayload()); +} + + +void AudioRtpRecord::init() +{ + // init noise reduction process + // _noiseSuppress = new NoiseSuppress (getCodecFrameSize(), getCodecSampleRate()); + // _audioProcess = new AudioProcessing (_noiseSuppress); + + // _audioRtpRecord._noiseSuppress + // _audioRtpRecord. +} + +void AudioRtpRecord::initBuffers() +{ + // Set sampling rate, main buffer choose the highest one + _manager->getAudioDriver()->getMainBuffer()->setInternalSamplingRate (_codecSampleRate); + + // may be different than one already set + _converterSamplingRate = _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); + + // initialize SampleRate converter using AudioLayer's sampling rate + // (internal buffers initialized with maximal sampling rate and frame size) + _converter = new SamplerateConverter (_layerSampleRate, _layerFrameSize); + + int nbSamplesMax = (int) (_codecSampleRate * _layerFrameSize /1000) *2; + _micData = new SFLDataFormat[nbSamplesMax]; + _micDataConverted = new SFLDataFormat[nbSamplesMax]; + _micDataEncoded = new unsigned char[nbSamplesMax*2]; + _spkrDataConverted = new SFLDataFormat[nbSamplesMax]; + _spkrDataDecoded = new SFLDataFormat[nbSamplesMax]; + + + memset (_micData, 0, nbSamplesMax*sizeof (SFLDataFormat)); + memset (_micDataConverted, 0, nbSamplesMax*sizeof (SFLDataFormat)); + memset (_micDataEncoded, 0, nbSamplesMax*2); + memset (_spkrDataConverted, 0, nbSamplesMax*sizeof (SFLDataFormat)); + memset (_spkrDataDecoded, 0, nbSamplesMax*sizeof (SFLDataFormat)); + + _manager->addStream (_ca->getCallId()); +} + + +void AudioRtpRecordHandler::putDtmfEvent (int digit) +{ + + sfl::DtmfEvent *dtmf = new sfl::DtmfEvent(); + + dtmf->payload.event = digit; + dtmf->payload.ebit = false; // end of event bit + dtmf->payload.rbit = false; // reserved bit + dtmf->payload.duration = 1; // duration for this event + dtmf->newevent = true; + dtmf->length = 1000; + + _eventQueue.push_back (dtmf); + + _debug ("AudioRtpSession: Put Dtmf Event %d", _eventQueue.size()); +} + +int AudioRtpRecordHandler::processDataEncode (void) +{ + assert (_audiocodec); + assert (_audiolayer); + + int _mainBufferSampleRate = _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); + + // compute codec framesize in ms + float fixed_codec_framesize = computeCodecFrameSize (_audiocodec->getFrameSize(), _audiocodec->getClockRate()); + + // compute nb of byte to get coresponding to 20 ms at audio layer frame size (44.1 khz) + int maxBytesToGet = computeNbByteAudioLayer (fixed_codec_framesize); + + // available bytes inside ringbuffer + int availBytesFromMic = _manager->getAudioDriver()->getMainBuffer()->availForGet (_ca->getCallId()); + + // set available byte to maxByteToGet + int bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet; + + if (bytesAvail == 0) { + memset (_micDataEncoded, 0, sizeof (SFLDataFormat)); + return _audiocodec->getFrameSize(); + } + + // Get bytes from micRingBuffer to data_from_mic + int nbSample = _manager->getAudioDriver()->getMainBuffer()->getData (_micData , bytesAvail, 100, _ca->getCallId()) / sizeof (SFLDataFormat); + + if (!_micFadeInComplete) + _micFadeInComplete = fadeIn (_micData, nbSample, &_micAmplFactor); + + if (nbSample == 0) + return nbSample; + + // nb bytes to be sent over RTP + int compSize = 0; + + // test if resampling is required + if (_audiocodec->getClockRate() != _mainBufferSampleRate) { + int nb_sample_up = nbSample; + + _nSamplesMic = nbSample; + + nbSample = _converter->downsampleData (_micData , _micDataConverted , _audiocodec->getClockRate(), _mainBufferSampleRate, nb_sample_up); + + if (_manager->audioPreference.getNoiseReduce()) + _audioProcess->processAudio (_micDataConverted, nbSample*sizeof (SFLDataFormat)); + + compSize = _audiocodec->codecEncode (_micDataEncoded, _micDataConverted, nbSample*sizeof (SFLDataFormat)); + + } else { + + _nSamplesMic = nbSample; + + if (_manager->audioPreference.getNoiseReduce()) + _audioProcess->processAudio (_micData, nbSample*sizeof (SFLDataFormat)); + + // no resampling required + compSize = _audiocodec->codecEncode (_micDataEncoded, _micData, nbSample*sizeof (SFLDataFormat)); + + } + + return compSize; +} + +void AudioRtpRecordHandler::processDataDecode (unsigned char * spkrData, unsigned int size) +{ + if (_audiocodec != NULL) { + + + int _mainBufferSampleRate = _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); + + // Return the size of data in bytes + int expandedSize = _audiocodec->codecDecode (_spkrDataDecoded , spkrData , size); + + // buffer _receiveDataDecoded ----> short int or int16, coded on 2 bytes + int nbSample = expandedSize / sizeof (SFLDataFormat); + + if (!_spkrFadeInComplete) + _spkrFadeInComplete = fadeIn (_spkrDataDecoded, nbSample, &_spkrAmplFactor); + + // test if resampling is required + if (_audiocodec->getClockRate() != _mainBufferSampleRate) { + + // Do sample rate conversion + int nb_sample_down = nbSample; + + nbSample = _converter->upsampleData (_spkrDataDecoded, _spkrDataConverted, _codecSampleRate, _mainBufferSampleRate, nb_sample_down); + + // Store the number of samples for recording + _nSamplesSpkr = nbSample; + + // put data in audio layer, size in byte + _manager->getAudioDriver()->getMainBuffer()->putData (_spkrDataConverted, nbSample * sizeof (SFLDataFormat), 100, _ca->getCallId()); + + + } else { + // Store the number of samples for recording + _nSamplesSpkr = nbSample; + + // put data in audio layer, size in byte + _manager->getAudioDriver()->getMainBuffer()->putData (_spkrDataDecoded, expandedSize, 100, _ca->getCallId()); + } + + // Notify (with a beep) an incoming call when there is already a call + if (_manager->incomingCallWaiting() > 0) { + _countNotificationTime += _time->getSecond(); + int countTimeModulo = _countNotificationTime % 5000; + + // _debug("countNotificationTime: %d\n", countNotificationTime); + // _debug("countTimeModulo: %d\n", countTimeModulo); + if ( (countTimeModulo - _countNotificationTime) < 0) { + _manager->notificationIncomingCall(); + } + + _countNotificationTime = countTimeModulo; + } + + } +} + + +bool AudioRtpRecordHandler::fadeIn (SFLDataFormat *audio, int size, SFLDataFormat *factor) +{ + + // apply amplitude factor; + while (size) { + size--; + audio[size] /= *factor; + } + + // decrease factor + *factor /= FADEIN_STEP_SIZE; + + // if factor reach 0, thsi function should no be called anymore + if (*factor == 0) + return true; + + return false; +} + + + +} + diff --git a/sflphone-common/src/audio/audiortp/AudioRtpRecordHandler.h b/sflphone-common/src/audio/audiortp/AudioRtpRecordHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..76d63ccce3cd99a76131eb60ce5099005c64e85d --- /dev/null +++ b/sflphone-common/src/audio/audiortp/AudioRtpRecordHandler.h @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc. + * Author: Alexandre Savard <alexandre.savard@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. + * + * 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. + */ + +#ifndef AUDIORTPRECORDHANDLER_H_ +#define AUDIORTPRECORDHANDLER_H_ + +#include "audio/codecs/audiocodec.h" +#include "audio/samplerateconverter.h" +#include "audio/audioprocessing.h" +#include "audio/noisesuppress.h" + +namespace sfl +{ + +typedef struct DtmfEvent { + ost::RTPPacket::RFC2833Payload payload; + int length; + bool newevent; +} DtmfEvent; + +typedef list<DtmfEvent *> EventQueue; + +/** + * Class meant to store internal data in order to encode/decode, + * resample, process, and packetize audio streams. This class should not be + * handled directly. Use AudioRtpRecorrdHandeler + */ +class AudioRtpRecord { +public: + AudioRtpRecord(); + virtual ~AudioRtpRecord(); + + /** + * Return a pointer to the current codec instance + */ + inline AudioCodec *getAudioCodec() const; + + /** + * Get current payload type (static or dynamic) + */ + int getCodecPayloadType() const; + + /** + * Return wether or not this codec has dynamic payload to be + * negotiated during SDP session + */ + bool getHasDynamicPayload() const; + + /** + * Get current audio layer frame size + */ + int getAudioLayerFrameSize() const; + + /** + * Get current audio layer sampling rate + */ + int getAudioLayerSampleRate() const; + + /** + * Get current codec frame size + */ + int getCodecFrameSize() const; + + /** + * Return current sample rate + */ + int getCodecSampleRate() const; + + /** + * Get the sampling rate converter for this session + */ + SamplerateConverter *getConverter() const; + + /** + * Get sampling rate converter's sampling rate + */ + inline int getConverterSamplingRate() const; + + /** + * Return a poiner to the DTMF event queue + */ + EventQueue *getEventQueue() const; + + /** + * Return the number of DTMF event waiting in the queue + */ + int getEventQueueSize() const; + + unsigned char *getEncodedData() const; + + /** + * Return a pointer to the current codec instance + */ + void setAudioCodec(AudioCodec *audioCodec); + + /** + * Set current codec payload (static or dynamic) + */ + void setCodecPayloadType(int codecPayloadType); + + /** + * Set audio layer frame size + */ + void setAudioLayerFrameSize(int _audioLayerFrameSize); + + /** + * Set current audio layer sampling rate + * used to process sampling rate conversion + */ + void setAudioLayerSampleRate(int _audioLayerSampleRate); + + /** + * Set codec frame size used to compute sampling rate conversion + */ + void setCodecFrameSize(int _codecFrameSize); + + /** + * Set codec sampling rate used to compute sampling rate conversion + */ + void setCodecSampleRate(int _codecSampleRate); + + /** + * Set sampling rate converter for this session + */ + void setConverter(SamplerateConverter *_converter); + + /** + * Set converter sampling rate used to compute conversion buffer size + */ + void setConverterSamplingRate(int _converterSamplingRate); + +private: + /** + * Pointer to the session's codec + */ + AudioCodec * _audioCodec; + + /** + * Codec payload type + */ + int _codecPayloadType; + + /** + * Dynamic payload are negotiated during sdp session while static payload + * are predefined numbers identifying the codec + */ + bool _hasDynamicPayloadType; + + /** + * Mic-data related buffers + */ + SFLDataFormat* _micData; + SFLDataFormat* _micDataConverted; + unsigned char* _micDataEncoded; + + /** + * Speaker-data related buffers + */ + SFLDataFormat* _spkrDataDecoded; + SFLDataFormat* _spkrDataConverted; + + /** + * Sample rate converter object + */ + SamplerateConverter * _converter; + + /** + * Variables to process audio stream: sample rate for playing sound (typically 44100HZ) + */ + int _audioLayerSampleRate; + + /** + * Sample rate of the codec we use to encode and decode (most of time 8000HZ) + */ + int _codecSampleRate; + + /** + * Length of the sound frame we capture in ms (typically 20ms) + */ + int _audioLayerFrameSize; + + /** + * Codecs frame size in samples (20 ms => 882 at 44.1kHz) + * The exact value is stored in the codec + */ + int _codecFrameSize; + + /** + * Sampling rate of audio converter + */ + int _converterSamplingRate; + + /** + * EventQueue used to store list of DTMF- + */ + EventQueue _eventQueue; + + /** + * State of mic fade in + */ + bool _micFadeInComplete; + + /** + * State of spkr fade in + */ + bool _spkrFadeInComplete; + + /** + * Ampliturde factor to fade in mic data + */ + SFLDataFormat _micAmplFactor; + + /** + * Amplitude factor to fade in spkr data + */ + SFLDataFormat _spkrAmplFactor; + + /** + * Audio process containing noise reduction engine + */ + AudioProcessing *_audioProcess; + + /** + * Noise reduction engine + */ + NoiseSuppress *_noiseSuppress; + +}; + + +class AudioRtpRecordHandler { +public: + AudioRtpRecordHandler(); + virtual ~AudioRtpRecordHandler(); + + /** + * Set rtp media for this session + */ + void setRtpMedia(AudioCodec* audioCodec); + + int getCodecPayloadType(void) { return _audioRtpRecord.getCodecPayloadType(); } + + int getCodecSampleRate(void) { return _audioRtpRecord.getCodecSampleRate(); } + + int getCodecFrameSize(void) { return _audioRtpRecord.getCodecFrameSize(); } + + int getHasDynamicPayload(void) { return _audioRtpRecord.getHasDynamicPayload(); } + + int getAudioLayerFrameSize(void) { return _audioRtpRecord.getAudioLayerFrameSize(); } + + int getAudioLayerSampleRate(void) { return _audioRtpRecord.getAudioLayerSampleRate(); } + + EventQueue *getEventQueue(void) { return _audioRtpRecord.getEventQueue(); } + + int getEventQueueSize(void) { return _audioRtpRecord.getEventQueueSize(); } + + unsigned char *getEncodedData(void) {return _audioRtpRecord.getEncodedData(); } + + inline float computeCodecFrameSize (int codecSamplePerFrame, int codecClockRate) { + return ( (float) codecSamplePerFrame * 1000.0) / (float) codecClockRate; + } + + int computeNbByteAudioLayer (float codecFrameSize) { + return (int) ( ( (float) _audioRtpRecord.getCodecSampleRate() * codecFrameSize * sizeof (SFLDataFormat)) / 1000.0); + } + + void init(void); + + /** + * Allocate memory for RTP buffers and fill them with zeros + * @prereq Session codec needs to be initialized prior calling this method + */ + void initBuffers (void); + + /** + * Encode audio data from mainbuffer + */ + int processDataEncode (void); + + /** + * Decode audio data received from peer + */ + void processDataDecode (unsigned char * spkrData, unsigned int size); + + /** + * Ramp In audio data to avoid audio click from peer + */ + bool fadeIn (SFLDataFormat *audio, int size, SFLDataFormat *factor); + +private: + + AudioRtpRecord _audioRtpRecord; + +}; + +} + +#endif /* AUDIORTPRECORD_H_ */ diff --git a/sflphone-common/src/audio/audiortp/AudioRtpSession.cpp b/sflphone-common/src/audio/audiortp/AudioRtpSession.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64d92c5e674befb47678ed68bb0c5c93e1a85af8 --- /dev/null +++ b/sflphone-common/src/audio/audiortp/AudioRtpSession.cpp @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc. + * Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com> + * Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com> + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Yan Morin <yan.morin@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@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. + * + * 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. + */ + +#include "AudioRtpSession.h" + +namespace sfl +{ + +AudioRtpSession::AudioRtpSession (ManagerImpl * manager, SIPCall * sipcall) : + ost::SymmetricRTPSession (ost::InetHostAddress (sipcall->getLocalIp().c_str()), sipcall->getLocalAudioPort()) + , _time (new ost::Time()) + , _mainloopSemaphore (0) + , _manager (manager) + , _timestamp (0) + , _timestampIncrement (0) + , _timestampCount (0) + , _countNotificationTime (0) + , _ca (sipcall) +{ + setCancel (cancelDefault); + + assert (_ca); + + _info ("AudioRtpSession: Local audio port %i will be used", _ca->getLocalAudioPort()); + +} + +AudioRtpSession::~AudioRtpSession() +{ + _debug ("AudioRtpSession: Delete AudioRtpSession instance"); + + try { + terminate(); + } catch (...) { + _debugException ("AudioRtpSession: Thread destructor didn't terminate correctly"); + throw; + } + + _manager->getAudioDriver()->getMainBuffer()->unBindAll (_ca->getCallId()); + + if(_time) + delete _time; + _time = NULL; + +} + +void AudioRtpSession::setSessionTimeouts (void) +{ + setSchedulingTimeout (sfl::schedulingTimeout); + setExpireTimeout (sfl::expireTimeout); +} + +void AudioRtpSession::setSessionMedia (AudioCodec* audioCodec) +{ + // set internal codec info for this session + setRtpMedia(audioCodec); + + // store codec info locally + int payloadType = getCodecPayloadType(); + int frameSize = getCodecFrameSize(); + int smplRate = getCodecSampleRate(); + bool dynamic = getHasDynamicPayload(); + + // G722 requires timestamp to be incremented at 8 kHz + if (payloadType == 9) + _timestampIncrement = 160; + else + _timestampIncrement = frameSize; + + _debug ("AudioRtpSession: Codec sampling rate: %d", smplRate); + _debug ("AudioRtpSession: Codec frame size: %d", frameSize); + _debug ("AudioRtpSession: RTP timestamp increment: %d", _timestampIncrement); + + // Even if specified as a 16 kHz codec, G722 requires rtp sending rate to be 8 kHz + if (payloadType == 9) { + _debug ("AudioRtpSession: Setting G722 payload format"); + setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) payloadType, smplRate)); + } else if (dynamic) { + _debug ("AudioRtpSession: Setting dynamic payload format"); + setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) payloadType, smplRate)); + } else if (dynamic && payloadType != 9) { + _debug ("AudioRtpSession: Setting static payload format"); + setPayloadFormat (ost::StaticPayloadFormat ( (ost::StaticPayloadType) payloadType)); + } + +} + +void AudioRtpSession::setDestinationIpAddress (void) +{ + if (_ca == NULL) { + _error ("AudioRtpSession: Sipcall is gone."); + throw AudioRtpSessionException(); + } + + _info ("AudioRtpSession: Setting IP address for the RTP session"); + + // Store remote ip in case we would need to forget current destination + _remote_ip = ost::InetHostAddress (_ca->getLocalSDP()->get_remote_ip().c_str()); + + if (!_remote_ip) { + _warn ("AudioRtpSession: Target IP address (%s) is not correct!", + _ca->getLocalSDP()->get_remote_ip().data()); + return; + } + + // Store remote port in case we would need to forget current destination + _remote_port = (unsigned short) _ca->getLocalSDP()->get_remote_audio_port(); + + _info ("AudioRtpSession: New remote address for session: %s:%d", + _ca->getLocalSDP()->get_remote_ip().data(), _remote_port); + + if (!addDestination (_remote_ip, _remote_port)) { + _warn ("AudioRtpSession: Can't add new destination to session!"); + return; + } +} + +void AudioRtpSession::updateDestinationIpAddress (void) +{ + // Destination address are stored in a list in ccrtp + // This method remove the current destination entry + + if (!forgetDestination (_remote_ip, _remote_port, _remote_port+1)) + _warn ("AudioRtpSession: Could not remove previous destination"); + + // new destination is stored in call + // we just need to recall this method + setDestinationIpAddress(); +} + +void AudioRtpSession::sendDtmfEvent (sfl::DtmfEvent *dtmf) +{ + _debug ("AudioRtpSession: Send Dtmf %d", getEventQueueSize()); + + _timestamp += 160; + + // discard equivalent size of audio + processDataEncode(); + + // change Payload type for DTMF payload + setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) 101, 8000)); + + // Set marker in case this is a new Event + if (dtmf->newevent) + setMark (true); + + putData (_timestamp, (const unsigned char*) (& (dtmf->payload)), sizeof (ost::RTPPacket::RFC2833Payload)); + + // This is no more a new event + if (dtmf->newevent) { + dtmf->newevent = false; + setMark (false); + } + + // get back the payload to audio + setPayloadFormat (ost::StaticPayloadFormat ( (ost::StaticPayloadType) getCodecPayloadType())); + + // decrease length remaining to process for this event + dtmf->length -= 160; + + dtmf->payload.duration += 1; + + // next packet is going to be the last one + if ( (dtmf->length - 160) < 160) + dtmf->payload.ebit = true; + + if (dtmf->length < 160) { + delete dtmf; + getEventQueue()->pop_front(); + } +} + +bool onRTPPacketRecv (ost::IncomingRTPPkt&) +{ + _debug ("AudioRtpSession: onRTPPacketRecv"); + + return true; +} + + +void AudioRtpSession::sendMicData() +{ + // Increment timestamp for outgoing packet + _timestamp += _timestampIncrement; + + int compSize = processDataEncode(); + + // putData put the data on RTP queue, sendImmediate bypass this queue + putData (_timestamp, getEncodedData(), compSize); +} + + +void AudioRtpSession::receiveSpeakerData () +{ + const ost::AppDataUnit* adu = NULL; + + int packetTimestamp = getFirstTimestamp(); + + adu = getData (packetTimestamp); + + if (!adu) { + return; + } + + unsigned char* spkrDataIn = NULL; + unsigned int size = 0; + + if (adu) { + + spkrDataIn = (unsigned char*) adu->getData(); // data in char + size = adu->getSize(); // size in char + + } else { + _debug ("AudioRtpSession: No RTP packet available"); + } + + // DTMF over RTP, size must be over 4 in order to process it as voice data + if (size > 4) { + processDataDecode (spkrDataIn, size); + } + + delete adu; +} + + +int AudioRtpSession::startRtpThread (AudioCodec* audiocodec) +{ + _debug ("RTP: Starting main thread"); + setSessionTimeouts(); + setSessionMedia (audiocodec); + initBuffers(); + return start (_mainloopSemaphore); +} + +void AudioRtpSession::run () +{ + + // Timestamp must be initialized randomly + _timestamp = getCurrentTimestamp(); + + int threadSleep = 0; + + if (getCodecSampleRate() != 0) { + threadSleep = (getCodecFrameSize() * 1000) / getCodecSampleRate(); + } else { + // TODO should not be dependent of audio layer frame size + threadSleep = getAudioLayerFrameSize(); + } + + TimerPort::setTimer (threadSleep); + + // Set recording sampling rate + _ca->setRecordingSmplRate (getCodecSampleRate()); + + // Start audio stream (if not started) AND flush all buffers (main and urgent) + _manager->getAudioDriver()->startStream(); + startRunning(); + + _debug ("AudioRtpSession: Entering mainloop for call %s",_ca->getCallId().c_str()); + + while (!testCancel()) { + + // Reset timestamp to make sure the timing information are up to date + if (_timestampCount > RTP_TIMESTAMP_RESET_FREQ) { + _timestamp = getCurrentTimestamp(); + _timestampCount = 0; + } + + _timestampCount++; + + _manager->getAudioLayerMutex()->enter(); + + // TODO should not be linked to audio layer here + // converterSamplingRate = _audiolayer->getMainBuffer()->getInternalSamplingRate(); + // _converterSamplingRate = _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); + + // Send session + if (getEventQueueSize() > 0) { + sendDtmfEvent (getEventQueue()->front()); + } else { + sendMicData (); + } + + // Recv session + // TODO should not be called here anymore + // receiveSpeakerData (); + + _manager->getAudioLayerMutex()->leave(); + + // Let's wait for the next transmit cycle + Thread::sleep (TimerPort::getTimer()); + TimerPort::incTimer (threadSleep); + } + + _debug ("AudioRtpSession: Left main loop for call %s", _ca->getCallId().c_str()); +} + +} diff --git a/sflphone-common/src/audio/audiortp/AudioRtpSession.h b/sflphone-common/src/audio/audiortp/AudioRtpSession.h index 2483b56bdc3041b79afa7fbf3bc97fe0a58681bc..07d215df1da59cca650a0c786eab27c82bc25e5d 100644 --- a/sflphone-common/src/audio/audiortp/AudioRtpSession.h +++ b/sflphone-common/src/audio/audiortp/AudioRtpSession.h @@ -40,13 +40,13 @@ #include "global.h" +#include "AudioRtpRecordHandler.h" #include "sip/sipcall.h" #include "sip/sdp.h" #include "audio/audiolayer.h" #include "audio/codecs/audiocodec.h" -#include "audio/samplerateconverter.h" -#include "audio/audioprocessing.h" -#include "audio/noisesuppress.h" + + #include "managerimpl.h" @@ -77,17 +77,9 @@ class AudioRtpSessionException: public std::exception } }; -typedef struct DtmfEvent { - ost::RTPPacket::RFC2833Payload payload; - int length; - bool newevent; -} DtmfEvent; - -typedef list<DtmfEvent *> EventQueue; - -template <typename D> -class AudioRtpSession : public ost::Thread, public ost::TimerPort +// class AudioRtpSession : public ost::Thread, public ost::TimerPort, public AudioRtpRecordHandler, public ost::SymmetricRTPSession +class AudioRtpSession : public ost::TimerPort, public ost::SymmetricRTPSession, public AudioRtpRecordHandler { public: /** @@ -118,21 +110,8 @@ class AudioRtpSession : public ost::Thread, public ost::TimerPort */ void sendDtmfEvent (sfl::DtmfEvent *dtmf); - inline float computeCodecFrameSize (int codecSamplePerFrame, int codecClockRate) { - return ( (float) codecSamplePerFrame * 1000.0) / (float) codecClockRate; - } - - int computeNbByteAudioLayer (float codecFrameSize) { - return (int) ( ( (float) _converterSamplingRate * codecFrameSize * sizeof (SFLDataFormat)) / 1000.0); - } - private: - /** - * Allocate memory for RTP buffers and fill them with zeros - */ - void initBuffers (void); - /** * Set RTP Sockets send/receive timeouts */ @@ -148,15 +127,6 @@ class AudioRtpSession : public ost::Thread, public ost::TimerPort */ void setDestinationIpAddress (void); - /** - * Encode audio data from mainbuffer - */ - int processDataEncode (void); - - /** - * Decode audio data received from peer - */ - void processDataDecode (unsigned char * spkrData, unsigned int size); /** * Send encoded data to peer @@ -168,11 +138,6 @@ class AudioRtpSession : public ost::Thread, public ost::TimerPort */ void receiveSpeakerData (); - /** - * Ramp In audio data to avoid audio click from peer - */ - bool fadeIn (SFLDataFormat *audio, int size, SFLDataFormat *factor); - ost::Time * _time; // This semaphore is not used @@ -195,62 +160,12 @@ class AudioRtpSession : public ost::Thread, public ost::TimerPort // this destination and update a new one unsigned short _remote_port; - // Pointer to the session's codec - AudioCodec * _audiocodec; - - // Pointer to audio layer - AudioLayer * _audiolayer; - - /** Mic-data related buffers */ - SFLDataFormat* _micData; - SFLDataFormat* _micDataConverted; - unsigned char* _micDataEncoded; - - /** Speaker-data related buffers */ - SFLDataFormat* _spkrDataDecoded; - SFLDataFormat* _spkrDataConverted; - - /** Sample rate converter object */ - SamplerateConverter * _converter; - - /** Variables to process audio stream: sample rate for playing sound (typically 44100HZ) */ - int _layerSampleRate; - - /** Sample rate of the codec we use to encode and decode (most of time 8000HZ) */ - int _codecSampleRate; - - /** Length of the sound frame we capture in ms (typically 20ms) */ - int _layerFrameSize; - - /** Codecs frame size in samples (20 ms => 882 at 44.1kHz) - The exact value is stored in the codec */ - int _codecFrameSize; - - /** Speaker buffer length in samples once the data are resampled - * (used for mixing and recording) - */ - int _nSamplesSpkr; - - /** Mic buffer length in samples once the data are resampled - * (used for mixing and recording) - */ - int _nSamplesMic; - - /** - * Maximum number of sample for audio buffers (mic and spkr) - */ - int _nbSamplesMax; /** * Manager instance. */ ManagerImpl * _manager; - /** - * Sampling rate of audio converter - */ - int _converterSamplingRate; - /** * Timestamp for this session */ @@ -272,660 +187,12 @@ class AudioRtpSession : public ost::Thread, public ost::TimerPort */ int _countNotificationTime; - /** - * EventQueue used to store list of DTMF- - */ - EventQueue _eventQueue; - - /** - * Packet size in ms - */ - int _packetLength; - - int _ts; - - /** - * Current time in ms - */ - int _currentTime; - - /** - * State of mic fade in - */ - bool _micFadeInComplete; - - /** - * State of spkr fade in - */ - bool _spkrFadeInComplete; - - /** - * Ampliturde factor to fade in mic data - */ - SFLDataFormat _micAmplFactor; - - /** - * Amplitude factor to fade in spkr data - */ - SFLDataFormat _spkrAmplFactor; - - /** - * Audio process containing noise reduction engine - */ - AudioProcessing *_audioProcess; - - /** - * Noise reduction engine - */ - NoiseSuppress *_noiseSuppress; protected: SIPCall * _ca; - - bool onRTPPacketRecv (ost::IncomingRTPPkt&); }; -template <typename D> -AudioRtpSession<D>::AudioRtpSession (ManagerImpl * manager, SIPCall * sipcall) : - _time (new ost::Time()), - _mainloopSemaphore (0), - _audiocodec (NULL), - _audiolayer (NULL), - _micData (NULL), - _micDataConverted (NULL), - _micDataEncoded (NULL), - _spkrDataDecoded (NULL), - _spkrDataConverted (NULL), - _converter (NULL), - _layerSampleRate (0), - _codecSampleRate (0), - _layerFrameSize (0), - _manager (manager), - _converterSamplingRate (0), - _timestamp (0), - _timestampIncrement (0), - _timestampCount (0), - _countNotificationTime (0), - _micFadeInComplete (false), - _spkrFadeInComplete (false), - _micAmplFactor (32000), - _spkrAmplFactor (32000), - _audioProcess (NULL), - _noiseSuppress (NULL), - _ca (sipcall) -{ - setCancel (cancelDefault); - - assert (_ca); - - _info ("Rtp: Local audio port %i will be used", _ca->getLocalAudioPort()); - - //mic, we receive from soundcard in stereo, and we send encoded - _audiolayer = _manager->getAudioDriver(); - - if (_audiolayer == NULL) { - throw AudioRtpSessionException(); - } - - _layerFrameSize = _audiolayer->getFrameSize(); // in ms - _layerSampleRate = _audiolayer->getSampleRate(); - - _ts= 0; - _packetLength = 20; - _currentTime = 0; - -} - -template <typename D> -AudioRtpSession<D>::~AudioRtpSession() -{ - _debug ("RTP: Delete AudioRtpSession instance"); - - try { - terminate(); - } catch (...) { - _debugException ("Thread destructor didn't terminate correctly"); - throw; - } - - _manager->getAudioDriver()->getMainBuffer()->unBindAll (_ca->getCallId()); - - if (_micData) { - delete [] _micData; - _micData = NULL; - } - - if (_micDataConverted) { - delete [] _micDataConverted; - _micDataConverted = NULL; - } - - if (_micDataEncoded) { - delete [] _micDataEncoded; - _micDataEncoded = NULL; - } - - if (_spkrDataDecoded) { - delete [] _spkrDataDecoded; - _spkrDataDecoded = NULL; - } - - if (_spkrDataConverted) { - delete [] _spkrDataConverted; - _spkrDataConverted = NULL; - } - - delete _time; - delete _converter; - - if (_audiocodec) { - delete _audiocodec; - _audiocodec = NULL; - } - - if (_audioProcess) { - delete _audioProcess; - _audioProcess = NULL; - } - - if (_noiseSuppress) { - delete _noiseSuppress; - _noiseSuppress = NULL; - } -} - -template <typename D> -void AudioRtpSession<D>::initBuffers() -{ - // Set sampling rate, main buffer choose the highest one - _manager->getAudioDriver()->getMainBuffer()->setInternalSamplingRate (_codecSampleRate); - - // may be different than one already set - _converterSamplingRate = _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); - - // initialize SampleRate converter using AudioLayer's sampling rate - // (internal buffers initialized with maximal sampling rate and frame size) - _converter = new SamplerateConverter (_layerSampleRate, _layerFrameSize); - - int nbSamplesMax = (int) (_codecSampleRate * _layerFrameSize /1000) *2; - _micData = new SFLDataFormat[nbSamplesMax]; - _micDataConverted = new SFLDataFormat[nbSamplesMax]; - _micDataEncoded = new unsigned char[nbSamplesMax*2]; - _spkrDataConverted = new SFLDataFormat[nbSamplesMax]; - _spkrDataDecoded = new SFLDataFormat[nbSamplesMax]; - - - memset (_micData, 0, nbSamplesMax*sizeof (SFLDataFormat)); - memset (_micDataConverted, 0, nbSamplesMax*sizeof (SFLDataFormat)); - memset (_micDataEncoded, 0, nbSamplesMax*2); - memset (_spkrDataConverted, 0, nbSamplesMax*sizeof (SFLDataFormat)); - memset (_spkrDataDecoded, 0, nbSamplesMax*sizeof (SFLDataFormat)); - - _manager->addStream (_ca->getCallId()); -} - -template <typename D> -void AudioRtpSession<D>::setSessionTimeouts (void) -{ - try { - static_cast<D*> (this)->setSchedulingTimeout (schedulingTimeout); - static_cast<D*> (this)->setExpireTimeout (expireTimeout); - } catch (...) { - _debugException ("AudioRtpSession: Initialization failed while setting timeouts"); - throw AudioRtpSessionException(); - } -} - -template <typename D> -void AudioRtpSession<D>::setSessionMedia (AudioCodec* audiocodec) -{ - _audiocodec = audiocodec; - - _debug ("RTP: Init codec payload %i", _audiocodec->getPayload()); - - _codecSampleRate = _audiocodec->getClockRate(); - _codecFrameSize = _audiocodec->getFrameSize(); - - // G722 requires timestamp to be incremented at 8 kHz - if (_audiocodec->getPayload() == 9) - _timestampIncrement = 160; - else - _timestampIncrement = _codecFrameSize; - - - _debug ("RTP: Codec sampling rate: %d", _codecSampleRate); - _debug ("RTP: Codec frame size: %d", _codecFrameSize); - _debug ("RTP: RTP timestamp increment: %d", _timestampIncrement); - - // Even if specified as a 16 kHz codec, G722 requires rtp sending rate to be 8 kHz - if (_audiocodec->getPayload() == 9) { - _debug ("RTP: Setting G722 payload format"); - static_cast<D*> (this)->setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) _audiocodec->getPayload(), _audiocodec->getClockRate())); - } else if (_audiocodec->hasDynamicPayload()) { - _debug ("RTP: Setting dynamic payload format"); - static_cast<D*> (this)->setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) _audiocodec->getPayload(), _audiocodec->getClockRate())); - } else if (!_audiocodec->hasDynamicPayload() && _audiocodec->getPayload() != 9) { - _debug ("RTP: Setting static payload format"); - static_cast<D*> (this)->setPayloadFormat (ost::StaticPayloadFormat ( (ost::StaticPayloadType) _audiocodec->getPayload())); - } - -} - -template <typename D> -void AudioRtpSession<D>::setDestinationIpAddress (void) -{ - if (_ca == NULL) { - _error ("RTP: Sipcall is gone."); - throw AudioRtpSessionException(); - } - - _info ("RTP: Setting IP address for the RTP session"); - - // Store remote ip in case we would need to forget current destination - _remote_ip = ost::InetHostAddress (_ca->getLocalSDP()->get_remote_ip().c_str()); - - if (!_remote_ip) { - _warn ("RTP: Target IP address (%s) is not correct!", - _ca->getLocalSDP()->get_remote_ip().data()); - return; - } - - // Store remote port in case we would need to forget current destination - _remote_port = (unsigned short) _ca->getLocalSDP()->get_remote_audio_port(); - - _info ("RTP: New remote address for session: %s:%d", - _ca->getLocalSDP()->get_remote_ip().data(), _remote_port); - - if (! static_cast<D*> (this)->addDestination (_remote_ip, _remote_port)) { - _warn ("RTP: Can't add new destination to session!"); - return; - } -} - -template <typename D> -void AudioRtpSession<D>::updateDestinationIpAddress (void) -{ - // Destination address are stored in a list in ccrtp - // This method remove the current destination entry - - if (!static_cast<D*> (this)->forgetDestination (_remote_ip, _remote_port, _remote_port+1)) - _warn ("RTP: Could not remove previous destination"); - - // new destination is stored in call - // we just need to recall this method - setDestinationIpAddress(); -} - -template<typename D> -void AudioRtpSession<D>::putDtmfEvent (int digit) -{ - - sfl::DtmfEvent *dtmf = new sfl::DtmfEvent(); - - dtmf->payload.event = digit; - dtmf->payload.ebit = false; // end of event bit - dtmf->payload.rbit = false; // reserved bit - dtmf->payload.duration = 1; // duration for this event - dtmf->newevent = true; - dtmf->length = 1000; - - _eventQueue.push_back (dtmf); - - _debug ("RTP: Put Dtmf Event %d", _eventQueue.size()); - -} - -template<typename D> -void AudioRtpSession<D>::sendDtmfEvent (sfl::DtmfEvent *dtmf) -{ - _debug ("RTP: Send Dtmf %d", _eventQueue.size()); - - _timestamp += 160; - - // discard equivalent size of audio - processDataEncode(); - - // change Payload type for DTMF payload - static_cast<D*> (this)->setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) 101, 8000)); - - // Set marker in case this is a new Event - if (dtmf->newevent) - static_cast<D*> (this)->setMark (true); - - static_cast<D*> (this)->putData (_timestamp, (const unsigned char*) (& (dtmf->payload)), sizeof (ost::RTPPacket::RFC2833Payload)); - - // This is no more a new event - if (dtmf->newevent) { - dtmf->newevent = false; - static_cast<D*> (this)->setMark (false); - } - - // get back the payload to audio - static_cast<D*> (this)->setPayloadFormat (ost::StaticPayloadFormat ( (ost::StaticPayloadType) _audiocodec->getPayload())); - - // decrease length remaining to process for this event - dtmf->length -= 160; - - dtmf->payload.duration += 1; - - // next packet is going to be the last one - if ( (dtmf->length - 160) < 160) - dtmf->payload.ebit = true; - - if (dtmf->length < 160) { - delete dtmf; - _eventQueue.pop_front(); - } -} - -template <typename D> -bool onRTPPacketRecv (ost::IncomingRTPPkt&) -{ - _debug ("AudioRtpSession: onRTPPacketRecv"); - - return true; -} - - -template <typename D> -int AudioRtpSession<D>::processDataEncode (void) -{ - assert (_audiocodec); - assert (_audiolayer); - - int _mainBufferSampleRate = _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); - - // compute codec framesize in ms - float fixed_codec_framesize = computeCodecFrameSize (_audiocodec->getFrameSize(), _audiocodec->getClockRate()); - - // compute nb of byte to get coresponding to 20 ms at audio layer frame size (44.1 khz) - int maxBytesToGet = computeNbByteAudioLayer (fixed_codec_framesize); - - // available bytes inside ringbuffer - int availBytesFromMic = _manager->getAudioDriver()->getMainBuffer()->availForGet (_ca->getCallId()); - - // set available byte to maxByteToGet - int bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet; - - if (bytesAvail == 0) { - memset (_micDataEncoded, 0, sizeof (SFLDataFormat)); - return _audiocodec->getFrameSize(); - } - - // Get bytes from micRingBuffer to data_from_mic - int nbSample = _manager->getAudioDriver()->getMainBuffer()->getData (_micData , bytesAvail, 100, _ca->getCallId()) / sizeof (SFLDataFormat); - - if (!_micFadeInComplete) - _micFadeInComplete = fadeIn (_micData, nbSample, &_micAmplFactor); - - if (nbSample == 0) - return nbSample; - - // nb bytes to be sent over RTP - int compSize = 0; - - // test if resampling is required - if (_audiocodec->getClockRate() != _mainBufferSampleRate) { - int nb_sample_up = nbSample; - - _nSamplesMic = nbSample; - - nbSample = _converter->downsampleData (_micData , _micDataConverted , _audiocodec->getClockRate(), _mainBufferSampleRate, nb_sample_up); - - if (_manager->audioPreference.getNoiseReduce()) - _audioProcess->processAudio (_micDataConverted, nbSample*sizeof (SFLDataFormat)); - - compSize = _audiocodec->codecEncode (_micDataEncoded, _micDataConverted, nbSample*sizeof (SFLDataFormat)); - - } else { - - _nSamplesMic = nbSample; - - if (_manager->audioPreference.getNoiseReduce()) - _audioProcess->processAudio (_micData, nbSample*sizeof (SFLDataFormat)); - - // no resampling required - compSize = _audiocodec->codecEncode (_micDataEncoded, _micData, nbSample*sizeof (SFLDataFormat)); - - } - - return compSize; -} - -template <typename D> -void AudioRtpSession<D>::processDataDecode (unsigned char * spkrData, unsigned int size) -{ - if (_audiocodec != NULL) { - - - int _mainBufferSampleRate = _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); - - // Return the size of data in bytes - int expandedSize = _audiocodec->codecDecode (_spkrDataDecoded , spkrData , size); - - // buffer _receiveDataDecoded ----> short int or int16, coded on 2 bytes - int nbSample = expandedSize / sizeof (SFLDataFormat); - - if (!_spkrFadeInComplete) - _spkrFadeInComplete = fadeIn (_spkrDataDecoded, nbSample, &_spkrAmplFactor); - - // test if resampling is required - if (_audiocodec->getClockRate() != _mainBufferSampleRate) { - - // Do sample rate conversion - int nb_sample_down = nbSample; - - nbSample = _converter->upsampleData (_spkrDataDecoded, _spkrDataConverted, _codecSampleRate, _mainBufferSampleRate, nb_sample_down); - - // Store the number of samples for recording - _nSamplesSpkr = nbSample; - - // put data in audio layer, size in byte - _manager->getAudioDriver()->getMainBuffer()->putData (_spkrDataConverted, nbSample * sizeof (SFLDataFormat), 100, _ca->getCallId()); - - - } else { - // Store the number of samples for recording - _nSamplesSpkr = nbSample; - - // put data in audio layer, size in byte - _manager->getAudioDriver()->getMainBuffer()->putData (_spkrDataDecoded, expandedSize, 100, _ca->getCallId()); - } - - // Notify (with a beep) an incoming call when there is already a call - if (_manager->incomingCallWaiting() > 0) { - _countNotificationTime += _time->getSecond(); - int countTimeModulo = _countNotificationTime % 5000; - - // _debug("countNotificationTime: %d\n", countNotificationTime); - // _debug("countTimeModulo: %d\n", countTimeModulo); - if ( (countTimeModulo - _countNotificationTime) < 0) { - _manager->notificationIncomingCall(); - } - - _countNotificationTime = countTimeModulo; - } - - } -} - -template <typename D> -void AudioRtpSession<D>::sendMicData() -{ - // STEP: - // 1. get data from mic - // 2. convert it to int16 - good sample, good rate - // 3. encode it - // 4. send it - - // Increment timestamp for outgoing packet - _timestamp += _timestampIncrement; - - if (!_audiolayer) { - _debug ("No audiolayer available for MIC"); - return; - } - - if (!_audiocodec) { - _debug ("No audiocodec available for MIC"); - return; - } - - int compSize = processDataEncode(); - - // putData put the data on RTP queue, sendImmediate bypass this queue - static_cast<D*> (this)->putData (_timestamp, _micDataEncoded, compSize); -} - - -template <typename D> -void AudioRtpSession<D>::receiveSpeakerData () -{ - - if (!_audiolayer) { - _debug ("No audiolayer available for speaker"); - return; - } - - if (!_audiocodec) { - _debug ("No audiocodec available for speaker"); - return; - } - - const ost::AppDataUnit* adu = NULL; - - int packetTimestamp = static_cast<D*> (this)->getFirstTimestamp(); - - adu = static_cast<D*> (this)->getData (packetTimestamp); - - if (!adu) { - return; - } - - unsigned char* spkrDataIn = NULL; - unsigned int size = 0; - - if (adu) { - - spkrDataIn = (unsigned char*) adu->getData(); // data in char - size = adu->getSize(); // size in char - - } else { - _debug ("No RTP packet available !!!!!!!!!!!!!!!!!!!!!!!\n"); - } - - // DTMF over RTP, size must be over 4 in order to process it as voice data - if (size > 4) { - processDataDecode (spkrDataIn, size); - } - - delete adu; -} - -template <typename D> -bool AudioRtpSession<D>::fadeIn (SFLDataFormat *audio, int size, SFLDataFormat *factor) -{ - - // apply amplitude factor; - while (size) { - size--; - audio[size] /= *factor; - } - - // decrease factor - *factor /= FADEIN_STEP_SIZE; - - // if factor reach 0, thsi function should no be called anymore - if (*factor == 0) - return true; - - return false; -} - -template <typename D> -int AudioRtpSession<D>::startRtpThread (AudioCodec* audiocodec) -{ - _debug ("RTP: Starting main thread"); - setSessionTimeouts(); - setSessionMedia (audiocodec); - initBuffers(); - return start (_mainloopSemaphore); -} - -template <typename D> -void AudioRtpSession<D>::run () -{ - - // Timestamp must be initialized randomly - _timestamp = static_cast<D*> (this)->getCurrentTimestamp(); - - int threadSleep = 0; - - if (_codecSampleRate != 0) { - threadSleep = (_codecFrameSize * 1000) / _codecSampleRate; - } else { - threadSleep = _layerFrameSize; - } - - TimerPort::setTimer (threadSleep); - - if (_audiolayer == NULL) { - _error ("RTP: Error: Audiolayer is null, cannot start the audio stream"); - throw AudioRtpSessionException(); - } - - // Set recording sampling rate - _ca->setRecordingSmplRate (_audiocodec->getClockRate()); - - // init noise reduction process - _noiseSuppress = new NoiseSuppress (_codecFrameSize, _audiocodec->getClockRate()); - _audioProcess = new AudioProcessing (_noiseSuppress); - - // Start audio stream (if not started) AND flush all buffers (main and urgent) - _manager->getAudioDriver()->startStream(); - static_cast<D*> (this)->startRunning(); - - _debug ("RTP: Entering mainloop for call %s",_ca->getCallId().c_str()); - - _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); - - while (!testCancel()) { - - // Reset timestamp to make sure the timing information are up to date - if (_timestampCount > RTP_TIMESTAMP_RESET_FREQ) { - _timestamp = static_cast<D*> (this)->getCurrentTimestamp(); - _timestampCount = 0; - } - - _timestampCount++; - - - _manager->getAudioLayerMutex()->enter(); - - // converterSamplingRate = _audiolayer->getMainBuffer()->getInternalSamplingRate(); - _converterSamplingRate = _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); - - // Send session - if (_eventQueue.size() > 0) { - sendDtmfEvent (_eventQueue.front()); - } else { - sendMicData (); - } - - // Recv session - receiveSpeakerData (); - - _manager->getAudioLayerMutex()->leave(); - - // Let's wait for the next transmit cycle - Thread::sleep (TimerPort::getTimer()); - TimerPort::incTimer (threadSleep); - } - - _debug ("RTP: Left main loop for call%s", _ca->getCallId().c_str()); -} - } #endif // __AUDIO_RTP_SESSION_H__ diff --git a/sflphone-common/src/audio/audiortp/AudioSrtpSession.h b/sflphone-common/src/audio/audiortp/AudioSrtpSession.h index 281cff8d9cdd87e60925aec13a902716f3c071c2..0b24c4ec4b17de87f31e1cc16beca2b726d1033d 100644 --- a/sflphone-common/src/audio/audiortp/AudioSrtpSession.h +++ b/sflphone-common/src/audio/audiortp/AudioSrtpSession.h @@ -74,7 +74,7 @@ class SrtpException: public std::exception } }; -class AudioSrtpSession : public ost::SymmetricRTPSession, public AudioRtpSession<AudioSrtpSession> +class AudioSrtpSession : public AudioRtpSession { public: diff --git a/sflphone-common/src/audio/audiortp/AudioSymmetricRtpSession.h b/sflphone-common/src/audio/audiortp/AudioSymmetricRtpSession.h index a2275e3b508da1727d365fb03c2bfb10ff8bee80..330c8122f261e157673d8fedc38f47ae06e980ab 100644 --- a/sflphone-common/src/audio/audiortp/AudioSymmetricRtpSession.h +++ b/sflphone-common/src/audio/audiortp/AudioSymmetricRtpSession.h @@ -37,13 +37,11 @@ class ManagerImpl; namespace sfl { -class AudioSymmetricRtpSession : public ost::SymmetricRTPSession, public AudioRtpSession<AudioSymmetricRtpSession> +class AudioSymmetricRtpSession : public AudioRtpSession { public: - AudioSymmetricRtpSession (ManagerImpl * manager, SIPCall * sipcall) : - ost::SymmetricRTPSession (ost::InetHostAddress (sipcall->getLocalIp().c_str()), sipcall->getLocalAudioPort()), - AudioRtpSession<AudioSymmetricRtpSession> (manager, sipcall) { - _debug ("AudioSymmetricRtpSession initialized\n"); + AudioSymmetricRtpSession (ManagerImpl * manager, SIPCall * sipcall) : AudioRtpSession (manager, sipcall) { + } }; } diff --git a/sflphone-common/src/audio/audiortp/AudioZrtpSession.cpp b/sflphone-common/src/audio/audiortp/AudioZrtpSession.cpp index 6632bc2a61c63f87223b32b8447e13c6f5e4febe..c450b934121688a8e8aeec28ba9896322cf986b4 100644 --- a/sflphone-common/src/audio/audiortp/AudioZrtpSession.cpp +++ b/sflphone-common/src/audio/audiortp/AudioZrtpSession.cpp @@ -43,14 +43,47 @@ namespace sfl { AudioZrtpSession::AudioZrtpSession (ManagerImpl * manager, SIPCall * sipcall, const std::string& zidFilename) : - ost::SymmetricZRTPSession (ost::InetHostAddress (sipcall->getLocalIp().c_str()), sipcall->getLocalAudioPort()), - AudioRtpSession<AudioZrtpSession> (manager, sipcall), - _zidFilename (zidFilename) + ost::SymmetricZRTPSession (ost::InetHostAddress (sipcall->getLocalIp().c_str()), sipcall->getLocalAudioPort()) + , _zidFilename (zidFilename) + , _time (new ost::Time()) + , _mainloopSemaphore (0) + , _manager (manager) + , _timestamp (0) + , _timestampIncrement (0) + , _timestampCount (0) + , _countNotificationTime (0) + , _ca (sipcall) { _debug ("AudioZrtpSession initialized"); initializeZid(); + + setCancel (cancelDefault); + + assert (_ca); + + _info ("AudioZrtpSession: Local audio port %i will be used", _ca->getLocalAudioPort()); +} + +AudioZrtpSession::~AudioZrtpSession() +{ + _debug ("AudioZrtpSession: Delete AudioRtpSession instance"); + + try { + terminate(); + } catch (...) { + _debugException ("AudioZrtpSession: Thread destructor didn't terminate correctly"); + throw; + } + + _manager->getAudioDriver()->getMainBuffer()->unBindAll (_ca->getCallId()); + + if(_time) + delete _time; + _time = NULL; } + + void AudioZrtpSession::initializeZid (void) { @@ -95,4 +128,256 @@ void AudioZrtpSession::initializeZid (void) return; } + +void AudioRtpSession::setSessionTimeouts (void) +{ + setSchedulingTimeout (sfl::schedulingTimeout); + setExpireTimeout (sfl::expireTimeout); +} + +void AudioRtpSession::setSessionMedia (AudioCodec* audioCodec) +{ + // set internal codec info for this session + setRtpMedia(audioCodec); + + // store codec info locally + int payloadType = getCodecPayloadType(); + int frameSize = getCodecFrameSize(); + int smplRate = getCodecSampleRate(); + bool dynamic = getHasDynamicPayload(); + + // G722 requires timestamp to be incremented at 8 kHz + if (payloadType == 9) + _timestampIncrement = 160; + else + _timestampIncrement = frameSize; + + _debug ("AudioRtpSession: Codec sampling rate: %d", smplRate); + _debug ("AudioRtpSession: Codec frame size: %d", frameSize); + _debug ("AudioRtpSession: RTP timestamp increment: %d", _timestampIncrement); + + // Even if specified as a 16 kHz codec, G722 requires rtp sending rate to be 8 kHz + if (payloadType == 9) { + _debug ("AudioRtpSession: Setting G722 payload format"); + setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) payloadType, smplRate)); + } else if (dynamic) { + _debug ("AudioRtpSession: Setting dynamic payload format"); + setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) payloadType, smplRate)); + } else if (dynamic && payloadType != 9) { + _debug ("AudioRtpSession: Setting static payload format"); + setPayloadFormat (ost::StaticPayloadFormat ( (ost::StaticPayloadType) payloadType)); + } + +} + +void AudioRtpSession::setDestinationIpAddress (void) +{ + if (_ca == NULL) { + _error ("AudioRtpSession: Sipcall is gone."); + throw AudioRtpSessionException(); + } + + _info ("AudioRtpSession: Setting IP address for the RTP session"); + + // Store remote ip in case we would need to forget current destination + _remote_ip = ost::InetHostAddress (_ca->getLocalSDP()->get_remote_ip().c_str()); + + if (!_remote_ip) { + _warn ("AudioRtpSession: Target IP address (%s) is not correct!", + _ca->getLocalSDP()->get_remote_ip().data()); + return; + } + + // Store remote port in case we would need to forget current destination + _remote_port = (unsigned short) _ca->getLocalSDP()->get_remote_audio_port(); + + _info ("AudioRtpSession: New remote address for session: %s:%d", + _ca->getLocalSDP()->get_remote_ip().data(), _remote_port); + + if (!addDestination (_remote_ip, _remote_port)) { + _warn ("AudioRtpSession: Can't add new destination to session!"); + return; + } +} + +void AudioRtpSession::updateDestinationIpAddress (void) +{ + // Destination address are stored in a list in ccrtp + // This method remove the current destination entry + + if (!forgetDestination (_remote_ip, _remote_port, _remote_port+1)) + _warn ("AudioRtpSession: Could not remove previous destination"); + + // new destination is stored in call + // we just need to recall this method + setDestinationIpAddress(); +} + +void AudioRtpSession::sendDtmfEvent (sfl::DtmfEvent *dtmf) +{ + _debug ("AudioRtpSession: Send Dtmf %d", getEventQueueSize()); + + _timestamp += 160; + + // discard equivalent size of audio + processDataEncode(); + + // change Payload type for DTMF payload + setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) 101, 8000)); + + // Set marker in case this is a new Event + if (dtmf->newevent) + setMark (true); + + putData (_timestamp, (const unsigned char*) (& (dtmf->payload)), sizeof (ost::RTPPacket::RFC2833Payload)); + + // This is no more a new event + if (dtmf->newevent) { + dtmf->newevent = false; + setMark (false); + } + + // get back the payload to audio + setPayloadFormat (ost::StaticPayloadFormat ( (ost::StaticPayloadType) getCodecPayloadType())); + + // decrease length remaining to process for this event + dtmf->length -= 160; + + dtmf->payload.duration += 1; + + // next packet is going to be the last one + if ( (dtmf->length - 160) < 160) + dtmf->payload.ebit = true; + + if (dtmf->length < 160) { + delete dtmf; + getEventQueue()->pop_front(); + } +} + +bool onRTPPacketRecv (ost::IncomingRTPPkt&) +{ + _debug ("AudioRtpSession: onRTPPacketRecv"); + + return true; +} + + +void AudioRtpSession::sendMicData() +{ + // Increment timestamp for outgoing packet + _timestamp += _timestampIncrement; + + int compSize = processDataEncode(); + + // putData put the data on RTP queue, sendImmediate bypass this queue + putData (_timestamp, getEncodedData(), compSize); +} + + +void AudioRtpSession::receiveSpeakerData () +{ + const ost::AppDataUnit* adu = NULL; + + int packetTimestamp = getFirstTimestamp(); + + adu = getData (packetTimestamp); + + if (!adu) { + return; + } + + unsigned char* spkrDataIn = NULL; + unsigned int size = 0; + + if (adu) { + + spkrDataIn = (unsigned char*) adu->getData(); // data in char + size = adu->getSize(); // size in char + + } else { + _debug ("AudioRtpSession: No RTP packet available"); + } + + // DTMF over RTP, size must be over 4 in order to process it as voice data + if (size > 4) { + processDataDecode (spkrDataIn, size); + } + + delete adu; +} + + +int AudioRtpSession::startRtpThread (AudioCodec* audiocodec) +{ + _debug ("RTP: Starting main thread"); + setSessionTimeouts(); + setSessionMedia (audiocodec); + initBuffers(); + return start (_mainloopSemaphore); +} + +void AudioRtpSession::run () +{ + + // Timestamp must be initialized randomly + _timestamp = getCurrentTimestamp(); + + int threadSleep = 0; + + if (getCodecSampleRate() != 0) { + threadSleep = (getCodecFrameSize() * 1000) / getCodecSampleRate(); + } else { + // TODO should not be dependent of audio layer frame size + threadSleep = getAudioLayerFrameSize(); + } + + TimerPort::setTimer (threadSleep); + + // Set recording sampling rate + _ca->setRecordingSmplRate (getCodecSampleRate()); + + // Start audio stream (if not started) AND flush all buffers (main and urgent) + _manager->getAudioDriver()->startStream(); + startRunning(); + + _debug ("AudioRtpSession: Entering mainloop for call %s",_ca->getCallId().c_str()); + + while (!testCancel()) { + + // Reset timestamp to make sure the timing information are up to date + if (_timestampCount > RTP_TIMESTAMP_RESET_FREQ) { + _timestamp = getCurrentTimestamp(); + _timestampCount = 0; + } + + _timestampCount++; + + _manager->getAudioLayerMutex()->enter(); + + // TODO should not be linked to audio layer here + // converterSamplingRate = _audiolayer->getMainBuffer()->getInternalSamplingRate(); + // _converterSamplingRate = _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); + + // Send session + if (getEventQueueSize() > 0) { + sendDtmfEvent (getEventQueue()->front()); + } else { + sendMicData (); + } + + // Recv session + // TODO should not be called here anymore + // receiveSpeakerData (); + + _manager->getAudioLayerMutex()->leave(); + + // Let's wait for the next transmit cycle + Thread::sleep (TimerPort::getTimer()); + TimerPort::incTimer (threadSleep); + } + + _debug ("AudioRtpSession: Left main loop for call %s", _ca->getCallId().c_str()); +} + } diff --git a/sflphone-common/src/audio/audiortp/AudioZrtpSession.h b/sflphone-common/src/audio/audiortp/AudioZrtpSession.h index 4eb68d364d8a6e057dc74de65bf2d13e9706dcc2..c50b08662aecfa90d42539487dcedccd62af2c7d 100644 --- a/sflphone-common/src/audio/audiortp/AudioZrtpSession.h +++ b/sflphone-common/src/audio/audiortp/AudioZrtpSession.h @@ -31,7 +31,7 @@ #include <libzrtpcpp/zrtpccrtp.h> -#include "AudioRtpSession.h" +#include "AudioRtpRecordHandler.h" class ManagerImpl; class SIPCall; @@ -46,7 +46,7 @@ class ZrtpZidException: public std::exception } }; -class AudioZrtpSession : public ost::SymmetricZRTPSession, public AudioRtpSession<AudioZrtpSession> +class AudioZrtpSession : public ost::TimerPort, public ost::SymmetricZRTPSession, public AudioRtpRecordHandler { public: AudioZrtpSession (ManagerImpl * manager, SIPCall * sipcall, const std::string& zidFilename); @@ -54,6 +54,56 @@ class AudioZrtpSession : public ost::SymmetricZRTPSession, public AudioRtpSessio private: void initializeZid (void); std::string _zidFilename; + + ost::Time * _time; + + // This semaphore is not used + // but is needed in order to avoid + // ambiguous compiling problem. + // It is set to 0, and since it is + // optional in ost::thread, then + // it amounts to the same as doing + // start() with no semaphore at all. + ost::Semaphore * _mainloopSemaphore; + + // Main destination address for this rtp session. + // Stored in case or reINVITE, which may require to forget + // this destination and update a new one. + ost::InetHostAddress _remote_ip; + + + // Main destination port for this rtp session. + // Stored in case reINVITE, which may require to forget + // this destination and update a new one + unsigned short _remote_port; + + /** + * Manager instance. + */ + ManagerImpl * _manager; + + /** + * Timestamp for this session + */ + int _timestamp; + + /** + * Timestamp incrementation value based on codec period length (framesize) + * except for G722 which require a 8 kHz incrementation. + */ + int _timestampIncrement; + + /** + * Timestamp reset freqeuncy specified in number of packet sent + */ + short _timestampCount; + + /** + * Time counter used to trigger incoming call notification + */ + int _countNotificationTime; + + SIPCall * _ca; }; } diff --git a/sflphone-common/src/audio/audiortp/Makefile.am b/sflphone-common/src/audio/audiortp/Makefile.am index d2ab3aaec714d9065241752748284fe4ea59a0c2..f75a5973ea2feff0ebab76bbd6e5d3fb3922262f 100644 --- a/sflphone-common/src/audio/audiortp/Makefile.am +++ b/sflphone-common/src/audio/audiortp/Makefile.am @@ -3,12 +3,15 @@ include $(top_srcdir)/globals.mak noinst_LTLIBRARIES = libaudiortp.la libaudiortp_la_SOURCES = \ + AudioRtpSession.cpp \ + AudioRtpRecordHandler.cpp \ AudioRtpFactory.cpp \ AudioZrtpSession.cpp \ ZrtpSessionCallback.cpp \ AudioSrtpSession.cpp noinst_HEADERS = \ + AudioRtpRecordHandler.h \ AudioRtpFactory.h \ AudioRtpSession.h \ AudioSymmetricRtpSession.h \