diff --git a/sflphone-common/src/sip/sdp.cpp b/sflphone-common/src/sip/sdp.cpp index 771de13d676daead54396aad6b96133289d40b9c..b921c47e2d500df55689d9e6a67867151ba0a936 100644 --- a/sflphone-common/src/sip/sdp.cpp +++ b/sflphone-common/src/sip/sdp.cpp @@ -49,14 +49,20 @@ static const pj_str_t STR_CRYPTO = { (char*) "crypto", 6 }; Sdp::Sdp (pj_pool_t *pool) - : _negociator (NULL) - , _local_media_cap() - , _session_media (0) - , localIpAddr ("") + : memPool (pool) + , negociator (NULL) , localSession (NULL) - , _negociated_offer (NULL) - , _pool (pool) - , localPublishedAudioPort (0) + , remoteSession(NULL) + , activeLocalSession (NULL) + , activeRemoteSession (NULL) + , localAudioMediaCap() + , sessionAudioMedia (0) + , localIpAddr ("") + , remoteIpAddr ("") + , localAudioPort (0) + , remoteAudioPort (0) + , zrtpHelloHash ("") + , srtpCrypto() { } @@ -64,7 +70,93 @@ Sdp::~Sdp() { } -void Sdp::set_media_descriptor_line (sdpMedia *media, pjmedia_sdp_media** p_med) +void Sdp::setActiveLocalSdpSession (const pjmedia_sdp_session *sdp) +{ + + int nb_media, nb_codecs; + int i,j, port; + pjmedia_sdp_media *current; + sdpMedia *media = NULL; + std::string type, dir; + CodecsMap codecs_list; + pjmedia_sdp_attr *attribute = NULL; + pjmedia_sdp_rtpmap *rtpmap; + + _debug ("SDP: Set active local SDP session"); + + activeLocalSession = (pjmedia_sdp_session*) sdp; + + codecs_list = Manager::instance().getCodecDescriptorMap().getCodecsMap(); + + // retrieve the media information + nb_media = activeLocalSession->media_count; + + for (i=0; i<nb_media ; i++) { + // Retrieve the media + current = activeLocalSession->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++) { + attribute = pjmedia_sdp_media_find_attr (current, &STR_RTPMAP, NULL); + // pj_strtoul(attribute->pt) + + if (!attribute) + return; + + pjmedia_sdp_attr_to_rtpmap (memPool, attribute, &rtpmap); + + CodecsMap::iterator iter = codecs_list.find ( (AudioCodecType) pj_strtoul (&rtpmap->pt)); + + if (iter==codecs_list.end()) + return; + + media->add_codec (iter->second); + } + + sessionAudioMedia.push_back (media); + } +} + +void Sdp::setActiveRemoteSdpSession (const pjmedia_sdp_session *sdp) +{ + + std::string type, dir; + CodecsMap codecs_list; + + _debug ("SDP: Set negotiated SDP"); + + activeRemoteSession = (pjmedia_sdp_session*) sdp; +} + +AudioCodec* Sdp::getSessionMedia (void) +{ + + int nb_media; + int nb_codec; + sfl::Codec *codec = NULL; + std::vector<sdpMedia*> media_list; + + _debug ("SDP: Get session media"); + + media_list = getSessionMediaList (); + nb_media = media_list.size(); + + if (nb_media > 0) { + nb_codec = media_list[0]->get_media_codec_list().size(); + + if (nb_codec > 0) { + codec = media_list[0]->get_media_codec_list() [0]; + } + } + + return static_cast<AudioCodec *>(codec); +} + +void Sdp::setMediaDescriptorLine (sdpMedia *media, pjmedia_sdp_media** p_med) { pjmedia_sdp_media* med; @@ -74,21 +166,21 @@ void Sdp::set_media_descriptor_line (sdpMedia *media, pjmedia_sdp_media** p_med) int count, i; std::string tmp; - med = PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_media); + med = PJ_POOL_ZALLOC_T (memPool, pjmedia_sdp_media); // Get the right media format - pj_strdup (_pool, &med->desc.media, + pj_strdup (memPool, &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(); // in case of sdes, media are tagged as "RTP/SAVP", RTP/AVP elsewhere - if (_srtp_crypto.empty()) { + if (srtpCrypto.empty()) { - pj_strdup (_pool, &med->desc.transport, &STR_RTP_AVP); + pj_strdup (memPool, &med->desc.transport, &STR_RTP_AVP); } else { - pj_strdup (_pool, &med->desc.transport, &STR_RTP_SAVP); + pj_strdup (memPool, &med->desc.transport, &STR_RTP_SAVP); } // Media format ( RTP payload ) @@ -99,9 +191,9 @@ void Sdp::set_media_descriptor_line (sdpMedia *media, pjmedia_sdp_media** p_med) for (i=0; i<count; i++) { codec = media->get_media_codec_list() [i]; - tmp = this->convert_int_to_string (codec->getPayloadType ()); + tmp = this->convertIntToString (codec->getPayloadType ()); _debug ("%s", tmp.c_str()); - pj_strdup2 (_pool, &med->desc.fmt[i], tmp.c_str()); + pj_strdup2 (memPool, &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 @@ -119,37 +211,71 @@ void Sdp::set_media_descriptor_line (sdpMedia *media, pjmedia_sdp_media** p_med) rtpmap.param.slen = 0; - pjmedia_sdp_rtpmap_to_attr (_pool, &rtpmap, &attr); + pjmedia_sdp_rtpmap_to_attr (memPool, &rtpmap, &attr); med->attr[med->attr_count++] = attr; } // Add the direction stream - attr = (pjmedia_sdp_attr*) pj_pool_zalloc (_pool, sizeof (pjmedia_sdp_attr)); + attr = (pjmedia_sdp_attr*) pj_pool_zalloc (memPool, sizeof (pjmedia_sdp_attr)); - pj_strdup2 (_pool, &attr->name, media->get_stream_direction_str().c_str()); + pj_strdup2 (memPool, &attr->name, media->get_stream_direction_str().c_str()); med->attr[ med->attr_count++] = attr; - if (!_zrtp_hello_hash.empty()) { + if (!zrtpHelloHash.empty()) { try { - sdp_add_zrtp_attribute (med,_zrtp_hello_hash); + addZrtpAttribute (med,zrtpHelloHash); } catch (...) { throw; } - } else { - _warn ("No hash specified"); } *p_med = med; } -int Sdp::createLocalSession (CodecOrder selectedCodecs) +void Sdp::setLocalMediaCapabilities (CodecOrder selectedCodecs) { - pj_status_t status; + unsigned int i; + sdpMedia *audio; + CodecsMap codecs_list; + CodecsMap::iterator iter; - _info ("SDP: Create local offer"); + // Clean it first + localAudioMediaCap.clear(); + + _debug ("SDP: Fetch local media capabilities. Local extern audio port: %i" , getLocalPublishedAudioPort()); + + /* Only one audio media used right now */ + audio = new sdpMedia (MIME_TYPE_AUDIO); + audio->set_port (getLocalPublishedAudioPort()); + + /* We retrieve the codecs selected by the user */ + codecs_list = Manager::instance().getCodecDescriptorMap().getCodecsMap(); + + if (selectedCodecs.size() == 0) { + throw SdpException ("No selected codec while building local SDP offer"); + } + + for (i=0; i<selectedCodecs.size(); i++) { + iter=codecs_list.find (selectedCodecs[i]); + + if (iter!=codecs_list.end()) { + audio->add_codec (iter->second); + } else { + _warn ("SDP: Couldn't find audio codec"); + } + } + + localAudioMediaCap.push_back (audio); +} + +int Sdp::createLocalSession (CodecOrder selectedCodecs) +{ + char buffer[1000]; + + _info ("SDP: Create local session"); // Build local media capabilities setLocalMediaCapabilities (selectedCodecs); @@ -157,29 +283,32 @@ int Sdp::createLocalSession (CodecOrder selectedCodecs) // Reference: RFC 4566 [5] /* Create and initialize basic SDP session */ - this->localSession = PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_session); - this->localSession->conn = PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_conn); + localSession = PJ_POOL_ZALLOC_T (memPool, pjmedia_sdp_session); + localSession->conn = PJ_POOL_ZALLOC_T (memPool, 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_add_media_description(); - - if (!_srtp_crypto.empty()) { - sdp_add_sdes_attribute (_srtp_crypto); + addProtocol(); + addOrigin(); + addSessionName(); + addConnectionInfo(); + addTiming(); + addMediaDescription(); + + if (!srtpCrypto.empty()) { + addSdesAttribute (srtpCrypto); } + memset(buffer, 0, 1000); + pjmedia_sdp_print(getLocalSdpSession(), buffer, 1000); + _debug("SDP: Local SDP Session: %s\n\n", buffer); + // Validate the sdp session - return pjmedia_sdp_validate (this->localSession); + return pjmedia_sdp_validate (localSession); } -int Sdp::createInitialOffer (CodecOrder selectedCodecs) +int Sdp::createOffer (CodecOrder selectedCodecs) { - pj_status_t status; pjmedia_sdp_neg_state state; @@ -187,33 +316,32 @@ int Sdp::createInitialOffer (CodecOrder selectedCodecs) // Build the SDP session descriptor status = createLocalSession (selectedCodecs); - if (status != PJ_SUCCESS) { _error ("SDP: Error: Failed to create initial offer"); return status; } // Create the SDP negociator instance with local offer - status = pjmedia_sdp_neg_create_w_local_offer (_pool, getLocalSdpSession(), &_negociator); - + status = pjmedia_sdp_neg_create_w_local_offer (memPool, getLocalSdpSession(), &negociator); if (status != PJ_SUCCESS) { _error ("SDP: Error: Failed to create an initial SDP negociator"); return status; } - state = pjmedia_sdp_neg_get_state (_negociator); + state = pjmedia_sdp_neg_get_state (negociator); + + _debug("SDP: Negociator state %s\n", pjmedia_sdp_neg_state_str(state)); PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); return PJ_SUCCESS; } -int Sdp::receivingInitialOffer (pjmedia_sdp_session* remote, CodecOrder selectedCodecs) +int Sdp::recieveOffer (const pjmedia_sdp_session* remote, CodecOrder selectedCodecs) { - _debug ("SDP: Receiving initial offer"); + char buffer[1000]; - // 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 ) + _debug ("SDP: Receiving initial offer"); pj_status_t status; @@ -221,63 +349,67 @@ int Sdp::receivingInitialOffer (pjmedia_sdp_session* remote, CodecOrder selected return !PJ_SUCCESS; } - // 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 - status = createLocalSession (selectedCodecs); - - if (status != PJ_SUCCESS) { - _error ("SDP: Error: Failed to create initial offer"); - return status; + memset(buffer, 0, 1000); + pjmedia_sdp_print(remote, buffer, 1000); + _debug("SDP: Remote SDP Session: %s\n\n", buffer); + + // If called for the first time + if(localSession == NULL) { + // Build the local offer to respond + status = createLocalSession (selectedCodecs); + if (status != PJ_SUCCESS) { + _error ("SDP: Error: Failed to create initial offer"); + return status; + } } - // Retrieve some useful remote information - this->set_media_transport_info_from_remote_sdp (remote); + remoteSession = pjmedia_sdp_session_clone (memPool, remote); - status = pjmedia_sdp_neg_create_w_remote_offer (_pool, getLocalSdpSession(), remote, &_negociator); + status = pjmedia_sdp_neg_create_w_remote_offer (memPool, getLocalSdpSession(), getRemoteSdpSession(), &negociator); PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); return PJ_SUCCESS; } -int Sdp::receivingAnswerAfterInitialOffer(pjmedia_sdp_session* remote) +int Sdp::receivingAnswerAfterInitialOffer(const pjmedia_sdp_session* remote) { pj_status_t status; - if(pjmedia_sdp_neg_get_state(_negociator) != PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { + if(pjmedia_sdp_neg_get_state(negociator) != PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { _warn("SDP: Session not in a valid state for receiving answer"); } - status = pjmedia_sdp_neg_set_remote_answer(_pool, _negociator, remote); + status = pjmedia_sdp_neg_set_remote_answer(memPool, negociator, remote); if(status != PJ_SUCCESS) { _warn("SDP: Error: Could not set SDP remote answer"); } - if(pjmedia_sdp_neg_get_state(_negociator) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { + if(pjmedia_sdp_neg_get_state(negociator) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { _warn("SDP: Session not in a valid state after receiving answer"); } + _debug("SDP: Negotiator state %s\n", pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(negociator))); + return status; } -int Sdp::generateAnswerFromInitialOffer(void) +int Sdp::generateAnswerAfterInitialOffer(void) { pj_status_t status; - if(pjmedia_sdp_neg_get_state(_negociator) != PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) { + if(pjmedia_sdp_neg_get_state(negociator) != PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) { _warn("SDP: Session not in a valid state for generating answer"); } - status = pjmedia_sdp_neg_set_local_answer (_pool, _negociator, localSession); + status = pjmedia_sdp_neg_set_local_answer (memPool, negociator, localSession); if(status != PJ_SUCCESS) { _warn("SDP: Error: could not set SDP local answer"); } - if(pjmedia_sdp_neg_get_state(_negociator) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { + if(pjmedia_sdp_neg_get_state(negociator) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { _warn("SDP: Session not in a valid state after generating answer"); } @@ -288,95 +420,56 @@ int Sdp::generateAnswerFromInitialOffer(void) pj_status_t Sdp::startNegociation() { pj_status_t status; + const pjmedia_sdp_session *active_local; + const pjmedia_sdp_session *active_remote; _debug ("SDP: Start negotiation"); - if(_negociator == NULL) { + if(negociator == NULL) { _error("SDP: Error: Negociator is NULL in SDP session"); } - if(pjmedia_sdp_neg_get_state(_negociator) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { + if(pjmedia_sdp_neg_get_state(negociator) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { _warn("SDP: Warning: negociator not in wright state for negotiation"); } - status = pjmedia_sdp_neg_negotiate (_pool, _negociator, 0); + status = pjmedia_sdp_neg_negotiate (memPool, negociator, 0); + if(status != PJ_SUCCESS) { + return status; + } + + _debug("SDP: Negotiator state %s\n", pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(negociator))); + + status = pjmedia_sdp_neg_get_active_local(negociator, &active_local); + if(status != PJ_SUCCESS) { + _error("SDP: Could not retrieve local active session"); + } + + setActiveLocalSdpSession(active_local); + + status = pjmedia_sdp_neg_get_active_remote(negociator, &active_remote); + if(status != PJ_SUCCESS) { + _error("SDP: Could not retrieve remote active session"); + } + + setActiveRemoteSdpSession(active_remote); return status; } -//pj_status_t Sdp::check_sdp_answer (pjsip_inv_session *inv, pjsip_rx_data *rdata) -//{ -// -// static const pj_str_t str_application = { (char*) "application", 11 }; -// static const pj_str_t str_sdp = { (char*) "sdp", 3 }; -// pj_status_t status; -// pjsip_msg * message = NULL; -// pjmedia_sdp_session * remote_sdp = NULL; -// -// _debug ("Sdp: Check SDP answer"); -// -// if (pjmedia_sdp_neg_get_state (inv->neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { -// -// message = rdata->msg_info.msg; -// -// if (message == NULL) { -// _error ("SDP: No message"); -// return PJMEDIA_SDP_EINSDP; -// } -// -// if (message->body == NULL) { -// _error ("SDP: Empty message body"); -// return PJMEDIA_SDP_EINSDP; -// } -// -// if (pj_stricmp (&message->body->content_type.type, &str_application) || pj_stricmp (&message->body->content_type.subtype, &str_sdp)) { -// _error ("SDP: Incoming Message does not contain SDP"); -// return PJMEDIA_SDP_EINSDP; -// } -// -// // Parse the SDP body. -// status = pjmedia_sdp_parse (rdata->tp_info.pool, (char*) message->body->data, message->body->len, &remote_sdp); -// -// if (status == PJ_SUCCESS) { -// status = pjmedia_sdp_validate (remote_sdp); -// } -// -// if (status != PJ_SUCCESS) { -// _warn ("SDP: cannot be validated"); -// return PJMEDIA_SDP_EINSDP; -// } -// -// // This is an answer -// _debug ("SDP: Got SDP answer %s", pjsip_rx_data_get_info (rdata)); -// -// status = pjmedia_sdp_neg_set_remote_answer (inv->pool, inv->neg, remote_sdp); -// -// if (status != PJ_SUCCESS) { -// _error ("SDP: Error: while processing remote answer %s", pjsip_rx_data_get_info (rdata)); -// return PJMEDIA_SDP_EINSDP; -// } -// -// // Prefer our codecs to remote when possible -// pjmedia_sdp_neg_set_prefer_remote_codec_order (inv->neg, 0); -// -// status = pjmedia_sdp_neg_negotiate (inv->pool, inv->neg, 0); -// -// _debug ("Negotiation returned with status %d PJ_SUCCESS being %d", status, PJ_SUCCESS); -// } else { -// _debug ("No need to check sdp answer since we are UAS"); -// return PJ_SUCCESS; -// } -// -// return status; -//} - -void Sdp::sdp_add_protocol (void) +void Sdp::updateInternalState() { + + // Populate internal field + setMediaTransportInfoFromRemoteSdp (activeRemoteSession); +} + +void Sdp::addProtocol (void) { this->localSession->origin.version = 0; } -void Sdp::sdp_add_origin (void) +void Sdp::addOrigin (void) { pj_time_val tv; @@ -393,14 +486,14 @@ void Sdp::sdp_add_origin (void) this->localSession->origin.addr = pj_str ( (char*) localIpAddr.c_str()); } -void Sdp::sdp_add_session_name (void) +void Sdp::addSessionName (void) { this->localSession->name = STR_SDP_NAME; } -void Sdp::sdp_add_connection_info (void) +void Sdp::addConnectionInfo (void) { this->localSession->conn->net_type = localSession->origin.net_type; @@ -409,7 +502,7 @@ void Sdp::sdp_add_connection_info (void) } -void Sdp::sdp_add_timing (void) +void Sdp::addTiming (void) { // RFC 3264: An offer/answer model session description protocol @@ -419,33 +512,33 @@ void Sdp::sdp_add_timing (void) this->localSession->time.start = this->localSession->time.stop = 0; } -void Sdp::sdp_add_attributes() +void Sdp::addAttributes() { pjmedia_sdp_attr *a; this->localSession->attr_count = 1; - a = PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_attr); + a = PJ_POOL_ZALLOC_T (memPool, pjmedia_sdp_attr); a->name=STR_SENDRECV; localSession->attr[0] = a; } -void Sdp::sdp_add_media_description() +void Sdp::addMediaDescription() { pjmedia_sdp_media* med; int nb_media, i; - med = PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_media); + med = PJ_POOL_ZALLOC_T (memPool, pjmedia_sdp_media); nb_media = getLocalMediaCap().size(); this->localSession->media_count = nb_media; for (i=0; i<nb_media; i++) { - set_media_descriptor_line (getLocalMediaCap() [i], &med); + setMediaDescriptorLine (getLocalMediaCap() [i], &med); this->localSession->media[i] = med; } } -void Sdp::sdp_add_sdes_attribute (std::vector<std::string>& crypto) throw (SdpException) +void Sdp::addSdesAttribute (std::vector<std::string>& crypto) throw (SdpException) { // temporary buffer used to store crypto attribute @@ -456,9 +549,9 @@ void Sdp::sdp_add_sdes_attribute (std::vector<std::string>& crypto) throw (SdpEx while (iter != crypto.end()) { // the attribute to add to sdp - pjmedia_sdp_attr *attribute = (pjmedia_sdp_attr*) pj_pool_zalloc (_pool, sizeof (pjmedia_sdp_attr)); + pjmedia_sdp_attr *attribute = (pjmedia_sdp_attr*) pj_pool_zalloc (memPool, sizeof (pjmedia_sdp_attr)); - attribute->name = pj_strdup3 (_pool, "crypto"); + attribute->name = pj_strdup3 (memPool, "crypto"); // _debug("crypto from sdp: %s", crypto.c_str()); @@ -467,7 +560,7 @@ void Sdp::sdp_add_sdes_attribute (std::vector<std::string>& crypto) throw (SdpEx "%.*s", (int) (*iter).size(), (*iter).c_str()); attribute->value.slen = len; - attribute->value.ptr = (char*) pj_pool_alloc (_pool, attribute->value.slen+1); + attribute->value.ptr = (char*) pj_pool_alloc (memPool, attribute->value.slen+1); pj_memcpy (attribute->value.ptr, tempbuf, attribute->value.slen+1); // get number of media for this SDP @@ -488,15 +581,15 @@ void Sdp::sdp_add_sdes_attribute (std::vector<std::string>& crypto) throw (SdpEx } -void Sdp::sdp_add_zrtp_attribute (pjmedia_sdp_media* media, std::string hash) throw (SdpException) +void Sdp::addZrtpAttribute (pjmedia_sdp_media* media, std::string hash) throw (SdpException) { pjmedia_sdp_attr *attribute; char tempbuf[256]; int len; - attribute = (pjmedia_sdp_attr*) pj_pool_zalloc (_pool, sizeof (pjmedia_sdp_attr)); + attribute = (pjmedia_sdp_attr*) pj_pool_zalloc (memPool, sizeof (pjmedia_sdp_attr)); - attribute->name = pj_strdup3 (_pool, "zrtp-hash"); + attribute->name = pj_strdup3 (memPool, "zrtp-hash"); /* Format: ":version value" */ len = pj_ansi_snprintf (tempbuf, sizeof (tempbuf), @@ -507,7 +600,7 @@ void Sdp::sdp_add_zrtp_attribute (pjmedia_sdp_media* media, std::string hash) th hash.c_str()); attribute->value.slen = len; - attribute->value.ptr = (char*) pj_pool_alloc (_pool, attribute->value.slen+1); + attribute->value.ptr = (char*) pj_pool_alloc (memPool, attribute->value.slen+1); pj_memcpy (attribute->value.ptr, tempbuf, attribute->value.slen+1); if (pjmedia_sdp_media_add_attr (media, attribute) != PJ_SUCCESS) { @@ -515,15 +608,15 @@ void Sdp::sdp_add_zrtp_attribute (pjmedia_sdp_media* media, std::string hash) th } } -std::string Sdp::media_to_string (void) +std::string Sdp::mediaToString (void) { int size, i; std::ostringstream res; - size = _local_media_cap.size(); + size = localAudioMediaCap.size(); for (i = 0; i < size ; i++) { - res << _local_media_cap[i]->to_string(); + res << localAudioMediaCap[i]->to_string(); } res << std::endl; @@ -535,117 +628,42 @@ void Sdp::clean_session_media() { _info ("SDP: Clean session media"); - if (_session_media.size() > 0) { + if (sessionAudioMedia.size() > 0) { - std::vector<sdpMedia *>::iterator iter = _session_media.begin(); + std::vector<sdpMedia *>::iterator iter = sessionAudioMedia.begin(); sdpMedia *media; - while (iter != _session_media.end()) { + while (iter != sessionAudioMedia.end()) { _debug ("delete media"); media = *iter; delete media; iter++; } - _session_media.clear(); + sessionAudioMedia.clear(); } } -void Sdp::clean_local_media_capabilities() +void Sdp::cleanLocalMediaCapabilities() { _info ("SDP: Clean local media capabilities"); - if (_local_media_cap.size() > 0) { + if (localAudioMediaCap.size() > 0) { - std::vector<sdpMedia *>::iterator iter = _local_media_cap.begin(); + std::vector<sdpMedia *>::iterator iter = localAudioMediaCap.begin(); sdpMedia *media; - while (iter != _local_media_cap.end()) { + while (iter != localAudioMediaCap.end()) { media = *iter; delete media; iter++; } - _local_media_cap.clear(); - } -} - -void Sdp::set_negotiated_sdp (const pjmedia_sdp_session *sdp) -{ - - int nb_media, nb_codecs; - int i,j, port; - pjmedia_sdp_media *current; - sdpMedia *media = NULL; - std::string type, dir; - CodecsMap codecs_list; - pjmedia_sdp_attr *attribute = NULL; - pjmedia_sdp_rtpmap *rtpmap; - - _debug ("SDP: Set negotiated SDP"); - - _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++) { - attribute = pjmedia_sdp_media_find_attr (current, &STR_RTPMAP, NULL); - // pj_strtoul(attribute->pt) - - if (!attribute) - return; - - pjmedia_sdp_attr_to_rtpmap (_pool, attribute, &rtpmap); - - CodecsMap::iterator iter = codecs_list.find ( (AudioCodecType) pj_strtoul (&rtpmap->pt)); - - if (iter==codecs_list.end()) - return; - - media->add_codec (iter->second); - } - - _session_media.push_back (media); + localAudioMediaCap.clear(); } } -AudioCodec* Sdp::get_session_media (void) -{ - - int nb_media; - int nb_codec; - sfl::Codec *codec = NULL; - std::vector<sdpMedia*> media_list; - - _debug ("SDP: Get session media"); - - media_list = get_session_media_list (); - nb_media = media_list.size(); - - if (nb_media > 0) { - nb_codec = media_list[0]->get_media_codec_list().size(); - - if (nb_codec > 0) { - codec = media_list[0]->get_media_codec_list() [0]; - } - } - - return static_cast<AudioCodec *>(codec); -} - void Sdp::toString (void) { @@ -683,44 +701,7 @@ void Sdp::toString (void) _debug ("LOCAL SDP: \n%s", sdp.str().c_str()); } -void Sdp::setLocalMediaCapabilities (CodecOrder selectedCodecs) -{ - - unsigned int i; - sdpMedia *audio; - CodecsMap codecs_list; - CodecsMap::iterator iter; - - // Clean it first - _local_media_cap.clear(); - - _debug ("SDP: Fetch local media capabilities. Local extern audio port: %i" , getLocalPublishedAudioPort()); - - /* Only one audio media used right now */ - audio = new sdpMedia (MIME_TYPE_AUDIO); - audio->set_port (getLocalPublishedAudioPort()); - - /* We retrieve the codecs selected by the user */ - codecs_list = Manager::instance().getCodecDescriptorMap().getCodecsMap(); - - if (selectedCodecs.size() == 0) { - throw SdpException ("No selected codec while building local SDP offer"); - } - - for (i=0; i<selectedCodecs.size(); i++) { - iter=codecs_list.find (selectedCodecs[i]); - - if (iter!=codecs_list.end()) { - audio->add_codec (iter->second); - } else { - _warn ("SDP: Couldn't find audio codec"); - } - } - - _local_media_cap.push_back (audio); -} - -void Sdp::attribute_port_to_all_media (int port) +void Sdp::setPortToAllMedia (int port) { std::vector<sdpMedia*> medias; @@ -736,14 +717,14 @@ void Sdp::attribute_port_to_all_media (int port) } } -std::string Sdp::convert_int_to_string (int value) +std::string Sdp::convertIntToString (int value) { std::ostringstream result; result << value; return result.str(); } -void Sdp::set_remote_ip_from_sdp (const pjmedia_sdp_session *r_sdp) +void Sdp::setRemoteIpFromSdp (const pjmedia_sdp_session *r_sdp) { std::string remote_ip (r_sdp->conn->addr.ptr, r_sdp->conn->addr.slen); @@ -751,7 +732,7 @@ void Sdp::set_remote_ip_from_sdp (const pjmedia_sdp_session *r_sdp) this->setRemoteIP (remote_ip); } -void Sdp::set_remote_audio_port_from_sdp (pjmedia_sdp_media *r_media) +void Sdp::setRemoteAudioPortFromSdp (pjmedia_sdp_media *r_media) { int remote_port; @@ -761,7 +742,7 @@ void Sdp::set_remote_audio_port_from_sdp (pjmedia_sdp_media *r_media) this->setRemoteAudioPort (remote_port); } -void Sdp::set_media_transport_info_from_remote_sdp (const pjmedia_sdp_session *remote_sdp) +void Sdp::setMediaTransportInfoFromRemoteSdp (const pjmedia_sdp_session *remote_sdp) { _info ("SDP: Fetching media from sdp"); @@ -771,20 +752,20 @@ void Sdp::set_media_transport_info_from_remote_sdp (const pjmedia_sdp_session *r pjmedia_sdp_media *r_media; - this->get_remote_sdp_media_from_offer (remote_sdp, &r_media); + getRemoteSdpMediaFromOffer (remote_sdp, &r_media); if (r_media==NULL) { _warn ("SDP: Error: no remote sdp media found in the remote offer"); return; } - this->set_remote_audio_port_from_sdp (r_media); + setRemoteAudioPortFromSdp (r_media); - this->set_remote_ip_from_sdp (remote_sdp); + setRemoteIpFromSdp (remote_sdp); } -void Sdp::get_remote_sdp_media_from_offer (const pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media** r_media) +void Sdp::getRemoteSdpMediaFromOffer (const pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media** r_media) { int count, i; @@ -802,7 +783,7 @@ void Sdp::get_remote_sdp_media_from_offer (const pjmedia_sdp_session* remote_sdp } } -void Sdp::get_remote_sdp_crypto_from_offer (const pjmedia_sdp_session* remote_sdp, CryptoOffer& crypto_offer) +void Sdp::getRemoteSdpCryptoFromOffer (const pjmedia_sdp_session* remote_sdp, CryptoOffer& crypto_offer) { int i, j; diff --git a/sflphone-common/src/sip/sdp.h b/sflphone-common/src/sip/sdp.h index 613c8edb8b2b8e9e2f549eaf32a71e81fda9a51a..e44fcf5fa333607adb783d0e99da6c9e610b1ff8 100644 --- a/sflphone-common/src/sip/sdp.h +++ b/sflphone-common/src/sip/sdp.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc. - * * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -64,6 +64,7 @@ class SdpException : public std::exception }; typedef std::vector<std::string> CryptoOffer; +typedef std::vector<sdpMedia *> SdpMediaList; class Sdp { @@ -85,31 +86,79 @@ class Sdp * * @return std::vector<sdpMedia*> the vector containing the different media */ - std::vector<sdpMedia*> getLocalMediaCap (void) { - return _local_media_cap; + std::vector<sdpMedia *> getLocalMediaCap (void) { + return localAudioMediaCap; } - /* - * Read accessor. Get the sdp session information - * - * @return pjmedia_sdp_session The structure that describes a SDP session - */ - pjmedia_sdp_session* getLocalSdpSession (void) { - return localSession; - } /** * Accessor for the internal memory pool */ inline pj_pool_t *getMemoryPool (void) { - return _pool; + return memPool; + } + + /** + * Read accessor. Get the local passive sdp session information before negotiation + * + * @return The structure that describes a SDP session + */ + pjmedia_sdp_session *getLocalSdpSession (void) { + return localSession; + } + + /** + * Read accessor. Get the remote passive sdp session information before negotiation + * + * @return The structure that describe the SDP session + */ + pjmedia_sdp_session *getRemoteSdpSession(void) { + return remoteSession; + } + + /** + * Set the negociated sdp offer from the sip payload. + * + * @param sdp the negociated offer + */ + void setActiveLocalSdpSession (const pjmedia_sdp_session *sdp); + + /** + * read accessor. Return the negociated local session + * + * @return pjmedia_sdp_session The negociated offer + */ + pjmedia_sdp_session* getActiveLocalSdpSession (void) { + return activeLocalSession; + } + + /** + * Retrieve the negociated sdp offer from the sip payload. + * + * @param sdp the negociated offer + */ + void setActiveRemoteSdpSession (const pjmedia_sdp_session *sdp); + + /** + * read accessor. Return the negociated offer + * + * @return pjmedia_sdp_session The negociated offer + */ + pjmedia_sdp_session* getActiveRemoteSdpSession (void) { + return activeRemoteSession; } + + /** + * Return the codec of the first media after negociation + */ + AudioCodec* getSessionMedia (void); + /* * On building an invite outside a dialog, build the local offer and create the * SDP negociator instance with it. */ - int createInitialOffer (CodecOrder selectedCodecs); + int createOffer (CodecOrder selectedCodecs); /* * On receiving an invite outside a dialog, build the local offer and create the @@ -117,7 +166,7 @@ class Sdp * * @param remote The remote offer */ - int receivingInitialOffer (pjmedia_sdp_session* remote, CodecOrder selectedCodecs); + int recieveOffer (const pjmedia_sdp_session* remote, CodecOrder selectedCodecs); /* * On receiving a message, check if it contains SDP and negotiate. Should be used for @@ -127,20 +176,25 @@ class Sdp * @param inv The the invitation * @param rdata The remote data */ + int receivingAnswerAfterInitialOffer(const pjmedia_sdp_session* remote); - int receivingAnswerAfterInitialOffer(pjmedia_sdp_session* remote); - - int generateAnswerFromInitialOffer(void); + /** + * Generate answer after receiving Initial Offer + */ + int generateAnswerAfterInitialOffer(void); - /* - * Start the sdp negociation. - * - * @return pj_status_t 0 on success - * 1 otherwise - */ + /** + * Start the sdp negociation. + * + * @return pj_status_t 0 on success + * 1 otherwise + */ pj_status_t startNegociation (void); - // pj_status_t check_sdp_answer (pjsip_inv_session *inv, pjsip_rx_data *rdata); + /** + * Update internal state after negotiation + */ + void updateInternalState(void); /** * Remove all media in the session media vector. @@ -150,35 +204,13 @@ class Sdp /** * Remove all media in local media capability vector */ - void clean_local_media_capabilities (void); + void cleanLocalMediaCapabilities (void); - /* + /** * 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; - } - - - /* - * Retrieve the negociated sdp offer from the sip payload. - * - * @param sdp the negociated offer - */ - void set_negotiated_sdp (const pjmedia_sdp_session *sdp); + std::string mediaToString (void); /* * Attribute the specified port to every medias provided @@ -187,7 +219,7 @@ class Sdp * * @param port The media port */ - void attribute_port_to_all_media (int port); + void setPortToAllMedia (int port); /* @@ -208,14 +240,14 @@ class Sdp * @param Set the published audio port */ void setLocalPublishedAudioPort (int port) { - localPublishedAudioPort = port; + localAudioPort = port; } /** * @return The published audio port */ int getLocalPublishedAudioPort (void) { - return localPublishedAudioPort; + return localAudioPort; } /** @@ -250,31 +282,20 @@ class Sdp return remoteAudioPort; } - void set_media_transport_info_from_remote_sdp (const pjmedia_sdp_session *remote_sdp); - - std::vector<sdpMedia*> get_session_media_list (void) { - return _session_media; + std::vector<sdpMedia*> getSessionMediaList (void) { + return sessionAudioMedia; } - void get_remote_sdp_crypto_from_offer (const pjmedia_sdp_session* remote_sdp, CryptoOffer& crypto_offer); + void getRemoteSdpCryptoFromOffer (const pjmedia_sdp_session* remote_sdp, CryptoOffer& crypto_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); - /* Set the zrtp hash that was previously calculated from the hello message in the zrtp layer. * This hash value is unique at the media level. Therefore, if video support is added, one would * have to set the correct zrtp-hash value in the corresponding media section. * @param hash The hello hash of a rtp session. (Only audio at the moment) */ inline void set_zrtp_hash (const std::string& hash) { - _zrtp_hello_hash = hash; + zrtpHelloHash = hash; _debug ("Zrtp hash set with %s\n", hash.c_str()); } @@ -282,21 +303,21 @@ class Sdp * @param mk The Master Key of a srtp session. */ inline void set_srtp_crypto (const std::vector<std::string> lc) { - _srtp_crypto = lc; + srtpCrypto = lc; } void toString (void); - /** negociator */ - pjmedia_sdp_neg *_negociator; private: - /** * The pool to allocate memory, ownership to SipCall * SDP should not release the pool itself */ - pj_pool_t *_pool; + pj_pool_t *memPool; + + /** negociator */ + pjmedia_sdp_neg *negociator; /** * Local SDP @@ -308,39 +329,73 @@ class Sdp */ pjmedia_sdp_session *remoteSession; - /* 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; + /** + * The negociated SDP remote session + * 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 *activeLocalSession; - /** Codec Map */ - std::vector<sdpMedia *> _local_media_cap; + /** + * The negociated SDP remote session + * 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 *activeRemoteSession; - /* The media that will be used by the session (after the SDP negociation) */ - std::vector<sdpMedia *> _session_media; + /** + * Codec Map used for offer + */ + SdpMediaList localAudioMediaCap; - /** IP address */ - std::string localIpAddr; + /** + * The media that will be used by the session (after the SDP negociation) + */ + SdpMediaList sessionAudioMedia; - /** Local audio port */ - int localPublishedAudioPort; + /** + * IP address + */ + std::string localIpAddr; - /** Remote's IP address */ + /** + * Remote's IP address + */ std::string remoteIpAddr; - /** Remote audio port */ - unsigned int remoteAudioPort; + /** + * Local audio port + */ + int localAudioPort; + /** + * Remote audio port + */ + unsigned int remoteAudioPort; - std::string _zrtp_hello_hash; + /** + * Zrtp hello hash + */ + std::string zrtpHelloHash; - /** "a=crypto" sdes local attributes obtained from AudioSrtpSession */ - std::vector<std::string> _srtp_crypto; + /** + * "a=crypto" sdes local attributes obtained from AudioSrtpSession + */ + std::vector<std::string> srtpCrypto; Sdp (const Sdp&); //No Copy Constructor Sdp& operator= (const Sdp&); //No Assignment Operator + /* + * 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 setMediaDescriptorLine (sdpMedia* media, pjmedia_sdp_media** p_med); + /** * Build the local media capabilities for this session * @param List of codec in preference order @@ -352,96 +407,89 @@ class Sdp */ int createLocalSession (CodecOrder selectedCodecs); + /* + * Mandatory field: Protocol version ("v=") + * Add the protocol version in the SDP session description + */ + void addProtocol (void); + /* * 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); + void addOrigin (void); /* - * Mandatory field: Protocol version ("v=") - * Add the protocol version in the SDP session description + * Mandatory field: Session name ("s=") + * Add a textual session name. */ - void sdp_add_protocol (void); + void addSessionName (void); /* * Optional field: Connection data ("c=") * Contains connection data. */ - void sdp_add_connection_info (void); + void addConnectionInfo (void); /* - * Mandatory field: Session name ("s=") - * Add a textual session name. + * Mandatory field: Timing ("t=") + * Specify the start and the stop time for a session. */ - void sdp_add_session_name (void); + void addTiming (void); + /* * Optional field: Session information ("s=") * Provides textual information about the session. */ - void sdp_add_session_info (void) {} + void addSessionInfo (void) {} /* * Optional field: Uri ("u=") * Add a pointer to additional information about the session. */ - void sdp_add_uri (void) {} + void addUri (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) {} + void addEmail (void) {} /* * Optional field: Bandwidth ("b=") * Denotes the proposed bandwidth to be used by the session or the media . */ - void sdp_add_bandwidth (void) {} + void addBandwidth (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) {} + void addTimeZone (void) {} /* * Optional field: Encryption keys ("k=") */ - void sdp_add_encryption_key (void) {} + void addEncryptionKey (void) {} /* * Optional field: Attributes ("a=") */ - void sdp_add_attributes(); + void addAttributes(); /* * Mandatory field: Media descriptions ("m=") */ - void sdp_add_media_description(); - - std::string convert_int_to_string (int value); - - void set_remote_ip_from_sdp (const pjmedia_sdp_session *r_sdp); - - void set_remote_audio_port_from_sdp (pjmedia_sdp_media *r_media); - - void get_remote_sdp_media_from_offer (const pjmedia_sdp_session* r_sdp, pjmedia_sdp_media** r_media); - + void addMediaDescription(); /* * Adds a sdes attribute to the given media section. * * @param media The media to add the srtp attribute to */ - void sdp_add_sdes_attribute (std::vector<std::string>& crypto) throw (SdpException); + void addSdesAttribute (std::vector<std::string>& crypto) throw (SdpException); /* * Adds a zrtp-hash attribute to @@ -452,7 +500,19 @@ class Sdp * @param media The media to add the zrtp-hash attribute to * @param hash The hash to which the attribute should be set to */ - void sdp_add_zrtp_attribute (pjmedia_sdp_media* media, std::string hash) throw (SdpException); + void addZrtpAttribute (pjmedia_sdp_media* media, std::string hash) throw (SdpException); + + std::string convertIntToString (int value); + + void setRemoteIpFromSdp (const pjmedia_sdp_session *r_sdp); + + void setRemoteAudioPortFromSdp (pjmedia_sdp_media *r_media); + + void setMediaTransportInfoFromRemoteSdp (const pjmedia_sdp_session *remote_sdp); + + void getRemoteSdpMediaFromOffer (const pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media** r_media); + + }; diff --git a/sflphone-common/src/sip/sipvoiplink.cpp b/sflphone-common/src/sip/sipvoiplink.cpp index 3da10d680b5236ad7119ab207428d8d9a1098308..87a501d4b8f611429b7b23487c336a0ecaef18fe 100644 --- a/sflphone-common/src/sip/sipvoiplink.cpp +++ b/sflphone-common/src/sip/sipvoiplink.cpp @@ -185,24 +185,28 @@ void setVoicemailInfo (AccountID account, pjsip_msg_body *body); pj_bool_t stun_sock_on_status_cb (pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status); pj_bool_t stun_sock_on_rx_data_cb (pj_stun_sock *stun_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *src_addr, unsigned addr_len); - /* * Session callback - * Called when the invite session state has changed. + * Called after SDP offer/answer session has completed. * * @param inv A pointer on a pjsip_inv_session structure - * @param e A pointer on a pjsip_event structure + * @param status A pj_status_t structure */ -void invite_session_state_changed_cb (pjsip_inv_session *inv, pjsip_event *e); +void sdp_media_update_cb (pjsip_inv_session *inv, pj_status_t status UNUSED); + + +void sdp_request_offer_cb (pjsip_inv_session *inv, const pjmedia_sdp_session *offer); + +void sdp_create_offer_cb (pjsip_inv_session *inv, pjmedia_sdp_session **p_offer); /* * Session callback - * Called after SDP offer/answer session has completed. + * Called when the invite session state has changed. * * @param inv A pointer on a pjsip_inv_session structure - * @param status A pj_status_t structure + * @param e A pointer on a pjsip_event structure */ -void sdp_media_update_cb (pjsip_inv_session *inv, pj_status_t status UNUSED); +void invite_session_state_changed_cb (pjsip_inv_session *inv, pjsip_event *e); /* * Called when the invite usage module has created a new dialog and invite @@ -224,9 +228,6 @@ void outgoing_request_forked_cb (pjsip_inv_session *inv, pjsip_event *e); */ void transaction_state_changed_cb (pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e); -void invite_request_offer_cb (pjsip_inv_session *inv, const pjmedia_sdp_session *offer); - -void invite_create_offer_cb (pjsip_inv_session *inv, pjmedia_sdp_session **p_offer); /* * Registration callback @@ -706,7 +707,7 @@ Call *SIPVoIPLink::newOutgoingCall (const CallID& id, const std::string& toUrl) // Building the local SDP offer call->getLocalSDP()->setLocalIP (addrSdp); - status = call->getLocalSDP()->createInitialOffer (account->getActiveCodecs ()); + // status = call->getLocalSDP()->createOffer (account->getActiveCodecs ()); if (status != PJ_SUCCESS) { delete call; @@ -1042,7 +1043,7 @@ SIPVoIPLink::offhold (const CallID& id) } // Retreive previously selected codec - sfl::Codec *sessionMedia = call->getLocalSDP()->get_session_media(); + sfl::Codec *sessionMedia = call->getLocalSDP()->getSessionMedia(); if (!sessionMedia) return false; @@ -1334,7 +1335,7 @@ SIPVoIPLink::getCurrentCodecName() call = getSIPCall (Manager::instance().getCurrentCallId()); if (call) - ac = call->getLocalSDP()->get_session_media(); + ac = call->getLocalSDP()->getSessionMedia(); if (ac) name = ac->getMimeSubtype(); @@ -1759,7 +1760,7 @@ bool SIPVoIPLink::SIPNewIpToIpCall (const CallID& id, const std::string& to) // Building the local SDP offer call->getLocalSDP()->setLocalIP (addrSdp); - call->getLocalSDP()->createInitialOffer (account->getActiveCodecs ()); + //call->getLocalSDP()->createOffer (account->getActiveCodecs ()); // Init TLS transport if enabled if (account->isTlsEnabled()) { @@ -1979,8 +1980,8 @@ bool SIPVoIPLink::pjsipInit() inv_cb.on_new_session = &outgoing_request_forked_cb; inv_cb.on_media_update = &sdp_media_update_cb; inv_cb.on_tsx_state_changed = &transaction_state_changed_cb; - inv_cb.on_rx_offer = &invite_request_offer_cb; - inv_cb.on_create_offer = &invite_create_offer_cb; + inv_cb.on_rx_offer = &sdp_request_offer_cb; + inv_cb.on_create_offer = &sdp_create_offer_cb; // Initialize session invite module status = pjsip_inv_usage_init (_endpt, &inv_cb); @@ -3255,24 +3256,104 @@ void invite_session_state_changed_cb (pjsip_inv_session *inv, pjsip_event *e) } +void sdp_request_offer_cb (pjsip_inv_session *inv, const pjmedia_sdp_session *offer) +{ + _info ("UserAgent: Received SDP offer"); + + +#ifdef CAN_REINVITE + _debug ("UserAgent: %s (%d): on_rx_offer REINVITE", __FILE__, __LINE__); + + 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)); + + SIPAccount *account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (accId)); + + status = call->getLocalSDP()->recieveOffer (offer, account->getActiveCodecs ()); + call->getLocalSDP()->startNegociation(); + + status = pjsip_inv_set_sdp_answer (call->getInvSession(), call->getLocalSDP()->getLocalSdpSession()); + + if (link) { + link->SIPHandleReinvite (call); + } +#endif + +} + +void sdp_create_offer_cb (pjsip_inv_session *inv, pjmedia_sdp_session **p_offer) +{ + _info ("UserAgent: Create new SDP offer"); + + /* Retrieve the call information */ + SIPCall * call = NULL; + call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); + + CallID callid = call->getCallId(); + AccountID accountid = Manager::instance().getAccountFromCall (callid); + + SIPAccount *account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (accountid)); + + SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink (accountid)); + + // Set the local address + std::string localAddress = link->getInterfaceAddrFromName (account->getLocalInterface ()); + // Set SDP parameters - Set to local + std::string addrSdp = localAddress; + + _debug ("UserAgent: Local Address for IP2IP call: %s", localAddress.c_str()); + + // If local address bound to ANY, reslove it using PJSIP + if (localAddress == "0.0.0.0") { + link->loadSIPLocalIP (&localAddress); + } + + // Local address to appear in SDP + if (addrSdp == "0.0.0.0") { + addrSdp = localAddress; + } + + // Set local address for RTP media + setCallAudioLocal (call, localAddress); + + // Building the local SDP offer + call->getLocalSDP()->setLocalIP (addrSdp); + call->getLocalSDP()->createOffer (account->getActiveCodecs()); + + *p_offer = call->getLocalSDP()->getLocalSdpSession(); + +} + // This callback is called after SDP offer/answer session has completed. void sdp_media_update_cb (pjsip_inv_session *inv, pj_status_t status) { _debug ("UserAgent: Call media update"); const pjmedia_sdp_session *remote_sdp; - + const pjmedia_sdp_session *local_sdp; SIPVoIPLink * link = NULL; SIPCall * call; + char buffer[1000]; call = reinterpret_cast<SIPCall *> (inv->mod_data[getModId()]); - if (!call) { + if (call == NULL) { _debug ("UserAgent: Call declined by peer, SDP negociation stopped"); return; } link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink (AccountNULL)); - if (link == NULL) { _warn ("UserAgent: Error: Failed to get sip link"); return; @@ -3286,22 +3367,32 @@ void sdp_media_update_cb (pjsip_inv_session *inv, pj_status_t status) } if (!inv->neg) { + _warn ("UserAgent: Error: no negotiator for this session"); return; } - // Get sdp session from answer + // Retreive SDP session for this call + Sdp *sdpSession = call->getLocalSDP(); + + // Get active session sessions pjmedia_sdp_neg_get_active_remote (inv->neg, &remote_sdp); + pjmedia_sdp_neg_get_active_local (inv->neg, &local_sdp); - // Clean the resulting sdp offer to create a new one (in case of a reinvite) - call->getLocalSDP()->clean_session_media(); + // Print SDP session + memset(buffer, 0, 1000); + pjmedia_sdp_print(remote_sdp, buffer, 1000); + _debug("SDP: Remote active SDP Session: %s\n", buffer); - // Set the fresh negotiated one, no matter if that was an offer or answer. - // The local sdp is updated in case of an answer, even if the remote sdp - // is kept internally. - call->getLocalSDP()->set_negotiated_sdp (remote_sdp); + memset(buffer, 0, 1000); + pjmedia_sdp_print(local_sdp, buffer, 1000); + _debug("SDP: Local active SDP Session: %s\n", buffer); - // Set remote ip / port - call->getLocalSDP()->set_media_transport_info_from_remote_sdp (remote_sdp); + // Set active SDP sessions + sdpSession->setActiveRemoteSdpSession(remote_sdp); + sdpSession->setActiveLocalSdpSession(local_sdp); + + // Update internal field for + sdpSession->updateInternalState(); try { call->getAudioRtp()->updateDestinationIpAddress(); @@ -3311,7 +3402,7 @@ void sdp_media_update_cb (pjsip_inv_session *inv, pj_status_t status) // Get the crypto attribute containing srtp's cryptographic context (keys, cipher) CryptoOffer crypto_offer; - call->getLocalSDP()->get_remote_sdp_crypto_from_offer (remote_sdp, crypto_offer); + call->getLocalSDP()->getRemoteSdpCryptoFromOffer (remote_sdp, crypto_offer); bool nego_success = false; @@ -3364,14 +3455,10 @@ void sdp_media_update_cb (pjsip_inv_session *inv, pj_status_t status) call->getAudioRtp()->initAudioRtpSession (call); } - // Start audio rtp session. - - Sdp *sdpSession = call->getLocalSDP(); - if (!sdpSession) return; - AudioCodec *sessionMedia = sdpSession->get_session_media(); + AudioCodec *sessionMedia = sdpSession->getSessionMedia(); if (!sessionMedia) return; @@ -3891,7 +3978,7 @@ transaction_request_cb (pjsip_rx_data *rdata) } - status = call->getLocalSDP()->receivingInitialOffer (r_sdp, account->getActiveCodecs ()); + status = call->getLocalSDP()->recieveOffer (r_sdp, account->getActiveCodecs ()); sfl::Codec* audiocodec = Manager::instance().getCodecDescriptorMap().instantiateCodec (PAYLOAD_CODEC_ULAW); @@ -4428,83 +4515,6 @@ void transfer_server_cb (pjsip_evsub *sub, pjsip_event *event) } } -void invite_request_offer_cb (pjsip_inv_session *inv, const pjmedia_sdp_session *offer UNUSED) -{ - _info ("UserAgent: Received SDP offer"); - - -#ifdef CAN_REINVITE - _debug ("UserAgent: %s (%d): on_rx_offer REINVITE", __FILE__, __LINE__); - - 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, account->getActiveCodecs ()); - - status=pjsip_inv_set_sdp_answer (call->getInvSession(), call->getLocalSDP()->getLocalSdpSession()); - - if (link) - link->SIPHandleReinvite (call); - -#endif - -} - -void invite_create_offer_cb (pjsip_inv_session *inv, pjmedia_sdp_session **p_offer) -{ - _info ("UserAgent: Create new SDP offer"); - - /* Retrieve the call information */ - SIPCall * call = NULL; - call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); - - CallID callid = call->getCallId(); - AccountID accountid = Manager::instance().getAccountFromCall (callid); - - SIPAccount *account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (accountid)); - - SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink (accountid)); - - // Set the local address - std::string localAddress = link->getInterfaceAddrFromName (account->getLocalInterface ()); - // Set SDP parameters - Set to local - std::string addrSdp = localAddress; - - _debug ("UserAgent: Local Address for IP2IP call: %s", localAddress.c_str()); - - // If local address bound to ANY, reslove it using PJSIP - if (localAddress == "0.0.0.0") { - link->loadSIPLocalIP (&localAddress); - } - - // Local address to appear in SDP - if (addrSdp == "0.0.0.0") { - addrSdp = localAddress; - } - - // Set local address for RTP media - setCallAudioLocal (call, localAddress); - - // Building the local SDP offer - call->getLocalSDP()->setLocalIP (addrSdp); - call->getLocalSDP()->createInitialOffer (account->getActiveCodecs()); - - *p_offer = call->getLocalSDP()->getLocalSdpSession(); - -} - - void handleIncomingOptions (pjsip_rx_data *rdata) { @@ -4595,7 +4605,7 @@ bool setCallAudioLocal (SIPCall* call, std::string localIP) call->setLocalAudioPort (callLocalAudioPort); call->setLocalExternAudioPort (callLocalExternAudioPort); - call->getLocalSDP()->attribute_port_to_all_media (callLocalExternAudioPort); + call->getLocalSDP()->setPortToAllMedia (callLocalExternAudioPort); return true; } else { diff --git a/sflphone-common/test/sdptest.cpp b/sflphone-common/test/sdptest.cpp index a078e0ebb7ef1a7a77bc0423c986ddc5bbcf6da3..9b5740a27a362cc8f4b32dedc6d9fc921f740809 100644 --- a/sflphone-common/test/sdptest.cpp +++ b/sflphone-common/test/sdptest.cpp @@ -28,9 +28,76 @@ * as that of the covered work. */ +#include <iostream> +#include <string.h> + #include "sdptest.h" -#include <iostream> + +enum session_type +{ + REMOTE_OFFER, + LOCAL_OFFER, +}; + +static char *sdp_answer1 = "v=0\r\n" + "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n" + "s= \r\n" + "c=IN IP4 host.example.com\r\n" + "t=0 0\r\n" + "m=audio 49920 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "m=video 0 RTP/AVP 31\r\n" + "m=video 53002 RTP/AVP 32\r\n" + "a=rtpmap:32 MPV/90000\r\n"; + +static char *sdp_offer1 = "v=0\r\n" + "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n" + "s= \r\n" + "c=IN IP4 host.example.com\r\n" + "t=0 0\r\n" + "m=audio 49920 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "m=video 0 RTP/AVP 31\r\n" + "m=video 53002 RTP/AVP 32\r\n" + "a=rtpmap:32 MPV/90000\r\n"; + +static char *sdp_answer2 = "v=0\r\n" + "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n" + "s= \r\n" + "c=IN IP4 host.example.com\r\n" + "t=0 0\r\n" + "m=audio 49920 RTP/AVP 3 97 9\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=rtpmap:9 G722/8000\r\n" + "m=video 0 RTP/AVP 31\r\n" + "m=video 53002 RTP/AVP 32\r\n" + "a=rtpmap:32 MPV/90000\r\n"; + +static char *sdp_offer2 = "v=0\r\n" + "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n" + "s= \r\n" + "c=IN IP4 host.example.com\r\n" + "t=0 0\r\n" + "m=audio 49920 RTP/AVP 3 97 9\r\n" + "a=rtpmap:3 GSM/8000\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=rtpmap:9 G722/8000\r\n" + "m=video 0 RTP/AVP 31\r\n" + "m=video 53002 RTP/AVP 32\r\n" + "a=rtpmap:32 MPV/90000\r\n"; + +static char *sdp_reinvite = "v=0\r\n" + "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n" + "s= \r\n" + "c=IN IP4 host.exampleReinvite.com\r\n" + "t=0 0\r\n" + "m=audio 42445 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "m=video 0 RTP/AVP 31\r\n" + "m=video 53002 RTP/AVP 32\r\n" + "a=rtpmap:32 MPV/90000\r\n"; void SDPTest::setUp() @@ -47,19 +114,203 @@ void SDPTest::tearDown() delete _session; _session = NULL; - pj_pool_release(_testPool); + pj_pool_release(_testPool); +} + + +void SDPTest::testInitialOfferFirstCodec () +{ + std::cout << "------------ SDPTest::testInitialOfferFirstCodec --------------" << std::endl; + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 0); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 0); + CPPUNIT_ASSERT(_session->getLocalIP() == ""); + CPPUNIT_ASSERT(_session->getRemoteIP() == ""); + + CodecOrder codecSelection; + pjmedia_sdp_session *remoteAnswer; + + codecSelection.push_back(PAYLOAD_CODEC_ULAW); + codecSelection.push_back(PAYLOAD_CODEC_ALAW); + codecSelection.push_back(PAYLOAD_CODEC_G722); + + _session->setLocalIP("127.0.0.1"); + _session->setLocalPublishedAudioPort(49567); + + _session->createOffer(codecSelection); + + // pjmedia_sdp_parse(_testPool, test[0].offer_answer[0].sdp2, strlen(test[0].offer_answer[0].sdp2), &remoteAnswer); + pjmedia_sdp_parse(_testPool, sdp_answer1, strlen(sdp_answer1), &remoteAnswer); + + _session->receivingAnswerAfterInitialOffer(remoteAnswer); + + _session->startNegociation(); + + _session->updateInternalState(); + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 49567); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 49920); + CPPUNIT_ASSERT(_session->getLocalIP() == "127.0.0.1"); + CPPUNIT_ASSERT(_session->getRemoteIP() == "host.example.com"); + CPPUNIT_ASSERT(_session->getSessionMedia()->getMimeSubtype() == "PCMU"); + +} + +void SDPTest::testInitialAnswerFirstCodec () +{ + std::cout << "------------ SDPTest::testInitialAnswerFirstCodec -------------" << std::endl; + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 0); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 0); + CPPUNIT_ASSERT(_session->getLocalIP() == ""); + CPPUNIT_ASSERT(_session->getRemoteIP() == ""); + + CodecOrder codecSelection; + pjmedia_sdp_session *remoteOffer; + + codecSelection.push_back(PAYLOAD_CODEC_ULAW); + codecSelection.push_back(PAYLOAD_CODEC_ALAW); + codecSelection.push_back(PAYLOAD_CODEC_G722); + + pjmedia_sdp_parse(_testPool, sdp_offer1, strlen(sdp_offer1), &remoteOffer); + + _session->setLocalIP("127.0.0.1"); + _session->setLocalPublishedAudioPort(49567); + + _session->recieveOffer(remoteOffer, codecSelection); + + _session->startNegociation(); + + _session->updateInternalState(); + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 49567); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 49920); + CPPUNIT_ASSERT(_session->getLocalIP() == "127.0.0.1"); + CPPUNIT_ASSERT(_session->getRemoteIP() == "host.example.com"); + CPPUNIT_ASSERT(_session->getSessionMedia()->getMimeSubtype() == "PCMU"); + +} + +void SDPTest::testInitialOfferLastCodec () +{ + std::cout << "------------ SDPTest::testInitialOfferLastCodec --------------------" << std::endl; + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 0); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 0); + CPPUNIT_ASSERT(_session->getLocalIP() == ""); + CPPUNIT_ASSERT(_session->getRemoteIP() == ""); + + CodecOrder codecSelection; + pjmedia_sdp_session *remoteAnswer; + + codecSelection.push_back(PAYLOAD_CODEC_ULAW); + codecSelection.push_back(PAYLOAD_CODEC_ALAW); + codecSelection.push_back(PAYLOAD_CODEC_G722); + + _session->setLocalIP("127.0.0.1"); + _session->setLocalPublishedAudioPort(49567); + + _session->createOffer(codecSelection); + + // pjmedia_sdp_parse(_testPool, test[0].offer_answer[0].sdp2, strlen(test[0].offer_answer[0].sdp2), &remoteAnswer); + pjmedia_sdp_parse(_testPool, sdp_answer2, strlen(sdp_answer2), &remoteAnswer); + + _session->receivingAnswerAfterInitialOffer(remoteAnswer); + + _session->startNegociation(); + + _session->updateInternalState(); + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 49567); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 49920); + CPPUNIT_ASSERT(_session->getLocalIP() == "127.0.0.1"); + CPPUNIT_ASSERT(_session->getRemoteIP() == "host.example.com"); + CPPUNIT_ASSERT(_session->getSessionMedia()->getMimeSubtype() == "G722"); + } +void SDPTest::testInitialAnswerLastCodec () +{ + std::cout << "------------ SDPTest::testInitialAnswerLastCodec ------------" << std::endl; + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 0); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 0); + CPPUNIT_ASSERT(_session->getLocalIP() == ""); + CPPUNIT_ASSERT(_session->getRemoteIP() == ""); + + CodecOrder codecSelection; + pjmedia_sdp_session *remoteOffer; -void SDPTest::testInitialOffer () + codecSelection.push_back(PAYLOAD_CODEC_ULAW); + codecSelection.push_back(PAYLOAD_CODEC_ALAW); + codecSelection.push_back(PAYLOAD_CODEC_G722); + + pjmedia_sdp_parse(_testPool, sdp_offer2, strlen(sdp_offer2), &remoteOffer); + + _session->setLocalIP("127.0.0.1"); + _session->setLocalPublishedAudioPort(49567); + + _session->recieveOffer(remoteOffer, codecSelection); + + _session->startNegociation(); + + _session->updateInternalState(); + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 49567); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 49920); + CPPUNIT_ASSERT(_session->getLocalIP() == "127.0.0.1"); + CPPUNIT_ASSERT(_session->getRemoteIP() == "host.example.com"); + CPPUNIT_ASSERT(_session->getSessionMedia()->getMimeSubtype() == "G722"); + +} + + +void SDPTest::testReinvite () { - std::cout << "-------------------- SDPTest::testInitialOffer --------------------" << std::endl; + std::cout << "------------ SDPTest::testReinvite --------------------" << std::endl; + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 0); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 0); + CPPUNIT_ASSERT(_session->getLocalIP() == ""); + CPPUNIT_ASSERT(_session->getRemoteIP() == ""); CodecOrder codecSelection; + pjmedia_sdp_session *remoteAnswer; + pjmedia_sdp_session *reinviteOffer; codecSelection.push_back(PAYLOAD_CODEC_ULAW); codecSelection.push_back(PAYLOAD_CODEC_ALAW); codecSelection.push_back(PAYLOAD_CODEC_G722); - _session->createInitialOffer(codecSelection); + _session->setLocalIP("127.0.0.1"); + _session->setLocalPublishedAudioPort(49567); + + _session->createOffer(codecSelection); + + // pjmedia_sdp_parse(_testPool, test[0].offer_answer[0].sdp2, strlen(test[0].offer_answer[0].sdp2), &remoteAnswer); + pjmedia_sdp_parse(_testPool, sdp_answer1, strlen(sdp_answer1), &remoteAnswer); + + _session->receivingAnswerAfterInitialOffer(remoteAnswer); + + _session->startNegociation(); + + _session->updateInternalState(); + + CPPUNIT_ASSERT(_session->getLocalPublishedAudioPort() == 49567); + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 49920); + CPPUNIT_ASSERT(_session->getLocalIP() == "127.0.0.1"); + CPPUNIT_ASSERT(_session->getRemoteIP() == "host.example.com"); + CPPUNIT_ASSERT(_session->getSessionMedia()->getMimeSubtype() == "PCMU"); + + pjmedia_sdp_parse(_testPool, sdp_reinvite, strlen(sdp_reinvite), &reinviteOffer); + + _session->recieveOffer(reinviteOffer, codecSelection); + + _session->startNegociation(); + + _session->updateInternalState(); + + CPPUNIT_ASSERT(_session->getRemoteAudioPort() == 42445); + CPPUNIT_ASSERT(_session->getRemoteIP() == "host.exampleReinvite.com"); + } diff --git a/sflphone-common/test/sdptest.h b/sflphone-common/test/sdptest.h index 0b4888dc7b497d45f62f616d7272cdaad067f465..e810082403cf7f25bc2e4361be04cec6b0424b55 100644 --- a/sflphone-common/test/sdptest.h +++ b/sflphone-common/test/sdptest.h @@ -84,27 +84,39 @@ class SDPTest : public CppUnit::TestCase { * Use cppunit library macros to add unit test the factory */ CPPUNIT_TEST_SUITE( SDPTest ); - CPPUNIT_TEST ( testInitialOffer ); + CPPUNIT_TEST ( testInitialOfferLastCodec ); + CPPUNIT_TEST ( testInitialAnswerLastCodec ); + CPPUNIT_TEST ( testInitialOfferLastCodec ); + CPPUNIT_TEST ( testInitialAnswerLastCodec ); + CPPUNIT_TEST ( testReinvite ); CPPUNIT_TEST_SUITE_END(); - public: - SDPTest() : CppUnit::TestCase("SDP module Tests") {} +public: + SDPTest() : CppUnit::TestCase("SDP module Tests") {} - /* - * Code factoring - Common resources can be initialized here. - * This method is called by unitcpp before each test - */ - void setUp(); + /** + * Code factoring - Common resources can be initialized here. + * This method is called by unitcpp before each test + */ + void setUp(); - /* - * Code factoring - Common resources can be released here. - * This method is called by unitcpp after each test - */ - inline void tearDown (); + /** + * Code factoring - Common resources can be released here. + * This method is called by unitcpp after each test + */ + inline void tearDown (); - void testInitialOffer(); + void testInitialOfferFirstCodec(); - private: + void testInitialAnswerFirstCodec(); + + void testInitialOfferLastCodec(); + + void testInitialAnswerLastCodec(); + + void testReinvite (); + +private: Sdp *_session;