Import commercial version of YATE SIP and SDP implementation.

This commit is contained in:
Ioana Stanciu 2023-05-23 15:16:31 +03:00
parent 3539304e63
commit afc46246ca
13 changed files with 1068 additions and 169 deletions

View File

@ -155,6 +155,9 @@
; OBSOLETE - please use "enable" in section [options]
;options=enable
; update: bool: Enable receiving UPDATE transactions (RFC 3311)
;update=disable
; prack: bool: Enable acknowledging provisional 1xx answers (RFC 3262)
;prack=disable
@ -247,10 +250,15 @@
; Defaults to enable
;honor_dtmf_detect=enable
; rfc2833: bool: Offer RFC2833 telephone-event by default
; rfc2833: bool: Offer RFC2833 telephone-event 8KHz by default
; A numeric payload >= 96 can be provided
;rfc2833=yes
; rfc2833_RATE: bool: Offer RFC2833 telephone-event for specific rate (non 8KHz) by default
; A numeric payload >= 96 can be provided
; Supported rates (parameters): rfc2833_16000, rfc2833_32000
;rfc2833_RATE=yes
; privacy: bool: Process and generate privacy related SIP headers
;privacy=disable
@ -260,6 +268,9 @@
; forward_sdp: bool: Include the raw SDP body to be used as-is for forwarding RTP
;forward_sdp=disable
; forward_gpmd: bool: Propagate GPMD even when not forwarding RTP
;forward_gpmd=disable
; rtp_start: bool: Start RTP when sending 200 on incoming instead of receiving ACK
;rtp_start=disable
@ -370,12 +381,36 @@
; If set this parameter must be less than 'tcp_keepalive'
;tcp_keepalive_first=0
; ssdp_prefix: string: Prefix to use when handling SDP session level parameters
; This parameter is used when setting them in yate messages or handling them from there
; This parameter is applied on reload
; Prefix used to set parsed SDP: <ssdp_prefix>_ (default: ssdp_)
; Prefix used to update from yate messages: o<ssdp_prefix>_ (default: ossdp_)
; When updated from yate messages the prefix must be set in 'ossdp-prefix' message parameter
;ssdp_prefix=ssdp
; initial_headers: boolean: Put all headers from initial requests in yate message
; Handled for incoming channel preroute, user (un)register and messages sent on SIP
; requests received outside a dialog
; This parameter is applied on reload
;initial_headers=no
; reinvite_wait_initial: boolean: Wait for answered initial transaction termination when need to send
; a re-INVITE and initial transaction was not terminated
; Applicable for the inbound call leg
; This parameter is handled when answer (200 OK) was sent to initial transaction
; If enabled the module will not send an UPDATE even if supported by remote
; This parameter can be overridden from routing
; This parameter is applied on reload
;reinvite_wait_initial=no
; mixed_provisional: boolean: Accept mixed (non)reliable provisional responses to initial transaction
; When enabled (default) the dialog will accept non reliable provisional messages
; after receiving a reliable one
; This parameter can be overridden from routing
; This parameter is applied on reload
;mixed_provisional=yes
; warn_bind_fail_delay: integer/string: Delay failed to bind debug message
; This parameter may be used when listener is going to bind on an IP which may become
; available later

View File

@ -6,3 +6,4 @@ core*
*.orig
*~
.*.swp

View File

