-
Tristan Matthews authored
Resolved Conflicts: sflphone-common/src/sip/sdp.cpp
Tristan Matthews authoredResolved Conflicts: sflphone-common/src/sip/sdp.cpp
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sdp.cpp 23.33 KiB
/*
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include "sdp.h"
#include "sdpmedia.h"
#include "global.h"
#include "manager.h"
static const char* const ZRTP_VERSION = "1.10";
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_RTP_SAVP = { (char*) "RTP/SAVP", 8 };
static const pj_str_t STR_SDP_NAME = { (char*) "sflphone", 8 };
static const pj_str_t STR_SENDRECV = { (char*) "sendrecv", 8 };
static const pj_str_t STR_RTPMAP = { (char*) "rtpmap", 6 };
static const pj_str_t STR_CRYPTO = { (char*) "crypto", 6 };
static const pj_str_t STR_TELEPHONE_EVENT = { (char*) "telephone-event", 15};
Sdp::Sdp (pj_pool_t *pool)
: memPool_(pool)
, negotiator_(NULL)
, localSession_(NULL)
, remoteSession_(NULL)
, activeLocalSession_(NULL)
, activeRemoteSession_(NULL)
, localAudioMediaCap_()
, sessionAudioMedia_(0)
, localIpAddr_("")
, remoteIpAddr_("")
, localAudioPort_(0)
, remoteAudioPort_(0)
, zrtpHelloHash_("")
, srtpCrypto_()
, telephoneEventPayload_(101) // same as asterisk
{
}
void Sdp::setActiveLocalSdpSession (const pjmedia_sdp_session *sdp)
{
int nb_media, nb_codecs;
int 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().getAudioCodecFactory().getCodecsMap();
// retrieve the media information
nb_media = activeLocalSession_->media_count;
for (int 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 (int j = 0; j < nb_codecs; j++) {
attribute = pjmedia_sdp_media_find_attr(current, &STR_RTPMAP, NULL);
// pj_strtoul(attribute->pt)
if (!attribute)
{
delete media;
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())
{
delete media;
return;
}
media->add_codec (iter->second);
}
sessionAudioMedia_.push_back (media);
}
}
void Sdp::setActiveRemoteSdpSession (const pjmedia_sdp_session *sdp)
{
_debug ("SDP: Set negotiated SDP");
activeRemoteSession_ = (pjmedia_sdp_session*) sdp;
getRemoteSdpTelephoneEventFromOffer(sdp);
}
bool Sdp::hasSessionMedia(void) const
{
return not sessionAudioMedia_.empty();
}
sfl::AudioCodec* Sdp::getSessionMedia (void)
{
int nbMedia;
int nbCodec;
sfl::Codec *codec = NULL;
std::vector<sdpMedia *> mediaList;
_debug ("SDP: Get session media");
nbMedia = sessionAudioMedia_.size();
if (nbMedia <= 0) {
_error("SDP: Error: No media in session description");
throw SdpException("No media description for this SDP");
}
nbCodec = sessionAudioMedia_[0]->get_media_codec_list().size();
if (nbCodec <= 0) {
_error("SDP: Error: No codec description for this media");
throw SdpException("No codec description for this media");
}
codec = sessionAudioMedia_[0]->get_media_codec_list()[0];
return static_cast<sfl::AudioCodec *>(codec);
}
namespace
{
std::string convertIntToString (int value)
{
std::ostringstream result;
result << value;
return result.str();
}
} // end anonymous namespace
void Sdp::setMediaDescriptorLine (sdpMedia *media, pjmedia_sdp_media** p_med)
{
pjmedia_sdp_media* med;
pjmedia_sdp_rtpmap rtpmap;
pjmedia_sdp_attr *attr;
sfl::Codec *codec;
int count, i;
std::string tmp;
med = PJ_POOL_ZALLOC_T (memPool_, pjmedia_sdp_media);
// Get the right media format
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 (srtpCrypto_.empty()) {
pj_strdup (memPool_, &med->desc.transport, &STR_RTP_AVP);
} else {
pj_strdup (memPool_, &med->desc.transport, &STR_RTP_SAVP);
}
// Media format ( RTP payload )
count = media->get_media_codec_list().size();
med->desc.fmt_count = count;
// add the payload list
for (i=0; i<count; i++) {
codec = media->get_media_codec_list() [i];
tmp = convertIntToString (codec->getPayloadType ());
_debug ("%s", 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
// 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->getMimeSubtype().c_str());
// G722 require G722/8000 media description even if it is 16000 codec
if (codec->getPayloadType () == 9) {
rtpmap.clock_rate = 8000;
} else {
rtpmap.clock_rate = codec->getClockRate();
}
rtpmap.param.slen = 0;
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 (memPool_, sizeof (pjmedia_sdp_attr));
pj_strdup2 (memPool_, &attr->name, media->get_stream_direction_str().c_str());
med->attr[ med->attr_count++] = attr;
if (!zrtpHelloHash_.empty()) {
addZrtpAttribute (med, zrtpHelloHash_);
}
setTelephoneEventRtpmap(med);
*p_med = med;
}
void Sdp::setTelephoneEventRtpmap(pjmedia_sdp_media *med)
{
pjmedia_sdp_attr *attr_rtpmap = NULL;
pjmedia_sdp_attr *attr_fmtp = NULL;
attr_rtpmap = static_cast<pjmedia_sdp_attr *>(pj_pool_zalloc(memPool_, sizeof(pjmedia_sdp_attr)));
attr_rtpmap->name = pj_str((char *) "rtpmap");
attr_rtpmap->value = pj_str((char *) "101 telephone-event/8000");
med->attr[med->attr_count++] = attr_rtpmap;
attr_fmtp = static_cast<pjmedia_sdp_attr *>(pj_pool_zalloc(memPool_, sizeof(pjmedia_sdp_attr)));
attr_fmtp->name = pj_str((char *) "fmtp");
attr_fmtp->value = pj_str((char *) "101 0-15");
med->attr[med->attr_count++] = attr_fmtp;
}
void Sdp::setLocalMediaCapabilities (CodecOrder selectedCodecs)
{
unsigned int i;
sdpMedia *audio;
CodecsMap codecs_list;
CodecsMap::iterator iter;
// 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().getAudioCodecFactory().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);
// Reference: RFC 4566 [5]
/* Create and initialize basic SDP session */
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 */
addProtocol();
addOrigin();
addSessionName();
addConnectionInfo();
addTiming();
addAudioMediaDescription();
if (!srtpCrypto_.empty()) {
addSdesAttribute (srtpCrypto_);
}
memset(buffer, 0, 1000);
pjmedia_sdp_print(localSession_, buffer, 1000);
_debug("SDP: Local SDP Session:\n%s", buffer);
// Validate the sdp session
return pjmedia_sdp_validate (localSession_);
}
int Sdp::createOffer (CodecOrder selectedCodecs)
{
pj_status_t status;
pjmedia_sdp_neg_state state;
_info ("SDP: Create initial offer");
// 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 negotiator_ instance with local offer
status = pjmedia_sdp_neg_create_w_local_offer (memPool_, localSession_, &negotiator_);
if (status != PJ_SUCCESS) {
_error ("SDP: Error: Failed to create an initial SDP negotiator");
return status;
}
state = pjmedia_sdp_neg_get_state (negotiator_);
PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1);
return PJ_SUCCESS;
}
int Sdp::receiveOffer (const pjmedia_sdp_session* remote, CodecOrder selectedCodecs)
{
char buffer[1000];
_debug ("SDP: Receiving initial offer");
pj_status_t status;
if (!remote) {
return !PJ_SUCCESS;
}
memset(buffer, 0, 1000);
pjmedia_sdp_print(remote, buffer, 1000);
_debug("SDP: Remote SDP Session:\n%s", 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;
}
}
remoteSession_ = pjmedia_sdp_session_clone (memPool_, remote);
status = pjmedia_sdp_neg_create_w_remote_offer (memPool_, localSession_,
remoteSession_, &negotiator_);
PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1);
return PJ_SUCCESS;
}
int Sdp::receivingAnswerAfterInitialOffer(const pjmedia_sdp_session* remote)
{
pj_status_t status;
if(pjmedia_sdp_neg_get_state(negotiator_) != PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) {
_warn("SDP: Session not in a valid state for receiving answer");
}
status = pjmedia_sdp_neg_set_remote_answer(memPool_, negotiator_, remote);
if(status != PJ_SUCCESS) {
_warn("SDP: Error: Could not set SDP remote answer");
}
if(pjmedia_sdp_neg_get_state(negotiator_) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
_warn("SDP: Session not in a valid state after receiving answer");
}
return status;
}
int Sdp::generateAnswerAfterInitialOffer(void)
{
pj_status_t status;
if(pjmedia_sdp_neg_get_state(negotiator_) != PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) {
_warn("SDP: Session not in a valid state for generating answer");
}
status = pjmedia_sdp_neg_set_local_answer (memPool_, negotiator_, localSession_);
if(status != PJ_SUCCESS) {
_warn("SDP: Error: could not set SDP local answer");
}
if(pjmedia_sdp_neg_get_state(negotiator_) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
_warn("SDP: Session not in a valid state after generating answer");
}
return status;
}
pj_status_t Sdp::startNegotiation()
{
pj_status_t status;
const pjmedia_sdp_session *active_local;
const pjmedia_sdp_session *active_remote;
_debug ("SDP: Start negotiation");
if(negotiator_ == NULL) {
_error("SDP: Error: negotiator is NULL in SDP session");
}
if(pjmedia_sdp_neg_get_state(negotiator_) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
_warn("SDP: Warning: negotiator not in wright state for negotiation");
}
status = pjmedia_sdp_neg_negotiate (memPool_, negotiator_, 0);
if(status != PJ_SUCCESS) {
return status;
}
status = pjmedia_sdp_neg_get_active_local(negotiator_, &active_local);
if(status != PJ_SUCCESS) {
_error("SDP: Could not retrieve local active session");
}
setActiveLocalSdpSession(active_local);
status = pjmedia_sdp_neg_get_active_remote(negotiator_, &active_remote);
if(status != PJ_SUCCESS) {
_error("SDP: Could not retrieve remote active session");
}
setActiveRemoteSdpSession(active_remote);
return status;
}
void Sdp::updateInternalState() {
// Populate internal field
setMediaTransportInfoFromRemoteSdp (activeRemoteSession_);
}
void Sdp::addProtocol ()
{
localSession_->origin.version = 0;
}
void Sdp::addOrigin ()
{
pj_time_val tv;
pj_gettimeofday (&tv);
localSession_->origin.user = pj_str (pj_gethostname()->ptr);
// Use Network Time Protocol format timestamp to ensure uniqueness.
localSession_->origin.id = tv.sec + 2208988800UL;
// The type of network ( IN for INternet )
localSession_->origin.net_type = STR_IN;
// The type of address
localSession_->origin.addr_type = STR_IP4;
// The address of the machine from which the session was created
localSession_->origin.addr = pj_str ( (char*) localIpAddr_.c_str());
}
void Sdp::addSessionName ()
{
localSession_->name = STR_SDP_NAME;
}
void Sdp::addConnectionInfo ()
{
localSession_->conn->net_type = localSession_->origin.net_type;
localSession_->conn->addr_type = localSession_->origin.addr_type;
localSession_->conn->addr = localSession_->origin.addr;
}
void Sdp::addTiming ()
{
// 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".
localSession_->time.start = localSession_->time.stop = 0;
}
void Sdp::addAttributes()
{
pjmedia_sdp_attr *a;
localSession_->attr_count = 1;
a = PJ_POOL_ZALLOC_T (memPool_, pjmedia_sdp_attr);
a->name=STR_SENDRECV;
localSession_->attr[0] = a;
}
void Sdp::addAudioMediaDescription()
{
pjmedia_sdp_media* med;
int nb_media, i;
med = PJ_POOL_ZALLOC_T (memPool_, pjmedia_sdp_media);
nb_media = localAudioMediaCap_.size();
// For DTMF RTP events
localSession_->media_count = nb_media;
for (i=0; i<nb_media; i++) {
setMediaDescriptorLine (localAudioMediaCap_[i], &med);
localSession_->media[i] = med;
}
}
void Sdp::addSdesAttribute (const std::vector<std::string>& crypto)
{
// temporary buffer used to store crypto attribute
char tempbuf[256];
std::vector<std::string>::const_iterator iter = crypto.begin();
while (iter != crypto.end()) {
// the attribute to add to sdp
pjmedia_sdp_attr *attribute = (pjmedia_sdp_attr*) pj_pool_zalloc (memPool_, sizeof (pjmedia_sdp_attr));
attribute->name = pj_strdup3 (memPool_, "crypto");
// _debug("crypto from sdp: %s", crypto.c_str());
int len = pj_ansi_snprintf (tempbuf, sizeof (tempbuf),
"%.*s", (int) (*iter).size(), (*iter).c_str());
attribute->value.slen = len;
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
int media_count = localSession_->media_count;
// add crypto attribute to media
for (int i = 0; i < media_count; i++) {
if (pjmedia_sdp_media_add_attr (localSession_->media[i], attribute) != PJ_SUCCESS) {
// if(pjmedia_sdp_attr_add(&(_local_offer->attr_count), _local_offer->attr, attribute) != PJ_SUCCESS){
throw SdpException ("Could not add sdes attribute to media");
}
}
++iter;
}
}
void Sdp::addZrtpAttribute (pjmedia_sdp_media* media, std::string hash)
{
pjmedia_sdp_attr *attribute;
char tempbuf[256];
int len;
attribute = (pjmedia_sdp_attr*) pj_pool_zalloc (memPool_, sizeof (pjmedia_sdp_attr));
attribute->name = pj_strdup3 (memPool_, "zrtp-hash");
/* Format: ":version value" */
len = pj_ansi_snprintf (tempbuf, sizeof (tempbuf),
"%.*s %.*s",
4,
ZRTP_VERSION,
(int) hash.size(),
hash.c_str());
attribute->value.slen = len;
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) {
throw SdpException ("Could not add zrtp attribute to media");
}
}
void Sdp::cleanSessionMedia()
{
_info ("SDP: Clean session media");
if (not sessionAudioMedia_.empty()) {
std::vector<sdpMedia *>::iterator iter = sessionAudioMedia_.begin();
sdpMedia *media;
while (iter != sessionAudioMedia_.end()) {
_debug ("delete media");
media = *iter;
delete media;
++iter;
}
sessionAudioMedia_.clear();
}
}
void Sdp::cleanLocalMediaCapabilities()
{
_info ("SDP: Clean local media capabilities");
if (not localAudioMediaCap_.empty()) {
std::vector<sdpMedia *>::iterator iter = localAudioMediaCap_.begin();
sdpMedia *media;
while (iter != localAudioMediaCap_.end()) {
media = *iter;
delete media;
++iter;
}
localAudioMediaCap_.clear();
}
}
void Sdp::setPortToAllMedia (int port)
{
setLocalPublishedAudioPort (port);
int size = localAudioMediaCap_.size();
for (int i = 0; i < size; i++)
localAudioMediaCap_[i]->set_port (port);
}
void Sdp::addAttributeToLocalAudioMedia(std::string attr)
{
pjmedia_sdp_attr *attribute;
attribute = pjmedia_sdp_attr_create (memPool_, attr.c_str(), NULL);
pjmedia_sdp_media_add_attr (localSession_->media[0], attribute);
}
void Sdp::removeAttributeFromLocalAudioMedia(std::string attr)
{
pjmedia_sdp_media_remove_all_attr (localSession_->media[0], attr.c_str());
}
void Sdp::setRemoteIpFromSdp (const pjmedia_sdp_session *r_sdp)
{
std::string remote_ip (r_sdp->conn->addr.ptr, r_sdp->conn->addr.slen);
_info ("SDP: Remote IP from fetching SDP: %s", remote_ip.c_str());
this->setRemoteIP (remote_ip);
}
void Sdp::setRemoteAudioPortFromSdp (pjmedia_sdp_media *r_media)
{
_info ("SDP: Remote Audio Port from fetching SDP: %d", r_media->desc.port);
this->setRemoteAudioPort (r_media->desc.port);
}
void Sdp::setMediaTransportInfoFromRemoteSdp (const pjmedia_sdp_session *remote_sdp)
{
pjmedia_sdp_media *r_media;
_info ("SDP: Fetching media from sdp");
if (!remote_sdp) {
_error("Sdp: Error: Remote sdp is NULL while parsing media");
return;
}
getRemoteSdpMediaFromOffer (remote_sdp, &r_media);
if (r_media==NULL) {
_warn ("SDP: Error: no remote sdp media found in the remote offer");
return;
}
setRemoteAudioPortFromSdp (r_media);
setRemoteIpFromSdp (remote_sdp);
}
void Sdp::getRemoteSdpMediaFromOffer (const pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media** r_media)
{
int count;
if (!remote_sdp)
return;
count = remote_sdp->media_count;
*r_media = NULL;
for (int i = 0; i < count; ++i) {
if (pj_stricmp2 (&remote_sdp->media[i]->desc.media, "audio") == 0) {
*r_media = remote_sdp->media[i];
return;
}
}
}
void Sdp::getRemoteSdpTelephoneEventFromOffer(const pjmedia_sdp_session *remote_sdp)
{
int media_count, attr_count;
pjmedia_sdp_media *r_media = NULL;
pjmedia_sdp_attr *attribute;
pjmedia_sdp_rtpmap *rtpmap;
if(!remote_sdp) {
_error("Sdp: Error: Remote sdp is NULL while parsing telephone event attribute");
return;
}
media_count = remote_sdp->media_count;
for(int i = 0; i < media_count; i++) {
if(pj_stricmp2(&remote_sdp->media[i]->desc.media, "audio") == 0) {
r_media = remote_sdp->media[i];
break;
}
}
if(r_media == NULL) {
_error("Sdp: Error: Could not found dtmf event gfrom remote sdp");
return;
}
attr_count = r_media->attr_count;
attribute = pjmedia_sdp_attr_find(attr_count, r_media->attr, &STR_TELEPHONE_EVENT, NULL);
if(attribute != NULL) {
pjmedia_sdp_attr_to_rtpmap (memPool_, attribute, &rtpmap);
telephoneEventPayload_ = pj_strtoul (&rtpmap->pt);
}
}
void Sdp::getRemoteSdpCryptoFromOffer (const pjmedia_sdp_session* remote_sdp, CryptoOffer& crypto_offer)
{
int i, j;
int attr_count, media_count;
pjmedia_sdp_attr *attribute;
pjmedia_sdp_media *media;
// get the number of media for this sdp session
media_count = remote_sdp->media_count;
CryptoOffer remoteOffer;
// iterate over all media
for (i = 0; i < media_count; ++i) {
// get media
media = remote_sdp->media[i];
// get number of attribute for this memdia
attr_count = media->attr_count;
// iterate over all attribute for this media
for (j = 0; j < attr_count; j++) {
attribute = media->attr[j];
// test if this attribute is a crypto
if (pj_stricmp2 (&attribute->name, "crypto") == 0) {
std::string attr (attribute->value.ptr, attribute->value.slen);
// @TODO our parser require the "a=crypto:" to be present
std::string full_attr = "a=crypto:";
full_attr += attr;
crypto_offer.push_back (full_attr);
}
}
}
}