Allow dtmf send method(s) to be configured. Detect RFC2833 support when receiving remote capabilities.
git-svn-id: http://yate.null.ro/svn/yate/trunk@5265 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
2030126a5f
commit
e0fdf7c584
|
@ -28,8 +28,23 @@
|
|||
; needmedia: bool: Drop calls for which no common media could be negotiated
|
||||
;needmedia=yes
|
||||
|
||||
; dtmfinband: bool: Send keypad events as inband DTMF
|
||||
;dtmfinband=no
|
||||
; dtmfmethods: string: Comma separated list of methods used to send DTMFs
|
||||
; Allowed values in list:
|
||||
; h323: Use library send user input method
|
||||
; rfc2833: Use RFC 2833 signals if remote party advertised support
|
||||
; inband: Send tones in audio stream
|
||||
; The methods will be used in the listed order
|
||||
; Defaults to 'rfc2833,h323,inband' if missing or empty
|
||||
; Invalid values are ignored
|
||||
; E.g.
|
||||
; 'h323,foo' leads to 'h323'
|
||||
; 'foo,foo1' leads to 'rfc2833,h323,inband'
|
||||
; This parameter can be overridden from routing by 'odtmfmethods' for outgoing call leg
|
||||
; and 'idtmfmethods' for incoming call leg
|
||||
; Also, this parameter can be overridden in chan.dtmf messages by a 'methods' parameter
|
||||
; NOTE: When overridden from chan.dtmf an empty or invalid 'methods' parameter will be ignored
|
||||
; This parameter is applied on reload for new calls only
|
||||
;dtmfmethods=rfc2833,h323,inband
|
||||
|
||||
; Use an external RTP module instead of the native OpenH323 RTP stack, which is
|
||||
; very cpu intensive. If no external RTP can be found it will fallback to the
|
||||
|
|
|
@ -90,7 +90,6 @@ typedef PBoolean BOOL;
|
|||
using namespace TelEngine;
|
||||
namespace { // anonymous
|
||||
|
||||
static bool s_inband;
|
||||
static bool s_externalRtp;
|
||||
static bool s_fallbackRtp;
|
||||
static bool s_needMedia = true;
|
||||
|
@ -269,6 +268,57 @@ class YateGatekeeperServer;
|
|||
class YateH323EndPoint;
|
||||
class YateH323Chan;
|
||||
|
||||
class DtmfMethods
|
||||
{
|
||||
public:
|
||||
enum Method {
|
||||
H323,
|
||||
Rfc2833,
|
||||
Inband,
|
||||
MethodCount
|
||||
};
|
||||
inline DtmfMethods()
|
||||
{ setDefault(); }
|
||||
inline void set(int _0 = MethodCount, int _1 = MethodCount, int _2 = MethodCount) {
|
||||
m_methods[0] = _0;
|
||||
m_methods[1] = _1;
|
||||
m_methods[2] = _2;
|
||||
}
|
||||
inline void setDefault()
|
||||
{ set(Rfc2833,H323,Inband); }
|
||||
// Replace all methods from comma separated list
|
||||
// If no method is set use other or setDefEmpty (reset to default)
|
||||
// Return false if methods contain unknown methods
|
||||
bool set(const String& methods, const DtmfMethods* other, bool setDefEmpty = true);
|
||||
// Retrieve a method from deperecated parameters
|
||||
// Reset the method if the parameter is false
|
||||
// Display a message anyway if warn is not false
|
||||
// Return true if the parameter was found
|
||||
bool getDeprecatedDtmfMethod(const NamedList& list, const char* param, int method, bool* warn);
|
||||
// Reset a method
|
||||
void reset(int method);
|
||||
// Build a string list from methods
|
||||
void buildMethods(String& buf, const char* sep = ",");
|
||||
inline void printMethods(DebugEnabler* enabler, int level, const String& str) {
|
||||
String tmp;
|
||||
buildMethods(tmp);
|
||||
Debug(enabler,level,"Built DTMF methods '%s' from '%s'",tmp.safe(),str.safe());
|
||||
}
|
||||
inline int operator[](unsigned int index) {
|
||||
if (index < MethodCount)
|
||||
return m_methods[index];
|
||||
return MethodCount;
|
||||
}
|
||||
inline DtmfMethods& operator=(const DtmfMethods& other) {
|
||||
for (int i = 0; i < MethodCount; i++)
|
||||
m_methods[i] = other.m_methods[i];
|
||||
return *this;
|
||||
}
|
||||
static const TokenDict s_methodName[];
|
||||
protected:
|
||||
int m_methods[MethodCount];
|
||||
};
|
||||
|
||||
class H323Driver : public Driver
|
||||
{
|
||||
public:
|
||||
|
@ -451,6 +501,8 @@ public:
|
|||
virtual void OnCleared();
|
||||
virtual BOOL OnAlerting(const H323SignalPDU& alertingPDU, const PString& user);
|
||||
virtual BOOL OnReceivedProgress(const H323SignalPDU& pdu);
|
||||
virtual BOOL OnReceivedCapabilitySet(const H323Capabilities& remoteCaps,
|
||||
const H245_MultiplexCapability* muxCap, H245_TerminalCapabilitySetReject& reject);
|
||||
virtual void OnUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp);
|
||||
virtual void OnUserInputString(const PString& value);
|
||||
virtual BOOL OpenAudioChannel(BOOL isEncoding, unsigned bufferSize,
|
||||
|
@ -483,8 +535,16 @@ public:
|
|||
{ return m_nativeRtp; }
|
||||
inline void rtpLocal()
|
||||
{ m_passtrough = false; }
|
||||
inline bool rtpStarted() const
|
||||
{ return m_rtpStarted; }
|
||||
inline const String& rtpId() const
|
||||
{ return m_rtpid; }
|
||||
inline int dtmfPayload() const
|
||||
{ return m_dtmfPayload; }
|
||||
private:
|
||||
void setEpConn(bool created);
|
||||
// Retrieve RTP DTMF payload from local/remote caps. Return negative if not found
|
||||
int rtpDtmfPayload(bool local);
|
||||
String m_chanId;
|
||||
YateH323Chan* m_chan;
|
||||
Mutex* m_mutex;
|
||||
|
@ -500,6 +560,8 @@ private:
|
|||
String m_remoteAddr;
|
||||
int m_remotePort;
|
||||
bool m_needMedia;
|
||||
bool m_rtpStarted;
|
||||
int m_dtmfPayload;
|
||||
};
|
||||
|
||||
// this part has been inspired (more or less) from chan_h323 of project asterisk, credits to Jeremy McNamara for chan_h323 and to Mark Spencer for asterisk.
|
||||
|
@ -559,7 +621,7 @@ private:
|
|||
YateH323Connection* m_conn;
|
||||
H323Connection::CallEndReason m_reason;
|
||||
bool m_hungup;
|
||||
bool m_inband;
|
||||
DtmfMethods m_dtmfMethods;
|
||||
};
|
||||
|
||||
class YateGkRegThread : public PThread
|
||||
|
@ -692,6 +754,81 @@ protected:
|
|||
|
||||
unsigned int YateGkRegThread::s_count = 0;
|
||||
Mutex YateGkRegThread::s_mutexCount(false,"H323GkThreads");
|
||||
static DtmfMethods s_dtmfMethods;
|
||||
// Deprecated dtmf params warn
|
||||
static bool s_warnDtmfInbandCfg = true;
|
||||
static bool s_warnDtmfInbandCallExecute = true;
|
||||
|
||||
const TokenDict DtmfMethods::s_methodName[] = {
|
||||
{ "h323", H323},
|
||||
{ "rfc2833", Rfc2833},
|
||||
{ "inband", Inband},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
// Replace all methods from comma separated list
|
||||
// If no method is set use other or setDefEmpty (reset to default)
|
||||
bool DtmfMethods::set(const String& methods, const DtmfMethods* other, bool setDefEmpty)
|
||||
{
|
||||
set();
|
||||
bool found = false;
|
||||
bool ok = true;
|
||||
ObjList* m = methods.split(',');
|
||||
int i = 0;
|
||||
for (ObjList* o = m->skipNull(); o && i < MethodCount; o = o->skipNext()) {
|
||||
String* s = static_cast<String*>(o->get());
|
||||
int meth = lookup(s->trimBlanks(),s_methodName,MethodCount);
|
||||
if (meth != MethodCount) {
|
||||
m_methods[i++] = meth;
|
||||
found = true;
|
||||
}
|
||||
else if (*s)
|
||||
ok = false;
|
||||
}
|
||||
TelEngine::destruct(m);
|
||||
if (!found) {
|
||||
if (other)
|
||||
*this = *other;
|
||||
else if (setDefEmpty)
|
||||
setDefault();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Retrieve a method from deperecated parameters
|
||||
// Reset the method if the parameter is false
|
||||
// Display a message anyway if warn is not false
|
||||
bool DtmfMethods::getDeprecatedDtmfMethod(const NamedList& list, const char* param,
|
||||
int method, bool* warn)
|
||||
{
|
||||
String* p = list.getParam(param);
|
||||
if (!p)
|
||||
return false;
|
||||
if (!p->toBoolean())
|
||||
reset(method);
|
||||
if (warn && *warn) {
|
||||
*warn = false;
|
||||
Debug(&hplugin,DebugConf,"Deprecated '%s' in '%s'. Use 'dtmfmethods' instead!",param,list.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reset a method
|
||||
void DtmfMethods::reset(int method)
|
||||
{
|
||||
for (int i = 0; i < MethodCount; i++)
|
||||
if (m_methods[i] == method) {
|
||||
m_methods[i] = MethodCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build a string list from methods
|
||||
void DtmfMethods::buildMethods(String& buf, const char* sep)
|
||||
{
|
||||
for (int i = 0; i < MethodCount; i++)
|
||||
buf.append(lookup(m_methods[i],s_methodName),sep);
|
||||
}
|
||||
|
||||
|
||||
// Get a number of thread idle intervals from a time period
|
||||
|
@ -940,7 +1077,7 @@ bool YateH323EndPoint::initInternal(bool reg, const NamedList* params)
|
|||
Lock lck(m_mutex);
|
||||
DDebug(&hplugin,DebugAll,"Endpoint(%s)::initInternal(%u,%p) [%p]",
|
||||
safe(),reg,params,this);
|
||||
DisableDetectInBandDTMF(!(params && params->getBoolValue("dtmfinband",s_inband)));
|
||||
DisableDetectInBandDTMF(!(params && params->getBoolValue("dtmfinband")));
|
||||
DisableFastStart(params && !params->getBoolValue("faststart",true));
|
||||
DisableH245Tunneling(params && !params->getBoolValue("h245tunneling",true));
|
||||
DisableH245inSetup(!(params && params->getBoolValue("h245insetup")));
|
||||
|
@ -1532,7 +1669,8 @@ YateH323Connection::YateH323Connection(YateH323EndPoint& endpoint,
|
|||
: H323Connection(endpoint,callReference), m_chan(0), m_mutex(0),
|
||||
m_externalRtp(s_externalRtp), m_nativeRtp(false),
|
||||
m_passtrough(false), m_lockFormats(false),
|
||||
m_rtpPort(0), m_remotePort(0), m_needMedia(true)
|
||||
m_rtpPort(0), m_remotePort(0), m_needMedia(true),
|
||||
m_rtpStarted(false), m_dtmfPayload(-1)
|
||||
{
|
||||
Debug(&hplugin,DebugAll,"YateH323Connection::YateH323Connection(%p,%u,%p) [%p]",
|
||||
&endpoint,callReference,userdata,this);
|
||||
|
@ -1923,6 +2061,25 @@ BOOL YateH323Connection::OnReceivedProgress(const H323SignalPDU& pdu)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL YateH323Connection::OnReceivedCapabilitySet(const H323Capabilities& remoteCaps,
|
||||
const H245_MultiplexCapability* muxCap, H245_TerminalCapabilitySetReject& reject)
|
||||
{
|
||||
DDebug(this,DebugInfo,"YateH323Connection::OnReceivedCapabilitySet [%p]",this);
|
||||
bool ok = H323Connection::OnReceivedCapabilitySet(remoteCaps,muxCap,reject);
|
||||
int payload = rtpDtmfPayload(false);
|
||||
if (m_dtmfPayload != payload) {
|
||||
if (m_rtpStarted) {
|
||||
// TODO: Update external rtp event payload when implemented
|
||||
if (payload > 0)
|
||||
Debug(this,DebugInfo,"Unable to change event payload, disabling RFC 2833 [%p]",this);
|
||||
m_dtmfPayload = -3;
|
||||
}
|
||||
else
|
||||
m_dtmfPayload = payload;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void YateH323Connection::OnUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp)
|
||||
{
|
||||
Debug(this,DebugInfo,"YateH323Connection::OnUserInputTone '%c' duration=%u [%p]",tone,duration,this);
|
||||
|
@ -2147,6 +2304,8 @@ BOOL YateH323Connection::startExternalRTP(const char* remoteIP, WORD remotePort,
|
|||
}
|
||||
if (!m_externalRtp)
|
||||
return FALSE;
|
||||
if (m_dtmfPayload < 0)
|
||||
m_dtmfPayload = rtpDtmfPayload(true);
|
||||
Message m("chan.rtp");
|
||||
if (m_rtpid)
|
||||
m.setParam("rtpid",m_rtpid);
|
||||
|
@ -2158,6 +2317,8 @@ BOOL YateH323Connection::startExternalRTP(const char* remoteIP, WORD remotePort,
|
|||
m.addParam("format",format);
|
||||
if ((payload >= 0) && (payload < 127))
|
||||
m.addParam("payload",String(payload));
|
||||
if (m_dtmfPayload > 0)
|
||||
m.addParam("evpayload",String(m_dtmfPayload));
|
||||
|
||||
TelEngine::Lock lock(m_mutex);
|
||||
if (!(m_chan && m_chan->alive() && m_chan->driver()))
|
||||
|
@ -2166,6 +2327,7 @@ BOOL YateH323Connection::startExternalRTP(const char* remoteIP, WORD remotePort,
|
|||
lock.drop();
|
||||
if (Engine::dispatch(m)) {
|
||||
m_rtpid = m.getValue("rtpid");
|
||||
m_rtpStarted = true;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
|
@ -2214,6 +2376,22 @@ void YateH323Connection::setEpConn(bool created)
|
|||
ep->m_connCount--;
|
||||
}
|
||||
|
||||
// Retrieve RTP DTMF payload from local/remote caps
|
||||
int YateH323Connection::rtpDtmfPayload(bool local)
|
||||
{
|
||||
int payload = -1;
|
||||
const H323Capabilities& caps = local ? GetLocalCapabilities() : GetRemoteCapabilities();
|
||||
// NOTE: RFC2833 capability subtype is not set to H323_UserInputCapability::SignalToneRFC2833 in the library
|
||||
// It is set to 10000
|
||||
H323Capability* cap = caps.FindCapability(H323Capability::e_UserInput,10000);
|
||||
if (cap) {
|
||||
payload = cap->GetPayloadType();
|
||||
if (payload < 96 || payload > 127)
|
||||
payload = -2;
|
||||
}
|
||||
XDebug(this,DebugNote,"rtpDtmfPayload(%u) %d [%p]",local,payload,this);
|
||||
return payload;
|
||||
}
|
||||
|
||||
YateH323_ExternalRTPChannel::YateH323_ExternalRTPChannel(
|
||||
YateH323Connection& connection,
|
||||
|
@ -2579,7 +2757,7 @@ void YateH323Connection::setCallerID(const char* number, const char* name)
|
|||
YateH323Chan::YateH323Chan(YateH323Connection* conn,Message* msg,const char* addr)
|
||||
: Channel(hplugin,0,(msg != 0)),
|
||||
m_conn(conn), m_reason(H323Connection::EndedByLocalUser),
|
||||
m_hungup(false), m_inband(s_inband)
|
||||
m_hungup(false)
|
||||
{
|
||||
s_mutex.lock();
|
||||
s_chanCount++;
|
||||
|
@ -2589,8 +2767,17 @@ YateH323Chan::YateH323Chan(YateH323Connection* conn,Message* msg,const char* add
|
|||
conn,addr,direction(),this);
|
||||
setMaxcall(msg);
|
||||
Message* s = message("chan.startup",msg);
|
||||
s_cfgMutex.lock();
|
||||
m_dtmfMethods = s_dtmfMethods;
|
||||
s_cfgMutex.unlock();
|
||||
if (msg) {
|
||||
m_inband = msg->getBoolValue("dtmfinband",s_inband);
|
||||
String* meths = msg->getParam("odtmfmethods");
|
||||
if (meths) {
|
||||
DtmfMethods old = m_dtmfMethods;
|
||||
m_dtmfMethods.set(*meths,&old);
|
||||
}
|
||||
else
|
||||
m_dtmfMethods.getDeprecatedDtmfMethod(*msg,"dtmfinband",DtmfMethods::Inband,&s_warnDtmfInbandCallExecute);
|
||||
s->copyParams(*msg,"caller,callername,called,billid,callto,username");
|
||||
}
|
||||
Engine::enqueue(s);
|
||||
|
@ -2775,6 +2962,11 @@ bool YateH323Chan::callRouted(Message& msg)
|
|||
|
||||
void YateH323Chan::callAccept(Message& msg)
|
||||
{
|
||||
String* meths = msg.getParam(YSTRING("idtmfmethods"));
|
||||
if (meths) {
|
||||
DtmfMethods old = m_dtmfMethods;
|
||||
m_dtmfMethods.set(*meths,&old);
|
||||
}
|
||||
Channel::callAccept(msg);
|
||||
if (m_conn) {
|
||||
m_conn->rtpExecuted(msg);
|
||||
|
@ -2829,9 +3021,36 @@ bool YateH323Chan::msgTone(Message& msg, const char* tone)
|
|||
{
|
||||
if (!(tone && m_conn))
|
||||
return false;
|
||||
if (m_inband && dtmfInband(tone))
|
||||
return true;
|
||||
return m_conn->sendTone(msg,tone);
|
||||
DtmfMethods methods = m_dtmfMethods;
|
||||
const String* param = msg.getParam(YSTRING("methods"));
|
||||
if (param)
|
||||
methods.set(*param,&m_dtmfMethods);
|
||||
bool retVal = false;
|
||||
bool ok = false;
|
||||
for (int i = 0; !ok && i < DtmfMethods::MethodCount; i++) {
|
||||
int meth = methods[i];
|
||||
if (meth == DtmfMethods::H323) {
|
||||
while (*tone)
|
||||
m_conn->SendUserInputTone(*tone++);
|
||||
ok = true;
|
||||
retVal = true;
|
||||
}
|
||||
else if (meth == DtmfMethods::Rfc2833) {
|
||||
ok = m_conn->rtpStarted() && m_conn->rtpId() && m_conn->dtmfPayload() > 0;
|
||||
if (ok)
|
||||
msg.setParam("targetid",m_conn->rtpId());
|
||||
}
|
||||
else if (meth == DtmfMethods::Inband) {
|
||||
ok = dtmfInband(tone);
|
||||
retVal = ok;
|
||||
}
|
||||
}
|
||||
if (!ok && debugAt(DebugNote)) {
|
||||
String tmp;
|
||||
methods.buildMethods(tmp);
|
||||
Debug(this,DebugNote,"Failed to send tones '%s' methods=%s [%p]",tone,tmp.c_str(),this);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool YateH323Chan::msgText(Message& msg, const char* text)
|
||||
|
@ -3017,8 +3236,21 @@ void H323Driver::initialize()
|
|||
s_cfgMutex.lock();
|
||||
s_cfg = Engine::configFile("h323chan");
|
||||
s_cfg.load();
|
||||
NamedList* general = s_cfg.getSection("general");
|
||||
if (general) {
|
||||
String* dtmfMethods = general->getParam("dtmfmethods");
|
||||
if (dtmfMethods) {
|
||||
if (!s_dtmfMethods.set(*dtmfMethods,0))
|
||||
s_dtmfMethods.printMethods(this,DebugConf,*dtmfMethods);
|
||||
}
|
||||
else {
|
||||
s_dtmfMethods.setDefault();
|
||||
s_dtmfMethods.getDeprecatedDtmfMethod(*general,"dtmfinband",DtmfMethods::Inband,&s_warnDtmfInbandCfg);
|
||||
}
|
||||
}
|
||||
else
|
||||
s_dtmfMethods.setDefault();
|
||||
s_cfgMutex.unlock();
|
||||
s_inband = s_cfg.getBoolValue("general","dtmfinband",false);
|
||||
s_externalRtp = s_cfg.getBoolValue("general","external_rtp",true);
|
||||
s_passtrough = s_cfg.getBoolValue("general","forward_rtp",false);
|
||||
s_fallbackRtp = s_cfg.getBoolValue("general","fallback_rtp",true);
|
||||
|
|
Loading…
Reference in New Issue