Skip to content
Snippets Groups Projects
Select Git revision
  • 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
30 results

AudioRtpSession.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    AudioRtpSession.cpp 12.41 KiB
    /*
     *  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"
    
    #include "sip/sdp.h"
    #include "audio/audiolayer.h"
    #include "manager.h"
    
    namespace sfl
    {
    
    inline uint32
    timeval2microtimeout(const timeval& t)
    { return ((t.tv_sec * 1000000ul) + t.tv_usec); }
    
    
    AudioRtpSession::AudioRtpSession (ManagerImpl * manager, SIPCall * sipcall) :
    		// ost::SymmetricRTPSession (ost::InetHostAddress (sipcall->getLocalIp().c_str()), sipcall->getLocalAudioPort()),
    		AudioRtpRecordHandler(manager, sipcall),
    		ost::TRTPSessionBase<ost::DualRTPUDPIPv4Channel,ost::DualRTPUDPIPv4Channel,ost::AVPQueue>(ost::InetHostAddress (sipcall->getLocalIp().c_str()),
    																sipcall->getLocalAudioPort(),
    																0,
    																ost::MembershipBookkeeping::defaultMembersHashSize,
    																ost::defaultApplication())
    																		, _time (new ost::Time())
            																	, _mainloopSemaphore (0)
            																	, _manager (manager)
            																	, _timestamp (0)
            																	, _timestampIncrement (0)
            																	, _timestampCount (0)
            																	, _countNotificationTime (0)
            																	, _ca (sipcall)
    {
        setCancel (cancelDefault);
    
        assert (_ca);
    
        _info ("AudioRtpSession: Setting new RTP session with destination %s:%d", _ca->getLocalIp().c_str(), _ca->getLocalAudioPort());
    
        // static_cast<ost::DualRTPUDPIPv4Channel>(dso)->sendSocket->setTypeOfService(ost::Socket::tosLowDelay);
        // static_cast<ost::DualRTPChannel<ost::DualRTPUDPIPv4Channel> >(dso)->sendSocket->setTypeOfService(ost::Socket::tosLowDelay);
    
        // setTypeOfService(tosEnhanced);
    }
    
    AudioRtpSession::~AudioRtpSession()
    {
        _info ("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)
    {
    	_debug("AudioRtpSession: Set session scheduling timeout (%d) and expireTimeout (%d)", sfl::schedulingTimeout, sfl::expireTimeout);
    
    	setSchedulingTimeout (sfl::schedulingTimeout);
    	setExpireTimeout (sfl::expireTimeout);
    }
    
    void AudioRtpSession::setSessionMedia (AudioCodec* audioCodec)
    {
    	_debug("AudioRtpSession: Set session media");
    
    	// 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 == g722PayloadType)
            _timestampIncrement = g722RtpTimeincrement;
        else
            _timestampIncrement = frameSize;
    
        _debug ("AudioRptSession: Codec payload: %d", payloadType);
        _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 == g722PayloadType) {
            _debug ("AudioRtpSession: Setting G722 payload format");
            setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) payloadType, g722RtpClockRate));
        } 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)
    {
        _info ("AudioRtpSession: Setting IP address for the RTP session");
    
        if (_ca == NULL) {
            _error ("AudioRtpSession: Sipcall is gone.");
            throw AudioRtpSessionException();
        }
    
        // 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)
    {
        _debug("AudioRtpSession: Update destination ip address");
    
        // 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 AudioRtpSession::onRTPPacketRecv (ost::IncomingRTPPkt&)
    {
        _debug ("AudioRtpSession: onRTPPacketRecv");
    
    	// receiveSpeakerData ();
    
        return true;
    }
    
    
    
    void AudioRtpSession::sendMicData()
    {
        int compSize = processDataEncode();
    
        // If no data, return
        if(!compSize)
        	return;
    
        // Reset timestamp to make sure the timing information are up to date
        if (_timestampCount > RTP_TIMESTAMP_RESET_FREQ) {
            _timestamp = getCurrentTimestamp();
            _timestampCount = 0;
        }
    
        // getCurrentTimestamp();
        // RTPDataQueue::getTimestampIncrement();
    
        // Increment timestamp for outgoing packet
        _timestamp += _timestampIncrement;
        _debug("sendMicData: %d, timestamp increment %d", _timestamp, _timestampIncrement);
    
        // putData put the data on RTP queue, sendImmediate bypass this queue
        putData (_timestamp, getMicDataEncoded(), compSize);
        // sendData()
    }
    
    
    void AudioRtpSession::receiveSpeakerData ()
    {
    	
        const ost::AppDataUnit* adu = NULL;
    
        int packetTimestamp = getFirstTimestamp();
    
        adu = getData (packetTimestamp);
    
        if (!adu) {
        	_debug("receiveSpeakerData: no data!");
            return;
        }
    
        unsigned char* spkrDataIn = NULL;
        unsigned int size = 0;
    
        spkrDataIn  = (unsigned char*) adu->getData(); // data in char
        size = adu->getSize(); // size in char
    
    
        // DTMF over RTP, size must be over 4 in order to process it as voice data
        if (size > 4) {
            processDataDecode (spkrDataIn, size);
        }
    
        delete adu;
    }
    
    void AudioRtpSession::notifyIncomingCall()
    {
    	// Notify (with a beep) an incoming call when there is already a call
    	if (Manager::instance().incomingCallWaiting() > 0) {
    		_countNotificationTime += _time->getSecond();
    		int countTimeModulo = _countNotificationTime % 5000;
    
    		if ( (countTimeModulo - _countNotificationTime) < 0) {
    			Manager::instance().notificationIncomingCall();
    		}
    		_countNotificationTime = countTimeModulo;
    	}
    }
    
    
    int AudioRtpSession::startRtpThread (AudioCodec* audiocodec)
    {
        _debug ("AudioRtpSession: Starting main thread");
        setSessionTimeouts();
        setSessionMedia (audiocodec);
        initBuffers();
        initNoiseSuppress();
        enableStack();
        int ret = start (_mainloopSemaphore);
        return ret;
    }
    
    void AudioRtpSession::run ()
    {
    
        // Timestamp must be initialized randomly, already done when instantiating outgoing queue
        _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();
    
        _debug ("AudioRtpSession: Entering mainloop for call %s",_ca->getCallId().c_str());
    
    	uint32 timeout = 0;
    	while ( isActive() ) {
    		if ( timeout < 1000 ){ // !(timeout/1000)
    			timeout = getSchedulingTimeout();
    		}
    
    		// timeout = ;
    
    		_manager->getAudioLayerMutex()->enter();
    
    		// Send session
    		if (getEventQueueSize() > 0) {
    			sendDtmfEvent (getEventQueue()->front());
    		} else {
    			sendMicData ();
    		}
    
    		receiveSpeakerData();
    
    		// This also should be moved
    		notifyIncomingCall();
    
    		_manager->getAudioLayerMutex()->leave();
    
    		setCancel(cancelDeferred);
    		controlReceptionService();
    		controlTransmissionService();
    		setCancel(cancelImmediate);
    		uint32 maxWait = timeval2microtimeout(getRTCPCheckInterval());
    		// make sure the scheduling timeout is
    		// <= the check interval for RTCP
    		// packets
    		_debug("timeout before: %d, maxwait %d", timeout, maxWait);
    		timeout = (timeout > maxWait)? maxWait : timeout;
    		_debug("timeout after: %d", timeout);
    
    		if ( timeout < 1000 ) { // !(timeout/1000)
    			setCancel(cancelDeferred);
    			dispatchDataPacket();
    			setCancel(cancelImmediate);
    			timerTick();
    		} else {
    			if ( isPendingData(timeout/1000) ) {
    				setCancel(cancelDeferred);
    				if (isActive()) { // take in only if active
    					takeInDataPacket();
    				}
    				setCancel(cancelImmediate);
    			}
    			timeout = 0;
    		}
    	}
    
        _debug ("AudioRtpSession: Left main loop for call %s", _ca->getCallId().c_str());
    }
    
    }