diff --git a/conf.d/yate.conf.sample b/conf.d/yate.conf.sample index ae68de2b..3d6dac9f 100644 --- a/conf.d/yate.conf.sample +++ b/conf.d/yate.conf.sample @@ -105,3 +105,6 @@ h323chan.yate=yes ; maxchans: int: Maximum number of channels running at once in each driver ;maxchans=0 + +; dtmfdups: bool: Allow duplicate DTMFs (detected with different methods) +;dtmfdups=disable diff --git a/engine/Channel.cpp b/engine/Channel.cpp index 8ef27e52..442601b9 100644 --- a/engine/Channel.cpp +++ b/engine/Channel.cpp @@ -282,7 +282,7 @@ Channel::Channel(Driver* driver, const char* id, bool outgoing) : CallEndpoint(id), m_driver(driver), m_outgoing(outgoing), m_timeout(0), m_maxcall(0), - m_sequence(0), m_answered(false) + m_dtmfSeq(0), m_answered(false) { init(); } @@ -291,7 +291,7 @@ Channel::Channel(Driver& driver, const char* id, bool outgoing) : CallEndpoint(id), m_driver(&driver), m_outgoing(outgoing), m_timeout(0), m_maxcall(0), - m_sequence(0), m_answered(false) + m_dtmfSeq(0), m_answered(false) { init(); } @@ -435,11 +435,6 @@ void Channel::complete(Message& msg, bool minimal) const msg.setParam("id",id()); if (m_driver) msg.setParam("module",m_driver->name()); - if ((msg == "chan.dtmf") && !msg.getParam("sequence")) { - // need to add sequence number used to detect reorders - Lock lock(mutex()); - msg.addParam("sequence",String(m_sequence++)); - } if (minimal) return; @@ -568,6 +563,14 @@ bool Channel::msgMasquerade(Message& msg) Debug(this,DebugInfo,"Masquerading ringing operation [%p]",this); status("ringing"); } + else if (msg == "chan.dtmf") { + // add sequence, stop the message if it was a disallowed DTMF duplicate + if (dtmfSequence(msg) && m_driver && !m_driver->m_dtmfDups) { + Debug(this,DebugInfo,"Stopping duplicate DTMF '%s' [%p]", + msg.getValue("text"),this); + return true; + } + } return false; } @@ -684,6 +687,41 @@ void Channel::callRejected(const char* error, const char* reason, const Message* status("rejected"); } +bool Channel::dtmfSequence(Message& msg) +{ + if ((msg != "chan.dtmf") || msg.getParam("sequence")) + return false; + bool duplicate = false; + const String* detected = msg.getParam("detected"); + const String* text = msg.getParam("text"); + Lock lock(mutex()); + unsigned int seq = m_dtmfSeq; + if (text && detected && (*text == m_dtmfText) && (*detected != m_dtmfDetected)) + duplicate = true; + else { + seq = ++m_dtmfSeq; + m_dtmfText = text; + m_dtmfDetected = detected; + } + // need to add sequence number used to detect reorders + msg.addParam("sequence",String(seq)); + msg.addParam("duplicate",String::boolText(duplicate)); + return duplicate; +} + +bool Channel::dtmfEnqueue(Message* msg) +{ + if (!msg) + return false; + if (dtmfSequence(*msg) && m_driver && !m_driver->m_dtmfDups) { + Debug(this,DebugInfo,"Dropping duplicate DTMF '%s' [%p]", + msg->getValue("text"),this); + TelEngine::destruct(msg); + return false; + } + return Engine::enqueue(msg); +} + bool Channel::dtmfInband(const char* tone) { if (null(tone)) @@ -1038,7 +1076,7 @@ Driver::Driver(const char* name, const char* type) m_init(false), m_varchan(true), m_routing(0), m_routed(0), m_total(0), m_nextid(0), m_timeout(0), - m_maxroute(0), m_maxchans(0) + m_maxroute(0), m_maxchans(0), m_dtmfDups(false) { m_prefix << name << "/"; } @@ -1323,6 +1361,7 @@ void Driver::loadLimits() timeout(Engine::config().getIntValue("telephony","timeout")); maxRoute(Engine::config().getIntValue("telephony","maxroute")); maxChans(Engine::config().getIntValue("telephony","maxchans")); + dtmfDups(Engine::config().getBoolValue("telephony","dtmfdups")); } unsigned int Driver::nextid() diff --git a/modules/h323chan.cpp b/modules/h323chan.cpp index 17f4abd1..2d7fc5ce 100644 --- a/modules/h323chan.cpp +++ b/modules/h323chan.cpp @@ -1370,7 +1370,8 @@ void YateH323Connection::OnUserInputTone(char tone, unsigned duration, unsigned buf[1] = 0; m->addParam("text",buf); m->addParam("duration",String(duration)); - Engine::enqueue(m); + m->addParam("detected","h323"); + m_chan->dtmfEnqueue(m); } void YateH323Connection::OnUserInputString(const PString &value) diff --git a/modules/server/analog.cpp b/modules/server/analog.cpp index d64947a9..78aee286 100644 --- a/modules/server/analog.cpp +++ b/modules/server/analog.cpp @@ -1655,7 +1655,8 @@ void AnalogChannel::evDigits(const char* text, bool tone) m->addParam("text",text); if (!tone) m->addParam("pulse",String::boolText(true)); - Engine::enqueue(m); + m->addParam("detected","analog"); + dtmfEnqueue(m); } // Line got off hook. Terminate ringing @@ -2334,6 +2335,7 @@ void AnalogCallRec::evDigits(bool fxsEvent, const char* text, bool tone) if (!tone) m->addParam("pulse",String::boolText(true)); m->addParam("sender",callertype(fxsEvent)); + m->addParam("detected","analog"); Engine::enqueue(m); } diff --git a/modules/server/mgcpca.cpp b/modules/server/mgcpca.cpp index f893914f..9f7ef7ab 100644 --- a/modules/server/mgcpca.cpp +++ b/modules/server/mgcpca.cpp @@ -386,6 +386,7 @@ void MGCPWrapper::gotDTMF(char tone) m->addParam("id",m_master); m->addParam("message","chan.dtmf"); m->addParam("text",buf); + m->addParam("detected","mgcp"); Engine::enqueue(m); } diff --git a/modules/server/ysigchan.cpp b/modules/server/ysigchan.cpp index 6f32ed2a..b14188c2 100644 --- a/modules/server/ysigchan.cpp +++ b/modules/server/ysigchan.cpp @@ -884,7 +884,7 @@ void SigChannel::evInfo(SignallingEvent* event) Message* m = message("chan.dtmf"); m->addParam("text",tmp); m->addParam("detected",inband ? "inband" : "signal"); - Engine::enqueue(m); + dtmfEnqueue(m); } } diff --git a/modules/yiaxchan.cpp b/modules/yiaxchan.cpp index 7cc0b213..94b57ef4 100644 --- a/modules/yiaxchan.cpp +++ b/modules/yiaxchan.cpp @@ -1563,7 +1563,7 @@ void YIAXConnection::handleEvent(IAXEvent* event) Message* m = message("chan.dtmf"); m->addParam("text",dtmf); m->addParam("detected","iax-event"); - Engine::enqueue(m); + dtmfEnqueue(m); } break; case IAXEvent::Noise: diff --git a/modules/yjinglechan.cpp b/modules/yjinglechan.cpp index ada0cb1c..9825e866 100644 --- a/modules/yjinglechan.cpp +++ b/modules/yjinglechan.cpp @@ -1351,7 +1351,8 @@ void YJGConnection::handleEvent(JGEvent* event) if (event->reason() == "button-up" && event->text()) { Message* m = message("chan.dtmf"); m->addParam("text",event->text()); - Engine::enqueue(m); + m->addParam("detected","jingle"); + dtmfEnqueue(m); } break; case JGSession::ActDtmfMethod: diff --git a/modules/ysipchan.cpp b/modules/ysipchan.cpp index f88ddb27..d3ea52ce 100644 --- a/modules/ysipchan.cpp +++ b/modules/ysipchan.cpp @@ -3313,7 +3313,7 @@ void YateSIPConnection::doInfo(SIPTransaction* t) copySipHeaders(*msg,*t->initialMessage()); msg->addParam("text",tmp); msg->addParam("detected","sip-info"); - Engine::enqueue(msg); + dtmfEnqueue(msg); } } diff --git a/yatephone.h b/yatephone.h index f9bdbf66..4df6f3fd 100644 --- a/yatephone.h +++ b/yatephone.h @@ -1491,7 +1491,9 @@ private: bool m_outgoing; u_int64_t m_timeout; u_int64_t m_maxcall; - mutable unsigned int m_sequence; + unsigned int m_dtmfSeq; + String m_dtmfText; + String m_dtmfDetected; protected: String m_status; @@ -1867,6 +1869,20 @@ protected: inline void setOutgoing(bool outgoing = true) { m_outgoing = outgoing; } + /** + * Add sequence number to chan.dtmf message, check for duplicates + * @param msg chan.dtmf message to apply sequence number + * @return True if the message is a duplicate (same tone, different method) + */ + bool dtmfSequence(Message& msg); + + /** + * Add sequence number to chan.dtmf and enqueue it, delete if duplicate + * @param msg chan.dtmf message to sequence and enqueue + * @return True if the message was enqueued, false if was a duplicate + */ + bool dtmfEnqueue(Message* msg); + /** * Attempt to install an override data source to send DTMF inband. * Needs a tone generator module capable to override with "tone/dtmfstr/xyz" @@ -1909,6 +1925,7 @@ private: int m_timeout; int m_maxroute; int m_maxchans; + bool m_dtmfDups; public: /** @@ -2121,6 +2138,13 @@ protected: inline void maxChans(int ncalls) { m_maxchans = ncalls; } + /** + * Set the DTMF duplicates allowed flag + * @param duplicates True to allow DTMF duplicate messages + */ + inline void dtmfDups(bool duplicates) + { m_dtmfDups = duplicates; } + private: Driver(); // no default constructor please };