@ -5,7 +5,7 @@
* SDP media handling
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
@ -23,16 +23,24 @@
namespace TelEngine {
const TokenDict SDPMedia::s_sdpDir[] = {
{"sendrecv", DirBidir},
{"sendonly", DirSend},
{"recvonly", DirRecv},
{"inactive", DirInactive},
{0, 0},
};
/*
* SDPMedia
*/
SDPMedia::SDPMedia(const char* media, const char* transport, const char* formats,
int rport, int lport)
: NamedList(media),
m_audio(true), m_video(false), m_modified(false), m_securable(true),
m_audio(true), m_video(false), m_modified(false), m_securable(true), m_haveRfc3833(false),
m_localChanged(false),
m_transport(transport), m_formats(formats),
m_rfc2833(String::boolText(false))
m_lDir(0), m_rDir(0)
{
DDebug(DebugAll,"SDPMedia::SDPMedia('%s','%s','%s',%d,%d) [%p]",
media,transport,formats,rport,lport,this);
@ -103,10 +111,10 @@ bool SDPMedia::sameAs(const SDPMedia* other, bool ignorePort, bool checkStarted)
}
// Check RFC 2833
if (m_rfc2833 != m.m_rfc2833) {
if (m_rfc2833.payload(m_format) != m.m_rfc2833.payload(m_format)) {
XDebug(DebugAll,
"SDPMedia::sameAs(%p) rfc2833=%s other_rfc2833=%s: not matched [%p]",
other,m_rfc2833.c_str(),m.m_rfc2833.c_str(),this);
"SDPMedia::sameAs(%p) format='%s' rfc2833=%d other_rfc2833=%d: not matched [%p]",
other,m_format.c_str(),m_rfc2833.payload(m_format),m.m_rfc2833.payload(m_format),this);
return false;
}
@ -231,6 +239,34 @@ void SDPMedia::crypto(const char* desc, bool remote)
m_securable = false;
}
void SDPMedia::direction(int value, bool remote)
{
int& v = remote ? m_rDir : m_lDir;
if (v == value)
return;
DDebug(DebugAll,"SDPMedia set %s direction %s -> %s [%p]",remote ? "remote" : "local",
lookup(v,s_sdpDir),lookup(value,s_sdpDir),this);
v = value;
}
// Retrieve negotiated media direction to be sent to remote or set in RTP
int SDPMedia::direction(int sessLDir)
{
int dir = m_lDir ? m_lDir : sessLDir;
if (!m_rDir || dir)
return dir;
switch (m_rDir) {
case DirBidir:
case DirInactive:
return m_rDir;
case DirSend:
return DirRecv;
case DirRecv:
return DirSend;
}
return 0;
}
// Put the list of net media in a parameter list
void SDPMedia::putMedia(NamedList& msg, bool putPort)
{
@ -240,7 +276,7 @@ void SDPMedia::putMedia(NamedList& msg, bool putPort)
if (mappings())
msg.addParam("rtp_mapping" + suffix(),mappings());
if (isAudio())
msg.addParam("rtp_rfc2833",rfc2833());
m_rfc2833.put(msg);
if (putPort)
msg.addParam("rtp_port" + suffix(),remotePort());
if (remoteCrypto())
@ -261,6 +297,7 @@ void SDPMedia::putMedia(NamedList& msg, bool putPort)
// Copy RTP related data from old media
void SDPMedia::keepRtp(const SDPMedia& other)
{
m_haveRfc3833 = other.m_haveRfc3833;
m_formats = other.m_formats;
m_format = other.m_format;
m_rfc2833 = other.m_rfc2833;

View File

@ -5,7 +5,7 @@
* SDP media handling
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
@ -23,6 +23,13 @@
namespace TelEngine {
// RFC 2833 default payloads
#define RFC2833_8khz 101
#define RFC2833_16khz 108
#define RFC2833_32khz 109
static Rfc2833 s_rfc2833;
/*
* SDPParser
*/
@ -60,6 +67,7 @@ const TokenDict SDPParser::s_payloads[] = {
{ "h263-1998", 111 },
{ "h263-2000", 112 },
{ "h264", 114 },
{ "h265", 116 },
{ "vp8", 113 },
{ "vp9", 115 },
{ "mpv", 32 },
@ -104,6 +112,7 @@ const TokenDict SDPParser::s_rtpmap[] = {
{ "H263-1998/90000", 111 },
{ "H263-2000/90000", 112 },
{ "H264/90000", 114 },
{ "H265/90000", 116 },
{ "VP8/90000", 113 },
{ "VP9/90000", 115 },
{ "MPV/90000", 32 },
@ -133,6 +142,76 @@ static const TokenDict s_sdpFmtParamsCheck[] = {
{ 0, 0 },
};
const String Rfc2833::s_rates[RateCount] = {"8000", "16000", "32000"};
void Rfc2833::update(const NamedList& params, const Rfc2833& defaults, bool force, const String& param)
{
String pref = param.safe("rfc2833");
for (int i = 0; i < RateCount; ++i) {
const String* p = params.getParam((i == Rate8khz) ? pref : pref + "_" + s_rates[i]);
if (p)
update(i,*p,defaults);
else if (force)
m_payloads[i] = defaults[i];
}
}
// Update payload for specific rate
void Rfc2833::update(int rate, const String& value, const Rfc2833& defaults)
{
if (rate >= RateCount)
return;
if (value.toBoolean(true)) {
int val = value.toInteger();
if (96 <= val && val <= 127)
m_payloads[rate] = val;
else
m_payloads[rate] = defaults[rate];
}
else
m_payloads[rate] = -1;
}
void Rfc2833::put(NamedList& params, const String& param) const
{
String pref = param.safe("rtp_rfc2833");
for (int i = 0; i < RateCount; ++i) {
if (m_payloads[i] >= 0) {
if (i == Rate8khz)
params.addParam(pref,String(m_payloads[i]));
else
params.addParam(pref + "_" + s_rates[i],String(m_payloads[i]));
}
else if (i == Rate8khz)
params.addParam(pref,String::boolText(false));
}
}
String& Rfc2833::dump(String& buf) const
{
String tmp;
for (int i = 0; i < RateCount; ++i) {
if (m_payloads[i] >= 0)
tmp.append(s_rates[i] + "=" + String(m_payloads[i]),",");
}
return buf.append(tmp);
}
// Select RFC 2833 rate for given media format
int Rfc2833::fmtRate(const String& fmt)
{
int pos = fmt.find('/');
if (pos <= 0)
return Rate8khz;
int r = rate(fmt.substr(pos + 1));
// G722 uses 8KHz
if (r != Rate8khz && fmt.substr(0,pos) == YSTRING("g722"))
return Rate8khz;
return r;
}
// Utility used in SDPParser::parse
// Retrieve a SDP line 'param<payload>' contents
// Trim contents spaces
@ -156,7 +235,7 @@ static inline String& getPayloadLine(String& buf, ObjList& list, int payload, co
// Parse a received SDP body
ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedia,
const String& media, bool force)
const String& media, bool force, bool handleDir)
{
DDebug(DebugAll,"SDPParser::parse(%p,%s,%p,'%s',%s)",
&sdp,addr.c_str(),oldMedia,media.safe(),String::boolText(force));
@ -178,6 +257,20 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
addr = tmp;
}
}
// Obtain session level direction
int sessRDir = 0;
if (handleDir) {
for (const ObjList* o = sdp.lines().skipNull(); o; o = o->skipNext()) {
const NamedString* l = static_cast<const NamedString*>(o->get());
if (l->name() == YSTRING("m"))
break;
if (l->name() != YSTRING("a"))
continue;
SDPMedia::setDirection(sessRDir,*l);
if (sessRDir)
break;
}
}
Lock lock(this);
ObjList* lst = 0;
bool defcodecs = m_codecs.getBoolValue("default",true);
@ -220,7 +313,9 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
ObjList* dest = &params;
bool first = true;
int ptime = 0;
int rfc2833 = -1;
Rfc2833 rfc2833;
Rfc2833 mediaAvailable;
int dir = sessRDir;
// Remember format related lines
ObjList fmtLines;
ObjList* fmtA = &fmtLines;
@ -291,7 +386,9 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
continue;
}
if (line.startsWith("TELEPHONE-EVENT/")) {
rfc2833 = var;
int rate = Rfc2833::rate(line.substr(16));
if (rate < Rfc2833::RateCount)
rfc2833[rate] = var;
payload.clear();
continue;
}
@ -323,10 +420,15 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
int pos = line.find(':');
if (pos >= 0)
dest = dest->append(new NamedString(line.substr(0,pos),line.substr(pos+1)));
else
else {
dest = dest->append(new NamedString(line));
if (handleDir)
SDPMedia::setDirection(dir,line);
}
}
}
else if (handleDir)
SDPMedia::setDirection(dir,line);
}
if (var < 0)
break;
@ -432,10 +534,24 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
dest = dest->append(new NamedString("fmtp:" + payload,fmtp));
if ((payload == "g729") && m_hacks.getBoolValue(YSTRING("g729_annexb"),annexB))
aux << ",g729b";
int r = Rfc2833::fmtRate(payload);
if (r < Rfc2833::RateCount)
mediaAvailable[r] = 1;
}
}
fmt += aux;
DDebug(this,DebugAll,"Formats '%s' mappings '%s'",fmt.c_str(),mappings.c_str());
// Change supported RFC 2833 from available media rates
for (int i = 0; i < Rfc2833::RateCount; ++i)
if (mediaAvailable[i] < 0)
rfc2833[i] = -1;
#ifdef DEBUG
String extraD;
if (type == YSTRING("audio")) {
String tmp;
extraD << " RFC 2833: " << rfc2833.dump(tmp);
}
DDebug(this,DebugAll,"Formats '%s' mappings '%s'%s",fmt.c_str(),mappings.c_str(),extraD.safe());
#endif
SDPMedia* net = 0;
// try to take the media descriptor from the old list
if (oldMedia) {
@ -456,6 +572,7 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
net->mappings(mappings);
net->rfc2833(rfc2833);
net->crypto(crypto,true);
net->direction(dir,true);
if (!lst)
lst = new ObjList;
lst->append(net);
@ -469,7 +586,13 @@ ObjList* SDPParser::parse(const MimeSdpBody& sdp, String& addr, ObjList* oldMedi
// Update configuration
void SDPParser::initialize(const NamedList* codecs, const NamedList* hacks, const NamedList* general)
{
const NamedList& safeGen = general ? *general : NamedList::empty();
Lock lock(this);
if (s_rfc2833[Rfc2833::Rate8khz] < 0) {
s_rfc2833[Rfc2833::Rate8khz] = RFC2833_8khz;
s_rfc2833[Rfc2833::Rate16khz] = RFC2833_16khz;
s_rfc2833[Rfc2833::Rate32khz] = RFC2833_32khz;
}
m_codecs.clearParams();
m_hacks.clearParams();
if (codecs)
@ -497,20 +620,18 @@ void SDPParser::initialize(const NamedList* codecs, const NamedList* hacks, cons
m_audioFormats.c_str());
}
m_ignorePort = m_hacks.getBoolValue("ignore_sdp_port",false);
m_rfc2833 = 101;
m_rfc2833.update(safeGen,s_rfc2833);
String tmp;
Debug(this,DebugAll,"Initialized RFC 2833: %s",m_rfc2833.dump(tmp).c_str());
m_secure = false;
m_gpmd = false;
m_sdpForward = false;
if (general) {
if (general->getBoolValue("rfc2833",true)) {
m_rfc2833 = general->getIntValue("rfc2833",m_rfc2833);
if (m_rfc2833 < 96 || m_rfc2833 > 127)
m_rfc2833 = 101;
}
else
m_rfc2833 = -1;
m_secure = general->getBoolValue("secure",m_secure);
m_gpmd = general->getBoolValue("forward_gpmd",m_gpmd);
m_sdpForward = general->getBoolValue("forward_sdp",m_sdpForward);
}
m_ssdpParam = general ? general->getValue(YSTRING("ssdp_prefix"),"ssdp") : "ssdp";
}
}; // namespace TelEngine

