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:
parent
39e6ae4ecb
commit
a497425f07
|
@ -183,5 +183,5 @@ context=default
|
|||
; do a DoS attack over your server.
|
||||
;maxqueue=5
|
||||
|
||||
; called: string: Default number to call
|
||||
called=8989989
|
||||
; called: string: Default number to call if not present in call setup
|
||||
;called=
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
; type= pri_net,pri_cpe
|
||||
|
||||
[general]
|
||||
;buflen=480
|
||||
;buflen=160
|
||||
;restart=0
|
||||
;dumpevents=no
|
||||
;dtmfinband=no
|
||||
|
||||
[span 1]
|
||||
;dgroup=w1g1
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
; type= pri_net,pri_cpe,bri_net_ptmp,bri_cpe_ptmp,bri_net,bri_cpe
|
||||
|
||||
[general]
|
||||
;buflen=480
|
||||
;buflen=160
|
||||
;restart=0
|
||||
;dumpevents=no
|
||||
;dtmfinband=no
|
||||
|
||||
[span 1]
|
||||
chans=31
|
||||
|
|
|
@ -36,7 +36,8 @@ extern int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req);
|
|||
|
||||
using namespace TelEngine;
|
||||
|
||||
static int s_buflen = 480;
|
||||
// default buffer length: 20 ms
|
||||
static int s_buflen = 160;
|
||||
|
||||
#ifdef PRI_NEW_SET_API
|
||||
#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);
|
||||
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_dplan = cfg.getIntValue(sect,"dialplan",dict_str2dplan,PRI_UNKNOWN);
|
||||
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
|
||||
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);
|
||||
char *caller = (char *)msg.getValue("caller");
|
||||
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");
|
||||
Engine::enqueue(m);
|
||||
|
||||
m_inband = m_span->inband();
|
||||
openData(lookup(ev.layer1,dict_str2law),0);
|
||||
|
||||
m = message("call.route");
|
||||
|
@ -858,8 +862,22 @@ bool PriChan::msgAnswered(Message& msg)
|
|||
|
||||
bool PriChan::msgTone(Message& msg, const char* tone)
|
||||
{
|
||||
for (; !null(tone); tone++)
|
||||
sendDigit(*tone);
|
||||
if (null(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;
|
||||
}
|
||||
|
||||
|
@ -1054,7 +1072,7 @@ void PriDriver::netParams(Configuration& cfg, const String& sect, int chans, int
|
|||
void PriDriver::init(const char* configName)
|
||||
{
|
||||
Configuration cfg(Engine::configFile(configName));
|
||||
s_buflen = cfg.getIntValue("general","buflen",480);
|
||||
s_buflen = cfg.getIntValue("general","buflen",160);
|
||||
if (!m_spans.count()) {
|
||||
int chan1 = 1;
|
||||
for (int span = 1;;span++) {
|
||||
|
|
|
@ -72,6 +72,8 @@ public:
|
|||
{ return m_pres; }
|
||||
inline unsigned int overlapped() const
|
||||
{ return m_overlapped; }
|
||||
inline bool inband() const
|
||||
{ return m_inband; }
|
||||
inline bool outOfOrder() const
|
||||
{ return !m_ok; }
|
||||
inline int buflen() const
|
||||
|
@ -105,6 +107,7 @@ protected:
|
|||
int m_pres;
|
||||
int m_buflen;
|
||||
int m_layer1;
|
||||
bool m_inband;
|
||||
unsigned int m_overlapped;
|
||||
String m_callednumber;
|
||||
struct pri *m_pri;
|
||||
|
@ -160,6 +163,8 @@ public:
|
|||
{ return m_abschan; }
|
||||
inline bool inUse() const
|
||||
{ return (m_ring || m_call); }
|
||||
inline bool inband() const
|
||||
{ return m_inband; }
|
||||
void ring(pri_event_ring &ev);
|
||||
void hangup(int cause = 0);
|
||||
void sendDigit(char digit);
|
||||
|
@ -187,6 +192,7 @@ protected:
|
|||
unsigned int m_bufsize;
|
||||
int m_abschan;
|
||||
bool m_isdn;
|
||||
bool m_inband;
|
||||
};
|
||||
|
||||
class PriDriver : public Driver
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
// 40ms silence, 120ms tone, 40ms silence, total 200ms - slow but safe
|
||||
#define DTMF_LEN 960
|
||||
#define DTMF_GAP 320
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static ObjList tones;
|
||||
|
@ -93,7 +97,7 @@ public:
|
|||
static ToneSource* getTone(String& tone);
|
||||
static const Tone* getBlock(String& tone);
|
||||
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:
|
||||
ToneSource();
|
||||
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 } };
|
||||
|
||||
// 20ms silence, 85ms tone, 20ms silence, total 125ms
|
||||
#define DTMF_GAP 160
|
||||
#define DTMF_LEN 680
|
||||
|
||||
#define MAKE_DTMF(s) { \
|
||||
{ DTMF_GAP, 0 }, \
|
||||
{ DTMF_LEN, ToneData::getData(s)->data() }, \
|
||||
|
@ -204,14 +204,14 @@ static const Tone t_dtmf[][4] = {
|
|||
MAKE_DTMF("1209+770"),
|
||||
MAKE_DTMF("1336+770"),
|
||||
MAKE_DTMF("1477+770"),
|
||||
MAKE_DTMF("1209+825"),
|
||||
MAKE_DTMF("1336+825"),
|
||||
MAKE_DTMF("1477+825"),
|
||||
MAKE_DTMF("1209+852"),
|
||||
MAKE_DTMF("1336+852"),
|
||||
MAKE_DTMF("1477+852"),
|
||||
MAKE_DTMF("1209+941"),
|
||||
MAKE_DTMF("1477+941"),
|
||||
MAKE_DTMF("1633+697"),
|
||||
MAKE_DTMF("1633+770"),
|
||||
MAKE_DTMF("1633+825"),
|
||||
MAKE_DTMF("1633+852"),
|
||||
MAKE_DTMF("1633+941")
|
||||
};
|
||||
#undef MAKE_DTMF
|
||||
|
@ -346,7 +346,7 @@ const short* ToneData::data()
|
|||
else
|
||||
y += z;
|
||||
}
|
||||
*tmp++ = (short)(y*10000);
|
||||
*tmp++ = (short)(y*5000);
|
||||
}
|
||||
}
|
||||
m_data = dat;
|
||||
|
@ -372,14 +372,14 @@ ToneData* ToneData::getData(const char* desc)
|
|||
|
||||
ToneSource::ToneSource()
|
||||
: 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);
|
||||
}
|
||||
|
||||
ToneSource::ToneSource(String& tone)
|
||||
: 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);
|
||||
m_tone = getBlock(tone);
|
||||
|
@ -423,12 +423,15 @@ const Tone* ToneSource::getBlock(String& tone)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Build an user defined cadence
|
||||
Tone* ToneSource::buildCadence(const String& desc)
|
||||
{
|
||||
// TBD
|
||||
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())
|
||||
return 0;
|
||||
|
@ -438,7 +441,7 @@ Tone* ToneSource::buildDtmf(const String& dtmf)
|
|||
Tone* t = tmp;
|
||||
|
||||
for (unsigned int i = 0; i < dtmf.length(); i++) {
|
||||
t->nsamples = i ? DTMF_GAP : (2*DTMF_GAP);
|
||||
t->nsamples = gap;
|
||||
t->data = 0;
|
||||
t++;
|
||||
|
||||
|
@ -453,12 +456,12 @@ Tone* ToneSource::buildDtmf(const String& dtmf)
|
|||
c -= ('a' - 12);
|
||||
else c = -1;
|
||||
|
||||
t->nsamples = DTMF_LEN;
|
||||
t->data = ((c > 0) && (c < 16)) ? t_dtmf[c][1].data : 0;
|
||||
t->nsamples = len;
|
||||
t->data = ((c >= 0) && (c < 16)) ? t_dtmf[c][1].data : 0;
|
||||
t++;
|
||||
}
|
||||
|
||||
t->nsamples = DTMF_GAP;
|
||||
t->nsamples = gap;
|
||||
t->data = 0;
|
||||
t++;
|
||||
t->nsamples = 0;
|
||||
|
@ -548,13 +551,21 @@ TempSource::TempSource(String& desc)
|
|||
return;
|
||||
if (desc.startSkip("*",false))
|
||||
m_repeat = 0;
|
||||
// try first the named tones
|
||||
m_tone = getBlock(desc);
|
||||
if (m_tone)
|
||||
return;
|
||||
// for performance reason accept an entire string of DTMFs
|
||||
if (desc.startSkip("dtmfstr/",false)) {
|
||||
m_tone = m_single = buildDtmf(desc);
|
||||
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);
|
||||
if (!td)
|
||||
return;
|
||||
|
@ -675,6 +686,9 @@ bool AttachHandler::received(Message& msg)
|
|||
return false;
|
||||
}
|
||||
|
||||
// if single attach was requested we can return true if everything is ok
|
||||
bool ret = msg.getBoolValue("single");
|
||||
|
||||
Lock lock(__plugin);
|
||||
if (src) {
|
||||
ToneSource* t = ToneSource::getTone(src);
|
||||
|
@ -683,8 +697,10 @@ bool AttachHandler::received(Message& msg)
|
|||
t->deref();
|
||||
msg.clearParam("source");
|
||||
}
|
||||
else
|
||||
else {
|
||||
Debug(DebugWarn,"No source tone '%s' could be attached to %p",src.c_str(),de);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
if (ovr) {
|
||||
DataConsumer* c = de->getConsumer();
|
||||
|
@ -692,13 +708,17 @@ bool AttachHandler::received(Message& msg)
|
|||
TempSource* t = new TempSource(ovr);
|
||||
if (DataTranslator::attachChain(t,c,true) && t->startup())
|
||||
msg.clearParam("override");
|
||||
else
|
||||
else {
|
||||
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);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ToneGenDriver::statusModule(String& str)
|
||||
|
|
|
@ -261,12 +261,13 @@ void WaveSource::run()
|
|||
tpos += (r*(u_int64_t)1000000/m_brate);
|
||||
} while (r > 0);
|
||||
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");
|
||||
m->addParam("targetid",m_id);
|
||||
m->userData(m_chan);
|
||||
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);
|
||||
if (m_chan && m_autoclose)
|
||||
m_chan->disconnect("eof");
|
||||
if (!m_chan)
|
||||
deref();
|
||||
}
|
||||
|
||||
WaveConsumer::WaveConsumer(const String& file, CallEndpoint* chan, unsigned maxlen)
|
||||
|
@ -375,7 +378,7 @@ WaveChan::~WaveChan()
|
|||
|
||||
bool AttachHandler::received(Message &msg)
|
||||
{
|
||||
int more = 2;
|
||||
int more = 3;
|
||||
String src(msg.getValue("source"));
|
||||
if (src.null())
|
||||
more--;
|
||||
|
@ -415,9 +418,33 @@ bool AttachHandler::received(Message &msg)
|
|||
else
|
||||
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;
|
||||
|
||||
// if single attach was requested we can return true if everything is ok
|
||||
bool ret = msg.getBoolValue("single");
|
||||
|
||||
String ml(msg.getValue("maxlen"));
|
||||
unsigned maxlen = ml.toInteger(0);
|
||||
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());
|
||||
if (!cons.null())
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -434,6 +463,7 @@ bool AttachHandler::received(Message &msg)
|
|||
s->setNotify(msg.getValue("notify"));
|
||||
ch->setSource(s);
|
||||
s->deref();
|
||||
msg.clearParam("source");
|
||||
}
|
||||
|
||||
if (!cons.null()) {
|
||||
|
@ -441,10 +471,30 @@ bool AttachHandler::received(Message &msg)
|
|||
c->setNotify(msg.getValue("notify"));
|
||||
ch->setConsumer(c);
|
||||
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
|
||||
return !more;
|
||||
return ret && !more;
|
||||
}
|
||||
|
||||
bool RecordHandler::received(Message &msg)
|
||||
|
|
Loading…
Reference in New Issue