diff --git a/src/Makefile.am b/src/Makefile.am index dc83d84f8a88dce7600a278648ba0b2d265180b3..7af7dbfec2d5350a0c1b9aa2d9cc6cfa4ec44835 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,6 +29,8 @@ sflphoned_SOURCES = \ call.cpp \ account.cpp \ sipcall.cpp \ + sdp.cpp \ + sdpmedia.cpp \ $(IAXSOURCES) sflphoned_CXXFLAGS = \ @@ -67,7 +69,9 @@ noinst_HEADERS = \ accountcreator.h \ sipvoiplink.h \ call.h \ - sipcall.h + sipcall.h \ + sdp.h \ + sdpmedia.h libsflphone_la_LIBADD = \ $(src)/libs/stund/libstun.la \ diff --git a/src/audio/audiortp.cpp b/src/audio/audiortp.cpp index 24042f7d2f4ce289962eba87805dd1a25a56cc4e..02972a3058afc50dc73fc175fd7aba350e9732f0 100644 --- a/src/audio/audiortp.cpp +++ b/src/audio/audiortp.cpp @@ -128,6 +128,7 @@ AudioRtpRTX::AudioRtpRTX (SIPCall *sipcall, bool sym) : time(new ost::Time()), _ _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; @@ -185,15 +186,16 @@ AudioRtpRTX::initBuffers() void AudioRtpRTX::initAudioRtpSession (void) { + try { if (_ca == 0) { return; } - _audiocodec = Manager::instance().getCodecDescriptorMap().getCodec( _ca->getAudioCodec() ); - _codecSampleRate = _audiocodec->getClockRate(); + _audiocodec = _ca->getLocalSDP()->get_session_media (); + _codecSampleRate = _audiocodec->getClockRate(); - _debug("Init audio RTP session\n"); - ost::InetHostAddress remote_ip(_ca->getRemoteIp().c_str()); + 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->getRemoteIp().data()); + _debug("! ARTP Thread Error: Target IP address [%s] is not correct!\n", _ca->getLocalSDP()->get_remote_ip().data()); return; } @@ -210,12 +212,12 @@ AudioRtpRTX::initAudioRtpSession (void) } if (!_sym) { - if ( !_sessionRecv->addDestination(remote_ip, (unsigned short) _ca->getRemoteAudioPort()) ) { - _debug("AudioRTP Thread Error: could not connect to port %d\n", _ca->getRemoteAudioPort()); + 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->getRemoteAudioPort())) { - _debug("! ARTP Thread Error: could not connect to port %d\n", _ca->getRemoteAudioPort()); + 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; } @@ -233,7 +235,7 @@ AudioRtpRTX::initAudioRtpSession (void) //_debug("AudioRTP Thread: Added session destination %s\n", remote_ip.getHostname() ); - if (!_session->addDestination (remote_ip, (unsigned short) _ca->getRemoteAudioPort())) { + if (!_session->addDestination (remote_ip, (unsigned short)_ca->getLocalSDP()->get_remote_audio_port() )) { return; } @@ -251,7 +253,87 @@ AudioRtpRTX::initAudioRtpSession (void) } 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; + }*/ } diff --git a/src/call.cpp b/src/call.cpp index 32cc4d7bcda73c5da7f5fb5f38d4eac563d92f33..b1625d346e3bc726bb6c48c66cf91a49e1323ad5 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -22,14 +22,10 @@ Call::Call(const CallID& id, Call::CallType type) : _callMutex() - , _codecMap() - , _audioCodec() , _audioStarted(false) , _localIPAddress("") , _localAudioPort(0) , _localExternalAudioPort(0) - , _remoteIPAddress("") - , _remoteAudioPort(0) , _id(id) , _type(type) , _connectionState(Call::Disconnected) @@ -85,12 +81,6 @@ Call::getState() return _callState; } -CodecDescriptor& -Call::getCodecMap() -{ - return _codecMap; -} - const std::string& Call::getLocalIp() { @@ -105,26 +95,6 @@ Call::getLocalAudioPort() return _localAudioPort; } -unsigned int -Call::getRemoteAudioPort() -{ - ost::MutexLock m(_callMutex); - return _remoteAudioPort; -} - -const std::string& -Call::getRemoteIp() -{ - ost::MutexLock m(_callMutex); - return _remoteIPAddress; -} - -AudioCodecType -Call::getAudioCodec() -{ - return _audioCodec; -} - void Call::setAudioStart(bool start) { diff --git a/src/call.h b/src/call.h index 6b247e036017d684056f9db64a59d8a1460d35ae..d82ff7ba583c2253571532ddcd7f80561cf3aad2 100644 --- a/src/call.h +++ b/src/call.h @@ -23,7 +23,6 @@ #include <cc++/thread.h> // for mutex #include <sstream> -#include "audio/codecDescriptor.h" #include "plug-in/audiorecorder/audiorecord.h" /* @@ -145,20 +144,7 @@ class Call{ */ bool isAudioStarted(); - // AUDIO - /** - * Set internal codec Map: initialization only, not protected - * @param map The codec map - */ - void setCodecMap(const CodecDescriptor& map) { _codecMap = map; } - - /** - * Get internal codec Map: initialization only, not protected - * @return CodecDescriptor The codec map - */ - CodecDescriptor& getCodecMap(); - - /** + /** * Set my IP [not protected] * @param ip The local IP address */ @@ -194,24 +180,6 @@ class Call{ */ unsigned int getLocalAudioPort(); - /** - * Return audio port at destination [mutex protected] - * @return unsigned int The remote audio port - */ - unsigned int getRemoteAudioPort(); - - /** - * Return IP of destination [mutex protected] - * @return const std:string The remote IP address - */ - const std::string& getRemoteIp(); - - /** - * Return audio codec [mutex protected] - * @return AudioCodecType The payload of the codec - */ - AudioCodecType getAudioCodec(); - /** * @return Return the file name for this call */ @@ -246,30 +214,6 @@ class Call{ /** Protect every attribute that can be changed by two threads */ ost::Mutex _callMutex; - /** - * Set remote's IP addr. [not protected] - * @param ip The remote IP address - */ - void setRemoteIP(const std::string& ip) { _remoteIPAddress = ip; } - - /** - * Set remote's audio port. [not protected] - * @param port The remote audio port - */ - void setRemoteAudioPort(unsigned int port) { _remoteAudioPort = port; } - - /** - * Set the audio codec used. [not protected] - * @param audioCodec The payload of the codec - */ - void setAudioCodec(AudioCodecType audioCodec) { _audioCodec = audioCodec; } - - /** Codec Map */ - CodecDescriptor _codecMap; - - /** Codec pointer */ - AudioCodecType _audioCodec; - bool _audioStarted; // Informations about call socket / audio @@ -283,13 +227,7 @@ class Call{ /** Port assigned to my machine by the NAT, as seen by remote peer (he connects there) */ unsigned int _localExternalAudioPort; - /** Remote's IP address */ - std::string _remoteIPAddress; - - /** Remote's audio port */ - unsigned int _remoteAudioPort; - - + private: /** Unique ID of the call */ diff --git a/src/iaxcall.cpp b/src/iaxcall.cpp index 05327d77d6a15719bf26ce6b394a08ba33a992a1..c2fc71c6ba70db21e319975479a6a4df8eec571c 100644 --- a/src/iaxcall.cpp +++ b/src/iaxcall.cpp @@ -112,3 +112,16 @@ IAXCall::getFirstMatchingFormat(int needles) } return 0; } + +CodecDescriptor& IAXCall::getCodecMap() +{ + return _codecMap; +} + +AudioCodecType IAXCall::getAudioCodec() +{ + return _audioCodec; +} + + + diff --git a/src/iaxcall.h b/src/iaxcall.h index b572a7418bb9fc7b24153a10f8938f2b27afa3ae..0a728248c9c114aad21aaaf6306eb080a8a05875 100644 --- a/src/iaxcall.h +++ b/src/iaxcall.h @@ -21,6 +21,8 @@ #define IAXCALL_H #include "call.h" +#include "audio/codecDescriptor.h" + #include <iax2/iax-client.h> #include <iax2/frame.h> @@ -90,11 +92,41 @@ public: */ int getFirstMatchingFormat(int needles); + // AUDIO + /** + * Set internal codec Map: initialization only, not protected + * @param map The codec map + */ + void setCodecMap(const CodecDescriptor& map) { _codecMap = map; } + + /** + * Get internal codec Map: initialization only, not protected + * @return CodecDescriptor The codec map + */ + CodecDescriptor& getCodecMap(); + + /** + * Return audio codec [mutex protected] + * @return AudioCodecType The payload of the codec + */ + AudioCodecType getAudioCodec(); private: /** Each call is associated with an iax_session */ struct iax_session* _session; + /** + * Set the audio codec used. [not protected] + * @param audioCodec The payload of the codec + */ + void setAudioCodec(AudioCodecType audioCodec) { _audioCodec = audioCodec; } + + /** Codec Map */ + CodecDescriptor _codecMap; + + /** Codec pointer */ + AudioCodecType _audioCodec; + /** * Format currently in use in the conversation, * sent in each outgoing voice packet. diff --git a/src/sdp.cpp b/src/sdp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..011875e2ef90491bac7b74e8f51711e5f600a5c7 --- /dev/null +++ b/src/sdp.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * + * Author: Emmanuel Milou <emmanuel.milou@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 "sdp.h" +#include "global.h" +#include "manager.h" + + +static const pj_str_t STR_AUDIO = { (char*)"audio", 5}; +static const pj_str_t STR_VIDEO = { (char*)"video", 5}; +static const pj_str_t STR_IN = { (char*)"IN", 2 }; +static const pj_str_t STR_IP4 = { (char*)"IP4", 3}; +static const pj_str_t STR_IP6 = { (char*)"IP6", 3}; +static const pj_str_t STR_RTP_AVP = { (char*)"RTP/AVP", 7 }; +static const pj_str_t STR_SDP_NAME = { (char*)"sflphone", 8 }; +static const pj_str_t STR_SENDRECV = { (char*)"sendrecv", 8 }; + +Sdp::Sdp( pj_pool_t *pool ) + : _local_media_cap(), _session_media(0), _ip_addr( "" ), _local_offer( NULL ), _negociated_offer(NULL), _negociator(NULL), _pool(NULL), _local_extern_audio_port(0) +{ + _pool = pool; +} + +Sdp::~Sdp() { + + //unsigned int k; + + /* + for( k=0; k<_session_media.size(); k++ ){ + delete _session_media[k]; + _session_media[k] = 0; + }*/ + + //for( k=0; k<_local_media_cap.size(); k++ ){ + // delete _local_media_cap[k]; + //_local_media_cap[k] = 0; + //} +} + +void Sdp::set_media_descriptor_line( sdpMedia *media, pjmedia_sdp_media** p_med ) { + + pjmedia_sdp_media* med; + pjmedia_sdp_rtpmap rtpmap; + pjmedia_sdp_attr *attr; + AudioCodec *codec; + int count, i; + std::string tmp; + + med = PJ_POOL_ZALLOC_T( _pool, pjmedia_sdp_media ); + + // Get the right media format + pj_strdup(_pool, &med->desc.media, + ( media->get_media_type() == MIME_TYPE_AUDIO ) ? &STR_AUDIO : &STR_VIDEO ); + med->desc.port_count = 1; + med->desc.port = media->get_port(); + pj_strdup (_pool, &med->desc.transport, &STR_RTP_AVP); + + // Media format ( RTP payload ) + count = media->get_media_codec_list().size(); + med->desc.fmt_count = count; + + // add the payload list + for(i=0; i<count; i++){ + codec = media->get_media_codec_list()[i]; + tmp = this->convert_int_to_string (codec->getPayload ()); + _debug ("%s\n", tmp.c_str()); + pj_strdup2( _pool, &med->desc.fmt[i], tmp.c_str()); + + // Add a rtpmap field for each codec + // We could add one only for dynamic payloads because the codecs with static RTP payloads + // are entirely defined in the RFC 3351, but if we want to add other attributes like an asymmetric + // connection, the rtpmap attribute will be useful to specify for which codec it is applicable + rtpmap.pt = med->desc.fmt[i]; + rtpmap.enc_name = pj_str( (char*) codec->getCodecName().c_str() ); + rtpmap.clock_rate = codec->getClockRate(); + // Add the channel number only if different from 1 + if( codec->getChannel() > 1 ) + rtpmap.param = pj_str( (char*) codec->getChannel() ); + else + rtpmap.param.slen = 0; + pjmedia_sdp_rtpmap_to_attr( _pool, &rtpmap, &attr ); + med->attr[med->attr_count++] = attr; + } + + // Add the direction stream + attr = (pjmedia_sdp_attr*)pj_pool_zalloc( _pool, sizeof(pjmedia_sdp_attr) ); + pj_strdup2( _pool, &attr->name, media->get_stream_direction_str().c_str()); + med->attr[ med->attr_count++] = attr; + + *p_med = med; +} + +int Sdp::create_local_offer (){ + pj_status_t status; + + _debug ("Create local offer\n"); + // Build local media capabilities + set_local_media_capabilities (); + + // Reference: RFC 4566 [5] + + /* Create and initialize basic SDP session */ + this->_local_offer = PJ_POOL_ZALLOC_T(_pool, pjmedia_sdp_session); + this->_local_offer->conn = PJ_POOL_ZALLOC_T(_pool, pjmedia_sdp_conn); + + /* Initialize the fields of the struct */ + sdp_add_protocol(); + sdp_add_origin(); + sdp_add_session_name(); + sdp_add_connection_info(); + sdp_add_timing(); + //sdp_addAttributes( _pool ); + sdp_add_media_description( ); + + //toString (); + + // Validate the sdp session + status = pjmedia_sdp_validate( this->_local_offer ); + if (status != PJ_SUCCESS) + return status; + + return PJ_SUCCESS; +} + +int Sdp::create_initial_offer( ){ + pj_status_t status; + pjmedia_sdp_neg_state state; + + _debug ("Create initial offer\n"); + // Build the SDP session descriptor + create_local_offer( ); + + // Create the SDP negociator instance with local offer + status = pjmedia_sdp_neg_create_w_local_offer( _pool, get_local_sdp_session(), &_negociator); + state = pjmedia_sdp_neg_get_state( _negociator ); + + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + return PJ_SUCCESS; +} + +int Sdp::receiving_initial_offer( pjmedia_sdp_session* remote ){ + // Create the SDP negociator instance by calling + // pjmedia_sdp_neg_create_w_remote_offer with the remote offer, and by providing the local offer ( optional ) + + pj_status_t status; + pjmedia_sdp_neg_state state; + + _debug ("Receiving initial offer\n"); + + // Create the SDP negociator instance by calling + // pjmedia_sdp_neg_create_w_remote_offer with the remote offer, and by providing the local offer ( optional ) + + // Build the local offer to respond + create_local_offer( ); + + // Retrieve some useful remote information + this->fetch_media_transport_info_from_remote_sdp (remote); + + status = pjmedia_sdp_neg_create_w_remote_offer( _pool, + get_local_sdp_session(), remote, &_negociator ); + state = pjmedia_sdp_neg_get_state( _negociator ); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + return PJ_SUCCESS; +} + +void Sdp::sdp_add_protocol( void ){ + this->_local_offer->origin.version = 0; +} + +void Sdp::sdp_add_origin( void ){ + pj_time_val tv; + pj_gettimeofday(&tv); + + this->_local_offer->origin.user = pj_str(pj_gethostname()->ptr); + // Use Network Time Protocol format timestamp to ensure uniqueness. + this->_local_offer->origin.id = tv.sec + 2208988800UL; + // The type of network ( IN for INternet ) + this->_local_offer->origin.net_type = STR_IN; + // The type of address + this->_local_offer->origin.addr_type = STR_IP4; + // The address of the machine from which the session was created + this->_local_offer->origin.addr = pj_str( (char*)_ip_addr.c_str() ); +} + +void Sdp::sdp_add_session_name( void ){ + this->_local_offer->name = STR_SDP_NAME; +} + + +void Sdp::sdp_add_connection_info( void ){ + this->_local_offer->conn->net_type = _local_offer->origin.net_type; + this->_local_offer->conn->addr_type = _local_offer->origin.addr_type; + this->_local_offer->conn->addr = _local_offer->origin.addr; +} + + +void Sdp::sdp_add_timing( void ){ + // RFC 3264: An offer/answer model session description protocol + // As the session is created and destroyed through an external signaling mean (SIP), the line + // should have a value of "0 0". + + this->_local_offer->time.start = this->_local_offer->time.stop = 0; +} + +void Sdp::sdp_add_attributes( ){ + pjmedia_sdp_attr *a; + this->_local_offer->attr_count = 1; + a = PJ_POOL_ZALLOC_T(_pool, pjmedia_sdp_attr); + a->name=STR_SENDRECV; + _local_offer->attr[0] = a; +} + + +void Sdp::sdp_add_media_description( ){ + pjmedia_sdp_media* med; + int nb_media, i; + + med = PJ_POOL_ZALLOC_T(_pool, pjmedia_sdp_media); + nb_media = get_local_media_cap().size(); + this->_local_offer->media_count = nb_media; + + for( i=0; i<nb_media; i++ ){ + set_media_descriptor_line( get_local_media_cap()[i], &med ); + this->_local_offer->media[i] = med; + } +} + + +std::string Sdp::media_to_string( void ){ + int size, i; + std::ostringstream res; + + size = _local_media_cap.size(); + for( i = 0; i < size ; i++ ){ + res << _local_media_cap[i]->to_string(); + } + + res << std::endl; + return res.str(); +} + +void Sdp::clean_session_media(){ + _session_media.clear(); +} + +void Sdp::set_negociated_offer( const pjmedia_sdp_session *sdp ){ + + int nb_media, nb_codecs; + int i,j, port; + pjmedia_sdp_media *current; + sdpMedia *media; + std::string type, dir; + CodecsMap codecs_list; + CodecsMap::iterator iter; + AudioCodec *codec_to_add; + + _negociated_offer = (pjmedia_sdp_session*)sdp; + + //this->fetch_remote_ip_from_sdp ((pjmedia_sdp_session*)sdp); + + codecs_list = Manager::instance().getCodecDescriptorMap().getCodecsMap(); + + // retrieve the media information + nb_media = _negociated_offer->media_count; + for( i=0; i<nb_media ; i++ ){ + // Retrieve the media + current = _negociated_offer->media[i]; + type = current->desc.media.ptr; + port = current->desc.port; + media = new sdpMedia( type, port ); + // Retrieve the payload + nb_codecs = current->desc.fmt_count; // Must be one + for( j=0 ; j<nb_codecs ; j++ ){ + iter = codecs_list.find((AudioCodecType)atoi(current->desc.fmt[j].ptr)); + if (iter==codecs_list.end()) + return; + media->add_codec(iter->second); + } + _session_media.push_back(media); + } +} + +AudioCodec* Sdp::get_session_media( void ){ + + int nb_media; + int nb_codec; + + nb_media = _session_media.size(); + nb_codec = _session_media[0]->get_media_codec_list().size(); + + return _session_media[0]->get_media_codec_list()[0]; +} + + +void Sdp::toString (void) { + + std::ostringstream sdp; + int count, i; + + sdp << "origin= " << _local_offer->origin.user.ptr << "\n"; + sdp << "origin.id= " << _local_offer->origin.id << "\n"; + sdp << "origin.version= " << _local_offer->origin.version<< "\n"; + sdp << "origin.net_type= " << _local_offer->origin.net_type.ptr<< "\n"; + sdp << "origin.addr_type= " << _local_offer->origin.addr_type.ptr<< "\n"; + + sdp << "name=" << _local_offer->name.ptr<< "\n"; + + sdp << "conn.net_type=" << _local_offer->conn->net_type.ptr<< "\n"; + sdp << "conn.addr_type=" << _local_offer->conn->addr_type.ptr<< "\n"; + sdp << "conn.addr=" << _local_offer->conn->addr.ptr<< "\n"; + + sdp << "start=" <<_local_offer->time.start<< "\n"; + sdp << "stop=" <<_local_offer->time.stop<< "\n"; + + sdp << "attr_count=" << _local_offer->attr_count << "\n"; + sdp << "media_count=" << _local_offer->media_count << "\n"; + sdp << "m=" << _local_offer->media[0]->desc.media.ptr << " "; + sdp << _local_offer->media[0]->desc.port << " "; + sdp << _local_offer->media[0]->desc.transport.ptr << " "; + count = _local_offer->media[0]->desc.fmt_count; + for (i=0; i<count; i++) { + sdp << _local_offer->media[0]->desc.fmt[i].ptr << " "; + } + sdp << "\n"; + + _debug ("LOCAL SDP: \n%s\n", sdp.str().c_str()); + +} + +void Sdp::set_local_media_capabilities () { + + CodecOrder selected_codecs; + int i; + sdpMedia *audio; + CodecsMap codecs_list; + CodecsMap::iterator iter; + + // Clean it first + _local_media_cap.clear(); + + _debug ("Fetch local media capabilities .......... %i\n" , get_local_extern_audio_port()); + + /* Only one audio media used right now */ + audio = new sdpMedia(MIME_TYPE_AUDIO); + audio->set_port (get_local_extern_audio_port()); + + /* We retrieve the codecs selected by the user */ + selected_codecs = Manager::instance().getCodecDescriptorMap().getActiveCodecs(); + codecs_list = Manager::instance().getCodecDescriptorMap().getCodecsMap(); + for (i=0; i<selected_codecs.size(); i++){ + iter=codecs_list.find(selected_codecs[i]); + if (iter!=codecs_list.end()){ + audio->add_codec (iter->second); + } + } + _local_media_cap.push_back (audio); + _debug ("%s\n", audio->to_string ().c_str()); +} + +void Sdp::attribute_port_to_all_media (int port) { + + std::vector<sdpMedia*> medias; + int i, size; + + set_local_extern_audio_port (port); + + medias = get_local_media_cap (); + size = medias.size(); + + for(i=0; i<size; i++) { + medias[i]->set_port (port); + } +} + +std::string Sdp::convert_int_to_string (int value) { + std::ostringstream result; + result << value; + return result.str(); +} + +void Sdp::fetch_remote_ip_from_sdp (pjmedia_sdp_session *r_sdp) { + + std::string remote_ip; + + remote_ip = r_sdp->conn->addr.ptr; + _debug("************************************************** Remote Audio IP: %s\n", remote_ip.c_str()); + this->set_remote_ip(remote_ip); +} + +void Sdp::fetch_remote_audio_port_from_sdp (pjmedia_sdp_media *r_media){ + + int remote_port; + + remote_port = r_media->desc.port; + _debug(" Remote Audio Port: %d\n", remote_port); + this->set_remote_audio_port(remote_port); +} + +void Sdp::fetch_media_transport_info_from_remote_sdp (pjmedia_sdp_session *remote_sdp) { + + pjmedia_sdp_media *r_media; + + this->get_remote_sdp_media_from_offer (remote_sdp, &r_media); + + if (r_media==NULL){ + _debug("SDP Failure: no remote sdp media found in the remote offer\n"); + return; + } + + this->fetch_remote_audio_port_from_sdp (r_media); + this->fetch_remote_ip_from_sdp (remote_sdp); +} + +void Sdp::get_remote_sdp_media_from_offer (pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media** r_media){ + int count, i; + + count = remote_sdp->media_count; + *r_media = NULL; + for(i = 0; i < count; ++i) { + if(pj_stricmp2(&remote_sdp->media[i]->desc.media, "audio") == 0) { + *r_media = remote_sdp->media[i]; + return; + } + } +} + diff --git a/src/sdp.h b/src/sdp.h new file mode 100644 index 0000000000000000000000000000000000000000..9bbd0d5344e43b37594a759067e9a9f4b82f7695 --- /dev/null +++ b/src/sdp.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * + * Author: Emmanuel Milou <emmanuel.milou@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. + */ + +#ifndef _SDP_H +#define _SDP_H + +#include <pjmedia/sdp.h> +#include <pjmedia/sdp_neg.h> +#include <pjsip/sip_transport.h> +#include <pjlib.h> +#include <pj/pool.h> +#include <pj/assert.h> + +#include "audio/codecDescriptor.h" +#include "sdpmedia.h" + +class Sdp { + + public: + + /* + * Class Constructor. + * + * @param ip_addr + */ + Sdp(pj_pool_t *pool); + + /* Class destructor */ + ~Sdp(); + + /* + * Read accessor. Get the list of the local media capabilities. + * + * @return std::vector<sdpMedia*> the vector containing the different media + */ + std::vector<sdpMedia*> get_local_media_cap( void ) { return _local_media_cap; } + + /* + * Read accessor. Get the sdp session information + * + * @return pjmedia_sdp_session The structure that describes a SDP session + */ + pjmedia_sdp_session* get_local_sdp_session( void ) { return _local_offer; } + + /* + * Write accessor. Set the local IP address that will be used in the sdp session + */ + void set_ip_address( std::string ip_addr ) { _ip_addr = ip_addr; } + + /* + * Read accessor. Get the local IP address + */ + std::string get_ip_address( void ) { return _ip_addr; } + + /* + * Build the local SDP offer + */ + int create_local_offer( ); + + /* + * Build the sdp media section + * Add rtpmap field if necessary + * + * @param media The media to add to SDP + * @param med The structure to receive the media section + */ + void set_media_descriptor_line( sdpMedia* media, pjmedia_sdp_media** p_med ); + + /* + * On building an invite outside a dialog, build the local offer and create the + * SDP negociator instance with it. + */ + int create_initial_offer( ); + + /* + * On receiving an invite outside a dialog, build the local offer and create the + * SDP negociator instance with the remote offer. + * + * @param remote The remote offer + */ + int receiving_initial_offer( pjmedia_sdp_session* remote ); + + /* + * Remove all media in the session media vector. + */ + void clean_session_media(); + + /* + * Return a string description of the media added to the session, + * ie the local media capabilities + */ + std::string media_to_string( void ); + + /* + * Return the codec of the first media after negociation + */ + AudioCodec* get_session_media( void ); + + /* + * read accessor. Return the negociated offer + * + * @return pjmedia_sdp_session The negociated offer + */ + pjmedia_sdp_session* get_negociated_offer( void ){ + return _negociated_offer; + } + + /* + * Start the sdp negociation. + * + * @return pj_status_t 0 on success + * 1 otherwise + */ + pj_status_t start_negociation( void ){ + return pjmedia_sdp_neg_negotiate( + _pool, _negociator, 0); + } + + /* + * Retrieve the negociated sdp offer from the sip payload. + * + * @param sdp the negociated offer + */ + void set_negociated_offer( const pjmedia_sdp_session *sdp ); + + /* + * Attribute the specified port to every medias provided + * This is valid only because we are using one media + * We should change this to support multiple medias + * + * @param port The media port + */ + void attribute_port_to_all_media (int port); + + void set_local_extern_audio_port(int port){ _local_extern_audio_port = port; } + + int get_local_extern_audio_port (void){ return _local_extern_audio_port; } + + void toString (void); + + /** + * Set remote's IP addr. [not protected] + * @param ip The remote IP address + */ + void set_remote_ip(const std::string& ip) { _remote_ip_addr = ip; } + + /** + * Return IP of destination [mutex protected] + * @return const std:string The remote IP address + */ + const std::string& get_remote_ip() { return _remote_ip_addr; } + + /** + * Set remote's audio port. [not protected] + * @param port The remote audio port + */ + void set_remote_audio_port(unsigned int port) { _remote_audio_port = port; } + + /** + * Return audio port at destination [mutex protected] + * @return unsigned int The remote audio port + */ + unsigned int get_remote_audio_port() { return _remote_audio_port; } + + void fetch_media_transport_info_from_remote_sdp (pjmedia_sdp_session *remote_sdp); + private: + /** Codec Map */ + std::vector<sdpMedia*> _local_media_cap; + + /* The media that will be used by the session (after the SDP negociation) */ + std::vector<sdpMedia*> _session_media; + + /** negociator */ + pjmedia_sdp_neg *_negociator; + + /** IP address */ + std::string _ip_addr; + + /** Remote's IP address */ + std::string _remote_ip_addr; + + /** Local SDP */ + pjmedia_sdp_session *_local_offer; + + /* The negociated SDP offer */ + // Explanation: each endpoint's offer is negociated, and a new sdp offer results from this + // negociation, with the compatible media from each part + pjmedia_sdp_session *_negociated_offer; + + /** Local audio port */ + int _local_extern_audio_port; + + /** Remote's audio port */ + unsigned int _remote_audio_port; + + // The pool to allocate memory + pj_pool_t *_pool; + + Sdp(const Sdp&); //No Copy Constructor + Sdp& operator=(const Sdp&); //No Assignment Operator + + void set_local_media_capabilities (); + + /* + * Mandatory field: Origin ("o=") + * Gives the originator of the session. + * Serves as a globally unique identifier for this version of this session description. + */ + void sdp_add_origin( void ); + + /* + * Mandatory field: Protocol version ("v=") + * Add the protocol version in the SDP session description + */ + void sdp_add_protocol( void ); + + /* + * Optional field: Connection data ("c=") + * Contains connection data. + */ + void sdp_add_connection_info( void ); + + /* + * Mandatory field: Session name ("s=") + * Add a textual session name. + */ + void sdp_add_session_name( void ); + + /* + * Optional field: Session information ("s=") + * Provides textual information about the session. + */ + void sdp_add_session_info( void ){} + + /* + * Optional field: Uri ("u=") + * Add a pointer to additional information about the session. + */ + void sdp_add_uri( void ) {} + + /* + * Optional fields: Email address and phone number ("e=" and "p=") + * Add contact information for the person responsible for the conference. + */ + void sdp_add_email( void ) {} + + /* + * Optional field: Bandwidth ("b=") + * Denotes the proposed bandwidth to be used by the session or the media . + */ + void sdp_add_bandwidth( void ) {} + + /* + * Mandatory field: Timing ("t=") + * Specify the start and the stop time for a session. + */ + void sdp_add_timing( void ); + + /* + * Optional field: Time zones ("z=") + */ + void sdp_add_time_zone( void ) {} + + /* + * Optional field: Encryption keys ("k=") + */ + void sdp_add_encryption_key( void ) {} + + /* + * Optional field: Attributes ("a=") + */ + void sdp_add_attributes( ); + + /* + * Mandatory field: Media descriptions ("m=") + */ + void sdp_add_media_description(); + + std::string convert_int_to_string (int value); + + void fetch_remote_ip_from_sdp (pjmedia_sdp_session *r_sdp); + + void fetch_remote_audio_port_from_sdp (pjmedia_sdp_media *r_media); + + void get_remote_sdp_media_from_offer (pjmedia_sdp_session* r_sdp, pjmedia_sdp_media** r_media); + +//////////////////////////////////////////////////////////////////3 +//////////////////////////////////////////////////////////////////// + +}; + + +#endif diff --git a/src/sdpmedia.cpp b/src/sdpmedia.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe2ef7615ca00e81a2de5cbc57c6366bdc6ef26a --- /dev/null +++ b/src/sdpmedia.cpp @@ -0,0 +1,158 @@ +/* + * + * Copyright (C) 2009 Savoir-Faire Linux inc. + * + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This file 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. + * + * Sropulpof 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 Sropulpof. If not, see <http:*www.gnu.org*licenses*>. + */ + +#include "sdpmedia.h" +#include <string.h> +#include <sstream> +#include <iostream> + +static const char* streamDirectionStr[] = +{ + "sendrecv", + "sendonly", + "recvonly", + "inactive" +}; + +static const char* mediaTypeStr[] = +{ + "audio", + "video", + "application", + "text", + "image", + "message" +}; + +sdpMedia::sdpMedia( int type ) + : _media_type( (mediaType)type ), _codec_list(0), _port( 0 ), _stream_type( SEND_RECEIVE ){} + + +sdpMedia::sdpMedia( std::string type, int port, std::string dir) + : _media_type( (mediaType)-1), _codec_list(0), _port(port), + _stream_type((streamDirection)-1){ + unsigned int i; + const char* tmp; + + for( i=0 ; i<MEDIA_COUNT ; i++){ + tmp = mediaTypeStr[i]; + if( strcmp(type.c_str(), tmp) == 0 ){ + _media_type = (mediaType)i; + break; + } + } + + if( strcmp( dir.c_str(), "default") == 0 ) + dir = DEFAULT_STREAM_DIRECTION; + for( i=0; i<DIR_COUNT; i++ ){ + tmp = streamDirectionStr[i]; + if( strcmp(dir.c_str(), tmp) == 0){ + _stream_type = (streamDirection)i; + break; + } + } +} + + +sdpMedia::~sdpMedia() +{ + int i; + for(i=0; i<(int)_codec_list.size(); i++) + delete _codec_list[i]; +} + + +std::string sdpMedia::get_media_type_str( void ){ + std::string value; + + // Test the range to be sure we know the media + if( _media_type >= 0 && _media_type < MEDIA_COUNT ) + value = mediaTypeStr[ _media_type ]; + else + value = "unknown"; + return value; +} + + +void sdpMedia::add_codec( AudioCodec* codec ){ + + _codec_list.push_back (codec); +} + +void sdpMedia::remove_codec( std::string codecName ) +{ + // Look for the codec by its encoding name + int i; + int size; + std::string enc_name; + std::vector<AudioCodec*>::iterator iter; + + size = _codec_list.size(); + std::cout << "vector size: " << size << std::endl; + + for( i=0 ; i<size ; i++ ){ + std::cout << _codec_list[i]->getCodecName().c_str() << std::endl; + if( strcmp(_codec_list[i]->getCodecName().c_str(), codecName.c_str()) == 0 ){ + std::cout << "erase " <<_codec_list[i]->getCodecName() << std::endl; + iter = _codec_list.begin()+i; + _codec_list.erase(iter); + break; + } + } +} + + +void sdpMedia::clear_codec_list( void ) { + // Erase every codecs from the list + _codec_list.clear(); +} + + +std::string sdpMedia::get_stream_direction_str( void ) { + std::string value; + + // Test the range of the value + if( _stream_type >= 0 && _stream_type < DIR_COUNT ) + value = streamDirectionStr[ _stream_type ]; + else + value = "unknown"; + return value; +} + + +std::string sdpMedia::to_string( void ){ + std::ostringstream display; + int size, i; + + size = _codec_list.size(); + + display << get_media_type_str(); + display << ":" << get_port(); + display << ":"; + for(i=0; i<size; i++){ + display << _codec_list[i]->getCodecName() << "/"; + } + + display << ":" << get_stream_direction_str() << std::endl; + + return display.str(); +} + + diff --git a/src/sdpmedia.h b/src/sdpmedia.h new file mode 100644 index 0000000000000000000000000000000000000000..7a089e517ed2888e9c01b5b9ecbb482be1d8a4ac --- /dev/null +++ b/src/sdpmedia.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * This file 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. + * + * Sropulpof 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 Sropulpof. If not, see <http:*www.gnu.org*licenses*>. + */ + +#ifndef _SDP_MEDIA +#define _SDP_MEDIA + +#include <vector> + +#include "audio/codecDescriptor.h" + +#define DEFAULT_STREAM_DIRECTION "sendrecv" + +#define MIME_TYPE_AUDIO 0 +#define MIME_TYPE_VIDEO 1 +#define MIME_TYPE_UNKNOWN 2 + +/* + * @file sdpmedia.h + * @brief A class to describe a media. It can be either a video codec or an audio codec. + * it maintains internally a list of codecs to use in the SDP session and negociation + */ + +/* + * This enum contains the different media stream direction. + * To be added in the SDP attributes + * The last one is only here to have to size information, otherwise the enum struct doesn't provide any means to know it + */ +enum streamDirection { + SEND_RECEIVE, + SEND_ONLY, + RECEIVE_ONLY, + INACTIVE, + DIR_COUNT +}; + +/* + * This enum contains the different media types. + * To be added in the SDP attributes + * The last one is only here to have to size information, otherwise the enum struct doesn't provide any means to know it + */ +enum mediaType { + AUDIOMEDIA, + VIDEO, + APPLICATION, + TEXT, + IMAGE, + MESSAGE, + MEDIA_COUNT +}; + +typedef enum streamDirection streamDirection; +typedef enum mediaType mediaType; + +#include "audio/codecs/audiocodec.h" + +class sdpMedia +{ + public: + sdpMedia( int type ); + sdpMedia( std::string type, int port, std::string dir = DEFAULT_STREAM_DIRECTION); + ~sdpMedia(); + + /* + * Read accessor. Return the list of codecs + */ + std::vector<AudioCodec*> get_media_codec_list() { return _codec_list; } + + /* + * Read accessor. Return the type of media + */ + mediaType get_media_type() { return _media_type; } + + /* + * Read accessor. Return the type of media + */ + std::string get_media_type_str(); + + /* + * Set the media type + */ + void set_media_type( int type ) { _media_type = (mediaType)type; } + + /* + * Read accessor. Return the transport port + */ + int get_port() { return _port; } + + /* + * Write accessor. Set the transport port + */ + void set_port( int port ) { _port = port; } + + /* + * Add a codec in the current media codecs vector + * + * @param payload The payload type + */ + void add_codec( AudioCodec *codec ); + + /* + * Remove a codec from the current media codecs vector + * + * @param codec_name The codec encoding name + */ + void remove_codec( std::string codec_name ); + + /* + * Remove all the codecs from the list + */ + void clear_codec_list( void ); + + /* + * Return a string description of the current media + */ + std::string to_string( void ); + + /* + * Set the stream direction of the current media + * ie: sendrecv, sendonly,... + */ + void set_stream_direction( int direction ) { _stream_type = (streamDirection)direction; } + + /* + * Get the stream direction of the current media + * ie: sendrecv, sendonly,... + */ + streamDirection get_stream_direction( void ) { return _stream_type; } + + /* + * Get the stream direction string description of the current media + * ie: sendrecv, sendonly,... + */ + std::string get_stream_direction_str( void ); + + private: + /* The type of media */ + mediaType _media_type; + + /* The media codec vector */ + std::vector< AudioCodec* > _codec_list; + + /* the transport port */ + int _port; + + /* The stream direction */ + streamDirection _stream_type; +}; + +#endif // _SDP_MEDIA diff --git a/src/sipcall.cpp b/src/sipcall.cpp index 74a183c84f47f3c75025f99eac799e26be4480a7..43aabe062073070ac69a3b22bdaf8dc945cac5ca 100644 --- a/src/sipcall.cpp +++ b/src/sipcall.cpp @@ -23,301 +23,25 @@ #include "sipcall.h" #include "global.h" // for _debug -#define _SENDRECV 0 -#define _SENDONLY 1 -#define _RECVONLY 2 -SIPCall::SIPCall(const CallID& id, Call::CallType type) : Call(id, type) +SIPCall::SIPCall(const CallID& id, Call::CallType type, pj_pool_t *pool) : Call(id, type) , _cid(0) , _did(0) , _tid(0) - , _localSDP(NULL) - , _negociator(NULL) - , _ipAddr("") , _xferSub(NULL) , _invSession(NULL) + , _local_sdp(0) { + _local_sdp = new Sdp (pool); _debug("SIPCALL::Constructor for this clss is called \n"); } SIPCall::~SIPCall() { - _debug("SIPCALL::Destructor for this clss is called \n"); + delete _local_sdp; _local_sdp = 0; + _debug("SIPCALL::Destructor for this clss is called \n"); } -bool -SIPCall::SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool) -{ - pj_status_t status; - - // We retrieve the remote sdp offer in the rdata struct to begin the negociation - pjmedia_sdp_session* remote_sdp = getRemoteSDPFromRequest(rdata); - if (remote_sdp == 0) { - return false; - } - - // Have to do some stuff here with the SDP - _localSDP = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session); - _localSDP->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); - - _localSDP->origin.version = 0; - sdpAddOrigin(); - _localSDP->name = pj_str((char*)"sflphone"); - sdpAddConnectionInfo(); - _localSDP->time.start = _localSDP->time.stop = 0; - sdpAddMediaDescription(pool); - - status = pjmedia_sdp_validate( _localSDP ); - if (status != PJ_SUCCESS) { - _debug("Can not generate valid local sdp\n"); - return false; - } - - _debug("Before create negociator!\n"); - status = pjmedia_sdp_neg_create_w_remote_offer(pool, _localSDP, remote_sdp, &_negociator); - if (status != PJ_SUCCESS) { - _debug("Can not create negociator\n"); - return false; - } - _debug("After create negociator!\n"); - - pjmedia_sdp_media* remote_med = getRemoteMedia(remote_sdp); - if (remote_med == 0) { - _debug("SIP Failure: unable to get remote media\n"); - return false; - } - - _debug("Before set audio!\n"); - if (!setRemoteAudioFromSDP(remote_sdp, remote_med)) { - _debug("SIP Failure: unable to set IP address and port from SDP\n"); - return false; - } - - _debug("Before set codec!\n"); - if (!setAudioCodecFromSDP(remote_med)) { - _debug("SIP Failure: unable to set audio codecs from the remote SDP\n"); - return false; - } - - return true; -} - -bool -SIPCall::SIPCallAnsweredWithoutHold(pjsip_rx_data *rdata) -{ - pjmedia_sdp_session* remote_sdp = getRemoteSDPFromRequest(rdata); - if (remote_sdp == NULL) { - _debug("SIP Failure: no remote sdp\n"); - return false; - } - - pjmedia_sdp_media* remote_med = getRemoteMedia(remote_sdp); - if (remote_med==NULL) { - return false; - } - - _debug("Before set audio!\n"); - if (!setRemoteAudioFromSDP(remote_sdp, remote_med)) { - _debug("SIP Failure: unable to set IP address and port from SDP\n"); - return false; - } - - _debug("Before set codec!\n"); - if (!setAudioCodecFromSDP(remote_med)) { - _debug("SIP Failure: unable to set audio codecs from the remote SDP\n"); - return false; - } - - return true; -} - - -pjmedia_sdp_session* -SIPCall::getRemoteSDPFromRequest(pjsip_rx_data *rdata) -{ - pjmedia_sdp_session *sdp; - pjsip_msg *msg; - pjsip_msg_body *body; - - msg = rdata->msg_info.msg; - body = msg->body; - - pjmedia_sdp_parse( rdata->tp_info.pool, (char*)body->data, body->len, &sdp ); - - return sdp; -} - -bool -SIPCall::setRemoteAudioFromSDP(pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media *remote_med) -{ - std::string remoteIP(remote_sdp->conn->addr.ptr, remote_sdp->conn->addr.slen); - _debug(" Remote Audio IP: %s\n", remoteIP.data()); - setRemoteIP(remoteIP); - int remotePort = remote_med->desc.port; - _debug(" Remote Audio Port: %d\n", remotePort); - setRemoteAudioPort(remotePort); - - return true; -} - -bool -SIPCall::setAudioCodecFromSDP(pjmedia_sdp_media* remote_med) -{ - // Remote Payload - int payLoad = -1; - int codecCount = remote_med->desc.fmt_count; - for(int i = 0; i < codecCount; i++) { - payLoad = atoi(remote_med->desc.fmt[i].ptr); - if (_codecMap.isActive((AudioCodecType)payLoad)) - break; - - payLoad = -1; - } - - if(payLoad != -1) { - _debug(" Payload: %d\n", payLoad); - setAudioCodec((AudioCodecType)payLoad); - } else - return false; - - return true; -} -void SIPCall::sdpAddOrigin( void ) -{ - pj_time_val tv; - pj_gettimeofday(&tv); - - _localSDP->origin.user = pj_str(pj_gethostname()->ptr); - // Use Network Time Protocol format timestamp to ensure uniqueness. - _localSDP->origin.id = tv.sec + 2208988800UL; - // The type of network ( IN for INternet ) - _localSDP->origin.net_type = pj_str((char*)"IN"); //STR_IN; - // The type of address - _localSDP->origin.addr_type = pj_str((char*)"IP4"); //STR_IP4; - // The address of the machine from which the session was created - _localSDP->origin.addr = pj_str( (char*)_ipAddr.c_str() ); -} - -void SIPCall::sdpAddConnectionInfo( void ) -{ - _localSDP->conn->net_type = _localSDP->origin.net_type; - _localSDP->conn->addr_type = _localSDP->origin.addr_type; - _localSDP->conn->addr = _localSDP->origin.addr; -} - -void SIPCall::sdpAddMediaDescription(pj_pool_t* pool) -{ - pjmedia_sdp_media* med; - pjmedia_sdp_attr *attr; - pjmedia_sdp_rtpmap rtpMap; - //int nbMedia, i; - - med = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); - //nbMedia = getSDPMediaList().size(); - _localSDP->media_count = 1; - - med->desc.media = pj_str((char*)"audio"); - med->desc.port_count = 1; - med->desc.port = getLocalExternAudioPort(); - med->desc.transport = pj_str((char*)"RTP/AVP"); - - CodecOrder::iterator itr; - - itr = _codecMap.getActiveCodecs().begin(); - int count = _codecMap.getActiveCodecs().size(); - med->desc.fmt_count = count; - - int i = 0; - - while(itr != _codecMap.getActiveCodecs().end()) { - std::ostringstream format; - format << *itr; - pj_strdup2(pool, &med->desc.fmt[i], format.str().data()); - - rtpMap.pt = med->desc.fmt[i]; - rtpMap.enc_name = pj_str((char *)_codecMap.getCodecName(*itr).data()); - rtpMap.clock_rate = _codecMap.getSampleRate(*itr); - if(_codecMap.getChannel(*itr) > 1) { - std::ostringstream channel; - channel << _codecMap.getChannel(*itr); - rtpMap.param = pj_str((char *)channel.str().data()); - } else - rtpMap.param.slen = 0; - - pjmedia_sdp_rtpmap_to_attr( pool, &rtpMap, &attr ); - med->attr[i] = attr; - i++; - itr++; - } - - //FIXME! Add the direction stream - attr = (pjmedia_sdp_attr*)pj_pool_zalloc( pool, sizeof(pjmedia_sdp_attr) ); - pj_strdup2( pool, &attr->name, "sendrecv"); - med->attr[ i++] = attr; - med->attr_count = i; - - _localSDP->media[0] = med; - /*for( i=0; i<nbMedia; i++ ){ - getMediaDescriptorLine( getSDPMediaList()[i], pool, &med ); - this->_local_offer->media[i] = med; - } */ - -} - -pjmedia_sdp_media* SIPCall::getRemoteMedia(pjmedia_sdp_session *remote_sdp) -{ - int count, i; - - count = remote_sdp->media_count; - for(i = 0; i < count; ++i) { - if(pj_stricmp2(&remote_sdp->media[i]->desc.media, "audio") == 0) - return remote_sdp->media[i]; - } - - return NULL; -} - -bool SIPCall::startNegociation(pj_pool_t *pool) -{ - pj_status_t status; - _debug("Before negotiate!\n"); - status = pjmedia_sdp_neg_negotiate(pool, _negociator, 0); - - return (status == PJ_SUCCESS); -} - -bool SIPCall::createInitialOffer(pj_pool_t *pool) -{ - pj_status_t status; - - // Have to do some stuff here with the SDP - _localSDP = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session); - _localSDP->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); - - _localSDP->origin.version = 0; - sdpAddOrigin(); - _localSDP->name = pj_str((char*)"sflphone"); - sdpAddConnectionInfo(); - _localSDP->time.start = _localSDP->time.stop = 0; - sdpAddMediaDescription(pool); - - _debug("Before validate SDP!\n"); - status = pjmedia_sdp_validate( _localSDP ); - if (status != PJ_SUCCESS) { - _debug("Can not generate valid local sdp %d\n", status); - return false; - } - - _debug("Before create negociator!\n"); - // Create the SDP negociator instance with local offer - status = pjmedia_sdp_neg_create_w_local_offer( pool, _localSDP, &_negociator); - //state = pjmedia_sdp_neg_get_state( _negociator ); - - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - return true; - -} diff --git a/src/sipcall.h b/src/sipcall.h index 2ef3d2793451e85bd1fd48151428959cfd7db854..fd0c01bea6569e1c2cf3e51d703259acfd090cf4 100644 --- a/src/sipcall.h +++ b/src/sipcall.h @@ -23,7 +23,7 @@ #include "call.h" #include "sipvoiplink.h" -#include "audio/codecDescriptor.h" +#include "sdp.h" class AudioCodec; @@ -41,7 +41,7 @@ class SIPCall : public Call * @param type The type of the call. Could be Incoming * Outgoing */ - SIPCall(const CallID& id, Call::CallType type); + SIPCall(const CallID& id, Call::CallType type, pj_pool_t *pool ); /** * Destructor @@ -78,127 +78,37 @@ class SIPCall : public Call */ int getTid() { return _tid; } + /** * Transaction identifier * @param tid SIP transaction id */ void setTid(int tid) { _tid = tid; } - /** - * Setup incoming call, and verify for errors, before ringing the user. - * @param pjsip_rx_data *rdata - * @param pj_pool_t *pool - * @return bool True on success - * false otherwise - */ - bool SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool); - - bool SIPCallAnsweredWithoutHold(pjsip_rx_data *rdata); - - /** - * Save IP Address - * @param ip std::string - * @return void - */ - void setIp(std::string ip) {_ipAddr = ip;} - - /** - * Get the local SDP - * @param void - * @return _localSDP pjmedia_sdp_session - */ - pjmedia_sdp_session* getLocalSDPSession( void ) { return _localSDP; } - - /** - * Begin negociation of media information between caller and callee - * @param pj_pool_t *pool - * @return bool True if ok - */ - bool startNegociation(pj_pool_t *pool); - - /** - * Create the localSDP, media negociation and codec information - * @param pj_pool_t *pool - * @return void - */ - bool createInitialOffer(pj_pool_t *pool); - void setXferSub(pjsip_evsub* sub) {_xferSub = sub;} pjsip_evsub *getXferSub() {return _xferSub;} void setInvSession(pjsip_inv_session* inv) {_invSession = inv;} pjsip_inv_session *getInvSession() {return _invSession;} - private: - - // Copy Constructor - SIPCall(const SIPCall& rh); + Sdp* getLocalSDP (void) { return _local_sdp; } - // Assignment Operator - SIPCall& operator=( const SIPCall& rh); + void setLocalSDP (Sdp *local_sdp) { _local_sdp = local_sdp; } - /** - * Get a valid remote SDP or return a 400 bad request response if invalid - * @param - * @return - */ - pjmedia_sdp_session* getRemoteSDPFromRequest(pjsip_rx_data *rdata); - - /** - * Get a valid remote media - * @param remote_sdp pjmedia_sdp_session* - * @return pjmedia_sdp_media*. A valid sdp_media_t or 0 - */ - pjmedia_sdp_media* getRemoteMedia(pjmedia_sdp_session *remote_sdp); - - /** - * Set Audio Port and Audio IP from Remote SDP Info - * @param remote_med Remote Media info - * @param remote_sdp Remote SDP pointer - * @return bool True if everything is set correctly - */ - bool setRemoteAudioFromSDP(pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media* remote_med); + private: - /** - * Set Audio Codec with the remote choice - * @param remote_med Remote Media info - * @return bool True if everything is set correctly - */ - bool setAudioCodecFromSDP(pjmedia_sdp_media* remote_med); + Sdp *_local_sdp; - /** SIP call id */ int _cid; - - /** SIP domain id */ int _did; - - /** SIP transaction id */ int _tid; - /** Local SDP */ - pjmedia_sdp_session *_localSDP; + // Copy Constructor + SIPCall(const SIPCall& rh); - /** negociator */ - pjmedia_sdp_neg *_negociator; - - /** - * Set origin information for local SDP - */ - void sdpAddOrigin( void ); + // Assignment Operator + SIPCall& operator=( const SIPCall& rh); - /** - * Set connection information for local SDP - */ - void sdpAddConnectionInfo( void ); - /** - * Set media information including codec for localSDP - * @param pj_pool_t* pool - * @return void - */ - void sdpAddMediaDescription(pj_pool_t* pool); - - /** IP address */ - std::string _ipAddr; pjsip_evsub *_xferSub; pjsip_inv_session *_invSession; diff --git a/src/sipvoiplink.cpp b/src/sipvoiplink.cpp index 61bf0cb49ffc1d4910b60e5899e0c0d4a1b56666..acb8d8b0e5e38efb266f586b9cff5a078b7f8ea2 100644 --- a/src/sipvoiplink.cpp +++ b/src/sipvoiplink.cpp @@ -25,8 +25,28 @@ #include "sipaccount.h" #include "audio/audiortp.h" +#define CAN_REINVITE 1 + /**************** EXTERN VARIABLES AND FUNCTIONS (callbacks) **************************/ +/* + * Retrieve the SDP of the peer contained in the offer + * + * @param rdata The request data + * @param r_sdp The pjmedia_sdp_media to stock the remote SDP + */ +void get_remote_sdp_from_offer( pjsip_rx_data *rdata, pjmedia_sdp_session** r_sdp ); + +int getModId(); + +/** + * * Set audio (SDP) configuration for a call + * * localport, localip, localexternalport + * * @param call a SIPCall valid pointer + * * @return bool True + * */ +bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server); + /* * The global pool factory */ @@ -53,20 +73,11 @@ pjsip_module _mod_ua; pj_thread_t *thread; pj_thread_desc desc; - /** * Get the number of voicemail waiting in a SIP message */ void set_voicemail_info( AccountID account, pjsip_msg_body *body ); -/** - * Set audio (SDP) configuration for a call - * localport, localip, localexternalport - * @param call a SIPCall valid pointer - * @return bool True - */ -bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server); - // Documentated from the PJSIP Developer's Guide, available on the pjsip website/ /* @@ -107,6 +118,8 @@ void call_on_forked(pjsip_inv_session *inv, pjsip_event *e); */ void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e); +void on_rx_offer( pjsip_inv_session *inv, const pjmedia_sdp_session *offer ); + /* * Registration callback */ @@ -205,6 +218,7 @@ SIPVoIPLink::terminate() delete _evThread; _evThread = NULL; } + /* Clean shutdown of pjsip library */ if( initDone() ) { @@ -244,7 +258,21 @@ SIPVoIPLink::terminateOneCall(const CallID& id) } } +void get_remote_sdp_from_offer( pjsip_rx_data *rdata, pjmedia_sdp_session** r_sdp ){ + pjmedia_sdp_session *sdp; + pjsip_msg *msg; + pjsip_msg_body *body; + // Get the message + msg = rdata->msg_info.msg; + // Get the body message + body = msg->body; + + // Parse the remote request to get the sdp session + pjmedia_sdp_parse( rdata->tp_info.pool, (char*)body->data, body->len, &sdp ); + + *r_sdp = sdp; +} void SIPVoIPLink::getEvent() @@ -261,7 +289,7 @@ SIPVoIPLink::getEvent() int SIPVoIPLink::sendRegister( AccountID id ) { - _debug("sendRegister called!!!!!!!!!!!!!!!!!!!!!!! \n"); + _debug("sendRegister called!!!!!!!!!!!!!!!!!!!!!!! \n"); pj_status_t status; int expire_value; char contactTmp[256]; @@ -407,7 +435,7 @@ SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) { Account* account; - SIPCall* call = new SIPCall(id, Call::Outgoing); + SIPCall* call = new SIPCall(id, Call::Outgoing, _pool); if (call) { account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(Manager::instance().getAccountFromCall(id))); @@ -419,14 +447,17 @@ SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) delete call; call=0; return call; } - //call->setPeerNumber(toUrl); + call->setPeerNumber(getSipTo(toUrl, account->getHostname())); - + setCallAudioLocal(call, getLocalIPAddress(), useStun(), getStunServer()); + call->initRecFileName(); _debug("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data()); - // we have to add the codec before using it in SIPOutgoingInvite... - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + // Building the local SDP offer + call->getLocalSDP()->set_ip_address(getLocalIP()); + call->getLocalSDP()->create_initial_offer(); + if ( SIPOutgoingInvite(call) ) { call->setConnectionState(Call::Progressing); call->setState(Call::Active); @@ -438,7 +469,7 @@ SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) return call; } - bool + bool SIPVoIPLink::answer(const CallID& id) { @@ -446,6 +477,8 @@ SIPVoIPLink::answer(const CallID& id) SIPCall *call; pj_status_t status; pjsip_tx_data *tdata; + Sdp *local_sdp; + pjsip_inv_session *inv_session; _debug("SIPVoIPLink::answer: start answering \n"); @@ -456,15 +489,19 @@ SIPVoIPLink::answer(const CallID& id) return false; } - // User answered the incoming call, tell peer this news - if (call->startNegociation(_pool)) { - // Create and send a 200(OK) response + local_sdp = call->getLocalSDP(); + inv_session = call->getInvSession(); + status = local_sdp->start_negociation (); + + if (status == PJ_SUCCESS) { _debug("SIPVoIPLink::answer:UserAgent: Negociation success! : call %s \n", call->getCallId().c_str()); - status = pjsip_inv_answer(call->getInvSession(), PJSIP_SC_OK, NULL, NULL, &tdata); + // Create and send a 200(OK) response + status = pjsip_inv_answer(inv_session, PJSIP_SC_OK, NULL, NULL, &tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - status = pjsip_inv_send_msg(call->getInvSession(), tdata); + status = pjsip_inv_send_msg(inv_session, tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + // Start the RTP sessions _debug("SIPVoIPLink::answer: Starting AudioRTP when answering : call %s \n", call->getCallId().c_str()); if (_audiortp->createNewSession(call) >= 0) { call->setAudioStart(true); @@ -475,10 +512,21 @@ SIPVoIPLink::answer(const CallID& id) _debug("SIPVoIPLink::answer: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__); } } - _debug("SIPVoIPLink::answer: fail terminate call %s \n",call->getCallId().c_str()); - terminateOneCall(call->getCallId()); - removeCall(call->getCallId()); - return false; + else { + // Create and send a 488/Not acceptable here + // because the SDP negociation failed + status = pjsip_inv_answer( inv_session, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, NULL, + &tdata ); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + status = pjsip_inv_send_msg( inv_session, tdata ); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Terminate the call + _debug("SIPVoIPLink::answer: fail terminate call %s \n",call->getCallId().c_str()); + terminateOneCall(call->getCallId()); + removeCall(call->getCallId()); + return false; + } } bool @@ -575,9 +623,6 @@ SIPVoIPLink::onhold(const CallID& id) { pj_status_t status; - pjsip_tx_data *tdata; - pjmedia_sdp_attr *attr; - pjmedia_sdp_session* local_sdp; SIPCall* call; call = getSIPCall(id); @@ -589,43 +634,54 @@ SIPVoIPLink::onhold(const CallID& id) call->setAudioStart(false); call->setState(Call::Hold); _debug("* SIP Info: Stopping AudioRTP for onhold action\n"); - //_mutexSIP.enterMutex(); _audiortp->closeRtpSession(); - //_mutexSIP.leaveMutex(); - local_sdp = call->getLocalSDPSession(); + /* Create re-INVITE with new offer */ + status = inv_session_reinvite (call, "sendonly"); + + return (status == PJ_SUCCESS); +} + +int SIPVoIPLink::inv_session_reinvite (SIPCall *call, std::string direction) { + + pj_status_t status; + pjsip_tx_data *tdata; + pjmedia_sdp_session *local_sdp; + pjmedia_sdp_attr *attr; + + local_sdp = call->getLocalSDP()->get_local_sdp_session(); if( local_sdp == NULL ){ _debug("! SIP Failure: unable to find local_sdp\n"); return false; } - /* Create re-INVITE with new offer */ - // Remove all the attributes with the specified name + // reinvite only if connected + // Build the local SDP offer + status = call->getLocalSDP()->create_initial_offer( ); pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendrecv"); - attr = pjmedia_sdp_attr_create(_pool, "sendonly", NULL); + attr = pjmedia_sdp_attr_create(_pool, direction.c_str(), NULL); pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp, &tdata); - if( status != PJ_SUCCESS ) - { - _debug("On hold: creation of the Re-invite request failed\n"); - return false; - } - /* Send the request */ - status = pjsip_inv_send_msg( call->getInvSession(), tdata); + // Build the reinvite request + status = pjsip_inv_reinvite( call->getInvSession(), NULL, + local_sdp, &tdata ); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - return (status == PJ_SUCCESS); + // Send it + status = pjsip_inv_send_msg( call->getInvSession(), tdata ); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + return PJ_SUCCESS; } + bool SIPVoIPLink::offhold(const CallID& id) { SIPCall *call; pj_status_t status; - pjsip_tx_data *tdata; - pjmedia_sdp_attr *attr; - pjmedia_sdp_session* local_sdp; call = getSIPCall(id); @@ -634,28 +690,9 @@ SIPVoIPLink::offhold(const CallID& id) return false; } - local_sdp = call->getLocalSDPSession(); - if( local_sdp == NULL ){ - _debug("! SIP Failure: unable to find local_sdp\n"); - return false; - } - /* Create re-INVITE with new offer */ - // Remove all the attributes with the specified name - pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendonly"); - attr = pjmedia_sdp_attr_create(_pool, "sendrecv", NULL); - pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); - - status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp , &tdata); - if( status != PJ_SUCCESS ) - { - _debug("Off hold: creation of the Re-invite request failed\n"); - return false; - } - - /* Send the request */ - status = pjsip_inv_send_msg( call->getInvSession(), tdata); - if( status != PJ_SUCCESS ) + status = inv_session_reinvite (call, "sendrecv"); + if (status != PJ_SUCCESS) return false; // Enable audio @@ -802,15 +839,15 @@ SIPVoIPLink::isRecording(const CallID& id) } -std::string + std::string SIPVoIPLink::getCurrentCodecName() { - SIPCall *call = getSIPCall(Manager::instance().getCurrentCallId()); + SIPCall *call = getSIPCall(Manager::instance().getCurrentCallId()); - AudioCodec *ac = call->getCodecMap().getCodec(call->getAudioCodec()); + AudioCodec *ac = call->getLocalSDP()->get_session_media(); - return ac->getCodecName(); + return ac->getCodecName(); } bool @@ -893,6 +930,7 @@ SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) pj_str_t from, to, contact; AccountID id; SIPAccount *account; + pjsip_inv_session *inv; if (!call) return false; @@ -920,15 +958,8 @@ SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) &dialog); PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - setCallAudioLocal(call, getLocalIPAddress(), useStun(), getStunServer()); - call->setIp(getLocalIP()); - - // Building the local SDP offer - call->createInitialOffer(_pool); - // Create the invite session for this call - pjsip_inv_session *inv; - status = pjsip_inv_create_uac(dialog, call->getLocalSDPSession(), 0, &inv); + status = pjsip_inv_create_uac(dialog, call->getLocalSDP()->get_local_sdp_session(), 0, &inv); PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); // Set auth information @@ -979,28 +1010,6 @@ std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostnam return true; } - bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server) - { - // Setting Audio - unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; - unsigned int callLocalExternAudioPort = callLocalAudioPort; - if (stun) { - // If use Stun server - if (Manager::instance().behindNat(server, callLocalAudioPort)) { - callLocalExternAudioPort = Manager::instance().getFirewallPort(); - } - } - _debug(" Setting local audio port to: %d\n", callLocalAudioPort); - _debug(" Setting local audio port (external) to: %d\n", callLocalExternAudioPort); - - // Set local audio port for SIPCall(id) - call->setLocalIp(localIP); - call->setLocalAudioPort(callLocalAudioPort); - call->setLocalExternAudioPort(callLocalExternAudioPort); - - return true; - } - void SIPVoIPLink::SIPCallServerFailure(SIPCall *call) { @@ -1064,17 +1073,25 @@ std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostnam void SIPVoIPLink::SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata) { - //SIPCall* call = dynamic_cast<SIPCall *>(theCall);//findSIPCallWithCid(event->cid); + + pjmedia_sdp_session *r_sdp; + if (!call) { _debug("! SIP Failure: unknown call\n"); return; } - //call->setDid(event->did); if (call->getConnectionState() != Call::Connected) { - //call->SIPCallAnswered(event); - call->SIPCallAnsweredWithoutHold(rdata); + _debug ("Get remote SDP from offer\n"); + get_remote_sdp_from_offer (rdata, &r_sdp); + if (r_sdp==NULL) { + _debug("SIP Failure: no remote sdp session\n"); + return; + } + _debug ("Get remote media information from offer\n"); + call->getLocalSDP()->fetch_media_transport_info_from_remote_sdp (r_sdp); + _debug ("Update call state\n"); call->setConnectionState(Call::Connected); call->setState(Call::Active); @@ -1089,13 +1106,12 @@ std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostnam } } else { _debug("* SIP Info: Answering call (on/off hold to send ACK)\n"); - //call->SIPCallAnswered(event); } } -SIPCall* -SIPVoIPLink::getSIPCall(const CallID& id) + SIPCall* + SIPVoIPLink::getSIPCall(const CallID& id) { Call* call = getCall(id); if (call) { @@ -1105,9 +1121,9 @@ SIPVoIPLink::getSIPCall(const CallID& id) } -void SIPVoIPLink::setStunServer( const std::string &server ) -{ - if(server != "") { + void SIPVoIPLink::setStunServer( const std::string &server ) + { + if(server != "") { useStun(true); _stunServer = server; @@ -1260,6 +1276,7 @@ void SIPVoIPLink::setStunServer( const std::string &server ) inv_cb.on_new_session = &call_on_forked; inv_cb.on_media_update = &call_on_media_update; inv_cb.on_tsx_state_changed = &call_on_tsx_changed; + inv_cb.on_rx_offer = &on_rx_offer; // Initialize session invite module status = pjsip_inv_usage_init(_endpt, &inv_cb); @@ -1343,8 +1360,8 @@ void SIPVoIPLink::setStunServer( const std::string &server ) // Init bound address to ANY pj_memset(&bound_addr, 0, sizeof (bound_addr)); - - + + bound_addr.sin_addr.s_addr = pj_htonl(PJ_INADDR_ANY); bound_addr.sin_port = pj_htons((pj_uint16_t) _localPort); bound_addr.sin_family = PJ_AF_INET; @@ -1354,10 +1371,10 @@ void SIPVoIPLink::setStunServer( const std::string &server ) _debug("bound_addr.sin_port %i \n", bound_addr.sin_port); - + _debug("UserAgent: Use IP: %s\n", _localExternAddress.data()); - - + + // Create UDP-Server (default port: 5060) strcpy(tmpIP, _localExternAddress.data()); pj_strdup2(_pool, &a_name.host, tmpIP); @@ -1451,7 +1468,7 @@ void SIPVoIPLink::setStunServer( const std::string &server ) /* Done. */ } - int SIPVoIPLink::getModId(){ + int getModId(){ return _mod_ua.id; } @@ -1490,18 +1507,40 @@ void SIPVoIPLink::setStunServer( const std::string &server ) Manager::instance().startVoiceMessageNotification(account, voicemail); } + void SIPVoIPLink::handle_reinvite (SIPCall *call) { + + // Close the previous RTP session + _audiortp->closeRtpSession (); + call->setAudioStart (false); + + // Create a new one with new info + if (_audiortp->createNewSession (call) >= 0) { + call->setAudioStart (true); + } + } + + /*******************************/ /* CALLBACKS IMPLEMENTATION */ /*******************************/ void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e){ - PJ_UNUSED_ARG(inv); + SIPCall *call; + AccountID accId; + SIPVoIPLink *link; + pjsip_rx_data *rdata; + + _debug (" ***************************** NEW CALL STATE %i **************************\n", inv->state); - SIPCall *call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); + /* Retrieve the call information */ + call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); if(!call) return; + //Retrieve the body message + rdata = e->body.tsx_state.src.rdata; + /* If this is an outgoing INVITE that was created because of * REFER/transfer, send NOTIFY to transferer. */ @@ -1558,135 +1597,90 @@ void SIPVoIPLink::setStunServer( const std::string &server ) } } } - } + else { - void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED) { - _debug("call_on_media_updated\n"); - } - - void call_on_forked(pjsip_inv_session *inv, pjsip_event *e){ - _debug("call_on_forked\n"); - } - - void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e){ - - pjsip_rx_data *rdata; - AccountID accId; - SIPCall *call; - SIPVoIPLink *link; - pjsip_msg *msg; - - if(pj_strcmp2(&tsx->method.name, "INFO") == 0) { - // Receive a INFO message, ingore it! - return; - } - - //Retrieve the body message - rdata = e->body.tsx_state.src.rdata; - - if (tsx->role == PJSIP_ROLE_UAC) { - switch (tsx->state) { - case PJSIP_TSX_STATE_TERMINATED: - if (tsx->status_code == 200 && - pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) != 0) { - // Peer answered the outgoing call - _debug("UserAgent: Peer answered the outgoing call!\n"); - call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); - if (call == NULL) - return; + // The call is ringing + if (inv->state == PJSIP_INV_STATE_EARLY){ + _debug ("*************************** PJSIP_INV_STATE_EARLY - PEER RINGING ***********************************\n"); + call->setConnectionState(Call::Ringing); + Manager::instance().peerRingingCall(call->getCallId()); + } - //_debug("UserAgent: The call id is %s\n", call->getCallId().data()); + // We receive a ACK - The connection is established + else if( inv->state == PJSIP_INV_STATE_CONFIRMED ){ + _debug ("*************************** PJSIP_INV_STATE_CONFIRMED ***********************************\n"); + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) + link->SIPCallAnswered(call, rdata); + } + else if( inv->state == PJSIP_INV_STATE_DISCONNECTED ){ + _debug ("*************************** PJSIP_INV_STATE_DISCONNECTED %i***********************************\n", inv->cause); + switch( inv->cause ) + { + /* The call terminates normally - BYE / CANCEL */ + case PJSIP_SC_OK: + case PJSIP_SC_DECLINE: accId = Manager::instance().getAccountFromCall(call->getCallId()); link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) - link->SIPCallAnswered(call, rdata); - } else if (tsx->status_code / 100 == 5) { - _debug("UserAgent: 5xx error message received\n"); - } - break; - case PJSIP_TSX_STATE_PROCEEDING: - // Peer is ringing for the outgoing call - msg = rdata->msg_info.msg; - - call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); - if (call == NULL) - return; - - if (msg->line.status.code == 180) { - _debug("UserAgent: Peer is ringing!\n"); - - call->setConnectionState(Call::Ringing); - Manager::instance().peerRingingCall(call->getCallId()); - } - break; - case PJSIP_TSX_STATE_COMPLETED: - if (tsx->status_code == 407 || tsx->status_code == 401) //FIXME - break; - if (tsx->status_code / 100 == 6 || tsx->status_code / 100 == 4) { - // We get error message of outgoing call from server - _debug("UserAgent: Server error message is received!\n"); - call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); - if (call == NULL) { - _debug("UserAgent: Call has been removed!\n"); - return; + if (link) { + link->SIPCallClosed(call); } + break; + + /* The call connection failed */ + case PJSIP_SC_NOT_FOUND: /* peer not found */ + case PJSIP_SC_REQUEST_TIMEOUT: /* request timeout */ + case PJSIP_SC_NOT_ACCEPTABLE_HERE: /* no compatible codecs */ + case PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE: + case PJSIP_SC_UNSUPPORTED_MEDIA_TYPE: accId = Manager::instance().getAccountFromCall(call->getCallId()); link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); if (link) { link->SIPCallServerFailure(call); } - } - break; - default: - break; - } // end of switch + break; + + default: + _debug ("sipvoiplink.cpp - line 1635 : Unhandled call state. This is probably a bug.\n"); + break; + } + } - } else { - switch (tsx->state) { - case PJSIP_TSX_STATE_TRYING: - if (pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) == 0) { - // Peer ask me to transfer call to another number. - _debug("UserAgent: Incoming REFER request!\n"); - //onCallTransfered(inv, e->body.tsx_state.src.rdata); - } - break; - case PJSIP_TSX_STATE_COMPLETED: - if (tsx->status_code == 200 && tsx->method.id == PJSIP_BYE_METHOD) { - // Peer hangup the call - _debug("UserAgent: Peer hangup(bye) message is received!\n"); - call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); - if (call == NULL) { - _debug("UserAgent: Call has been removed!\n"); - return; - } - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) { - link->SIPCallClosed(call); - } - } else if (tsx->status_code == 200 && tsx->method.id == PJSIP_CANCEL_METHOD) { - // Peer refuse the call - _debug("UserAgent: Cancel message is received!\n"); - call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); - if (call == NULL) { - _debug("UserAgent: Call has been removed!\n"); - return; - } - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) { - link->SIPCallClosed(call); - } - } - break; - default: - break; - } // end of switch } } + void call_on_media_update( pjsip_inv_session *inv, pj_status_t status) { + _debug("call_on_media_updated\n"); + + const pjmedia_sdp_session *r_sdp; + SIPCall *call; + + if (status != PJ_SUCCESS) { + _debug ("Error while negociating the offer\n"); + return; + } + + // Get the new sdp, result of the negociation + pjmedia_sdp_neg_get_active_local( inv->neg, &r_sdp ); + + call = reinterpret_cast<SIPCall *> (inv->mod_data[getModId()]); + // Clean the resulting sdp offer to create a new one (in case of a reinvite) + call->getLocalSDP()->clean_session_media(); + // Set the fresh negociated one + call->getLocalSDP()->set_negociated_offer( r_sdp ); + } + + void call_on_forked(pjsip_inv_session *inv, pjsip_event *e){ + _debug("call_on_forked\n"); + } + + void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e){ + _debug ("call_on_tsx_changed\n"); + } + void regc_cb(struct pjsip_regc_cbparam *param){ //AccountID *id = static_cast<AccountID *> (param->token); @@ -1754,6 +1748,8 @@ void SIPVoIPLink::setStunServer( const std::string &server ) SIPVoIPLink *link; CallID id; SIPCall* call; + pjsip_inv_session *inv; + pjmedia_sdp_session *r_sdp; // voicemail part std::string method_name; @@ -1840,7 +1836,7 @@ void SIPVoIPLink::setStunServer( const std::string &server ) // Generate a new call ID for the incoming call! id = Manager::instance().getNewCallID(); - call = new SIPCall(id, Call::Incoming); + call = new SIPCall(id, Call::Incoming, _pool); /* If an error occured at the call creation */ if (!call) { @@ -1848,43 +1844,32 @@ void SIPVoIPLink::setStunServer( const std::string &server ) return false; } + // Have to do some stuff with the SDP // Set the codec map, IP, peer number and so on... for the SIPCall object setCallAudioLocal(call, link->getLocalIPAddress(), link->useStun(), link->getStunServer()); - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + // We retrieve the remote sdp offer in the rdata struct to begin the negociation + call->getLocalSDP()->set_ip_address(link->getLocalIPAddress()); + get_remote_sdp_from_offer( rdata, &r_sdp ); + call->getLocalSDP()->receiving_initial_offer( r_sdp ); + call->setConnectionState(Call::Progressing); - call->setIp(link->getLocalIPAddress()); call->setPeerNumber(peerNumber); call->initRecFileName(); - /* Call the SIPCallInvite function to generate the local sdp, - * remote sdp and negociator. - * This function is also used to set the parameters of audio RTP, including: - * local IP and port number - * remote IP and port number - * possilbe audio codec will be used in this call - */ - if (call->SIPCallInvite(rdata, _pool)) { - - // Notify UI there is an incoming call - if (Manager::instance().incomingCall(call, account_id)) { - // Add this call to the callAccountMap in ManagerImpl - Manager::instance().getAccountLink(account_id)->addCall(call); - } else { - // Fail to notify UI - delete call; - call = NULL; - _debug("UserAgent: Fail to notify UI!\n"); - return false; - } + // Notify UI there is an incoming call + if (Manager::instance().incomingCall(call, account_id)) { + // Add this call to the callAccountMap in ManagerImpl + Manager::instance().getAccountLink(account_id)->addCall(call); } else { - // Fail to collect call information + // Fail to notify UI delete call; call = NULL; - _debug("UserAgent: Call SIPCallInvite failed!\n"); + _debug("UserAgent: Fail to notify UI!\n"); return false; } + /* Create the local dialog (UAS) */ status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dialog); if (status != PJ_SUCCESS) { @@ -1894,8 +1879,7 @@ void SIPVoIPLink::setStunServer( const std::string &server ) } // Specify media capability during invite session creation - pjsip_inv_session *inv; - status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDPSession(), 0, &inv); + status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDP()->get_local_sdp_session(), 0, &inv); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); // Associate the call in the invite session @@ -2306,3 +2290,58 @@ void SIPVoIPLink::setStunServer( const std::string &server ) _debug("UserAgent: Xfer server subscription terminated\n"); } } + + void on_rx_offer( pjsip_inv_session *inv, const pjmedia_sdp_session *offer ){ + + _debug ( "********************************* REINVITE RECEIVED *******************************\n" ); + +#ifdef CAN_REINVITE + _debug ("reinvite SIP\n"); + + SIPCall *call; + pj_status_t status; + AccountID accId; + SIPVoIPLink *link; + + call = (SIPCall*)inv->mod_data[getModId()]; + if (!call) + return; + + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + + call->getLocalSDP()->receiving_initial_offer( (pjmedia_sdp_session*)offer); + status=pjsip_inv_set_sdp_answer( call->getInvSession(), call->getLocalSDP()->get_local_sdp_session() ); + if (link) + link->handle_reinvite (call); +#endif + + } + +/*****************************************************************************************************************/ + + + bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server) { + // Setting Audio + unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; + unsigned int callLocalExternAudioPort = callLocalAudioPort; + if (stun) { + // If use Stun server + if (Manager::instance().behindNat(server, callLocalAudioPort)) { + callLocalExternAudioPort = Manager::instance().getFirewallPort(); + } + } + _debug(" Setting local audio port to: %d\n", callLocalAudioPort); + _debug(" Setting local audio port (external) to: %d\n", callLocalExternAudioPort); + + // Set local audio port for SIPCall(id) + call->setLocalIp(localIP); + call->setLocalAudioPort(callLocalAudioPort); + call->setLocalExternAudioPort(callLocalExternAudioPort); + + call->getLocalSDP()->attribute_port_to_all_media (callLocalExternAudioPort); + + return true; + } + + diff --git a/src/sipvoiplink.h b/src/sipvoiplink.h index fef56310f9c5f8fe66c24d0d9d1c72c8d30ea320..3180dd5c2b6a6e1972cd62cbd4939a43c89b589a 100644 --- a/src/sipvoiplink.h +++ b/src/sipvoiplink.h @@ -33,7 +33,6 @@ #include <pjnath/stun_config.h> /////////////////////////////// - class EventThread; class SIPCall; class AudioRtp; @@ -113,7 +112,7 @@ class SIPVoIPLink : public VoIPLink /** * Answer the call * @param id The call identifier - * @return bool True on success + * @return int True on success */ bool answer(const CallID& id); @@ -267,6 +266,13 @@ class SIPVoIPLink : public VoIPLink */ void SIPCallReleased(SIPCall *call); + /** + * Handle a re-invite request by the remote peer. + * A re-invite is an invite request inside a dialog. + * When receiving a re-invite, we close the current rtp session and create a new one with the updated information + */ + void handle_reinvite (SIPCall *call); + /** * SIPCall accessor * @param id The call identifier @@ -304,6 +310,8 @@ class SIPVoIPLink : public VoIPLink */ std::string getCurrentCodecName(); + int inv_session_reinvite (SIPCall *call, std::string direction=""); + private: /** @@ -316,7 +324,6 @@ class SIPVoIPLink : public VoIPLink static SIPVoIPLink* _instance; void busy_sleep(unsigned msec); - int getModId(); /** * Initialize the PJSIP library diff --git a/src/user_cfg.h b/src/user_cfg.h index fea650490eb60e5e34d77339b086c5610442ab47..a5ab0625f91101b9fa943d9bddef934b4049ecfb 100644 --- a/src/user_cfg.h +++ b/src/user_cfg.h @@ -45,8 +45,6 @@ #define VOLUME_MICRO "Volume.micro" /** Mic volume */ #define RECORD_PATH "Record.path" /** Recording path */ -#define VIDEO "Video" /** Section Video */ - #define PREFERENCES "Preferences" /** Section Preferences */ #define CONFIG_DIALPAD "Dialpad.display" /** Display dialpad preferences */ #define CONFIG_SEARCHBAR "Searchbar.display" /** Whether or nor display the search bar */