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

AudioZrtpSession.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    AudioZrtpSession.cpp 13.02 KiB
    /*
     *  Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc.
     *  Author: Pierre-Luc Bacon <pierre-luc.bacon@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 "AudioZrtpSession.h"
    #include "ZrtpSessionCallback.h"
    #include "user_cfg.h"
    
    #include "sip/sipcall.h"
    #include "sip/sdp.h"
    #include "audio/audiolayer.h"
    #include "manager.h"
    
    #include <libzrtpcpp/ZrtpQueue.h>
    #include <libzrtpcpp/ZrtpUserCallback.h>
    
    #include <cstdio>
    #include <cstring>
    #include <cerrno>
    
    namespace sfl
    {
    
    AudioZrtpSession::AudioZrtpSession (ManagerImpl * manager, SIPCall * sipcall, const std::string& zidFilename) :
            ost::SymmetricZRTPSession (ost::InetHostAddress (sipcall->getLocalIp().c_str()), sipcall->getLocalAudioPort()),
            AudioRtpRecordHandler(manager, sipcall)
    																											  , _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: Setting new RTP session with destination %s:%d", _ca->getLocalIp().c_str(), _ca->getLocalAudioPort());
    
        setTypeOfService(tosEnhanced);
    }
    
    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)
    {
    
        if (_zidFilename.empty()) {
            throw ZrtpZidException();
        }
    
        std::string zidCompleteFilename;
    
        // xdg_config = std::string (HOMEDIR) + DIR_SEPARATOR_STR + ".cache/sflphone";
    
        std::string xdg_config = std::string (HOMEDIR) + DIR_SEPARATOR_STR + ".cache" + DIR_SEPARATOR_STR + PROGDIR + "/" + _zidFilename;
    
        _debug ("    xdg_config %s", xdg_config.c_str());
    
        if (XDG_CACHE_HOME != NULL) {
            std::string xdg_env = std::string (XDG_CACHE_HOME) + _zidFilename;
            _debug ("    xdg_env %s", xdg_env.c_str());
            (xdg_env.length() > 0) ? zidCompleteFilename = xdg_env : zidCompleteFilename = xdg_config;
        } else
            zidCompleteFilename = xdg_config;
    
    
        if (initialize (zidCompleteFilename.c_str()) >= 0) {
            _debug ("Register callbacks");
            setEnableZrtp (true);
            setUserCallback (new ZrtpSessionCallback (_ca));
            return;
        }
    
        _debug ("Initialization from ZID file failed. Trying to remove...");
    
        if (remove (zidCompleteFilename.c_str()) !=0) {
            _debug ("Failed to remove zid file because of: %s", strerror (errno));
            throw ZrtpZidException();
        }
    
        if (initialize (zidCompleteFilename.c_str()) < 0) {
            _debug ("ZRTP initialization failed");
            throw ZrtpZidException();
        }
    
        return;
    }
    
    void AudioZrtpSession::setSessionTimeouts (void)
    {
    	_debug("AudioZrtpSession: Set session scheduling timeout (%d) and expireTimeout (%d)", sfl::schedulingTimeout, sfl::expireTimeout);
    
    	setSchedulingTimeout (sfl::schedulingTimeout);
    	setExpireTimeout (sfl::expireTimeout);
    }
    
    void AudioZrtpSession::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 ("AudioZrtpSession: Codec payload: %d", payloadType);
        _debug ("AudioZrtpSession: Codec sampling rate: %d", smplRate);
        _debug ("AudioZrtpSession: Codec frame size: %d", frameSize);
        _debug ("AudioZrtpSession: 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 ("AudioZrtpSession: Setting G722 payload format");
            setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) payloadType, g722RtpClockRate));
        } else if (dynamic) {
            _debug ("AudioZrtpSession: Setting dynamic payload format");
            setPayloadFormat (ost::DynamicPayloadFormat ( (ost::PayloadType) payloadType, smplRate));
        } else if (dynamic && payloadType != g722PayloadType) {
            _debug ("AudioZrtpSession: Setting static payload format");
            setPayloadFormat (ost::StaticPayloadFormat ( (ost::StaticPayloadType) payloadType));
        }
    
    }
    
    void AudioZrtpSession::setDestinationIpAddress (void)
    {
        _info ("AudioZrtpSession: Setting IP address for the RTP session");
    
        if (_ca == NULL) {
            _error ("AudioZrtpSession: 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 ("AudioZrtpSession: 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 ("AudioZrtpSession: New remote address for session: %s:%d",
               _ca->getLocalSDP()->get_remote_ip().data(), _remote_port);
    
        if (!addDestination (_remote_ip, _remote_port)) {
            _warn ("AudioZrtpSession: Can't add new destination to session!");
            return;
        }
    }
    
    void AudioZrtpSession::updateDestinationIpAddress (void)
    {
    	_debug("AudioZrtpSession: 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 ("AudioZrtpSession: Could not remove previous destination");
    
        // new destination is stored in call
        // we just need to recall this method
        setDestinationIpAddress();
    }
    
    void AudioZrtpSession::sendDtmfEvent (sfl::DtmfEvent *dtmf)
    {
        _debug ("AudioZrtpSession: 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 AudioZrtpSession::onRTPPacketRecv (ost::IncomingRTPPkt&)
    {
    	receiveSpeakerData();
    
        return true;
    }
    
    
    void AudioZrtpSession::sendMicData()
    {
    	int compSize = processDataEncode();
    
    	// if no data return
    	if(!compSize)
    		return;
    
        // Increment timestamp for outgoing packet
        _timestamp += _timestampIncrement;
    
        // Reset timestamp to make sure the timing information are up to date
        if (_timestampCount > RTP_TIMESTAMP_RESET_FREQ) {
            _timestamp = getCurrentTimestamp();
            _timestampCount = 0;
        }
    
        // Increment timestamp for outgoing packet
        _timestamp += _timestampIncrement;
    
        // putData put the data on RTP queue, sendImmediate bypass this queue
        putData (_timestamp, getMicDataEncoded(), compSize);
    }
    
    
    void AudioZrtpSession::receiveSpeakerData ()
    {
        const ost::AppDataUnit* adu = NULL;
    
        int packetTimestamp = getFirstTimestamp();
    
        adu = getData (packetTimestamp);
    
        if (!adu) {
            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 AudioZrtpSession::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 AudioZrtpSession::startRtpThread (AudioCodec* audiocodec)
    {
        _debug ("AudioZrtpSession: Starting main thread");
        setSessionTimeouts();
        setSessionMedia (audiocodec);
        initBuffers();
        initNoiseSuppress();
        enableStack();
    
        int ret = start(_mainloopSemaphore);
    
        return ret;
    }
    
    void AudioZrtpSession::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();
    
        _debug ("AudioZrtpSession: Entering mainloop for call %s",_ca->getCallId().c_str());
    
        uint32 timeout = 0;
        while ( isActive() ) {
    
        	if ( timeout < 1000 ){ // !(timeout/1000)
        		timeout = getSchedulingTimeout();
        	}
    
            _manager->getAudioLayerMutex()->enter();
    
            // Send session
            if (getEventQueueSize() > 0) {
                sendDtmfEvent (getEventQueue()->front());
            } else {
                sendMicData ();
            }
    
            // 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
            timeout = (timeout > maxWait)? maxWait : 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 ("AudioZrtpSession: Left main loop for call %s", _ca->getCallId().c_str());
    }
    
    }