Skip to content
Snippets Groups Projects
Select Git revision
  • 511293d152bb6c40852e1e77d440cd708ce80e90
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/windowsReleaseTest
  • release/releaseTest
  • release/releaseWindowsTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 4.0.0
  • 2.2.0
  • 2.1.0
  • 2.0.1
  • 2.0.0
  • 1.4.1
  • 1.4.0
  • 1.3.0
  • 1.2.0
  • 1.1.0
31 results

audiortp.cpp

  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    audiortp.cpp 21.04 KiB
    /*
     *  Copyright (C) 2004-2008 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 <sstream>
    
    #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                                                          
    ////////////////////////////////////////////////////////////////////////////////
    AudioRtp::AudioRtp() :_RTXThread(0), _symmetric(), _threadMutex()
    {
    }
    
    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("**********************************************************\n");
            _debug("! ARTP Failure: Thread already exists..., stopping it\n");
            _debug("**********************************************************\n");
            delete _RTXThread; _RTXThread = 0;
        }
    
        // 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;
    }
    
    
    bool
    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;
        }
        AudioLayer* audiolayer = Manager::instance().getAudioDriver();
        audiolayer->stopStream();
    
        return true;
    }
    
    
    void
    AudioRtp::setRecording() {
    
        _debug("AudioRtp::setRecording\n");
        _RTXThread->_ca->setRecording();
    
    }
    
    
    
    ////////////////////////////////////////////////////////////////////////////////
    // AudioRtpRTX Class                                                          //
    ////////////////////////////////////////////////////////////////////////////////
    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)
    {
    
        setCancel(cancelDefault);
        // 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 {
            _debug ("%i\n", _ca->getLocalAudioPort());
            _session = new ost::SymmetricRTPSession (local_ip, _ca->getLocalAudioPort());
            _sessionRecv = NULL;
            _sessionSend = NULL;
        }
    }
    
    AudioRtpRTX::~AudioRtpRTX () {
        _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;
        }
    
    
        delete [] micData;  micData = NULL;
        delete [] micDataConverted;  micDataConverted = NULL;
        delete [] micDataEncoded;  micDataEncoded = NULL;
    
        delete [] spkrDataDecoded; spkrDataDecoded = NULL;
        delete [] spkrDataConverted; spkrDataConverted = NULL;
    
        delete time; time = NULL;
    
        delete converter; converter = NULL;
    
    }
    
    
    
        void
    AudioRtpRTX::initBuffers()
    {
        converter = new SamplerateConverter( _layerSampleRate , _layerFrameSize );
    
        int nbSamplesMax = (int) (_layerSampleRate * _layerFrameSize /1000);
    
        micData = new SFLDataFormat[nbSamplesMax];
        micDataConverted = new SFLDataFormat[nbSamplesMax];
        micDataEncoded = new unsigned char[nbSamplesMax];
    
        spkrDataConverted = new SFLDataFormat[nbSamplesMax];
        spkrDataDecoded = new SFLDataFormat[nbSamplesMax];
    }
    
        void
    AudioRtpRTX::initAudioRtpSession (void) 
    {
    
        try {
            if (_ca == 0) { return; }
            _audiocodec = _ca->getLocalSDP()->get_session_media ();
    
            if (_audiocodec == NULL) { return; }
    
            _codecSampleRate = _audiocodec->getClockRate();
            _codecFrameSize = _audiocodec->getFrameSize();
            
    
            ost::InetHostAddress remote_ip(_ca->getLocalSDP()->get_remote_ip().c_str());
            _debug("Init audio RTP session %s\n", _ca->getLocalSDP()->get_remote_ip().data());
            if (!remote_ip) {
                _debug("! ARTP Thread Error: Target IP address [%s] is not correct!\n", _ca->getLocalSDP()->get_remote_ip().data());
                return;
            }
    
    
            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->getLocalSDP()->get_remote_audio_port()) ) {
                    _debug("AudioRTP Thread Error: could not connect to port %d\n",  _ca->getLocalSDP()->get_remote_audio_port());
                    return;
                }
                if (!_sessionSend->addDestination (remote_ip, (unsigned short) _ca->getLocalSDP()->get_remote_audio_port())) {
                    _debug("! ARTP Thread Error: could not connect to port %d\n", _ca->getLocalSDP()->get_remote_audio_port());
                    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 {
    
                if (!_session->addDestination (remote_ip, (unsigned short)_ca->getLocalSDP()->get_remote_audio_port() )) {
                    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;
        }    
    
    /*
        std::string remoteIP;
        unsigned int remotePort;
    
        try {
            if (_ca == 0) { return; }
    
            _audiocodec = _ca->getLocalSDP()->get_session_media ();
    
            if(_audiocodec == 0) { return; }
    
            _codecSampleRate = _audiocodec->getClockRate();	
    
            remoteIP = _ca->getLocalSDP()->get_remote_ip();
            //remoteIP = "192.168.1.234";
            remotePort = _ca->getLocalSDP()->get_remote_audio_port();
            _debug("Init audio RTP session - remote IP = %s\n", remoteIP.c_str());
            ost::InetHostAddress remote_ip(remoteIP.c_str());
            if (!remote_ip) {
                _debug("! ARTP Thread Error: Target IP address [%s] is not correct!\n", remoteIP.data());
                return;
            }
    
            if (!_sym) {
                _sessionRecv->setSchedulingTimeout (10000);
                _sessionRecv->setExpireTimeout(1000000);
    
                _sessionSend->setSchedulingTimeout(10000);
                _sessionSend->setExpireTimeout(1000000);
            } else {
                _session->setSchedulingTimeout(10000);
                _session->setExpireTimeout(1000000);
            }
    
            if (!_sym) {
                _debug("! AudioRTP Thread: Added session destination %s:%d\n", remote_ip.getHostname(), remotePort );
                if ( !_sessionRecv->addDestination(remote_ip, (unsigned short) remotePort) ) {
                    _debug("AudioRTP Thread Error: could not connect to port %d\n",  remotePort);
                    return;
                }
                if (!_sessionSend->addDestination (remote_ip, (unsigned short) remotePort)) {
                    _debug("! ARTP Thread Error: could not connect to port %d\n",  remotePort);
                    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:%d\n", remote_ip.getHostname(), remotePort );
    
                if (!_session->addDestination (remote_ip, (unsigned short) remotePort)) {
                    _debug ("could not connect to port %d\n", remotePort);
                    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::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 {
    
        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;
    
        // compute codec framesize in ms
        float fixed_codec_framesize = ((float)_audiocodec->getFrameSize() * 1000.0) / (float)_audiocodec->getClockRate();
    
        int maxBytesToGet = (int)((float)_layerSampleRate * fixed_codec_framesize * (float)sizeof(SFLDataFormat) / 1000.0);
        
        // available bytes inside ringbuffer
        int availBytesFromMic = audiolayer->canGetMic();
        
        int bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet;
    
        if (bytesAvail == 0)
          return;
    
        // Get bytes from micRingBuffer to data_from_mic
        int nbSample = audiolayer->getMic( micData , bytesAvail ) / sizeof(SFLDataFormat);
    
        // nb bytes to be sent over RTP
        int compSize = 0;
    
        // test if resampling is required
        if (_audiocodec->getClockRate() != _layerSampleRate) {
    
            int nb_sample_up = nbSample;
            // _debug("_nbSample audiolayer->getMic(): %i \n", nbSample);
        
            // Store the length of the mic buffer in samples for recording
            _nSamplesMic = nbSample;
    
            int nbSamplesMax = _layerFrameSize * _audiocodec->getClockRate() / 1000;
            // _debug("_nbSamplesMax %i\n", nbSamplesMax);
    
            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...
                memset( micDataConverted + nbSample, 0, (nbSamplesMax-nbSample)*sizeof(int16));
                nbSample = nbSamplesMax;
            }
            compSize = _audiocodec->codecEncode( micDataEncoded, micDataConverted, nbSample*sizeof(int16));
    
        } else {
            // no resampling required
    
            int nbSamplesMax = _codecFrameSize;
            compSize = _audiocodec->codecEncode( micDataEncoded, micData, nbSample*sizeof(int16));
    
        }
    
        // 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, micDataEncoded, compSize);
        } else {
            _session->putData(timestamp, micDataEncoded, compSize);
        }
        /*} 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* spkrData  = (unsigned char*)adu->getData(); // data in char
        unsigned int size = adu->getSize(); // size in char
    
    
        // Decode data with relevant codec
        unsigned int max = (unsigned int)(_codecSampleRate * _layerFrameSize / 1000);
    
        // if ( size > max ) {
        //      _debug("We have received from RTP a packet larger than expected: %d VS %d\n", size, max);
        //      _debug("The packet size has been cropped\n");
        //      size=max;
        // }
    
        // test if resampling is required 
        if (_audiocodec != NULL) {
    
    
            int expandedSize = _audiocodec->codecDecode( spkrDataDecoded , spkrData , size );
            //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 ((unsigned int)nbInt16 > max) {
            //     _debug("We have decoded an RTP packet larger than expected: %d VS %d. Cropping.\n", nbInt16, max);
            //     nbInt16=max;
            // }
            int nbSample = nbInt16;
    
            if (_audiocodec->getClockRate() != _layerSampleRate) {
    
                // Do sample rate conversion
                int nb_sample_down = nbSample;
                nbSample = reSampleData(_codecSampleRate , nb_sample_down, UP_SAMPLING);
    
                // #ifdef DATAFORMAT_IS_FLOAT
                // #else
                // #endif
    
                // Stor the number of samples for recording
                _nSamplesSpkr = nbSample;
    
                //audiolayer->playSamples( spkrDataConverted, nbSample * sizeof(SFLDataFormat), true);
                audiolayer->putMain (spkrDataConverted, nbSample * sizeof(SFLDataFormat));
            } else {
    
                // Stor the number of samples for recording
                _nSamplesSpkr = nbSample;
                audiolayer->putMain (spkrDataDecoded, 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 converter->upsampleData( spkrDataDecoded , spkrDataConverted , sampleRate_codec , _layerSampleRate , nbSamples );
        }
        else if(status==DOWN_SAMPLING){
            return converter->downsampleData( micData , micDataConverted , sampleRate_codec , _layerSampleRate , nbSamples );
        }
        else
            return 0;
    }
    
    
    
    void
    AudioRtpRTX::run () {
    
      //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; 
    
      int sessionWaiting;
    
      //try {
    
        // Init the session
        initAudioRtpSession();
    
    
        // step = (int) (_layerFrameSize * _codecSampleRate / 1000);
        step = _codecFrameSize;
        // 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
        // step = (int) (_layerFrameSize * _codecSampleRate / 1000);
        step = _codecFrameSize;
    
        int countTime = 0; // for receive
        
        int threadSleep = 0;
        if (_codecSampleRate != 0)
            threadSleep = (_codecFrameSize * 1000) / _codecSampleRate;
        else
          threadSleep = _layerFrameSize;
    
        TimerPort::setTimer(threadSleep);
    
        audiolayer->startStream();
        _start.post();
        _debug("- ARTP Action: Start call %s\n",_ca->getCallId().c_str());
        while (!testCancel()) {
    
         
          // printf("AudioRtpRTX::run() _session->getFirstTimestamp() %i \n",_session->getFirstTimestamp());
        
          // printf("AudioRtpRTX::run() _session->isWaiting() %i \n",_session->isWaiting());
          /////////////////////
          ////////////////////////////
          // Send session
          ////////////////////////////
    
          sessionWaiting = _session->isWaiting();
    
          sendSessionFromMic(timestamp); 
          timestamp += step;
          
          ////////////////////////////
          // Recv session
          ////////////////////////////
          receiveSessionForSpkr(countTime);
          
          // Let's wait for the next transmit cycle
    
          
          if(sessionWaiting == 1){
            // Record mic and speaker during conversation
            _ca->recAudio.recData(spkrDataConverted,micData,_nSamplesSpkr,_nSamplesMic);
          }
          else {
            // Record mic only while leaving a message
            _ca->recAudio.recData(micData,_nSamplesMic);
          }
    
          Thread::sleep(TimerPort::getTimer());
          // TimerPort::incTimer(20); // 'frameSize' ms
          TimerPort::incTimer(threadSleep);
          
        }
        
        audiolayer->stopStream();
        _debug("- ARTP Action: Stop call %s\n",_ca->getCallId().c_str());
      //} catch(std::exception &e) {
        //_start.post();
        //_debug("! ARTP: Stop %s\n", e.what());
        //throw;
      //} catch(...) {
        //_start.post();
        //_debugException("* ARTP Action: Stop");
        //throw;
      //}
        
    }
    
    
    // EOF