Added separate library for interpreting lines of a SDP.
git-svn-id: http://yate.null.ro/svn/yate/trunk@2804 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
c41229b2a4
commit
3d1283260e
|
@ -1023,6 +1023,7 @@ AC_CONFIG_FILES([packing/rpm/yate.spec
|
|||
libs/ilbc/Makefile
|
||||
libs/ysip/Makefile
|
||||
libs/yrtp/Makefile
|
||||
libs/ysdp/Makefile
|
||||
libs/yiax/Makefile
|
||||
libs/yxml/Makefile
|
||||
libs/yjingle/Makefile
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
Makefile
|
||||
YateLocal*
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,56 @@
|
|||
# Makefile
|
||||
# This file holds the make rules for the libyatesip
|
||||
|
||||
DEBUG :=
|
||||
|
||||
CXX := @CXX@ -Wall
|
||||
AR := ar
|
||||
DEFS :=
|
||||
INCLUDES := -I@top_srcdir@ -I../.. -I@srcdir@
|
||||
CFLAGS := @CFLAGS@ @MODULE_CPPFLAGS@ @INLINE_FLAGS@
|
||||
LDFLAGS:= @LDFLAGS@ -L../.. -lyate
|
||||
INCFILES := @top_srcdir@/yatemime.h @top_srcdir@/yatephone.h @srcdir@/yatesdp.h
|
||||
|
||||
PROGS=
|
||||
LIBS = libyatesdp.a
|
||||
OBJS = media.o session.o parser.o
|
||||
|
||||
LOCALFLAGS =
|
||||
LOCALLIBS =
|
||||
COMPILE = $(CXX) $(DEFS) $(DEBUG) $(INCLUDES) $(CFLAGS)
|
||||
LINK = $(CXX) $(LDFLAGS)
|
||||
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
|
||||
# include optional local make rules
|
||||
-include YateLocal.mak
|
||||
|
||||
.PHONY: all debug ddebug xdebug
|
||||
all: $(LIBS) $(PROGS)
|
||||
|
||||
debug:
|
||||
$(MAKE) all DEBUG=-g3 MODSTRIP=
|
||||
|
||||
ddebug:
|
||||
$(MAKE) all DEBUG='-g3 -DDEBUG' MODSTRIP=
|
||||
|
||||
xdebug:
|
||||
$(MAKE) all DEBUG='-g3 -DXDEBUG' MODSTRIP=
|
||||
|
||||
.PHONY: strip
|
||||
strip: all
|
||||
strip --strip-debug --discard-locals $(PROGS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@-$(RM) $(PROGS) $(LIBS) $(OBJS) core 2>/dev/null
|
||||
|
||||
%.o: @srcdir@/%.cpp $(INCFILES)
|
||||
$(COMPILE) -c $<
|
||||
|
||||
Makefile: @srcdir@/Makefile.in ../../config.status
|
||||
cd ../.. && ./config.status
|
||||
|
||||
libyatesdp.a: $(OBJS)
|
||||
$(AR) rcs $@ $^
|
|
@ -0,0 +1,189 @@
|
|||
/**
|
||||
* media.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* SDP media handling
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2009 Null Team
|
||||
*
|
||||
* 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 2 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <yatesdp.h>
|
||||
|
||||
namespace TelEngine {
|
||||
|
||||
/*
|
||||
* SDPMedia
|
||||
*/
|
||||
SDPMedia::SDPMedia(const char* media, const char* transport, const char* formats,
|
||||
int rport, int lport)
|
||||
: NamedList(media),
|
||||
m_audio(true), m_modified(false), m_securable(true), m_localChanged(false),
|
||||
m_transport(transport), m_formats(formats),
|
||||
m_rfc2833(String::boolText(false))
|
||||
{
|
||||
DDebug(DebugAll,"SDPMedia::SDPMedia('%s','%s','%s',%d,%d) [%p]",
|
||||
media,transport,formats,rport,lport,this);
|
||||
if (String::operator!=("audio")) {
|
||||
m_audio = false;
|
||||
m_suffix << "_" << media;
|
||||
}
|
||||
int q = m_formats.find(',');
|
||||
m_format = m_formats.substr(0,q);
|
||||
if (rport >= 0)
|
||||
m_rPort = rport;
|
||||
if (lport >= 0)
|
||||
m_lPort = lport;
|
||||
}
|
||||
|
||||
SDPMedia::~SDPMedia()
|
||||
{
|
||||
DDebug(DebugAll,"SDPMedia::~SDPMedia() '%s' [%p]",c_str(),this);
|
||||
}
|
||||
|
||||
const char* SDPMedia::fmtList() const
|
||||
{
|
||||
if (m_formats)
|
||||
return m_formats.c_str();
|
||||
if (m_format)
|
||||
return m_format.c_str();
|
||||
// unspecified audio assumed to support G711
|
||||
if (m_audio)
|
||||
return "alaw,mulaw";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Update members with data taken from a SDP, return true if something changed
|
||||
bool SDPMedia::update(const char* formats, int rport, int lport)
|
||||
{
|
||||
DDebug(DebugAll,"SDPMedia::update('%s',%d,%d) [%p]",formats,rport,lport,this);
|
||||
bool chg = false;
|
||||
String tmp(formats);
|
||||
if (m_formats != tmp) {
|
||||
if ((tmp.find(',') < 0) && m_formats && m_formats.find(tmp) < 0)
|
||||
Debug(DebugInfo,"Not changing to '%s' from '%s' [%p]",
|
||||
formats,m_formats.c_str(),this);
|
||||
else {
|
||||
chg = true;
|
||||
m_formats = tmp;
|
||||
int q = m_formats.find(',');
|
||||
m_format = m_formats.substr(0,q);
|
||||
}
|
||||
}
|
||||
if (rport >= 0) {
|
||||
tmp = rport;
|
||||
if (m_rPort != tmp) {
|
||||
chg = true;
|
||||
m_rPort = tmp;
|
||||
}
|
||||
}
|
||||
if (lport >= 0) {
|
||||
tmp = lport;
|
||||
if (m_lPort != tmp) {
|
||||
m_localChanged = true;
|
||||
chg = true;
|
||||
m_lPort = tmp;
|
||||
}
|
||||
}
|
||||
return chg;
|
||||
}
|
||||
|
||||
// Update members from a dispatched "chan.rtp" message
|
||||
void SDPMedia::update(const NamedList& msg, bool pickFormat)
|
||||
{
|
||||
DDebug(DebugAll,"SDPMedia::update('%s',%s) [%p]",
|
||||
msg.c_str(),String::boolText(pickFormat),this);
|
||||
m_id = msg.getValue("rtpid",m_id);
|
||||
m_lPort = msg.getValue("localport",m_lPort);
|
||||
if (pickFormat) {
|
||||
const char* format = msg.getValue("format");
|
||||
if (format) {
|
||||
m_format = format;
|
||||
if ((m_formats != m_format) && (msg.getIntValue("remoteport") > 0)) {
|
||||
Debug(DebugNote,"Choosing started '%s' format '%s' [%p]",
|
||||
c_str(),format,this);
|
||||
m_formats = m_format;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add or replace a parameter by name and value, set the modified flag
|
||||
void SDPMedia::parameter(const char* name, const char* value, bool append)
|
||||
{
|
||||
if (!name)
|
||||
return;
|
||||
m_modified = true;
|
||||
if (append)
|
||||
addParam(name,value);
|
||||
else
|
||||
setParam(name,value);
|
||||
}
|
||||
|
||||
// Add or replace a parameter, set the modified flag
|
||||
void SDPMedia::parameter(NamedString* param, bool append)
|
||||
{
|
||||
if (!param)
|
||||
return;
|
||||
m_modified = true;
|
||||
if (append)
|
||||
addParam(param);
|
||||
else
|
||||
setParam(param);
|
||||
}
|
||||
|
||||
void SDPMedia::crypto(const char* desc, bool remote)
|
||||
{
|
||||
String& sdes = remote ? m_rCrypto : m_lCrypto;
|
||||
if (sdes != desc) {
|
||||
sdes = desc;
|
||||
m_modified = true;
|
||||
}
|
||||
if (remote && !desc)
|
||||
m_securable = false;
|
||||
}
|
||||
|
||||
// Put the list of net media in a parameter list
|
||||
void SDPMedia::putMedia(NamedList& msg, bool putPort)
|
||||
{
|
||||
msg.addParam("media" + suffix(),"yes");
|
||||
msg.addParam("formats" + suffix(),formats());
|
||||
msg.addParam("transport" + suffix(),transport());
|
||||
if (mappings())
|
||||
msg.addParam("rtp_mapping" + suffix(),mappings());
|
||||
if (isAudio())
|
||||
msg.addParam("rtp_rfc2833",rfc2833());
|
||||
if (putPort)
|
||||
msg.addParam("rtp_port" + suffix(),remotePort());
|
||||
if (remoteCrypto())
|
||||
msg.addParam("crypto" + suffix(),remoteCrypto());
|
||||
// must handle encryption differently
|
||||
const char* enc = getValue("encryption");
|
||||
if (enc)
|
||||
msg.addParam("encryption" + suffix(),enc);
|
||||
clearParam("encryption");
|
||||
unsigned int n = length();
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
const NamedString* param = getParam(i);
|
||||
if (param)
|
||||
msg.addParam("sdp" + suffix() + "_" + param->name(),*param);
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace TelEngine
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,331 @@
|
|||
/**
|
||||
* parser.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* SDP media handling
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2009 Null Team
|
||||
*
|
||||
* 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 2 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <yatesdp.h>
|
||||
|
||||
namespace TelEngine {
|
||||
|
||||
/*
|
||||
* SDPParser
|
||||
*/
|
||||
// Yate Payloads for the AV profile
|
||||
const TokenDict SDPParser::s_payloads[] = {
|
||||
{ "mulaw", 0 },
|
||||
{ "alaw", 8 },
|
||||
{ "gsm", 3 },
|
||||
{ "lpc10", 7 },
|
||||
{ "slin", 11 },
|
||||
{ "g726", 2 },
|
||||
{ "g722", 9 },
|
||||
{ "g723", 4 },
|
||||
{ "g728", 15 },
|
||||
{ "g729", 18 },
|
||||
{ "ilbc", 98 },
|
||||
{ "ilbc20", 98 },
|
||||
{ "ilbc30", 98 },
|
||||
{ "amr", 96 },
|
||||
{ "amr-o", 96 },
|
||||
{ "amr/16000", 99 },
|
||||
{ "amr-o/16000", 99 },
|
||||
{ "speex", 102 },
|
||||
{ "speex/16000", 103 },
|
||||
{ "speex/32000", 104 },
|
||||
{ "h261", 31 },
|
||||
{ "h263", 34 },
|
||||
{ "mpv", 32 },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
// SDP Payloads for the AV profile
|
||||
const TokenDict SDPParser::s_rtpmap[] = {
|
||||
{ "PCMU/8000", 0 },
|
||||
{ "PCMA/8000", 8 },
|
||||
{ "GSM/8000", 3 },
|
||||
{ "LPC/8000", 7 },
|
||||
{ "L16/8000", 11 },
|
||||
{ "G726-32/8000", 2 },
|
||||
{ "G722/8000", 9 },
|
||||
{ "G723/8000", 4 },
|
||||
{ "G728/8000", 15 },
|
||||
{ "G729/8000", 18 },
|
||||
{ "G729A/8000", 18 },
|
||||
{ "ILBC/8000", 98 },
|
||||
{ "AMR/8000", 96 },
|
||||
{ "AMR-WB/16000", 99 },
|
||||
{ "SPEEX/8000", 102 },
|
||||
{ "SPEEX/16000", 103 },
|
||||
{ "SPEEX/32000", 104 },
|
||||
{ "H261/90000", 31 },
|
||||
{ "H263/90000", 34 },
|
||||
{ "MPV/90000", 32 },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
// Parse a received SDP body
|
||||
ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedia,
|
||||
const String& media)
|
||||
{
|
||||
DDebug(DebugAll,"SDPParser::parse(%p,%s,%p,'%s')",&sdp,addr.c_str(),oldMedia,media.safe());
|
||||
const NamedString* c = sdp.getLine("c");
|
||||
if (c) {
|
||||
String tmp(*c);
|
||||
if (tmp.startSkip("IN IP4")) {
|
||||
tmp.trimBlanks();
|
||||
// Handle the case media is muted
|
||||
if (tmp == "0.0.0.0")
|
||||
tmp.clear();
|
||||
addr = tmp;
|
||||
}
|
||||
}
|
||||
Lock lock(this);
|
||||
ObjList* lst = 0;
|
||||
bool defcodecs = m_codecs.getBoolValue("default",true);
|
||||
c = sdp.getLine("m");
|
||||
for (; c; c = sdp.getNextLine(c)) {
|
||||
String tmp(*c);
|
||||
int sep = tmp.find(' ');
|
||||
if (sep < 1)
|
||||
continue;
|
||||
String type = tmp.substr(0,sep);
|
||||
tmp >> " ";
|
||||
if (media && (type != media))
|
||||
continue;
|
||||
int port = 0;
|
||||
tmp >> port >> " ";
|
||||
sep = tmp.find(' ');
|
||||
if (sep < 1)
|
||||
continue;
|
||||
bool rtp = true;
|
||||
String trans(tmp,sep);
|
||||
tmp = tmp.c_str() + sep;
|
||||
if ((trans &= "RTP/AVP") || (trans &= "RTP/SAVP") ||
|
||||
(trans &= "RTP/AVPF") || (trans &= "RTP/SAVPF"))
|
||||
trans.toUpper();
|
||||
else if ((trans &= "udptl") || (trans &= "tcp")) {
|
||||
trans.toLower();
|
||||
rtp = false;
|
||||
}
|
||||
else {
|
||||
Debug(this,DebugWarn,"Unknown SDP transport '%s' for media '%s'",
|
||||
trans.c_str(),type.c_str());
|
||||
continue;
|
||||
}
|
||||
String fmt;
|
||||
String aux;
|
||||
String mappings;
|
||||
String crypto;
|
||||
ObjList params;
|
||||
bool first = true;
|
||||
int ptime = 0;
|
||||
int rfc2833 = -1;
|
||||
while (tmp[0] == ' ') {
|
||||
int var = -1;
|
||||
tmp >> " " >> var;
|
||||
if (var < 0) {
|
||||
if (rtp || fmt || aux || tmp.null())
|
||||
continue;
|
||||
// brutal but effective
|
||||
for (char* p = const_cast<char*>(tmp.c_str()); *p; p++) {
|
||||
if (*p == ' ')
|
||||
*p = ',';
|
||||
}
|
||||
Debug(this,DebugInfo,"Assuming format list '%s' for media '%s'",
|
||||
tmp.c_str(),type.c_str());
|
||||
fmt = tmp;
|
||||
tmp.clear();
|
||||
}
|
||||
int mode = 0;
|
||||
bool annexB = m_codecs.getBoolValue("g729_annexb",false);
|
||||
bool amrOctet = m_codecs.getBoolValue("amr_octet",false);
|
||||
int defmap = -1;
|
||||
String payload(lookup(var,s_payloads));
|
||||
|
||||
const ObjList* l = sdp.lines().find(c);
|
||||
while (l && (l = l->skipNext())) {
|
||||
const NamedString* s = static_cast<NamedString*>(l->get());
|
||||
if (s->name() == "m")
|
||||
break;
|
||||
if (s->name() != "a")
|
||||
continue;
|
||||
String line(*s);
|
||||
if (line.startSkip("ptime:",false))
|
||||
line >> ptime;
|
||||
else if (line.startSkip("rtpmap:",false)) {
|
||||
int num = var - 1;
|
||||
line >> num >> " ";
|
||||
if (num == var) {
|
||||
line.trimBlanks().toUpper();
|
||||
if (line.startsWith("G729B/")) {
|
||||
// some devices add a second map for same payload
|
||||
annexB = true;
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith("TELEPHONE-EVENT/")) {
|
||||
rfc2833 = var;
|
||||
continue;
|
||||
}
|
||||
const char* pload = 0;
|
||||
for (const TokenDict* map = s_rtpmap; map->token; map++) {
|
||||
if (line.startsWith(map->token,false,true)) {
|
||||
defmap = map->value;
|
||||
pload = lookup(defmap,s_payloads);
|
||||
break;
|
||||
}
|
||||
}
|
||||
payload = pload;
|
||||
}
|
||||
}
|
||||
else if (line.startSkip("fmtp:",false)) {
|
||||
int num = var - 1;
|
||||
line >> num >> " ";
|
||||
if (num == var) {
|
||||
if (line.startSkip("mode=",false))
|
||||
line >> mode;
|
||||
else if (line.startSkip("annexb=",false))
|
||||
line >> annexB;
|
||||
else if (line.startSkip("octet-align=",false))
|
||||
amrOctet = (0 != line.toInteger(0));
|
||||
}
|
||||
}
|
||||
else if (first) {
|
||||
if (line.startSkip("crypto:",false)) {
|
||||
if (crypto.null())
|
||||
crypto = line;
|
||||
else
|
||||
Debug(this,DebugMild,"Ignoring SDES: '%s'",line.c_str());
|
||||
}
|
||||
else {
|
||||
int pos = line.find(':');
|
||||
if (pos >= 0)
|
||||
params.append(new NamedString(line.substr(0,pos),line.substr(pos+1)));
|
||||
else
|
||||
params.append(new NamedString(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (var < 0)
|
||||
break;
|
||||
first = false;
|
||||
|
||||
if (payload == "ilbc") {
|
||||
const char* forced = m_hacks.getValue("ilbc_forced");
|
||||
if (forced)
|
||||
payload = forced;
|
||||
else if ((mode == 20) || (ptime == 20))
|
||||
payload = "ilbc20";
|
||||
else if ((mode == 30) || (ptime == 30))
|
||||
payload = "ilbc30";
|
||||
else
|
||||
payload = m_hacks.getValue("ilbc_default","ilbc30");
|
||||
}
|
||||
|
||||
if (amrOctet && payload == "amr")
|
||||
payload = "amr-o";
|
||||
|
||||
XDebug(this,DebugAll,"Payload %d format '%s'",var,payload.c_str());
|
||||
if (payload && m_codecs.getBoolValue(payload,defcodecs && DataTranslator::canConvert(payload))) {
|
||||
if (fmt)
|
||||
fmt << ",";
|
||||
fmt << payload;
|
||||
if (var != defmap) {
|
||||
if (mappings)
|
||||
mappings << ",";
|
||||
mappings << payload << "=" << var;
|
||||
}
|
||||
if ((payload == "g729") && m_hacks.getBoolValue("g729_annexb",annexB))
|
||||
aux << ",g729b";
|
||||
}
|
||||
}
|
||||
fmt += aux;
|
||||
DDebug(this,DebugAll,"Formats '%s' mappings '%s'",fmt.c_str(),mappings.c_str());
|
||||
SDPMedia* net = 0;
|
||||
// try to take the media descriptor from the old list
|
||||
if (oldMedia) {
|
||||
ObjList* om = oldMedia->find(type);
|
||||
if (om)
|
||||
net = static_cast<SDPMedia*>(om->remove(false));
|
||||
}
|
||||
bool append = false;
|
||||
if (net)
|
||||
net->update(fmt,port);
|
||||
else {
|
||||
net = new SDPMedia(type,trans,fmt,port);
|
||||
append = true;
|
||||
}
|
||||
while (NamedString* par = static_cast<NamedString*>(params.remove(false)))
|
||||
net->parameter(par,append);
|
||||
net->setModified(false);
|
||||
net->mappings(mappings);
|
||||
net->rfc2833(rfc2833);
|
||||
net->crypto(crypto,true);
|
||||
if (!lst)
|
||||
lst = new ObjList;
|
||||
lst->append(net);
|
||||
// found media - get out
|
||||
if (media)
|
||||
break;
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
|
||||
// Update configuration
|
||||
void SDPParser::initialize(const NamedList* codecs, const NamedList* hacks, const NamedList* general)
|
||||
{
|
||||
Lock lock(this);
|
||||
m_codecs.clear();
|
||||
m_hacks.clear();
|
||||
if (codecs)
|
||||
m_codecs.copyParams(*codecs);
|
||||
if (hacks)
|
||||
m_hacks.copyParams(*hacks);
|
||||
bool defcodecs = m_codecs.getBoolValue("default",true);
|
||||
m_audioFormats = "";
|
||||
String audio = "audio";
|
||||
for (const TokenDict* dict = s_payloads; dict->token; dict++) {
|
||||
DataFormat fmt(dict->token);
|
||||
const FormatInfo* info = fmt.getInfo();
|
||||
if (info && (audio == info->type)) {
|
||||
if (m_codecs.getBoolValue(fmt,defcodecs && DataTranslator::canConvert(fmt)))
|
||||
m_audioFormats.append(fmt,",");
|
||||
}
|
||||
}
|
||||
if (!m_audioFormats) {
|
||||
Debug(this,DebugWarn,"No default audio codecs, using defaults");
|
||||
m_audioFormats = "alaw,mulaw";
|
||||
}
|
||||
DDebug(this,DebugNote,"Default audio codecs: %s",m_audioFormats.c_str());
|
||||
m_ignorePort = m_hacks.getBoolValue("ignore_sdp_port",false);
|
||||
m_rfc2833 = true;
|
||||
m_secure = false;
|
||||
m_sdpForward = false;
|
||||
if (general) {
|
||||
m_rfc2833 = general->getBoolValue("rfc2833",m_rfc2833);
|
||||
m_secure = general->getBoolValue("secure",m_secure);
|
||||
m_sdpForward = general->getBoolValue("forward_sdp",m_sdpForward);
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace TelEngine
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,741 @@
|
|||
/**
|
||||
* session.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* SDP media handling
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2009 Null Team
|
||||
*
|
||||
* 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 2 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <yatesdp.h>
|
||||
|
||||
namespace TelEngine {
|
||||
|
||||
/*
|
||||
* SDPSession
|
||||
*/
|
||||
SDPSession::SDPSession(SDPParser* parser)
|
||||
: m_parser(parser), m_mediaStatus(MediaMissing),
|
||||
m_rtpForward(false), m_sdpForward(false), m_rtpMedia(0),
|
||||
m_sdpSession(0), m_sdpVersion(0),
|
||||
m_secure(m_parser->m_secure), m_rfc2833(m_parser->m_rfc2833)
|
||||
{
|
||||
}
|
||||
|
||||
SDPSession::SDPSession(SDPParser* parser, NamedList& params)
|
||||
: m_parser(parser), m_mediaStatus(MediaMissing),
|
||||
m_rtpForward(false), m_sdpForward(false), m_rtpMedia(0),
|
||||
m_sdpSession(0), m_sdpVersion(0)
|
||||
{
|
||||
m_rtpForward = params.getBoolValue("rtp_forward");
|
||||
m_secure = params.getBoolValue("secure",parser->m_secure);
|
||||
m_rfc2833 = params.getBoolValue("rfc2833",parser->m_rfc2833);
|
||||
}
|
||||
|
||||
SDPSession::~SDPSession()
|
||||
{
|
||||
resetSdp();
|
||||
}
|
||||
|
||||
// Set new media list. Return true if changed
|
||||
bool SDPSession::setMedia(ObjList* media)
|
||||
{
|
||||
if (media == m_rtpMedia)
|
||||
return false;
|
||||
DDebug(m_parser,DebugAll,"SDPSession::setMedia(%p) [%p]",media,this);
|
||||
ObjList* tmp = m_rtpMedia;
|
||||
m_rtpMedia = media;
|
||||
bool chg = m_rtpMedia != 0;
|
||||
if (tmp) {
|
||||
chg = false;
|
||||
for (ObjList* o = tmp->skipNull(); o; o = o->skipNext()) {
|
||||
SDPMedia* m = static_cast<SDPMedia*>(o->get());
|
||||
if (media && m->sameAs(static_cast<SDPMedia*>((*media)[*m]),m_parser->ignorePort()))
|
||||
continue;
|
||||
chg = true;
|
||||
mediaChanged(*m);
|
||||
}
|
||||
TelEngine::destruct(tmp);
|
||||
}
|
||||
return chg;
|
||||
}
|
||||
|
||||
// Put the list of net media in a parameter list
|
||||
void SDPSession::putMedia(NamedList& msg, ObjList* mList, bool putPort)
|
||||
{
|
||||
if (!mList)
|
||||
return;
|
||||
for (mList = mList->skipNull(); mList; mList = mList->skipNext()) {
|
||||
SDPMedia* m = static_cast<SDPMedia*>(mList->get());
|
||||
m->putMedia(msg,putPort);
|
||||
}
|
||||
}
|
||||
|
||||
// Build and dispatch a chan.rtp message for a given media. Update media on success
|
||||
bool SDPSession::dispatchRtp(SDPMedia* media, const char* addr, bool start,
|
||||
bool pick, RefObject* context)
|
||||
{
|
||||
DDebug(m_parser,DebugAll,"SDPSession::dispatchRtp(%p,%s,%u,%u,%p) [%p]",
|
||||
media,addr,start,pick,context,this);
|
||||
Message* m = buildChanRtp(media,addr,start,context);
|
||||
if (!(m && Engine::dispatch(m))) {
|
||||
TelEngine::destruct(m);
|
||||
return false;
|
||||
}
|
||||
media->update(*m,start);
|
||||
if (!pick) {
|
||||
TelEngine::destruct(m);
|
||||
return true;
|
||||
}
|
||||
m_rtpForward = false;
|
||||
m_rtpLocalAddr = m->getValue("localip",m_rtpLocalAddr);
|
||||
m_mediaStatus = MediaStarted;
|
||||
const char* sdpPrefix = m->getValue("osdp-prefix","osdp");
|
||||
if (sdpPrefix) {
|
||||
unsigned int n = m->length();
|
||||
for (unsigned int j = 0; j < n; j++) {
|
||||
const NamedString* param = m->getParam(j);
|
||||
if (!param)
|
||||
continue;
|
||||
String tmp = param->name();
|
||||
if (tmp.startSkip(sdpPrefix,false) && tmp.startSkip("_",false) && tmp)
|
||||
media->parameter(tmp,*param,false);
|
||||
}
|
||||
}
|
||||
if (m_secure) {
|
||||
int tag = m->getIntValue("crypto_tag",1);
|
||||
tag = m->getIntValue("ocrypto_tag",tag);
|
||||
const String* suite = m->getParam("ocrypto_suite");
|
||||
const String* key = m->getParam("ocrypto_key");
|
||||
const String* params = m->getParam("ocrypto_params");
|
||||
if (suite && key && (tag > 0)) {
|
||||
String sdes(tag);
|
||||
sdes << " " << *suite << " " << *key;
|
||||
if (params)
|
||||
sdes << " " << *params;
|
||||
media->crypto(sdes,false);
|
||||
}
|
||||
}
|
||||
TelEngine::destruct(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Repeatedly calls dispatchRtp() for each media in the list
|
||||
// Update it on success. Remove it on failure
|
||||
bool SDPSession::dispatchRtp(const char* addr, bool start, RefObject* context)
|
||||
{
|
||||
if (!m_rtpMedia)
|
||||
return false;
|
||||
DDebug(m_parser,DebugAll,"SDPSession::dispatchRtp(%s,%u,%p) [%p]",
|
||||
addr,start,context,this);
|
||||
bool ok = false;
|
||||
ObjList* o = m_rtpMedia->skipNull();
|
||||
while (o) {
|
||||
SDPMedia* m = static_cast<SDPMedia*>(o->get());
|
||||
if (dispatchRtp(m,addr,start,true,context)) {
|
||||
ok = true;
|
||||
o = o->skipNext();
|
||||
}
|
||||
else {
|
||||
Debug(m_parser,DebugMild,
|
||||
"Removing failed SDP media '%s' format '%s' from offer [%p]",
|
||||
m->c_str(),m->format().safe(),this);
|
||||
o->remove();
|
||||
o = o->skipNull();
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Try to start RTP for all media
|
||||
bool SDPSession::startRtp(RefObject* context)
|
||||
{
|
||||
if (m_rtpForward || !m_rtpMedia || (m_mediaStatus != MediaStarted))
|
||||
return false;
|
||||
DDebug(m_parser,DebugAll,"SDPSession::startRtp(%p) [%p]",context,this);
|
||||
bool ok = false;
|
||||
for (ObjList* o = m_rtpMedia->skipNull(); o; o = o->skipNext()) {
|
||||
SDPMedia* m = static_cast<SDPMedia*>(o->get());
|
||||
ok = dispatchRtp(m,m_rtpAddr,true,false,context) || ok;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Update from parameters. Build a default SDP if no media is found in params
|
||||
bool SDPSession::updateSDP(const NamedList& params)
|
||||
{
|
||||
DDebug(m_parser,DebugAll,"SDPSession::updateSdp('%s') [%p]",params.c_str(),this);
|
||||
bool defaults = true;
|
||||
const char* sdpPrefix = params.getValue("osdp-prefix","osdp");
|
||||
ObjList* lst = 0;
|
||||
unsigned int n = params.length();
|
||||
String defFormats;
|
||||
m_parser->getAudioFormats(defFormats);
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
const NamedString* p = params.getParam(i);
|
||||
if (!p)
|
||||
continue;
|
||||
// search for rtp_port or rtp_port_MEDIANAME parameters
|
||||
String tmp(p->name());
|
||||
if (!tmp.startSkip("media",false))
|
||||
continue;
|
||||
if (tmp && (tmp[0] != '_'))
|
||||
continue;
|
||||
// since we found at least one media declaration disable defaults
|
||||
defaults = false;
|
||||
// now tmp holds the suffix for the media, null for audio
|
||||
bool audio = tmp.null();
|
||||
// check if media is supported, default only for audio
|
||||
if (!p->toBoolean(audio))
|
||||
continue;
|
||||
String fmts = params.getValue("formats" + tmp);
|
||||
if (audio && fmts.null())
|
||||
fmts = defFormats;
|
||||
if (fmts.null())
|
||||
continue;
|
||||
String trans = params.getValue("transport" + tmp,"RTP/AVP");
|
||||
String crypto;
|
||||
if (m_secure)
|
||||
crypto = params.getValue("crypto" + tmp);
|
||||
if (audio)
|
||||
tmp = "audio";
|
||||
else
|
||||
tmp >> "_";
|
||||
SDPMedia* rtp = 0;
|
||||
// try to take the media descriptor from the old list
|
||||
if (m_rtpMedia) {
|
||||
ObjList* om = m_rtpMedia->find(tmp);
|
||||
if (om)
|
||||
rtp = static_cast<SDPMedia*>(om->remove(false));
|
||||
}
|
||||
bool append = false;
|
||||
if (rtp)
|
||||
rtp->update(fmts);
|
||||
else {
|
||||
rtp = new SDPMedia(tmp,trans,fmts);
|
||||
append = true;
|
||||
}
|
||||
rtp->crypto(crypto,false);
|
||||
if (sdpPrefix) {
|
||||
for (unsigned int j = 0; j < n; j++) {
|
||||
const NamedString* param = params.getParam(j);
|
||||
if (!param)
|
||||
continue;
|
||||
tmp = param->name();
|
||||
if (tmp.startSkip(sdpPrefix + rtp->suffix() + "_",false) && (tmp.find('_') < 0))
|
||||
rtp->parameter(tmp,*param,append);
|
||||
}
|
||||
}
|
||||
if (!lst)
|
||||
lst = new ObjList;
|
||||
lst->append(rtp);
|
||||
}
|
||||
if (defaults && !lst) {
|
||||
lst = new ObjList;
|
||||
lst->append(new SDPMedia("audio","RTP/AVP",params.getValue("formats",defFormats)));
|
||||
}
|
||||
return setMedia(lst);
|
||||
}
|
||||
|
||||
|
||||
// Update RTP/SDP data from parameters
|
||||
// Return true if media changed
|
||||
bool SDPSession::updateRtpSDP(const NamedList& params)
|
||||
{
|
||||
DDebug(m_parser,DebugAll,"SDPSession::updateRtpSDP(%s) [%p]",params.c_str(),this);
|
||||
String addr;
|
||||
ObjList* tmp = updateRtpSDP(params,addr,m_rtpMedia);
|
||||
if (tmp) {
|
||||
bool chg = (m_rtpLocalAddr != addr);
|
||||
m_rtpLocalAddr = addr;
|
||||
return setMedia(tmp) || chg;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Creates a SDP body from transport address and list of media descriptors
|
||||
// Use own list if given media list is 0
|
||||
MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
|
||||
{
|
||||
DDebug(m_parser,DebugAll,"SDPSession::createSDP('%s',%p) [%p]",addr,mediaList,this);
|
||||
if (!mediaList)
|
||||
mediaList = m_rtpMedia;
|
||||
// if we got no media descriptors we simply create no SDP
|
||||
if (!mediaList)
|
||||
return 0;
|
||||
if (m_sdpSession)
|
||||
++m_sdpVersion;
|
||||
else
|
||||
m_sdpVersion = m_sdpSession = Time::secNow();
|
||||
|
||||
// no address means on hold or muted
|
||||
String origin;
|
||||
origin << "yate " << m_sdpSession << " " << m_sdpVersion;
|
||||
origin << " IN IP4 " << (addr ? addr : m_host.safe());
|
||||
String conn;
|
||||
conn << "IN IP4 " << (addr ? addr : "0.0.0.0");
|
||||
|
||||
MimeSdpBody* sdp = new MimeSdpBody;
|
||||
sdp->addLine("v","0");
|
||||
sdp->addLine("o",origin);
|
||||
sdp->addLine("s",m_parser->m_sessionName);
|
||||
sdp->addLine("c",conn);
|
||||
sdp->addLine("t","0 0");
|
||||
|
||||
Lock lock(m_parser);
|
||||
bool defcodecs = m_parser->m_codecs.getBoolValue("default",true);
|
||||
for (ObjList* ml = mediaList->skipNull(); ml; ml = ml->skipNext()) {
|
||||
SDPMedia* m = static_cast<SDPMedia*>(ml->get());
|
||||
String mline(m->fmtList());
|
||||
ObjList* l = mline.split(',',false);
|
||||
mline = *m;
|
||||
mline << " " << (m->localPort() ? m->localPort().c_str() : "0") << " " << m->transport();
|
||||
ObjList* map = m->mappings().split(',',false);
|
||||
ObjList rtpmap;
|
||||
String frm;
|
||||
int ptime = 0;
|
||||
ObjList* f = l;
|
||||
for (; f; f = f->next()) {
|
||||
String* s = static_cast<String*>(f->get());
|
||||
if (s) {
|
||||
int mode = 0;
|
||||
if (*s == "ilbc20")
|
||||
ptime = mode = 20;
|
||||
else if (*s == "ilbc30")
|
||||
ptime = mode = 30;
|
||||
else if (*s == "g729b")
|
||||
continue;
|
||||
int payload = s->toInteger(SDPParser::s_payloads,-1);
|
||||
int defcode = payload;
|
||||
String tmp = *s;
|
||||
tmp << "=";
|
||||
for (ObjList* pl = map; pl; pl = pl->next()) {
|
||||
String* mapping = static_cast<String*>(pl->get());
|
||||
if (!mapping)
|
||||
continue;
|
||||
if (mapping->startsWith(tmp)) {
|
||||
payload = -1;
|
||||
tmp = *mapping;
|
||||
tmp >> "=" >> payload;
|
||||
XDebug(m_parser,DebugAll,"RTP mapped payload %d for '%s' [%p]",
|
||||
payload,s->c_str(),this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (payload >= 0) {
|
||||
if (defcode < 0)
|
||||
defcode = payload;
|
||||
const char* map = lookup(defcode,SDPParser::s_rtpmap);
|
||||
if (map && m_parser->m_codecs.getBoolValue(*s,defcodecs && DataTranslator::canConvert(*s))) {
|
||||
frm << " " << payload;
|
||||
String* temp = new String("rtpmap:");
|
||||
*temp << payload << " " << map;
|
||||
rtpmap.append(temp);
|
||||
if (mode) {
|
||||
temp = new String("fmtp:");
|
||||
*temp << payload << " mode=" << mode;
|
||||
rtpmap.append(temp);
|
||||
}
|
||||
if (*s == "g729") {
|
||||
temp = new String("fmtp:");
|
||||
*temp << payload << " annexb=" <<
|
||||
((0 != l->find("g729b")) ? "yes" : "no");
|
||||
rtpmap.append(temp);
|
||||
}
|
||||
else if (*s == "amr") {
|
||||
temp = new String("fmtp:");
|
||||
*temp << payload << " octet-align=0";
|
||||
rtpmap.append(temp);
|
||||
}
|
||||
else if (*s == "amr-o") {
|
||||
temp = new String("fmtp:");
|
||||
*temp << payload << " octet-align=1";
|
||||
rtpmap.append(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TelEngine::destruct(l);
|
||||
TelEngine::destruct(map);
|
||||
|
||||
if (m_rfc2833 && frm && m->isAudio()) {
|
||||
int rfc2833 = m->rfc2833().toInteger(-1);
|
||||
if (rfc2833 < 0)
|
||||
rfc2833 = 101;
|
||||
// claim to support telephone events
|
||||
frm << " " << rfc2833;
|
||||
String* s = new String;
|
||||
*s << "rtpmap:" << rfc2833 << " telephone-event/8000";
|
||||
rtpmap.append(s);
|
||||
}
|
||||
|
||||
if (frm.null()) {
|
||||
if (m->isAudio() || !m->fmtList()) {
|
||||
Debug(m_parser,DebugMild,"No formats for '%s', excluding from SDP [%p]",
|
||||
m->c_str(),this);
|
||||
continue;
|
||||
}
|
||||
Debug(m_parser,DebugInfo,"Assuming formats '%s' for media '%s' [%p]",
|
||||
m->fmtList(),m->c_str(),this);
|
||||
frm << " " << m->fmtList();
|
||||
// brutal but effective
|
||||
for (char* p = const_cast<char*>(frm.c_str()); *p; p++) {
|
||||
if (*p == ',')
|
||||
*p = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (ptime) {
|
||||
String* temp = new String("ptime:");
|
||||
*temp << ptime;
|
||||
rtpmap.append(temp);
|
||||
}
|
||||
|
||||
sdp->addLine("m",mline + frm);
|
||||
bool enc = false;
|
||||
if (m->isModified()) {
|
||||
unsigned int n = m->length();
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
const NamedString* param = m->getParam(i);
|
||||
if (param) {
|
||||
String tmp = param->name();
|
||||
if (*param)
|
||||
tmp << ":" << *param;
|
||||
sdp->addLine("a",tmp);
|
||||
enc = enc || (param->name() == "encryption");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (f = rtpmap.skipNull(); f; f = f->skipNext()) {
|
||||
String* s = static_cast<String*>(f->get());
|
||||
if (s)
|
||||
sdp->addLine("a",*s);
|
||||
}
|
||||
if (addr && m->localCrypto()) {
|
||||
sdp->addLine("a","crypto:" + m->localCrypto());
|
||||
if (!enc)
|
||||
sdp->addLine("a","encryption:optional");
|
||||
}
|
||||
}
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
// Creates a SDP body for the current media status
|
||||
MimeSdpBody* SDPSession::createSDP()
|
||||
{
|
||||
switch (m_mediaStatus) {
|
||||
case MediaStarted:
|
||||
return createSDP(getRtpAddr());
|
||||
case MediaMuted:
|
||||
return createSDP(0);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a SDP from RTP address data present in message
|
||||
MimeSdpBody* SDPSession::createPasstroughSDP(NamedList& msg, bool update)
|
||||
{
|
||||
String tmp = msg.getValue("rtp_forward");
|
||||
msg.clearParam("rtp_forward");
|
||||
if (!(m_rtpForward && tmp.toBoolean()))
|
||||
return 0;
|
||||
String* raw = msg.getParam("sdp_raw");
|
||||
if (raw) {
|
||||
m_sdpForward = m_sdpForward || m_parser->sdpForward();
|
||||
if (m_sdpForward) {
|
||||
msg.setParam("rtp_forward","accepted");
|
||||
return new MimeSdpBody("application/sdp",raw->safe(),raw->length());
|
||||
}
|
||||
}
|
||||
String addr;
|
||||
ObjList* lst = updateRtpSDP(msg,addr,update ? m_rtpMedia : 0);
|
||||
if (!lst)
|
||||
return 0;
|
||||
MimeSdpBody* sdp = createSDP(addr,lst);
|
||||
if (update) {
|
||||
m_rtpLocalAddr = addr;
|
||||
setMedia(lst);
|
||||
}
|
||||
else
|
||||
TelEngine::destruct(lst);
|
||||
if (sdp)
|
||||
msg.setParam("rtp_forward","accepted");
|
||||
return sdp;
|
||||
}
|
||||
|
||||
// Update media format lists from parameters
|
||||
void SDPSession::updateFormats(const NamedList& msg)
|
||||
{
|
||||
if (!m_rtpMedia)
|
||||
return;
|
||||
unsigned int n = msg.length();
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
const NamedString* p = msg.getParam(i);
|
||||
if (!p)
|
||||
continue;
|
||||
// search for formats_MEDIANAME parameters
|
||||
String tmp = p->name();
|
||||
if (!tmp.startSkip("formats",false))
|
||||
continue;
|
||||
if (tmp && (tmp[0] != '_'))
|
||||
continue;
|
||||
if (tmp.null())
|
||||
tmp = "audio";
|
||||
else
|
||||
tmp = tmp.substr(1);
|
||||
SDPMedia* rtp = static_cast<SDPMedia*>(m_rtpMedia->operator[](tmp));
|
||||
if (rtp && rtp->update(*p))
|
||||
Debug(m_parser,DebugNote,"Formats for '%s' changed to '%s' [%p]",
|
||||
tmp.c_str(),p->c_str(),this);
|
||||
}
|
||||
}
|
||||
|
||||
// Add raw SDP forwarding parameter
|
||||
bool SDPSession::addSdpParams(NamedList& msg, const MimeBody* body)
|
||||
{
|
||||
if (!(m_sdpForward && body))
|
||||
return false;
|
||||
const MimeSdpBody* sdp =
|
||||
static_cast<const MimeSdpBody*>(body->isSDP() ? body : body->getFirst("application/sdp"));
|
||||
if (!sdp)
|
||||
return false;
|
||||
const DataBlock& raw = sdp->getBody();
|
||||
String tmp((const char*)raw.data(),raw.length());
|
||||
return addSdpParams(msg,tmp);
|
||||
}
|
||||
|
||||
// Add raw SDP forwarding parameter
|
||||
bool SDPSession::addSdpParams(NamedList& msg, const String& rawSdp)
|
||||
{
|
||||
if (!m_sdpForward)
|
||||
return false;
|
||||
msg.setParam("rtp_forward","yes");
|
||||
msg.addParam("sdp_raw",rawSdp);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add RTP forwarding parameters to a message
|
||||
bool SDPSession::addRtpParams(NamedList& msg, const String& natAddr,
|
||||
const MimeBody* body, bool force)
|
||||
{
|
||||
if (!(m_rtpMedia && m_rtpAddr))
|
||||
return false;
|
||||
putMedia(msg,false);
|
||||
if (force || (!startRtp() && m_rtpForward)) {
|
||||
if (natAddr)
|
||||
msg.addParam("rtp_nat_addr",natAddr);
|
||||
msg.addParam("rtp_forward","yes");
|
||||
msg.addParam("rtp_addr",m_rtpAddr);
|
||||
for (ObjList* o = m_rtpMedia->skipNull(); o; o = o->skipNext()) {
|
||||
SDPMedia* m = static_cast<SDPMedia*>(o->get());
|
||||
msg.addParam("rtp_port" + m->suffix(),m->remotePort());
|
||||
if (m->isAudio())
|
||||
msg.addParam("rtp_rfc2833",m->rfc2833());
|
||||
}
|
||||
addSdpParams(msg,body);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset this object to default values
|
||||
void SDPSession::resetSdp()
|
||||
{
|
||||
m_mediaStatus = MediaMissing;
|
||||
TelEngine::destruct(m_rtpMedia);
|
||||
m_rtpForward = false;
|
||||
m_sdpForward = false;
|
||||
m_externalAddr.clear();
|
||||
m_rtpAddr.clear();
|
||||
m_rtpLocalAddr.clear();
|
||||
m_sdpSession = 0;
|
||||
m_sdpVersion = 0;
|
||||
m_host.clear();
|
||||
m_secure = m_parser->secure();
|
||||
m_rfc2833 = m_parser->rfc2833();
|
||||
}
|
||||
|
||||
// Build a populated chan.rtp message
|
||||
Message* SDPSession::buildChanRtp(SDPMedia* media, const char* addr, bool start, RefObject* context)
|
||||
{
|
||||
if (!(media && addr))
|
||||
return 0;
|
||||
Message* m = buildChanRtp(context);
|
||||
if (!m)
|
||||
return 0;
|
||||
m->addParam("media",*media);
|
||||
m->addParam("transport",media->transport());
|
||||
m->addParam("direction","bidir");
|
||||
if (m_rtpLocalAddr)
|
||||
m->addParam("localip",m_rtpLocalAddr);
|
||||
m->addParam("remoteip",addr);
|
||||
if (start) {
|
||||
m->addParam("remoteport",media->remotePort());
|
||||
m->addParam("format",media->format());
|
||||
String tmp = media->format();
|
||||
tmp << "=";
|
||||
ObjList* mappings = media->mappings().split(',',false);
|
||||
for (ObjList* pl = mappings; pl; pl = pl->next()) {
|
||||
String* mapping = static_cast<String*>(pl->get());
|
||||
if (!mapping)
|
||||
continue;
|
||||
if (mapping->startsWith(tmp)) {
|
||||
tmp = *mapping;
|
||||
tmp >> "=";
|
||||
m->addParam("payload",tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m->addParam("evpayload",media->rfc2833());
|
||||
TelEngine::destruct(mappings);
|
||||
}
|
||||
if (m_secure) {
|
||||
if (media->remoteCrypto()) {
|
||||
String sdes = media->remoteCrypto();
|
||||
Regexp r("^\\([0-9]\\+\\) \\+\\([^ ]\\+\\) \\+\\([^ ]\\+\\) *\\(.*\\)$");
|
||||
if (sdes.matches(r)) {
|
||||
m->addParam("secure",String::boolText(true));
|
||||
m->addParam("crypto_tag",sdes.matchString(1));
|
||||
m->addParam("crypto_suite",sdes.matchString(2));
|
||||
m->addParam("crypto_key",sdes.matchString(3));
|
||||
if (sdes.matchLength(4))
|
||||
m->addParam("crypto_params",sdes.matchString(4));
|
||||
}
|
||||
else
|
||||
Debug(m_parser,DebugWarn,"Invalid SDES: '%s' [%p]",sdes.c_str(),this);
|
||||
}
|
||||
else if (media->securable())
|
||||
m->addParam("secure",String::boolText(true));
|
||||
}
|
||||
else
|
||||
media->crypto(0,true);
|
||||
unsigned int n = media->length();
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
const NamedString* param = media->getParam(i);
|
||||
if (!param)
|
||||
continue;
|
||||
m->addParam("sdp_" + param->name(),*param);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
// Check if local RTP data changed for at least one media
|
||||
bool SDPSession::localRtpChanged() const
|
||||
{
|
||||
if (!m_rtpMedia)
|
||||
return false;
|
||||
for (ObjList* o = m_rtpMedia->skipNull(); o; o = o->skipNext()) {
|
||||
SDPMedia* m = static_cast<SDPMedia*>(o->get());
|
||||
if (m->localChanged())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set or reset the local RTP data changed flag for all media
|
||||
void SDPSession::setLocalRtpChanged(bool chg)
|
||||
{
|
||||
if (!m_rtpMedia)
|
||||
return;
|
||||
for (ObjList* o = m_rtpMedia->skipNull(); o; o = o->skipNext())
|
||||
(static_cast<SDPMedia*>(o->get()))->setLocalChanged(chg);
|
||||
}
|
||||
|
||||
// Update RTP/SDP data from parameters
|
||||
ObjList* SDPSession::updateRtpSDP(const NamedList& params, String& rtpAddr, ObjList* oldList)
|
||||
{
|
||||
DDebug(DebugAll,"SDPSession::updateRtpSDP(%s,%s,%p)",params.c_str(),rtpAddr.c_str(),oldList);
|
||||
rtpAddr = params.getValue("rtp_addr");
|
||||
if (!rtpAddr)
|
||||
return 0;
|
||||
const char* sdpPrefix = params.getValue("osdp-prefix","osdp");
|
||||
ObjList* lst = 0;
|
||||
unsigned int n = params.length();
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
const NamedString* p = params.getParam(i);
|
||||
if (!p)
|
||||
continue;
|
||||
// search for rtp_port or rtp_port_MEDIANAME parameters
|
||||
String tmp = p->name();
|
||||
if (!tmp.startSkip("rtp_port",false))
|
||||
continue;
|
||||
if (tmp && (tmp[0] != '_'))
|
||||
continue;
|
||||
// now tmp holds the suffix for the media, null for audio
|
||||
bool audio = tmp.null();
|
||||
// check if media is supported, default only for audio
|
||||
if (!params.getBoolValue("media" + tmp,audio))
|
||||
continue;
|
||||
int port = p->toInteger();
|
||||
if (!port)
|
||||
continue;
|
||||
const char* fmts = params.getValue("formats" + tmp);
|
||||
if (!fmts)
|
||||
continue;
|
||||
String trans = params.getValue("transport" + tmp,"RTP/AVP");
|
||||
if (audio)
|
||||
tmp = "audio";
|
||||
else
|
||||
tmp >> "_";
|
||||
SDPMedia* rtp = 0;
|
||||
// try to take the media descriptor from the old list
|
||||
if (oldList) {
|
||||
ObjList* om = oldList->find(tmp);
|
||||
if (om)
|
||||
rtp = static_cast<SDPMedia*>(om->remove(false));
|
||||
}
|
||||
bool append = false;
|
||||
if (rtp)
|
||||
rtp->update(fmts,-1,port);
|
||||
else {
|
||||
rtp = new SDPMedia(tmp,trans,fmts,-1,port);
|
||||
append = true;
|
||||
}
|
||||
if (sdpPrefix) {
|
||||
for (unsigned int j = 0; j < n; j++) {
|
||||
const NamedString* param = params.getParam(j);
|
||||
if (!param)
|
||||
continue;
|
||||
tmp = param->name();
|
||||
if (tmp.startSkip(sdpPrefix + rtp->suffix() + "_",false) && (tmp.find('_') < 0))
|
||||
rtp->parameter(tmp,*param,append);
|
||||
}
|
||||
}
|
||||
rtp->mappings(params.getValue("rtp_mapping" + rtp->suffix()));
|
||||
if (audio)
|
||||
rtp->rfc2833(params.getIntValue("rtp_rfc2833",-1));
|
||||
rtp->crypto(params.getValue("crypto" + rtp->suffix()),false);
|
||||
if (!lst)
|
||||
lst = new ObjList;
|
||||
lst->append(rtp);
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
|
||||
// Media changed notification.
|
||||
void SDPSession::mediaChanged(const String& name)
|
||||
{
|
||||
XDebug(m_parser,DebugAll,"SDPSession::mediaChanged(%s) [%p]",name.c_str(),this);
|
||||
}
|
||||
|
||||
}; // namespace TelEngine
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,711 @@
|
|||
/*
|
||||
* yatesdp.h
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* SDP media handling
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2009 Null Team
|
||||
*
|
||||
* 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 2 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __YATESDP_H
|
||||
#define __YATESDP_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#error C++ is required
|
||||
#endif
|
||||
|
||||
#include <yatemime.h>
|
||||
#include <yatephone.h>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
|
||||
#ifdef LIBYSDP_EXPORTS
|
||||
#define YSDP_API __declspec(dllexport)
|
||||
#else
|
||||
#ifndef LIBYSDP_STATIC
|
||||
#define YSDP_API __declspec(dllimport)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* _WINDOWS */
|
||||
|
||||
#ifndef YSDP_API
|
||||
#define YSDP_API
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Holds all Telephony Engine related classes.
|
||||
*/
|
||||
namespace TelEngine {
|
||||
|
||||
class SDPMedia;
|
||||
class SDPSession;
|
||||
class SDPParser;
|
||||
|
||||
/**
|
||||
* This class holds a single SDP media description
|
||||
* @short SDP media description
|
||||
*/
|
||||
class YSDP_API SDPMedia : public NamedList
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param media Media type name
|
||||
* @param transport Transport name
|
||||
* @param formats Comma separated list of formats
|
||||
* @param rport Optional remote media port
|
||||
* @param lport Optional local media port
|
||||
*/
|
||||
SDPMedia(const char* media, const char* transport, const char* formats,
|
||||
int rport = -1, int lport = -1);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~SDPMedia();
|
||||
|
||||
/**
|
||||
* Check if this media type is audio
|
||||
* @return True if this media describe an audio one
|
||||
*/
|
||||
inline bool isAudio() const
|
||||
{ return m_audio; }
|
||||
|
||||
/**
|
||||
* Check if a media parameter changed
|
||||
* @return True if a media changed
|
||||
*/
|
||||
inline bool isModified() const
|
||||
{ return m_modified; }
|
||||
|
||||
/**
|
||||
* Set or reset media parameter changed flag
|
||||
* @param modified The new value of the media parameter changed flag
|
||||
*/
|
||||
inline void setModified(bool modified = true)
|
||||
{ m_modified = modified; }
|
||||
|
||||
/**
|
||||
* Retrieve the media suffix (built from type)
|
||||
* @return Media suffix
|
||||
*/
|
||||
inline const String& suffix() const
|
||||
{ return m_suffix; }
|
||||
|
||||
/**
|
||||
* Retrieve the media transport name
|
||||
* @return The media transport name
|
||||
*/
|
||||
inline const String& transport() const
|
||||
{ return m_transport; }
|
||||
|
||||
/**
|
||||
* Retrieve the media id
|
||||
* @return The media id
|
||||
*/
|
||||
inline const String& id() const
|
||||
{ return m_id; }
|
||||
|
||||
/**
|
||||
* Retrieve the current media format
|
||||
* @return The current media format
|
||||
*/
|
||||
inline const String& format() const
|
||||
{ return m_format; }
|
||||
|
||||
/**
|
||||
* Retrieve the formats set for this media
|
||||
* @return Comma separated list of media formats
|
||||
*/
|
||||
inline const String& formats() const
|
||||
{ return m_formats; }
|
||||
|
||||
/**
|
||||
* Retrieve the remote media port
|
||||
* @return The remote media port
|
||||
*/
|
||||
inline const String& remotePort() const
|
||||
{ return m_rPort; }
|
||||
|
||||
/**
|
||||
* Retrieve the local media port
|
||||
* @return The local media port
|
||||
*/
|
||||
inline const String& localPort() const
|
||||
{ return m_lPort; }
|
||||
|
||||
/**
|
||||
* Retrieve rtp payload mappings
|
||||
* @return Rtp payload mappings
|
||||
*/
|
||||
inline const String& mappings() const
|
||||
{ return m_mappings; }
|
||||
|
||||
/**
|
||||
* Set rtp payload mappings for this media
|
||||
* @param newMap New rtp payload mappings
|
||||
*/
|
||||
inline void mappings(const char* newMap)
|
||||
{ if (newMap) m_mappings = newMap; }
|
||||
|
||||
/**
|
||||
* Retrieve RFC2833 status or payload of this media
|
||||
* @return RFC2833 status or payload of this media
|
||||
*/
|
||||
inline const String& rfc2833() const
|
||||
{ return m_rfc2833; }
|
||||
|
||||
/**
|
||||
* Set RFC2833 status or payload of this media
|
||||
* @param payload SDP numeric payload to set.
|
||||
* Set it to a negative value to reset RFC2833
|
||||
*/
|
||||
inline void rfc2833(int payload)
|
||||
{
|
||||
if (payload >= 0)
|
||||
m_rfc2833 = payload;
|
||||
else
|
||||
m_rfc2833 = String::boolText(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve remote crypto description
|
||||
* @return Remote crypto description
|
||||
*/
|
||||
inline const String& remoteCrypto() const
|
||||
{ return m_rCrypto; }
|
||||
|
||||
/**
|
||||
* Retrieve local crypto description
|
||||
* @return Local crypto description
|
||||
*/
|
||||
inline const String& localCrypto() const
|
||||
{ return m_lCrypto; }
|
||||
|
||||
/**
|
||||
* Check if this media is securable
|
||||
* @return True if this media is securable
|
||||
*/
|
||||
inline bool securable() const
|
||||
{ return m_securable; }
|
||||
|
||||
/**
|
||||
* Compare this media with another one
|
||||
* @param other The media to compare with
|
||||
* @param ignorePort Ignore differences caused only by port number
|
||||
* @return True if both media have the same formats, transport and remote port
|
||||
*/
|
||||
inline bool sameAs(const SDPMedia* other, bool ignorePort = false) const
|
||||
{ return other && (other->formats() == m_formats) &&
|
||||
(other->transport() == m_transport) &&
|
||||
((ignorePort && other->remotePort() && m_rPort) ||
|
||||
(other->remotePort() == m_rPort)); }
|
||||
|
||||
/**
|
||||
* Check if local part of this media changed
|
||||
* @return True if local part of this media changed
|
||||
*/
|
||||
inline bool localChanged() const
|
||||
{ return m_localChanged; }
|
||||
|
||||
/**
|
||||
* Set or reset local media changed flag
|
||||
* @param chg The new value for local media changed flag
|
||||
*/
|
||||
inline void setLocalChanged(bool chg = false)
|
||||
{ m_localChanged = chg; }
|
||||
|
||||
/**
|
||||
* Retrieve a formats list from this media
|
||||
* @return Comma separated list of media formats (from formats list,
|
||||
* current format or a default G711, 'alaw,mulaw', list
|
||||
*/
|
||||
const char* fmtList() const;
|
||||
|
||||
/**
|
||||
* Update this media from formats and ports
|
||||
* @param formats New media formats
|
||||
* @param rport Optional remote media port
|
||||
* @param lport Optional local media port
|
||||
* @return True if media changed
|
||||
*/
|
||||
bool update(const char* formats, int rport = -1, int lport = -1);
|
||||
|
||||
/**
|
||||
* Update from a chan.rtp message (rtp id and local port)
|
||||
* @param msg The list of parameters
|
||||
* @param pickFormat True to update media format(s) from the list
|
||||
*/
|
||||
void update(const NamedList& msg, bool pickFormat);
|
||||
|
||||
/**
|
||||
* Add or replace a parameter by name and value, set the modified flag
|
||||
* @param name Parameter name
|
||||
* @param value Parameter value
|
||||
* @param append True to append, false to replace
|
||||
*/
|
||||
void parameter(const char* name, const char* value, bool append);
|
||||
|
||||
/**
|
||||
* Add or replace a parameter, set the modified flag
|
||||
* @param param The parameter
|
||||
* @param append True to append, false to replace
|
||||
*/
|
||||
void parameter(NamedString* param, bool append);
|
||||
|
||||
/**
|
||||
* Set a new crypto description, set the modified flag if changed.
|
||||
* Reset the media securable flag if the remote crypto is empty
|
||||
* @param desc The new crypto description
|
||||
* @param remote True to set the remote crypto, false to set the local one
|
||||
*/
|
||||
void crypto(const char* desc, bool remote);
|
||||
|
||||
/**
|
||||
* Put this net media in a parameter list
|
||||
* @param msg Destination list
|
||||
* @param putPort True to add remote media port
|
||||
*/
|
||||
void putMedia(NamedList& msg, bool putPort = true);
|
||||
|
||||
private:
|
||||
bool m_audio;
|
||||
bool m_modified;
|
||||
bool m_securable;
|
||||
// local rtp data changed flag
|
||||
bool m_localChanged;
|
||||
// suffix used for this type
|
||||
String m_suffix;
|
||||
// transport protocol
|
||||
String m_transport;
|
||||
// list of supported format names
|
||||
String m_formats;
|
||||
// format used for sending data
|
||||
String m_format;
|
||||
// id of the local media channel
|
||||
String m_id;
|
||||
// remote media port
|
||||
String m_rPort;
|
||||
// mappings of RTP payloads
|
||||
String m_mappings;
|
||||
// local media port
|
||||
String m_lPort;
|
||||
// payload for telephone/event
|
||||
String m_rfc2833;
|
||||
// remote security descriptor
|
||||
String m_rCrypto;
|
||||
// local security descriptor
|
||||
String m_lCrypto;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This class holds RTP/SDP data for multiple media types
|
||||
* NOTE: The SDPParser pointer held by this class is assumed to be non NULL
|
||||
* @short A holder for a SDP session
|
||||
*/
|
||||
class YSDP_API SDPSession
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* RTP media status enumeration
|
||||
*/
|
||||
enum {
|
||||
MediaMissing,
|
||||
MediaStarted,
|
||||
MediaMuted
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param parser The SDP parser whose data this object will use
|
||||
*/
|
||||
SDPSession(SDPParser* parser);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param parser The SDP parser whose data this object will use
|
||||
* @param params SDP session parameters
|
||||
*/
|
||||
SDPSession(SDPParser* parser, NamedList& params);
|
||||
|
||||
/**
|
||||
* Destructor. Reset the object
|
||||
*/
|
||||
virtual ~SDPSession();
|
||||
|
||||
/**
|
||||
* Get RTP local host
|
||||
* @return RTP local host
|
||||
*/
|
||||
inline const String& getHost() const
|
||||
{ return m_host; }
|
||||
|
||||
/**
|
||||
* Get local RTP address
|
||||
* @return Local RTP address (external or local)
|
||||
*/
|
||||
inline const String& getRtpAddr() const
|
||||
{ return m_externalAddr ? m_externalAddr : m_rtpLocalAddr; }
|
||||
|
||||
/**
|
||||
* Set a new media list
|
||||
* @param media New media list
|
||||
* @return True if media changed
|
||||
*/
|
||||
bool setMedia(ObjList* media);
|
||||
|
||||
/**
|
||||
* Put specified media parameters into a list of parameters
|
||||
* @param msg Destination list
|
||||
* @param media List of SDP media information
|
||||
* @param putPort True to add the media port
|
||||
*/
|
||||
static void putMedia(NamedList& msg, ObjList* media, bool putPort = true);
|
||||
|
||||
/**
|
||||
* Put session media parameters into a list of parameters
|
||||
* @param msg Destination list
|
||||
* @param putPort True to add the media port
|
||||
*/
|
||||
inline void putMedia(NamedList& msg, bool putPort = true)
|
||||
{ putMedia(msg,m_rtpMedia,putPort); }
|
||||
|
||||
/**
|
||||
* Build and dispatch a chan.rtp message for a given media. Update media on success
|
||||
* @param media The media to use
|
||||
* @param addr Remote RTP address
|
||||
* @param start True to request RTP start
|
||||
* @param pick True to update local parameters (other then media) from returned message
|
||||
* @param context Pointer to user provided context, optional
|
||||
* @return True if the message was succesfully handled
|
||||
*/
|
||||
bool dispatchRtp(SDPMedia* media, const char* addr, bool start, bool pick, RefObject* context = 0);
|
||||
|
||||
/**
|
||||
* Calls dispatchRtp() for each media in the list
|
||||
* Update it on success. Remove it on failure
|
||||
* @param addr Remote RTP address
|
||||
* @param start True to request RTP start
|
||||
* @param context Pointer to user provided context, optional
|
||||
* @return True if the message was succesfully handled for at least one media
|
||||
*/
|
||||
bool dispatchRtp(const char* addr, bool start, RefObject* context = 0);
|
||||
|
||||
/**
|
||||
* Try to start RTP (calls dispatchRtp()) for each media in the list
|
||||
* @param context Pointer to user provided context, optional
|
||||
* @return True if at least one media was started
|
||||
*/
|
||||
bool startRtp(RefObject* context = 0);
|
||||
|
||||
/**
|
||||
* Update from parameters. Build a default SDP from parser formats if no media is found in params
|
||||
* @param params List of parameters to update from
|
||||
* @return True if media changed
|
||||
*/
|
||||
bool updateSDP(const NamedList& params);
|
||||
|
||||
/**
|
||||
* Update RTP/SDP data from parameters
|
||||
* @param params List of parameters to update from
|
||||
* @return True if media or local address changed
|
||||
*/
|
||||
bool updateRtpSDP(const NamedList& params);
|
||||
|
||||
/**
|
||||
* Creates a SDP body from transport address and list of media descriptors
|
||||
* @param addr The address to set. Use own host if empty
|
||||
* @param mediaList Optional media list. Use own list if the given one is 0
|
||||
* @return MimeSdpBody pointer or 0 if there is no media to set
|
||||
*/
|
||||
MimeSdpBody* createSDP(const char* addr, ObjList* mediaList = 0);
|
||||
|
||||
/**
|
||||
* Creates a SDP body for current media status
|
||||
* @return MimeSdpBody pointer or 0 if media is missing
|
||||
*/
|
||||
MimeSdpBody* createSDP();
|
||||
|
||||
/**
|
||||
* Creates a SDP from RTP address data present in message.
|
||||
* Use the raw SDP if present.
|
||||
* @param msg The list of parameters
|
||||
* @param update True to update RTP/SDP data if raw SDP is not found in the list
|
||||
* @return MimeSdpBody pointer or 0
|
||||
*/
|
||||
MimeSdpBody* createPasstroughSDP(NamedList& msg, bool update = true);
|
||||
|
||||
/**
|
||||
* Creates a set of unstarted external RTP channels from remote addr and
|
||||
* builds SDP from them
|
||||
* @param addr Remote RTP address used when dispatching the chan.rtp message
|
||||
* @param msg List of parameters used to update data
|
||||
* @return MimeSdpBody pointer or 0
|
||||
*/
|
||||
inline MimeSdpBody* createRtpSDP(const char* addr, const NamedList& msg)
|
||||
{ updateSDP(msg); return createRtpSDP(addr,false); }
|
||||
|
||||
/**
|
||||
* Creates a set of RTP channels from address and media info and builds SDP from them
|
||||
* @param addr Remote RTP address used when dispatching the chan.rtp message
|
||||
* @param start True to create a started RTP
|
||||
* @return MimeSdpBody pointer or 0
|
||||
*/
|
||||
inline MimeSdpBody* createRtpSDP(const char* addr, bool start)
|
||||
{ return dispatchRtp(addr,start) ? createSDP(getRtpAddr()) : 0; }
|
||||
|
||||
/**
|
||||
* Creates a set of started external RTP channels from remote addr and
|
||||
* builds SDP from them
|
||||
* @param start True to create a started RTP
|
||||
* @return MimeSdpBody pointer or 0
|
||||
*/
|
||||
inline MimeSdpBody* createRtpSDP(bool start)
|
||||
{
|
||||
if (m_rtpAddr.null()) {
|
||||
m_mediaStatus = MediaMuted;
|
||||
return createSDP(0);
|
||||
}
|
||||
return createRtpSDP(m_rtpAddr,start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update media format lists from parameters
|
||||
* @param msg Parameter list
|
||||
*/
|
||||
void updateFormats(const NamedList& msg);
|
||||
|
||||
/**
|
||||
* Add raw SDP forwarding parameter from body if SDP forward is enabled
|
||||
* @param msg Destination list
|
||||
* @param body Mime body to process
|
||||
* @return True if the parameter was added
|
||||
*/
|
||||
bool addSdpParams(NamedList& msg, const MimeBody* body);
|
||||
|
||||
/**
|
||||
* Add raw SDP forwarding parameter if SDP forward is enabled
|
||||
* @param msg Destination list
|
||||
* @param rawSdp The raw sdp content
|
||||
* @return True if the parameter was added
|
||||
*/
|
||||
bool addSdpParams(NamedList& msg, const String& rawSdp);
|
||||
|
||||
/**
|
||||
* Add RTP forwarding parameters to a message (media and address)
|
||||
* @param msg Destination list
|
||||
* @param natAddr Optional NAT address if detected
|
||||
* @param body Pointer to the body to extract raw SDP from
|
||||
* @param force True to override RTP forward flag
|
||||
* @return True if RTP data was added. Media is always added if present and
|
||||
* remote address is not empty
|
||||
*/
|
||||
bool addRtpParams(NamedList& msg, const String& natAddr = String::empty(),
|
||||
const MimeBody* body = 0, bool force = false);
|
||||
|
||||
/**
|
||||
* Reset this object to default values
|
||||
*/
|
||||
virtual void resetSdp();
|
||||
|
||||
/**
|
||||
* Build a chan.rtp message and populate with media information
|
||||
* @param media The media list
|
||||
* @param addr Remote RTP address
|
||||
* @param start True to request RTP start
|
||||
* @param context Pointer to reference counted user provided context
|
||||
* @return The message with media information, NULL if media or addr are missing
|
||||
*/
|
||||
virtual Message* buildChanRtp(SDPMedia* media, const char* addr, bool start, RefObject* context);
|
||||
|
||||
/**
|
||||
* Build a chan.rtp message without media information
|
||||
* @param context Pointer to reference counted user provided context
|
||||
* @return The message with user data set but no media information
|
||||
*/
|
||||
virtual Message* buildChanRtp(RefObject* context) = 0;
|
||||
|
||||
/**
|
||||
* Check if local RTP data changed for at least one media
|
||||
* @return True if local RTP data changed for at least one media
|
||||
*/
|
||||
bool localRtpChanged() const;
|
||||
|
||||
/**
|
||||
* Set or reset the local RTP data changed flag for all media
|
||||
* @param chg The new value for local RTP data changed flag of all media
|
||||
*/
|
||||
void setLocalRtpChanged(bool chg = false);
|
||||
|
||||
/**
|
||||
* Update RTP/SDP data from parameters
|
||||
* @param params Parameter list
|
||||
* @param rtpAddr String to be filled with rtp address from the list
|
||||
* @param oldList Optional existing media list (found media will be removed
|
||||
* from it and added to the returned list
|
||||
* @return List of media or 0 if not found or rtpAddr is empty
|
||||
*/
|
||||
static ObjList* updateRtpSDP(const NamedList& params, String& rtpAddr,
|
||||
ObjList* oldList = 0);
|
||||
|
||||
SDPParser* m_parser;
|
||||
int m_mediaStatus;
|
||||
bool m_rtpForward; // Forward RTP flag
|
||||
bool m_sdpForward; // Forward SDP (only if RTP is forwarded)
|
||||
String m_externalAddr; // Our external IP address, possibly outside of a NAT
|
||||
String m_rtpAddr; // Remote RTP address
|
||||
String m_rtpLocalAddr; // Local RTP address
|
||||
ObjList* m_rtpMedia; // List of media descriptors
|
||||
int m_sdpSession; // Unique SDP session number
|
||||
int m_sdpVersion; // SDP version number, incremented each time we generate a new SDP
|
||||
String m_host;
|
||||
bool m_secure;
|
||||
bool m_rfc2833; // Offer RFC 2833 to remote party
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Media changed notification.
|
||||
* This method is called when setting new media and an old one changed
|
||||
* @param name Changed media name
|
||||
*/
|
||||
virtual void mediaChanged(const String& name);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds a SDP parser and additional data used by SDP objects
|
||||
* @short A SDP parser
|
||||
*/
|
||||
class YSDP_API SDPParser : public DebugEnabler, public Mutex
|
||||
{
|
||||
friend class SDPSession;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param dbgName Debug name of this parser
|
||||
* @param sessName Name of the session in SDP
|
||||
* @param fmts Default media formats
|
||||
*/
|
||||
inline SDPParser(const char* dbgName, const char* sessName, const char* fmts = "alaw,mulaw")
|
||||
: Mutex(true,"SDPParser"), m_sdpForward(false),
|
||||
m_rfc2833(true), m_secure(false), m_ignorePort(false),
|
||||
m_sessionName(sessName), m_audioFormats(fmts),
|
||||
m_codecs(""), m_hacks("")
|
||||
{ debugName(dbgName); }
|
||||
|
||||
/**
|
||||
* Get the formats list
|
||||
* This method is thread safe
|
||||
* @param buf String to be filled with comma separated list of formats
|
||||
*/
|
||||
inline void getAudioFormats(String& buf)
|
||||
{ Lock lock(this); buf = m_audioFormats; }
|
||||
|
||||
/**
|
||||
* Get the RFC 2833 offer flag
|
||||
* @return True if RFC 2883 telephony events will be offered
|
||||
*/
|
||||
inline bool rfc2833() const
|
||||
{ return m_rfc2833; }
|
||||
|
||||
/**
|
||||
* Get the secure offer flag
|
||||
* @return True if SDES descriptors for SRTP will be offered
|
||||
*/
|
||||
inline bool secure() const
|
||||
{ return m_secure; }
|
||||
|
||||
/**
|
||||
* Get the SDP forward flag
|
||||
* @return True if raw SDP should be added to RTP forward offer
|
||||
*/
|
||||
inline bool sdpForward() const
|
||||
{ return m_sdpForward; }
|
||||
|
||||
/**
|
||||
* Get the RTP port change ignore flag
|
||||
* @return True if a port change should not cause an offer change
|
||||
*/
|
||||
inline bool ignorePort() const
|
||||
{ return m_ignorePort; }
|
||||
|
||||
/**
|
||||
* Parse a received SDP body
|
||||
* This method is thread safe
|
||||
* @param sdp Received SDP body
|
||||
* @param addr Remote address
|
||||
* @param oldMedia Optional list of existing media (an already existing media
|
||||
* will be moved to returned list)
|
||||
* @param media Optional expected media type. If not empty this will be the
|
||||
* only media type returned (if found)
|
||||
* @return List of SDPMedia objects, may be NULL
|
||||
*/
|
||||
ObjList* parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedia = 0,
|
||||
const String& media = String::empty());
|
||||
|
||||
/**
|
||||
* Parse a received SDP body, returns NULL if SDP is not present
|
||||
* This method is thread safe
|
||||
* @param sdp Pointer to received SDP body
|
||||
* @param addr Remote address
|
||||
* @param oldMedia Optional list of existing media (an already existing media
|
||||
* will be moved to returned list)
|
||||
* @param media Optional expected media type. If not empty this will be the
|
||||
* only media type returned (if found)
|
||||
* @return List of SDPMedia objects, may be NULL
|
||||
*/
|
||||
inline ObjList* parse(const MimeSdpBody* sdp, String& addr, ObjList* oldMedia = 0,
|
||||
const String& media = String::empty())
|
||||
{ return sdp ? parse(*sdp,addr,oldMedia,media) : 0; }
|
||||
|
||||
/**
|
||||
* Update configuration. This method should be called after a configuration file is loaded
|
||||
* @param codecs List of supported codecs
|
||||
* @param hacks List of hacks
|
||||
* @param general List of general settings
|
||||
*/
|
||||
void initialize(const NamedList* codecs, const NamedList* hacks, const NamedList* general = 0);
|
||||
|
||||
/**
|
||||
* Yate Payloads for the AV profile
|
||||
*/
|
||||
static const TokenDict s_payloads[];
|
||||
|
||||
/**
|
||||
* SDP Payloads for the AV profile
|
||||
*/
|
||||
static const TokenDict s_rtpmap[];
|
||||
|
||||
private:
|
||||
bool m_sdpForward; // Include raw SDP for forwarding
|
||||
bool m_rfc2833; // Offer RFC 2833 to remote party
|
||||
bool m_secure; // Offer SRTP
|
||||
bool m_ignorePort; // Ignore port only changes in SDP
|
||||
String m_sessionName;
|
||||
String m_audioFormats; // Default audio formats to be advertised to remote party
|
||||
NamedList m_codecs; // Codecs configuration list
|
||||
NamedList m_hacks; // Various potentially standard breaking settings
|
||||
};
|
||||
|
||||
}; // namespace TelEngine
|
||||
|
||||
#endif /* __YATESDP_H */
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
Loading…
Reference in New Issue