diff --git a/conf.d/yiaxchan.conf.sample b/conf.d/yiaxchan.conf.sample index 57a4e168..0ff34ce5 100644 --- a/conf.d/yiaxchan.conf.sample +++ b/conf.d/yiaxchan.conf.sample @@ -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 diff --git a/libs/yiax/engine.cpp b/libs/yiax/engine.cpp index 7f5d2f07..5135e6bb 100644 --- a/libs/yiax/engine.cpp +++ b/libs/yiax/engine.cpp @@ -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(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(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(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() diff --git a/libs/yiax/frame.cpp b/libs/yiax/frame.cpp index a610e264..9121744c 100644 --- a/libs/yiax/frame.cpp +++ b/libs/yiax/frame.cpp @@ -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(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(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; } diff --git a/libs/yiax/transaction.cpp b/libs/yiax/transaction.cpp index f9404e32..8e98e155 100644 --- a/libs/yiax/transaction.cpp +++ b/libs/yiax/transaction.cpp @@ -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(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(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: */ diff --git a/libs/yiax/yateiax.h b/libs/yiax/yateiax.h index 60a08cc7..92a04283 100644 --- a/libs/yiax/yateiax.h +++ b/libs/yiax/yateiax.h @@ -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 }; } diff --git a/modules/yiaxchan.cpp b/modules/yiaxchan.cpp index 2a4a0f44..ad6b1196 100644 --- a/modules/yiaxchan.cpp +++ b/modules/yiaxchan.cpp @@ -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(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(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);