diff --git a/src/Makefile.am b/src/Makefile.am index e0a77e282182d317e1214f753d2e1482d6d0b72f..7af7dbfec2d5350a0c1b9aa2d9cc6cfa4ec44835 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,6 +30,7 @@ sflphoned_SOURCES = \ account.cpp \ sipcall.cpp \ sdp.cpp \ + sdpmedia.cpp \ $(IAXSOURCES) sflphoned_CXXFLAGS = \ @@ -69,7 +70,8 @@ noinst_HEADERS = \ sipvoiplink.h \ call.h \ sipcall.h \ - sdp.h + sdp.h \ + sdpmedia.h libsflphone_la_LIBADD = \ $(src)/libs/stund/libstun.la \ diff --git a/src/sdp.cpp b/src/sdp.cpp index f89dd5b540cd059ce35b3e6d8bc451e32fa1075a..7b847bbbeab70e4bf26d0ab98f424d47e951032f 100644 --- a/src/sdp.cpp +++ b/src/sdp.cpp @@ -20,368 +20,336 @@ #include "sdp.h" #include "global.h" +#include "manager.h" -#define _SENDRECV 0 -#define _SENDONLY 1 -#define _RECVONLY 2 - Sdp::Sdp( pj_pool_t *pool ) : _localSDP(NULL) - , _negociator(NULL) - , _codecMap() - , _audioCodec() - , _ipAddr("") +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", 7 }; +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) { _pool = pool; } -Sdp::~Sdp() {} +Sdp::~Sdp() { - bool -Sdp::SIPCallInvite(pjsip_rx_data *rdata) -{ - pj_status_t status; + unsigned int k; - // 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) { - _debug ("Failed to retrieve the remote sdp offer in the rdata struct to begin the negociation\n"); - return false; + for( k=0; k<_session_media.size(); k++ ){ + delete _session_media[k]; + _session_media[k] = 0; } +} - // 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); +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::ostringstream tmp; - _localSDP->origin.version = 0; - sdpAddOrigin(); - _localSDP->name = pj_str((char*)"sflphone"); - sdpAddConnectionInfo(); - _localSDP->time.start = _localSDP->time.stop = 0; - sdpAddMediaDescription(); + med = PJ_POOL_ZALLOC_T( _pool, pjmedia_sdp_media ); - // DEBUG - toString (); + // 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); - status = pjmedia_sdp_validate( _localSDP ); - if (status != PJ_SUCCESS) { - _debug("Can not generate valid local sdp\n"); - return false; - } + // Media format ( RTP payload ) + count = media->get_media_codec_list().size(); + med->desc.fmt_count = count; - _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; + // add the payload list + for(i=0; i<count; i++){ + codec = media->get_media_codec_list()[i]; + tmp << codec->getPayload (); + pj_strdup2( _pool, &med->desc.fmt[i], tmp.str().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; } - _debug("After create negociator!\n"); + + // 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; - pjmedia_sdp_media* remote_med = getRemoteMedia(remote_sdp); - if (remote_med == 0) { - _debug("SIP Failure: unable to get remote media\n"); - return false; - } + *p_med = med; +} - _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; - } +int Sdp::create_local_offer (){ + pj_status_t status; - _debug("Before set codec!\n"); - if (!setAudioCodecFromSDP(remote_med)) { - _debug("SIP Failure: unable to set audio codecs from the remote SDP\n"); - return false; - } + // Build local media capabilities + set_local_media_capabilities (); - return true; -} + // Reference: RFC 4566 [5] - bool -Sdp::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; - } + /* 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); - pjmedia_sdp_media* remote_med = getRemoteMedia(remote_sdp); - if (remote_med==NULL) { - return false; - } + /* 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( ); - _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; - } + // Validate the sdp session + status = pjmedia_sdp_validate( this->_local_offer ); + if (status != PJ_SUCCESS) + return status; + return PJ_SUCCESS; +} - _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* -Sdp::getRemoteSDPFromRequest(pjsip_rx_data *rdata) -{ - pjmedia_sdp_session *sdp; - pjsip_msg *msg; - pjsip_msg_body *body; +int Sdp::create_initial_offer( ){ + pj_status_t status; + pjmedia_sdp_neg_state state; - msg = rdata->msg_info.msg; - body = msg->body; + // Build the SDP session descriptor + create_local_offer( ); - pjmedia_sdp_parse( rdata->tp_info.pool, (char*)body->data, body->len, &sdp ); + // 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 ); - return sdp; -} + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - bool -Sdp::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; + return PJ_SUCCESS; } - bool -Sdp::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; - } +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; - if(payLoad != -1) { - _debug(" Payload: %d\n", payLoad); - setAudioCodec((AudioCodecType)payLoad); - } else - return false; + // 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( ); + + 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 true; + return PJ_SUCCESS; } -void Sdp::sdpAddOrigin( void ) -{ +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); - _localSDP->origin.user = pj_str(pj_gethostname()->ptr); + this->_local_offer->origin.user = pj_str(pj_gethostname()->ptr); // Use Network Time Protocol format timestamp to ensure uniqueness. - _localSDP->origin.id = tv.sec + 2208988800UL; + this->_local_offer->origin.id = tv.sec + 2208988800UL; // The type of network ( IN for INternet ) - _localSDP->origin.net_type = pj_str((char*)"IN"); //STR_IN; + this->_local_offer->origin.net_type = STR_IN; // The type of address - _localSDP->origin.addr_type = pj_str((char*)"IP4"); //STR_IP4; + this->_local_offer->origin.addr_type = STR_IP4; // The address of the machine from which the session was created - _localSDP->origin.addr = pj_str( (char*)_ipAddr.c_str() ); + this->_local_offer->origin.addr = pj_str( (char*)_ip_addr.c_str() ); } -void Sdp::sdpAddConnectionInfo( void ) -{ - _localSDP->conn->net_type = _localSDP->origin.net_type; - _debug("IP : %s\n", _localSDP->origin.net_type.ptr ); - _localSDP->conn->addr_type = _localSDP->origin.addr_type; - _debug("IP : %s\n", _localSDP->origin.addr_type.ptr ); - _localSDP->conn->addr = _localSDP->origin.addr; - _debug("IP : %s\n", _localSDP->origin.addr.ptr ); -} -void Sdp::sdpAddMediaDescription() -{ - pjmedia_sdp_media* med; - pjmedia_sdp_attr *attr; - pjmedia_sdp_rtpmap rtpMap; - //int nbMedia, i; +void Sdp::sdp_add_session_name( void ){ + this->_local_offer->name = STR_SDP_NAME; +} - 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"); +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; +} - CodecOrder::iterator itr; - itr = _codecMap.getActiveCodecs().begin(); - int count = _codecMap.getActiveCodecs().size(); - _debug ("Add media description %i\n", count); - med->desc.fmt_count = count; +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". - int i = 0; - - while(itr != _codecMap.getActiveCodecs().end()) { - _debug ("First media description\n"); - 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++; - } + this->_local_offer->time.start = this->_local_offer->time.stop = 0; +} - //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; +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; +} - _localSDP->media[0] = med; - /*for( i=0; i<nbMedia; i++ ){ - getMediaDescriptorLine( getSDPMediaList()[i], pool, &med ); - this->_local_offer->media[i] = med; - } */ -} +void Sdp::sdp_add_media_description( ){ + pjmedia_sdp_media* med; + int nb_media, i; -pjmedia_sdp_media* Sdp::getRemoteMedia(pjmedia_sdp_session *remote_sdp) -{ - int count, i; + med = PJ_POOL_ZALLOC_T(_pool, pjmedia_sdp_media); + nb_media = get_local_media_cap().size(); + this->_local_offer->media_count = nb_media; - 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]; + for( i=0; i<nb_media; i++ ){ + set_media_descriptor_line( get_local_media_cap()[i], &med ); + this->_local_offer->media[i] = med; } - - return NULL; } -bool Sdp::startNegociation() -{ - pj_status_t status; - _debug("Before negotiate!\n"); - status = pjmedia_sdp_neg_negotiate(_pool, _negociator, 0); - return (status == PJ_SUCCESS); -} - -bool Sdp::createLocalOffer (void) { - pj_status_t status; +std::string Sdp::media_to_string( void ){ + int size, i; + std::ostringstream res; - // 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(); - - _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; + size = _local_media_cap.size(); + for( i = 0; i < size ; i++ ){ + res << _local_media_cap[i]->to_string(); } - - + res << std::endl; + return res.str(); } -bool Sdp::createInitialOffer() -{ - - pj_status_t status; - - createLocalOffer (); - - _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; +void Sdp::clean_session_media(){ + _session_media.clear(); } -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; +void Sdp::set_negociated_offer( const pjmedia_sdp_session *sdp ){ - // 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 ) + 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; - // Build the local offer to respond - createLocalOffer(); + _negociated_offer = (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); + } +} - status = pjmedia_sdp_neg_create_w_remote_offer( _pool, - getLocalSDPSession(), remote, &_negociator ); - state = pjmedia_sdp_neg_get_state( _negociator ); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); +AudioCodec* Sdp::get_session_media( void ){ - return PJ_SUCCESS; + 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; - sdp << "origin= " << _localSDP->origin.user.ptr << "\n"; - sdp << "origin.id= " << _localSDP->origin.id << "\n"; - sdp << "origin.version= " << _localSDP->origin.version<< "\n"; - sdp << "origin.net_type= " << _localSDP->origin.net_type.ptr<< "\n"; - sdp << "origin.addr_type= " << _localSDP->origin.addr_type.ptr<< "\n"; + 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=" << _localSDP->name.ptr<< "\n"; + sdp << "name=" << _local_offer->name.ptr<< "\n"; - sdp << "conn.net_type=" << _localSDP->conn->net_type.ptr<< "\n"; - sdp << "conn.addr_type=" << _localSDP->conn->addr_type.ptr<< "\n"; - sdp << "conn.addr=" << _localSDP->conn->addr.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=" <<_localSDP->time.start<< "\n"; - sdp << "stop=" <<_localSDP->time.stop<< "\n"; + sdp << "start=" <<_local_offer->time.start<< "\n"; + sdp << "stop=" <<_local_offer->time.stop<< "\n"; - sdp << "attr_count=" << _localSDP->attr_count << "\n"; - sdp << "media_count=" << _localSDP->media_count << "\n"; - sdp << "m=" << _localSDP->media[0]->desc.media.ptr << "\n"; - sdp << "port=" << _localSDP->media[0]->desc.port << "\n"; - sdp << "transport=" << _localSDP->media[0]->desc.transport.ptr << "\n"; - sdp << "fmt_count=" << _localSDP->media[0]->desc.fmt_count << "\n"; - sdp << "fmt=" << _localSDP->media[0]->desc.fmt[0].ptr << "\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 << "\n"; + sdp << "port=" << _local_offer->media[0]->desc.port << "\n"; + sdp << "transport=" << _local_offer->media[0]->desc.transport.ptr << "\n"; + sdp << "fmt_count=" << _local_offer->media[0]->desc.fmt_count << "\n"; + sdp << "fmt=" << _local_offer->media[0]->desc.fmt[0].ptr << "\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; + AudioCodec *codec_to_add; + + /* Only one audio media used right now */ + audio = new sdpMedia(MIME_TYPE_AUDIO); + + /* 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]); - //_debug ("LOCAL SDP: \n%s\n", sdp.str().c_str()); + if (iter==codecs_list.end()) + return; + audio->add_codec (iter->second); + } } diff --git a/src/sdp.h b/src/sdp.h index 99d96ba7ac058e135ba1925d074970ad9edeed8e..4b846dce2e980aba3095a58a8fe4f7d1df4cfe1d 100644 --- a/src/sdp.h +++ b/src/sdp.h @@ -29,74 +29,125 @@ #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(); - /** - * 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 + /* + * Read accessor. Get the list of the local media capabilities. + * + * @return std::vector<sdpMedia*> the vector containing the different media */ - bool SIPCallInvite(pjsip_rx_data *rdata); + std::vector<sdpMedia*> get_local_media_cap( void ) { return _local_media_cap; } - bool SIPCallAnsweredWithoutHold(pjsip_rx_data *rdata); + void set_local_media_cap (void); - /** - * Get the local SDP - * @param void - * @return _localSDP pjmedia_sdp_session + /* + * Read accessor. Get the sdp session information + * + * @return pjmedia_sdp_session The structure that describes a SDP session */ - pjmedia_sdp_session* getLocalSDPSession( void ) { return _localSDP; } + pjmedia_sdp_session* get_local_sdp_session( void ) { return _local_offer; } - /** - * Begin negociation of media information between caller and callee - * @param pj_pool_t *pool - * @return bool True if ok + /* + * Write accessor. Set the local IP address that will be used in the sdp session */ - bool startNegociation(); + void set_ip_address( std::string ip_addr ) { _ip_addr = ip_addr; } - /** - * Create the localSDP, media negociation and codec information - * @param pj_pool_t *pool - * @return void + /* + * 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 */ - bool createInitialOffer(); + void set_media_descriptor_line( sdpMedia* media, pjmedia_sdp_media** p_med ); - bool createLocalOffer (); + /* + * On building an invite outside a dialog, build the local offer and create the + * SDP negociator instance with it. + */ + int create_initial_offer( ); - /** - * Set internal codec Map: initialization only, not protected - * @param map The codec map + /* + * 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 setCodecMap(const CodecDescriptor& map) { _codecMap = map; } + void clean_session_media(); - /** - * Save IP Address - * @param ip std::string - * @return void + /* + * Return a string description of the media added to the session, + * ie the local media capabilities */ - void setIp(std::string ip) {_ipAddr = ip;} + std::string media_to_string( void ); - /** - * Get internal codec Map: initialization only, not protected - * @return CodecDescriptor The codec map + /* + * 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 */ - CodecDescriptor& getCodecMap(){ return _codecMap; } + 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 ); + + ///////////////////////////////////////////////////////////////////////////33 void setLocalExternAudioPort(int port){ _localPort = port; } int getLocalExternAudioPort (void){ return _localPort; } - int receiving_initial_offer( pjmedia_sdp_session* remote ); - void toString (void); AudioCodecType getAudioCodec (void) { return _audioCodec; } @@ -125,6 +176,7 @@ class Sdp { */ const std::string& getRemoteIp() { return _remoteIPAddress; } + ///////////////////////////////////////////////////////////////////////// private: /** @@ -133,74 +185,124 @@ class Sdp { */ void setAudioCodec(AudioCodecType audioCodec) { _audioCodec = audioCodec; } - /** Codec pointer */ - AudioCodecType _audioCodec; + /** 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 _ipAddr; + std::string _ip_addr; - int _localPort; + /** Local SDP */ + pjmedia_sdp_session *_local_offer; - /** Remote's IP address */ - std::string _remoteIPAddress; + /* 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; - /** Remote's audio port */ - unsigned int _remoteAudioPort; + // 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 (); - /** - * Get a valid remote media - * @param remote_sdp pjmedia_sdp_session* - * @return pjmedia_sdp_media*. A valid sdp_media_t or 0 + /* + * Mandatory field: Origin ("o=") + * Gives the originator of the session. + * Serves as a globally unique identifier for this version of this session description. */ - pjmedia_sdp_media* getRemoteMedia(pjmedia_sdp_session *remote_sdp); + void sdp_add_origin( void ); - pjmedia_sdp_session * getRemoteSDPFromRequest (pjsip_rx_data *rdata); + /* + * Mandatory field: Protocol version ("v=") + * Add the protocol version in the SDP session description + */ + void sdp_add_protocol( void ); - /** - * 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 + /* + * Optional field: Connection data ("c=") + * Contains connection data. + */ + void sdp_add_connection_info( void ); + + /* + * Mandatory field: Session name ("s=") + * Add a textual session name. */ - bool setRemoteAudioFromSDP(pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media* remote_med); + void sdp_add_session_name( void ); - /** - * Set Audio Codec with the remote choice - * @param remote_med Remote Media info - * @return bool True if everything is set correctly + /* + * Optional field: Session information ("s=") + * Provides textual information about the session. */ - bool setAudioCodecFromSDP(pjmedia_sdp_media* remote_med); + void sdp_add_session_info( void ){} - /** Local SDP */ - pjmedia_sdp_session *_localSDP; - pjmedia_sdp_session *_negociated_offer; + /* + * Optional field: Uri ("u=") + * Add a pointer to additional information about the session. + */ + void sdp_add_uri( void ) {} - /** negociator */ - pjmedia_sdp_neg *_negociator; + /* + * 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 ) {} - // The pool to allocate memory - pj_pool_t *_pool; + /* + * Optional field: Bandwidth ("b=") + * Denotes the proposed bandwidth to be used by the session or the media . + */ + void sdp_add_bandwidth( void ) {} - /** Codec Map */ - CodecDescriptor _codecMap; + /* + * Mandatory field: Timing ("t=") + * Specify the start and the stop time for a session. + */ + void sdp_add_timing( void ); - /** - * Set origin information for local SDP + /* + * Optional field: Time zones ("z=") */ - void sdpAddOrigin( void ); + void sdp_add_time_zone( void ) {} - /** - * Set connection information for local SDP + /* + * Optional field: Encryption keys ("k=") */ - void sdpAddConnectionInfo( void ); - /** - * Set media information including codec for localSDP - * @param pj_pool_t* pool - * @return void + void sdp_add_encryption_key( void ) {} + + /* + * Optional field: Attributes ("a=") + */ + void sdp_add_attributes( ); + + /* + * Mandatory field: Media descriptions ("m=") */ - void sdpAddMediaDescription(); + void sdp_add_media_description(); + + +//////////////////////////////////////////////////////////////////3 + /** Codec pointer */ + AudioCodecType _audioCodec; + + int _localPort; + + /** Remote's IP address */ + std::string _remoteIPAddress; + + /** Remote's audio port */ + unsigned int _remoteAudioPort; +//////////////////////////////////////////////////////////////////// + }; diff --git a/src/sipvoiplink.cpp b/src/sipvoiplink.cpp index a1fec949673df9f2ae270c6031a46a987574ee7f..90f607801039886d34b68b99f7470b138f03b6a5 100644 --- a/src/sipvoiplink.cpp +++ b/src/sipvoiplink.cpp @@ -29,6 +29,14 @@ /**************** 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(); /* @@ -250,6 +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 @@ -267,7 +290,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]; @@ -425,14 +448,16 @@ SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) delete call; call=0; return call; } - //call->setPeerNumber(toUrl); + call->setPeerNumber(getSipTo(toUrl, account->getHostname())); - + 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->getLocalSDP()->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); @@ -463,7 +488,7 @@ SIPVoIPLink::answer(const CallID& id) } // User answered the incoming call, tell peer this news - if (call->getLocalSDP()->startNegociation()) { + if (call->getLocalSDP()->start_negociation()) { // Create and send a 200(OK) response _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); @@ -581,9 +606,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); @@ -595,43 +617,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->getLocalSDP()->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); 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); @@ -640,28 +673,9 @@ SIPVoIPLink::offhold(const CallID& id) return false; } - local_sdp = call->getLocalSDP()->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, "sendonly"); + if (status != PJ_SUCCESS) return false; // Enable audio @@ -808,15 +822,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->getLocalSDP()->getCodecMap().getCodec(call->getLocalSDP()->getAudioCodec()); + AudioCodec *ac = call->getLocalSDP()->get_session_media(); - return ac->getCodecName(); + return ac->getCodecName(); } bool @@ -927,16 +941,10 @@ SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); setCallAudioLocal(call, getLocalIPAddress(), useStun(), getStunServer()); - call->getLocalSDP()->setIp(getLocalIP()); - - // Building the local SDP offer - call->getLocalSDP()->createInitialOffer(); - - call->getLocalSDP()->toString (); // Create the invite session for this call pjsip_inv_session *inv; - status = pjsip_inv_create_uac(dialog, call->getLocalSDP()->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 @@ -1082,7 +1090,7 @@ std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostnam if (call->getConnectionState() != Call::Connected) { //call->SIPCallAnswered(event); - call->getLocalSDP()->SIPCallAnsweredWithoutHold(rdata); + //call->getLocalSDP()->SIPCallAnsweredWithoutHold(rdata); call->setConnectionState(Call::Connected); call->setState(Call::Active); @@ -1103,8 +1111,8 @@ std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostnam } -SIPCall* -SIPVoIPLink::getSIPCall(const CallID& id) + SIPCall* + SIPVoIPLink::getSIPCall(const CallID& id) { Call* call = getCall(id); if (call) { @@ -1114,9 +1122,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; @@ -1353,8 +1361,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; @@ -1364,10 +1372,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); @@ -1573,6 +1581,17 @@ void SIPVoIPLink::setStunServer( const std::string &server ) void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED) { _debug("call_on_media_updated\n"); + + const pjmedia_sdp_session *r_sdp; + + if (status != PJ_SUCCESS) { + return; + } + + // Get the new sdp, result of the negociation + pjmedia_sdp_neg_get_active_local( inv->neg, &r_sdp ); + + // Clean the resulting sdp offer to create a new one (in case of a reinvite) } void call_on_forked(pjsip_inv_session *inv, pjsip_event *e){ @@ -1768,6 +1787,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; @@ -1862,43 +1883,33 @@ void SIPVoIPLink::setStunServer( const std::string &server ) return false; } + // Have to do some stuff with the SDP + // 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 ); + // Set the codec map, IP, peer number and so on... for the SIPCall object setCallAudioLocal(call, link->getLocalIPAddress(), link->useStun(), link->getStunServer()); - call->getLocalSDP()->setCodecMap(Manager::instance().getCodecDescriptorMap()); + call->setConnectionState(Call::Progressing); - call->getLocalSDP()->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->getLocalSDP()->SIPCallInvite(rdata)) { - - // 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) { @@ -1908,8 +1919,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->getLocalSDP()->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 @@ -2324,19 +2334,18 @@ void SIPVoIPLink::setStunServer( const std::string &server ) void on_rx_offer( pjsip_inv_session *inv, const pjmedia_sdp_session *offer ){ #ifdef CAN_REINVITE - - _debug ("reinvite SIP\n"); + _debug ("reinvite SIP\n"); - SIPCall *call; - pj_status_t status; - - call = (SIPCall*)inv->mod_data[getModId()]; - if (!call) - return; + SIPCall *call; + pj_status_t status; + + call = (SIPCall*)inv->mod_data[getModId()]; + if (!call) + return; - call->getLocalSDP()->receiving_initial_offer( (pjmedia_sdp_session*)offer); - _debug("audio codec stté dans l'objet call: %i\n", call->getLocalSDP()->getAudioCodec()); - status=pjsip_inv_set_sdp_answer( call->getInvSession(), call->getLocalSDP()->getLocalSDPSession() ); + call->getLocalSDP()->receiving_initial_offer( (pjmedia_sdp_session*)offer); + _debug("audio codec setté dans l'objet call: %i\n", call->getLocalSDP()->getAudioCodec()); + status=pjsip_inv_set_sdp_answer( call->getInvSession(), call->getLocalSDP()->get_local_sdp_session() ); #endif } diff --git a/src/sipvoiplink.h b/src/sipvoiplink.h index 8790b84d1415a94cb7c1cd855f0108e764c2f3e5..597ec1c32794b9f142d13f978a6f98d136b9d65f 100644 --- a/src/sipvoiplink.h +++ b/src/sipvoiplink.h @@ -303,6 +303,8 @@ class SIPVoIPLink : public VoIPLink */ std::string getCurrentCodecName(); + int inv_session_reinvite (SIPCall *call, std::string direction=""); + private: /** 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 */