Added support for multiple data sniffers.
git-svn-id: http://voip.null.ro/svn/yate@1021 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
4db3bf1944
commit
dbcc8c9fce
|
@ -585,6 +585,12 @@ void Channel::callAccept(Message& msg)
|
|||
if (m_billid.null())
|
||||
m_billid = msg.getValue("billid");
|
||||
m_targetid = msg.getValue("targetid");
|
||||
String detect = msg.getValue("tonedetect_in");
|
||||
if (detect && detect.toBoolean(true)) {
|
||||
if (detect.toBoolean(false))
|
||||
detect = "tone/*";
|
||||
toneDetect(detect);
|
||||
}
|
||||
if (msg.getBoolValue("autoanswer"))
|
||||
msgAnswered(msg);
|
||||
else if (msg.getBoolValue("autoring"))
|
||||
|
@ -599,6 +605,16 @@ void Channel::callAccept(Message& msg)
|
|||
}
|
||||
}
|
||||
|
||||
void Channel::callConnect(Message& msg)
|
||||
{
|
||||
String detect = msg.getValue("tonedetect_out");
|
||||
if (detect && detect.toBoolean(true)) {
|
||||
if (detect.toBoolean(false))
|
||||
detect = "tone/*";
|
||||
toneDetect(detect);
|
||||
}
|
||||
}
|
||||
|
||||
void Channel::callRejected(const char* error, const char* reason, const Message* msg)
|
||||
{
|
||||
Debug(this,DebugMild,"Call rejected error='%s' reason='%s' [%p]",error,reason,this);
|
||||
|
@ -619,6 +635,18 @@ bool Channel::dtmfInband(const char* tone)
|
|||
return Engine::dispatch(m);
|
||||
}
|
||||
|
||||
bool Channel::toneDetect(const char* sniffer)
|
||||
{
|
||||
if (null(sniffer))
|
||||
return false;
|
||||
Message m("chan.attach");
|
||||
complete(m,true);
|
||||
m.userData(this);
|
||||
m.setParam("sniffer",sniffer);
|
||||
m.setParam("single","yes");
|
||||
return Engine::dispatch(m);
|
||||
}
|
||||
|
||||
bool Channel::setDebug(Message& msg)
|
||||
{
|
||||
String str = msg.getValue("line");
|
||||
|
|
|
@ -616,6 +616,7 @@ DataEndpoint::~DataEndpoint()
|
|||
disconnect();
|
||||
setPeerRecord();
|
||||
setCallRecord();
|
||||
clearSniffers();
|
||||
setSource();
|
||||
setConsumer();
|
||||
}
|
||||
|
@ -748,6 +749,9 @@ void DataEndpoint::setSource(DataSource* source)
|
|||
if (m_callRecord->getConnSource())
|
||||
Debug(DebugWarn,"consumer source not cleared in %p",m_callRecord);
|
||||
}
|
||||
ObjList* l = m_sniffers.skipNull();
|
||||
for (; l; l = l->skipNext())
|
||||
DataTranslator::detachChain(temp,static_cast<DataConsumer*>(l->get()));
|
||||
temp->deref();
|
||||
}
|
||||
if (source) {
|
||||
|
@ -758,6 +762,9 @@ void DataEndpoint::setSource(DataSource* source)
|
|||
DataTranslator::attachChain(source,c2);
|
||||
if (m_callRecord)
|
||||
DataTranslator::attachChain(source,m_callRecord);
|
||||
ObjList* l = m_sniffers.skipNull();
|
||||
for (; l; l = l->skipNext())
|
||||
DataTranslator::attachChain(source,static_cast<DataConsumer*>(l->get()));
|
||||
}
|
||||
m_source = source;
|
||||
if (c1)
|
||||
|
@ -842,6 +849,53 @@ void DataEndpoint::setCallRecord(DataConsumer* consumer)
|
|||
}
|
||||
}
|
||||
|
||||
bool DataEndpoint::addSniffer(DataConsumer* sniffer)
|
||||
{
|
||||
if (!sniffer)
|
||||
return false;
|
||||
Lock lock(s_dataMutex);
|
||||
if (m_sniffers.find(sniffer))
|
||||
return false;
|
||||
if (!sniffer->ref())
|
||||
return false;
|
||||
XDebug(DebugInfo,"DataEndpoint::addSniffer(%p) s=%p [%p]",
|
||||
sniffer,m_source,this);
|
||||
m_sniffers.append(sniffer);
|
||||
if (m_source)
|
||||
DataTranslator::attachChain(m_source,sniffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DataEndpoint::delSniffer(DataConsumer* sniffer)
|
||||
{
|
||||
if (!sniffer)
|
||||
return false;
|
||||
Lock lock(s_dataMutex);
|
||||
XDebug(DebugInfo,"DataEndpoint::delSniffer(%p) s=%p [%p]",
|
||||
sniffer,m_source,this);
|
||||
if (!m_sniffers.remove(sniffer,false))
|
||||
return false;
|
||||
if (m_source)
|
||||
DataTranslator::detachChain(m_source,sniffer);
|
||||
sniffer->deref();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataEndpoint::clearSniffers()
|
||||
{
|
||||
Lock lock(s_dataMutex);
|
||||
for (;;) {
|
||||
DataConsumer* sniffer = static_cast<DataConsumer*>(m_sniffers.remove(false));
|
||||
if (!sniffer)
|
||||
return;
|
||||
XDebug(DebugInfo,"DataEndpoint::clearSniffers() sn=%p s=%p [%p]",
|
||||
sniffer,m_source,this);
|
||||
if (m_source)
|
||||
DataTranslator::detachChain(m_source,sniffer);
|
||||
sniffer->deref();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ThreadedSource::~ThreadedSource()
|
||||
{
|
||||
|
|
|
@ -956,6 +956,7 @@ YateH323Connection::YateH323Connection(YateH323EndPoint& endpoint,
|
|||
|
||||
CallEndpoint* ch = YOBJECT(CallEndpoint,msg->userData());
|
||||
if (ch && ch->connect(m_chan,msg->getValue("reason"))) {
|
||||
m_chan->callConnect(*msg);
|
||||
m_chan->setTarget(msg->getValue("id"));
|
||||
msg->setParam("peerid",m_chan->id());
|
||||
msg->setParam("targetid",m_chan->id());
|
||||
|
|
|
@ -898,8 +898,10 @@ bool PriChan::call(Message &msg, const char *called)
|
|||
CallEndpoint *ch = static_cast<CallEndpoint*>(msg.userData());
|
||||
if (ch) {
|
||||
openData(lookup(layer1,dict_str2law),msg.getIntValue("cancelecho",dict_numtaps));
|
||||
if (connect(ch,msg.getValue("reason")))
|
||||
if (connect(ch,msg.getValue("reason"))) {
|
||||
msg.setParam("peerid",id());
|
||||
callConnect(msg);
|
||||
}
|
||||
m_targetid = msg.getValue("id");
|
||||
msg.setParam("targetid",id());
|
||||
}
|
||||
|
|
|
@ -34,35 +34,41 @@ namespace { // anonymous
|
|||
#define NPOLES 2
|
||||
#define GAIN 1.167519293e+02
|
||||
|
||||
#define FAX_THRESHOLD 2000.0
|
||||
#define FAX_THRESHOLD_ABS 2000.0
|
||||
#define FAX_THRESHOLD_REL 0.8
|
||||
|
||||
class ToneConsumer : public DataConsumer
|
||||
{
|
||||
public:
|
||||
ToneConsumer(const String &id);
|
||||
ToneConsumer(const String& id, const String& name);
|
||||
virtual ~ToneConsumer();
|
||||
virtual void Consume(const DataBlock& data, unsigned long tStamp);
|
||||
virtual const String& toString() const
|
||||
{ return m_name; }
|
||||
inline const String& id() const
|
||||
{ return m_id; }
|
||||
private:
|
||||
void init();
|
||||
private:
|
||||
static void update(double& avg, double val);
|
||||
String m_id;
|
||||
String m_name;
|
||||
bool m_found;
|
||||
float m_xv[NZEROS+1], m_yv[NPOLES+1], m_avg;
|
||||
double m_xv[NZEROS+1], m_yv[NPOLES+1];
|
||||
double m_pwr, m_sig;
|
||||
};
|
||||
|
||||
class DetectHandler : public MessageHandler
|
||||
class AttachHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
DetectHandler() : MessageHandler("chan.detectdtmf") { }
|
||||
virtual bool received(Message &msg);
|
||||
AttachHandler() : MessageHandler("chan.attach") { }
|
||||
virtual bool received(Message& msg);
|
||||
};
|
||||
|
||||
class RecordHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
RecordHandler() : MessageHandler("chan.record") { }
|
||||
virtual bool received(Message &msg);
|
||||
virtual bool received(Message& msg);
|
||||
};
|
||||
|
||||
class ToneDetectorModule : public Module
|
||||
|
@ -78,10 +84,11 @@ private:
|
|||
static ToneDetectorModule plugin;
|
||||
|
||||
|
||||
ToneConsumer::ToneConsumer(const String &id)
|
||||
: m_id(id), m_found(false)
|
||||
ToneConsumer::ToneConsumer(const String& id, const String& name)
|
||||
: m_id(id), m_name(name), m_found(false)
|
||||
{
|
||||
Debug(&plugin,DebugAll,"ToneConsumer::ToneConsumer(%s) [%p]",id.c_str(),this);
|
||||
Debug(&plugin,DebugAll,"ToneConsumer::ToneConsumer(%s,'%s') [%p]",
|
||||
id.c_str(),name.c_str(),this);
|
||||
init();
|
||||
}
|
||||
|
||||
|
@ -94,7 +101,12 @@ void ToneConsumer::init()
|
|||
{
|
||||
m_xv[0] = m_xv[1] = 0.0;
|
||||
m_yv[0] = m_yv[1] = 0.0;
|
||||
m_avg = 0.0;
|
||||
m_pwr = m_sig = 0.0;
|
||||
}
|
||||
|
||||
void ToneConsumer::update(double& avg, double val)
|
||||
{
|
||||
avg = 0.9*avg + 0.1*val*val;
|
||||
}
|
||||
|
||||
void ToneConsumer::Consume(const DataBlock& data, unsigned long timeDelta)
|
||||
|
@ -105,15 +117,17 @@ void ToneConsumer::Consume(const DataBlock& data, unsigned long timeDelta)
|
|||
for (unsigned int i=0; i<data.length(); i+=2) {
|
||||
// mkfilter generated CNG detector (1100Hz)
|
||||
m_xv[0] = m_xv[1]; m_xv[1] = m_xv[2];
|
||||
m_xv[2] = *s++ / GAIN;
|
||||
m_yv[0] = m_yv[1]; m_yv[1] = m_yv[2];
|
||||
m_xv[2] = *s++ / GAIN;
|
||||
m_yv[2] = (m_xv[2] - m_xv[0]) +
|
||||
(-0.9828696621 * m_yv[0]) +
|
||||
( 1.2877708321 * m_yv[1]);
|
||||
m_avg = 0.9*m_avg + 0.1*fabs(m_yv[2]);
|
||||
update(m_pwr,m_xv[2]);
|
||||
update(m_sig,m_yv[2]);
|
||||
|
||||
if (m_avg > FAX_THRESHOLD) {
|
||||
DDebug(DebugInfo,"Fax detected on %s, average=%f",m_id.c_str(),m_avg);
|
||||
if ((m_sig > FAX_THRESHOLD_ABS) && (m_sig > m_pwr*FAX_THRESHOLD_REL)) {
|
||||
DDebug(&plugin,DebugInfo,"Fax detected on %s, signal=%f, total=%f",
|
||||
m_id.c_str(),m_sig,m_pwr);
|
||||
// prepare for new detection
|
||||
init();
|
||||
m_found = true;
|
||||
|
@ -121,46 +135,58 @@ void ToneConsumer::Consume(const DataBlock& data, unsigned long timeDelta)
|
|||
m->addParam("message","call.fax");
|
||||
m->addParam("id",m_id);
|
||||
Engine::enqueue(m);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
XDebug(&plugin,DebugInfo,"Fax detector on %s: signal=%f, total=%f",
|
||||
m_id.c_str(),m_sig,m_pwr);
|
||||
}
|
||||
|
||||
|
||||
// Attach a tone detector on "chan.detectdtmf" - needs a DataSource
|
||||
bool DetectHandler::received(Message &msg)
|
||||
// Attach a tone detector on "chan.attach" as consumer or sniffer
|
||||
bool AttachHandler::received(Message& msg)
|
||||
{
|
||||
String src(msg.getValue("consumer"));
|
||||
if (src.null())
|
||||
return false;
|
||||
Regexp r("^tone/$");
|
||||
if (!src.matches(r))
|
||||
String cons(msg.getValue("consumer"));
|
||||
if (!cons.startsWith("tone/"))
|
||||
cons.clear();
|
||||
String snif(msg.getValue("sniffer"));
|
||||
if (!snif.startsWith("tone/"))
|
||||
snif.clear();
|
||||
if (cons.null() && snif.null())
|
||||
return false;
|
||||
CallEndpoint* ch = static_cast<CallEndpoint *>(msg.userObject("CallEndpoint"));
|
||||
if (ch) {
|
||||
DataSource* s = ch->getSource();
|
||||
if (s) {
|
||||
ToneConsumer* c = new ToneConsumer(ch->id());
|
||||
DataTranslator::attachChain(s,c);
|
||||
if (cons) {
|
||||
ToneConsumer* c = new ToneConsumer(ch->id(),cons);
|
||||
ch->setConsumer(c);
|
||||
c->deref();
|
||||
return true;
|
||||
}
|
||||
if (snif) {
|
||||
DataEndpoint* de = ch->setEndpoint();
|
||||
// try to reinit sniffer if one already exists
|
||||
ToneConsumer* c = static_cast<ToneConsumer*>(de->getSniffer(snif));
|
||||
if (c)
|
||||
c->init();
|
||||
else {
|
||||
c = new ToneConsumer(ch->id(),snif);
|
||||
de->addSniffer(c);
|
||||
c->deref();
|
||||
}
|
||||
}
|
||||
return msg.getBoolValue("single");
|
||||
}
|
||||
else
|
||||
Debug(DebugWarn,"ToneDetector attach request with no data source!");
|
||||
Debug(&plugin,DebugWarn,"ToneDetector attach request with no call endpoint!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Attach a tone detector on "chan.record" - needs just a CallEndpoint
|
||||
bool RecordHandler::received(Message &msg)
|
||||
bool RecordHandler::received(Message& msg)
|
||||
{
|
||||
String src(msg.getValue("call"));
|
||||
String id(msg.getValue("id"));
|
||||
if (src.null())
|
||||
return false;
|
||||
Regexp r("^tone/$");
|
||||
if (!src.matches(r))
|
||||
if (!src.startsWith("tone/"))
|
||||
return false;
|
||||
DataEndpoint* de = static_cast<DataEndpoint *>(msg.userObject("DataEndpoint"));
|
||||
CallEndpoint* ch = static_cast<CallEndpoint *>(msg.userObject("CallEndpoint"));
|
||||
|
@ -170,13 +196,13 @@ bool RecordHandler::received(Message &msg)
|
|||
de = ch->setEndpoint();
|
||||
}
|
||||
if (de) {
|
||||
ToneConsumer* c = new ToneConsumer(id);
|
||||
ToneConsumer* c = new ToneConsumer(id,src);
|
||||
de->setCallRecord(c);
|
||||
c->deref();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
Debug(DebugWarn,"ToneDetector record request with no call endpoint!");
|
||||
Debug(&plugin,DebugWarn,"ToneDetector record request with no call endpoint!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -198,7 +224,7 @@ void ToneDetectorModule::initialize()
|
|||
setup();
|
||||
if (m_first) {
|
||||
m_first = false;
|
||||
Engine::install(new DetectHandler);
|
||||
Engine::install(new AttachHandler);
|
||||
Engine::install(new RecordHandler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1118,6 +1118,7 @@ bool YIAXDriver::msgExecute(Message& msg, String& dest)
|
|||
tr->setUserData(conn);
|
||||
Channel* ch = static_cast<Channel*>(msg.userData());
|
||||
if (ch && conn->connect(ch,msg.getValue("reason"))) {
|
||||
conn->callConnect(msg);
|
||||
msg.setParam("peerid",conn->id());
|
||||
msg.setParam("targetid",conn->id());
|
||||
// Enable trunking if trunkout parameter is enabled
|
||||
|
|
|
@ -3505,6 +3505,7 @@ bool SIPDriver::msgExecute(Message& msg, String& dest)
|
|||
if (conn->getTransaction()) {
|
||||
CallEndpoint* ch = static_cast<CallEndpoint*>(msg.userData());
|
||||
if (ch && conn->connect(ch,msg.getValue("reason"))) {
|
||||
conn->callConnect(msg);
|
||||
msg.setParam("peerid",conn->id());
|
||||
msg.setParam("targetid",conn->id());
|
||||
conn->deref();
|
||||
|
|
43
yatephone.h
43
yatephone.h
|
@ -884,6 +884,33 @@ public:
|
|||
inline DataConsumer* getCallRecord() const
|
||||
{ return m_callRecord; }
|
||||
|
||||
/**
|
||||
* Adds a data consumer to the list of sniffers of the local call data
|
||||
* @param sniffer Pointer to the DataConsumer to add to sniffer list
|
||||
* @return True if the sniffer was added to list, false if NULL or already added
|
||||
*/
|
||||
bool addSniffer(DataConsumer* sniffer);
|
||||
|
||||
/**
|
||||
* Remove a data consumer from the list of sniffers of the local call data
|
||||
* @param sniffer Pointer to the DataConsumer to remove from sniffer list
|
||||
* @return True if the sniffer was removed from list
|
||||
*/
|
||||
bool delSniffer(DataConsumer* sniffer);
|
||||
|
||||
/**
|
||||
* Find a sniffer by name
|
||||
* @param name Name of the sniffer to find
|
||||
* @return Pointer to DataConsumer or NULL if not found
|
||||
*/
|
||||
inline DataConsumer* getSniffer(const String& name)
|
||||
{ return static_cast<DataConsumer*>(m_sniffers[name]); }
|
||||
|
||||
/**
|
||||
* Removes all sniffers from the list and dereferences them
|
||||
*/
|
||||
void clearSniffers();
|
||||
|
||||
/**
|
||||
* Get a pointer to the peer endpoint
|
||||
* @return A pointer to the peer endpoint or NULL
|
||||
|
@ -922,6 +949,7 @@ private:
|
|||
CallEndpoint* m_call;
|
||||
DataConsumer* m_peerRecord;
|
||||
DataConsumer* m_callRecord;
|
||||
ObjList m_sniffers;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1454,6 +1482,13 @@ public:
|
|||
*/
|
||||
virtual void callRejected(const char* error, const char* reason = 0, const Message* msg = 0);
|
||||
|
||||
/**
|
||||
* Common processing after connecting the outgoing call, should be called
|
||||
* from Driver's msgExecute()
|
||||
* @param msg Notification call.execute message while being dispatched
|
||||
*/
|
||||
virtual void callConnect(Message& msg);
|
||||
|
||||
/**
|
||||
* Set the local debugging level
|
||||
* @param msg Debug setting message
|
||||
|
@ -1660,6 +1695,14 @@ protected:
|
|||
*/
|
||||
bool dtmfInband(const char* tone);
|
||||
|
||||
/**
|
||||
* Attempt to install a data sniffer to detect inband tones
|
||||
* Needs a tone detector module capable of attaching sniffer consumers.
|
||||
* @param sniffer Name of the sniffer to install
|
||||
* @return True on success
|
||||
*/
|
||||
bool toneDetect(const char* sniffer = "tone/*");
|
||||
|
||||
private:
|
||||
void init();
|
||||
Channel(); // no default constructor please
|
||||
|
|
Loading…
Reference in New Issue