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:
paulc 2006-09-08 09:50:19 +00:00
parent 4db3bf1944
commit dbcc8c9fce
8 changed files with 195 additions and 39 deletions

View File

@ -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");

View File

@ -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()
{

View File

@ -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());

View File

@ -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());
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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();

View File

@ -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