Project 'savoirfairelinux/ring-client-uwp' was moved to 'savoirfairelinux/jami-client-uwp'. Please update any links and bookmarks that may still have the old path.
Select Git revision
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sdp.cpp 26.19 KiB
/*
* Copyright (C) 2004-2012 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sdp.h"
#include "logger.h"
#include "manager.h"
#include <algorithm>
#ifdef SFL_VIDEO
#include "video/libav_utils.h"
#endif
using std::string;
using std::map;
using std::vector;
using std::stringstream;
Sdp::Sdp(pj_pool_t *pool)
: memPool_(pool)
, negotiator_(NULL)
, localSession_(NULL)
, remoteSession_(NULL)
, activeLocalSession_(NULL)
, activeRemoteSession_(NULL)
, audio_codec_list_()
, video_codec_list_()
, sessionAudioMedia_()
, sessionVideoMedia_()
, localIpAddr_()
, remoteIpAddr_()
, localAudioDataPort_(0)
, localAudioControlPort_(0)
, localVideoDataPort_(0)
, localVideoControlPort_(0)
, remoteAudioPort_(0)
, remoteVideoPort_(0)
, zrtpHelloHash_()
, srtpCrypto_()
, telephoneEventPayload_(101) // same as asterisk
{}
namespace {
bool hasPayload(const std::vector<sfl::AudioCodec*> &codecs, int pt)
{
for (std::vector<sfl::AudioCodec*>::const_iterator i = codecs.begin(); i != codecs.end(); ++i)
if (*i and (*i)->getPayloadType() == pt)
return true;
return false;
}
bool hasCodec(const std::vector<std::string> &codecs, const std::string &codec)
{
return std::find(codecs.begin(), codecs.end(), codec) != codecs.end();
}
}
void Sdp::setActiveLocalSdpSession(const pjmedia_sdp_session *sdp)
{
activeLocalSession_ = (pjmedia_sdp_session*) sdp;
for (unsigned i = 0; i < activeLocalSession_->media_count; ++i) {
pjmedia_sdp_media *current = activeLocalSession_->media[i];
for (unsigned fmt = 0; fmt < current->desc.fmt_count; ++fmt) {
static const pj_str_t STR_RTPMAP = { (char*) "rtpmap", 6 };
pjmedia_sdp_attr *rtpMapAttribute = pjmedia_sdp_media_find_attr(current, &STR_RTPMAP, ¤t->desc.fmt[fmt]);
if (!rtpMapAttribute) {
ERROR("Could not find rtpmap attribute");
break;
}
pjmedia_sdp_rtpmap *rtpmap;
pjmedia_sdp_attr_to_rtpmap(memPool_, rtpMapAttribute, &rtpmap);
if (!pj_stricmp2(¤t->desc.media, "audio")) {
const unsigned long pt = pj_strtoul(¤t->desc.fmt[fmt]);
if (pt != telephoneEventPayload_ and not hasPayload(sessionAudioMedia_, pt)) {
sfl::AudioCodec *codec = Manager::instance().audioCodecFactory.getCodec(pt);
if (codec)
sessionAudioMedia_.push_back(codec);
else
ERROR("Could not get codec for payload type %lu", pt);
}
} else if (!pj_stricmp2(¤t->desc.media, "video")) {
const string codec(rtpmap->enc_name.ptr, rtpmap->enc_name.slen);
if (not hasCodec(sessionVideoMedia_, codec))
sessionVideoMedia_.push_back(codec);
}
}
}
}
void Sdp::setActiveRemoteSdpSession(const pjmedia_sdp_session *sdp)
{
if (!sdp) {
ERROR("Remote sdp is NULL");
return;
}
activeRemoteSession_ = (pjmedia_sdp_session*) sdp;
bool parsedTelelphoneEvent = false;
for (unsigned i = 0; i < sdp->media_count; i++) {
pjmedia_sdp_media *r_media = sdp->media[i];
if (!pj_stricmp2(&r_media->desc.media, "audio")) {
if (not parsedTelelphoneEvent) {
static const pj_str_t STR_TELEPHONE_EVENT = { (char*) "telephone-event", 15};
pjmedia_sdp_attr *telephoneEvent = pjmedia_sdp_attr_find(r_media->attr_count, r_media->attr, &STR_TELEPHONE_EVENT, NULL);
if (telephoneEvent != NULL) {
pjmedia_sdp_rtpmap *rtpmap;
pjmedia_sdp_attr_to_rtpmap(memPool_, telephoneEvent, &rtpmap);
telephoneEventPayload_ = pj_strtoul(&rtpmap->pt);
parsedTelelphoneEvent = true;
}
}
// add audio codecs from remote as needed
for (unsigned fmt = 0; fmt < r_media->desc.fmt_count; ++fmt) {
const unsigned long pt = pj_strtoul(&r_media->desc.fmt[fmt]);
if (pt != telephoneEventPayload_ and not hasPayload(sessionAudioMedia_, pt)) {
sfl::AudioCodec *codec = Manager::instance().audioCodecFactory.getCodec(pt);
if (codec) {
DEBUG("Adding codec with new payload type %d", pt);
sessionAudioMedia_.push_back(codec);
} else
DEBUG("Could not get codec for payload type %lu", pt);
}
}
}
}
DEBUG("Ready to decode %u audio codecs", sessionAudioMedia_.size());
}
string Sdp::getSessionVideoCodec() const
{
if (sessionVideoMedia_.empty()) {
DEBUG("Session video media is empty");
return "";
}
return sessionVideoMedia_[0];
}
string Sdp::getAudioCodecNames() const
{
std::string result;
char sep = ' ';
for (std::vector<sfl::AudioCodec*>::const_iterator i = sessionAudioMedia_.begin();
i != sessionAudioMedia_.end(); ++i) {
if (i == sessionAudioMedia_.end() - 1)
sep = '\0';
if (*i)
result += (*i)->getMimeSubtype() + sep;
}
return result;
}
void Sdp::getSessionAudioMedia(std::vector<sfl::AudioCodec*> &codecs) const
{
codecs = sessionAudioMedia_;
}
pjmedia_sdp_media *
Sdp::setMediaDescriptorLines(bool audio)
{
pjmedia_sdp_media *med = PJ_POOL_ZALLOC_T(memPool_, pjmedia_sdp_media);
med->desc.media = audio ? pj_str((char*) "audio") : pj_str((char*) "video");
med->desc.port_count = 1;
med->desc.port = audio ? localAudioDataPort_ : localVideoDataPort_;
// in case of sdes, media are tagged as "RTP/SAVP", RTP/AVP elsewhere
med->desc.transport = pj_str(srtpCrypto_.empty() ? (char*) "RTP/AVP" : (char*) "RTP/SAVP");
int dynamic_payload = 96;
med->desc.fmt_count = audio ? audio_codec_list_.size() : video_codec_list_.size();
for (unsigned i = 0; i < med->desc.fmt_count; ++i) {
unsigned clock_rate;
string enc_name;
int payload;
if (audio) {
sfl::AudioCodec *codec = audio_codec_list_[i];
payload = codec->getPayloadType();
enc_name = codec->getMimeSubtype();
clock_rate = codec->getClockRate();
// G722 require G722/8000 media description even if it is 16000 codec
if (codec->getPayloadType () == 9)
clock_rate = 8000;
} else {
// FIXME: get this key from header
enc_name = video_codec_list_[i]["name"];
clock_rate = 90000;
payload = dynamic_payload;
}
std::ostringstream s;
s << payload;
pj_strdup2(memPool_, &med->desc.fmt[i], s.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
pjmedia_sdp_rtpmap rtpmap;
rtpmap.pt = med->desc.fmt[i];
rtpmap.enc_name = pj_str((char*) enc_name.c_str());
rtpmap.clock_rate = clock_rate;
rtpmap.param.ptr = ((char* const)"");
rtpmap.param.slen = 0;
pjmedia_sdp_attr *attr;
pjmedia_sdp_rtpmap_to_attr(memPool_, &rtpmap, &attr);
med->attr[med->attr_count++] = attr;
#ifdef SFL_VIDEO
if (enc_name == "H264") {
std::ostringstream os;
// FIXME: this should not be hardcoded, it will determine what profile and level
// our peer will send us
std::string profileLevelID(video_codec_list_[i]["parameters"]);
if (profileLevelID.empty())
profileLevelID = libav_utils::MAX_H264_PROFILE_LEVEL_ID;
os << "fmtp:" << dynamic_payload << " " << profileLevelID;
med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_, os.str().c_str(), NULL);
}
#endif
if (not audio)
dynamic_payload++;
}
med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_, "sendrecv", NULL);
if (!zrtpHelloHash_.empty())
addZrtpAttribute(med, zrtpHelloHash_);
if (audio) {
setTelephoneEventRtpmap(med);
addRTCPAttribute(med); // video has its own RTCP
}
return med;
}
void Sdp::addRTCPAttribute(pjmedia_sdp_media *med)
{
std::ostringstream os;
os << localIpAddr_ << ":" << localAudioControlPort_;
const std::string str(os.str());
pj_str_t input_str = pj_str((char*) str.c_str());
pj_sockaddr outputAddr;
pj_status_t status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, &input_str, &outputAddr);
if (status != PJ_SUCCESS) {
ERROR("Could not parse address %s", str.c_str());
return;
}
pjmedia_sdp_attr *attr = pjmedia_sdp_attr_create_rtcp(memPool_, &outputAddr);
if (attr)
pjmedia_sdp_attr_add(&med->attr_count, med->attr, attr);
}
void
Sdp::updatePorts(const std::vector<pj_sockaddr_in> &sockets)
{
localAudioDataPort_ = pj_ntohs(sockets[0].sin_port);
localAudioControlPort_ = pj_ntohs(sockets[1].sin_port);
localVideoDataPort_ = pj_ntohs(sockets[2].sin_port);
localVideoControlPort_ = pj_ntohs(sockets[3].sin_port);
}
void Sdp::setTelephoneEventRtpmap(pjmedia_sdp_media *med)
{
std::ostringstream s;
s << telephoneEventPayload_;
++med->desc.fmt_count;
pj_strdup2(memPool_, &med->desc.fmt[med->desc.fmt_count - 1], s.str().c_str());
pjmedia_sdp_attr *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;
pjmedia_sdp_attr *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::setLocalMediaVideoCapabilities(const vector<map<string, string> > &codecs)
{
video_codec_list_.clear();
#ifdef SFL_VIDEO
if (codecs.empty())
WARN("No selected video codec while building local SDP offer");
else
video_codec_list_ = codecs;
#else
(void) codecs;
#endif
}
void Sdp::setLocalMediaAudioCapabilities(const vector<int> &selectedCodecs)
{
if (selectedCodecs.empty())
WARN("No selected codec while building local SDP offer");
audio_codec_list_.clear();
for (vector<int>::const_iterator i = selectedCodecs.begin(); i != selectedCodecs.end(); ++i) {
sfl::AudioCodec *codec = Manager::instance().audioCodecFactory.getCodec(*i);
if (codec)
audio_codec_list_.push_back(codec);
else
WARN("Couldn't find audio codec");
}
}
namespace {
void printSession(const pjmedia_sdp_session *session)
{
char buffer[2048];
size_t size = pjmedia_sdp_print(session, buffer, sizeof(buffer));
string sessionStr(buffer, std::min(size, sizeof(buffer)));
DEBUG("%s", sessionStr.c_str());
}
}
int Sdp::createLocalSession(const vector<int> &selectedAudioCodecs, const vector<map<string, string> > &selectedVideoCodecs)
{
setLocalMediaAudioCapabilities(selectedAudioCodecs);
setLocalMediaVideoCapabilities(selectedVideoCodecs);
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 */
localSession_->origin.version = 0;
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;
localSession_->origin.net_type = pj_str((char*) "IN");
localSession_->origin.addr_type = pj_str((char*) "IP4");
localSession_->origin.addr = pj_str((char*) localIpAddr_.c_str());
localSession_->name = pj_str((char*) PACKAGE);
localSession_->conn->net_type = localSession_->origin.net_type;
localSession_->conn->addr_type = localSession_->origin.addr_type;
localSession_->conn->addr = localSession_->origin.addr;
// 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 = 0;
localSession_->time.stop = 0;
// For DTMF RTP events
const bool audio = true;
localSession_->media_count = 1;
localSession_->media[0] = setMediaDescriptorLines(audio);
if (not selectedVideoCodecs.empty()) {
localSession_->media[1] = setMediaDescriptorLines(!audio);
++localSession_->media_count;
}
if (!srtpCrypto_.empty())
addSdesAttribute(srtpCrypto_);
DEBUG("SDP: Local SDP Session:");
printSession(localSession_);
return pjmedia_sdp_validate(localSession_);
}
bool
Sdp::createOffer(const vector<int> &selectedCodecs,
const vector<map<string, string> > &videoCodecs)
{
if (createLocalSession(selectedCodecs, videoCodecs) != PJ_SUCCESS) {
ERROR("Failed to create initial offer");
return false;
}
if (pjmedia_sdp_neg_create_w_local_offer(memPool_, localSession_, &negotiator_) != PJ_SUCCESS) {
ERROR("Failed to create an initial SDP negotiator");
return false;
}
return true;
}
void Sdp::receiveOffer(const pjmedia_sdp_session* remote,
const vector<int> &selectedCodecs,
const vector<map<string, string> > &videoCodecs)
{
if (!remote) {
ERROR("Remote session is NULL");
return;
}
DEBUG("Remote SDP Session:");
printSession(remote);
if (!localSession_ and createLocalSession(selectedCodecs, videoCodecs) != PJ_SUCCESS) {
ERROR("Failed to create initial offer");
return;
}
remoteSession_ = pjmedia_sdp_session_clone(memPool_, remote);
if (pjmedia_sdp_neg_create_w_remote_offer(memPool_, localSession_,
remoteSession_, &negotiator_) != PJ_SUCCESS)
ERROR("Failed to initialize negotiator");
}
void Sdp::startNegotiation()
{
if (negotiator_ == NULL) {
ERROR("Can't start negotiation with invalid negotiator");
return;
}
const pjmedia_sdp_session *active_local;
const pjmedia_sdp_session *active_remote;
if (pjmedia_sdp_neg_get_state(negotiator_) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)
WARN("Negotiator not in right state for negotiation");
if (pjmedia_sdp_neg_negotiate(memPool_, negotiator_, 0) != PJ_SUCCESS)
return;
if (pjmedia_sdp_neg_get_active_local(negotiator_, &active_local) != PJ_SUCCESS)
ERROR("Could not retrieve local active session");
else
setActiveLocalSdpSession(active_local);
if (pjmedia_sdp_neg_get_active_remote(negotiator_, &active_remote) != PJ_SUCCESS)
ERROR("Could not retrieve remote active session");
else
setActiveRemoteSdpSession(active_remote);
}
namespace
{
vector<string> split(const string &s, char delim)
{
vector<string> elems;
stringstream ss(s);
string item;
while(getline(ss, item, delim))
elems.push_back(item);
return elems;
}
} // end anonymous namespace
string Sdp::getLineFromSession(const pjmedia_sdp_session *sess, const string &keyword) const
{
char buffer[2048];
int size = pjmedia_sdp_print(sess, buffer, sizeof buffer);
string sdp(buffer, size);
const vector<string> tokens(split(sdp, '\n'));
for (vector<string>::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
if ((*iter).find(keyword) != string::npos)
return *iter;
return "";
}
// FIXME:
// Here we filter out parts of the SDP that libavformat doesn't need to
// know about...we should probably give the video decoder thread the original
// SDP and deal with the streams properly at that level
string Sdp::getIncomingVideoDescription() const
{
pjmedia_sdp_session *videoSession = pjmedia_sdp_session_clone(memPool_, activeLocalSession_);
if (!videoSession) {
ERROR("Could not clone SDP");
return "";
}
// deactivate non-video media
bool hasVideo = false;
for (unsigned i = 0; i < videoSession->media_count; i++)
if (pj_stricmp2(&videoSession->media[i]->desc.media, "video")) {
if (pjmedia_sdp_media_deactivate(memPool_, videoSession->media[i]) != PJ_SUCCESS)
ERROR("Could not deactivate media");
} else {
hasVideo = true;
}
if (not hasVideo) {
DEBUG("No video present in active local SDP");
return "";
}
char buffer[4096];
size_t size = pjmedia_sdp_print(videoSession, buffer, sizeof(buffer));
string sessionStr(buffer, std::min(size, sizeof(buffer)));
// FIXME: find a way to get rid of the "m=audio..." line with PJSIP
const size_t audioPos = sessionStr.find("m=audio");
const size_t newline2 = sessionStr.find('\n', audioPos);
const size_t newline1 = sessionStr.rfind('\n', audioPos);
sessionStr.erase(newline1, newline2 - newline1);
return sessionStr;
}
std::string Sdp::getOutgoingVideoCodec() const
{
string str("a=rtpmap:");
std::stringstream os;
os << getOutgoingVideoPayload();
str += os.str();
string vCodecLine(getLineFromSession(activeRemoteSession_, str));
char codec_buf[32];
codec_buf[0] = '\0';
sscanf(vCodecLine.c_str(), "a=rtpmap:%*d %31[^/]", codec_buf);
return string(codec_buf);
}
namespace {
vector<map<string, string> >::const_iterator
findCodecInList(const vector<map<string, string> > &codecs, const string &codec)
{
for (vector<map<string, string> >::const_iterator i = codecs.begin(); i != codecs.end(); ++i) {
map<string, string>::const_iterator name = i->find("name");
if (name != i->end() and (codec == name->second))
return i;
}
return codecs.end();
}
}
std::string
Sdp::getOutgoingVideoField(const std::string &codec, const char *key) const
{
const vector<map<string, string> >::const_iterator i = findCodecInList(video_codec_list_, codec);
if (i != video_codec_list_.end()) {
map<string, string>::const_iterator field = i->find(key);
if (field != i->end())
return field->second;
}
return "";
}
int
Sdp::getOutgoingVideoPayload() const
{
string videoLine(getLineFromSession(activeRemoteSession_, "m=video"));
int payload_num;
if (sscanf(videoLine.c_str(), "m=video %*d %*s %d", &payload_num) != 1)
payload_num = 0;
return payload_num;
}
void
Sdp::getProfileLevelID(const pjmedia_sdp_session *session,
std::string &profile, int payload) const
{
std::ostringstream os;
os << "a=fmtp:" << payload;
string fmtpLine(getLineFromSession(session, os.str()));
const std::string needle("profile-level-id=");
const size_t DIGITS_IN_PROFILE_LEVEL_ID = 6;
const size_t needleLength = needle.size() + DIGITS_IN_PROFILE_LEVEL_ID;
const size_t pos = fmtpLine.find(needle);
if (pos != std::string::npos and fmtpLine.size() >= (pos + needleLength)) {
profile = fmtpLine.substr(pos, needleLength);
DEBUG("Using %s", profile.c_str());
}
}
void Sdp::addSdesAttribute(const vector<std::string>& crypto)
{
for (vector<std::string>::const_iterator iter = crypto.begin();
iter != crypto.end(); ++iter) {
pj_str_t val = { (char*)(*iter).c_str(), static_cast<pj_ssize_t>((*iter).size()) };
pjmedia_sdp_attr *attr = pjmedia_sdp_attr_create(memPool_, "crypto", &val);
for (unsigned i = 0; i < localSession_->media_count; i++)
if (pjmedia_sdp_media_add_attr(localSession_->media[i], attr) != PJ_SUCCESS)
throw SdpException("Could not add sdes attribute to media");
}
}
void Sdp::addZrtpAttribute(pjmedia_sdp_media* media, std::string hash)
{
/* Format: ":version value" */
std::string val = "1.10 " + hash;
pj_str_t value = { (char*)val.c_str(), static_cast<pj_ssize_t>(val.size()) };
pjmedia_sdp_attr *attr = pjmedia_sdp_attr_create(memPool_, "zrtp-hash", &value);
if (pjmedia_sdp_media_add_attr(media, attr) != PJ_SUCCESS)
throw SdpException("Could not add zrtp attribute to media");
}
namespace {
// Returns index of desired media attribute, or -1 if not found */
int getIndexOfAttribute(const pjmedia_sdp_session * const session, const char * const type)
{
if (!session) {
ERROR("Session is NULL when looking for \"%s\" attribute", type);
return -1;
}
size_t i = 0;
while (i < session->media_count and pj_stricmp2(&session->media[i]->desc.media, type) != 0)
++i;
if (i == session->media_count)
return -1;
else
return i;
}
}
void Sdp::addAttributeToLocalAudioMedia(const char *attr)
{
const int i = getIndexOfAttribute(localSession_, "audio");
if (i == -1)
return;
pjmedia_sdp_attr *attribute = pjmedia_sdp_attr_create(memPool_, attr, NULL);
pjmedia_sdp_media_add_attr(localSession_->media[i], attribute);
}
void Sdp::removeAttributeFromLocalAudioMedia(const char *attr)
{
const int i = getIndexOfAttribute(localSession_, "audio");
if (i == -1)
return;
pjmedia_sdp_media_remove_all_attr(localSession_->media[i], attr);
}
void Sdp::removeAttributeFromLocalVideoMedia(const char *attr)
{
const int i = getIndexOfAttribute(localSession_, "video");
if (i == -1)
return;
pjmedia_sdp_media_remove_all_attr(localSession_->media[i], attr);
}
void Sdp::addAttributeToLocalVideoMedia(const char *attr)
{
const int i = getIndexOfAttribute(localSession_, "video");
if (i == -1)
return;
pjmedia_sdp_attr *attribute = pjmedia_sdp_attr_create(memPool_, attr, NULL);
pjmedia_sdp_media_add_attr(localSession_->media[i], attribute);
}
void
Sdp::updateRemoteIP(unsigned index)
{
// Remote connection information may be in the SDP Session or in the media
// for that session
pjmedia_sdp_conn *conn = activeRemoteSession_->conn ? activeRemoteSession_->conn :
activeRemoteSession_->media[index]->conn ? activeRemoteSession_->media[index]->conn :
NULL;
if (conn)
remoteIpAddr_ = std::string(conn->addr.ptr, conn->addr.slen);
else
ERROR("Could not get remote IP from SDP or SDP Media");
}
void Sdp::setMediaTransportInfoFromRemoteSdp()
{
if (!activeRemoteSession_) {
ERROR("Remote sdp is NULL while parsing media");
return;
}
for (unsigned i = 0; i < activeRemoteSession_->media_count; ++i) {
if (pj_stricmp2(&activeRemoteSession_->media[i]->desc.media, "audio") == 0) {
remoteAudioPort_ = activeRemoteSession_->media[i]->desc.port;
updateRemoteIP(i);
} else if (pj_stricmp2(&activeRemoteSession_->media[i]->desc.media, "video") == 0) {
remoteVideoPort_ = activeRemoteSession_->media[i]->desc.port;
updateRemoteIP(i);
}
}
}
void Sdp::getRemoteSdpCryptoFromOffer(const pjmedia_sdp_session* remote_sdp, CryptoOffer& crypto_offer)
{
for (unsigned i = 0; i < remote_sdp->media_count; ++i) {
pjmedia_sdp_media *media = remote_sdp->media[i];
for (unsigned j = 0; j < media->attr_count; j++) {
pjmedia_sdp_attr *attribute = media->attr[j];
// @TODO our parser require the "a=crypto:" to be present
if (pj_stricmp2(&attribute->name, "crypto") == 0)
crypto_offer.push_back("a=crypto:" + std::string(attribute->value.ptr, attribute->value.slen));
}
}
}
bool Sdp::getOutgoingVideoSettings(map<string, string> &args) const
{
#ifdef SFL_VIDEO
string codec(getOutgoingVideoCodec());
if (not codec.empty()) {
const string encoder(libav_utils::encodersMap()[codec]);
if (encoder.empty()) {
DEBUG("Couldn't find encoder for \"%s\"\n", codec.c_str());
return false;
} else {
args["codec"] = encoder;
args["bitrate"] = getOutgoingVideoField(codec, "bitrate");
const int payload = getOutgoingVideoPayload();
std::ostringstream os;
os << payload;
args["payload_type"] = os.str();
// override with profile-level-id from remote, if present
getProfileLevelID(activeRemoteSession_, args["parameters"], payload);
}
return true;
}
#else
(void) args;
#endif
return false;
}