View File

@ -5,7 +5,7 @@
* SDP media handling
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
@ -31,7 +31,9 @@ SDPSession::SDPSession(SDPParser* parser)
m_rtpForward(false), m_sdpForward(parser->sdpForward()), m_rtpMedia(0),
m_sdpSession(0), m_sdpVersion(0), m_sdpHash(YSTRING_INIT_HASH),
m_secure(m_parser->m_secure), m_rfc2833(m_parser->m_rfc2833),
m_ipv6(false), m_amrExtra(""), m_enabler(0), m_ptr(0)
m_ipv6(false), m_gpmd(parser->m_gpmd),
m_amrExtra(""), m_parsedParams(0), m_createSdpParams(""),
m_enabler(0), m_ptr(0)
{
setSdpDebug();
}
@ -40,14 +42,17 @@ SDPSession::SDPSession(SDPParser* parser, NamedList& params)
: m_parser(parser), m_mediaStatus(MediaMissing),
m_rtpForward(false), m_sdpForward(parser->sdpForward()), m_rtpMedia(0),
m_sdpSession(0), m_sdpVersion(0), m_sdpHash(YSTRING_INIT_HASH),
m_ipv6(false), m_amrExtra(""), m_enabler(0), m_ptr(0)
m_ipv6(false), m_gpmd(false),
m_amrExtra(""), m_parsedParams(0), m_createSdpParams(""),
m_enabler(0), m_ptr(0)
{
setSdpDebug();
m_rtpForward = params.getBoolValue("rtp_forward");
m_rtpForward = params.getBoolValue(YSTRING("rtp_forward"));
m_sdpForward = params.getBoolValue(YSTRING("forward_sdp"),m_sdpForward);
m_secure = params.getBoolValue("secure",parser->m_secure);
m_secure = params.getBoolValue(YSTRING("secure"),parser->m_secure);
m_gpmd = params.getBoolValue(YSTRING("forward_gpmd"),parser->m_gpmd);
m_rfc2833 = parser->m_rfc2833;
setRfc2833(params.getParam("rfc2833"));
setRfc2833(params,false);
}
SDPSession::~SDPSession()
@ -91,8 +96,15 @@ bool SDPSession::setMedia(ObjList* media, bool preserveExisting)
}
// Put the list of net media in a parameter list
void SDPSession::putMedia(NamedList& msg, ObjList* mList, bool putPort)
void SDPSession::putMedia(NamedList& msg, ObjList* mList, bool putPort, const NamedList* sessParams)
{
if (sessParams) {
const char* prefix = sessParams->safe("ssdp_");
for (ObjList* o = sessParams->paramList()->skipNull(); o; o = o->skipNext()) {
const NamedString* ns = static_cast<const NamedString*>(o->get());
msg.addParam(prefix + ns->name(),*ns);
}
}
if (!mList)
return;
bool audio = false;
@ -110,15 +122,19 @@ void SDPSession::putMedia(NamedList& msg, ObjList* mList, bool putPort)
}
// Update the RFC 2833 availability and payload
void SDPSession::setRfc2833(const String& value)
void SDPSession::setRfc2833(const String& value, int rate)
{
if (value.toBoolean(true)) {
m_rfc2833 = value.toInteger(m_parser->m_rfc2833);
if (m_rfc2833 < 96 || m_rfc2833 > 127)
m_rfc2833 = value.toBoolean(false) ? 101 : m_parser->m_rfc2833;
}
else
m_rfc2833 = -1;
m_rfc2833.update(rate,value,m_parser->m_rfc2833);
}
// Update the RFC 2833 availability and payload
void SDPSession::setRfc2833(const NamedList& params, bool force)
{
m_rfc2833.update(params,m_parser->m_rfc2833,force);
#ifdef DEBUG
String tmp;
TraceDebug(m_traceId,m_enabler,DebugAll,"Updated RFC 2833: %s [%p]",m_rfc2833.dump(tmp).c_str(),m_ptr);
#endif
}
// Build and dispatch a chan.rtp message for a given media. Update media on success
@ -147,6 +163,7 @@ bool SDPSession::dispatchRtp(SDPMedia* media, const char* addr, bool start,
if (!sdpPrefix.endsWith("_"))
sdpPrefix += "_";
unsigned int n = m->length();
int dir = SDPMedia::DirUnknown;
for (unsigned int j = 0; j < n; j++) {
const NamedString* param = m->getParam(j);
if (!param)
@ -157,8 +174,10 @@ bool SDPSession::dispatchRtp(SDPMedia* media, const char* addr, bool start,
"Updating (from RTP message) %s parameter '%s' to '%s' [%p]",
media->c_str(),tmp.c_str(),param->c_str(),this);
media->parameter(tmp,*param,false);
SDPMedia::setDirection(dir,tmp);
}
}
media->direction(dir,false);
}
if (m_secure) {
int tag = m->getIntValue("crypto_tag",1);
@ -225,6 +244,7 @@ bool SDPSession::updateSDP(const NamedList& params, bool defaults)
DDebug(m_enabler,DebugAll,"SDPSession::updateSdp('%s',%s) [%p]",
params.c_str(),String::boolText(defaults),m_ptr);
const char* sdpPrefix = params.getValue("osdp-prefix","osdp");
updateSessionParams(params);
ObjList* lst = 0;
unsigned int n = params.length();
String defFormats;
@ -309,6 +329,7 @@ bool SDPSession::updateRtpSDP(const NamedList& params)
String addr;
ObjList* tmp = updateRtpSDP(params,addr,m_rtpMedia);
if (tmp) {
updateSessionParams(params);
bool chg = (m_rtpLocalAddr != addr);
m_rtpLocalAddr = addr;
return setMedia(tmp) || chg;
@ -387,21 +408,21 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
sdp->addLine("t","0 0");
Lock lock(m_parser);
addSdpParams(sdp,m_createSdpParams);
int sessDir = 0;
// TODO: Add session level direction
bool defcodecs = m_parser->m_codecs.getBoolValue("default",true);
for (ObjList* ml = mList->skipNull(); ml; ml = ml->skipNext()) {
SDPMedia* m = static_cast<SDPMedia*>(ml->get());
int rfc2833 = 0;
if ((m_rfc2833 >= 0) && m->isAudio()) {
Rfc2833 rfc2833;
if (m->isAudio()) {
if (!m_rtpForward) {
rfc2833 = m->rfc2833().toInteger(m_rfc2833);
if (rfc2833 < 96 || rfc2833 > 127)
rfc2833 = 101;
}
else if (m->rfc2833().toBoolean(true)) {
rfc2833 = m->rfc2833().toInteger();
if (rfc2833 < 96 || rfc2833 > 127)
rfc2833 = 0;
const Rfc2833& mr = m->rfc2833();
for (int i = 0; i < Rfc2833::RateCount; ++i)
rfc2833[i] = (mr[i] >= 0) ? mr[i] : m_rfc2833[i];
}
else
rfc2833 = m->rfc2833();
}
String mline(m->fmtList());
ObjList* l = mline.split(',',false);
@ -413,6 +434,7 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
String frm;
int ptime = 0;
ObjList* f = l;
Rfc2833 rfc2833Send;
for (; f; f = f->next()) {
const String* s = static_cast<const String*>(f->get());
if (s) {
@ -467,7 +489,7 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
}
// allocate free and non-standard is possible
for (pload = 96; pload < 127; pload++) {
if (pload == rfc2833)
if (rfc2833.includes(pload))
continue;
if (lookup(pload,SDPParser::s_rtpmap))
continue;
@ -480,7 +502,7 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
break;
// none free, allocate from "standard" ones too
for (pload = 96; pload < 127; pload++) {
if (pload == rfc2833)
if (rfc2833.includes(pload))
continue;
if ((bmap & (1 << (pload - 96))) == 0) {
payload = pload;
@ -500,6 +522,12 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
defcode = payload;
const char* map = lookup(defcode,SDPParser::s_rtpmap);
if (map && m_parser->m_codecs.getBoolValue(*s,defcodecs && DataTranslator::canConvert(*s))) {
// Update RFC 2833 support to advertise
if (m->isAudio()) {
int r = Rfc2833::fmtRate(*s);
if (r < Rfc2833::RateCount)
rfc2833Send[r] = rfc2833[r];
}
frm << " " << payload;
String* temp = new String("rtpmap:");
*temp << payload << " " << map;
@ -536,7 +564,7 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
setFmtpLine(temp,payload,*fmtp);
if (temp)
dest = dest->append(temp);
if (mediaList) {
if (mediaList || m_gpmd) {
// RTP forward propagates General Purpose Media Descriptor
const String* gpmd = m->getParam("gpmd:" + *s);
if (gpmd) {
@ -552,12 +580,15 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
TelEngine::destruct(l);
TelEngine::destruct(map);
if (rfc2833 && frm) {
// claim to support telephone events
frm << " " << rfc2833;
String* s = new String;
*s << "rtpmap:" << rfc2833 << " telephone-event/8000";
dest = dest->append(s);
if (frm && m->isAudio()) {
for (int i = 0; i < Rfc2833::RateCount; ++i) {
if (rfc2833Send[i] < 0)
continue;
frm << " " << rfc2833Send[i];
String* s = new String;
s->printf("rtpmap:%d telephone-event/%s",rfc2833Send[i],Rfc2833::rateValue(i).c_str());
dest = dest->append(s);
}
}
if (frm.null()) {
@ -584,26 +615,9 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
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 && (param->name().find(':') < 0)) {
const char* type = "a";
String tmp = param->name();
if (tmp.startSkip("BW-",false)) {
if (!tmp)
continue;
type = "b";
}
else
enc = enc || (tmp == "encryption");
if (*param)
tmp << ":" << *param;
sdp->addLine(type,tmp);
}
}
}
bool addedDir = false;
if (m->isModified())
addSdpParams(sdp,*m,&enc,&addedDir);
for (f = rtpmap.skipNull(); f; f = f->skipNext()) {
String* s = static_cast<String*>(f->get());
if (s)
@ -614,6 +628,11 @@ MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList)
if (!enc)
sdp->addLine("a","encryption:optional");
}
if (!addedDir) {
const char* dir = lookup(m->direction(sessDir),SDPMedia::s_sdpDir);
if (dir)
sdp->addLine("a",dir);
}
}
// increment version if body hash changed
if ((YSTRING_INIT_HASH != m_sdpHash) && (sdp->hash() != m_sdpHash))
@ -655,6 +674,7 @@ MimeSdpBody* SDPSession::createPasstroughSDP(NamedList& msg, bool update,
msg.setParam("rtp_forward","accepted");
return new MimeSdpBody("application/sdp",raw->safe(),raw->length());
}
updateSessionParams(msg);
String addr;
ObjList* lst = updateRtpSDP(msg,addr,update ? m_rtpMedia : 0,allowEmptyAddr);
if (!lst)
@ -813,7 +833,7 @@ bool SDPSession::addRtpParams(NamedList& msg, const String& natAddr,
SDPMedia* m = static_cast<SDPMedia*>(o->get());
msg.addParam("rtp_port" + m->suffix(),m->remotePort());
if (m->isAudio())
msg.addParam("rtp_rfc2833",m->rfc2833());
m->rfc2833().put(msg);
}
addSdpParams(msg,body);
return true;
@ -836,8 +856,11 @@ void SDPSession::resetSdp(bool all)
m_host.clear();
if (all) {
m_secure = m_parser->secure();
m_gpmd = m_parser->gpmd();
m_rfc2833 = m_parser->rfc2833();
}
setSessionParams(0);
m_createSdpParams.clearParams();
}
// Build a populated chan.rtp message
@ -864,7 +887,11 @@ Message* SDPSession::buildChanRtp(SDPMedia* media, const char* addr, bool start,
int payload = SDPMedia::payloadMapping(media->mappings(),media->format());
if (payload >= 0)
m->addParam("payload",String(payload));
m->addParam("evpayload",media->rfc2833());
int evpayload = media->selectRfc2833(media->format());
if (evpayload >= 0)
m->addParam("evpayload",String(evpayload));
else
m->addParam("evpayload",String::boolText(false));
}
if (m_secure) {
if (media->remoteCrypto()) {
@ -970,20 +997,32 @@ ObjList* SDPSession::updateRtpSDP(const NamedList& params, String& rtpAddr, ObjL
rtp = new SDPMedia(tmp,trans,fmts,-1,port);
append = true;
}
int dir = SDPMedia::DirUnknown;
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))
if (tmp.startSkip(sdpPrefix + rtp->suffix() + "_",false) && (tmp.find('_') < 0)) {
rtp->parameter(tmp,*param,append);
SDPMedia::setDirection(dir,tmp);
}
}
}
rtp->mappings(params.getValue("rtp_mapping" + rtp->suffix()));
if (audio)
rtp->rfc2833(params.getIntValue("rtp_rfc2833",-1));
if (audio) {
Rfc2833 rfc2833;
for (int i = 0; i < Rfc2833::RateCount; ++i) {
if (i == Rfc2833::Rate8khz)
rfc2833[i] = params.getIntValue(YSTRING("rtp_rfc2833"),-1);
else
rfc2833[i] = params.getIntValue("rtp_rfc2833_" + Rfc2833::rateValue(i),-1);
}
rtp->rfc2833(rfc2833);
}
rtp->crypto(params.getValue("crypto" + rtp->suffix()),false);
rtp->direction(dir,false);
if (!lst)
lst = new ObjList;
lst->append(rtp);
@ -1055,6 +1094,66 @@ void SDPSession::setFormatsExtra(const NamedList& list, bool out)
}
}
// Parse a received SDP body, process session level parameters.
NamedList* SDPSession::parseSessionParams(const MimeSdpBody* sdp)
{
NamedList* nl = 0;
for (const ObjList* o = (sdp ? sdp->lines().skipNull() : 0); o; o = o->skipNext()) {
const NamedString* l = static_cast<const NamedString*>(o->get());
if (l->name() == YSTRING("m"))
break;
const char* pref = 0;
if (l->name() == YSTRING("b"))
pref = "BW-";
else if (l->name() != YSTRING("a"))
continue;
NamedString* ns = 0;
int pos = l->find(':');
if (pos >= 0)
ns = new NamedString(pref + l->substr(0,pos),l->substr(pos + 1));
else
ns = new NamedString(pref + *l);
XDebug(m_enabler,DebugAll,"Parsed sess SDP param %s='%s' [%p]",
ns->name().c_str(),ns->safe(),m_ptr);
if (!nl) {
Lock lck(m_parser);
String pref = m_parser->m_ssdpParam;
if (!pref)
break;
nl = new NamedList(pref + "_");
}
nl->addParam(ns);
}
return nl;
}
// Replace SDP sesison
void SDPSession::updateSessionParams(const NamedList& nl)
{
String pref;
Lock lck(m_parser);
if (m_parser->m_ssdpParam)
pref = "o" + m_parser->m_ssdpParam;
lck.drop();
pref = nl.getValue(YSTRING("ossdp-prefix"),pref);
XDebug(m_enabler,DebugAll,"updateSessionParams('%s') pref='%s' [%p]",
nl.c_str(),pref.c_str(),m_ptr);
if (!pref)
return;
pref << "_";
m_createSdpParams.clearParams();
int pLen = pref.length();
for (ObjList* o = nl.paramList()->skipNull(); o; o = o->skipNext()) {
const NamedString* p = static_cast<const NamedString*>(o->get());
if (p->name().startsWith(pref) && p->name().rfind('_') < pLen) {
NamedString* ns = new NamedString(p->name().substr(pLen),*p);
m_createSdpParams.addParam(ns);
XDebug(m_enabler,DebugAll,"Added sess create SDP param %s='%s' [%p]",
ns->name().c_str(),ns->safe(),m_ptr);
}
}
}
// Add extra AMR params to fmtp line
void SDPSession::addFmtpAmrExtra(String& buf, const String* fmtp)
{
@ -1094,6 +1193,32 @@ void SDPSession::addFmtpAmrExtra(String& buf, const String* fmtp)
TelEngine::destruct(l);
}
// Add session or media parameters
void SDPSession::addSdpParams(MimeSdpBody* sdp, const NamedList& params, bool* enc, bool* dir)
{
for (ObjList* o = params.paramList()->skipNull(); o; o = o->skipNext()) {
const NamedString* p = static_cast<const NamedString*>(o->get());
if (p->name().find(':') >= 0)
continue;
const char* type = "a";
String tmp = p->name();
if (tmp.startSkip("BW-",false)) {
if (!tmp)
continue;
type = "b";
}
else {
if (dir)
*dir = *dir || lookup(tmp,SDPMedia::s_sdpDir);
if (enc)
*enc = *enc || (tmp == YSTRING("encryption"));
}
if (*p)
tmp << ":" << *p;
sdp->addLine(type,tmp);
}
}
}; // namespace TelEngine
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -5,7 +5,7 @@
* SDP media handling
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
* Copyright (C) 2004-2023 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
@ -54,6 +54,138 @@ class SDPMedia;
class SDPSession;
class SDPParser;
/**
* This class holds RFC 2833 payloads for known audio rates
* @short RFC 2833 payloads for known audio rates
*/
class YSDP_API Rfc2833
{
public:
/**
* Suported (known) rates
*/
enum Rate {
Rate8khz = 0,
Rate16khz,
Rate32khz,
RateCount,
};
/**
* Constructor
*/
inline Rfc2833()
{ set(); }
/**
* Check if a payload is present in the list
* @param payload Payload to check
*/
inline bool includes(int payload) const {
for (int i = 0; i < RateCount; ++i)
if (m_payloads[i] == payload)
return true;
return false;
}
/**
* Select RFC 2833 payload for given media format
* @param fmt Format internal name
* @return Payload value, negative if not available
*/
inline int payload(const String& fmt) const {
int r = fmtRate(fmt);
return r < RateCount ? m_payloads[r] : -1;
}
/**
* Replace all values
* @param other Optional values to replace with. Reset if not present
*/
inline void set(const Rfc2833* other = 0) {
for (int i = 0; i < RateCount; ++i)
m_payloads[i] = other ? (*other)[i] : -1;
}
/**
* Update payloads
* @param params Parameters list
* @param defaults Payload list with default values
* @param force True to force updating if a needed parameter is not present
* @param param Parameter name. defaults to "rfc2833"
*/
void update(const NamedList& params, const Rfc2833& defaults, bool force = true,
const String& param = String::empty());
/**
* Update payload for specific rate
* @param value Payload
* @param rate Rate value
* @param defaults Payload list with default values
*/
void update(int rate, const String& value, const Rfc2833& defaults);
/**
* Put RFC 2833 parameters in a parameter list
* @param params Destination list
* @param param Parameter name. Defaults to "rtp_rfc2833"
*/
void put(NamedList& params, const String& param = String::empty()) const;
inline int operator[](int index) const
{ return m_payloads[index]; }
inline int& operator[](int index)
{ return m_payloads[index]; }
/**
* Assignment operator
*/
inline Rfc2833& operator=(const Rfc2833& other)
{ set(&other); return *this; }
/**
* Dump (append) payloads to destination string
* Format: rate=payload[rate1=payload1...],
* @param buf Destination string
* @return Destination string
*/
String& dump(String& buf) const;
/**
* Select RFC 2833 rate for given media format
* @param fmt Format internal name
* @return Rate value as enumeration
*/
static int fmtRate(const String& fmt);
/**
* Retrieve rate enumeration value from rate value
* @param s Rate value
* @return Rate as enumeration, RateCount if not found
*/
static inline int rate(const String& s) {
for (int r = 0; r < RateCount; ++r)
if (s_rates[r] == s)
return r;
return RateCount;
}
/**
* Retrieve rate value from rate enumeration value
* @param index Rate enumeration index
* @return Rate name, empty string if not found
*/
static inline const String& rateValue(int index)
{ return index < RateCount ? s_rates[index] : String::empty(); }
protected:
static const String s_rates[RateCount];
int m_payloads[RateCount];
};
/**
* This class holds a single SDP media description
* @short SDP media description
@ -61,6 +193,17 @@ class SDPParser;
class YSDP_API SDPMedia : public NamedList
{
public:
/**
* RTP session/media direction
*/
enum Direction {
DirUnknown = 0,
DirRecv = 1,
DirSend = 2,
DirBidir = 3,
DirInactive = 4,
};
/**
* Constructor
* @param media Media type name
@ -176,23 +319,35 @@ public:
{ if (newMap) m_mappings = newMap; }
/**
* Retrieve RFC2833 status or payload of this media
* @return RFC2833 status or payload of this media
* Retrieve RFC 2833 payloads of this media
* @return RFC 2833 payloads of this media
*/
inline const String& rfc2833() const
inline const Rfc2833& 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
* Set RFC 2833 payloads of this media
* @param values SDP RFC 2833 payloads to set
*/
inline void rfc2833(int payload)
{
if (payload >= 0)
m_rfc2833 = payload;
else
m_rfc2833 = String::boolText(false);
inline void rfc2833(const Rfc2833& values)
{ m_rfc2833 = values; }
/**
* Check if RFC 2833 was selected for this media
* @return True if RFC 2833 was selected for this media, false otherwise
*/
inline bool haveRfc2833() const
{ return m_haveRfc3833; }
/**
* Select RFC 2833 payload for given media format
* @param fmt Format internal name
* @return Payload value, negative if not available
*/
inline int selectRfc2833(const String& fmt) {
int rVal = m_rfc2833.payload(fmt);
m_haveRfc3833 = (rVal >= 0);
return rVal;
}
/**
@ -286,6 +441,20 @@ public:
*/
void crypto(const char* desc, bool remote);
/**
* Set media direction
* @param value New direction
* @param remote True to set the remote direction, false to set the local one
*/
void direction(int value, bool remote);
/**
* Retrieve negotiated media direction to be sent to remote
* @param sessLDir SDP session level local direction, if known
* @return Media direction
*/
int direction(int sessLDir = 0);
/**
* Put this net media in a parameter list
* @param msg Destination list
@ -299,6 +468,17 @@ public:
*/
void keepRtp(const SDPMedia& other);
/**
* Retrieve direction if known
* @param dir Destination variable to set the direction in
* @param name Direction name
*/
static inline void setDirection(int& dir, const char* name) {
int d = lookup(name,s_sdpDir);
if (d != DirUnknown)
dir = d;
}
/**
* Retrieve format mapping to payload
* @param mappings Mappings list
@ -307,11 +487,17 @@ public:
*/
static int payloadMapping(const String& mappings, const String& fmt);
/**
* SDP media direction dictionary
*/
static const TokenDict s_sdpDir[];
private:
bool m_audio;
bool m_video;
bool m_modified;
bool m_securable;
bool m_haveRfc3833;
// local rtp data changed flag
bool m_localChanged;
// suffix used for this type
@ -330,12 +516,15 @@ private:
String m_mappings;
// local media port
String m_lPort;
// payload for telephone/event
String m_rfc2833;
// payloads for telephone/event
Rfc2833 m_rfc2833;
// remote security descriptor
String m_rCrypto;
// local security descriptor
String m_lCrypto;
// Local / remote media direction
int m_lDir;
int m_rDir;
};
@ -401,8 +590,10 @@ public:
* @param msg Destination list
* @param media List of SDP media information
* @param putPort True to add the media port
* @param sessParams Optional session level SDP parameters
*/
static void putMedia(NamedList& msg, ObjList* media, bool putPort = true);
static void putMedia(NamedList& msg, ObjList* media, bool putPort = true,
const NamedList* sessParams = 0);
/**
* Put session media parameters into a list of parameters
@ -410,7 +601,7 @@ public:
* @param putPort True to add the media port
*/
inline void putMedia(NamedList& msg, bool putPort = true)
{ putMedia(msg,m_rtpMedia,putPort); }
{ putMedia(msg,m_rtpMedia,putPort,m_parsedParams); }
/**
* Retrieve a single media description
@ -423,15 +614,16 @@ public:
/**
* Update the RFC 2833 availability and payload
* @param value String to get payload or availability
* @param rate Rate to set
*/
void setRfc2833(const String& value);
void setRfc2833(const String& value, int rate = Rfc2833::Rate8khz);
/**
* Update the RFC 2833 availability and payload
* @param value Pointer to string to get payload or availability
* @param params Parameters list
* @param force Force update even if needed parameters are not present
*/
inline void setRfc2833(const String* value)
{ if (value) setRfc2833(*value); }
void setRfc2833(const NamedList& params, bool force);
/**
* Build and dispatch a chan.rtp message for a given media. Update media on success
@ -656,6 +848,37 @@ protected:
*/
void setFormatsExtra(const NamedList& list, bool out);
/**
* Parse a received SDP body, process session level parameters
* @param sdp Pointer to received SDP body
* @return List with handled session level parameters, may be NULL
*/
NamedList* parseSessionParams(const MimeSdpBody* sdp);
/**
* Replace parsed SDP session level parameters
* Consume receveived pointer
* @param params Parsed SDP session params
*/
inline void setSessionParams(NamedList* params) {
TelEngine::destruct(m_parsedParams);
m_parsedParams = params;
}
/**
* Parse a received SDP body, process session level parameters.
* Replace
* @param sdp Pointer to received SDP body
*/
inline void processSessionParams(const MimeSdpBody* sdp)
{ setSessionParams(parseSessionParams(sdp)); }
/**
* Update SDP session level parameters to be used when building SDP
* @param params Parameters list
*/
void updateSessionParams(const NamedList& params);
SDPParser* m_parser;
int m_mediaStatus;
bool m_rtpForward; // Forward RTP flag
@ -671,13 +894,18 @@ protected:
unsigned int m_sdpHash; // SDP content hash
String m_host;
bool m_secure;
int m_rfc2833; // Payload of RFC 2833 for remote party
Rfc2833 m_rfc2833; // Payloads of RFC 2833 for remote party
bool m_ipv6; // IPv6 support
bool m_gpmd; // Handle GPMD even if not RTP forwarding
NamedList m_amrExtra; // Extra AMR codec parameters
NamedList* m_parsedParams; // Parsed session level parameters
NamedList m_createSdpParams; // Session level parameters used on SDP create
private:
// Add extra AMR params to fmtp line
void addFmtpAmrExtra(String& buf, const String* fmtp);
// Add session or media parameters when creating SDP
void addSdpParams(MimeSdpBody* sdp, const NamedList& params, bool* enc = 0, bool* dir = 0);
DebugEnabler* m_enabler; // Debug enabler used for output
void* m_ptr; // Pointer to show in debug messages
@ -701,8 +929,7 @@ public:
*/
inline SDPParser(const char* dbgName, const char* sessName, const char* fmts = "alaw,mulaw")
: Mutex(true,"SDPParser"),
m_rfc2833(101),
m_sdpForward(false), m_secure(false), m_ignorePort(false),
m_sdpForward(false), m_secure(false), m_gpmd(false), m_ignorePort(false),
m_sessionName(sessName), m_audioFormats(fmts),
m_codecs(""), m_hacks("")
{ debugName(dbgName); }
@ -716,10 +943,10 @@ public: