Override (local announcement) fully supported in tone and wavefile.

Inband DTMF for PRI channels by using the tone generator.
Reduced the default PRI buffer from 60 to 20 msec.


git-svn-id: http://yate.null.ro/svn/yate/trunk@528 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2005-09-28 22:06:11 +00:00
parent 39e6ae4ecb
commit a497425f07
7 changed files with 130 additions and 34 deletions

View File

@ -183,5 +183,5 @@ context=default
; do a DoS attack over your server. ; do a DoS attack over your server.
;maxqueue=5 ;maxqueue=5
; called: string: Default number to call ; called: string: Default number to call if not present in call setup
called=8989989 ;called=

View File

@ -13,9 +13,10 @@
; type= pri_net,pri_cpe ; type= pri_net,pri_cpe
[general] [general]
;buflen=480 ;buflen=160
;restart=0 ;restart=0
;dumpevents=no ;dumpevents=no
;dtmfinband=no
[span 1] [span 1]
;dgroup=w1g1 ;dgroup=w1g1

View File

@ -13,9 +13,10 @@
; type= pri_net,pri_cpe,bri_net_ptmp,bri_cpe_ptmp,bri_net,bri_cpe ; type= pri_net,pri_cpe,bri_net_ptmp,bri_cpe_ptmp,bri_net,bri_cpe
[general] [general]
;buflen=480 ;buflen=160
;restart=0 ;restart=0
;dumpevents=no ;dumpevents=no
;dtmfinband=no
[span 1] [span 1]
chans=31 chans=31

View File

@ -36,7 +36,8 @@ extern int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req);
using namespace TelEngine; using namespace TelEngine;
static int s_buflen = 480; // default buffer length: 20 ms
static int s_buflen = 160;
#ifdef PRI_NEW_SET_API #ifdef PRI_NEW_SET_API
#define PRI_CB_STR struct pri *pri, #define PRI_CB_STR struct pri *pri,
@ -245,6 +246,7 @@ PriSpan::PriSpan(struct pri *_pri, PriDriver* driver, int span, int first, int c
Debug(m_driver,DebugAll,"PriSpan::PriSpan() [%p]",this); Debug(m_driver,DebugAll,"PriSpan::PriSpan() [%p]",this);
int buflength = cfg.getIntValue(sect,"buflen", s_buflen); int buflength = cfg.getIntValue(sect,"buflen", s_buflen);
m_inband = cfg.getBoolValue(sect,"dtmfinband",cfg.getBoolValue("general","dtmfinband"));
m_layer1 = cfg.getIntValue(sect,"format",dict_str2law,(chans == 24) ? PRI_LAYER_1_ULAW : PRI_LAYER_1_ALAW); m_layer1 = cfg.getIntValue(sect,"format",dict_str2law,(chans == 24) ? PRI_LAYER_1_ULAW : PRI_LAYER_1_ALAW);
m_dplan = cfg.getIntValue(sect,"dialplan",dict_str2dplan,PRI_UNKNOWN); m_dplan = cfg.getIntValue(sect,"dialplan",dict_str2dplan,PRI_UNKNOWN);
m_pres = cfg.getIntValue(sect,"presentation",dict_str2pres,PRES_ALLOWED_USER_NUMBER_NOT_SCREENED); m_pres = cfg.getIntValue(sect,"presentation",dict_str2pres,PRES_ALLOWED_USER_NUMBER_NOT_SCREENED);
@ -753,6 +755,7 @@ bool PriChan::call(Message &msg, const char *called)
} }
else else
msg.userData(this); msg.userData(this);
m_inband = msg.getBoolValue("dtmfinband",m_span->inband());
Output("Calling '%s' on %s (%d/%d)",called,id().c_str(),m_span->span(),m_chan); Output("Calling '%s' on %s (%d/%d)",called,id().c_str(),m_span->span(),m_chan);
char *caller = (char *)msg.getValue("caller"); char *caller = (char *)msg.getValue("caller");
int callerplan = msg.getIntValue("callerplan",dict_str2dplan,m_span->dplan()); int callerplan = msg.getIntValue("callerplan",dict_str2dplan,m_span->dplan());
@ -805,6 +808,7 @@ void PriChan::ring(pri_event_ring &ev)
m->addParam("direction","incoming"); m->addParam("direction","incoming");
Engine::enqueue(m); Engine::enqueue(m);
m_inband = m_span->inband();
openData(lookup(ev.layer1,dict_str2law),0); openData(lookup(ev.layer1,dict_str2law),0);
m = message("call.route"); m = message("call.route");
@ -858,8 +862,22 @@ bool PriChan::msgAnswered(Message& msg)
bool PriChan::msgTone(Message& msg, const char* tone) bool PriChan::msgTone(Message& msg, const char* tone)
{ {
for (; !null(tone); tone++) if (null(tone))
sendDigit(*tone); return false;
if (m_inband) {
Message m("chan.attach");
complete(m,true);
m.userData(this);
String tmp("tone/dtmfstr/");
tmp += tone;
m.setParam("override",tmp);
m.setParam("single","yes");
if (Engine::dispatch(m))
return true;
// if we failed try to send as signalling anyway
}
while (*tone)
sendDigit(*tone++);
return true; return true;
} }
@ -1054,7 +1072,7 @@ void PriDriver::netParams(Configuration& cfg, const String& sect, int chans, int
void PriDriver::init(const char* configName) void PriDriver::init(const char* configName)
{ {
Configuration cfg(Engine::configFile(configName)); Configuration cfg(Engine::configFile(configName));
s_buflen = cfg.getIntValue("general","buflen",480); s_buflen = cfg.getIntValue("general","buflen",160);
if (!m_spans.count()) { if (!m_spans.count()) {
int chan1 = 1; int chan1 = 1;
for (int span = 1;;span++) { for (int span = 1;;span++) {

View File

@ -72,6 +72,8 @@ public:
{ return m_pres; } { return m_pres; }
inline unsigned int overlapped() const inline unsigned int overlapped() const
{ return m_overlapped; } { return m_overlapped; }
inline bool inband() const
{ return m_inband; }
inline bool outOfOrder() const inline bool outOfOrder() const
{ return !m_ok; } { return !m_ok; }
inline int buflen() const inline int buflen() const
@ -105,6 +107,7 @@ protected:
int m_pres; int m_pres;
int m_buflen; int m_buflen;
int m_layer1; int m_layer1;
bool m_inband;
unsigned int m_overlapped; unsigned int m_overlapped;
String m_callednumber; String m_callednumber;
struct pri *m_pri; struct pri *m_pri;
@ -160,6 +163,8 @@ public:
{ return m_abschan; } { return m_abschan; }
inline bool inUse() const inline bool inUse() const
{ return (m_ring || m_call); } { return (m_ring || m_call); }
inline bool inband() const
{ return m_inband; }
void ring(pri_event_ring &ev); void ring(pri_event_ring &ev);
void hangup(int cause = 0); void hangup(int cause = 0);
void sendDigit(char digit); void sendDigit(char digit);
@ -187,6 +192,7 @@ protected:
unsigned int m_bufsize; unsigned int m_bufsize;
int m_abschan; int m_abschan;
bool m_isdn; bool m_isdn;
bool m_inband;
}; };
class PriDriver : public Driver class PriDriver : public Driver

View File

@ -32,6 +32,10 @@
#define M_PI 3.14159265358979323846 #define M_PI 3.14159265358979323846
#endif #endif
// 40ms silence, 120ms tone, 40ms silence, total 200ms - slow but safe
#define DTMF_LEN 960
#define DTMF_GAP 320
using namespace TelEngine; using namespace TelEngine;
static ObjList tones; static ObjList tones;
@ -93,7 +97,7 @@ public:
static ToneSource* getTone(String& tone); static ToneSource* getTone(String& tone);
static const Tone* getBlock(String& tone); static const Tone* getBlock(String& tone);
static Tone* buildCadence(const String& desc); static Tone* buildCadence(const String& desc);
static Tone* buildDtmf(const String& dtmf); static Tone* buildDtmf(const String& dtmf, int len = DTMF_LEN, int gap = DTMF_GAP);
protected: protected:
ToneSource(); ToneSource();
ToneSource(String& tone); ToneSource(String& tone);
@ -185,10 +189,6 @@ static const Tone t_silence[] = { { 8000, 0 }, { 0, 0 } };
static const Tone t_noise[] = { { 2000, ToneData::getData("noise")->data() }, { 0, 0 } }; static const Tone t_noise[] = { { 2000, ToneData::getData("noise")->data() }, { 0, 0 } };
// 20ms silence, 85ms tone, 20ms silence, total 125ms
#define DTMF_GAP 160
#define DTMF_LEN 680
#define MAKE_DTMF(s) { \ #define MAKE_DTMF(s) { \
{ DTMF_GAP, 0 }, \ { DTMF_GAP, 0 }, \
{ DTMF_LEN, ToneData::getData(s)->data() }, \ { DTMF_LEN, ToneData::getData(s)->data() }, \
@ -204,14 +204,14 @@ static const Tone t_dtmf[][4] = {
MAKE_DTMF("1209+770"), MAKE_DTMF("1209+770"),
MAKE_DTMF("1336+770"), MAKE_DTMF("1336+770"),
MAKE_DTMF("1477+770"), MAKE_DTMF("1477+770"),
MAKE_DTMF("1209+825"), MAKE_DTMF("1209+852"),
MAKE_DTMF("1336+825"), MAKE_DTMF("1336+852"),
MAKE_DTMF("1477+825"), MAKE_DTMF("1477+852"),
MAKE_DTMF("1209+941"), MAKE_DTMF("1209+941"),
MAKE_DTMF("1477+941"), MAKE_DTMF("1477+941"),
MAKE_DTMF("1633+697"), MAKE_DTMF("1633+697"),
MAKE_DTMF("1633+770"), MAKE_DTMF("1633+770"),
MAKE_DTMF("1633+825"), MAKE_DTMF("1633+852"),
MAKE_DTMF("1633+941") MAKE_DTMF("1633+941")
}; };
#undef MAKE_DTMF #undef MAKE_DTMF
@ -346,7 +346,7 @@ const short* ToneData::data()
else else
y += z; y += z;
} }
*tmp++ = (short)(y*10000); *tmp++ = (short)(y*5000);
} }
} }
m_data = dat; m_data = dat;
@ -372,14 +372,14 @@ ToneData* ToneData::getData(const char* desc)
ToneSource::ToneSource() ToneSource::ToneSource()
: m_tone(0), m_repeat(1), : m_tone(0), m_repeat(1),
m_data(0,480), m_brate(16000), m_total(0), m_time(0) m_data(0,320), m_brate(16000), m_total(0), m_time(0)
{ {
Debug(&__plugin,DebugAll,"ToneSource::ToneSource() [%p]",this); Debug(&__plugin,DebugAll,"ToneSource::ToneSource() [%p]",this);
} }
ToneSource::ToneSource(String& tone) ToneSource::ToneSource(String& tone)
: m_name(tone), m_tone(0), m_repeat(0), : m_name(tone), m_tone(0), m_repeat(0),
m_data(0,480), m_brate(16000), m_total(0), m_time(0) m_data(0,320), m_brate(16000), m_total(0), m_time(0)
{ {
Debug(&__plugin,DebugAll,"ToneSource::ToneSource(\"%s\") [%p]",tone.c_str(),this); Debug(&__plugin,DebugAll,"ToneSource::ToneSource(\"%s\") [%p]",tone.c_str(),this);
m_tone = getBlock(tone); m_tone = getBlock(tone);
@ -423,12 +423,15 @@ const Tone* ToneSource::getBlock(String& tone)
return 0; return 0;
} }
// Build an user defined cadence
Tone* ToneSource::buildCadence(const String& desc) Tone* ToneSource::buildCadence(const String& desc)
{ {
// TBD
return 0; return 0;
} }
Tone* ToneSource::buildDtmf(const String& dtmf) // Build a cadence out of DTMFs
Tone* ToneSource::buildDtmf(const String& dtmf, int len, int gap)
{ {
if (dtmf.null()) if (dtmf.null())
return 0; return 0;
@ -438,7 +441,7 @@ Tone* ToneSource::buildDtmf(const String& dtmf)
Tone* t = tmp; Tone* t = tmp;
for (unsigned int i = 0; i < dtmf.length(); i++) { for (unsigned int i = 0; i < dtmf.length(); i++) {
t->nsamples = i ? DTMF_GAP : (2*DTMF_GAP); t->nsamples = gap;
t->data = 0; t->data = 0;
t++; t++;
@ -453,12 +456,12 @@ Tone* ToneSource::buildDtmf(const String& dtmf)
c -= ('a' - 12); c -= ('a' - 12);
else c = -1; else c = -1;
t->nsamples = DTMF_LEN; t->nsamples = len;
t->data = ((c > 0) && (c < 16)) ? t_dtmf[c][1].data : 0; t->data = ((c >= 0) && (c < 16)) ? t_dtmf[c][1].data : 0;
t++; t++;
} }
t->nsamples = DTMF_GAP; t->nsamples = gap;
t->data = 0; t->data = 0;
t++; t++;
t->nsamples = 0; t->nsamples = 0;
@ -548,13 +551,21 @@ TempSource::TempSource(String& desc)
return; return;
if (desc.startSkip("*",false)) if (desc.startSkip("*",false))
m_repeat = 0; m_repeat = 0;
// try first the named tones
m_tone = getBlock(desc); m_tone = getBlock(desc);
if (m_tone) if (m_tone)
return; return;
// for performance reason accept an entire string of DTMFs
if (desc.startSkip("dtmfstr/",false)) { if (desc.startSkip("dtmfstr/",false)) {
m_tone = m_single = buildDtmf(desc); m_tone = m_single = buildDtmf(desc);
return; return;
} }
// or an entire user defined cadence of tones
if (desc.startSkip("cadence/",false)) {
m_tone = m_single = buildCadence(desc);
return;
}
// now try to build a single tone
ToneData* td = ToneData::getData(desc); ToneData* td = ToneData::getData(desc);
if (!td) if (!td)
return; return;
@ -675,6 +686,9 @@ bool AttachHandler::received(Message& msg)
return false; return false;
} }
// if single attach was requested we can return true if everything is ok
bool ret = msg.getBoolValue("single");
Lock lock(__plugin); Lock lock(__plugin);
if (src) { if (src) {
ToneSource* t = ToneSource::getTone(src); ToneSource* t = ToneSource::getTone(src);
@ -683,8 +697,10 @@ bool AttachHandler::received(Message& msg)
t->deref(); t->deref();
msg.clearParam("source"); msg.clearParam("source");
} }
else else {
Debug(DebugWarn,"No source tone '%s' could be attached to %p",src.c_str(),de); Debug(DebugWarn,"No source tone '%s' could be attached to %p",src.c_str(),de);
ret = false;
}
} }
if (ovr) { if (ovr) {
DataConsumer* c = de->getConsumer(); DataConsumer* c = de->getConsumer();
@ -692,13 +708,17 @@ bool AttachHandler::received(Message& msg)
TempSource* t = new TempSource(ovr); TempSource* t = new TempSource(ovr);
if (DataTranslator::attachChain(t,c,true) && t->startup()) if (DataTranslator::attachChain(t,c,true) && t->startup())
msg.clearParam("override"); msg.clearParam("override");
else else {
Debug(DebugWarn,"Override source tone '%s' failed to start [%p]",ovr.c_str(),t); Debug(DebugWarn,"Override source tone '%s' failed to start [%p]",ovr.c_str(),t);
ret = false;
}
} }
else else {
Debug(DebugWarn,"Requested override '%s' to missing consumer of %p",ovr.c_str(),de); Debug(DebugWarn,"Requested override '%s' to missing consumer of %p",ovr.c_str(),de);
ret = false;
}
} }
return false; return ret;
} }
void ToneGenDriver::statusModule(String& str) void ToneGenDriver::statusModule(String& str)

