Implemented sending of trunk without miniframes timestamp. Fixed timestamp handling when processing trunk without miniframes timestamps. Check for thread termination when parsing lists tp process transactions and trunks.
git-svn-id: http://yate.null.ro/svn/yate/trunk@5535 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
9d8f0a75b0
commit
3021fe3b2a
|
@ -32,6 +32,66 @@
|
|||
; Defaults to user-provided if missing or incorrect
|
||||
;screening=user-provided
|
||||
|
||||
; trunk_timestamps: boolean: Configure how trunked audio data is sent, enable it for
|
||||
; trunked data with timestamps and disable it to send trunked data without timestamps
|
||||
; This parameter is applied on reload
|
||||
; It can be overridden when routing by 'trunkin_timestamps' for incoming calls
|
||||
; or 'trunkout_timestamps' for outgoing calls
|
||||
; This parameter is applied when a new trunk is created (a trunked call is created and
|
||||
; there is no trunk for the same remote address)
|
||||
; Defaults to yes
|
||||
;trunk_timestamps=yes
|
||||
|
||||
; trunk_sendinterval: integer: Interval, in milliseconds, to send trunked trunked audio data
|
||||
; The interval is measured from the first packet put in a trunk
|
||||
; Trunked data is sent when this interval ellapses or the buffer is full
|
||||
; This parameter is applied on reload
|
||||
; It can be overridden when routing by 'trunkin_sendinterval' for incoming calls
|
||||
; or 'trunkout_sendinterval' for outgoing calls
|
||||
; This parameter is applied when a new trunk is created (a trunked call is created and
|
||||
; there is no trunk for the same remote address)
|
||||
; Minimum allowed value is 5
|
||||
; Defaults to 20
|
||||
;trunk_sendinterval=20
|
||||
|
||||
; trunk_maxlen: integer: Maximum value for trunked data frames.
|
||||
; This value includes the length if trunk frame header (8 bytes)
|
||||
; Trunked data is sent when the send interval ellapses or the buffer is full
|
||||
; This parameter is applied on reload
|
||||
; It can be overridden when routing by 'trunkin_maxlen' for incoming calls
|
||||
; or 'trunkout_maxlen' for outgoing calls
|
||||
; This parameter is applied when a new trunk is created (a trunked call is created and
|
||||
; there is no trunk for the same remote address)
|
||||
; Minimum allowed value is 20
|
||||
; Defaults to 1400
|
||||
;trunk_maxlen=1400
|
||||
|
||||
; trunk_nominits_sync_use_ts: boolean: Configure how to re-build timestamps when
|
||||
; processing incoming trunked audio without miniframe timestamps
|
||||
; When enabled the transaction will use trunk timestamp and last received full voice
|
||||
; frame time and timestamp to build miniframe timestamps
|
||||
; When disabled the transaction will use the time difference between current time and
|
||||
; last received full voice frame to build the miniframe timestamps
|
||||
; This parameter is applied on reload
|
||||
; It can be overridden when routing by 'trunkin_nominits_sync_use_ts' for incoming calls
|
||||
; or 'trunkout_nominits_sync_use_ts' for outgoing calls
|
||||
; Defaults to yes
|
||||
;trunk_nominits_sync_use_ts=yes
|
||||
|
||||
; trunk_nominits_ts_diff_restart: integer: The difference (in milliseconds) between
|
||||
; current timestamp and first timestamp of incoming trunked audio data without miniframe
|
||||
; timestamps at which to restart timestamps build data
|
||||
; This value is used when received trunk timestamp is older then first timestamp
|
||||
; If the difference is less then this value the miniframes will be dropped
|
||||
; This will deal with trunk timestamp wraparound or restarted by remote party
|
||||
; This parameter is ignored if trunk_nominits_sync_use_ts is disabled
|
||||
; This parameter is applied on reload
|
||||
; It can be overridden when routing by 'trunkin_nominits_ts_diff_restart' for incoming
|
||||
; calls or 'trunkout_nominits_ts_diff_restart' for outgoing calls
|
||||
; Minimum allowed value is 1000
|
||||
; Defaults to 5000
|
||||
;trunk_nominits_ts_diff_restart=5000
|
||||
|
||||
; calltoken_in: boolean: Use call token ip address authentication on incoming calls
|
||||
; Note: If the caller don't support the call token IAX extension the call request
|
||||
; will be ignored anyway
|
||||
|
|
|
@ -57,8 +57,7 @@ static void buildSecretDigest(String& buf, const String& secret, unsigned int t,
|
|||
|
||||
IAXEngine::IAXEngine(const char* iface, int port, u_int16_t transListCount, u_int16_t retransCount, u_int16_t retransInterval,
|
||||
u_int16_t authTimeout, u_int16_t transTimeout, u_int16_t maxFullFrameDataLen,
|
||||
u_int32_t format, u_int32_t capab, u_int32_t trunkSendInterval, bool authRequired,
|
||||
NamedList* params)
|
||||
u_int32_t format, u_int32_t capab, bool authRequired, NamedList* params)
|
||||
: Mutex(true,"IAXEngine"),
|
||||
m_lastGetEvIndex(0),
|
||||
m_authRequired(authRequired),
|
||||
|
@ -81,8 +80,7 @@ IAXEngine::IAXEngine(const char* iface, int port, u_int16_t transListCount, u_in
|
|||
m_adjustTsOutThreshold(IAX2_ADJUSTTSOUT_THRES),
|
||||
m_adjustTsOutOverrun(IAX2_ADJUSTTSOUT_OVER),
|
||||
m_adjustTsOutUnderrun(IAX2_ADJUSTTSOUT_UNDER),
|
||||
m_mutexTrunk(true,"IAXEngine::Trunk"),
|
||||
m_trunkSendInterval(trunkSendInterval)
|
||||
m_mutexTrunk(false,"IAXEngine::Trunk")
|
||||
{
|
||||
debugName("iaxengine");
|
||||
Debug(this,DebugAll,"Automatically request authentication set to '%s'.",
|
||||
|
@ -412,6 +410,7 @@ void IAXEngine::initialize(const NamedList& params)
|
|||
m_callerNumType = lookup(params["numtype"],IAXInfoElement::s_typeOfNumber);
|
||||
m_callingPres = lookup(params["presentation"],IAXInfoElement::s_presentation) |
|
||||
lookup(params["screening"],IAXInfoElement::s_screening);
|
||||
m_trunkInfoDef.init(params,"trunk_");
|
||||
initOutDataAdjust(params);
|
||||
}
|
||||
|
||||
|
@ -420,6 +419,8 @@ void IAXEngine::readSocket(SocketAddr& addr)
|
|||
unsigned char buf[1500];
|
||||
|
||||
while (1) {
|
||||
if (Thread::check(false))
|
||||
break;
|
||||
int len = m_socket.recvFrom(buf,sizeof(buf),addr);
|
||||
if (len == Socket::socketError()) {
|
||||
if (!m_socket.canRetry()) {
|
||||
|
@ -428,7 +429,7 @@ void IAXEngine::readSocket(SocketAddr& addr)
|
|||
Debug(this,DebugWarn,"Socket read error: %s (%d)",
|
||||
tmp.c_str(),m_socket.error());
|
||||
}
|
||||
Thread::idle(true);
|
||||
Thread::idle(false);
|
||||
continue;
|
||||
}
|
||||
addFrame(addr,buf,len);
|
||||
|
@ -471,10 +472,10 @@ bool IAXEngine::writeSocket(const void* buf, int len, const SocketAddr& addr,
|
|||
void IAXEngine::runGetEvents()
|
||||
{
|
||||
while (1) {
|
||||
if (!process()) {
|
||||
Thread::idle(true);
|
||||
continue;
|
||||
}
|
||||
if (Thread::check(false))
|
||||
break;
|
||||
if (!process())
|
||||
Thread::idle(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -537,21 +538,27 @@ void IAXEngine::decodeDateTime(u_int32_t dt, unsigned int& year, unsigned int& m
|
|||
sec = dt & 0x1f;
|
||||
}
|
||||
|
||||
bool IAXEngine::processTrunkFrames(u_int32_t time)
|
||||
bool IAXEngine::processTrunkFrames(const Time& time)
|
||||
{
|
||||
Lock lock(&m_mutexTrunk);
|
||||
Lock lck(m_mutexTrunk);
|
||||
bool sent = false;
|
||||
for (ObjList* l = m_trunkList.skipNull(); l; l = l->skipNext()) {
|
||||
for (ObjList* l = m_trunkList.skipNull(); l;) {
|
||||
if (Thread::check(false))
|
||||
break;
|
||||
IAXMetaTrunkFrame* frame = static_cast<IAXMetaTrunkFrame*>(l->get());
|
||||
// Frame has mini frame(s) ?
|
||||
if (!frame->timestamp())
|
||||
if (frame->refcount() != 1) {
|
||||
l = l->skipNext();
|
||||
if (frame->timerTick(time))
|
||||
sent = true;
|
||||
continue;
|
||||
int32_t interval = time - frame->timestamp();
|
||||
if (!interval || (interval && (u_int32_t)interval < m_trunkSendInterval))
|
||||
continue;
|
||||
// If the time wrapped around, send it. Worst case: we'll send an empty frame
|
||||
frame->send(time);
|
||||
sent = true;
|
||||
}
|
||||
Debug(this,DebugAll,
|
||||
"Removing trunk frame (%p) '%s:%d' timestamps=%s maxlen=%u interval=%ums",
|
||||
frame,frame->addr().host().c_str(),frame->addr().port(),
|
||||
String::boolText(frame->trunkTimestamps()),frame->maxLen(),
|
||||
frame->sendInterval());
|
||||
l->remove();
|
||||
l = l->skipNull();
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
@ -573,15 +580,18 @@ IAXEvent* IAXEngine::getEvent(u_int64_t time)
|
|||
// Find for incomplete transactions
|
||||
l = m_incompleteTransList.skipNull();
|
||||
for (; l; l = l->next()) {
|
||||
if (Thread::check(false))
|
||||
break;
|
||||
tr = static_cast<IAXTransaction*>(l->get());
|
||||
if (tr && 0 != (ev = tr->getEvent(time))) {
|
||||
unlock();
|
||||
return ev;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Find for complete transactions, start with current index
|
||||
while (m_lastGetEvIndex < m_transListCount) {
|
||||
if (Thread::check(false))
|
||||
break;
|
||||
l = m_transList[m_lastGetEvIndex++]->skipNull();
|
||||
if (!l)
|
||||
continue;
|
||||
|
@ -633,18 +643,16 @@ void IAXEngine::releaseCallNo(u_int16_t lcallno)
|
|||
m_lUsedCallNo[lcallno] = false;
|
||||
}
|
||||
|
||||
IAXTransaction* IAXEngine::startLocalTransaction(IAXTransaction::Type type, const SocketAddr& addr, IAXIEList& ieList, bool trunking)
|
||||
IAXTransaction* IAXEngine::startLocalTransaction(IAXTransaction::Type type,
|
||||
const SocketAddr& addr, IAXIEList& ieList)
|
||||
{
|
||||
Lock lock(this);
|
||||
u_int16_t lcn = generateCallNo();
|
||||
if (!lcn)
|
||||
return 0;
|
||||
IAXTransaction* tr = IAXTransaction::factoryOut(this,type,lcn,addr,ieList);
|
||||
if (tr) {
|
||||
if (tr)
|
||||
m_incompleteTransList.append(tr);
|
||||
if (trunking)
|
||||
enableTrunking(tr);
|
||||
}
|
||||
else
|
||||
releaseCallNo(lcn);
|
||||
return tr;
|
||||
|
@ -774,31 +782,55 @@ void IAXEngine::defaultEventHandler(IAXEvent* event)
|
|||
}
|
||||
}
|
||||
|
||||
void IAXEngine::enableTrunking(IAXTransaction* trans)
|
||||
void IAXEngine::enableTrunking(IAXTransaction* trans, const NamedList* params,
|
||||
const String& prefix)
|
||||
{
|
||||
if (!trans || trans->type() != IAXTransaction::New)
|
||||
return;
|
||||
Lock lock(&m_mutexTrunk);
|
||||
Lock lock(m_mutexTrunk);
|
||||
IAXMetaTrunkFrame* frame;
|
||||
// Already enabled ?
|
||||
for (ObjList* l = m_trunkList.skipNull(); l; l = l->next()) {
|
||||
for (ObjList* l = m_trunkList.skipNull(); l; l = l->skipNext()) {
|
||||
frame = static_cast<IAXMetaTrunkFrame*>(l->get());
|
||||
if (frame && frame->addr() == trans->remoteAddr()) {
|
||||
if (frame->addr() == trans->remoteAddr()) {
|
||||
trans->enableTrunking(frame);
|
||||
return;
|
||||
}
|
||||
}
|
||||
frame = new IAXMetaTrunkFrame(this,trans->remoteAddr());
|
||||
if (trans->enableTrunking(frame))
|
||||
IAXTrunkInfo tmp;
|
||||
IAXTrunkInfo* trunk = &m_trunkInfoDef;
|
||||
if (params) {
|
||||
tmp.initTrunking(*params,prefix,trunk,true);
|
||||
trunk = &tmp;
|
||||
}
|
||||
frame = new IAXMetaTrunkFrame(this,trans->remoteAddr(),trunk->m_timestamps,
|
||||
trunk->m_maxLen,trunk->m_sendInterval);
|
||||
if (trans->enableTrunking(frame)) {
|
||||
m_trunkList.append(frame);
|
||||
// Deref frame: Only transactions are allowed to keep references for it
|
||||
frame->deref();
|
||||
Debug(this,DebugAll,
|
||||
"Added trunk frame (%p) '%s:%d' timestamps=%s maxlen=%u interval=%ums",
|
||||
frame,frame->addr().host().c_str(),frame->addr().port(),
|
||||
String::boolText(frame->trunkTimestamps()),frame->maxLen(),
|
||||
frame->sendInterval());
|
||||
}
|
||||
else
|
||||
TelEngine::destruct(frame);
|
||||
}
|
||||
|
||||
void IAXEngine::removeTrunkFrame(IAXMetaTrunkFrame* trunkFrame)
|
||||
// Init incoming trunking data for a given transaction
|
||||
void IAXEngine::initTrunkIn(IAXTransaction* trans, const NamedList* params,
|
||||
const String& prefix)
|
||||
{
|
||||
Lock lock(&m_mutexTrunk);
|
||||
m_trunkList.remove(trunkFrame,false);
|
||||
if (!trans)
|
||||
return;
|
||||
IAXTrunkInfo tmp;
|
||||
IAXTrunkInfo* trunk = &m_trunkInfoDef;
|
||||
if (params) {
|
||||
tmp.initTrunking(*params,prefix,trunk,false);
|
||||
trunk = &tmp;
|
||||
}
|
||||
trans->m_trunkInSyncUsingTs = trunk->m_trunkInSyncUsingTs;
|
||||
trans->m_trunkInTsDiffRestart = trunk->m_trunkInTsDiffRestart;
|
||||
}
|
||||
|
||||
void IAXEngine::runProcessTrunkFrames()
|
||||
|
|
|
@ -50,25 +50,24 @@ static inline void setStringFromInteger(String& dest, u_int32_t value, u_int8_t
|
|||
class IAXTrunkFrameTrans : public GenObject
|
||||
{
|
||||
public:
|
||||
inline IAXTrunkFrameTrans(IAXTransaction* tr, u_int32_t frameTs)
|
||||
: m_tr(tr), m_frameTs(frameTs), m_trunkIndex(0)
|
||||
inline IAXTrunkFrameTrans(u_int16_t callNo)
|
||||
: m_callNo(callNo)
|
||||
{}
|
||||
~IAXTrunkFrameTrans()
|
||||
{ TelEngine::destruct(m_tr); }
|
||||
static IAXTrunkFrameTrans* find(ObjList& list, u_int16_t rCallNo);
|
||||
IAXTransaction* m_tr;
|
||||
u_int32_t m_frameTs;
|
||||
unsigned int m_trunkIndex;
|
||||
static IAXTrunkFrameTrans* get(ObjList& list, u_int16_t rCallNo);
|
||||
u_int16_t m_callNo;
|
||||
ObjList m_blocks;
|
||||
};
|
||||
|
||||
IAXTrunkFrameTrans* IAXTrunkFrameTrans::find(ObjList& list, u_int16_t rCallNo)
|
||||
IAXTrunkFrameTrans* IAXTrunkFrameTrans::get(ObjList& list, u_int16_t callNo)
|
||||
{
|
||||
for (ObjList* o = list.skipNull(); o; o = o->skipNext()) {
|
||||
IAXTrunkFrameTrans* t = static_cast<IAXTrunkFrameTrans*>(o->get());
|
||||
if (t->m_tr->remoteCallNo() == rCallNo)
|
||||
if (t->m_callNo == callNo)
|
||||
return t;
|
||||
}
|
||||
return 0;
|
||||
IAXTrunkFrameTrans* t = new IAXTrunkFrameTrans(callNo);
|
||||
list.append(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1080,43 +1079,33 @@ IAXFrame* IAXFrame::parse(const unsigned char* buf, unsigned int len, IAXEngine*
|
|||
u_int32_t ts = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
|
||||
buf += 8;
|
||||
len -= 8;
|
||||
u_int64_t timeMs = Time::msecNow();
|
||||
Time now;
|
||||
ObjList list;
|
||||
while (len >= 4) {
|
||||
u_int16_t dlen = (buf[2] << 8) | buf[3];
|
||||
if ((unsigned int)(dlen + 4) > len)
|
||||
return 0;
|
||||
break;
|
||||
scn = (buf[0] << 8) | buf[1];
|
||||
bool retrans = false;
|
||||
if (scn & 0x8000) {
|
||||
retrans = true;
|
||||
scn &= 0x7fff;
|
||||
}
|
||||
IAXTrunkFrameTrans* t = IAXTrunkFrameTrans::find(list,scn);
|
||||
if (!t) {
|
||||
IAXTransaction* tr = engine->findTransaction(*addr,scn);
|
||||
if (tr) {
|
||||
u_int32_t frameTs = 0;
|
||||
if (tr->updateTrunkRecvTs(frameTs,ts,timeMs)) {
|
||||
t = new IAXTrunkFrameTrans(tr,frameTs);
|
||||
list.append(t);
|
||||
}
|
||||
else
|
||||
TelEngine::destruct(tr);
|
||||
}
|
||||
}
|
||||
else
|
||||
// Adjust timestamp: there are more packets for the same transaction
|
||||
t->m_frameTs++;
|
||||
if (t) {
|
||||
IAXFrame* frame = new IAXFrame(IAXFrame::Voice,scn,t->m_frameTs,retrans,buf+4,dlen);
|
||||
if (!t->m_tr->processFrame(frame))
|
||||
frame->deref();
|
||||
}
|
||||
IAXTrunkFrameTrans* t = IAXTrunkFrameTrans::get(list,scn);
|
||||
t->m_blocks.append(new DataBlock((void*)(buf+4),dlen));
|
||||
dlen += 4;
|
||||
buf += dlen;
|
||||
len -= dlen;
|
||||
}
|
||||
for (ObjList* o = list.skipNull(); o; o = o->skipNext()) {
|
||||
IAXTrunkFrameTrans* t = static_cast<IAXTrunkFrameTrans*>(o->get());
|
||||
IAXTransaction* tr = engine->findTransaction(*addr,t->m_callNo);
|
||||
if (!tr)
|
||||
continue;
|
||||
tr->processMiniNoTs(ts,t->m_blocks,now);
|
||||
TelEngine::destruct(tr);
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1450,74 +1439,158 @@ void IAXFrameOut::adjustAuthTimeout(u_int64_t nextTransTime)
|
|||
m_nextTransTime = nextTransTime;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IAXTrunkInfo
|
||||
*/
|
||||
// Init all data from parameters
|
||||
void IAXTrunkInfo::init(const NamedList& params, const String& prefix,
|
||||
const IAXTrunkInfo* def)
|
||||
{
|
||||
initTrunking(params,prefix,def,true);
|
||||
initTrunking(params,prefix,def,false);
|
||||
}
|
||||
|
||||
// Init from parameters
|
||||
void IAXTrunkInfo::initTrunking(const NamedList& params, const String& prefix,
|
||||
const IAXTrunkInfo* def, bool out)
|
||||
{
|
||||
if (out) {
|
||||
m_timestamps = params.getBoolValue(prefix + "timestamps",
|
||||
!def || def->m_timestamps);
|
||||
m_sendInterval = params.getIntValue(prefix + "sendinterval",
|
||||
def ? def->m_sendInterval : IAX2_TRUNKFRAME_SEND_DEF,IAX2_TRUNKFRAME_SEND_MIN);
|
||||
m_maxLen = params.getIntValue(prefix + "maxlen",
|
||||
def ? def->m_maxLen : IAX2_TRUNKFRAME_LEN_DEF,IAX2_TRUNKFRAME_LEN_MIN);
|
||||
}
|
||||
else {
|
||||
m_trunkInSyncUsingTs = params.getBoolValue(prefix + "nominits_sync_use_ts",
|
||||
!def || def->m_trunkInSyncUsingTs);
|
||||
m_trunkInTsDiffRestart = params.getIntValue(prefix + "nominits_ts_diff_restart",
|
||||
def ? m_trunkInTsDiffRestart : 5000,1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IAXMetaTrunkFrame
|
||||
*/
|
||||
#define IAX2_METATRUNK_HEADERLENGTH 8
|
||||
#define IAX2_MINIFRAME_HEADERLENGTH 6
|
||||
|
||||
IAXMetaTrunkFrame::IAXMetaTrunkFrame(IAXEngine* engine, const SocketAddr& addr)
|
||||
: Mutex(true,"IAXMetaTrunkFrame"),
|
||||
m_data(0), m_dataAddIdx(IAX2_METATRUNK_HEADERLENGTH), m_engine(engine), m_addr(addr)
|
||||
IAXMetaTrunkFrame::IAXMetaTrunkFrame(IAXEngine* engine, const SocketAddr& addr,
|
||||
bool timestamps, unsigned int maxLen, unsigned int sendInterval)
|
||||
: Mutex(false,"IAXMetaTrunkFrame"),
|
||||
m_data(0), m_dataAddIdx(IAX2_TRUNKFRAME_HEADERLENGTH),
|
||||
m_timeStamp(0), m_send(0), m_lastSentTs(0),
|
||||
m_sendInterval(sendInterval),
|
||||
m_engine(engine), m_addr(addr),
|
||||
m_trunkTimestamps(timestamps), m_maxLen(maxLen), m_maxDataLen(0),
|
||||
m_miniHdrLen(timestamps ? 6 : 4)
|
||||
{
|
||||
m_data = new u_int8_t[m_engine->maxFullFrameDataLen()];
|
||||
// Make sure we don't receive invalid values
|
||||
if (m_maxLen < IAX2_TRUNKFRAME_LEN_MIN)
|
||||
m_maxLen = IAX2_TRUNKFRAME_LEN_MIN;
|
||||
m_data = new u_int8_t[m_maxLen];
|
||||
// Audio data length can't be greater the remaining space
|
||||
// Also make sure we can put it in 2 bytes
|
||||
m_maxDataLen = m_maxLen - IAX2_TRUNKFRAME_HEADERLENGTH - m_miniHdrLen;
|
||||
if (m_maxDataLen > 0xffff)
|
||||
m_maxDataLen = 0xffff;
|
||||
// Meta indicator
|
||||
*(u_int16_t*)m_data = 0;
|
||||
// Meta command & Command data (use timestamps)
|
||||
m_data[2] = 1;
|
||||
m_data[3] = 1;
|
||||
// Frame timestamp
|
||||
setTimestamp((u_int32_t)Time::msecNow());
|
||||
m_data[3] = m_trunkTimestamps ? 1 : 0;
|
||||
}
|
||||
|
||||
IAXMetaTrunkFrame::~IAXMetaTrunkFrame()
|
||||
{
|
||||
m_engine->removeTrunkFrame(this);
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
void IAXMetaTrunkFrame::setTimestamp(u_int32_t tStamp)
|
||||
{
|
||||
m_timestamp = tStamp;
|
||||
m_data[4] = (u_int8_t)(tStamp >> 24);
|
||||
m_data[5] = (u_int8_t)(tStamp >> 16);
|
||||
m_data[6] = (u_int8_t)(tStamp >> 8);
|
||||
m_data[7] = (u_int8_t)tStamp;
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
bool IAXMetaTrunkFrame::add(u_int16_t sCallNo, const DataBlock& data, u_int32_t tStamp)
|
||||
{
|
||||
Lock lock(this);
|
||||
bool b = true;
|
||||
// Do we have data ?
|
||||
if (!data.length())
|
||||
return b;
|
||||
return false;
|
||||
// Avoid buffer overflow
|
||||
if (data.length() > m_maxDataLen) {
|
||||
Debug(m_engine,DebugGoOn,
|
||||
"Trunk frame '%s:%d' can't add %u bytes (max=%u) for call %u [%p]",
|
||||
m_addr.host().c_str(),m_addr.port(),data.length(),m_maxDataLen,sCallNo,this);
|
||||
return false;
|
||||
}
|
||||
Lock lck(this);
|
||||
if (!m_timeStamp)
|
||||
setTimestamp(Time::now());
|
||||
bool b = true;
|
||||
// If no more room, send it
|
||||
if (m_dataAddIdx + data.length() + IAX2_MINIFRAME_HEADERLENGTH > m_engine->maxFullFrameDataLen())
|
||||
b = send((u_int32_t)Time::msecNow());
|
||||
// Is the first mini frame ?
|
||||
if (m_dataAddIdx == IAX2_METATRUNK_HEADERLENGTH)
|
||||
m_timestamp = (u_int32_t)Time::msecNow();
|
||||
if (m_dataAddIdx + data.length() + m_miniHdrLen > m_maxLen)
|
||||
b = doSend();
|
||||
XDebug(m_engine,DebugAll,"Trunk frame '%s:%d' adding %u payload bytes call=%u [%p]",
|
||||
m_addr.host().c_str(),m_addr.port(),data.length(),sCallNo,this);
|
||||
// Add the mini frame
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)(data.length() >> 8);
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)data.length();
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)(sCallNo >> 8);
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)sCallNo;
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)(tStamp >> 8);
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)tStamp;
|
||||
if (m_trunkTimestamps) {
|
||||
// data length + call no + timestamp
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)(data.length() >> 8);
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)data.length();
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)(sCallNo >> 8);
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)sCallNo;
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)(tStamp >> 8);
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)tStamp;
|
||||
}
|
||||
else {
|
||||
// call no + data length
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)(sCallNo >> 8);
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)sCallNo;
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)(data.length() >> 8);
|
||||
m_data[m_dataAddIdx++] = (u_int8_t)data.length();
|
||||
}
|
||||
memcpy(m_data + m_dataAddIdx,data.data(),data.length());
|
||||
m_dataAddIdx += data.length();
|
||||
return b;
|
||||
}
|
||||
|
||||
bool IAXMetaTrunkFrame::send(u_int32_t tStamp)
|
||||
// Send this frame to remote peer
|
||||
bool IAXMetaTrunkFrame::doSend(const Time& now, bool onTime)
|
||||
{
|
||||
Lock lock(this);
|
||||
setTimestamp(tStamp);
|
||||
#define IAX2_TRUNKDATA_DELTA 160
|
||||
bool dontSend = (m_dataAddIdx <= IAX2_TRUNKFRAME_HEADERLENGTH);
|
||||
u_int64_t ellapsed = (now - m_timeStamp) / 1000;
|
||||
if (ellapsed <= 0xffffffff) {
|
||||
// Sent on time: set timestamp from send interval
|
||||
// Sent on buffer full: set timestamp from ellapsed time
|
||||
u_int32_t ts = 0;
|
||||
if (onTime) {
|
||||
setSendTime(now);
|
||||
ts = m_lastSentTs + m_sendInterval;
|
||||
if (ts != ellapsed) {
|
||||
// Adjust timestamp
|
||||
if (ts > ellapsed) {
|
||||
if ((ts - ellapsed) >= IAX2_TRUNKDATA_DELTA)
|
||||
ts = (u_int32_t)ellapsed;
|
||||
}
|
||||
else if ((ellapsed - ts) >= IAX2_TRUNKDATA_DELTA)
|
||||
ts = (u_int32_t)ellapsed;
|
||||
}
|
||||
}
|
||||
else
|
||||
ts = (u_int32_t)ellapsed;
|
||||
if (ts > m_lastSentTs || dontSend)
|
||||
m_lastSentTs = ts;
|
||||
else
|
||||
m_lastSentTs++;
|
||||
}
|
||||
else {
|
||||
// Timestamp wraparound: reset
|
||||
setTimestamp(now);
|
||||
m_lastSentTs = 0;
|
||||
}
|
||||
if (dontSend)
|
||||
return false;
|
||||
XDebug(m_engine,DebugAll,"Trunk frame '%s:%d' sending %u tStamp=%u [%p]",
|
||||
m_addr.host().c_str(),m_addr.port(),m_dataAddIdx,m_lastSentTs,this);
|
||||
setTimestamp(m_lastSentTs);
|
||||
bool b = m_engine->writeSocket(m_data,m_dataAddIdx,m_addr);
|
||||
// Reset index & timestamp
|
||||
m_dataAddIdx = IAX2_METATRUNK_HEADERLENGTH;
|
||||
m_timestamp = 0;
|
||||
m_dataAddIdx = IAX2_TRUNKFRAME_HEADERLENGTH;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,8 +94,15 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t
|
|||
m_adjustTsOutThreshold(0),
|
||||
m_adjustTsOutOverrun(0),
|
||||
m_adjustTsOutUnderrun(0),
|
||||
m_lastVoiceFrameIn(0),
|
||||
m_lastVoiceFrameInTs(0),
|
||||
m_reqVoiceVNAK(0),
|
||||
m_trunkFrame(0),
|
||||
m_trunkInOffsetTimeMs(0), m_trunkInLastTs(0), m_warnTrunkInTimestamp(true)
|
||||
m_trunkInSyncUsingTs(true),
|
||||
m_trunkInStartTime(0),
|
||||
m_trunkInTsDelta(0),
|
||||
m_trunkInTsDiffRestart(5000),
|
||||
m_trunkInFirstTs(0)
|
||||
{
|
||||
// Setup transaction
|
||||
m_retransCount = engine->retransCount();
|
||||
|
@ -123,6 +130,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t
|
|||
localCallNo(),remoteCallNo(),typeName(),m_addr.host().c_str(),m_addr.port(),this);
|
||||
engine->getOutDataAdjust(m_adjustTsOutThreshold,m_adjustTsOutOverrun,
|
||||
m_adjustTsOutUnderrun);
|
||||
engine->initTrunkIn(this);
|
||||
// Append frame to incoming list
|
||||
Lock lock(this);
|
||||
m_inFrames.append(frame);
|
||||
|
@ -163,8 +171,15 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
|
|||
m_adjustTsOutThreshold(0),
|
||||
m_adjustTsOutOverrun(0),
|
||||
m_adjustTsOutUnderrun(0),
|
||||
m_lastVoiceFrameIn(0),
|
||||
m_lastVoiceFrameInTs(0),
|
||||
m_reqVoiceVNAK(0),
|
||||
m_trunkFrame(0),
|
||||
m_trunkInOffsetTimeMs(0), m_trunkInLastTs(0), m_warnTrunkInTimestamp(true)
|
||||
m_trunkInSyncUsingTs(true),
|
||||
m_trunkInStartTime(0),
|
||||
m_trunkInTsDelta(0),
|
||||
m_trunkInTsDiffRestart(5000),
|
||||
m_trunkInFirstTs(0)
|
||||
{
|
||||
Debug(m_engine,DebugAll,"Transaction(%u,%u) outgoing type=%s remote=%s:%d [%p]",
|
||||
localCallNo(),remoteCallNo(),typeName(),m_addr.host().c_str(),m_addr.port(),this);
|
||||
|
@ -224,6 +239,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
|
|||
}
|
||||
engine->getOutDataAdjust(m_adjustTsOutThreshold,m_adjustTsOutOverrun,
|
||||
m_adjustTsOutUnderrun);
|
||||
engine->initTrunkIn(this);
|
||||
postFrameIes(IAXFrame::IAX,frametype,ies);
|
||||
changeState(NewLocalInvite);
|
||||
}
|
||||
|
@ -312,8 +328,12 @@ IAXTransaction* IAXTransaction::processFrame(IAXFrame* frame)
|
|||
t = IAXFormat::Video;
|
||||
if (!processMediaFrame(full,t))
|
||||
return 0;
|
||||
// Frame accepted: process voice data
|
||||
lock.drop();
|
||||
if (t == IAXFormat::Audio) {
|
||||
Lock lck(m_dataAudio.m_inMutex);
|
||||
m_lastVoiceFrameIn = Time::now();
|
||||
m_lastVoiceFrameInTs = frame->timeStamp();
|
||||
}
|
||||
return processMedia(frame->data(),frame->timeStamp(),t,true,frame->mark());
|
||||
}
|
||||
// Process incoming Ping
|
||||
|
@ -347,6 +367,10 @@ IAXTransaction* IAXTransaction::processMedia(DataBlock& data, u_int32_t tStamp,
|
|||
return 0;
|
||||
}
|
||||
Lock lck(d->m_inMutex);
|
||||
if (type == IAXFormat::Audio && !m_lastVoiceFrameIn) {
|
||||
receivedVoiceMiniBeforeFull();
|
||||
return 0;
|
||||
}
|
||||
const IAXFormatDesc& desc = fmt->formatDesc(true);
|
||||
if (!desc.format()) {
|
||||
if (d->m_showInNoFmt) {
|
||||
|
@ -543,7 +567,7 @@ unsigned int IAXTransaction::sendMedia(const DataBlock& data, unsigned int tStam
|
|||
if (fullFrame) {
|
||||
// Send trunked frame before full frame to keep the media order
|
||||
if (m_trunkFrame)
|
||||
m_engine->sendTrunkFrame(m_trunkFrame);
|
||||
m_trunkFrame->send();
|
||||
postFrame(IAXFrame::Voice,fmt->out(),data.data(),data.length(),ts,true);
|
||||
sent = data.length();
|
||||
}
|
||||
|
@ -908,10 +932,13 @@ bool IAXTransaction::abortReg()
|
|||
|
||||
bool IAXTransaction::enableTrunking(IAXMetaTrunkFrame* trunkFrame)
|
||||
{
|
||||
if (!trunkFrame)
|
||||
return false;
|
||||
Lock lck(m_dataAudio.m_outMutex);
|
||||
if (m_trunkFrame)
|
||||
return false;
|
||||
// Get a reference to the trunk frame
|
||||
if (!(trunkFrame && trunkFrame->ref()))
|
||||
if (!trunkFrame->ref())
|
||||
return false;
|
||||
m_trunkFrame = trunkFrame;
|
||||
return true;
|
||||
|
@ -952,45 +979,49 @@ void IAXTransaction::processCallToken(const DataBlock& callToken)
|
|||
sendFrame(frame);
|
||||
}
|
||||
|
||||
// Update transaction incoming trunk data (used for trunk without timestamps)
|
||||
bool IAXTransaction::updateTrunkRecvTs(u_int32_t& frameTs, u_int32_t ts, u_int64_t currentTimeMs)
|
||||
// Process incoming audio miniframes from trunk without timestamps
|
||||
void IAXTransaction::processMiniNoTs(u_int32_t ts, ObjList& blocks, const Time& now)
|
||||
{
|
||||
Lock lck(this);
|
||||
if (m_trunkInLastTs) {
|
||||
bool ok = (ts > m_trunkInLastTs);
|
||||
if (!ok) {
|
||||
// Allow frames older then 5 seconds to restart trunk
|
||||
if ((m_trunkInLastTs - ts) >= 5000) {
|
||||
ok = true;
|
||||
m_trunkInOffsetTimeMs = 0;
|
||||
}
|
||||
else
|
||||
ok = (m_trunkInOffsetTimeMs == 0);
|
||||
}
|
||||
if (!ok) {
|
||||
if (m_warnTrunkInTimestamp) {
|
||||
Debug(m_engine,DebugNote,
|
||||
"Transaction(%u,%u) ignoring trunked mini-frame without timestamps from %s:%d with ts=%u last=%u [%p]",
|
||||
localCallNo(),remoteCallNo(),remoteAddr().host().c_str(),remoteAddr().port(),
|
||||
ts,m_trunkInLastTs,this);
|
||||
m_warnTrunkInTimestamp = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
m_warnTrunkInTimestamp = true;
|
||||
Lock lck(m_dataAudio.m_inMutex);
|
||||
if (!m_lastVoiceFrameIn) {
|
||||
receivedVoiceMiniBeforeFull();
|
||||
return;
|
||||
}
|
||||
m_trunkInLastTs = ts;
|
||||
if (!m_trunkInOffsetTimeMs) {
|
||||
if (!currentTimeMs)
|
||||
currentTimeMs = Time::msecNow();
|
||||
u_int64_t trunkStart = currentTimeMs - ts;
|
||||
m_trunkInOffsetTimeMs = (int64_t)m_timeStamp - trunkStart;
|
||||
u_int32_t tStamp = 0;
|
||||
if (m_trunkInSyncUsingTs) {
|
||||
if (m_trunkInStartTime) {
|
||||
if (ts < m_trunkInFirstTs) {
|
||||
// Restart?
|
||||
if ((m_trunkInFirstTs - ts) > m_trunkInTsDiffRestart)
|
||||
restartTrunkIn(now,ts);
|
||||
else {
|
||||
// Drop
|
||||
for (ObjList* o = blocks.skipNull(); o; o = o->skipNext()) {
|
||||
DataBlock* db = static_cast<DataBlock*>(o->get());
|
||||
if (db->length()) {
|
||||
m_dataAudio.m_ooPackets++;
|
||||
m_dataAudio.m_ooBytes += db->length();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
restartTrunkIn(now,ts);
|
||||
tStamp = m_trunkInTsDelta + (ts - m_trunkInFirstTs);
|
||||
}
|
||||
if (m_trunkInOffsetTimeMs >= 0)
|
||||
frameTs = ts - (u_int32_t)m_trunkInOffsetTimeMs;
|
||||
else
|
||||
frameTs = ts + (u_int32_t)(-m_trunkInOffsetTimeMs);
|
||||
return true;
|
||||
tStamp = (u_int32_t)((now - m_lastVoiceFrameIn) / 1000) + m_lastVoiceFrameInTs;
|
||||
XDebug(m_engine,DebugAll,"(%u,%u) processMiniNoTs(sync=%u packets=%u) %u --> %u [%p]",
|
||||
localCallNo(),remoteCallNo(),m_trunkInSyncUsingTs,blocks.count(),ts,tStamp,this);
|
||||
lck.drop();
|
||||
for (ObjList* o = blocks.skipNull(); o; o = o->skipNext()) {
|
||||
DataBlock* db = static_cast<DataBlock*>(o->get());
|
||||
// Signal full frame timestamp (we calculate it from full voice frame)
|
||||
processMedia(*db,tStamp,IAXFormat::Audio,true);
|
||||
tStamp++;
|
||||
}
|
||||
}
|
||||
|
||||
void IAXTransaction::print(bool printStats, bool printFrames, const char* location)
|
||||
|
@ -1197,8 +1228,11 @@ void IAXTransaction::postFrame(IAXFrame::Type type, u_int32_t subclass, void* da
|
|||
return;
|
||||
// Pong and LagRp don't need timestamp to be adjusted
|
||||
// Don't adjust for video
|
||||
if ((type != IAXFrame::IAX && type == IAXFrame::Video) ||
|
||||
(subclass != IAXControl::Pong && subclass != IAXControl::LagRp))
|
||||
if (type == IAXFrame::IAX) {
|
||||
if (subclass != IAXControl::Pong && subclass != IAXControl::LagRp)
|
||||
adjustTStamp(tStamp);
|
||||
}
|
||||
else if (type != IAXFrame::Video)
|
||||
adjustTStamp(tStamp);
|
||||
IAXFrameOut* frame = new IAXFrameOut(type,subclass,m_lCallNo,m_rCallNo,m_oSeqNo,m_iSeqNo,tStamp,
|
||||
(unsigned char*)data,len,m_retransCount,m_retransInterval,ackOnly,mark);
|
||||
|
@ -1998,4 +2032,17 @@ void IAXTransaction::postFrame(IAXFrameOut* frame)
|
|||
sendFrame(frame);
|
||||
}
|
||||
|
||||
void IAXTransaction::receivedVoiceMiniBeforeFull()
|
||||
{
|
||||
if (m_reqVoiceVNAK > 15)
|
||||
return;
|
||||
m_reqVoiceVNAK++;
|
||||
if (m_reqVoiceVNAK == 3)
|
||||
Debug(m_engine,DebugAll,
|
||||
"Transaction(%u,%u) received audio miniframe before full voice frame [%p]",
|
||||
localCallNo(),remoteCallNo(),this);
|
||||
if (0 == (m_reqVoiceVNAK % 3))
|
||||
sendVNAK();
|
||||
}
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
||||
|
|
|
@ -47,20 +47,38 @@
|
|||
*/
|
||||
namespace TelEngine {
|
||||
|
||||
class IAXInfoElement;
|
||||
class IAXInfoElementString;
|
||||
class IAXInfoElementNumeric;
|
||||
class IAXInfoElementBinary;
|
||||
class IAXFrame;
|
||||
class IAXFullFrame;
|
||||
class IAXFrameOut;
|
||||
class IAXEvent;
|
||||
class IAXEngine;
|
||||
class IAXInfoElement; // A single IAX2 Information Element
|
||||
class IAXInfoElementString; // A single IAX2 text Information Element
|
||||
class IAXInfoElementNumeric; // A single IAX2 numeric Information Element
|
||||
class IAXInfoElementBinary; // A single IAX2 numeric Information Element
|
||||
class IAXIEList; // Information Element container
|
||||
class IAXAuthMethod; // Wrapper class for authentication methods values
|
||||
class IAXFormatDesc; // IAX format description
|
||||
class IAXFormat; // Wrapper class for formats
|
||||
class IAXControl; // Wrapper class for subclasses of frames of type IAX
|
||||
class IAXFrame; // This class holds an IAX frame
|
||||
class IAXFullFrame; // This class holds an IAX full frame
|
||||
class IAXFrameOut; // This class holds an outgoing IAX full frame
|
||||
class IAXTrunkInfo; // Trunk info
|
||||
class IAXMetaTrunkFrame; // Meta trunk frame
|
||||
class IAXMediaData; // IAX2 transaction media data
|
||||
class IAXTransaction; // An IAX2 transaction
|
||||
class IAXEvent; // Event class
|
||||
class IAXEngine; // IAX engine
|
||||
|
||||
#define IAX_PROTOCOL_VERSION 0x0002 // Protocol version
|
||||
#define IAX2_MAX_CALLNO 32767 // Max call number value
|
||||
#define IAX2_MAX_TRANSINFRAMELIST 127 // Max transaction incoming frame list
|
||||
|
||||
// Trunk frame header length
|
||||
#define IAX2_TRUNKFRAME_HEADERLENGTH 8
|
||||
// Trunk frame length
|
||||
#define IAX2_TRUNKFRAME_LEN_MIN 20 // 16 bytes: meta header + miniframe with timestamps header
|
||||
#define IAX2_TRUNKFRAME_LEN_DEF 1400
|
||||
// Trunk frame send interval in milliseconds
|
||||
#define IAX2_TRUNKFRAME_SEND_MIN 5
|
||||
#define IAX2_TRUNKFRAME_SEND_DEF 20
|
||||
|
||||
/**
|
||||
* This class holds a single Information Element with no data
|
||||
* @short A single IAX2 Information Element
|
||||
|
@ -265,7 +283,7 @@ private:
|
|||
* This class holds a single Information Element with 1, 2 or 4 byte(s) length data
|
||||
* @short A single IAX2 numeric Information Element
|
||||
*/
|
||||
class IAXInfoElementNumeric : public IAXInfoElement
|
||||
class YIAX_API IAXInfoElementNumeric : public IAXInfoElement
|
||||
{
|
||||
public:
|
||||
/**
|
||||
|
@ -1384,6 +1402,50 @@ private:
|
|||
u_int64_t m_nextTransTime; // Next transmission time
|
||||
};
|
||||
|
||||
/**
|
||||
* This class holds trunk description
|
||||
* @short Trunk info
|
||||
*/
|
||||
class YIAX_API IAXTrunkInfo : public GenObject
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
inline IAXTrunkInfo()
|
||||
: m_timestamps(true), m_sendInterval(IAX2_TRUNKFRAME_SEND_DEF),
|
||||
m_maxLen(IAX2_TRUNKFRAME_LEN_DEF), m_trunkInSyncUsingTs(true),
|
||||
m_trunkInTsDiffRestart(5000)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Init all data from parameters
|
||||
* @param params Parameter list
|
||||
* @param prefix Parameter prefix (used as value for enable parameter also)
|
||||
* @param def Optional defaults
|
||||
*/
|
||||
void init(const NamedList& params, const String& prefix,
|
||||
const IAXTrunkInfo* def = 0);
|
||||
|
||||
/**
|
||||
* Init trunking from parameters
|
||||
* @param params Parameter list
|
||||
* @param prefix Parameter prefix (used as value for enable parameter also)
|
||||
* @param def Optional defaults
|
||||
* @param out True to init outgoing trunk data, false to init incoming trunk info
|
||||
*/
|
||||
void initTrunking(const NamedList& params, const String& prefix,
|
||||
const IAXTrunkInfo* def = 0, bool out = true);
|
||||
|
||||
bool m_timestamps; // Trunk type: with(out) timestamps
|
||||
unsigned int m_sendInterval; // Send interval
|
||||
unsigned int m_maxLen; // Max frame length
|
||||
bool m_trunkInSyncUsingTs; // Incoming trunk without timestamps: use trunk
|
||||
// time or trunk timestamp to re-build frame ts
|
||||
u_int32_t m_trunkInTsDiffRestart; // Incoming trunk without timestamp: diff between
|
||||
// timestamps at which we restart
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle meta trunk frame with timestamps
|
||||
* @short Meta trunk frame
|
||||
|
@ -1395,8 +1457,12 @@ public:
|
|||
* Constructor. Constructs an outgoing meta trunk frame
|
||||
* @param engine The engine that owns this frame
|
||||
* @param addr Remote peer address
|
||||
* @param timestamps True if miniframes have timestamps, false if not
|
||||
* @param maxLen Maximum frame length
|
||||
* @param sendInterval Trunk send interval in milliseconds
|
||||
*/
|
||||
IAXMetaTrunkFrame(IAXEngine* engine, const SocketAddr& addr);
|
||||
IAXMetaTrunkFrame(IAXEngine* engine, const SocketAddr& addr, bool timestamps,
|
||||
unsigned int maxLen, unsigned int sendInterval);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
|
@ -1411,17 +1477,25 @@ public:
|
|||
{ return m_addr; }
|
||||
|
||||
/**
|
||||
* Get the timestamp of this frame
|
||||
* @return The timestamp of this frame
|
||||
* Check if the frame is adding mini frames timestamps
|
||||
* @return True if the frame is adding mini frames timestamps
|
||||
*/
|
||||
inline u_int32_t timestamp()
|
||||
{ return m_timestamp; }
|
||||
inline bool trunkTimestamps() const
|
||||
{ return m_trunkTimestamps; }
|
||||
|
||||
/**
|
||||
* Set the timestamp of this frame
|
||||
* @param tStamp Timestamp value to set
|
||||
* Retrieve the send interval
|
||||
* @return Send interval in milliseconds
|
||||
*/
|
||||
void setTimestamp(u_int32_t tStamp);
|
||||
inline unsigned int sendInterval() const
|
||||
{ return m_sendInterval; }
|
||||
|
||||
/**
|
||||
* Retrieve the frame maximum length
|
||||
* @return Frame maximum length
|
||||
*/
|
||||
inline unsigned int maxLen() const
|
||||
{ return m_maxLen; }
|
||||
|
||||
/**
|
||||
* Add a mini frame. If no room, send before adding
|
||||
|
@ -1433,18 +1507,61 @@ public:
|
|||
bool add(u_int16_t sCallNo, const DataBlock& data, u_int32_t tStamp);
|
||||
|
||||
/**
|
||||
* Send this frame to remote peer
|
||||
* @param tStamp Frame timestamp
|
||||
* Send this frame to remote peer if the time arrived
|
||||
* @param now Current time
|
||||
* @return The result of the write operation
|
||||
*/
|
||||
bool send(u_int32_t tStamp = Time::msecNow());
|
||||
inline bool timerTick(const Time& now = Time()) {
|
||||
if (m_dataAddIdx == IAX2_TRUNKFRAME_HEADERLENGTH || !m_send)
|
||||
return false;
|
||||
Lock lck(this);
|
||||
return (now > m_send) && doSend(now,true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send this frame to remote peer if there is any data in buffer
|
||||
* @return The result of the write operation
|
||||
*/
|
||||
inline bool send() {
|
||||
if (m_dataAddIdx == IAX2_TRUNKFRAME_HEADERLENGTH)
|
||||
return false;
|
||||
Lock lck(this);
|
||||
return m_dataAddIdx != IAX2_TRUNKFRAME_HEADERLENGTH && doSend();
|
||||
}
|
||||
|
||||
private:
|
||||
IAXMetaTrunkFrame() {} // No default constructor
|
||||
// Send this frame to remote peer
|
||||
bool doSend(const Time& now = Time(), bool onTime = false);
|
||||
// Set timestamp and next time to send
|
||||
inline void setTimestamp(u_int64_t now) {
|
||||
m_timeStamp = now;
|
||||
m_send = now + (u_int64_t)m_sendInterval * 1000;
|
||||
}
|
||||
// Set next time to send
|
||||
inline void setSendTime(u_int64_t now)
|
||||
{ m_send = now + (u_int64_t)m_sendInterval * 1000; }
|
||||
|
||||
// Set the timestamp of this frame
|
||||
inline void setTimestamp(u_int32_t tStamp) {
|
||||
m_data[4] = (u_int8_t)(tStamp >> 24);
|
||||
m_data[5] = (u_int8_t)(tStamp >> 16);
|
||||
m_data[6] = (u_int8_t)(tStamp >> 8);
|
||||
m_data[7] = (u_int8_t)tStamp;
|
||||
}
|
||||
|
||||
u_int8_t* m_data; // Data buffer
|
||||
u_int16_t m_dataAddIdx; // Current add index
|
||||
u_int32_t m_timestamp; // Frame timestamp
|
||||
u_int64_t m_timeStamp; // First time data was added
|
||||
u_int64_t m_send; // Time to send
|
||||
u_int32_t m_lastSentTs; // Last sent timestamp
|
||||
unsigned int m_sendInterval;// Send interval in milliseconds
|
||||
IAXEngine* m_engine; // The engine that owns this frame
|
||||
SocketAddr m_addr; // Remote peer address
|
||||
bool m_trunkTimestamps; // Trunk type: with(out) timestamps
|
||||
unsigned int m_maxLen; // Max frame length
|
||||
unsigned int m_maxDataLen; // Max frame data length
|
||||
unsigned char m_miniHdrLen; // Miniframe header length
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1939,13 +2056,12 @@ public:
|
|||
void processCallToken(const DataBlock& callToken);
|
||||
|
||||
/**
|
||||
* Update transaction incoming trunk data (used for trunk without timestamps)
|
||||
* @param frameTs Address of variable to be set with frame timestamp
|
||||
* Process incoming audio miniframes from trunk without timestamps
|
||||
* @param ts Trunk frame timestamp
|
||||
* @param currentTimeMs Current time
|
||||
* @return True if accepted
|
||||
* @param blocks Received blocks
|
||||
* @param now Current time
|
||||
*/
|
||||
bool updateTrunkRecvTs(u_int32_t& frameTs, u_int32_t ts, u_int64_t currentTimeMs);
|
||||
void processMiniNoTs(u_int32_t ts, ObjList& blocks, const Time& now = Time());
|
||||
|
||||
/**
|
||||
* Print transaction data on stdin
|
||||
|
@ -2354,6 +2470,13 @@ protected:
|
|||
private:
|
||||
void adjustTStamp(u_int32_t& tStamp);
|
||||
void postFrame(IAXFrameOut* frame);
|
||||
void receivedVoiceMiniBeforeFull();
|
||||
inline void restartTrunkIn(u_int64_t now, u_int32_t ts) {
|
||||
m_trunkInStartTime = now;
|
||||
u_int64_t dt = (now - m_lastVoiceFrameIn) / 1000;
|
||||
m_trunkInTsDelta = m_lastVoiceFrameInTs + (u_int32_t)dt;
|
||||
m_trunkInFirstTs = ts;
|
||||
}
|
||||
|
||||
// Params
|
||||
bool m_localInitTrans; // True: local initiated transaction
|
||||
|
@ -2408,11 +2531,17 @@ private:
|
|||
// overrun (incoming data with rate greater then expected)
|
||||
unsigned int m_adjustTsOutUnderrun; // Value used to adjust outgoing data timestamp on data
|
||||
// underrun (incoming data with rate less then expected)
|
||||
u_int64_t m_lastVoiceFrameIn; // Time we received the last voice frame
|
||||
u_int32_t m_lastVoiceFrameInTs; // Timestamp in the last received voice frame
|
||||
int m_reqVoiceVNAK; // Send VNAK if not received full voice frame
|
||||
// Meta trunking
|
||||
IAXMetaTrunkFrame* m_trunkFrame; // Reference to a trunk frame if trunking is enabled for this transaction
|
||||
int64_t m_trunkInOffsetTimeMs; // Offset between transaction start and trunk start
|
||||
u_int32_t m_trunkInLastTs; // Last received trunk timestamp
|
||||
bool m_warnTrunkInTimestamp; // Warn incoming trunk invalid timestamp
|
||||
bool m_trunkInSyncUsingTs; // Incoming trunk without timestamps: generate timestamp
|
||||
// using time or using trunk timestamp
|
||||
u_int64_t m_trunkInStartTime; // First time we received trunk in data
|
||||
u_int32_t m_trunkInTsDelta; // Value used to re-build ts: last voice timestamp
|
||||
u_int32_t m_trunkInTsDiffRestart; // Incoming trunk without timestamp: diff between timestamps at which we restart
|
||||
u_int32_t m_trunkInFirstTs; // Incoming trunk without timestamp: first trunk timestamp
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2578,14 +2707,12 @@ public:
|
|||
* @param maxFullFrameDataLen Max full frame IE list (buffer) length
|
||||
* @param format Default media format
|
||||
* @param capab Media capabilities of this engine
|
||||
* @param trunkSendInterval Send trunk meta frame interval
|
||||
* @param authRequired Automatically challenge all clients for authentication
|
||||
* @param params Optional extra parameter list
|
||||
*/
|
||||
IAXEngine(const char* iface, int port, u_int16_t transListCount, u_int16_t retransCount, u_int16_t retransInterval,
|
||||
u_int16_t authTimeout, u_int16_t transTimeout, u_int16_t maxFullFrameDataLen,
|
||||
u_int32_t format, u_int32_t capab, u_int32_t trunkSendInterval, bool authRequired,
|
||||
NamedList* params = 0);
|
||||
u_int32_t format, u_int32_t capab, bool authRequired, NamedList* params = 0);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
|
@ -2825,16 +2952,30 @@ public:
|
|||
virtual void defaultEventHandler(IAXEvent* event);
|
||||
|
||||
/**
|
||||
* Enable trunking for the given transaction. Allocate a trunk meta frame if needded
|
||||
* Enable trunking for the given transaction. Allocate a trunk meta frame if needed.
|
||||
* Trunk data is ignored if a trunk object for transaction remote address already exists
|
||||
* @param trans Transaction to enable trunking for
|
||||
* @param params Optional trunk parameters list
|
||||
* @param prefix Trunk parameters name prefix
|
||||
*/
|
||||
void enableTrunking(IAXTransaction* trans);
|
||||
void enableTrunking(IAXTransaction* trans, const NamedList* params = 0,
|
||||
const String& prefix = String::empty());
|
||||
|
||||
/**
|
||||
* Remove a trunk meta frame from the queue and deref it
|
||||
* @param metaFrame The trunk meta frame to remove
|
||||
* Init incoming trunking data for a given transaction
|
||||
* @param trans Transaction to init
|
||||
* @param params Optional trunk parameters list
|
||||
* @param prefix Trunk parameters name prefix
|
||||
*/
|
||||
void removeTrunkFrame(IAXMetaTrunkFrame* metaFrame);
|
||||
void initTrunkIn(IAXTransaction* trans, const NamedList* params = 0,
|
||||
const String& prefix = String::empty());
|
||||
|
||||
/**
|
||||
* Retrieve the default trunk info data
|
||||
* @return Trunk info pointer, 0 if not found
|
||||
*/
|
||||
inline IAXTrunkInfo* trunkInfo()
|
||||
{ return &m_trunkInfoDef; }
|
||||
|
||||
/**
|
||||
* Send an INVAL frame
|
||||
|
@ -2843,17 +2984,6 @@ public:
|
|||
*/
|
||||
void sendInval(IAXFullFrame* frame, const SocketAddr& addr);
|
||||
|
||||
/**
|
||||
* Send a trunk frame
|
||||
* @param metaFrame The trunk meta frame to sent
|
||||
*/
|
||||
inline void sendTrunkFrame(IAXMetaTrunkFrame* metaFrame) {
|
||||
if (!metaFrame)
|
||||
return;
|
||||
Lock lck(m_mutexTrunk);
|
||||
metaFrame->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep calling processTrunkFrames to send trunked media data
|
||||
*/
|
||||
|
@ -2948,7 +3078,7 @@ protected:
|
|||
* @param time Time of the call
|
||||
* @return True if at least one frame was sent
|
||||
*/
|
||||
bool processTrunkFrames(u_int32_t time = Time::msecNow());
|
||||
bool processTrunkFrames(const Time& time = Time());
|
||||
|
||||
/**
|
||||
* Default event for connection transactions handler. This method may be overriden to perform custom
|
||||
|
@ -2983,10 +3113,10 @@ protected:
|
|||
* @param type Transaction type
|
||||
* @param addr Remote address to send the request
|
||||
* @param ieList First frame IE list
|
||||
* @param trunking Enable/disable trunking for this transaction
|
||||
* @return IAXTransaction pointer on success.
|
||||
*/
|
||||
IAXTransaction* startLocalTransaction(IAXTransaction::Type type, const SocketAddr& addr, IAXIEList& ieList, bool trunking = false);
|
||||
IAXTransaction* startLocalTransaction(IAXTransaction::Type type,
|
||||
const SocketAddr& addr, IAXIEList& ieList);
|
||||
|
||||
private:
|
||||
Socket m_socket; // Socket
|
||||
|
@ -3024,7 +3154,7 @@ private:
|
|||
// Trunking
|
||||
Mutex m_mutexTrunk; // Mutex for trunk operations
|
||||
ObjList m_trunkList; // Trunk frames list
|
||||
u_int32_t m_trunkSendInterval; // Trunk frame send interval
|
||||
IAXTrunkInfo m_trunkInfoDef; // Defaults for trunk data
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -284,17 +284,19 @@ public:
|
|||
* @param authTimeout Timeout (in seconds) of acknoledged auth frames sent
|
||||
* @param transTimeout Timeout (in seconds) on remote request of transactions belonging to this engine
|
||||
* @param maxFullFrameDataLen Max full frame IE list (buffer) length
|
||||
* @param trunkSendInterval Send trunk meta frame interval
|
||||
* @param authRequired Automatically challenge all clients for authentication
|
||||
*/
|
||||
YIAXEngine(const char* iface, int port, u_int16_t transListCount, u_int16_t retransCount, u_int16_t retransInterval,
|
||||
u_int16_t authTimeout, u_int16_t transTimeout,
|
||||
u_int16_t maxFullFrameDataLen, u_int32_t trunkSendInterval, bool authRequired,
|
||||
u_int16_t maxFullFrameDataLen, bool authRequired,
|
||||
NamedList* params);
|
||||
|
||||
virtual ~YIAXEngine()
|
||||
{}
|
||||
|
||||
// Setup a given transaction from parameters
|
||||
void initTransaction(IAXTransaction* tr, const NamedList& params);
|
||||
|
||||
/*
|
||||
* Process media from remote peer.
|
||||
* @param transaction IAXTransaction that owns the call leg
|
||||
|
@ -1032,14 +1034,25 @@ void YIAXTrunking::run()
|
|||
YIAXEngine::YIAXEngine(const char* iface, int port, u_int16_t transListCount,
|
||||
u_int16_t retransCount, u_int16_t retransInterval, u_int16_t authTimeout,
|
||||
u_int16_t transTimeout, u_int16_t maxFullFrameDataLen,
|
||||
u_int32_t trunkSendInterval, bool authRequired, NamedList* params)
|
||||
bool authRequired, NamedList* params)
|
||||
: IAXEngine(iface,port,transListCount,retransCount,retransInterval,authTimeout,
|
||||
transTimeout,maxFullFrameDataLen,0,0,
|
||||
trunkSendInterval,authRequired,params),
|
||||
transTimeout,maxFullFrameDataLen,0,0,authRequired,params),
|
||||
m_threadsCreated(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Setup a given transaction from parameters
|
||||
void YIAXEngine::initTransaction(IAXTransaction* tr, const NamedList& params)
|
||||
{
|
||||
if (!tr)
|
||||
return;
|
||||
String prefix = tr->outgoing() ? "trunkout" : "trunkin";
|
||||
String pref = prefix + "_";
|
||||
if (params.getBoolValue(prefix))
|
||||
enableTrunking(tr,¶ms,pref);
|
||||
initTrunkIn(tr,¶ms,pref);
|
||||
}
|
||||
|
||||
// Handle received voice data, forward it to connection's source
|
||||
void YIAXEngine::processMedia(IAXTransaction* transaction, DataBlock& data,
|
||||
u_int32_t tStamp, int type, bool mark)
|
||||
|
@ -1439,14 +1452,13 @@ void YIAXDriver::initialize()
|
|||
u_int16_t authTimeout = 30;
|
||||
u_int16_t transTimeout = 10;
|
||||
u_int16_t maxFullFrameDataLen = 1400;
|
||||
u_int32_t trunkSendInterval = 10;
|
||||
m_port = gen->getIntValue("port",4569);
|
||||
String iface = gen->getValue("addr","0.0.0.0");
|
||||
// set max chans
|
||||
maxChans(gen->getIntValue("maxchans",maxChans()));
|
||||
bool authReq = cfg.getBoolValue("registrar","auth_required",true);
|
||||
m_iaxEngine = new YIAXEngine(iface,m_port,transListCount,retransCount,retransInterval,authTimeout,
|
||||
transTimeout,maxFullFrameDataLen,trunkSendInterval,authReq,gen);
|
||||
m_iaxEngine = new YIAXEngine(iface,m_port,transListCount,retransCount,
|
||||
retransInterval,authTimeout,transTimeout,maxFullFrameDataLen,authReq,gen);
|
||||
m_iaxEngine->debugChain(this);
|
||||
m_iaxEngine->initFormats(cfg.getSection("formats"));
|
||||
int tos = gen->getIntValue("tos",dict_tos,0);
|
||||
|
@ -1536,9 +1548,7 @@ bool YIAXDriver::msgExecute(Message& msg, String& dest)
|
|||
conn->callConnect(msg);
|
||||
msg.setParam("peerid",conn->id());
|
||||
msg.setParam("targetid",conn->id());
|
||||
// Enable trunking if trunkout parameter is enabled
|
||||
if (msg.getBoolValue("trunkout"))
|
||||
m_iaxEngine->enableTrunking(tr);
|
||||
(static_cast<YIAXEngine*>(tr->getEngine()))->initTransaction(tr,msg);
|
||||
}
|
||||
else
|
||||
tr->setUserData(0);
|
||||
|
@ -1806,10 +1816,8 @@ void YIAXConnection::callAccept(Message& msg)
|
|||
u_int32_t ci = IAXFormat::mask(codecs,IAXFormat::Image);
|
||||
iplugin.updateCodecsFromRoute(ci,msg,IAXFormat::Image);
|
||||
iplugin.getEngine()->acceptFormatAndCapability(m_transaction,&ci,IAXFormat::Image);
|
||||
(static_cast<YIAXEngine*>(m_transaction->getEngine()))->initTransaction(m_transaction,msg);
|
||||
m_transaction->sendAccept();
|
||||
// Enable trunking if trunkin parameter is enabled
|
||||
if (msg.getBoolValue("trunkin"))
|
||||
m_iaxEngine->enableTrunking(m_transaction);
|
||||
}
|
||||
m_mutexTrans.unlock();
|
||||
Channel::callAccept(msg);
|
||||
|
|
Loading…
Reference in New Issue