View File

@ -261,12 +261,13 @@ void WaveSource::run()
tpos += (r*(u_int64_t)1000000/m_brate); tpos += (r*(u_int64_t)1000000/m_brate);
} while (r > 0); } while (r > 0);
Debug(&__plugin,DebugAll,"WaveSource [%p] end of data [%p] [%s] ",this,m_chan,m_id.c_str()); Debug(&__plugin,DebugAll,"WaveSource [%p] end of data [%p] [%s] ",this,m_chan,m_id.c_str());
if (m_chan && !m_id.null()) { if (m_id) {
Message *m = new Message("chan.notify"); Message *m = new Message("chan.notify");
m->addParam("targetid",m_id); m->addParam("targetid",m_id);
m->userData(m_chan); m->userData(m_chan);
Engine::enqueue(m); Engine::enqueue(m);
m_chan->setSource(); if (m_chan && (m_chan->getSource() == this))
m_chan->setSource();
} }
} }
@ -275,6 +276,8 @@ void WaveSource::cleanup()
Debug(&__plugin,DebugAll,"WaveSource [%p] cleanup, total=%u",this,m_total); Debug(&__plugin,DebugAll,"WaveSource [%p] cleanup, total=%u",this,m_total);
if (m_chan && m_autoclose) if (m_chan && m_autoclose)
m_chan->disconnect("eof"); m_chan->disconnect("eof");
if (!m_chan)
deref();
} }
WaveConsumer::WaveConsumer(const String& file, CallEndpoint* chan, unsigned maxlen) WaveConsumer::WaveConsumer(const String& file, CallEndpoint* chan, unsigned maxlen)
@ -375,7 +378,7 @@ WaveChan::~WaveChan()
bool AttachHandler::received(Message &msg) bool AttachHandler::received(Message &msg)
{ {
int more = 2; int more = 3;
String src(msg.getValue("source")); String src(msg.getValue("source"));
if (src.null()) if (src.null())
more--; more--;
@ -415,9 +418,33 @@ bool AttachHandler::received(Message &msg)
else else
cons = ""; cons = "";
} }
if (src.null() && cons.null())
String ovr(msg.getValue("override"));
if (ovr.null())
more--;
else {
Regexp r("^wave/\\([^/]*\\)/\\(.*\\)$");
if (ovr.matches(r)) {
if (ovr.matchString(1) == "play") {
ovr = ovr.matchString(2);
more--;
}
else {
Debug(DebugWarn,"Could not attach override with method '%s', use 'play'",
ovr.matchString(1).c_str());
ovr = "";
}
}
else
ovr = "";
}
if (src.null() && cons.null() && ovr.null())
return false; return false;
// if single attach was requested we can return true if everything is ok
bool ret = msg.getBoolValue("single");
String ml(msg.getValue("maxlen")); String ml(msg.getValue("maxlen"));
unsigned maxlen = ml.toInteger(0); unsigned maxlen = ml.toInteger(0);
CallEndpoint *ch = static_cast<CallEndpoint*>(msg.userData()); CallEndpoint *ch = static_cast<CallEndpoint*>(msg.userData());
@ -426,6 +453,8 @@ bool AttachHandler::received(Message &msg)
Debug(DebugWarn,"Wave source '%s' attach request with no data channel!",src.c_str()); Debug(DebugWarn,"Wave source '%s' attach request with no data channel!",src.c_str());
if (!cons.null()) if (!cons.null())
Debug(DebugWarn,"Wave consumer '%s' attach request with no data channel!",cons.c_str()); Debug(DebugWarn,"Wave consumer '%s' attach request with no data channel!",cons.c_str());
if (!ovr.null())
Debug(DebugWarn,"Wave override '%s' attach request with no data channel!",ovr.c_str());
return false; return false;
} }
@ -434,6 +463,7 @@ bool AttachHandler::received(Message &msg)
s->setNotify(msg.getValue("notify")); s->setNotify(msg.getValue("notify"));
ch->setSource(s); ch->setSource(s);
s->deref(); s->deref();
msg.clearParam("source");
} }
if (!cons.null()) { if (!cons.null()) {
@ -441,10 +471,30 @@ bool AttachHandler::received(Message &msg)
c->setNotify(msg.getValue("notify")); c->setNotify(msg.getValue("notify"));
ch->setConsumer(c); ch->setConsumer(c);
c->deref(); c->deref();
msg.clearParam("consumer");
}
while (!ovr.null()) {
DataConsumer* c = ch->getConsumer();
if (!c) {
Debug(DebugWarn,"Wave override '%s' attach request with no consumer!",ovr.c_str());
ret = false;
break;
}
WaveSource* s = new WaveSource(ovr,0,false);
s->setNotify(msg.getValue("notify"));
if (DataTranslator::attachChain(s,c,true))
msg.clearParam("override");
else {
Debug(DebugWarn,"Failed to override attach wave '%s' to consumer %p",ovr.c_str(),c);
s->deref();
ret = false;
}
break;
} }
// Stop dispatching if we handled all requested // Stop dispatching if we handled all requested
return !more; return ret && !more;
} }
bool RecordHandler::received(Message &msg) bool RecordHandler::received(Message &msg)